diff --git a/src/client/css/map.css b/src/client/css/map.css index b2646a7..264d09a 100644 --- a/src/client/css/map.css +++ b/src/client/css/map.css @@ -28,7 +28,7 @@ filter: drop-shadow(0px 0px 3px var(--neutral)); } -.marker-cluster-small[class] { /* overwrite default cluster style */ +.marker-cluster[class] { /* overwrite default cluster style */ background: none; div { background-color: var(--semiContrastBackground); diff --git a/src/controller/read.ts b/src/controller/read.ts index bd4b88c..4106251 100644 --- a/src/controller/read.ts +++ b/src/controller/read.ts @@ -17,7 +17,10 @@ router.get('/', return createError(res, 400, JSON.stringify({ errors: errors.array() }), next) } - const fileObj: File.Obj = file.getFile(res, next); + const fileObj: File.Obj = file.getFile(res, next, "read"); + // no content and no file found show empty data and exit + if (fileObj.content == false) { res.json(JSON.parse('{"entries": []}')); return } + fileObj.content = await file.readAsJson(res, fileObj.path, next) if (!fileObj.content || !Array.isArray(fileObj.content.entries)) { return createError(res, undefined, `File corrupt: ${fileObj.path}`, next); diff --git a/src/models/entry.ts b/src/models/entry.ts index 34dbb75..799177b 100644 --- a/src/models/entry.ts +++ b/src/models/entry.ts @@ -13,12 +13,13 @@ import logger from '@src/scripts/logger'; export const entry = { create: async (req: Request, res: Response, next: NextFunction) => { - const fileObj: File.Obj = file.getFile(res, next); + const fileObj: File.Obj = file.getFile(res, next, "write"); + if (!fileObj.content) { return createError(res, 500, "File does not exist: " + fileObj.path, next); } + fileObj.content = await file.readAsJson(res, fileObj.path, next); - if (!fileObj.content?.entries) { - return createError(res, 500, "File Content unavailable: " + fileObj.path, next); - } + if (!fileObj.content?.entries) {return createError(res, 500, "File Content unavailable: " + fileObj.path, next); } + const entries = fileObj.content.entries; const lastEntry = fileObj.content.entries.at(-1); let previousEntry = fileObj.content.entries.at(-1); // potentially overwritten if entry is set to ignore diff --git a/src/scripts/file.ts b/src/scripts/file.ts index a8b693b..fc8d013 100644 --- a/src/scripts/file.ts +++ b/src/scripts/file.ts @@ -5,11 +5,11 @@ import { create as createError } from '@src/middleware/error'; import { NextFunction, Response } from 'express'; import logger from '@src/scripts/logger'; -export const getFile = (res: Response, next: NextFunction): File.Obj => { +export const getFile = (res: Response, next: NextFunction, method: File.method): File.Obj => { const date = new Date(); const formattedDate = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`; const dirPath = path.resolve(__dirname, '../data'); - const filePath = path.resolve(dirPath, `data-${formattedDate}.json`); + let filePath = path.resolve(dirPath, `data-${formattedDate}.json`); if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); @@ -17,17 +17,24 @@ export const getFile = (res: Response, next: NextFunction): File.Obj => { } let fileExisted = true; - if (!fs.existsSync(filePath)) { // check if file exist + let olderFile = false; + if (!fs.existsSync(filePath)) { // if file does not exist fileExisted = false; - try { - fs.writeFileSync(filePath, '{"entries": []}'); - logger.log(`file: ${filePath} did not exist, but created now`); - } catch (err) { - createError(res, 500, "File cannot be written to", next); + const mostRecentFile = findMostRecentFile(dirPath); + if (method == "read" && mostRecentFile) { // when reading check old files + olderFile = true; + filePath = mostRecentFile; + } else if (method == "write") { + try { + fs.writeFileSync(filePath, '{"entries": []}'); + logger.log(`file: ${filePath} did not exist, but created now`); + } catch (err) { + createError(res, 500, "File cannot be written to", next); + } } } - return { path: filePath, content: fileExisted ? undefined : JSON.parse('{"entries": []}') }; // if the file did not exist before, the content is emptyString + return { path: filePath, content: method == "read" ? (fileExisted || olderFile) : JSON.parse('{"entries": []}') }; }; @@ -39,24 +46,58 @@ export async function readAsJson(res: Response, filePath: string, next: NextFunc try { return JSON.parse(data); } catch (err) { - createError(res, 500, "File contains wrong content: " + filePath, next); + createError(res, 500, "File contains wrong content: " + path.basename(filePath), next); } } -export const write = (res:Response, fileObj:File.Obj, next: NextFunction) => { +export const write = (res: Response, fileObj: File.Obj, next: NextFunction) => { if (!fs.existsSync(fileObj.path)) { // check if file exist createError(res, 500, "Can not write to file that does not exist: " + fileObj.path, next); } + + if (typeof fileObj.content == "boolean") { + createError(res, 500, `File (${fileObj.path}) cannot be written to, contents are not correct type`, next); + } + try { const content = JSON.stringify(fileObj.content, undefined, 2); fs.writeFileSync(fileObj.path, content); fileObj.content = JSON.parse(content); - logger.log(`written to file: ${fileObj.path} ${fileObj.content ? fileObj.content?.entries.length - 1 : ''}`); + if (typeof fileObj.content != "boolean") { + logger.log(`written to file: ${fileObj.path} ${fileObj.content?.entries ? fileObj.content.entries.length - 1 : ''}`); + } } catch (err) { createError(res, 500, `File (${fileObj.path}) cannot be written to`, next); - } + } + - return fileObj; // if the file did not exist before, the content is emptyString + return fileObj; }; + +const findMostRecentFile = (directoryPath: string) => { + // read all files from the directory + const files = fs.readdirSync(directoryPath); + + // initialize variables to keep track of the most recent file + let mostRecentFile = null; + let mostRecentTime = 0; + + files.forEach((file) => { + const filePath = path.join(directoryPath, file); + + // get file stats (including modified time) + const stats = fs.statSync(filePath); + + // check if it is a file (and not a directory) and most recent + if (stats.isFile() && stats.mtimeMs > mostRecentTime) { + if (stats.mtimeMs > mostRecentTime) { + mostRecentTime &&= stats.mtimeMs; + mostRecentFile = filePath; + } + } + }); + + return mostRecentFile; +} diff --git a/src/tests/integration.test.ts b/src/tests/integration.test.ts index e79be6f..9cad2b5 100644 --- a/src/tests/integration.test.ts +++ b/src/tests/integration.test.ts @@ -223,7 +223,7 @@ describe('API calls', () => { const response = await axios.get(url); expect(response.status).toBe(200); } - }, 20000); // adjust this to to fit your setup + }, 22000); // adjust this to to fit your setup test(`length of json should not exceed 1000`, async () => { const date = new Date(); diff --git a/types.d.ts b/types.d.ts index 0bab5de..f246bc4 100644 --- a/types.d.ts +++ b/types.d.ts @@ -24,8 +24,10 @@ namespace Response { namespace File { interface Obj { path: string, - content?: Models.IEntries; + content?: Models.IEntries | boolean; } + + type method = 'read' | 'write'; } namespace Models {