Files
dashboard/backend/src/services/cleanup.service.ts

259 lines
12 KiB
TypeScript

import pool from '../config/database';
import logger from '../utils/logger';
export interface CleanupResult {
count: number;
deleted: boolean;
}
class CleanupService {
async cleanupNotifications(olderThanDays: number, confirm: boolean): Promise<CleanupResult> {
const cutoff = `${olderThanDays} days`;
if (!confirm) {
const { rows } = await pool.query(
`SELECT COUNT(*)::int AS count FROM benachrichtigungen WHERE erstellt_am < NOW() - $1::interval`,
[cutoff]
);
return { count: rows[0].count, deleted: false };
}
const { rowCount } = await pool.query(
`DELETE FROM benachrichtigungen WHERE erstellt_am < NOW() - $1::interval`,
[cutoff]
);
logger.info(`Cleanup: deleted ${rowCount} notifications older than ${olderThanDays} days`);
return { count: rowCount ?? 0, deleted: true };
}
async cleanupAuditLog(olderThanDays: number, confirm: boolean): Promise<CleanupResult> {
const cutoff = `${olderThanDays} days`;
if (!confirm) {
const { rows } = await pool.query(
`SELECT COUNT(*)::int AS count FROM audit_log WHERE created_at < NOW() - $1::interval`,
[cutoff]
);
return { count: rows[0].count, deleted: false };
}
const { rowCount } = await pool.query(
`DELETE FROM audit_log WHERE created_at < NOW() - $1::interval`,
[cutoff]
);
logger.info(`Cleanup: deleted ${rowCount} audit_log entries older than ${olderThanDays} days`);
return { count: rowCount ?? 0, deleted: true };
}
async cleanupEvents(olderThanDays: number, confirm: boolean): Promise<CleanupResult> {
const cutoff = `${olderThanDays} days`;
if (!confirm) {
const { rows } = await pool.query(
`SELECT COUNT(*)::int AS count FROM events WHERE end_date < NOW() - $1::interval`,
[cutoff]
);
return { count: rows[0].count, deleted: false };
}
const { rowCount } = await pool.query(
`DELETE FROM events WHERE end_date < NOW() - $1::interval`,
[cutoff]
);
logger.info(`Cleanup: deleted ${rowCount} events older than ${olderThanDays} days`);
return { count: rowCount ?? 0, deleted: true };
}
async cleanupBookings(olderThanDays: number, confirm: boolean): Promise<CleanupResult> {
const cutoff = `${olderThanDays} days`;
if (!confirm) {
const { rows } = await pool.query(
`SELECT COUNT(*)::int AS count FROM fahrzeug_buchungen WHERE end_date < NOW() - $1::interval AND status IN ('completed', 'cancelled')`,
[cutoff]
);
return { count: rows[0].count, deleted: false };
}
const { rowCount } = await pool.query(
`DELETE FROM fahrzeug_buchungen WHERE end_date < NOW() - $1::interval AND status IN ('completed', 'cancelled')`,
[cutoff]
);
logger.info(`Cleanup: deleted ${rowCount} bookings older than ${olderThanDays} days`);
return { count: rowCount ?? 0, deleted: true };
}
async cleanupOrders(olderThanDays: number, confirm: boolean): Promise<CleanupResult> {
const cutoff = `${olderThanDays} days`;
if (!confirm) {
const { rows } = await pool.query(
`SELECT COUNT(*)::int AS count FROM bestellungen WHERE updated_at < NOW() - $1::interval AND status = 'abgeschlossen'`,
[cutoff]
);
return { count: rows[0].count, deleted: false };
}
const { rowCount } = await pool.query(
`DELETE FROM bestellungen WHERE updated_at < NOW() - $1::interval AND status = 'abgeschlossen'`,
[cutoff]
);
logger.info(`Cleanup: deleted ${rowCount} orders older than ${olderThanDays} days`);
return { count: rowCount ?? 0, deleted: true };
}
async cleanupVehicleHistory(olderThanDays: number, confirm: boolean): Promise<CleanupResult> {
const cutoff = `${olderThanDays} days`;
if (!confirm) {
const { rows } = await pool.query(
`SELECT COUNT(*)::int AS count FROM fahrzeug_wartungslog WHERE datum < NOW() - $1::interval`,
[cutoff]
);
return { count: rows[0].count, deleted: false };
}
const { rowCount } = await pool.query(
`DELETE FROM fahrzeug_wartungslog WHERE datum < NOW() - $1::interval`,
[cutoff]
);
logger.info(`Cleanup: deleted ${rowCount} vehicle history entries older than ${olderThanDays} days`);
return { count: rowCount ?? 0, deleted: true };
}
async cleanupEquipmentHistory(olderThanDays: number, confirm: boolean): Promise<CleanupResult> {
const cutoff = `${olderThanDays} days`;
if (!confirm) {
const { rows } = await pool.query(
`SELECT COUNT(*)::int AS count FROM ausruestung_wartungslog WHERE datum < NOW() - $1::interval`,
[cutoff]
);
return { count: rows[0].count, deleted: false };
}
const { rowCount } = await pool.query(
`DELETE FROM ausruestung_wartungslog WHERE datum < NOW() - $1::interval`,
[cutoff]
);
logger.info(`Cleanup: deleted ${rowCount} equipment history entries older than ${olderThanDays} days`);
return { count: rowCount ?? 0, deleted: true };
}
async resetBestellungenSequence(confirm: boolean): Promise<CleanupResult> {
if (!confirm) {
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM bestellungen');
return { count: rows[0].count, deleted: false };
}
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM bestellungen');
const count = rows[0].count;
// Delete related linking tables first, then main table
await pool.query('DELETE FROM ausruestung_anfrage_bestellung WHERE bestellung_id IN (SELECT id FROM bestellungen)');
await pool.query('TRUNCATE bestellungen CASCADE');
try { await pool.query('ALTER SEQUENCE bestellungen_id_seq RESTART WITH 1'); } catch { /* sequence may not exist */ }
logger.info(`Cleanup: truncated bestellungen (${count} rows) and reset sequence`);
return { count, deleted: true };
}
async resetAusruestungAnfragenSequence(confirm: boolean): Promise<CleanupResult> {
if (!confirm) {
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM ausruestung_anfragen');
return { count: rows[0].count, deleted: false };
}
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM ausruestung_anfragen');
const count = rows[0].count;
// Delete linking table first
await pool.query('DELETE FROM ausruestung_anfrage_bestellung WHERE anfrage_id IN (SELECT id FROM ausruestung_anfragen)');
await pool.query('TRUNCATE ausruestung_anfragen CASCADE');
try { await pool.query('ALTER SEQUENCE ausruestung_anfragen_id_seq RESTART WITH 1'); } catch { /* sequence may not exist */ }
logger.info(`Cleanup: truncated ausruestung_anfragen (${count} rows) and reset sequence`);
return { count, deleted: true };
}
async cleanupChecklistHistory(olderThanDays: number, confirm: boolean): Promise<CleanupResult> {
const cutoff = `${olderThanDays} days`;
if (!confirm) {
const { rows } = await pool.query(
`SELECT COUNT(*)::int AS count FROM checklist_ausfuehrungen WHERE ausgefuehrt_am < NOW() - $1::interval`,
[cutoff]
);
return { count: rows[0].count, deleted: false };
}
const { rowCount } = await pool.query(
`DELETE FROM checklist_ausfuehrungen WHERE ausgefuehrt_am < NOW() - $1::interval`,
[cutoff]
);
logger.info(`Cleanup: deleted ${rowCount} checklist executions older than ${olderThanDays} days`);
return { count: rowCount ?? 0, deleted: true };
}
async resetChecklistHistory(confirm: boolean): Promise<CleanupResult> {
if (!confirm) {
const { rows } = await pool.query(`SELECT COUNT(*)::int AS count FROM checklist_ausfuehrungen`);
return { count: rows[0].count, deleted: false };
}
await pool.query(`TRUNCATE checklist_ausfuehrungen CASCADE`);
await pool.query(`TRUNCATE checklist_faelligkeit CASCADE`);
logger.info('Cleanup: reset all checklist history');
return { count: 0, deleted: true };
}
async resetIssuesSequence(confirm: boolean): Promise<CleanupResult> {
if (!confirm) {
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM issues');
return { count: rows[0].count, deleted: false };
}
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM issues');
const count = rows[0].count;
await pool.query('TRUNCATE issues CASCADE');
try { await pool.query('ALTER SEQUENCE issues_id_seq RESTART WITH 1'); } catch { /* sequence may not exist */ }
logger.info(`Cleanup: truncated issues (${count} rows) and reset sequence`);
return { count, deleted: true };
}
async resetBuchhaltungTransaktionen(confirm: boolean): Promise<CleanupResult> {
if (!confirm) {
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM buchhaltung_transaktionen');
return { count: rows[0].count, deleted: false };
}
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM buchhaltung_transaktionen');
const count = rows[0].count;
await pool.query('TRUNCATE buchhaltung_transaktionen CASCADE');
try { await pool.query('ALTER SEQUENCE buchhaltung_transaktionen_id_seq RESTART WITH 1'); } catch { /* sequence may not exist */ }
logger.info(`Cleanup: truncated buchhaltung_transaktionen (${count} rows) and reset sequence`);
return { count, deleted: true };
}
async resetBuchhaltungKonten(confirm: boolean): Promise<CleanupResult> {
if (!confirm) {
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM buchhaltung_konten');
return { count: rows[0].count, deleted: false };
}
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM buchhaltung_konten');
const count = rows[0].count;
await pool.query('TRUNCATE buchhaltung_transaktionen CASCADE');
await pool.query('TRUNCATE buchhaltung_konten CASCADE');
try { await pool.query('ALTER SEQUENCE buchhaltung_konten_id_seq RESTART WITH 1'); } catch { /* sequence may not exist */ }
try { await pool.query('ALTER SEQUENCE buchhaltung_transaktionen_id_seq RESTART WITH 1'); } catch { /* sequence may not exist */ }
logger.info(`Cleanup: truncated buchhaltung_konten (${count} rows) and reset sequence`);
return { count, deleted: true };
}
async resetBuchhaltungBankkonten(confirm: boolean): Promise<CleanupResult> {
if (!confirm) {
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM buchhaltung_bankkonten');
return { count: rows[0].count, deleted: false };
}
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM buchhaltung_bankkonten');
const count = rows[0].count;
await pool.query('TRUNCATE buchhaltung_bankkonten CASCADE');
try { await pool.query('ALTER SEQUENCE buchhaltung_bankkonten_id_seq RESTART WITH 1'); } catch { /* sequence may not exist */ }
logger.info(`Cleanup: truncated buchhaltung_bankkonten (${count} rows) and reset sequence`);
return { count, deleted: true };
}
async resetPersoenlicheAusruestung(confirm: boolean): Promise<CleanupResult> {
if (!confirm) {
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM persoenliche_ausruestung WHERE geloescht_am IS NULL');
return { count: rows[0].count, deleted: false };
}
const { rows } = await pool.query('SELECT COUNT(*)::int AS count FROM persoenliche_ausruestung');
const count = rows[0].count;
// Clear FK references from request positions before truncating
await pool.query(`UPDATE ausruestung_anfrage_positionen SET zuweisung_persoenlich_id = NULL, zuweisung_typ = 'keine' WHERE zuweisung_persoenlich_id IS NOT NULL`);
// CASCADE removes persoenliche_ausruestung_eigenschaften rows automatically
await pool.query('TRUNCATE persoenliche_ausruestung CASCADE');
logger.info(`Cleanup: truncated persoenliche_ausruestung (${count} rows)`);
return { count, deleted: true };
}
}
export default new CleanupService();