Files
MapMil/centers/analytics/frontend/src/api/admin.ts
T
gitrusprusandCursor 5811ecb134 Add VPN admin tab with subscription proxy via cp-vpn for selected sources.
Persist settings in CA, expose /admin/vpn and /internal/vpn, and route Telegram (and optional web/nlp) traffic through mihomo SOCKS when enabled.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-16 23:41:33 +03:00

174 lines
4.4 KiB
TypeScript

import { ADMIN_BASE, request } from "./client";
import type {
AnalyticsSummary,
Consumer,
ConsumerCreate,
ConsumerUpdate,
EventCreate,
EventFilters,
EventListResponse,
EventRecord,
EventUpdate,
ParseJob,
ParseJobCreate,
ParseJobUpdate,
TimelinePoint,
TopItem,
VpnSettings,
VpnSettingsUpdate,
} from "../types/admin";
function buildQuery(params: Record<string, string | number | undefined>): string {
const search = new URLSearchParams();
for (const [key, value] of Object.entries(params)) {
if (value !== undefined && value !== "") {
search.set(key, String(value));
}
}
const qs = search.toString();
return qs ? `?${qs}` : "";
}
export function fetchJobs(): Promise<ParseJob[]> {
return request<ParseJob[]>("/jobs", undefined, ADMIN_BASE);
}
export function createJob(payload: ParseJobCreate): Promise<ParseJob> {
return request<ParseJob>(
"/jobs",
{ method: "POST", body: JSON.stringify(payload) },
ADMIN_BASE,
);
}
export function retryJob(jobId: number): Promise<ParseJob> {
return request<ParseJob>(
`/jobs/${jobId}/retry`,
{ method: "POST" },
ADMIN_BASE,
);
}
export function updateJob(jobId: number, payload: ParseJobUpdate): Promise<ParseJob> {
return request<ParseJob>(
`/jobs/${jobId}`,
{ method: "PATCH", body: JSON.stringify(payload) },
ADMIN_BASE,
);
}
export function deleteJob(jobId: number): Promise<void> {
return request<void>(
`/jobs/${jobId}`,
{ method: "DELETE" },
ADMIN_BASE,
);
}
export function fetchEvents(filters: EventFilters = {}): Promise<EventListResponse> {
return request<EventListResponse>(
`/events${buildQuery(filters as Record<string, string | number | undefined>)}`,
undefined,
ADMIN_BASE,
);
}
export function createEvent(payload: EventCreate): Promise<EventRecord> {
return request<EventRecord>(
"/events",
{ method: "POST", body: JSON.stringify(payload) },
ADMIN_BASE,
);
}
export function updateEvent(id: number, payload: EventUpdate): Promise<EventRecord> {
return request<EventRecord>(
`/events/${id}`,
{ method: "PATCH", body: JSON.stringify(payload) },
ADMIN_BASE,
);
}
export function deleteEvent(id: number): Promise<void> {
return request<void>(`/events/${id}`, { method: "DELETE" }, ADMIN_BASE);
}
export function fetchConsumers(): Promise<Consumer[]> {
return request<Consumer[]>("/consumers", undefined, ADMIN_BASE);
}
export function createConsumer(payload: ConsumerCreate): Promise<Consumer> {
return request<Consumer>(
"/consumers",
{ method: "POST", body: JSON.stringify(payload) },
ADMIN_BASE,
);
}
export function updateConsumer(id: number, payload: ConsumerUpdate): Promise<Consumer> {
return request<Consumer>(
`/consumers/${id}`,
{ method: "PATCH", body: JSON.stringify(payload) },
ADMIN_BASE,
);
}
export function rotateConsumerKey(id: number): Promise<Consumer> {
return request<Consumer>(
`/consumers/${id}/rotate-key`,
{ method: "POST" },
ADMIN_BASE,
);
}
export function fetchAnalyticsSummary(): Promise<AnalyticsSummary> {
return request<AnalyticsSummary>("/analytics/summary", undefined, ADMIN_BASE);
}
export function fetchAnalyticsTimeline(days = 30): Promise<TimelinePoint[]> {
return request<TimelinePoint[]>(
`/analytics/timeline${buildQuery({ days })}`,
undefined,
ADMIN_BASE,
);
}
export function fetchTopLocalities(limit = 10): Promise<TopItem[]> {
return request<TopItem[]>(
`/analytics/top-localities${buildQuery({ limit })}`,
undefined,
ADMIN_BASE,
);
}
export function fetchTopRegions(limit = 10): Promise<TopItem[]> {
return request<TopItem[]>(
`/analytics/top-regions${buildQuery({ limit })}`,
undefined,
ADMIN_BASE,
);
}
export async function testDistribution(apiKey: string, limit = 5): Promise<EventRecord[]> {
const response = await fetch(`/api/v1/events?limit=${limit}`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
if (!response.ok) {
const message = await response.text();
throw new Error(message || `Ошибка запроса: ${response.status}`);
}
return response.json() as Promise<EventRecord[]>;
}
export function fetchVpnSettings(): Promise<VpnSettings> {
return request<VpnSettings>("/vpn", undefined, ADMIN_BASE);
}
export function updateVpnSettings(payload: VpnSettingsUpdate): Promise<VpnSettings> {
return request<VpnSettings>(
"/vpn",
{ method: "PUT", body: JSON.stringify(payload) },
ADMIN_BASE,
);
}