import { apiClient, ensureCsrfCookie } from './client'; /** * API-вызовы для админских endpoint'ов SaaS (см. ImpersonationController). * * На MVP вызываются без auth:saas-admin middleware (см. routes/web.php). * Production: middleware('auth:saas-admin') + cookie session — apiClient уже * настроен на withCredentials. */ export interface ImpersonationInitPayload { tenant_id: number; requested_by: number; // на MVP параметром; на prod — request()->user()->id reason: string; // ≥30 chars (валидируется на backend) } export interface ImpersonationInitResponse { token_id: number; expires_at: string; // ISO8601 sent_to_email: string; /** dev-only: исчезнет после интеграции MailService на prod */ _dev_plain_code?: string; } export async function impersonationInit(payload: ImpersonationInitPayload): Promise { await ensureCsrfCookie(); const { data } = await apiClient.post('/api/admin/impersonation/init', payload); return data; } export interface ImpersonationVerifyPayload { token_id: number; code: string; // 6 цифр } export interface ImpersonationVerifyResponse { token_id: number; tenant_id: number; used_at: string; message: string; } export async function impersonationVerify(payload: ImpersonationVerifyPayload): Promise { await ensureCsrfCookie(); const { data } = await apiClient.post('/api/admin/impersonation/verify', payload); return data; } export interface ImpersonationEndResponse { token_id: number; session_ended_at: string; message: string; } export async function impersonationEnd(tokenId: number): Promise { await ensureCsrfCookie(); const { data } = await apiClient.post('/api/admin/impersonation/end', { token_id: tokenId, }); return data; } export interface ImpersonationActiveSession { token_id: number; tenant_id: number; tenant_name: string | null; requested_by: number; reason: string; sent_to_email: string; used_at: string; expires_at: string; } export interface ImpersonationRecentSession { token_id: number; tenant_id: number; tenant_name: string | null; requested_by: number; reason: string; used_at: string; session_ended_at: string; duration_seconds: number | null; } export async function impersonationActive(): Promise { const { data } = await apiClient.get<{ sessions: ImpersonationActiveSession[] }>('/api/admin/impersonation/active'); return data.sessions; } export async function impersonationRecent(): Promise { const { data } = await apiClient.get<{ sessions: ImpersonationRecentSession[] }>('/api/admin/impersonation/recent'); return data.sessions; } // === SaaS-admin → Тенанты: lookup для AdminTenantsView === export interface AdminTenant { id: number; subdomain: string; organization_name: string; contact_email: string; status: string; balance_rub: string; balance_leads: number; is_trial: boolean; last_activity_at: string | null; tariff_id: number | null; tariff_name: string | null; /** price_monthly активного тарифа если не-trial; иначе null. */ mrr_rub: string | null; desired_daily_numbers: number | null; chargeback_unrecovered_rub: string; created_at: string | null; } interface AdminTenantsStats { total: number; active: number; trial: number; overdue: number; } export interface ListAdminTenantsParams { status?: string; search?: string; limit?: number; offset?: number; } export interface ListAdminTenantsResponse { tenants: AdminTenant[]; total: number; limit: number; offset: number; stats: AdminTenantsStats; } export async function listAdminTenants(params: ListAdminTenantsParams = {}): Promise { const { data } = await apiClient.get('/api/admin/tenants', { params }); return data; } // === SaaS-admin → Тенанты → детали (для AdminTenantDetailView) === export interface ApiTenantUser { id: number; email: string; first_name: string | null; last_name: string | null; is_active: boolean; totp_enabled: boolean; last_active_at: string | null; last_login_at: string | null; } export interface ApiTenantProject { id: number; name: string; tag: string | null; is_active: boolean; daily_limit_target: number; suppliers_count: number; leads_today: number; } export interface ApiTenantBalanceTx { id: number; type: string; amount_rub: string; amount_leads: number; balance_rub_after: string | null; description: string | null; created_at: string; } export interface ApiTenantActivityEvent { id: number; event: string; deal_id: number; actor_email: string | null; context: Record | null; created_at: string; } interface ApiTenantMetrics { leads_today: number; leads_this_week: number; leads_this_month: number; avg_lead_cost_rub: number | null; runway_days: number | null; } export interface AdminTenantDetailResponse { tenant: AdminTenant; users: ApiTenantUser[]; projects: ApiTenantProject[]; balance_history: ApiTenantBalanceTx[]; activity: ApiTenantActivityEvent[]; metrics: ApiTenantMetrics; } export async function getAdminTenantDetail(subdomain: string): Promise { const { data } = await apiClient.get( `/api/admin/tenants/${encodeURIComponent(subdomain)}`, ); return data; } // === SaaS-admin → Биллинг: aggregates пополнений/списаний === export interface ApiAdminBillingTenant { id: number; subdomain: string; organization_name: string; contact_email: string; status: string; balance_rub: string; tariff_id: number | null; tariff_name: string | null; mrr_rub: string; monthly_topups_rub: string; monthly_charges_rub: string; last_payment_at: string | null; chargeback_unrecovered_rub: string; } interface ApiAdminBillingSummary { total_mrr_rub: string; monthly_revenue_rub: string; overdue_count: number; refunds_count_30d: number; } export interface ListAdminBillingResponse { tenants: ApiAdminBillingTenant[]; summary: ApiAdminBillingSummary; } export async function listAdminBilling(search = ''): Promise { const { data } = await apiClient.get('/api/admin/billing', { params: { search }, }); return data; } // === SaaS-admin → Инциденты === export interface ApiAdminIncident { id: number; incident_id: string; type: string; severity: 'low' | 'medium' | 'high' | 'critical'; summary: string; started_at: string; detected_at: string; resolved_at: string | null; status: 'open' | 'investigating' | 'resolved'; affected_tenants_count: number; affected_users_count: number | null; rkn_notified: boolean; rkn_notified_at: string | null; rkn_deadline_at: string | null; } interface ApiAdminIncidentsSummary { open: number; investigating: number; rkn_pending: number; total_unresolved: number; } export interface ListAdminIncidentsParams { type?: string; severity?: string; unresolved_only?: boolean; limit?: number; offset?: number; } export interface ListAdminIncidentsResponse { incidents: ApiAdminIncident[]; total: number; limit: number; offset: number; summary: ApiAdminIncidentsSummary; } export async function listAdminIncidents(params: ListAdminIncidentsParams = {}): Promise { const { data } = await apiClient.get('/api/admin/incidents', { params }); return data; } // === SaaS-admin → Система: system_settings edit-flow === export interface SystemSetting { key: string; value: string; type: 'int' | 'string' | 'decimal' | 'bool' | 'json'; description: string | null; updated_at: string; updated_by: number | null; } export async function listSystemSettings(): Promise { const { data } = await apiClient.get<{ settings: SystemSetting[] }>('/api/admin/system-settings'); return data.settings; } export interface UpdateSystemSettingPayload { value: string; reason: string; // ≥30 chars admin_user_id: number; // на prod удалится } export interface UpdateSystemSettingResponse { key: string; value: string; previous_value: string; updated_at: string; message: string; } export async function updateSystemSetting( key: string, payload: UpdateSystemSettingPayload, ): Promise { await ensureCsrfCookie(); const { data } = await apiClient.put( `/api/admin/system-settings/${encodeURIComponent(key)}`, payload, ); return data; }