feat: all project setup

This commit is contained in:
ivannoskov
2025-11-10 12:07:24 +03:00
parent fbfd7719bb
commit b0a9934220
99 changed files with 19597 additions and 0 deletions

739
API_DOCUMENTATION.md Normal file
View File

@@ -0,0 +1,739 @@
# API Документация
## Базовый URL
```
https://api.tgex.app/v1
```
## Аутентификация
Все запросы (кроме `/auth/*`) требуют JWT токен в заголовке:
```
Authorization: Bearer <token>
```
---
## 1. Аутентификация
### POST `/auth/init`
Инициализация процесса авторизации через Telegram
**Response:**
```typescript
{
bot_username: string;
start_param: string; // "login"
}
```
### POST `/auth/complete`
Завершение авторизации с одноразовым токеном
**Request:**
```typescript
{
token: string; // one-time token from bot
}
```
**Response:**
```typescript
{
access_token: string;
refresh_token: string;
user: User;
}
```
### POST `/auth/refresh`
Обновление access токена
**Request:**
```typescript
{
refresh_token: string;
}
```
**Response:**
```typescript
{
access_token: string;
}
```
### GET `/auth/me`
Получение информации о текущем пользователе
**Response:**
```typescript
User;
```
---
## 2. Типы данных
### User
```typescript
{
id: string;
telegram_id: number;
username: string | null;
first_name: string;
last_name: string | null;
created_at: string; // ISO 8601
}
```
### TargetChannel
```typescript
{
id: string;
telegram_id: number;
title: string;
username: string | null; // @channel_name
description: string | null;
is_active: boolean;
created_at: string;
updated_at: string;
// Stats
total_purchases: number;
total_subscriptions: number;
avg_cpf: number | null; // Average Cost Per Follower
}
```
### ExternalChannel
```typescript
{
id: string;
telegram_id: number | null;
title: string;
username: string | null;
link: string; // t.me link or custom
subscribers_count: number | null;
description: string | null;
created_at: string;
updated_at: string;
// Relations
target_channels: string[]; // IDs of target channels
// Stats
total_purchases: number;
avg_cpf: number | null;
avg_cpm: number | null;
}
```
### Creative
```typescript
{
id: string;
name: string;
text: string; // Template with {link} placeholder
target_channel_id: string;
is_archived: boolean;
created_at: string;
updated_at: string;
// Relations
target_channel: TargetChannel;
// Stats
total_purchases: number;
total_subscriptions: number;
avg_cpf: number | null;
}
```
### Purchase
```typescript
{
id: string;
target_channel_id: string;
external_channel_id: string;
creative_id: string;
// Purchase details
scheduled_date: string | null; // ISO 8601
actual_date: string | null;
cost: number | null; // in rubles
comment: string | null;
post_link: string | null; // Link to ad post in external channel
invite_link: string; // Generated invite link
invite_link_type: "public" | "private"; // with bot approval or not
is_archived: boolean;
created_at: string;
updated_at: string;
// Relations
target_channel: TargetChannel;
external_channel: ExternalChannel;
creative: Creative;
// Stats
subscriptions_count: number;
views_count: number | null;
cpf: number | null; // Cost Per Follower
cpm: number | null; // Cost Per Mile (1000 views)
conversion_rate: number | null; // subscriptions / views * 100
}
```
### Subscription
```typescript
{
id: string;
purchase_id: string;
telegram_user_id: number;
username: string | null;
joined_at: string; // ISO 8601
event_type: "chat_member" | "join_request";
}
```
### ViewsSnapshot
```typescript
{
id: string;
purchase_id: string;
views: number;
fetched_at: string; // ISO 8601
}
```
---
## 3. Целевые каналы
### GET `/target-channels`
Получение списка целевых каналов
**Query params:**
- `is_active?: boolean` - фильтр по активности
**Response:**
```typescript
{
data: TargetChannel[];
total: number;
}
```
### GET `/target-channels/:id`
Получение детальной информации о канале
**Response:**
```typescript
TargetChannel & {
recent_purchases: Purchase[]; // last 5
stats_by_period: {
period: 'day' | 'week' | 'month';
subscriptions: number;
cost: number;
cpf: number;
}[];
}
```
### PATCH `/target-channels/:id`
Обновление канала (только is_active)
**Request:**
```typescript
{
is_active: boolean;
}
```
**Response:**
```typescript
TargetChannel;
```
### DELETE `/target-channels/:id`
Отключение канала (soft delete)
**Response:**
```typescript
{
success: true;
}
```
---
## 4. Внешние каналы
### GET `/external-channels`
Получение списка внешних каналов
**Query params:**
- `target_channel_id?: string` - фильтр по целевому каналу
- `search?: string` - поиск по названию/username
**Response:**
```typescript
{
data: ExternalChannel[];
total: number;
}
```
### GET `/external-channels/:id`
Получение детальной информации
**Response:**
```typescript
ExternalChannel & {
purchases: Purchase[];
}
```
### POST `/external-channels`
Создание нового внешнего канала
**Request:**
```typescript
{
title: string;
username?: string;
link: string;
subscribers_count?: number;
description?: string;
target_channel_ids: string[]; // Привязка к целевым каналам
}
```
**Response:**
```typescript
ExternalChannel;
```
### PATCH `/external-channels/:id`
Обновление внешнего канала
**Request:**
```typescript
Partial<{
title: string;
username: string;
link: string;
subscribers_count: number;
description: string;
target_channel_ids: string[];
}>;
```
**Response:**
```typescript
ExternalChannel;
```
### DELETE `/external-channels/:id`
Удаление внешнего канала
**Response:**
```typescript
{
success: true;
}
```
### POST `/external-channels/import`
Импорт из Excel (Tgstat)
**Request:**
```
Content-Type: multipart/form-data
file: File (Excel)
target_channel_id: string
```
**Response:**
```typescript
{
imported: number;
skipped: number;
errors: string[];
channels: ExternalChannel[];
}
```
---
## 5. Креативы
### GET `/creatives`
Получение списка креативов
**Query params:**
- `target_channel_id?: string` - фильтр по целевому каналу
- `is_archived?: boolean` - фильтр по архивности
**Response:**
```typescript
{
data: Creative[];
total: number;
}
```
### GET `/creatives/:id`
Получение детальной информации
**Response:**
```typescript
Creative & {
purchases: Purchase[];
}
```
### POST `/creatives`
Создание нового креатива
**Request:**
```typescript
{
name: string;
text: string; // Must contain {link} placeholder
target_channel_id: string;
}
```
**Response:**
```typescript
Creative;
```
### PATCH `/creatives/:id`
Обновление креатива
**Request:**
```typescript
Partial<{
name: string;
text: string;
is_archived: boolean;
}>;
```
**Response:**
```typescript
Creative;
```
### DELETE `/creatives/:id`
Удаление креатива (только если нет активных закупов)
**Response:**
```typescript
{
success: true;
}
```
---
## 6. Закупы
### GET `/purchases`
Получение списка закупов
**Query params:**
- `target_channel_id?: string`
- `external_channel_id?: string`
- `creative_id?: string`
- `is_archived?: boolean`
- `date_from?: string` (ISO 8601)
- `date_to?: string` (ISO 8601)
- `sort?: 'date' | 'cost' | 'cpf' | 'subscriptions'`
- `order?: 'asc' | 'desc'`
**Response:**
```typescript
{
data: Purchase[];
total: number;
}
```
### GET `/purchases/:id`
Получение детальной информации о закупе
**Response:**
```typescript
Purchase & {
subscriptions: Subscription[];
views_history: ViewsSnapshot[];
}
```
### POST `/purchases`
Создание нового закупа
**Request:**
```typescript
{
target_channel_id: string;
external_channel_id: string;
creative_id: string;
scheduled_date?: string;
actual_date?: string;
cost?: number;
comment?: string;
post_link?: string;
invite_link_type: 'public' | 'private';
}
```
**Response:**
```typescript
Purchase & {
formatted_message: string; // Creative text with invite_link inserted
}
```
### PATCH `/purchases/:id`
Обновление закупа
**Request:**
```typescript
Partial<{
scheduled_date: string;
actual_date: string;
cost: number;
comment: string;
post_link: string;
is_archived: boolean;
}>;
```
**Response:**
```typescript
Purchase;
```
### DELETE `/purchases/:id`
Удаление закупа
**Response:**
```typescript
{
success: true;
}
```
### POST `/purchases/:id/refresh-views`
Принудительное обновление просмотров
**Response:**
```typescript
{
views: number;
fetched_at: string;
}
```
---
## 7. Аналитика
### GET `/analytics/overview`
Общая статистика
**Query params:**
- `target_channel_id?: string`
- `date_from?: string`
- `date_to?: string`
**Response:**
```typescript
{
total_purchases: number;
total_cost: number;
total_subscriptions: number;
avg_cpf: number;
avg_cpm: number;
avg_conversion_rate: number;
}
```
### GET `/analytics/costs`
Данные для графика затрат
**Query params:**
- `target_channel_id?: string`
- `date_from: string` (required)
- `date_to: string` (required)
- `group_by: 'day' | 'week' | 'month' | 'quarter' | 'year'`
**Response:**
```typescript
{
data: {
period: string; // ISO 8601 date (start of period)
cost: number;
purchases_count: number;
subscriptions: number;
}
[];
}
```
### GET `/analytics/channels`
Аналитика по каналам
**Query params:**
- `target_channel_id?: string`
- `date_from?: string`
- `date_to?: string`
- `type: 'external' | 'target'`
**Response:**
```typescript
{
data: {
channel_id: string;
channel_name: string;
purchases_count: number;
total_cost: number;
total_subscriptions: number;
avg_cpf: number;
avg_cpm: number;
}
[];
}
```
### GET `/analytics/creatives`
Аналитика по креативам
**Query params:**
- `target_channel_id?: string`
- `date_from?: string`
- `date_to?: string`
**Response:**
```typescript
{
data: {
creative_id: string;
creative_name: string;
purchases_count: number;
total_cost: number;
total_subscriptions: number;
avg_cpf: number;
conversion_rate: number;
}
[];
}
```
---
## 8. Коды ошибок
```typescript
{
error: {
code: string;
message: string;
details?: any;
}
}
```
Основные коды:
- `UNAUTHORIZED` - 401
- `FORBIDDEN` - 403
- `NOT_FOUND` - 404
- `VALIDATION_ERROR` - 422
- `INTERNAL_ERROR` - 500
- `CHANNEL_NOT_FOUND` - 404
- `CHANNEL_ALREADY_EXISTS` - 409
- `INSUFFICIENT_PERMISSIONS` - 403
- `CREATIVE_IN_USE` - 409
- `INVALID_INVITE_LINK` - 422