From ce7b69eca5d9dc669a2d2621763590d4df0e2b32 Mon Sep 17 00:00:00 2001 From: Egor Date: Sat, 17 Jan 2026 06:01:21 +0300 Subject: [PATCH] Add files via upload --- src/api/adminUsers.ts | 413 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 413 insertions(+) create mode 100644 src/api/adminUsers.ts diff --git a/src/api/adminUsers.ts b/src/api/adminUsers.ts new file mode 100644 index 0000000..02ce739 --- /dev/null +++ b/src/api/adminUsers.ts @@ -0,0 +1,413 @@ +import apiClient from './client' + +// ============ Types ============ + +export interface UserSubscriptionInfo { + id: number + status: string + is_trial: boolean + start_date: string | null + end_date: string | null + traffic_limit_gb: number + traffic_used_gb: number + device_limit: number + tariff_id: number | null + tariff_name: string | null + autopay_enabled: boolean + is_active: boolean + days_remaining: number +} + +export interface UserPromoGroupInfo { + id: number + name: string + is_default: boolean +} + +export interface UserListItem { + id: number + telegram_id: number + username: string | null + first_name: string | null + last_name: string | null + full_name: string + status: string + balance_kopeks: number + balance_rubles: number + created_at: string + last_activity: string | null + has_subscription: boolean + subscription_status: string | null + subscription_is_trial: boolean + subscription_end_date: string | null + promo_group_id: number | null + promo_group_name: string | null + total_spent_kopeks: number + purchase_count: number + has_restrictions: boolean + restriction_topup: boolean + restriction_subscription: boolean +} + +export interface UsersListResponse { + users: UserListItem[] + total: number + offset: number + limit: number +} + +export interface UserTransactionItem { + id: number + type: string + amount_kopeks: number + amount_rubles: number + description: string | null + payment_method: string | null + is_completed: boolean + created_at: string +} + +export interface UserReferralInfo { + referral_code: string + referrals_count: number + total_earnings_kopeks: number + commission_percent: number | null + referred_by_id: number | null + referred_by_username: string | null +} + +export interface UserDetailResponse { + id: number + telegram_id: number + username: string | null + first_name: string | null + last_name: string | null + full_name: string + status: string + language: string + balance_kopeks: number + balance_rubles: number + email: string | null + email_verified: boolean + created_at: string + updated_at: string | null + last_activity: string | null + cabinet_last_login: string | null + subscription: UserSubscriptionInfo | null + promo_group: UserPromoGroupInfo | null + referral: UserReferralInfo + total_spent_kopeks: number + purchase_count: number + used_promocodes: number + has_had_paid_subscription: boolean + lifetime_used_traffic_bytes: number + restriction_topup: boolean + restriction_subscription: boolean + restriction_reason: string | null + promo_offer_discount_percent: number + promo_offer_discount_source: string | null + promo_offer_discount_expires_at: string | null + recent_transactions: UserTransactionItem[] +} + +export interface UsersStatsResponse { + total_users: number + active_users: number + blocked_users: number + deleted_users: number + new_today: number + new_week: number + new_month: number + users_with_subscription: number + users_with_active_subscription: number + users_with_trial: number + users_with_expired_subscription: number + total_balance_kopeks: number + total_balance_rubles: number + avg_balance_kopeks: number + active_today: number + active_week: number + active_month: number +} + +// Available tariffs +export interface PeriodPriceInfo { + days: number + price_kopeks: number + price_rubles: number +} + +export interface UserAvailableTariff { + id: number + name: string + description: string | null + is_active: boolean + is_trial_available: boolean + traffic_limit_gb: number + device_limit: number + tier_level: number + display_order: number + period_prices: PeriodPriceInfo[] + is_daily: boolean + daily_price_kopeks: number + custom_days_enabled: boolean + price_per_day_kopeks: number + min_days: number + max_days: number + is_available: boolean + requires_promo_group: boolean +} + +export interface UserAvailableTariffsResponse { + user_id: number + promo_group_id: number | null + promo_group_name: string | null + tariffs: UserAvailableTariff[] + total: number + current_tariff_id: number | null + current_tariff_name: string | null +} + +// Sync types +export interface PanelUserInfo { + uuid: string | null + short_uuid: string | null + username: string | null + status: string | null + expire_at: string | null + traffic_limit_gb: number + traffic_used_gb: number + device_limit: number + subscription_url: string | null + active_squads: string[] +} + +export interface SyncFromPanelResponse { + success: boolean + message: string + panel_user: PanelUserInfo | null + changes: Record + errors: string[] +} + +export interface SyncToPanelResponse { + success: boolean + message: string + action: string + panel_uuid: string | null + changes: Record + errors: string[] +} + +export interface PanelSyncStatusResponse { + user_id: number + telegram_id: number + remnawave_uuid: string | null + last_sync: string | null + bot_subscription_status: string | null + bot_subscription_end_date: string | null + bot_traffic_limit_gb: number + bot_traffic_used_gb: number + panel_found: boolean + panel_status: string | null + panel_expire_at: string | null + panel_traffic_limit_gb: number + panel_traffic_used_gb: number + has_differences: boolean + differences: string[] +} + +// Update types +export interface UpdateBalanceRequest { + amount_kopeks: number + description?: string + create_transaction?: boolean +} + +export interface UpdateBalanceResponse { + success: boolean + old_balance_kopeks: number + new_balance_kopeks: number + message: string +} + +export interface UpdateSubscriptionRequest { + action: 'extend' | 'set_end_date' | 'change_tariff' | 'set_traffic' | 'toggle_autopay' | 'cancel' | 'activate' | 'create' + days?: number + end_date?: string + tariff_id?: number + traffic_limit_gb?: number + traffic_used_gb?: number + autopay_enabled?: boolean + is_trial?: boolean + device_limit?: number +} + +export interface UpdateSubscriptionResponse { + success: boolean + message: string + subscription: UserSubscriptionInfo | null +} + +export interface UpdateUserStatusResponse { + success: boolean + old_status: string + new_status: string + message: string +} + +export interface UpdateRestrictionsRequest { + restriction_topup?: boolean + restriction_subscription?: boolean + restriction_reason?: string +} + +export interface UpdateRestrictionsResponse { + success: boolean + restriction_topup: boolean + restriction_subscription: boolean + restriction_reason: string | null + message: string +} + +export interface SyncFromPanelRequest { + update_subscription?: boolean + update_traffic?: boolean + create_if_missing?: boolean +} + +export interface SyncToPanelRequest { + create_if_missing?: boolean + update_status?: boolean + update_traffic_limit?: boolean + update_expire_date?: boolean + update_squads?: boolean +} + +// ============ API ============ + +export const adminUsersApi = { + // List users + getUsers: async (params: { + offset?: number + limit?: number + search?: string + status?: 'active' | 'blocked' | 'deleted' + sort_by?: 'created_at' | 'balance' | 'traffic' | 'last_activity' | 'total_spent' | 'purchase_count' + } = {}): Promise => { + const response = await apiClient.get('/cabinet/admin/users', { params }) + return response.data + }, + + // Get users stats + getStats: async (): Promise => { + const response = await apiClient.get('/cabinet/admin/users/stats') + return response.data + }, + + // Get user detail + getUser: async (userId: number): Promise => { + const response = await apiClient.get(`/cabinet/admin/users/${userId}`) + return response.data + }, + + // Get user by telegram ID + getUserByTelegram: async (telegramId: number): Promise => { + const response = await apiClient.get(`/cabinet/admin/users/by-telegram/${telegramId}`) + return response.data + }, + + // Get available tariffs for user + getAvailableTariffs: async (userId: number, includeInactive = false): Promise => { + const response = await apiClient.get(`/cabinet/admin/users/${userId}/available-tariffs`, { + params: { include_inactive: includeInactive } + }) + return response.data + }, + + // Update balance + updateBalance: async (userId: number, data: UpdateBalanceRequest): Promise => { + const response = await apiClient.post(`/cabinet/admin/users/${userId}/balance`, data) + return response.data + }, + + // Update subscription + updateSubscription: async (userId: number, data: UpdateSubscriptionRequest): Promise => { + const response = await apiClient.post(`/cabinet/admin/users/${userId}/subscription`, data) + return response.data + }, + + // Update status + updateStatus: async (userId: number, status: 'active' | 'blocked' | 'deleted', reason?: string): Promise => { + const response = await apiClient.post(`/cabinet/admin/users/${userId}/status`, { status, reason }) + return response.data + }, + + // Block user + blockUser: async (userId: number, reason?: string): Promise => { + const response = await apiClient.post(`/cabinet/admin/users/${userId}/block`, null, { params: { reason } }) + return response.data + }, + + // Unblock user + unblockUser: async (userId: number): Promise => { + const response = await apiClient.post(`/cabinet/admin/users/${userId}/unblock`) + return response.data + }, + + // Update restrictions + updateRestrictions: async (userId: number, data: UpdateRestrictionsRequest): Promise => { + const response = await apiClient.post(`/cabinet/admin/users/${userId}/restrictions`, data) + return response.data + }, + + // Update promo group + updatePromoGroup: async (userId: number, promoGroupId: number | null): Promise<{ success: boolean; message: string }> => { + const response = await apiClient.post(`/cabinet/admin/users/${userId}/promo-group`, { promo_group_id: promoGroupId }) + return response.data + }, + + // Delete user + deleteUser: async (userId: number, softDelete = true, reason?: string): Promise<{ success: boolean; message: string }> => { + const response = await apiClient.delete(`/cabinet/admin/users/${userId}`, { + data: { soft_delete: softDelete, reason } + }) + return response.data + }, + + // Get referrals + getReferrals: async (userId: number, offset = 0, limit = 50): Promise => { + const response = await apiClient.get(`/cabinet/admin/users/${userId}/referrals`, { + params: { offset, limit } + }) + return response.data + }, + + // Get transactions + getTransactions: async (userId: number, params: { + offset?: number + limit?: number + transaction_type?: string + } = {}): Promise<{ transactions: UserTransactionItem[]; total: number; offset: number; limit: number }> => { + const response = await apiClient.get(`/cabinet/admin/users/${userId}/transactions`, { params }) + return response.data + }, + + // Sync status + getSyncStatus: async (userId: number): Promise => { + const response = await apiClient.get(`/cabinet/admin/users/${userId}/sync/status`) + return response.data + }, + + // Sync from panel + syncFromPanel: async (userId: number, data: SyncFromPanelRequest = {}): Promise => { + const response = await apiClient.post(`/cabinet/admin/users/${userId}/sync/from-panel`, data) + return response.data + }, + + // Sync to panel + syncToPanel: async (userId: number, data: SyncToPanelRequest = {}): Promise => { + const response = await apiClient.post(`/cabinet/admin/users/${userId}/sync/to-panel`, data) + return response.data + }, +}