feat: add "Reset for Smoke Test" button in admin settings
Truncates sources, curriculum, content, quiz banks/results/cache, topics and relations in dependency order so AI-generated state can be wiped between smoke runs without leaving dangling references. Handbook sync state is cleared by default (otherwise re-sync is a no-op); user progress and leaderboard are opt-in. Team members, settings, and LLM telemetry are preserved. UI lives in Admin → Settings → Danger Zone and requires typing RESET before the button enables. Per-collection deletion counts are reported. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -338,6 +338,56 @@ export async function getRecentLlmCalls(limit = 100) {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Reset for Smoke Testing ──────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Truncate the collections that hold AI-generated and progress data.
|
||||
*
|
||||
* Order matters: rows that reference `topics.id` (relations, content,
|
||||
* quiz_banks) are deleted before topics themselves so we never leave
|
||||
* dangling references mid-wipe.
|
||||
*
|
||||
* `team_members`, `settings`, and `llm_calls` are intentionally preserved.
|
||||
*
|
||||
* @param {{ includeHandbookState?: boolean, includeProgress?: boolean }} [opts]
|
||||
* @returns {Promise<Array<{collection: string, deleted: number, error?: string}>>}
|
||||
*/
|
||||
export async function resetForSmokeTest({
|
||||
includeHandbookState = true,
|
||||
includeProgress = false,
|
||||
} = {}) {
|
||||
const collections = [
|
||||
'relations',
|
||||
'content',
|
||||
'quiz_banks',
|
||||
'quiz_results',
|
||||
'quiz_cache',
|
||||
'curriculum',
|
||||
'sources',
|
||||
'topics',
|
||||
];
|
||||
if (includeHandbookState) collections.push('handbook_sync_state');
|
||||
if (includeProgress) collections.push('learn_progress', 'leaderboard');
|
||||
|
||||
const results = [];
|
||||
for (const name of collections) {
|
||||
try {
|
||||
const rows = await pb.collection(name).getFullList({ fields: 'id', requestKey: null });
|
||||
// Sequential deletes — bulk delete-many isn't in the JS SDK and parallel
|
||||
// deletes on the same collection sometimes 409 in PocketBase.
|
||||
let deleted = 0;
|
||||
for (const r of rows) {
|
||||
await pb.collection(name).delete(r.id, { requestKey: null });
|
||||
deleted++;
|
||||
}
|
||||
results.push({ collection: name, deleted });
|
||||
} catch (err) {
|
||||
results.push({ collection: name, deleted: 0, error: err?.message || String(err) });
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
// ── Handbook Sync State ───────────────────────────────────────────────────────
|
||||
|
||||
export async function getHandbookSyncStates() {
|
||||
|
||||
Reference in New Issue
Block a user