diff --git a/handlers/admin-get/cleanup.d.ts b/handlers/admin-get/cleanup.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/admin-get/cleanup.d.ts @@ -0,0 +1,3 @@ +import type { RequestHandler } from "express"; +export declare const handler: RequestHandler; +export default handler; diff --git a/handlers/admin-get/cleanup.js b/handlers/admin-get/cleanup.js new file mode 100644 index 00000000..48071123 --- /dev/null +++ b/handlers/admin-get/cleanup.js @@ -0,0 +1,6 @@ +export const handler = (_request, response) => { + response.render("admin-cleanup", { + headTitle: "Database Cleanup" + }); +}; +export default handler; diff --git a/handlers/admin-get/cleanup.ts b/handlers/admin-get/cleanup.ts new file mode 100644 index 00000000..fc4a8e86 --- /dev/null +++ b/handlers/admin-get/cleanup.ts @@ -0,0 +1,10 @@ +import type { RequestHandler } from "express"; + +export const handler: RequestHandler = (_request, response) => { + + response.render("admin-cleanup", { + headTitle: "Database Cleanup" + }); +}; + +export default handler; diff --git a/handlers/admin-post/doCleanupDatabase.d.ts b/handlers/admin-post/doCleanupDatabase.d.ts new file mode 100644 index 00000000..9621c611 --- /dev/null +++ b/handlers/admin-post/doCleanupDatabase.d.ts @@ -0,0 +1,3 @@ +import type { RequestHandler } from "express"; +export declare const handler: RequestHandler; +export default handler; diff --git a/handlers/admin-post/doCleanupDatabase.js b/handlers/admin-post/doCleanupDatabase.js new file mode 100644 index 00000000..06590ea4 --- /dev/null +++ b/handlers/admin-post/doCleanupDatabase.js @@ -0,0 +1,10 @@ +import { cleanupDatabase } from "../../helpers/lotOccupancyDB/cleanupDatabase.js"; +export const handler = async (request, response) => { + const recordCounts = cleanupDatabase(request.session); + response.json({ + success: true, + inactivedRecordCount: recordCounts.inactivedRecordCount, + purgedRecordCount: recordCounts.purgedRecordCount + }); +}; +export default handler; diff --git a/handlers/admin-post/doCleanupDatabase.ts b/handlers/admin-post/doCleanupDatabase.ts new file mode 100644 index 00000000..c0519a00 --- /dev/null +++ b/handlers/admin-post/doCleanupDatabase.ts @@ -0,0 +1,15 @@ +import type { RequestHandler } from "express"; + +import { cleanupDatabase } from "../../helpers/lotOccupancyDB/cleanupDatabase.js"; + +export const handler: RequestHandler = async (request, response) => { + const recordCounts = cleanupDatabase(request.session); + + response.json({ + success: true, + inactivedRecordCount: recordCounts.inactivedRecordCount, + purgedRecordCount: recordCounts.purgedRecordCount + }); +}; + +export default handler; diff --git a/helpers/functions.config.d.ts b/helpers/functions.config.d.ts index b1ddd020..8a9a7bfc 100644 --- a/helpers/functions.config.d.ts +++ b/helpers/functions.config.d.ts @@ -36,4 +36,5 @@ export declare function getProperty(propertyName: "settings.workOrders.workOrder export declare function getProperty(propertyName: "settings.workOrders.workOrderMilestoneDateRecentBeforeDays"): number; export declare function getProperty(propertyName: "settings.workOrders.workOrderMilestoneDateRecentAfterDays"): number; export declare function getProperty(propertyName: "settings.workOrders.calendarEmailAddress"): string; +export declare function getProperty(propertyName: "settings.adminCleanup.recordDeleteAgeDays"): number; export declare const keepAliveMillis: number; diff --git a/helpers/functions.config.js b/helpers/functions.config.js index e429a954..85f48db9 100644 --- a/helpers/functions.config.js +++ b/helpers/functions.config.js @@ -35,6 +35,7 @@ configFallbackValues.set("settings.workOrders.workOrderNumberLength", 6); configFallbackValues.set("settings.workOrders.workOrderMilestoneDateRecentBeforeDays", 5); configFallbackValues.set("settings.workOrders.workOrderMilestoneDateRecentAfterDays", 60); configFallbackValues.set("settings.workOrders.calendarEmailAddress", "no-reply@127.0.0.1"); +configFallbackValues.set("settings.adminCleanup.recordDeleteAgeDays", 60); export function getProperty(propertyName) { const propertyNameSplit = propertyName.split("."); let currentObject = config; diff --git a/helpers/functions.config.ts b/helpers/functions.config.ts index cc2d1e53..6adba478 100644 --- a/helpers/functions.config.ts +++ b/helpers/functions.config.ts @@ -55,6 +55,8 @@ configFallbackValues.set("settings.workOrders.workOrderMilestoneDateRecentBefore configFallbackValues.set("settings.workOrders.workOrderMilestoneDateRecentAfterDays", 60); configFallbackValues.set("settings.workOrders.calendarEmailAddress", "no-reply@127.0.0.1"); +configFallbackValues.set("settings.adminCleanup.recordDeleteAgeDays", 60); + /* * Set up function overloads */ @@ -125,6 +127,8 @@ export function getProperty( export function getProperty(propertyName: "settings.workOrders.calendarEmailAddress"): string; +export function getProperty(propertyName: "settings.adminCleanup.recordDeleteAgeDays"): number; + export function getProperty(propertyName: string): unknown { const propertyNameSplit = propertyName.split("."); diff --git a/helpers/lotOccupancyDB/cleanupDatabase.d.ts b/helpers/lotOccupancyDB/cleanupDatabase.d.ts new file mode 100644 index 00000000..e669edaa --- /dev/null +++ b/helpers/lotOccupancyDB/cleanupDatabase.d.ts @@ -0,0 +1,6 @@ +import type * as recordTypes from "../../types/recordTypes"; +export declare const cleanupDatabase: (requestSession: recordTypes.PartialSession) => { + inactivedRecordCount: number; + purgedRecordCount: number; +}; +export default cleanupDatabase; diff --git a/helpers/lotOccupancyDB/cleanupDatabase.js b/helpers/lotOccupancyDB/cleanupDatabase.js new file mode 100644 index 00000000..0414d394 --- /dev/null +++ b/helpers/lotOccupancyDB/cleanupDatabase.js @@ -0,0 +1,203 @@ +import sqlite from "better-sqlite3"; +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; +import * as configFunctions from "../functions.config.js"; +export const cleanupDatabase = (requestSession) => { + const database = sqlite(databasePath); + const rightNowMillis = Date.now(); + const recordDelete_timeMillisMin = rightNowMillis - + configFunctions.getProperty("settings.adminCleanup.recordDeleteAgeDays") * 86400 * 1000; + let inactivedRecordCount = 0; + let purgedRecordCount = 0; + inactivedRecordCount += database + .prepare("update WorkOrderComments" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and workOrderId in (select workOrderId from WorkOrders where recordDelete_timeMillis is not null)") + .run(requestSession.user.userName, rightNowMillis).changes; + purgedRecordCount += database + .prepare("delete from WorkOrderComments where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + inactivedRecordCount += database + .prepare("update WorkOrderLotOccupancies" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and workOrderId in (select workOrderId from WorkOrders where recordDelete_timeMillis is not null)") + .run(requestSession.user.userName, rightNowMillis).changes; + purgedRecordCount += database + .prepare("delete from WorkOrderLotOccupancies where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + inactivedRecordCount += database + .prepare("update WorkOrderLots" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and workOrderId in (select workOrderId from WorkOrders where recordDelete_timeMillis is not null)") + .run(requestSession.user.userName, rightNowMillis).changes; + purgedRecordCount += database + .prepare("delete from WorkOrderLots where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + inactivedRecordCount += database + .prepare("update WorkOrderMilestones" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and workOrderId in (select workOrderId from WorkOrders where recordDelete_timeMillis is not null)") + .run(requestSession.user.userName, rightNowMillis).changes; + purgedRecordCount += database + .prepare("delete from WorkOrderMilestones where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + purgedRecordCount += database + .prepare("delete from WorkOrders where recordDelete_timeMillis <= ?" + + " and workOrderId not in (select workOrderId from WorkOrderComments)" + + " and workOrderId not in (select workOrderId from WorkOrderLotOccupancies)" + + " and workOrderId not in (select workOrderId from WorkOrderLots)" + + " and workOrderId not in (select workOrderId from WorkOrderMilestones)") + .run(recordDelete_timeMillisMin).changes; + purgedRecordCount += database + .prepare("delete from WorkOrderMilestoneTypes where recordDelete_timeMillis <= ?" + + " and workOrderMilestoneTypeId not in (select workOrderMilestoneTypeId from WorkOrderMilestones)") + .run(recordDelete_timeMillisMin).changes; + purgedRecordCount += database + .prepare("delete from WorkOrderTypes where recordDelete_timeMillis <= ?" + + " and workOrderTypeId not in (select workOrderTypeId from WorkOrders)") + .run(recordDelete_timeMillisMin).changes; + inactivedRecordCount += database + .prepare("update LotOccupancyComments" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and lotOccupancyId in (select lotOccupancyId from LotOccupancies where recordDelete_timeMillis is not null)") + .run(requestSession.user.userName, rightNowMillis).changes; + purgedRecordCount += database + .prepare("delete from LotOccupancyComments where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + inactivedRecordCount += database + .prepare("update LotOccupancyFields" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and lotOccupancyId in (select lotOccupancyId from LotOccupancies where recordDelete_timeMillis is not null)") + .run(requestSession.user.userName, rightNowMillis).changes; + purgedRecordCount += database + .prepare("delete from LotOccupancyFields where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + inactivedRecordCount += database + .prepare("update LotOccupancyOccupants" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and lotOccupancyId in (select lotOccupancyId from LotOccupancies where recordDelete_timeMillis is not null)") + .run(requestSession.user.userName, rightNowMillis).changes; + purgedRecordCount += database + .prepare("delete from LotOccupancyOccupants where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + purgedRecordCount += database + .prepare("delete from LotOccupancyFees where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + purgedRecordCount += database + .prepare("delete from LotOccupancyTransactions where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + purgedRecordCount += database + .prepare("delete from LotOccupancies where recordDelete_timeMillis <= ?" + + " and lotOccupancyId not in (select lotOccupancyId from LotOccupancyComments)" + + " and lotOccupancyId not in (select lotOccupancyId from LotOccupancyFees)" + + " and lotOccupancyId not in (select lotOccupancyId from LotOccupancyFields)" + + " and lotOccupancyId not in (select lotOccupancyId from LotOccupancyOccupants)" + + " and lotOccupancyId not in (select lotOccupancyId from LotOccupancyTransactions)") + .run(recordDelete_timeMillisMin).changes; + inactivedRecordCount += database + .prepare("update Fees" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and feeCategoryId in (select feeCategoryId from FeeCategories where recordDelete_timeMillis is not null)") + .run(requestSession.user.userName, rightNowMillis).changes; + purgedRecordCount += database + .prepare("delete from Fees where recordDelete_timeMillis <= ?" + + " and feeId not in (select feeId from LotOccupancyFees)") + .run(recordDelete_timeMillisMin).changes; + purgedRecordCount += database + .prepare("delete from FeeCategories where recordDelete_timeMillis <= ?" + + " and feeCategoryId not in (select feeCategoryId from Fees)") + .run(recordDelete_timeMillisMin).changes; + inactivedRecordCount += database + .prepare("update OccupancyTypeFields" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and occupancyTypeId in (select occupancyTypeId from OccupancyTypes where recordDelete_timeMillis is not null)") + .run(requestSession.user.userName, rightNowMillis).changes; + purgedRecordCount += database + .prepare("delete from OccupancyTypeFields where recordDelete_timeMillis <= ?" + + " and occupancyTypeFieldId not in (select occupancyTypeFieldId from LotOccupancyFields)") + .run(recordDelete_timeMillisMin).changes; + purgedRecordCount += database + .prepare("delete from OccupancyTypes where recordDelete_timeMillis <= ?" + + " and occupancyTypeId not in (select occupancyTypeId from LotOccupancies)" + + " and occupancyTypeId not in (select occupancyTypeId from Fees)") + .run(recordDelete_timeMillisMin).changes; + purgedRecordCount += database + .prepare("delete from LotOccupantTypes where recordDelete_timeMillis <= ?" + + " and lotOccupantTypeId not in (select lotOccupantTypeId from LotOccupancyOccupants)") + .run(recordDelete_timeMillisMin).changes; + inactivedRecordCount += database + .prepare("update LotComments" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and lotId in (select lotId from Lots where recordDelete_timeMillis is not null)") + .run(requestSession.user.userName, rightNowMillis).changes; + purgedRecordCount += database + .prepare("delete from LotComments where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + inactivedRecordCount += database + .prepare("update LotFields" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and lotId in (select lotId from Lots where recordDelete_timeMillis is not null)") + .run(requestSession.user.userName, rightNowMillis).changes; + purgedRecordCount += database + .prepare("delete from LotFields where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + inactivedRecordCount += database + .prepare("update Lots" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and mapId in (select mapId from Maps where recordDelete_timeMillis is not null)") + .run(requestSession.user.userName, rightNowMillis).changes; + purgedRecordCount += database + .prepare("delete from Lots where recordDelete_timeMillis <= ?" + + " and lotId not in (select lotId from LotComments)" + + " and lotId not in (select lotId from LotFields)" + + " and lotId not in (select lotId from LotOccupancies)") + .run(recordDelete_timeMillisMin).changes; + purgedRecordCount += database + .prepare("delete from LotStatuses where recordDelete_timeMillis <= ?" + + " and lotStatusId not in (select lotStatusId from Lots)") + .run(recordDelete_timeMillisMin).changes; + inactivedRecordCount += database + .prepare("update LotTypeFields" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and lotTypeId in (select lotTypeId from LotTypes where recordDelete_timeMillis is not null)") + .run(requestSession.user.userName, rightNowMillis).changes; + purgedRecordCount += database + .prepare("delete from LotTypeFields where recordDelete_timeMillis <= ?" + + " and lotTypeFieldId not in (select lotTypeFieldId from LotFields)") + .run(recordDelete_timeMillisMin).changes; + purgedRecordCount += database + .prepare("delete from LotTypes where recordDelete_timeMillis <= ?" + + " and lotTypeId not in (select lotTypeId from Lots)") + .run(recordDelete_timeMillisMin).changes; + database.close(); + return { + inactivedRecordCount, + purgedRecordCount + }; +}; +export default cleanupDatabase; diff --git a/helpers/lotOccupancyDB/cleanupDatabase.ts b/helpers/lotOccupancyDB/cleanupDatabase.ts new file mode 100644 index 00000000..d934bddf --- /dev/null +++ b/helpers/lotOccupancyDB/cleanupDatabase.ts @@ -0,0 +1,350 @@ +import sqlite from "better-sqlite3"; + +import { lotOccupancyDB as databasePath } from "../../data/databasePaths.js"; + +import * as configFunctions from "../functions.config.js"; + +import type * as recordTypes from "../../types/recordTypes"; + +export const cleanupDatabase = (requestSession: recordTypes.PartialSession) => { + const database = sqlite(databasePath); + + const rightNowMillis = Date.now(); + const recordDelete_timeMillisMin = + rightNowMillis - + configFunctions.getProperty("settings.adminCleanup.recordDeleteAgeDays") * 86_400 * 1000; + + let inactivedRecordCount = 0; + let purgedRecordCount = 0; + + // Work Order Comments + + inactivedRecordCount += database + .prepare( + "update WorkOrderComments" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and workOrderId in (select workOrderId from WorkOrders where recordDelete_timeMillis is not null)" + ) + .run(requestSession.user.userName, rightNowMillis).changes; + + purgedRecordCount += database + .prepare("delete from WorkOrderComments where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + + // Work Order Lot Occupancies + + inactivedRecordCount += database + .prepare( + "update WorkOrderLotOccupancies" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and workOrderId in (select workOrderId from WorkOrders where recordDelete_timeMillis is not null)" + ) + .run(requestSession.user.userName, rightNowMillis).changes; + + purgedRecordCount += database + .prepare("delete from WorkOrderLotOccupancies where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + + // Work Order Lots + + inactivedRecordCount += database + .prepare( + "update WorkOrderLots" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and workOrderId in (select workOrderId from WorkOrders where recordDelete_timeMillis is not null)" + ) + .run(requestSession.user.userName, rightNowMillis).changes; + + purgedRecordCount += database + .prepare("delete from WorkOrderLots where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + + // Work Order Milestones + + inactivedRecordCount += database + .prepare( + "update WorkOrderMilestones" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and workOrderId in (select workOrderId from WorkOrders where recordDelete_timeMillis is not null)" + ) + .run(requestSession.user.userName, rightNowMillis).changes; + + purgedRecordCount += database + .prepare("delete from WorkOrderMilestones where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + + // Work Orders + + purgedRecordCount += database + .prepare( + "delete from WorkOrders where recordDelete_timeMillis <= ?" + + " and workOrderId not in (select workOrderId from WorkOrderComments)" + + " and workOrderId not in (select workOrderId from WorkOrderLotOccupancies)" + + " and workOrderId not in (select workOrderId from WorkOrderLots)" + + " and workOrderId not in (select workOrderId from WorkOrderMilestones)" + ) + .run(recordDelete_timeMillisMin).changes; + + // Work Order Milestone Types + + purgedRecordCount += database + .prepare( + "delete from WorkOrderMilestoneTypes where recordDelete_timeMillis <= ?" + + " and workOrderMilestoneTypeId not in (select workOrderMilestoneTypeId from WorkOrderMilestones)" + ) + .run(recordDelete_timeMillisMin).changes; + + // Work Order Types + + purgedRecordCount += database + .prepare( + "delete from WorkOrderTypes where recordDelete_timeMillis <= ?" + + " and workOrderTypeId not in (select workOrderTypeId from WorkOrders)" + ) + .run(recordDelete_timeMillisMin).changes; + + // Lot Occupancy Comments + + inactivedRecordCount += database + .prepare( + "update LotOccupancyComments" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and lotOccupancyId in (select lotOccupancyId from LotOccupancies where recordDelete_timeMillis is not null)" + ) + .run(requestSession.user.userName, rightNowMillis).changes; + + purgedRecordCount += database + .prepare("delete from LotOccupancyComments where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + + // Lot Occupancy Fields + + inactivedRecordCount += database + .prepare( + "update LotOccupancyFields" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and lotOccupancyId in (select lotOccupancyId from LotOccupancies where recordDelete_timeMillis is not null)" + ) + .run(requestSession.user.userName, rightNowMillis).changes; + + purgedRecordCount += database + .prepare("delete from LotOccupancyFields where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + + // Lot Occupancy Occupants + + inactivedRecordCount += database + .prepare( + "update LotOccupancyOccupants" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and lotOccupancyId in (select lotOccupancyId from LotOccupancies where recordDelete_timeMillis is not null)" + ) + .run(requestSession.user.userName, rightNowMillis).changes; + + purgedRecordCount += database + .prepare("delete from LotOccupancyOccupants where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + + // Lot Occupancy Fees/Transactions + // - Maintain financials, do not delete related. + + purgedRecordCount += database + .prepare("delete from LotOccupancyFees where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + + purgedRecordCount += database + .prepare("delete from LotOccupancyTransactions where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + + // Lot Occupancies + + purgedRecordCount += database + .prepare( + "delete from LotOccupancies where recordDelete_timeMillis <= ?" + + " and lotOccupancyId not in (select lotOccupancyId from LotOccupancyComments)" + + " and lotOccupancyId not in (select lotOccupancyId from LotOccupancyFees)" + + " and lotOccupancyId not in (select lotOccupancyId from LotOccupancyFields)" + + " and lotOccupancyId not in (select lotOccupancyId from LotOccupancyOccupants)" + + " and lotOccupancyId not in (select lotOccupancyId from LotOccupancyTransactions)" + ) + .run(recordDelete_timeMillisMin).changes; + + // Fees + + inactivedRecordCount += database + .prepare( + "update Fees" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and feeCategoryId in (select feeCategoryId from FeeCategories where recordDelete_timeMillis is not null)" + ) + .run(requestSession.user.userName, rightNowMillis).changes; + + purgedRecordCount += database + .prepare( + "delete from Fees where recordDelete_timeMillis <= ?" + + " and feeId not in (select feeId from LotOccupancyFees)" + ) + .run(recordDelete_timeMillisMin).changes; + + // Fee Categories + + purgedRecordCount += database + .prepare( + "delete from FeeCategories where recordDelete_timeMillis <= ?" + + " and feeCategoryId not in (select feeCategoryId from Fees)" + ) + .run(recordDelete_timeMillisMin).changes; + + // Occupancy Type Fields + + inactivedRecordCount += database + .prepare( + "update OccupancyTypeFields" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and occupancyTypeId in (select occupancyTypeId from OccupancyTypes where recordDelete_timeMillis is not null)" + ) + .run(requestSession.user.userName, rightNowMillis).changes; + + purgedRecordCount += database + .prepare( + "delete from OccupancyTypeFields where recordDelete_timeMillis <= ?" + + " and occupancyTypeFieldId not in (select occupancyTypeFieldId from LotOccupancyFields)" + ) + .run(recordDelete_timeMillisMin).changes; + + // Occupancy Types + + purgedRecordCount += database + .prepare( + "delete from OccupancyTypes where recordDelete_timeMillis <= ?" + + " and occupancyTypeId not in (select occupancyTypeId from LotOccupancies)" + + " and occupancyTypeId not in (select occupancyTypeId from Fees)" + ) + .run(recordDelete_timeMillisMin).changes; + + // Lot Occupant Types + + purgedRecordCount += database + .prepare( + "delete from LotOccupantTypes where recordDelete_timeMillis <= ?" + + " and lotOccupantTypeId not in (select lotOccupantTypeId from LotOccupancyOccupants)" + ) + .run(recordDelete_timeMillisMin).changes; + + // Lot Comments + + inactivedRecordCount += database + .prepare( + "update LotComments" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and lotId in (select lotId from Lots where recordDelete_timeMillis is not null)" + ) + .run(requestSession.user.userName, rightNowMillis).changes; + + purgedRecordCount += database + .prepare("delete from LotComments where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + + // Lot Fields + + inactivedRecordCount += database + .prepare( + "update LotFields" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and lotId in (select lotId from Lots where recordDelete_timeMillis is not null)" + ) + .run(requestSession.user.userName, rightNowMillis).changes; + + purgedRecordCount += database + .prepare("delete from LotFields where recordDelete_timeMillis <= ?") + .run(recordDelete_timeMillisMin).changes; + + // Lots + + inactivedRecordCount += database + .prepare( + "update Lots" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and mapId in (select mapId from Maps where recordDelete_timeMillis is not null)" + ) + .run(requestSession.user.userName, rightNowMillis).changes; + + purgedRecordCount += database + .prepare( + "delete from Lots where recordDelete_timeMillis <= ?" + + " and lotId not in (select lotId from LotComments)" + + " and lotId not in (select lotId from LotFields)" + + " and lotId not in (select lotId from LotOccupancies)" + ) + .run(recordDelete_timeMillisMin).changes; + + // Lot Statuses + + purgedRecordCount += database + .prepare( + "delete from LotStatuses where recordDelete_timeMillis <= ?" + + " and lotStatusId not in (select lotStatusId from Lots)" + ) + .run(recordDelete_timeMillisMin).changes; + + // Lot Type Fields + + inactivedRecordCount += database + .prepare( + "update LotTypeFields" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where recordDelete_timeMillis is null" + + " and lotTypeId in (select lotTypeId from LotTypes where recordDelete_timeMillis is not null)" + ) + .run(requestSession.user.userName, rightNowMillis).changes; + + purgedRecordCount += database + .prepare( + "delete from LotTypeFields where recordDelete_timeMillis <= ?" + + " and lotTypeFieldId not in (select lotTypeFieldId from LotFields)" + ) + .run(recordDelete_timeMillisMin).changes; + + // Lot Types + + purgedRecordCount += database + .prepare( + "delete from LotTypes where recordDelete_timeMillis <= ?" + + " and lotTypeId not in (select lotTypeId from Lots)" + ) + .run(recordDelete_timeMillisMin).changes; + + database.close(); + + return { + inactivedRecordCount, + purgedRecordCount + }; +}; + +export default cleanupDatabase; diff --git a/helpers/lotOccupancyDB/deleteFeeCategory.js b/helpers/lotOccupancyDB/deleteFeeCategory.js index 478ff8bd..0522140b 100644 --- a/helpers/lotOccupancyDB/deleteFeeCategory.js +++ b/helpers/lotOccupancyDB/deleteFeeCategory.js @@ -9,6 +9,12 @@ export const deleteFeeCategory = (feeCategoryId, requestSession) => { " recordDelete_timeMillis = ?" + " where feeCategoryId = ?") .run(requestSession.user.userName, rightNowMillis, feeCategoryId); + database + .prepare("update Fees" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where feeCategoryId = ?") + .run(requestSession.user.userName, rightNowMillis, feeCategoryId); database.close(); return result.changes > 0; }; diff --git a/helpers/lotOccupancyDB/deleteFeeCategory.ts b/helpers/lotOccupancyDB/deleteFeeCategory.ts index 25f60447..ecd0370d 100644 --- a/helpers/lotOccupancyDB/deleteFeeCategory.ts +++ b/helpers/lotOccupancyDB/deleteFeeCategory.ts @@ -21,6 +21,15 @@ export const deleteFeeCategory = ( ) .run(requestSession.user.userName, rightNowMillis, feeCategoryId); + database + .prepare( + "update Fees" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where feeCategoryId = ?" + ) + .run(requestSession.user.userName, rightNowMillis, feeCategoryId); + database.close(); return result.changes > 0; diff --git a/helpers/lotOccupancyDB/deleteLotOccupancy.js b/helpers/lotOccupancyDB/deleteLotOccupancy.js index 564854aa..25c5289a 100644 --- a/helpers/lotOccupancyDB/deleteLotOccupancy.js +++ b/helpers/lotOccupancyDB/deleteLotOccupancy.js @@ -9,6 +9,24 @@ export const deleteLotOccupancy = (lotOccupancyId, requestSession) => { " recordDelete_timeMillis = ?" + " where lotOccupancyId = ?") .run(requestSession.user.userName, rightNowMillis, lotOccupancyId); + database + .prepare("update LotOccupancyComments" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where lotOccupancyId = ?") + .run(requestSession.user.userName, rightNowMillis, lotOccupancyId); + database + .prepare("update LotOccupancyFields" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where lotOccupancyId = ?") + .run(requestSession.user.userName, rightNowMillis, lotOccupancyId); + database + .prepare("update LotOccupancyOccupants" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where lotOccupancyId = ?") + .run(requestSession.user.userName, rightNowMillis, lotOccupancyId); database.close(); return result.changes > 0; }; diff --git a/helpers/lotOccupancyDB/deleteLotOccupancy.ts b/helpers/lotOccupancyDB/deleteLotOccupancy.ts index a862b6ab..0715e1a0 100644 --- a/helpers/lotOccupancyDB/deleteLotOccupancy.ts +++ b/helpers/lotOccupancyDB/deleteLotOccupancy.ts @@ -21,6 +21,35 @@ export const deleteLotOccupancy = ( ) .run(requestSession.user.userName, rightNowMillis, lotOccupancyId); + database + .prepare( + "update LotOccupancyComments" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where lotOccupancyId = ?" + ) + .run(requestSession.user.userName, rightNowMillis, lotOccupancyId); + + database + .prepare( + "update LotOccupancyFields" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where lotOccupancyId = ?" + ) + .run(requestSession.user.userName, rightNowMillis, lotOccupancyId); + + database + .prepare( + "update LotOccupancyOccupants" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where lotOccupancyId = ?" + ) + .run(requestSession.user.userName, rightNowMillis, lotOccupancyId); + + // Do not auto-delete Fees or Transactions. Maintain financials. + database.close(); return result.changes > 0; diff --git a/helpers/lotOccupancyDB/deleteMap.js b/helpers/lotOccupancyDB/deleteMap.js index ea0aa39f..5a5e1032 100644 --- a/helpers/lotOccupancyDB/deleteMap.js +++ b/helpers/lotOccupancyDB/deleteMap.js @@ -9,6 +9,12 @@ export const deleteMap = (mapId, requestSession) => { " recordDelete_timeMillis = ?" + " where mapId = ?") .run(requestSession.user.userName, rightNowMillis, mapId); + database + .prepare("update Lots" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where mapId = ?") + .run(requestSession.user.userName, rightNowMillis, mapId); database.close(); return result.changes > 0; }; diff --git a/helpers/lotOccupancyDB/deleteMap.ts b/helpers/lotOccupancyDB/deleteMap.ts index 4e4bc8fa..a2c5eaa8 100644 --- a/helpers/lotOccupancyDB/deleteMap.ts +++ b/helpers/lotOccupancyDB/deleteMap.ts @@ -21,6 +21,15 @@ export const deleteMap = ( ) .run(requestSession.user.userName, rightNowMillis, mapId); + database + .prepare( + "update Lots" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where mapId = ?" + ) + .run(requestSession.user.userName, rightNowMillis, mapId); + database.close(); return result.changes > 0; diff --git a/helpers/lotOccupancyDB/deleteOccupancyType.js b/helpers/lotOccupancyDB/deleteOccupancyType.js index 48aba46e..4825da3e 100644 --- a/helpers/lotOccupancyDB/deleteOccupancyType.js +++ b/helpers/lotOccupancyDB/deleteOccupancyType.js @@ -10,6 +10,12 @@ export const deleteOccupancyType = (occupancyTypeId, requestSession) => { " recordDelete_timeMillis = ?" + " where occupancyTypeId = ?") .run(requestSession.user.userName, rightNowMillis, occupancyTypeId); + database + .prepare("update OccupancyTypeFields" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where occupancyTypeId = ?") + .run(requestSession.user.userName, rightNowMillis, occupancyTypeId); database.close(); clearOccupancyTypesCache(); return result.changes > 0; diff --git a/helpers/lotOccupancyDB/deleteOccupancyType.ts b/helpers/lotOccupancyDB/deleteOccupancyType.ts index c1ed78cc..3254c0b5 100644 --- a/helpers/lotOccupancyDB/deleteOccupancyType.ts +++ b/helpers/lotOccupancyDB/deleteOccupancyType.ts @@ -23,6 +23,15 @@ export const deleteOccupancyType = ( ) .run(requestSession.user.userName, rightNowMillis, occupancyTypeId); + database + .prepare( + "update OccupancyTypeFields" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where occupancyTypeId = ?" + ) + .run(requestSession.user.userName, rightNowMillis, occupancyTypeId) + database.close(); clearOccupancyTypesCache(); diff --git a/helpers/lotOccupancyDB/deleteWorkOrder.js b/helpers/lotOccupancyDB/deleteWorkOrder.js index c828e65c..80bc3d28 100644 --- a/helpers/lotOccupancyDB/deleteWorkOrder.js +++ b/helpers/lotOccupancyDB/deleteWorkOrder.js @@ -9,6 +9,30 @@ export const deleteWorkOrder = (workOrderId, requestSession) => { " recordDelete_timeMillis = ?" + " where workOrderId = ?") .run(requestSession.user.userName, rightNowMillis, workOrderId); + database + .prepare("update WorkOrderComments" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where workOrderId = ?") + .run(requestSession.user.userName, rightNowMillis, workOrderId); + database + .prepare("update WorkOrderLotOccupancies" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where workOrderId = ?") + .run(requestSession.user.userName, rightNowMillis, workOrderId); + database + .prepare("update WorkOrderLots" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where workOrderId = ?") + .run(requestSession.user.userName, rightNowMillis, workOrderId); + database + .prepare("update WorkOrderMilestones" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where workOrderId = ?") + .run(requestSession.user.userName, rightNowMillis, workOrderId); database.close(); return result.changes > 0; }; diff --git a/helpers/lotOccupancyDB/deleteWorkOrder.ts b/helpers/lotOccupancyDB/deleteWorkOrder.ts index 2c0f8751..02c8426c 100644 --- a/helpers/lotOccupancyDB/deleteWorkOrder.ts +++ b/helpers/lotOccupancyDB/deleteWorkOrder.ts @@ -21,6 +21,42 @@ export const deleteWorkOrder = ( ) .run(requestSession.user.userName, rightNowMillis, workOrderId); + database + .prepare( + "update WorkOrderComments" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where workOrderId = ?" + ) + .run(requestSession.user.userName, rightNowMillis, workOrderId); + + database + .prepare( + "update WorkOrderLotOccupancies" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where workOrderId = ?" + ) + .run(requestSession.user.userName, rightNowMillis, workOrderId); + + database + .prepare( + "update WorkOrderLots" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where workOrderId = ?" + ) + .run(requestSession.user.userName, rightNowMillis, workOrderId); + + database + .prepare( + "update WorkOrderMilestones" + + " set recordDelete_userName = ?," + + " recordDelete_timeMillis = ?" + + " where workOrderId = ?" + ) + .run(requestSession.user.userName, rightNowMillis, workOrderId); + database.close(); return result.changes > 0; diff --git a/helpers/lotOccupancyDB/getWorkOrderMilestones.js b/helpers/lotOccupancyDB/getWorkOrderMilestones.js index c2223799..4e61c822 100644 --- a/helpers/lotOccupancyDB/getWorkOrderMilestones.js +++ b/helpers/lotOccupancyDB/getWorkOrderMilestones.js @@ -11,7 +11,7 @@ export const getWorkOrderMilestones = (filters, options, connectedDatabase) => { }); database.function("userFn_dateIntegerToString", dateIntegerToString); database.function("userFn_timeIntegerToString", timeIntegerToString); - let sqlWhereClause = " where m.recordDelete_timeMillis is null"; + let sqlWhereClause = " where m.recordDelete_timeMillis is null and w.recordDelete_timeMillis is null"; const sqlParameters = []; if (filters.workOrderId) { sqlWhereClause += " and m.workOrderId = ?"; diff --git a/helpers/lotOccupancyDB/getWorkOrderMilestones.ts b/helpers/lotOccupancyDB/getWorkOrderMilestones.ts index a1d6fb28..05d7fde8 100644 --- a/helpers/lotOccupancyDB/getWorkOrderMilestones.ts +++ b/helpers/lotOccupancyDB/getWorkOrderMilestones.ts @@ -46,7 +46,7 @@ export const getWorkOrderMilestones = ( // Filters - let sqlWhereClause = " where m.recordDelete_timeMillis is null"; + let sqlWhereClause = " where m.recordDelete_timeMillis is null and w.recordDelete_timeMillis is null"; const sqlParameters = []; if (filters.workOrderId) { diff --git a/public-typescript/adminCleanup.d.ts b/public-typescript/adminCleanup.d.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/public-typescript/adminCleanup.d.ts @@ -0,0 +1 @@ +export {}; diff --git a/public-typescript/adminCleanup.js b/public-typescript/adminCleanup.js new file mode 100644 index 00000000..66401588 --- /dev/null +++ b/public-typescript/adminCleanup.js @@ -0,0 +1,33 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +(() => { + const urlPrefix = document.querySelector("main").dataset.urlPrefix; + const doCleanup = () => { + cityssm.postJSON(urlPrefix + "/admin/doCleanupDatabase", {}, (responseJSON) => { + if (responseJSON.success) { + bulmaJS.alert({ + title: "Database Cleaned Up Successfully", + message: responseJSON.inactivedRecordCount + " records inactivated, " + responseJSON.purgedRecordCount + " permanently deleted.", + contextualColorName: "success" + }); + } + else { + bulmaJS.alert({ + title: "Error Cleaning Database", + message: responseJSON.errorMessage, + contextualColorName: "danger" + }); + } + }); + }; + document.querySelector("#button--cleanupDatabase").addEventListener("click", () => { + bulmaJS.confirm({ + title: "Cleanup Database", + message: "Are you sure you want to cleanup up the database?", + okButton: { + text: "Yes, Cleanup Database", + callbackFunction: doCleanup + } + }); + }); +})(); diff --git a/public-typescript/adminCleanup.ts b/public-typescript/adminCleanup.ts new file mode 100644 index 00000000..00db86d1 --- /dev/null +++ b/public-typescript/adminCleanup.ts @@ -0,0 +1,50 @@ +/* eslint-disable unicorn/prefer-module */ + +import type { cityssmGlobal } from "@cityssm/bulma-webapp-js/src/types"; + +import type { BulmaJS } from "@cityssm/bulma-js/types"; + +declare const cityssm: cityssmGlobal; +declare const bulmaJS: BulmaJS; + +(() => { + const urlPrefix = document.querySelector("main").dataset.urlPrefix; + + const doCleanup = () => { + cityssm.postJSON( + urlPrefix + "/admin/doCleanupDatabase", + {}, + (responseJSON: { + success: boolean; + errorMessage?: string; + inactivedRecordCount: number; + purgedRecordCount: number; + }) => { + if (responseJSON.success) { + bulmaJS.alert({ + title: "Database Cleaned Up Successfully", + message: responseJSON.inactivedRecordCount + " records inactivated, " + responseJSON.purgedRecordCount + " permanently deleted.", + contextualColorName: "success" + }); + } else { + bulmaJS.alert({ + title: "Error Cleaning Database", + message: responseJSON.errorMessage, + contextualColorName: "danger" + }); + } + } + ); + }; + + document.querySelector("#button--cleanupDatabase").addEventListener("click", () => { + bulmaJS.confirm({ + title: "Cleanup Database", + message: "Are you sure you want to cleanup up the database?", + okButton: { + text: "Yes, Cleanup Database", + callbackFunction: doCleanup + } + }); + }); +})(); diff --git a/public-typescript/mapEdit.js b/public-typescript/mapEdit.js index 3ecff209..64573716 100644 --- a/public-typescript/mapEdit.js +++ b/public-typescript/mapEdit.js @@ -57,6 +57,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); title: "Delete " + exports.aliases.map, message: "Are you sure you want to delete this " + exports.aliases.map.toLowerCase() + + " and all related " + exports.aliases.lots.toLowerCase() + "?", contextualColorName: "warning", okButton: { diff --git a/public-typescript/mapEdit.ts b/public-typescript/mapEdit.ts index f2f1dd3f..015b7b09 100644 --- a/public-typescript/mapEdit.ts +++ b/public-typescript/mapEdit.ts @@ -86,6 +86,7 @@ declare const bulmaJS: BulmaJS; message: "Are you sure you want to delete this " + exports.aliases.map.toLowerCase() + + " and all related " + exports.aliases.lots.toLowerCase() + "?", contextualColorName: "warning", okButton: { diff --git a/public/javascripts/adminCleanup.min.js b/public/javascripts/adminCleanup.min.js new file mode 100644 index 00000000..8cbf468e --- /dev/null +++ b/public/javascripts/adminCleanup.min.js @@ -0,0 +1 @@ +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=document.querySelector("main").dataset.urlPrefix,a=()=>{cityssm.postJSON(e+"/admin/doCleanupDatabase",{},e=>{e.success?bulmaJS.alert({title:"Database Cleaned Up Successfully",message:e.inactivedRecordCount+" records inactivated, "+e.purgedRecordCount+" permanently deleted.",contextualColorName:"success"}):bulmaJS.alert({title:"Error Cleaning Database",message:e.errorMessage,contextualColorName:"danger"})})};document.querySelector("#button--cleanupDatabase").addEventListener("click",()=>{bulmaJS.confirm({title:"Cleanup Database",message:"Are you sure you want to cleanup up the database?",okButton:{text:"Yes, Cleanup Database",callbackFunction:a}})})})(); \ No newline at end of file diff --git a/public/javascripts/mapEdit.min.js b/public/javascripts/mapEdit.min.js index ceff5678..10a130ce 100644 --- a/public/javascripts/mapEdit.min.js +++ b/public/javascripts/mapEdit.min.js @@ -1 +1 @@ -"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=document.querySelector("main").dataset.urlPrefix,t=document.querySelector("#map--mapId").value,a=""===t,s=document.querySelector("#form--map");s.addEventListener("submit",t=>{t.preventDefault(),cityssm.postJSON(e+"/maps/"+(a?"doCreateMap":"doUpdateMap"),s,t=>{t.success?a?window.location.href=e+"/maps/"+t.mapId+"/edit":bulmaJS.alert({message:exports.aliases.map+" Updated Successfully",contextualColorName:"success"}):bulmaJS.alert({title:"Error Updating "+exports.aliases.map,message:t.errorMessage,contextualColorName:"danger"})})}),a||document.querySelector("#button--deleteMap").addEventListener("click",a=>{a.preventDefault();bulmaJS.confirm({title:"Delete "+exports.aliases.map,message:"Are you sure you want to delete this "+exports.aliases.map.toLowerCase()+"?",contextualColorName:"warning",okButton:{text:"Yes, Delete "+exports.aliases.map+"?",callbackFunction:()=>{cityssm.postJSON(e+"/maps/doDeleteMap",{mapId:t},t=>{t.success?window.location.href=e+"/maps?t="+Date.now():bulmaJS.alert({title:"Error Deleting "+exports.aliases.map,message:t.errorMessage,contextualColorName:"danger"})})}}})})})(); \ No newline at end of file +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),(()=>{const e=document.querySelector("main").dataset.urlPrefix,t=document.querySelector("#map--mapId").value,a=""===t,s=document.querySelector("#form--map");s.addEventListener("submit",t=>{t.preventDefault(),cityssm.postJSON(e+"/maps/"+(a?"doCreateMap":"doUpdateMap"),s,t=>{t.success?a?window.location.href=e+"/maps/"+t.mapId+"/edit":bulmaJS.alert({message:exports.aliases.map+" Updated Successfully",contextualColorName:"success"}):bulmaJS.alert({title:"Error Updating "+exports.aliases.map,message:t.errorMessage,contextualColorName:"danger"})})}),a||document.querySelector("#button--deleteMap").addEventListener("click",a=>{a.preventDefault();bulmaJS.confirm({title:"Delete "+exports.aliases.map,message:"Are you sure you want to delete this "+exports.aliases.map.toLowerCase()+" and all related "+exports.aliases.lots.toLowerCase()+"?",contextualColorName:"warning",okButton:{text:"Yes, Delete "+exports.aliases.map+"?",callbackFunction:()=>{cityssm.postJSON(e+"/maps/doDeleteMap",{mapId:t},t=>{t.success?window.location.href=e+"/maps?t="+Date.now():bulmaJS.alert({title:"Error Deleting "+exports.aliases.map,message:t.errorMessage,contextualColorName:"danger"})})}}})})})(); \ No newline at end of file diff --git a/routes/admin.js b/routes/admin.js index b140252a..39c5fdd3 100644 --- a/routes/admin.js +++ b/routes/admin.js @@ -43,6 +43,8 @@ import handler_doUpdateLotOccupantType from "../handlers/admin-post/doUpdateLotO import handler_doMoveLotOccupantTypeUp from "../handlers/admin-post/doMoveLotOccupantTypeUp.js"; import handler_doMoveLotOccupantTypeDown from "../handlers/admin-post/doMoveLotOccupantTypeDown.js"; import handler_doDeleteLotOccupantType from "../handlers/admin-post/doDeleteLotOccupantType.js"; +import handler_cleanup from "../handlers/admin-get/cleanup.js"; +import handler_doCleanupDatabase from "../handlers/admin-post/doCleanupDatabase.js"; export const router = Router(); router.get("/fees", permissionHandlers.adminGetHandler, handler_fees); router.post("/doAddFeeCategory", permissionHandlers.adminPostHandler, handler_doAddFeeCategory); @@ -87,4 +89,6 @@ router.post("/doUpdateLotOccupantType", permissionHandlers.adminPostHandler, han router.post("/doMoveLotOccupantTypeUp", permissionHandlers.adminPostHandler, handler_doMoveLotOccupantTypeUp); router.post("/doMoveLotOccupantTypeDown", permissionHandlers.adminPostHandler, handler_doMoveLotOccupantTypeDown); router.post("/doDeleteLotOccupantType", permissionHandlers.adminPostHandler, handler_doDeleteLotOccupantType); +router.get("/cleanup", permissionHandlers.adminGetHandler, handler_cleanup); +router.post("/doCleanupDatabase", permissionHandlers.adminPostHandler, handler_doCleanupDatabase); export default router; diff --git a/routes/admin.ts b/routes/admin.ts index cbfea3f3..f57adcb1 100644 --- a/routes/admin.ts +++ b/routes/admin.ts @@ -62,6 +62,11 @@ import handler_doMoveLotOccupantTypeUp from "../handlers/admin-post/doMoveLotOcc import handler_doMoveLotOccupantTypeDown from "../handlers/admin-post/doMoveLotOccupantTypeDown.js"; import handler_doDeleteLotOccupantType from "../handlers/admin-post/doDeleteLotOccupantType.js"; +// Cleanup + +import handler_cleanup from "../handlers/admin-get/cleanup.js"; +import handler_doCleanupDatabase from "../handlers/admin-post/doCleanupDatabase.js"; + export const router = Router(); /* @@ -70,11 +75,7 @@ export const router = Router(); router.get("/fees", permissionHandlers.adminGetHandler, handler_fees); -router.post( - "/doAddFeeCategory", - permissionHandlers.adminPostHandler, - handler_doAddFeeCategory -); +router.post("/doAddFeeCategory", permissionHandlers.adminPostHandler, handler_doAddFeeCategory); router.post( "/doUpdateFeeCategory", @@ -102,45 +103,21 @@ router.post( router.post("/doAddFee", permissionHandlers.adminPostHandler, handler_doAddFee); -router.post( - "/doUpdateFee", - permissionHandlers.adminPostHandler, - handler_doUpdateFee -); +router.post("/doUpdateFee", permissionHandlers.adminPostHandler, handler_doUpdateFee); -router.post( - "/doMoveFeeUp", - permissionHandlers.adminPostHandler, - handler_doMoveFeeUp -); +router.post("/doMoveFeeUp", permissionHandlers.adminPostHandler, handler_doMoveFeeUp); -router.post( - "/doMoveFeeDown", - permissionHandlers.adminPostHandler, - handler_doMoveFeeDown -); +router.post("/doMoveFeeDown", permissionHandlers.adminPostHandler, handler_doMoveFeeDown); -router.post( - "/doDeleteFee", - permissionHandlers.adminPostHandler, - handler_doDeleteFee -); +router.post("/doDeleteFee", permissionHandlers.adminPostHandler, handler_doDeleteFee); /* * Occupancy Type Management */ -router.get( - "/occupancyTypes", - permissionHandlers.adminGetHandler, - handler_occupancyTypes -); +router.get("/occupancyTypes", permissionHandlers.adminGetHandler, handler_occupancyTypes); -router.post( - "/doAddOccupancyType", - permissionHandlers.adminPostHandler, - handler_doAddOccupancyType -); +router.post("/doAddOccupancyType", permissionHandlers.adminPostHandler, handler_doAddOccupancyType); router.post( "/doUpdateOccupancyType", @@ -206,11 +183,7 @@ router.get("/tables", permissionHandlers.adminGetHandler, handler_tables); // Config Tables - Work Order Types -router.post( - "/doAddWorkOrderType", - permissionHandlers.adminPostHandler, - handler_doAddWorkOrderType -); +router.post("/doAddWorkOrderType", permissionHandlers.adminPostHandler, handler_doAddWorkOrderType); router.post( "/doUpdateWorkOrderType", @@ -269,23 +242,11 @@ router.post( // Config Tables - Lot Statuses -router.post( - "/doAddLotStatus", - permissionHandlers.adminPostHandler, - handler_doAddLotStatus -); +router.post("/doAddLotStatus", permissionHandlers.adminPostHandler, handler_doAddLotStatus); -router.post( - "/doUpdateLotStatus", - permissionHandlers.adminPostHandler, - handler_doUpdateLotStatus -); +router.post("/doUpdateLotStatus", permissionHandlers.adminPostHandler, handler_doUpdateLotStatus); -router.post( - "/doMoveLotStatusUp", - permissionHandlers.adminPostHandler, - handler_doMoveLotStatusUp -); +router.post("/doMoveLotStatusUp", permissionHandlers.adminPostHandler, handler_doMoveLotStatusUp); router.post( "/doMoveLotStatusDown", @@ -293,11 +254,7 @@ router.post( handler_doMoveLotStatusDown ); -router.post( - "/doDeleteLotStatus", - permissionHandlers.adminPostHandler, - handler_doDeleteLotStatus -); +router.post("/doDeleteLotStatus", permissionHandlers.adminPostHandler, handler_doDeleteLotStatus); // Config Tables - Lot Occupant Types @@ -331,4 +288,10 @@ router.post( handler_doDeleteLotOccupantType ); +// Cleanup + +router.get("/cleanup", permissionHandlers.adminGetHandler, handler_cleanup); + +router.post("/doCleanupDatabase", permissionHandlers.adminPostHandler, handler_doCleanupDatabase); + export default router; diff --git a/types/configTypes.d.ts b/types/configTypes.d.ts index 5b35d014..6f445aa3 100644 --- a/types/configTypes.d.ts +++ b/types/configTypes.d.ts @@ -48,6 +48,9 @@ export interface Config { workOrderMilestoneDateRecentAfterDays?: number; calendarEmailAddress?: string; }; + adminCleanup?: { + recordDeleteAgeDays?: number; + }; }; } interface ConfigApplication { diff --git a/types/configTypes.ts b/types/configTypes.ts index d885858e..5e0a5a58 100644 --- a/types/configTypes.ts +++ b/types/configTypes.ts @@ -48,6 +48,9 @@ export interface Config { workOrderMilestoneDateRecentAfterDays?: number; calendarEmailAddress?: string; }; + adminCleanup?: { + recordDeleteAgeDays?: number; + }; }; } diff --git a/views/_menu-admin.ejs b/views/_menu-admin.ejs index 6e118791..bb069326 100644 --- a/views/_menu-admin.ejs +++ b/views/_menu-admin.ejs @@ -21,5 +21,11 @@ Config Table Management +
+ +
++ Permanently delete records that have been previously deleted from the database. +
+