improvement for the backend api

This commit is contained in:
ivannoskov
2025-11-19 12:56:04 +03:00
parent b0a9934220
commit d4672ea32b
40 changed files with 2383 additions and 3313 deletions

View File

@@ -7,12 +7,9 @@
// ----------------------------------------------------------------------------
export interface User {
id: string;
id: string; // UUID
telegram_id: number;
username: string | null;
first_name: string;
last_name: string | null;
created_at: string; // ISO 8601
}
export interface AuthInitResponse {
@@ -42,189 +39,172 @@ export interface AuthRefreshResponse {
// Target Channel
// ----------------------------------------------------------------------------
// Основная структура как возвращает API
export interface TargetChannel {
id: string;
id: string; // UUID
telegram_id: number;
title: string;
username: string | null; // @channel_name
description: string | null;
username: 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
}
// Расширенная версия с дополнительными полями (пока не реализовано на бекенде)
export interface TargetChannelDetail extends TargetChannel {
recent_purchases: Purchase[]; // last 5
stats_by_period: {
period: "day" | "week" | "month";
subscriptions: number;
cost: number;
cpf: number;
}[];
// В будущем можно добавить статистику
}
export interface TargetChannelUpdateRequest {
is_active: boolean;
is_active?: boolean;
}
export interface TargetChannelsListResponse {
target_channels: TargetChannel[];
}
// ----------------------------------------------------------------------------
// External Channel
// ----------------------------------------------------------------------------
// Основная структура как возвращает API
export interface ExternalChannel {
id: string;
telegram_id: number | null;
id: string; // UUID
telegram_id: number;
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;
}
export interface ExternalChannelDetail extends ExternalChannel {
purchases: Purchase[];
subscribers_count: number | null;
}
export interface ExternalChannelCreateRequest {
telegram_id: number;
title: string;
username?: string;
link: string;
subscribers_count?: number;
description?: string;
target_channel_ids: string[]; // Привязка к целевым каналам
username?: string | null;
description?: string | null;
subscribers_count?: number | null;
target_channel_ids: string[]; // UUID[]
}
export interface ExternalChannelUpdateRequest {
title?: string;
username?: string;
link?: string;
subscribers_count?: number;
description?: string;
target_channel_ids?: string[];
username?: string | null;
description?: string | null;
subscribers_count?: number | null;
}
export interface ExternalChannelImportResponse {
imported: number;
skipped: number;
errors: string[];
channels: ExternalChannel[];
export interface ExternalChannelLinksUpdateRequest {
add_target_channel_ids?: string[];
remove_target_channel_ids?: string[];
}
export interface ExternalChannelsListResponse {
external_channels: ExternalChannel[];
}
// ----------------------------------------------------------------------------
// Creative
// ----------------------------------------------------------------------------
export type CreativeStatus = "active" | "archived";
export interface Creative {
id: string;
name: string;
text: string; // Template with {link} placeholder
text: string;
target_channel_id: string;
is_archived: boolean;
target_channel_title: string;
created_at: string;
updated_at: string;
// Relations
target_channel: TargetChannel;
// Stats
total_purchases: number;
total_subscriptions: number;
avg_cpf: number | null;
status: CreativeStatus;
placements_count: number;
}
export interface CreativeDetail extends Creative {
purchases: Purchase[];
export interface CreativesListResponse {
creatives: Creative[];
}
export interface CreativeCreateRequest {
name: string;
text: string; // Must contain {link} placeholder
text: string;
target_channel_id: string;
}
export interface CreativeUpdateRequest {
name?: string;
text?: string;
is_archived?: boolean;
status?: CreativeStatus;
}
// ----------------------------------------------------------------------------
// Purchase
// Placement (Purchase)
// ----------------------------------------------------------------------------
export type InviteLinkType = "public" | "private";
export type InviteLinkType = "public" | "approval";
export type PlacementStatus = "active" | "archived";
export type ViewsAvailability =
| "unknown"
| "available"
| "unavailable"
| "manual";
export interface Purchase {
export interface Placement {
id: string;
target_channel_id: string;
target_channel_title: string;
external_channel_id: string;
external_channel_title: string;
creative_id: string;
// Purchase details
scheduled_date: string | null; // ISO 8601
actual_date: string | null;
cost: number | null; // in rubles
creative_name: string;
placement_date: string;
cost: number | null;
comment: string | null;
post_link: string | null; // Link to ad post in external channel
invite_link: string; // Generated invite link
invite_link_type: InviteLinkType; // with bot approval or not
is_archived: boolean;
created_at: string;
updated_at: string;
// Relations
target_channel: TargetChannel;
external_channel: ExternalChannel;
creative: Creative;
// Stats
ad_post_url: string | null;
invite_link_type: InviteLinkType;
invite_link: string;
status: PlacementStatus;
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
views_availability: ViewsAvailability;
last_views_fetch_at: string | null;
created_at: string;
}
export interface PurchaseDetail extends Purchase {
subscriptions: Subscription[];
views_history: ViewsSnapshot[];
export interface PlacementsListResponse {
placements: Placement[];
}
export interface PurchaseCreateRequest {
export interface PlacementCreateRequest {
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: InviteLinkType;
placement_date: string;
cost?: number | null;
comment?: string | null;
ad_post_url?: string | null;
invite_link_type?: InviteLinkType;
}
export interface PurchaseCreateResponse extends Purchase {
formatted_message: string; // Creative text with invite_link inserted
export interface PlacementUpdateRequest {
placement_date?: string;
cost?: number | null;
comment?: string | null;
ad_post_url?: string | null;
status?: PlacementStatus;
}
export interface PurchaseUpdateRequest {
scheduled_date?: string;
actual_date?: string;
cost?: number;
comment?: string;
post_link?: string;
is_archived?: boolean;
}
// Legacy aliases for backwards compatibility
export type Purchase = Placement;
export type PurchaseCreateRequest = PlacementCreateRequest;
export type PurchaseUpdateRequest = PlacementUpdateRequest;
export interface PurchaseRefreshViewsResponse {
views: number;
export interface ViewsFetchResponse {
placement_id: string;
views_count: number | null;
views_availability: ViewsAvailability;
fetched_at: string;
error_message: string | null;
}
export interface ViewsHistoryResponse {
histories: ViewsSnapshot[];
}
// ----------------------------------------------------------------------------
@@ -372,7 +352,7 @@ export interface CreativeQueryParams {
is_archived?: boolean;
}
export interface PurchaseQueryParams {
export interface PlacementQueryParams {
target_channel_id?: string;
external_channel_id?: string;
creative_id?: string;