mirror of
https://github.com/joeseesun/wechat-radar.git
synced 2026-09-08 03:18:31 +09:00
90 lines
2.4 KiB
TypeScript
90 lines
2.4 KiB
TypeScript
import { NextRequest } from 'next/server';
|
|
import { wxSessions } from '@/lib/wx';
|
|
import { syncFullHistory } from '@/lib/stats-aggregator';
|
|
import { normalizeDate, normalizeRangeKey, rangeToWindow, type RangeKey } from '@/lib/range';
|
|
import { readConfig } from '@/lib/config';
|
|
import { cache, CK } from '@/lib/cache';
|
|
|
|
export const dynamic = 'force-dynamic';
|
|
export const maxDuration = 1800; // 30 min
|
|
|
|
interface RescanBody {
|
|
range?: RangeKey;
|
|
anchorDate?: string;
|
|
since?: string;
|
|
until?: string;
|
|
full?: boolean; // 一键全量:1 年
|
|
}
|
|
|
|
export async function POST(req: NextRequest) {
|
|
const body = (await req.json().catch(() => ({}))) as RescanBody;
|
|
|
|
let since: string;
|
|
let until: string;
|
|
let scope: string;
|
|
|
|
if (body.full) {
|
|
const w = rangeToWindow('year');
|
|
since = w.since;
|
|
until = w.until;
|
|
scope = 'full(365d)';
|
|
} else if (body.since && body.until) {
|
|
since = body.since;
|
|
until = body.until;
|
|
scope = `custom(${since}~${until})`;
|
|
} else {
|
|
const range = normalizeRangeKey(body.range, 'month');
|
|
const w = rangeToWindow(range, normalizeDate(body.anchorDate));
|
|
since = w.since;
|
|
until = w.until;
|
|
scope = range;
|
|
}
|
|
|
|
const sessions = await wxSessions(500);
|
|
const targets = sessions
|
|
.filter((s) => s.is_group)
|
|
.map((s) => ({ chatroomId: s.username, display: s.chat }));
|
|
|
|
const cfg = readConfig();
|
|
const concurrency = cfg.rescanConcurrency ?? 6;
|
|
|
|
const stream = new ReadableStream({
|
|
async start(controller) {
|
|
const enc = new TextEncoder();
|
|
const send = (obj: unknown) =>
|
|
controller.enqueue(enc.encode(`data: ${JSON.stringify(obj)}\n\n`));
|
|
|
|
send({ type: 'start', scope, since, until, groups: targets.length });
|
|
|
|
try {
|
|
const result = await syncFullHistory({
|
|
targets,
|
|
since,
|
|
until,
|
|
concurrency,
|
|
onProgress: (p) => send(p),
|
|
});
|
|
cache.del(CK.sessions());
|
|
send({
|
|
type: 'finished',
|
|
ok: result.ok,
|
|
failed: result.failed,
|
|
messages: result.messages,
|
|
});
|
|
} catch (e) {
|
|
send({ type: 'error', error: e instanceof Error ? e.message : 'unknown' });
|
|
} finally {
|
|
controller.close();
|
|
}
|
|
},
|
|
});
|
|
|
|
return new Response(stream, {
|
|
headers: {
|
|
'Content-Type': 'text/event-stream',
|
|
'Cache-Control': 'no-cache, no-transform',
|
|
Connection: 'keep-alive',
|
|
},
|
|
});
|
|
}
|