Harden wechat summary and login input

This commit is contained in:
john
2026-06-20 17:02:16 +08:00
parent 78907792db
commit dae3e2f350
3 changed files with 636 additions and 22 deletions
+101 -3
View File
@@ -16,6 +16,11 @@ import type {
SkillDefinition,
SkillMap,
UsageRecord,
WechatAdminSummary,
WechatBinding,
WechatDeliveryLog,
WechatDigestSubscription,
WechatMessage,
} from '../types';
export class ApiError extends Error {
@@ -94,6 +99,7 @@ async function portalFetch<T>(path: string, init?: RequestInit): Promise<T> {
try {
res = await fetch(path, {
...init,
credentials: 'include',
headers: { 'Content-Type': 'application/json', ...init?.headers },
});
} catch (err) {
@@ -119,7 +125,7 @@ async function portalFetch<T>(path: string, init?: RequestInit): Promise<T> {
export async function checkAuth(): Promise<AuthStatus> {
try {
const response = await fetch('/auth/status');
const response = await fetch('/auth/status', { credentials: 'include' });
if (!response.ok) return { authenticated: false };
const status = (await response.json()) as AuthStatus;
if (status.authenticated) resetUnauthorizedGuard();
@@ -134,6 +140,7 @@ export async function login(username: string, password: string): Promise<PortalU
try {
response = await fetch('/auth/login', {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
});
@@ -152,8 +159,7 @@ export async function login(username: string, password: string): Promise<PortalU
}
export async function logout(): Promise<void> {
if (import.meta.env.DEV) return;
await fetch('/auth/logout', { method: 'POST' });
await fetch('/auth/logout', { method: 'POST', credentials: 'include' });
}
// ── Pagination helpers ────────────────────────────────
@@ -207,6 +213,98 @@ export async function listAdminLedger(params?: {
return { items: result.entries ?? [], total, page: result.page ?? 1, pageSize, totalPages: Math.ceil(total / pageSize) };
}
// ── WeChat MP ─────────────────────────────────────────
export async function getWechatAdminSummary(): Promise<WechatAdminSummary> {
const summary = await portalFetch<Partial<WechatAdminSummary>>('/admin-api/wechat/summary');
return {
config: {
mpEnabled: summary.config?.mpEnabled ?? false,
scheduleEnabled: summary.config?.scheduleEnabled ?? false,
reminderWorkerEnabled: summary.config?.reminderWorkerEnabled ?? false,
appId: summary.config?.appId ?? null,
publicBaseUrl: summary.config?.publicBaseUrl ?? null,
bindPath: summary.config?.bindPath ?? null,
tokenEndpointConfigured: summary.config?.tokenEndpointConfigured ?? false,
customerServiceEndpointConfigured: summary.config?.customerServiceEndpointConfigured ?? false,
},
counts: {
boundUsers: summary.counts?.boundUsers ?? 0,
routes: {
total: summary.counts?.routes?.total ?? 0,
active: summary.counts?.routes?.active ?? 0,
},
recentMessages: summary.counts?.recentMessages ?? {},
digests: summary.counts?.digests ?? {},
recentDeliveries: summary.counts?.recentDeliveries ?? {},
},
};
}
export async function listWechatBindings(params?: {
search?: string;
limit?: number;
}): Promise<WechatBinding[]> {
const q = new URLSearchParams();
if (params?.search) q.set('search', params.search);
if (params?.limit) q.set('limit', String(params.limit));
const result = await portalFetch<{ bindings: WechatBinding[] }>(
`/admin-api/wechat/bindings${q.toString() ? `?${q}` : ''}`,
);
return result.bindings ?? [];
}
export async function listWechatMessages(params?: {
status?: string;
limit?: number;
}): Promise<WechatMessage[]> {
const q = new URLSearchParams();
if (params?.status) q.set('status', params.status);
if (params?.limit) q.set('limit', String(params.limit));
const result = await portalFetch<{ messages: WechatMessage[] }>(
`/admin-api/wechat/messages${q.toString() ? `?${q}` : ''}`,
);
return result.messages ?? [];
}
export async function listWechatDigests(params?: {
status?: string;
limit?: number;
}): Promise<WechatDigestSubscription[]> {
const q = new URLSearchParams();
if (params?.status) q.set('status', params.status);
if (params?.limit) q.set('limit', String(params.limit));
const result = await portalFetch<{ digests: WechatDigestSubscription[] }>(
`/admin-api/wechat/digests${q.toString() ? `?${q}` : ''}`,
);
return result.digests ?? [];
}
export async function listWechatDeliveries(params?: {
status?: string;
limit?: number;
}): Promise<WechatDeliveryLog[]> {
const q = new URLSearchParams();
if (params?.status) q.set('status', params.status);
if (params?.limit) q.set('limit', String(params.limit));
const result = await portalFetch<{ deliveries: WechatDeliveryLog[] }>(
`/admin-api/wechat/deliveries${q.toString() ? `?${q}` : ''}`,
);
return result.deliveries ?? [];
}
export async function clearWechatRoute(userId: string): Promise<{ ok: boolean; deleted: number }> {
return portalFetch(`/admin-api/wechat/users/${userId}/route/clear`, { method: 'POST' });
}
export async function cancelWechatDigest(id: string): Promise<{ ok: boolean }> {
return portalFetch(`/admin-api/wechat/digests/${id}/cancel`, { method: 'POST' });
}
export async function resumeWechatDigest(id: string): Promise<{ ok: boolean; nextRunAt: number }> {
return portalFetch(`/admin-api/wechat/digests/${id}/resume`, { method: 'POST' });
}
// ── Admin users ───────────────────────────────────────
export async function listAdminUsers(params?: {