diff --git a/src/helpers/capability.ts b/src/helpers/capability.ts index 650c9cb..f6d7cea 100644 --- a/src/helpers/capability.ts +++ b/src/helpers/capability.ts @@ -37,8 +37,21 @@ class Settings { } public reloadSettings() { - this.settings = JSON.parse(readFileSync('./config/options.json', 'utf-8')) - this.optionsToFlags() + try { + this.settings = JSON.parse(readFileSync('./config/options.json', 'utf-8')) + this.optionsToFlags() + } catch (error) { + if (error instanceof Error) { + if ('code' in error) { + if (error.code == "ENOENT") { + console.warn("WARNING: Capability file 'options.json' not found, enabling all modules.") + console.info("INFO: This warning will dissapear in future release of this program.") + this.settings = { clean: true, groups: true, key: true, menu: true, news: true, notif: true } + this.optionsToFlags() + } + } + } + } } public get flags() : number { diff --git a/src/helpers/filehandler.ts b/src/helpers/filehandler.ts new file mode 100644 index 0000000..f07625b --- /dev/null +++ b/src/helpers/filehandler.ts @@ -0,0 +1,42 @@ +import { project } from "@/utility"; +import { PathOrFileDescriptor, readFileSync, writeFileSync } from "node:fs"; + +export class FileHandler { + protected _value: T + public get value(): T { + return this._value; + } + public set value(value: T) { + this._value = value + this.save() + } + + constructor(public path: PathOrFileDescriptor, public settings?: { + defaultContent?: T, + name?: string, + project?: (keyof T)[] | { [key in keyof T]: any} + }) { + try { + this._value = JSON.parse(readFileSync(path, 'utf-8')) + console.log("Loaded user settings"); + } catch (error) { + if (error instanceof Error) { + if ('code' in error) { + if (error.code === "ENOENT") { + writeFileSync(path, JSON.stringify(settings.defaultContent, undefined, 2)) + console.log(`Created ${settings.name}`); + } + } + } + } + } + + private save() { + writeFileSync(this.path, JSON.stringify(project(this._value, this.settings.project), undefined, 2)) + } + + public reload() { + this._value = JSON.parse(readFileSync(this.path, { encoding: "utf-8" })) + console.log(`Reloaded ${this.settings.name}`); + } +} \ No newline at end of file diff --git a/src/helpers/security.ts b/src/helpers/security.ts index 741e8a6..026fbf5 100644 --- a/src/helpers/security.ts +++ b/src/helpers/security.ts @@ -18,8 +18,8 @@ class SecurityHelper { if (this.timeouts.has(uid)) { var t = this.timeouts.get(uid) t.attempts += 1 - if (t.attempts > usettings.settings.security.loginTimeout.attempts) { - this.onTimeout.set(uid, scheduleJob(new Date(Date.now() + usettings.settings.security.loginTimeout.lockout * 1000), () => { + if (t.attempts > usettings.value.security.loginTimeout.attempts) { + this.onTimeout.set(uid, scheduleJob(new Date(Date.now() + usettings.value.security.loginTimeout.lockout * 1000), () => { this.onTimeout.get(uid).cancel() this.onTimeout.delete(uid) })) @@ -30,7 +30,7 @@ class SecurityHelper { this.timeouts.set(uid, { attempts: 1, firstAttempt: new Date(), - expire: scheduleJob(new Date(Date.now() + usettings.settings.security.loginTimeout.time * 1000), () => { + expire: scheduleJob(new Date(Date.now() + usettings.value.security.loginTimeout.time * 1000), () => { this.timeouts.get(uid).expire.cancel() this.timeouts.delete(uid) }) diff --git a/src/helpers/usettings.ts b/src/helpers/usettings.ts index 14626a1..3777fd8 100644 --- a/src/helpers/usettings.ts +++ b/src/helpers/usettings.ts @@ -1,5 +1,6 @@ import { project } from "@/utility"; import { readFileSync, writeFileSync } from "node:fs"; +import { FileHandler } from "./filehandler"; export interface IUSettings { keyrooms: string[]; @@ -15,32 +16,32 @@ export interface IUSettings { loginTimeout: { attempts: number; time: number; - lockout: number; + lockout: number; } } } -class UOptions { - private _settings: IUSettings; - public get settings(): IUSettings { - return this._settings; - } - public set settings(value: IUSettings) { - this._settings = project(value, ['cleanThings', 'keyrooms', 'menu', 'rooms', 'security']) as typeof value - this.save() - } - +class UOptions extends FileHandler { constructor() { - this.reload() - } - - private save() { - writeFileSync("./config/usettings.json", JSON.stringify(this._settings, undefined, 2)) - } - - reload() { - this.settings = JSON.parse(readFileSync("./config/usettings.json", {encoding: "utf-8"})) - console.log("Reloaded user settings"); + const defaultSettings: IUSettings = { + keyrooms: [], + rooms: [], + cleanThings: [], + menu: { + defaultItems: { + sn: [], + kol: [], + } + }, + security: { + loginTimeout: { + attempts: 0, + time: 0, + lockout: 0 + } + } + } + super("./config/usettings.json", {defaultContent: defaultSettings, name: "user settings", project: ['cleanThings', 'keyrooms', 'menu', 'rooms', 'security']}) } } diff --git a/src/routes/api/admin/clean.ts b/src/routes/api/admin/clean.ts index 29f8975..1113eee 100644 --- a/src/routes/api/admin/clean.ts +++ b/src/routes/api/admin/clean.ts @@ -65,8 +65,8 @@ cleanRouter.delete("/:id", async (req, res) => { cleanRouter.get('/config', (req, res) => { res.send({ - rooms: usettings.settings.rooms, - things: usettings.settings.cleanThings + rooms: usettings.value.rooms, + things: usettings.value.cleanThings }) }) @@ -88,7 +88,7 @@ cleanRouter.delete('/attendence/:room', async (req, res) => { }) cleanRouter.get('/attendenceSummary', async (req, res) => { - var allRooms = usettings.settings.rooms + var allRooms = usettings.value.rooms var graded = (await Grade.find({date: new Date().setUTCHours(24,0,0,0)})).map(v => v.room) var ungraded = allRooms.filter(x => !graded.includes(x)) var summary = attendence.summary() diff --git a/src/routes/api/admin/keys.ts b/src/routes/api/admin/keys.ts index 9c6f195..dc4ab7d 100644 --- a/src/routes/api/admin/keys.ts +++ b/src/routes/api/admin/keys.ts @@ -34,7 +34,7 @@ keysRouter.post("/", async (req, res) => { keysRouter.get("/available", async (req, res) => { var taken = await Key.find({tb: {$exists: false}}, {}, {sort: {borrow: -1}}) var occ = Array.from(new Set(taken.map((v) => v.room))) - var all = Array.from(new Set(usettings.settings.keyrooms)) + var all = Array.from(new Set(usettings.value.keyrooms)) var free = all.filter(x => !occ.includes(x)) res.send(free) }) diff --git a/src/routes/api/admin/menu.ts b/src/routes/api/admin/menu.ts index 7886fbf..4083ea9 100644 --- a/src/routes/api/admin/menu.ts +++ b/src/routes/api/admin/menu.ts @@ -62,7 +62,7 @@ menuRouter.get('/print', async (req, res) => { var meals = await Menu.find({day: {$gte: start, $lte: end}}, undefined, {sort: {day: 1}}) var doc = meals.map(s => ` ${dayName(s.day)}
${s.day.getDate()}.${s.day.getMonth()}.${s.day.getFullYear()}r.
${s.dayTitle} - ${usettings.settings.menu.defaultItems.sn.join('
')}
${s.sn.fancy.join('
')}
${s.sn.second} + ${usettings.value.menu.defaultItems.sn.join('
')}
${s.sn.fancy.join('
')}
${s.sn.second} Z: ${s.ob.soup}
V: ${s.ob.vege}
@@ -71,7 +71,7 @@ menuRouter.get('/print', async (req, res) => { ${s.ob.drink}
${s.ob.other.join('
')} - ${s.day.getUTCDay() == 5 ? "Kolacja w domu!" : `${usettings.settings.menu.defaultItems.kol.join('
')}
${s.kol}`} + ${s.day.getUTCDay() == 5 ? "Kolacja w domu!" : `${usettings.value.menu.defaultItems.kol.join('
')}
${s.kol}`} `) var html = `${doc.join('\n')}
Jadłospis dekadowy
DzieńŚniadanieObiadKolacja
` res.type('html').send(html) diff --git a/src/routes/api/admin/settings.ts b/src/routes/api/admin/settings.ts index 6b2192a..c3f8f5a 100644 --- a/src/routes/api/admin/settings.ts +++ b/src/routes/api/admin/settings.ts @@ -7,11 +7,11 @@ export const settingsRouter = Router() settingsRouter.use(adminPerm(Perms.Superadmin)) settingsRouter.get('/', (req, res) => { - res.send(usettings.settings) + res.send(usettings.value) }) settingsRouter.post('/', (req, res) => { - usettings.settings = req.body + usettings.value = req.body res.send({status: 200}) }) diff --git a/src/routes/api/app/index.ts b/src/routes/api/app/index.ts index be3642b..1ef518f 100644 --- a/src/routes/api/app/index.ts +++ b/src/routes/api/app/index.ts @@ -62,7 +62,7 @@ appRouter.post("/menu/:timestamp", capability.mw(Features.Menu), async (req, res appRouter.get("/keys", capability.mw(Features.Key), async (req, res) => { var keys = await Key.find>({tb: {$exists: false}}, {room: 1}, {sort: {room: 1}}) var occ = keys.map(x=>x.room) - var all = usettings.settings.keyrooms + var all = usettings.value.keyrooms var free = all.filter(x=>!occ.includes(x)).sort().map(x => { return { room: x } }) diff --git a/src/routes/auth/index.ts b/src/routes/auth/index.ts index 2528f8a..a9f4947 100644 --- a/src/routes/auth/index.ts +++ b/src/routes/auth/index.ts @@ -82,7 +82,7 @@ authRouter.get("/check", islogged, (req, res, next) => { res.status(401).send({status: 401, message: "Your account has been locked."}) }) } - res.send({"admin": req.user.admin, "features": cap.flags, "room": req.user.room, "menu": {"defaultItems": usettings.settings.menu.defaultItems}, "vapid": vapidKeys.keys.publicKey}) + res.send({"admin": req.user.admin, "features": cap.flags, "room": req.user.room, "menu": {"defaultItems": usettings.value.menu.defaultItems}, "vapid": vapidKeys.keys.publicKey}) }) authRouter.put("/redirect", islogged, async (req, res) => { diff --git a/src/utility.ts b/src/utility.ts index cf676e7..716e645 100644 --- a/src/utility.ts +++ b/src/utility.ts @@ -38,18 +38,22 @@ var adminCond = (adminInt = 0, perm: Perms) => { return (adminInt & perm) == perm } -export function project(obj: T | any, projection: (keyof T)[] | { [key in keyof T]: any}): Partial { +export function project(obj: T | any, projection?: (keyof T)[] | { [key in keyof T]: any}): Partial { let obj2: Partial = {} - if (projection instanceof Array) { - for (let key of projection) { - if (key in obj) obj2[key] = obj[key] + if (projection) { + if (projection instanceof Array) { + for (let key of projection) { + if (key in obj) obj2[key] = obj[key] + } + } else { + for (let key in projection) { + if (key in obj) obj2[key] = obj[key] + } } + return obj2 } else { - for (let key in projection) { - if (key in obj) obj2[key] = obj[key] - } + return obj } - return obj2 } export {islogged, isadmin, adminPerm, Perms, adminCond}; \ No newline at end of file diff --git a/src/vapidKeys.ts b/src/vapidKeys.ts index b0426e4..3c7291a 100644 --- a/src/vapidKeys.ts +++ b/src/vapidKeys.ts @@ -20,11 +20,22 @@ class VapidKeysSettings { } reload() { - this._keys = JSON.parse(readFileSync("./config/keys.json", {encoding: "utf-8"})) - if (!(this._keys.privateKey && this._keys.publicKey)) { - this.keys = generateVAPIDKeys() + try { + this._keys = JSON.parse(readFileSync("./config/keys.json", {encoding: "utf-8"})) + } catch (error) { + if (error instanceof Error) { + if ('code' in error) { + if (error.code === "ENOENT") { + this.keys = generateVAPIDKeys(); + } + } + } + } finally { + if (!(this.keys.privateKey && this.keys.publicKey)) { + this.keys = generateVAPIDKeys() + } + console.log("Reloaded VAPID keys"); } - console.log("Reloaded VAPID keys"); } }