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>
174 lines
4.4 KiB
TypeScript
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,
|
|
);
|
|
}
|