diff --git a/bin/www.js b/bin/www.js index c92ceed7..7c5976ce 100644 --- a/bin/www.js +++ b/bin/www.js @@ -23,6 +23,7 @@ const clusterSettings = { exec: `${directoryName}/wwwProcess.js` }; cluster.setupPrimary(clusterSettings); +let doShutdown = false; const activeWorkers = new Map(); for (let index = 0; index < processCount; index += 1) { const worker = cluster.fork(); @@ -41,8 +42,11 @@ cluster.on('message', (worker, message) => { cluster.on('exit', (worker) => { debug(`Worker ${(worker.process.pid ?? 0).toString()} has been killed`); activeWorkers.delete(worker.process.pid ?? 0); - debug('Starting another worker'); - cluster.fork(); + if (!doShutdown) { + debug('Starting another worker'); + const newWorker = cluster.fork(); + activeWorkers.set(newWorker.process.pid ?? 0, newWorker); + } }); const ntfyStartupConfig = getConfigProperty('application.ntfyStartup'); if (ntfyStartupConfig !== undefined) { @@ -75,7 +79,16 @@ if (process.env.STARTUP_TEST === 'true') { debug(`Killing processes in ${killSeconds} seconds...`); setTimeout(() => { debug('Killing processes'); + doShutdown = true; // eslint-disable-next-line unicorn/no-process-exit process.exit(0); }, secondsToMillis(killSeconds)); } +exitHook(() => { + doShutdown = true; + debug('Shutting down...'); + for (const worker of activeWorkers.values()) { + debug(`Killing worker ${worker.process.pid}`); + worker.kill(); + } +}); diff --git a/bin/www.ts b/bin/www.ts index 36c640f2..3036eb4f 100644 --- a/bin/www.ts +++ b/bin/www.ts @@ -39,6 +39,7 @@ const clusterSettings = { cluster.setupPrimary(clusterSettings) +let doShutdown = false const activeWorkers = new Map() for (let index = 0; index < processCount; index += 1) { @@ -62,8 +63,12 @@ cluster.on('exit', (worker) => { debug(`Worker ${(worker.process.pid ?? 0).toString()} has been killed`) activeWorkers.delete(worker.process.pid ?? 0) - debug('Starting another worker') - cluster.fork() + if (!doShutdown) { + debug('Starting another worker') + const newWorker = cluster.fork() + + activeWorkers.set(newWorker.process.pid ?? 0, newWorker) + } }) const ntfyStartupConfig = getConfigProperty('application.ntfyStartup') @@ -107,7 +112,19 @@ if (process.env.STARTUP_TEST === 'true') { setTimeout(() => { debug('Killing processes') + doShutdown = true + // eslint-disable-next-line unicorn/no-process-exit process.exit(0) }, secondsToMillis(killSeconds)) } + +exitHook(() => { + doShutdown = true + debug('Shutting down...') + + for (const worker of activeWorkers.values()) { + debug(`Killing worker ${worker.process.pid}`) + worker.kill() + } +}) diff --git a/data/config.base.js b/data/config.base.js index a121f8dc..2c01e394 100644 --- a/data/config.base.js +++ b/data/config.base.js @@ -6,6 +6,7 @@ export const config = { settings: { adminCleanup: {}, burialSites: {}, + cemeteries: {}, contracts: {}, dynamicsGP: { integrationIsEnabled: false diff --git a/data/config.base.ts b/data/config.base.ts index a8632324..a53cd22d 100644 --- a/data/config.base.ts +++ b/data/config.base.ts @@ -8,6 +8,7 @@ export const config: Config = { settings: { adminCleanup: {}, burialSites: {}, + cemeteries: {}, contracts: {}, dynamicsGP: { integrationIsEnabled: false diff --git a/data/config.defaultValues.d.ts b/data/config.defaultValues.d.ts index 87df8336..7e689d6e 100644 --- a/data/config.defaultValues.d.ts +++ b/data/config.defaultValues.d.ts @@ -31,8 +31,10 @@ export declare const configDefaultValues: { 'settings.latitudeMin': number; 'settings.longitudeMax': number; 'settings.longitudeMin': number; + 'settings.cemeteries.refreshImageChanges': boolean; 'settings.burialSites.burialSiteNameSegments': ConfigBurialSiteNameSegments; 'settings.burialSites.burialSiteNameSegments.includeCemeteryKey': boolean; + 'settings.burialSites.refreshImageChanges': boolean; 'settings.contracts.burialSiteIdIsRequired': boolean; 'settings.contracts.contractEndDateIsRequired': boolean; 'settings.contracts.deathAgePeriods': string[]; diff --git a/data/config.defaultValues.js b/data/config.defaultValues.js index 03c10f14..832d1726 100644 --- a/data/config.defaultValues.js +++ b/data/config.defaultValues.js @@ -30,6 +30,7 @@ export const configDefaultValues = { 'settings.latitudeMin': -90, 'settings.longitudeMax': 180, 'settings.longitudeMin': -180, + 'settings.cemeteries.refreshImageChanges': false, 'settings.burialSites.burialSiteNameSegments': { includeCemeteryKey: false, separator: '-', @@ -44,6 +45,7 @@ export const configDefaultValues = { } }, 'settings.burialSites.burialSiteNameSegments.includeCemeteryKey': false, + 'settings.burialSites.refreshImageChanges': false, 'settings.contracts.burialSiteIdIsRequired': true, 'settings.contracts.contractEndDateIsRequired': false, 'settings.contracts.deathAgePeriods': [ diff --git a/data/config.defaultValues.ts b/data/config.defaultValues.ts index 96945079..7738d28b 100644 --- a/data/config.defaultValues.ts +++ b/data/config.defaultValues.ts @@ -49,6 +49,8 @@ export const configDefaultValues = { 'settings.longitudeMax': 180, 'settings.longitudeMin': -180, + 'settings.cemeteries.refreshImageChanges': false, + 'settings.burialSites.burialSiteNameSegments': { includeCemeteryKey: false, separator: '-', @@ -66,6 +68,8 @@ export const configDefaultValues = { 'settings.burialSites.burialSiteNameSegments.includeCemeteryKey': false, + 'settings.burialSites.refreshImageChanges': false, + 'settings.contracts.burialSiteIdIsRequired': true, 'settings.contracts.contractEndDateIsRequired': false, 'settings.contracts.deathAgePeriods': [ diff --git a/database/getBurialSite.js b/database/getBurialSite.js index 081e0878..c4e237fa 100644 --- a/database/getBurialSite.js +++ b/database/getBurialSite.js @@ -11,7 +11,9 @@ const baseSQL = `select l.burialSiteId, l.burialSiteNameSegment5, l.burialSiteName, l.burialSiteStatusId, s.burialSiteStatus, + l.cemeteryId, m.cemeteryName, + m.cemeteryLatitude, m.cemeteryLongitude, m.cemeterySvg, l.cemeterySvgId, l.burialSiteImage, l.burialSiteLatitude, l.burialSiteLongitude diff --git a/database/getBurialSite.ts b/database/getBurialSite.ts index edcb05fb..a41dcded 100644 --- a/database/getBurialSite.ts +++ b/database/getBurialSite.ts @@ -14,7 +14,9 @@ const baseSQL = `select l.burialSiteId, l.burialSiteNameSegment5, l.burialSiteName, l.burialSiteStatusId, s.burialSiteStatus, + l.cemeteryId, m.cemeteryName, + m.cemeteryLatitude, m.cemeteryLongitude, m.cemeterySvg, l.cemeterySvgId, l.burialSiteImage, l.burialSiteLatitude, l.burialSiteLongitude diff --git a/database/getCemetery.js b/database/getCemetery.js index 9196ae11..efc59de0 100644 --- a/database/getCemetery.js +++ b/database/getCemetery.js @@ -1,4 +1,10 @@ import { acquireConnection } from './pool.js'; +export default async function getCemetery(cemeteryId, connectedDatabase) { + return await _getCemetery('cemeteryId', cemeteryId, connectedDatabase); +} +export async function getCemeteryByKey(cemeteryKey, connectedDatabase) { + return await _getCemetery('cemeteryKey', cemeteryKey, connectedDatabase); +} async function _getCemetery(keyColumn, cemeteryIdOrKey, connectedDatabase) { const database = connectedDatabase ?? (await acquireConnection()); const cemetery = database @@ -6,7 +12,11 @@ async function _getCemetery(keyColumn, cemeteryIdOrKey, connectedDatabase) { m.cemeteryLatitude, m.cemeteryLongitude, m.cemeterySvg, m.cemeteryAddress1, m.cemeteryAddress2, m.cemeteryCity, m.cemeteryProvince, m.cemeteryPostalCode, m.cemeteryPhoneNumber, + p.cemeteryId as parentCemeteryId, p.cemeteryName as parentCemeteryName, + p.cemeteryLatitude as parentCemeteryLatitude, p.cemeteryLongitude as parentCemeteryLongitude, + p.cemeterySvg as parentCemeterySvg, + m.recordCreate_userName, m.recordCreate_timeMillis, m.recordUpdate_userName, m.recordUpdate_timeMillis, m.recordDelete_userName, m.recordDelete_timeMillis, @@ -30,9 +40,3 @@ async function _getCemetery(keyColumn, cemeteryIdOrKey, connectedDatabase) { } return cemetery; } -export default async function getCemetery(cemeteryId, connectedDatabase) { - return await _getCemetery('cemeteryId', cemeteryId, connectedDatabase); -} -export async function getCemeteryByKey(cemeteryKey, connectedDatabase) { - return await _getCemetery('cemeteryKey', cemeteryKey, connectedDatabase); -} diff --git a/database/getCemetery.ts b/database/getCemetery.ts index 922140c4..bc32a3d7 100644 --- a/database/getCemetery.ts +++ b/database/getCemetery.ts @@ -4,6 +4,20 @@ import type { Cemetery } from '../types/recordTypes.js' import { acquireConnection } from './pool.js' +export default async function getCemetery( + cemeteryId: number | string, + connectedDatabase?: PoolConnection +): Promise { + return await _getCemetery('cemeteryId', cemeteryId, connectedDatabase) +} + +export async function getCemeteryByKey( + cemeteryKey: string, + connectedDatabase?: PoolConnection +): Promise { + return await _getCemetery('cemeteryKey', cemeteryKey, connectedDatabase) +} + async function _getCemetery( keyColumn: 'cemeteryId' | 'cemeteryKey', cemeteryIdOrKey: number | string, @@ -17,7 +31,11 @@ async function _getCemetery( m.cemeteryLatitude, m.cemeteryLongitude, m.cemeterySvg, m.cemeteryAddress1, m.cemeteryAddress2, m.cemeteryCity, m.cemeteryProvince, m.cemeteryPostalCode, m.cemeteryPhoneNumber, + p.cemeteryId as parentCemeteryId, p.cemeteryName as parentCemeteryName, + p.cemeteryLatitude as parentCemeteryLatitude, p.cemeteryLongitude as parentCemeteryLongitude, + p.cemeterySvg as parentCemeterySvg, + m.recordCreate_userName, m.recordCreate_timeMillis, m.recordUpdate_userName, m.recordUpdate_timeMillis, m.recordDelete_userName, m.recordDelete_timeMillis, @@ -44,17 +62,3 @@ async function _getCemetery( return cemetery } - -export default async function getCemetery( - cemeteryId: number | string, - connectedDatabase?: PoolConnection -): Promise { - return await _getCemetery('cemeteryId', cemeteryId, connectedDatabase) -} - -export async function getCemeteryByKey( - cemeteryKey: string, - connectedDatabase?: PoolConnection -): Promise { - return await _getCemetery('cemeteryKey', cemeteryKey, connectedDatabase) -} diff --git a/database/getContracts.d.ts b/database/getContracts.d.ts index 0ae82270..40eed359 100644 --- a/database/getContracts.d.ts +++ b/database/getContracts.d.ts @@ -3,14 +3,14 @@ import { type DateString } from '@cityssm/utils-datetime'; import type { Contract } from '../types/recordTypes.js'; export interface GetContractsFilters { burialSiteId?: number | string; - contractTime?: '' | 'current' | 'future' | 'past'; - contractStartDateString?: DateString; contractEffectiveDateString?: string; + contractStartDateString?: DateString; + contractTime?: '' | 'current' | 'future' | 'past'; deceasedName?: string; contractTypeId?: number | string; cemeteryId?: number | string; burialSiteName?: string; - burialSiteNameSearchType?: '' | 'startsWith' | 'endsWith'; + burialSiteNameSearchType?: '' | 'endsWith' | 'startsWith'; burialSiteTypeId?: number | string; funeralHomeId?: number | string; notWorkOrderId?: number | string; diff --git a/database/getContracts.ts b/database/getContracts.ts index 41f97f16..66978b44 100644 --- a/database/getContracts.ts +++ b/database/getContracts.ts @@ -24,16 +24,16 @@ import { acquireConnection } from './pool.js' export interface GetContractsFilters { burialSiteId?: number | string - contractTime?: '' | 'current' | 'future' | 'past' - contractStartDateString?: DateString contractEffectiveDateString?: string + contractStartDateString?: DateString + contractTime?: '' | 'current' | 'future' | 'past' deceasedName?: string contractTypeId?: number | string cemeteryId?: number | string burialSiteName?: string - burialSiteNameSearchType?: '' | 'startsWith' | 'endsWith' + burialSiteNameSearchType?: '' | 'endsWith' | 'startsWith' burialSiteTypeId?: number | string funeralHomeId?: number | string diff --git a/handlers/admin-post/doBackupDatabase.js b/handlers/admin-post/doBackupDatabase.js index b87d39ab..e5256a25 100644 --- a/handlers/admin-post/doBackupDatabase.js +++ b/handlers/admin-post/doBackupDatabase.js @@ -1,8 +1,8 @@ -import { backupDatabase } from '../../helpers/functions.database.js'; +import { backupDatabase } from '../../helpers/database.helpers.js'; export default async function handler(_request, response) { const backupDatabasePath = await backupDatabase(); if (typeof backupDatabasePath === 'string') { - const backupDatabasePathSplit = backupDatabasePath.split(/[/\\]/g); + const backupDatabasePathSplit = backupDatabasePath.split(/[/\\]/); const fileName = backupDatabasePathSplit.at(-1); response.json({ success: true, diff --git a/handlers/admin-post/doBackupDatabase.ts b/handlers/admin-post/doBackupDatabase.ts index 18013e21..054e7995 100644 --- a/handlers/admin-post/doBackupDatabase.ts +++ b/handlers/admin-post/doBackupDatabase.ts @@ -1,6 +1,6 @@ import type { Request, Response } from 'express' -import { backupDatabase } from '../../helpers/functions.database.js' +import { backupDatabase } from '../../helpers/database.helpers.js' export default async function handler( _request: Request, @@ -9,7 +9,7 @@ export default async function handler( const backupDatabasePath = await backupDatabase() if (typeof backupDatabasePath === 'string') { - const backupDatabasePathSplit = backupDatabasePath.split(/[/\\]/g) + const backupDatabasePathSplit = backupDatabasePath.split(/[/\\]/) const fileName = backupDatabasePathSplit.at(-1) diff --git a/handlers/workOrders-post/doAddWorkOrderBurialSite.d.ts b/handlers/workOrders-post/doAddWorkOrderBurialSite.d.ts index b088724b..52d690e3 100644 --- a/handlers/workOrders-post/doAddWorkOrderBurialSite.d.ts +++ b/handlers/workOrders-post/doAddWorkOrderBurialSite.d.ts @@ -1,5 +1,5 @@ import type { Request, Response } from 'express'; export default function handler(request: Request, response: Response): Promise; diff --git a/handlers/workOrders-post/doAddWorkOrderBurialSite.js b/handlers/workOrders-post/doAddWorkOrderBurialSite.js index 1c5b6e12..ec95f3a9 100644 --- a/handlers/workOrders-post/doAddWorkOrderBurialSite.js +++ b/handlers/workOrders-post/doAddWorkOrderBurialSite.js @@ -2,8 +2,8 @@ import addWorkOrderBurialSite from '../../database/addWorkOrderBurialSite.js'; import getBurialSites from '../../database/getBurialSites.js'; export default async function handler(request, response) { const success = await addWorkOrderBurialSite({ - workOrderId: request.body.workOrderId, - burialSiteId: request.body.burialSiteId + burialSiteId: request.body.burialSiteId, + workOrderId: request.body.workOrderId }, request.session.user); const results = await getBurialSites({ workOrderId: request.body.workOrderId diff --git a/handlers/workOrders-post/doAddWorkOrderBurialSite.ts b/handlers/workOrders-post/doAddWorkOrderBurialSite.ts index 1660bcc3..41e07756 100644 --- a/handlers/workOrders-post/doAddWorkOrderBurialSite.ts +++ b/handlers/workOrders-post/doAddWorkOrderBurialSite.ts @@ -7,14 +7,14 @@ export default async function handler( request: Request< unknown, unknown, - { workOrderId: string; burialSiteId: string } + { burialSiteId: string; workOrderId: string; } >, response: Response ): Promise { const success = await addWorkOrderBurialSite( { - workOrderId: request.body.workOrderId, - burialSiteId: request.body.burialSiteId + burialSiteId: request.body.burialSiteId, + workOrderId: request.body.workOrderId }, request.session.user as User ) @@ -26,6 +26,7 @@ export default async function handler( { limit: -1, offset: 0, + includeContractCount: false } ) diff --git a/handlers/workOrders-post/doAddWorkOrderComment.d.ts b/handlers/workOrders-post/doAddWorkOrderComment.d.ts index 19d32e4d..63274cfb 100644 --- a/handlers/workOrders-post/doAddWorkOrderComment.d.ts +++ b/handlers/workOrders-post/doAddWorkOrderComment.d.ts @@ -1,2 +1,3 @@ import type { Request, Response } from 'express'; -export default function handler(request: Request, response: Response): Promise; +import { type AddWorkOrderCommentForm } from '../../database/addWorkOrderComment.js'; +export default function handler(request: Request, response: Response): Promise; diff --git a/handlers/workOrders-post/doAddWorkOrderComment.ts b/handlers/workOrders-post/doAddWorkOrderComment.ts index 65377f76..700e8506 100644 --- a/handlers/workOrders-post/doAddWorkOrderComment.ts +++ b/handlers/workOrders-post/doAddWorkOrderComment.ts @@ -6,13 +6,10 @@ import addWorkOrderComment, { import getWorkOrderComments from '../../database/getWorkOrderComments.js' export default async function handler( - request: Request, + request: Request, response: Response ): Promise { - await addWorkOrderComment( - request.body as AddWorkOrderCommentForm, - request.session.user as User - ) + await addWorkOrderComment(request.body, request.session.user as User) const workOrderComments = await getWorkOrderComments( request.body.workOrderId as string diff --git a/handlers/workOrders-post/doAddWorkOrderContract.js b/handlers/workOrders-post/doAddWorkOrderContract.js index bf502836..becd0334 100644 --- a/handlers/workOrders-post/doAddWorkOrderContract.js +++ b/handlers/workOrders-post/doAddWorkOrderContract.js @@ -10,8 +10,8 @@ export default async function handler(request, response) { }, { limit: -1, offset: 0, - includeInterments: true, includeFees: false, + includeInterments: true, includeTransactions: false }); response.json({ diff --git a/handlers/workOrders-post/doAddWorkOrderContract.ts b/handlers/workOrders-post/doAddWorkOrderContract.ts index 2a0dd000..6acfe7f8 100644 --- a/handlers/workOrders-post/doAddWorkOrderContract.ts +++ b/handlers/workOrders-post/doAddWorkOrderContract.ts @@ -26,8 +26,9 @@ export default async function handler( { limit: -1, offset: 0, - includeInterments: true, + includeFees: false, + includeInterments: true, includeTransactions: false } ) diff --git a/handlers/workOrders-post/doAddWorkOrderMilestone.d.ts b/handlers/workOrders-post/doAddWorkOrderMilestone.d.ts index 19d32e4d..d953f117 100644 --- a/handlers/workOrders-post/doAddWorkOrderMilestone.d.ts +++ b/handlers/workOrders-post/doAddWorkOrderMilestone.d.ts @@ -1,2 +1,3 @@ import type { Request, Response } from 'express'; -export default function handler(request: Request, response: Response): Promise; +import { type AddWorkOrderMilestoneForm } from '../../database/addWorkOrderMilestone.js'; +export default function handler(request: Request, response: Response): Promise; diff --git a/handlers/workOrders-post/doAddWorkOrderMilestone.ts b/handlers/workOrders-post/doAddWorkOrderMilestone.ts index f6fdfc7c..77ed798c 100644 --- a/handlers/workOrders-post/doAddWorkOrderMilestone.ts +++ b/handlers/workOrders-post/doAddWorkOrderMilestone.ts @@ -6,17 +6,17 @@ import addWorkOrderMilestone, { import getWorkOrderMilestones from '../../database/getWorkOrderMilestones.js' export default async function handler( - request: Request, + request: Request, response: Response ): Promise { const success = await addWorkOrderMilestone( - request.body as AddWorkOrderMilestoneForm, + request.body, request.session.user as User ) const workOrderMilestones = await getWorkOrderMilestones( { - workOrderId: request.body.workOrderId as string + workOrderId: request.body.workOrderId }, { orderBy: 'completion' diff --git a/handlers/workOrders-post/doCloseWorkOrder.d.ts b/handlers/workOrders-post/doCloseWorkOrder.d.ts index 19d32e4d..c7b0d12d 100644 --- a/handlers/workOrders-post/doCloseWorkOrder.d.ts +++ b/handlers/workOrders-post/doCloseWorkOrder.d.ts @@ -1,2 +1,3 @@ import type { Request, Response } from 'express'; -export default function handler(request: Request, response: Response): Promise; +import { type CloseWorkOrderForm } from '../../database/closeWorkOrder.js'; +export default function handler(request: Request, response: Response): Promise; diff --git a/handlers/workOrders-post/doCloseWorkOrder.ts b/handlers/workOrders-post/doCloseWorkOrder.ts index 682e7c10..f97ad73e 100644 --- a/handlers/workOrders-post/doCloseWorkOrder.ts +++ b/handlers/workOrders-post/doCloseWorkOrder.ts @@ -5,11 +5,11 @@ import closeWorkOrder, { } from '../../database/closeWorkOrder.js' export default async function handler( - request: Request, + request: Request, response: Response ): Promise { const success = await closeWorkOrder( - request.body as CloseWorkOrderForm, + request.body, request.session.user as User ) @@ -17,4 +17,3 @@ export default async function handler( success }) } - diff --git a/handlers/workOrders-post/doCompleteWorkOrderMilestone.d.ts b/handlers/workOrders-post/doCompleteWorkOrderMilestone.d.ts index 19d32e4d..28337bff 100644 --- a/handlers/workOrders-post/doCompleteWorkOrderMilestone.d.ts +++ b/handlers/workOrders-post/doCompleteWorkOrderMilestone.d.ts @@ -1,2 +1,5 @@ import type { Request, Response } from 'express'; -export default function handler(request: Request, response: Response): Promise; +export default function handler(request: Request, response: Response): Promise; diff --git a/handlers/workOrders-post/doCompleteWorkOrderMilestone.ts b/handlers/workOrders-post/doCompleteWorkOrderMilestone.ts index 29e05bbe..265b2115 100644 --- a/handlers/workOrders-post/doCompleteWorkOrderMilestone.ts +++ b/handlers/workOrders-post/doCompleteWorkOrderMilestone.ts @@ -4,19 +4,23 @@ import completeWorkOrderMilestone from '../../database/completeWorkOrderMileston import getWorkOrderMilestones from '../../database/getWorkOrderMilestones.js' export default async function handler( - request: Request, + request: Request< + unknown, + unknown, + { workOrderId: string; workOrderMilestoneId: string } + >, response: Response ): Promise { const success = await completeWorkOrderMilestone( { - workOrderMilestoneId: request.body.workOrderMilestoneId as string + workOrderMilestoneId: request.body.workOrderMilestoneId }, request.session.user as User ) const workOrderMilestones = await getWorkOrderMilestones( { - workOrderId: request.body.workOrderId as string + workOrderId: request.body.workOrderId }, { orderBy: 'completion' diff --git a/helpers/database.helpers.d.ts b/helpers/database.helpers.d.ts index 73c0839e..b0d493a4 100644 --- a/helpers/database.helpers.d.ts +++ b/helpers/database.helpers.d.ts @@ -2,4 +2,4 @@ export declare const useTestDatabases: boolean; export declare const sunriseDBLive = "data/sunrise.db"; export declare const sunriseDBTesting = "data/sunrise-testing.db"; export declare const sunriseDB: string; -export declare const backupFolder = "data/backups"; +export declare function backupDatabase(): Promise; diff --git a/helpers/database.helpers.js b/helpers/database.helpers.js index 6a1637bd..0d424c84 100644 --- a/helpers/database.helpers.js +++ b/helpers/database.helpers.js @@ -1,3 +1,4 @@ +import fs from 'node:fs/promises'; import Debug from 'debug'; import { DEBUG_NAMESPACE } from '../debug.config.js'; import { getConfigProperty } from './config.helpers.js'; @@ -10,4 +11,15 @@ if (useTestDatabases) { export const sunriseDBLive = 'data/sunrise.db'; export const sunriseDBTesting = 'data/sunrise-testing.db'; export const sunriseDB = useTestDatabases ? sunriseDBTesting : sunriseDBLive; -export const backupFolder = 'data/backups'; +const backupFolder = 'data/backups'; +export async function backupDatabase() { + const databasePathSplit = sunriseDB.split(/[/\\]/); + const backupDatabasePath = `${backupFolder}/${databasePathSplit.at(-1)}.${Date.now().toString()}`; + try { + await fs.copyFile(sunriseDB, backupDatabasePath); + return backupDatabasePath; + } + catch { + return false; + } +} diff --git a/helpers/database.helpers.ts b/helpers/database.helpers.ts index 6c3359db..ac206993 100644 --- a/helpers/database.helpers.ts +++ b/helpers/database.helpers.ts @@ -1,3 +1,5 @@ +import fs from 'node:fs/promises' + import Debug from 'debug' import { DEBUG_NAMESPACE } from '../debug.config.js' @@ -19,4 +21,17 @@ export const sunriseDBTesting = 'data/sunrise-testing.db' export const sunriseDB = useTestDatabases ? sunriseDBTesting : sunriseDBLive -export const backupFolder = 'data/backups' +const backupFolder = 'data/backups' + +export async function backupDatabase(): Promise { + const databasePathSplit = sunriseDB.split(/[/\\]/) + + const backupDatabasePath = `${backupFolder}/${databasePathSplit.at(-1)}.${Date.now().toString()}` + + try { + await fs.copyFile(sunriseDB, backupDatabasePath) + return backupDatabasePath + } catch { + return false + } +} diff --git a/helpers/functions.database.d.ts b/helpers/functions.database.d.ts deleted file mode 100644 index 78075fb7..00000000 --- a/helpers/functions.database.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare function backupDatabase(): Promise; diff --git a/helpers/functions.database.js b/helpers/functions.database.js deleted file mode 100644 index cd6d1b04..00000000 --- a/helpers/functions.database.js +++ /dev/null @@ -1,13 +0,0 @@ -import fs from 'node:fs/promises'; -import { backupFolder, sunriseDB as databasePath } from '../helpers/database.helpers.js'; -export async function backupDatabase() { - const databasePathSplit = databasePath.split(/[/\\]/); - const backupDatabasePath = `${backupFolder}/${databasePathSplit.at(-1)}.${Date.now().toString()}`; - try { - await fs.copyFile(databasePath, backupDatabasePath); - return backupDatabasePath; - } - catch { - return false; - } -} diff --git a/helpers/functions.database.ts b/helpers/functions.database.ts deleted file mode 100644 index 793eab3b..00000000 --- a/helpers/functions.database.ts +++ /dev/null @@ -1,19 +0,0 @@ -import fs from 'node:fs/promises' - -import { - backupFolder, - sunriseDB as databasePath -} from '../helpers/database.helpers.js' - -export async function backupDatabase(): Promise { - const databasePathSplit = databasePath.split(/[/\\]/) - - const backupDatabasePath = `${backupFolder}/${databasePathSplit.at(-1)}.${Date.now().toString()}` - - try { - await fs.copyFile(databasePath, backupDatabasePath) - return backupDatabasePath - } catch { - return false - } -} diff --git a/helpers/images.helpers.js b/helpers/images.helpers.js index 7e0f4aa4..3811191c 100644 --- a/helpers/images.helpers.js +++ b/helpers/images.helpers.js @@ -1,34 +1,83 @@ import fs from 'node:fs/promises'; import path from 'node:path'; +import chokidar from 'chokidar'; +import Debug from 'debug'; +import { DEBUG_NAMESPACE } from '../debug.config.js'; import { getConfigProperty } from './config.helpers.js'; -let burialSiteImages; +const debug = Debug(`${DEBUG_NAMESPACE}:images.helpers`); +/* + * Burial Site Images + */ +const burialSiteImagesFolder = path.join(getConfigProperty('settings.publicInternalPath'), 'images', 'burialSites'); +const burialSiteImageFileExtensions = ['jpg', 'jpeg', 'png']; +let burialSiteImages = []; export async function getBurialSiteImages() { - if (burialSiteImages === undefined) { - const files = await fs.readdir(path.join(getConfigProperty('settings.publicInternalPath'), 'images', 'burialSites')); + if (burialSiteImages.length === 0) { + // eslint-disable-next-line security/detect-non-literal-fs-filename + const files = await fs.readdir(burialSiteImagesFolder); const images = []; for (const file of files) { const lowerCaseFileName = file.toLowerCase(); - if (lowerCaseFileName.endsWith('.jpg') || - lowerCaseFileName.endsWith('.jpeg') || - lowerCaseFileName.endsWith('.png')) { - images.push(file); + for (const fileExtension of burialSiteImageFileExtensions) { + if (lowerCaseFileName.endsWith(`.${fileExtension}`)) { + images.push(file); + break; + } } } burialSiteImages = images; } return burialSiteImages; } -let cemeterySVGs; +function clearCachedBurialSiteImages() { + debug('Burial site images folder changed.'); + burialSiteImages = []; +} +if (getConfigProperty('settings.burialSites.refreshImageChanges')) { + debug('Burial site images watcher enabled.'); + const burialSitesWatcher = chokidar.watch(burialSiteImagesFolder, { + ignoreInitial: true, + persistent: true + }); + burialSitesWatcher.on('add', clearCachedBurialSiteImages); + burialSitesWatcher.on('change', clearCachedBurialSiteImages); + burialSitesWatcher.on('unlink', clearCachedBurialSiteImages); +} +/* + * Cemetery SVGs + */ +const cemeterySVGsFolder = path.join(getConfigProperty('settings.publicInternalPath'), 'images', 'cemeteries'); +const cemeterySVGFileExtensions = ['svg']; +let cemeterySVGs = []; export async function getCemeterySVGs() { - if (cemeterySVGs === undefined) { - const files = await fs.readdir(path.join(getConfigProperty('settings.publicInternalPath'), 'images', 'cemeteries')); + if (cemeterySVGs.length === 0) { + // eslint-disable-next-line security/detect-non-literal-fs-filename + const files = await fs.readdir(cemeterySVGsFolder); const SVGs = []; for (const file of files) { - if (file.toLowerCase().endsWith('.svg')) { - SVGs.push(file); + const lowerCaseFileName = file.toLowerCase(); + for (const fileExtension of cemeterySVGFileExtensions) { + if (lowerCaseFileName.endsWith(`.${fileExtension}`)) { + SVGs.push(file); + break; + } } } cemeterySVGs = SVGs; } return cemeterySVGs; } +function clearCachedCemeterySVGs() { + debug('Cemetery SVGs folder changed.'); + cemeterySVGs = []; +} +if (getConfigProperty('settings.cemeteries.refreshImageChanges')) { + debug('Cemetery SVGs watcher enabled.'); + const cemeteryWatcher = chokidar.watch(cemeterySVGsFolder, { + ignoreInitial: true, + persistent: true + }); + cemeteryWatcher.on('add', clearCachedCemeterySVGs); + cemeteryWatcher.on('change', clearCachedCemeterySVGs); + cemeteryWatcher.on('unlink', clearCachedCemeterySVGs); +} diff --git a/helpers/images.helpers.ts b/helpers/images.helpers.ts index 99bf7b45..3bb80ab9 100644 --- a/helpers/images.helpers.ts +++ b/helpers/images.helpers.ts @@ -1,30 +1,44 @@ import fs from 'node:fs/promises' import path from 'node:path' +import chokidar from 'chokidar' +import Debug from 'debug' + +import { DEBUG_NAMESPACE } from '../debug.config.js' + import { getConfigProperty } from './config.helpers.js' -let burialSiteImages: string[] | undefined +const debug = Debug(`${DEBUG_NAMESPACE}:images.helpers`) + +/* + * Burial Site Images + */ + +const burialSiteImagesFolder = path.join( + getConfigProperty('settings.publicInternalPath'), + 'images', + 'burialSites' +) + +const burialSiteImageFileExtensions = ['jpg', 'jpeg', 'png'] + +let burialSiteImages: string[] = [] export async function getBurialSiteImages(): Promise { - if (burialSiteImages === undefined) { - const files = await fs.readdir( - path.join( - getConfigProperty('settings.publicInternalPath'), - 'images', - 'burialSites' - ) - ) + if (burialSiteImages.length === 0) { + // eslint-disable-next-line security/detect-non-literal-fs-filename + const files = await fs.readdir(burialSiteImagesFolder) const images: string[] = [] for (const file of files) { const lowerCaseFileName = file.toLowerCase() - if ( - lowerCaseFileName.endsWith('.jpg') || - lowerCaseFileName.endsWith('.jpeg') || - lowerCaseFileName.endsWith('.png') - ) { - images.push(file) + + for (const fileExtension of burialSiteImageFileExtensions) { + if (lowerCaseFileName.endsWith(`.${fileExtension}`)) { + images.push(file) + break + } } } @@ -34,21 +48,53 @@ export async function getBurialSiteImages(): Promise { return burialSiteImages } -let cemeterySVGs: string[] | undefined +function clearCachedBurialSiteImages(): void { + debug('Burial site images folder changed.') + burialSiteImages = [] +} + +if (getConfigProperty('settings.burialSites.refreshImageChanges')) { + debug('Burial site images watcher enabled.') + + const burialSitesWatcher = chokidar.watch(burialSiteImagesFolder, { + ignoreInitial: true, + persistent: true + }) + + burialSitesWatcher.on('add', clearCachedBurialSiteImages) + burialSitesWatcher.on('change', clearCachedBurialSiteImages) + burialSitesWatcher.on('unlink', clearCachedBurialSiteImages) +} + +/* + * Cemetery SVGs + */ + +const cemeterySVGsFolder = path.join( + getConfigProperty('settings.publicInternalPath'), + 'images', + 'cemeteries' +) + +const cemeterySVGFileExtensions = ['svg'] + +let cemeterySVGs: string[] = [] export async function getCemeterySVGs(): Promise { - if (cemeterySVGs === undefined) { - const files = await fs.readdir(path.join( - getConfigProperty('settings.publicInternalPath'), - 'images', - 'cemeteries' - )) + if (cemeterySVGs.length === 0) { + // eslint-disable-next-line security/detect-non-literal-fs-filename + const files = await fs.readdir(cemeterySVGsFolder) const SVGs: string[] = [] for (const file of files) { - if (file.toLowerCase().endsWith('.svg')) { - SVGs.push(file) + const lowerCaseFileName = file.toLowerCase() + + for (const fileExtension of cemeterySVGFileExtensions) { + if (lowerCaseFileName.endsWith(`.${fileExtension}`)) { + SVGs.push(file) + break + } } } @@ -57,3 +103,21 @@ export async function getCemeterySVGs(): Promise { return cemeterySVGs } + +function clearCachedCemeterySVGs(): void { + debug('Cemetery SVGs folder changed.') + cemeterySVGs = [] +} + +if (getConfigProperty('settings.cemeteries.refreshImageChanges')) { + debug('Cemetery SVGs watcher enabled.') + + const cemeteryWatcher = chokidar.watch(cemeterySVGsFolder, { + ignoreInitial: true, + persistent: true + }) + + cemeteryWatcher.on('add', clearCachedCemeterySVGs) + cemeteryWatcher.on('change', clearCachedCemeterySVGs) + cemeteryWatcher.on('unlink', clearCachedCemeterySVGs) +} diff --git a/package-lock.json b/package-lock.json index eff3e5f1..65f2c750 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "better-sqlite3": "^11.9.1", "bulma-tooltip": "^3.0.2", "camelcase": "^8.0.0", + "chokidar": "^4.0.3", "compression": "^1.8.0", "cookie-parser": "^1.4.7", "cross-env": "^7.0.3", @@ -60,6 +61,7 @@ "@types/http-errors": "^2.0.4", "@types/leaflet": "^1.9.17", "@types/mssql": "^9.1.7", + "@types/node": "^22.14.1", "@types/node-windows": "^0.1.6", "@types/papaparse": "^5.3.15", "@types/randomcolor": "^0.5.9", @@ -2037,11 +2039,12 @@ } }, "node_modules/@types/node": { - "version": "18.18.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.13.tgz", - "integrity": "sha512-vXYZGRrSCreZmq1rEjMRLXJhiy8MrIeVasx+PCVlP414N7CJLHnMf+juVvjdprHyH+XRy3zKZLHeNueOpJCn0g==", + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.21.0" } }, "node_modules/@types/node-windows": { @@ -2606,6 +2609,7 @@ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3082,12 +3086,16 @@ } }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bindings": { @@ -3469,44 +3477,18 @@ } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">= 8.10.0" + "node": ">= 14.16.0" }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "funding": { + "url": "https://paulmillr.com/funding/" } }, "node_modules/chownr": { @@ -6269,6 +6251,21 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -7135,6 +7132,7 @@ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -9415,6 +9413,31 @@ "url": "https://opencollective.com/nodemon" } }, + "node_modules/nodemon/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/nodemon/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -9424,6 +9447,19 @@ "node": ">=4" } }, + "node_modules/nodemon/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/nodemon/node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -9477,6 +9513,7 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -10568,15 +10605,16 @@ "license": "MIT" }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", "engines": { - "node": ">=8.10.0" + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/refa": { @@ -12388,9 +12426,10 @@ "dev": true }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" }, "node_modules/unicorn-magic": { "version": "0.1.0", diff --git a/package.json b/package.json index 0ee3b347..4cd492c8 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "better-sqlite3": "^11.9.1", "bulma-tooltip": "^3.0.2", "camelcase": "^8.0.0", + "chokidar": "^4.0.3", "compression": "^1.8.0", "cookie-parser": "^1.4.7", "cross-env": "^7.0.3", @@ -83,6 +84,7 @@ "@types/http-errors": "^2.0.4", "@types/leaflet": "^1.9.17", "@types/mssql": "^9.1.7", + "@types/node": "^22.14.1", "@types/node-windows": "^0.1.6", "@types/papaparse": "^5.3.15", "@types/randomcolor": "^0.5.9", diff --git a/temp/legacyImportFromCsv/index.ts b/temp/legacyImportFromCsv/index.ts index f2cab908..19a36d49 100644 --- a/temp/legacyImportFromCsv/index.ts +++ b/temp/legacyImportFromCsv/index.ts @@ -485,7 +485,7 @@ async function importFromMasterCSV(): Promise { * Interment Record */ - let deceasedContractStartDateString: DateString | '' + let deceasedContractStartDateString: '' | DateString let deceasedContractId: number if (masterRow.CM_DECEASED_NAME !== '') { diff --git a/types/configTypes.d.ts b/types/configTypes.d.ts index fe906a7d..155bbdd2 100644 --- a/types/configTypes.d.ts +++ b/types/configTypes.d.ts @@ -30,8 +30,12 @@ export interface Config { fees: { taxPercentageDefault?: number; }; + cemeteries: { + refreshImageChanges?: boolean; + }; burialSites: { burialSiteNameSegments?: ConfigBurialSiteNameSegments; + refreshImageChanges?: boolean; }; contracts: { burialSiteIdIsRequired?: boolean; diff --git a/types/configTypes.ts b/types/configTypes.ts index ba2139cc..722b0de6 100644 --- a/types/configTypes.ts +++ b/types/configTypes.ts @@ -42,8 +42,13 @@ export interface Config { taxPercentageDefault?: number } + cemeteries: { + refreshImageChanges?: boolean + } + burialSites: { burialSiteNameSegments?: ConfigBurialSiteNameSegments + refreshImageChanges?: boolean } contracts: { diff --git a/types/recordTypes.d.ts b/types/recordTypes.d.ts index 0057b0cf..40c6c448 100644 --- a/types/recordTypes.d.ts +++ b/types/recordTypes.d.ts @@ -14,6 +14,8 @@ export interface BurialSite extends Record { cemeteryName?: string; cemeterySvg?: string; cemeterySvgId?: string; + cemeteryLatitude?: number; + cemeteryLongitude?: number; burialSiteImage?: string; burialSiteLatitude?: number; burialSiteLongitude?: number; @@ -69,6 +71,9 @@ export interface Cemetery extends Record { cemeteryName: string; parentCemeteryId?: number | null; parentCemeteryName?: string | null; + parentCemeteryLatitude?: number | null; + parentCemeteryLongitude?: number | null; + parentCemeterySvg?: string | null; cemeteryLatitude?: number; cemeteryLongitude?: number; cemeterySvg?: string; diff --git a/types/recordTypes.ts b/types/recordTypes.ts index a35f2603..f072bce2 100644 --- a/types/recordTypes.ts +++ b/types/recordTypes.ts @@ -19,6 +19,9 @@ export interface BurialSite extends Record { cemeterySvg?: string cemeterySvgId?: string + cemeteryLatitude?: number + cemeteryLongitude?: number + burialSiteImage?: string burialSiteLatitude?: number @@ -96,6 +99,10 @@ export interface Cemetery extends Record { parentCemeteryId?: number | null parentCemeteryName?: string | null + + parentCemeteryLatitude?: number | null + parentCemeteryLongitude?: number | null + parentCemeterySvg?: string | null cemeteryLatitude?: number cemeteryLongitude?: number diff --git a/views/admin-tables.ejs b/views/admin-tables.ejs index 14b8960f..1618a7d1 100644 --- a/views/admin-tables.ejs +++ b/views/admin-tables.ejs @@ -25,6 +25,13 @@

Config Table Management

+ +
+
+ Never change the meaning of the values in these tables.
+ When in doubt, create a new value instead of changing an existing one. +
+
    diff --git a/views/burialSite-view.ejs b/views/burialSite-view.ejs index 421201f8..c26bff0d 100644 --- a/views/burialSite-view.ejs +++ b/views/burialSite-view.ejs @@ -113,6 +113,14 @@
    <% if (burialSite.burialSiteLatitude && burialSite.burialSiteLongitude) { %>
    + <% } else if (burialSite.cemeteryLatitude && burialSite.cemeteryLongitude) { %> +
    +

    + Coordinates not available for this burial site.
    + Coordinates for the cemetery are shown instead. +

    +
    +
    <% } else { %>

    diff --git a/views/cemetery-view.ejs b/views/cemetery-view.ejs index 0dc6af43..1e4cada9 100644 --- a/views/cemetery-view.ejs +++ b/views/cemetery-view.ejs @@ -121,6 +121,14 @@

    <% if (cemetery.cemeteryLatitude && cemetery.cemeteryLongitude) { %>
    + <% } else if (cemetery.parentCemeteryLatitude && cemetery.parentCemeteryLongitude) { %> +
    +

    + Coordinates not available for this child cemtery.
    + Coordinates for the parent cemetery are shown instead. +

    +
    +
    <% } else { %>