AI Implementation feature(871): Admin Area System: Database Backup/Restore and Danger Zone (#61)

This commit was merged in pull request #61.
This commit is contained in:
2026-07-23 12:10:41 +00:00
parent 6bac67fad7
commit 470ddd30c3
42 changed files with 3996 additions and 55 deletions
@@ -1,4 +1,4 @@
import { DestroyRef, Injectable, computed, inject, signal } from '@angular/core';
import { DestroyRef, Injectable, computed, effect, inject, signal } from '@angular/core';
import { ScoreboardApiService } from './scoreboard.service';
import {
EventLogRow,
@@ -15,11 +15,13 @@ import {
parseSolveEventIntoRanking,
} from './scoreboard.pure';
import { EventSourceLike } from '../../core/services/event-status.pure';
import { SystemDataChangeService } from '../../core/services/system-data-change.service';
@Injectable({ providedIn: 'root' })
export class ScoreboardStore {
private readonly api: ScoreboardApiService;
private readonly destroyRef: DestroyRef;
private readonly dataChanges: SystemDataChangeService | null;
private readonly _ranking = signal<RankingRow[] | null>(null);
private readonly _matrix = signal<MatrixView | null>(null);
@@ -35,10 +37,26 @@ export class ScoreboardStore {
private createSource: (() => EventSourceLike) | null = null;
private loadAllInFlight: Promise<void> | null = null;
constructor(api?: ScoreboardApiService, destroyRef?: DestroyRef) {
constructor(api?: ScoreboardApiService, destroyRef?: DestroyRef, dataChanges?: SystemDataChangeService) {
this.api = api ?? inject(ScoreboardApiService);
this.destroyRef = destroyRef ?? inject(DestroyRef);
this.dataChanges = dataChanges ?? null;
this.destroyRef.onDestroy(() => this.stop());
if (!this.dataChanges) return;
const lastSeen = { ts: 0 };
const dc = this.dataChanges;
effect(() => {
const evts = dc.events();
const latest = evts[evts.length - 1];
if (!latest || latest.ts === lastSeen.ts) return;
lastSeen.ts = latest.ts;
if (latest.kind === 'scores-reset' || latest.kind === 'restore-completed' || latest.kind === 'challenges-wiped') {
this._ranking.set(null);
this._matrix.set(null);
this._eventLog.set(null);
this._graph.set(null);
}
});
}
readonly ranking = this._ranking.asReadonly();