feat: all project setup
This commit is contained in:
739
API_DOCUMENTATION.md
Normal file
739
API_DOCUMENTATION.md
Normal 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
|
||||
Reference in New Issue
Block a user