2026-08-07 10:44:03 +07:00
|
|
|
/**
|
2026-08-16 13:25:10 +07:00
|
|
|
* Server-only data layer (React Server Components / route handlers).
|
2026-08-07 10:44:03 +07:00
|
|
|
*
|
2026-08-16 13:25:10 +07:00
|
|
|
* The dashboard is fully tRPC-native: this module talks to the backend's
|
|
|
|
|
* tRPC HTTP endpoint (/trpc) via an httpLink client — the same appRouter the
|
|
|
|
|
* browser reaches over WebSocket. No legacy REST `/api/*` is used.
|
2026-08-07 10:44:03 +07:00
|
|
|
*
|
2026-08-16 13:25:10 +07:00
|
|
|
* The client is loosely typed (see ./types → TRPCClient); results are asserted
|
|
|
|
|
* to the frontend's local types at each call site.
|
|
|
|
|
*
|
|
|
|
|
* Never import this module from a client component. Browser code uses
|
|
|
|
|
* `@/lib/trpc/client` (wsLink) via the `@/lib/api/*` wrappers.
|
2026-08-07 10:44:03 +07:00
|
|
|
*/
|
|
|
|
|
|
2026-08-16 13:25:10 +07:00
|
|
|
import { createTRPCClient, httpBatchLink } from "@trpc/client";
|
2026-08-07 10:44:03 +07:00
|
|
|
import type {
|
|
|
|
|
AppConfig,
|
|
|
|
|
DashboardActivity,
|
|
|
|
|
DashboardStats,
|
|
|
|
|
Guild,
|
|
|
|
|
MediaState,
|
|
|
|
|
ModerationStats,
|
2026-08-16 13:25:10 +07:00
|
|
|
PaginatedModerationActions,
|
2026-08-07 10:44:03 +07:00
|
|
|
PaginatedRecordings,
|
|
|
|
|
VoiceStatus,
|
|
|
|
|
} from "@/lib/types";
|
2026-08-16 13:25:10 +07:00
|
|
|
import type { TRPCClient } from "../trpc/types";
|
2026-08-07 10:44:03 +07:00
|
|
|
|
|
|
|
|
const BACKEND_URL =
|
|
|
|
|
process.env.GMW_BACKEND_URL?.replace(/\/+$/, "") || "http://127.0.0.1:4001";
|
|
|
|
|
|
2026-08-16 13:25:10 +07:00
|
|
|
let _client: TRPCClient | null = null;
|
|
|
|
|
function serverTrpc(): TRPCClient {
|
|
|
|
|
if (!_client) {
|
|
|
|
|
_client = createTRPCClient({
|
|
|
|
|
links: [
|
|
|
|
|
httpBatchLink({
|
|
|
|
|
url: `${BACKEND_URL}/trpc`,
|
|
|
|
|
fetch(url, init) {
|
|
|
|
|
return fetch(url, { ...init, cache: "no-store" });
|
|
|
|
|
},
|
|
|
|
|
}),
|
|
|
|
|
],
|
|
|
|
|
}) as unknown as TRPCClient;
|
|
|
|
|
}
|
|
|
|
|
return _client;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-07 10:44:03 +07:00
|
|
|
export class ApiServerError extends Error {
|
|
|
|
|
statusCode: number;
|
|
|
|
|
constructor(message: string, statusCode: number) {
|
|
|
|
|
super(message);
|
|
|
|
|
this.name = "ApiServerError";
|
|
|
|
|
this.statusCode = statusCode;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- Dashboard ----
|
|
|
|
|
export async function getDashboardStats(): Promise<DashboardStats> {
|
2026-08-16 13:25:10 +07:00
|
|
|
return serverTrpc().dashboard.stats.query() as unknown as Promise<DashboardStats>;
|
2026-08-07 10:44:03 +07:00
|
|
|
}
|
|
|
|
|
export async function getActivity(days = 14): Promise<DashboardActivity> {
|
2026-08-16 13:25:10 +07:00
|
|
|
return serverTrpc().dashboard.activity.query({
|
|
|
|
|
days,
|
|
|
|
|
}) as unknown as Promise<DashboardActivity>;
|
2026-08-07 10:44:03 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- Media ----
|
|
|
|
|
export async function getMediaStatus(): Promise<MediaState> {
|
2026-08-16 13:25:10 +07:00
|
|
|
return serverTrpc().media.status.query() as unknown as Promise<MediaState>;
|
2026-08-07 10:44:03 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- Config ----
|
|
|
|
|
export async function getConfig(): Promise<AppConfig> {
|
2026-08-16 13:25:10 +07:00
|
|
|
return serverTrpc().config.get.query() as unknown as Promise<AppConfig>;
|
2026-08-07 10:44:03 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- Moderation ----
|
|
|
|
|
export async function getModerationStats(): Promise<ModerationStats> {
|
2026-08-16 13:25:10 +07:00
|
|
|
return serverTrpc().moderation.stats.query() as unknown as Promise<ModerationStats>;
|
2026-08-07 10:44:03 +07:00
|
|
|
}
|
2026-08-16 13:25:10 +07:00
|
|
|
export async function getModerationActions(limit = 100) {
|
|
|
|
|
const res = (await serverTrpc().moderation.actions.query({
|
|
|
|
|
limit,
|
|
|
|
|
})) as unknown as PaginatedModerationActions;
|
2026-08-07 10:44:03 +07:00
|
|
|
return res.data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- Voice ----
|
|
|
|
|
export async function getGuilds(): Promise<Guild[]> {
|
2026-08-16 13:25:10 +07:00
|
|
|
return serverTrpc().voice.guilds.query() as unknown as Promise<Guild[]>;
|
2026-08-07 10:44:03 +07:00
|
|
|
}
|
|
|
|
|
export async function getVoiceStatus(): Promise<VoiceStatus> {
|
2026-08-16 13:25:10 +07:00
|
|
|
return serverTrpc().voice.status.query() as unknown as Promise<VoiceStatus>;
|
2026-08-07 10:44:03 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- Recordings ----
|
|
|
|
|
export async function getRecordings(limit = 50): Promise<PaginatedRecordings> {
|
2026-08-16 13:25:10 +07:00
|
|
|
return serverTrpc().recordings.list.query({
|
|
|
|
|
limit,
|
|
|
|
|
}) as unknown as Promise<PaginatedRecordings>;
|
2026-08-07 10:44:03 +07:00
|
|
|
}
|