generated from Dicken/dickendock
round29: Scanner laufen jetzt als Hintergrund-Job im Backend (behebt API-Scan-504-Timeout und macht Scan-Status/-Fortschritt geraeteuebergreifend sichtbar)
This commit is contained in:
82
apps/backend/src/scanJobs.ts
Normal file
82
apps/backend/src/scanJobs.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
export type ScanJobType = "devices" | "apis";
|
||||
|
||||
export interface ScanJobProgress {
|
||||
current: number;
|
||||
total: number;
|
||||
currentLabel: string | null;
|
||||
}
|
||||
|
||||
export interface ScanJobStatus {
|
||||
running: boolean;
|
||||
startedAt: string | null;
|
||||
progress: ScanJobProgress | null;
|
||||
lastResult: Record<string, unknown> | null;
|
||||
lastError: string | null;
|
||||
cancelRequested: boolean;
|
||||
}
|
||||
|
||||
const jobs = new Map<ScanJobType, ScanJobStatus>();
|
||||
|
||||
function emptyStatus(): ScanJobStatus {
|
||||
return {
|
||||
running: false,
|
||||
startedAt: null,
|
||||
progress: null,
|
||||
lastResult: null,
|
||||
lastError: null,
|
||||
cancelRequested: false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Zentraler, server-seitiger Status je Scanner-Typ ("devices" = "Alle
|
||||
* Geräte scannen", "apis" = API-Scanner). Bewusst NICHT in der Datenbank,
|
||||
* nur im Arbeitsspeicher des Backend-Prozesses - der Zustand "ein Scan läuft
|
||||
* gerade" muss keinen Neustart überleben, aber er MUSS geräteübergreifend
|
||||
* sichtbar sein (jeder Browser/jedes Gerät, das den Adminbereich öffnet,
|
||||
* soll denselben laufenden Scan sehen). Ein rein browserseitiger Zustand
|
||||
* (sessionStorage) kann das prinzipiell nicht leisten.
|
||||
*/
|
||||
export function getJobStatus(type: ScanJobType): ScanJobStatus {
|
||||
return jobs.get(type) ?? emptyStatus();
|
||||
}
|
||||
|
||||
export function isJobRunning(type: ScanJobType): boolean {
|
||||
return getJobStatus(type).running;
|
||||
}
|
||||
|
||||
export function startJob(type: ScanJobType, total: number): void {
|
||||
jobs.set(type, {
|
||||
running: true,
|
||||
startedAt: new Date().toISOString(),
|
||||
progress: { current: 0, total, currentLabel: null },
|
||||
lastResult: null,
|
||||
lastError: null,
|
||||
cancelRequested: false,
|
||||
});
|
||||
}
|
||||
|
||||
export function updateJobProgress(type: ScanJobType, current: number, currentLabel: string | null): void {
|
||||
const job = jobs.get(type);
|
||||
if (!job) return;
|
||||
job.progress = { current, total: job.progress?.total ?? current, currentLabel };
|
||||
}
|
||||
|
||||
export function isCancelRequested(type: ScanJobType): boolean {
|
||||
return jobs.get(type)?.cancelRequested ?? false;
|
||||
}
|
||||
|
||||
export function requestCancel(type: ScanJobType): void {
|
||||
const job = jobs.get(type);
|
||||
if (job) job.cancelRequested = true;
|
||||
}
|
||||
|
||||
export function finishJob(type: ScanJobType, result: Record<string, unknown>): void {
|
||||
const job = jobs.get(type) ?? emptyStatus();
|
||||
jobs.set(type, { ...job, running: false, lastResult: result, cancelRequested: false });
|
||||
}
|
||||
|
||||
export function failJob(type: ScanJobType, error: string): void {
|
||||
const job = jobs.get(type) ?? emptyStatus();
|
||||
jobs.set(type, { ...job, running: false, lastError: error, cancelRequested: false });
|
||||
}
|
||||
Reference in New Issue
Block a user