mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 17:43:47 +00:00
Add force_delete_active_paid guard to prevent accidental deletion of active paid subscriptions. Shows warning with count and requires explicit checkbox confirmation. Also fixes allVisibleSubscriptionIds to use filteredUsers and getFilteredSubs to respect trialOnly filter on subscription sub-rows.
761 lines
21 KiB
TypeScript
761 lines
21 KiB
TypeScript
import apiClient from './client';
|
|
|
|
export interface TrafficPurchaseInfo {
|
|
id: number;
|
|
traffic_gb: number;
|
|
expires_at: string;
|
|
created_at: string;
|
|
days_remaining: number;
|
|
is_expired: boolean;
|
|
}
|
|
|
|
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;
|
|
purchased_traffic_gb: number;
|
|
traffic_purchases: TrafficPurchaseInfo[];
|
|
}
|
|
|
|
export interface UserPromoGroupInfo {
|
|
id: number;
|
|
name: string;
|
|
is_default: boolean;
|
|
}
|
|
|
|
export interface UserListItemSubscription {
|
|
id: number;
|
|
tariff_id: number | null;
|
|
tariff_name: string | null;
|
|
status: string;
|
|
is_trial: boolean;
|
|
end_date: string | null;
|
|
days_remaining: number;
|
|
traffic_used_gb: number;
|
|
traffic_limit_gb: number;
|
|
device_limit: number;
|
|
}
|
|
|
|
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;
|
|
tariff_id: number | null;
|
|
tariff_name: string | null;
|
|
traffic_used_gb: number;
|
|
traffic_limit_gb: number;
|
|
device_limit: number;
|
|
days_remaining: number;
|
|
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;
|
|
subscriptions?: UserListItemSubscription[];
|
|
}
|
|
|
|
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;
|
|
subscriptions: UserSubscriptionInfo[];
|
|
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;
|
|
campaign_name: string | null;
|
|
campaign_id: number | null;
|
|
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 UserPanelInfo {
|
|
found: boolean;
|
|
trojan_password: string | null;
|
|
vless_uuid: string | null;
|
|
ss_password: string | null;
|
|
subscription_url: string | null;
|
|
happ_link: string | null;
|
|
used_traffic_bytes: number;
|
|
lifetime_used_traffic_bytes: number;
|
|
traffic_limit_bytes: number;
|
|
first_connected_at: string | null;
|
|
online_at: string | null;
|
|
last_connected_node_uuid: string | null;
|
|
last_connected_node_name: string | null;
|
|
}
|
|
|
|
export interface SubscriptionRequestRecord {
|
|
id: number;
|
|
userUuid: string;
|
|
requestAt: string;
|
|
requestIp: string | null;
|
|
userAgent: string | null;
|
|
}
|
|
|
|
export interface SubscriptionRequestHistory {
|
|
total: number;
|
|
records: SubscriptionRequestRecord[];
|
|
}
|
|
|
|
export interface UserNodeUsageItem {
|
|
node_uuid: string;
|
|
node_name: string;
|
|
country_code: string;
|
|
total_bytes: number;
|
|
daily_bytes: number[];
|
|
}
|
|
|
|
export interface UserNodeUsageResponse {
|
|
items: UserNodeUsageItem[];
|
|
categories: string[];
|
|
period_days: number;
|
|
}
|
|
|
|
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;
|
|
device_price_kopeks: number | null;
|
|
max_device_limit: number | null;
|
|
traffic_topup_enabled: boolean;
|
|
traffic_topup_packages: Record<string, number>;
|
|
max_topup_traffic_gb: 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;
|
|
subscription_id: number | null;
|
|
subscription_tariff_name: 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'
|
|
| 'add_traffic'
|
|
| 'remove_traffic'
|
|
| 'set_device_limit'
|
|
| 'shorten';
|
|
subscription_id?: number;
|
|
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;
|
|
traffic_gb?: number;
|
|
traffic_purchase_id?: 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;
|
|
}
|
|
|
|
export interface AdminUserGiftItem {
|
|
id: number;
|
|
token: string;
|
|
status: string;
|
|
tariff_name: string | null;
|
|
period_days: number;
|
|
device_limit: number;
|
|
amount_kopeks: number;
|
|
payment_method: string | null;
|
|
gift_recipient_type: string | null;
|
|
gift_recipient_value: string | null;
|
|
gift_message: string | null;
|
|
buyer_user_id: number | null;
|
|
buyer_username: string | null;
|
|
buyer_full_name: string | null;
|
|
receiver_user_id: number | null;
|
|
receiver_username: string | null;
|
|
receiver_full_name: string | null;
|
|
created_at: string | null;
|
|
paid_at: string | null;
|
|
delivered_at: string | null;
|
|
}
|
|
|
|
export interface AdminUserGiftsResponse {
|
|
sent: AdminUserGiftItem[];
|
|
received: AdminUserGiftItem[];
|
|
sent_total: number;
|
|
received_total: number;
|
|
}
|
|
|
|
export const adminUsersApi = {
|
|
// List users
|
|
getUsers: async (
|
|
params: {
|
|
offset?: number;
|
|
limit?: number;
|
|
search?: string;
|
|
email?: string;
|
|
status?: 'active' | 'blocked' | 'deleted';
|
|
subscription_status?: string;
|
|
tariff_id?: string;
|
|
promo_group_id?: number;
|
|
campaign_id?: number;
|
|
partner_id?: number;
|
|
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;
|
|
},
|
|
|
|
// Update referral commission
|
|
updateReferralCommission: async (
|
|
userId: number,
|
|
commissionPercent: number | null,
|
|
): Promise<{ success: boolean; message: string }> => {
|
|
const response = await apiClient.post(`/cabinet/admin/users/${userId}/referral-commission`, {
|
|
commission_percent: commissionPercent,
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Delete user (soft delete, does NOT remove from Remnawave)
|
|
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;
|
|
},
|
|
|
|
// Full delete user (removes from bot DB + Remnawave panel)
|
|
fullDeleteUser: async (
|
|
userId: number,
|
|
): Promise<{
|
|
success: boolean;
|
|
message: string;
|
|
deleted_from_bot: boolean;
|
|
deleted_from_panel: boolean;
|
|
panel_error: string | null;
|
|
}> => {
|
|
const response = await apiClient.delete(`/cabinet/admin/users/${userId}/full`);
|
|
return response.data;
|
|
},
|
|
|
|
// Assign a referrer to this user
|
|
assignReferrer: async (
|
|
userId: number,
|
|
referrerId: number,
|
|
): Promise<{ success: boolean; message: string }> => {
|
|
const response = await apiClient.post(`/cabinet/admin/users/${userId}/assign-referrer`, {
|
|
referrer_id: referrerId,
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Remove this user's referrer
|
|
removeReferrer: async (userId: number): Promise<{ success: boolean; message: string }> => {
|
|
const response = await apiClient.delete(`/cabinet/admin/users/${userId}/referrer`);
|
|
return response.data;
|
|
},
|
|
|
|
// Remove a specific referral from this user's list
|
|
removeReferral: async (
|
|
userId: number,
|
|
referralUserId: number,
|
|
): Promise<{ success: boolean; message: string }> => {
|
|
const response = await apiClient.delete(
|
|
`/cabinet/admin/users/${userId}/referrals/${referralUserId}`,
|
|
);
|
|
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,
|
|
subscriptionId?: number,
|
|
): Promise<PanelSyncStatusResponse> => {
|
|
const params = subscriptionId != null ? { subscription_id: subscriptionId } : undefined;
|
|
const response = await apiClient.get(`/cabinet/admin/users/${userId}/sync/status`, { params });
|
|
return response.data;
|
|
},
|
|
|
|
// Sync from panel
|
|
syncFromPanel: async (
|
|
userId: number,
|
|
data: SyncFromPanelRequest = {},
|
|
subscriptionId?: number,
|
|
): Promise<SyncFromPanelResponse> => {
|
|
const params = subscriptionId != null ? { subscription_id: subscriptionId } : undefined;
|
|
const response = await apiClient.post(`/cabinet/admin/users/${userId}/sync/from-panel`, data, {
|
|
params,
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Sync to panel
|
|
syncToPanel: async (
|
|
userId: number,
|
|
data: SyncToPanelRequest = {},
|
|
subscriptionId?: number,
|
|
): Promise<SyncToPanelResponse> => {
|
|
const params = subscriptionId != null ? { subscription_id: subscriptionId } : undefined;
|
|
const response = await apiClient.post(`/cabinet/admin/users/${userId}/sync/to-panel`, data, {
|
|
params,
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Reset trial
|
|
resetTrial: async (userId: number): Promise<{ success: boolean; message: string }> => {
|
|
const response = await apiClient.post(`/cabinet/admin/users/${userId}/reset-trial`);
|
|
return response.data;
|
|
},
|
|
|
|
// Reset subscription
|
|
resetSubscription: async (userId: number): Promise<{ success: boolean; message: string }> => {
|
|
const response = await apiClient.post(`/cabinet/admin/users/${userId}/reset-subscription`);
|
|
return response.data;
|
|
},
|
|
|
|
// Disable user
|
|
disableUser: async (userId: number): Promise<{ success: boolean; message: string }> => {
|
|
const response = await apiClient.post(`/cabinet/admin/users/${userId}/disable`);
|
|
return response.data;
|
|
},
|
|
|
|
// Get panel info
|
|
getPanelInfo: async (userId: number, subscriptionId?: number): Promise<UserPanelInfo> => {
|
|
const response = await apiClient.get(`/cabinet/admin/users/${userId}/panel-info`, {
|
|
params: subscriptionId != null ? { subscription_id: subscriptionId } : undefined,
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Get node usage (always 30 days with daily breakdown)
|
|
getNodeUsage: async (userId: number, subscriptionId?: number): Promise<UserNodeUsageResponse> => {
|
|
const response = await apiClient.get(`/cabinet/admin/users/${userId}/node-usage`, {
|
|
params: subscriptionId != null ? { subscription_id: subscriptionId } : undefined,
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Get subscription request history from RemnaWave panel
|
|
getSubscriptionRequestHistory: async (
|
|
userId: number,
|
|
subscriptionId?: number,
|
|
offset = 0,
|
|
limit = 20,
|
|
): Promise<SubscriptionRequestHistory> => {
|
|
const params: Record<string, unknown> = { offset, limit };
|
|
if (subscriptionId != null) params.subscription_id = subscriptionId;
|
|
const response = await apiClient.get(
|
|
`/cabinet/admin/users/${userId}/subscription-request-history`,
|
|
{ params },
|
|
);
|
|
return response.data;
|
|
},
|
|
|
|
// Get user devices
|
|
getUserDevices: async (
|
|
userId: number,
|
|
subscriptionId?: number,
|
|
): Promise<{
|
|
devices: { hwid: string; platform: string; device_model: string; created_at: string | null }[];
|
|
total: number;
|
|
device_limit: number;
|
|
}> => {
|
|
const response = await apiClient.get(`/cabinet/admin/users/${userId}/devices`, {
|
|
params: subscriptionId != null ? { subscription_id: subscriptionId } : undefined,
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Delete single device
|
|
deleteUserDevice: async (
|
|
userId: number,
|
|
hwid: string,
|
|
subscriptionId?: number,
|
|
): Promise<{ success: boolean; message: string; deleted_hwid: string | null }> => {
|
|
const response = await apiClient.delete(`/cabinet/admin/users/${userId}/devices/${hwid}`, {
|
|
params: subscriptionId != null ? { subscription_id: subscriptionId } : undefined,
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Reset all devices
|
|
resetUserDevices: async (
|
|
userId: number,
|
|
subscriptionId?: number,
|
|
): Promise<{ success: boolean; message: string; deleted_count: number }> => {
|
|
const response = await apiClient.delete(`/cabinet/admin/users/${userId}/devices`, {
|
|
params: subscriptionId != null ? { subscription_id: subscriptionId } : undefined,
|
|
});
|
|
return response.data;
|
|
},
|
|
|
|
// Get user gifts
|
|
getUserGifts: async (userId: number): Promise<AdminUserGiftsResponse> => {
|
|
const response = await apiClient.get(`/cabinet/admin/users/${userId}/gifts`);
|
|
return response.data;
|
|
},
|
|
};
|