Files
bedolaga-cabinet/src/api/adminUsers.ts
c0mrade bc90ba3779 refactor: migrate to eslint flat config and format codebase with prettier
- Remove legacy .eslintrc.cjs and .eslintignore
- Add eslint.config.js with flat config, security rules (no-eval, no-implied-eval, no-new-func, no-script-url)
- Add .prettierrc and .prettierignore
- Format entire codebase with prettier
2026-01-27 17:37:31 +03:00

479 lines
12 KiB
TypeScript

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[];
remnawave_uuid: string | null;
}
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<string, unknown>;
errors: string[];
}
export interface SyncToPanelResponse {
success: boolean;
message: string;
action: string;
panel_uuid: string | null;
changes: Record<string, unknown>;
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;
bot_device_limit: number;
bot_squads: string[];
panel_found: boolean;
panel_status: string | null;
panel_expire_at: string | null;
panel_traffic_limit_gb: number;
panel_traffic_used_gb: number;
panel_device_limit: number;
panel_squads: string[];
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<UsersListResponse> => {
const response = await apiClient.get('/cabinet/admin/users', { params });
return response.data;
},
// Get users stats
getStats: async (): Promise<UsersStatsResponse> => {
const response = await apiClient.get('/cabinet/admin/users/stats');
return response.data;
},
// Get user detail
getUser: async (userId: number): Promise<UserDetailResponse> => {
const response = await apiClient.get(`/cabinet/admin/users/${userId}`);
return response.data;
},
// Get user by telegram ID
getUserByTelegram: async (telegramId: number): Promise<UserDetailResponse> => {
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<UserAvailableTariffsResponse> => {
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<UpdateBalanceResponse> => {
const response = await apiClient.post(`/cabinet/admin/users/${userId}/balance`, data);
return response.data;
},
// Update subscription
updateSubscription: async (
userId: number,
data: UpdateSubscriptionRequest,
): Promise<UpdateSubscriptionResponse> => {
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<UpdateUserStatusResponse> => {
const response = await apiClient.post(`/cabinet/admin/users/${userId}/status`, {
status,
reason,
});
return response.data;
},
// Block user
blockUser: async (userId: number, reason?: string): Promise<UpdateUserStatusResponse> => {
const response = await apiClient.post(`/cabinet/admin/users/${userId}/block`, null, {
params: { reason },
});
return response.data;
},
// Unblock user
unblockUser: async (userId: number): Promise<UpdateUserStatusResponse> => {
const response = await apiClient.post(`/cabinet/admin/users/${userId}/unblock`);
return response.data;
},
// Update restrictions
updateRestrictions: async (
userId: number,
data: UpdateRestrictionsRequest,
): Promise<UpdateRestrictionsResponse> => {
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<UsersListResponse> => {
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<PanelSyncStatusResponse> => {
const response = await apiClient.get(`/cabinet/admin/users/${userId}/sync/status`);
return response.data;
},
// Sync from panel
syncFromPanel: async (
userId: number,
data: SyncFromPanelRequest = {},
): Promise<SyncFromPanelResponse> => {
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<SyncToPanelResponse> => {
const response = await apiClient.post(`/cabinet/admin/users/${userId}/sync/to-panel`, data);
return response.data;
},
};