generated from Dicken/dickendock
103 lines
3.2 KiB
TypeScript
103 lines
3.2 KiB
TypeScript
export type ScanJobType = "devices" | "apis" | "fritzbox";
|
|
|
|
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 });
|
|
}
|
|
|
|
/**
|
|
* Sperrt ein einzelnes Gerät gegen ÜBERLAPPENDE Scans - unabhängig davon, ob
|
|
* der Scan über den Einzelgerät-Endpunkt oder als Teil des Sammel-Scans
|
|
* ("Alle Geräte scannen") läuft. Ohne das könnten zwei gleichzeitige Scans
|
|
* desselben Geräts beide unabhängig voneinander denselben neuen Dienst
|
|
* anlegen wollen (siehe UNIQUE-Index in client.ts als zusätzliches
|
|
* Sicherheitsnetz auf Datenbankebene).
|
|
*/
|
|
const devicesCurrentlyScanning = new Set<string>();
|
|
|
|
export function tryLockDevice(deviceId: string): boolean {
|
|
if (devicesCurrentlyScanning.has(deviceId)) return false;
|
|
devicesCurrentlyScanning.add(deviceId);
|
|
return true;
|
|
}
|
|
|
|
export function unlockDevice(deviceId: string): void {
|
|
devicesCurrentlyScanning.delete(deviceId);
|
|
}
|