fix: Added a check for missing config files. Added FileHandler class.
This commit is contained in:
@@ -37,8 +37,21 @@ class Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public reloadSettings() {
|
public reloadSettings() {
|
||||||
this.settings = JSON.parse(readFileSync('./config/options.json', 'utf-8'))
|
try {
|
||||||
this.optionsToFlags()
|
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 {
|
public get flags() : number {
|
||||||
|
|||||||
42
src/helpers/filehandler.ts
Normal file
42
src/helpers/filehandler.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { project } from "@/utility";
|
||||||
|
import { PathOrFileDescriptor, readFileSync, writeFileSync } from "node:fs";
|
||||||
|
|
||||||
|
export class FileHandler<T> {
|
||||||
|
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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,8 +18,8 @@ class SecurityHelper {
|
|||||||
if (this.timeouts.has(uid)) {
|
if (this.timeouts.has(uid)) {
|
||||||
var t = this.timeouts.get(uid)
|
var t = this.timeouts.get(uid)
|
||||||
t.attempts += 1
|
t.attempts += 1
|
||||||
if (t.attempts > usettings.settings.security.loginTimeout.attempts) {
|
if (t.attempts > usettings.value.security.loginTimeout.attempts) {
|
||||||
this.onTimeout.set(uid, scheduleJob(new Date(Date.now() + usettings.settings.security.loginTimeout.lockout * 1000), () => {
|
this.onTimeout.set(uid, scheduleJob(new Date(Date.now() + usettings.value.security.loginTimeout.lockout * 1000), () => {
|
||||||
this.onTimeout.get(uid).cancel()
|
this.onTimeout.get(uid).cancel()
|
||||||
this.onTimeout.delete(uid)
|
this.onTimeout.delete(uid)
|
||||||
}))
|
}))
|
||||||
@@ -30,7 +30,7 @@ class SecurityHelper {
|
|||||||
this.timeouts.set(uid, {
|
this.timeouts.set(uid, {
|
||||||
attempts: 1,
|
attempts: 1,
|
||||||
firstAttempt: new Date(),
|
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.get(uid).expire.cancel()
|
||||||
this.timeouts.delete(uid)
|
this.timeouts.delete(uid)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { project } from "@/utility";
|
import { project } from "@/utility";
|
||||||
import { readFileSync, writeFileSync } from "node:fs";
|
import { readFileSync, writeFileSync } from "node:fs";
|
||||||
|
import { FileHandler } from "./filehandler";
|
||||||
|
|
||||||
export interface IUSettings {
|
export interface IUSettings {
|
||||||
keyrooms: string[];
|
keyrooms: string[];
|
||||||
@@ -15,32 +16,32 @@ export interface IUSettings {
|
|||||||
loginTimeout: {
|
loginTimeout: {
|
||||||
attempts: number;
|
attempts: number;
|
||||||
time: number;
|
time: number;
|
||||||
lockout: number;
|
lockout: number;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UOptions {
|
class UOptions extends FileHandler<IUSettings> {
|
||||||
private _settings: IUSettings;
|
|
||||||
public get settings(): IUSettings {
|
|
||||||
return this._settings;
|
|
||||||
}
|
|
||||||
public set settings(value: IUSettings) {
|
|
||||||
this._settings = project<typeof value>(value, ['cleanThings', 'keyrooms', 'menu', 'rooms', 'security']) as typeof value
|
|
||||||
this.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.reload()
|
const defaultSettings: IUSettings = {
|
||||||
}
|
keyrooms: [],
|
||||||
|
rooms: [],
|
||||||
private save() {
|
cleanThings: [],
|
||||||
writeFileSync("./config/usettings.json", JSON.stringify(this._settings, undefined, 2))
|
menu: {
|
||||||
}
|
defaultItems: {
|
||||||
|
sn: [],
|
||||||
reload() {
|
kol: [],
|
||||||
this.settings = JSON.parse(readFileSync("./config/usettings.json", {encoding: "utf-8"}))
|
}
|
||||||
console.log("Reloaded user settings");
|
},
|
||||||
|
security: {
|
||||||
|
loginTimeout: {
|
||||||
|
attempts: 0,
|
||||||
|
time: 0,
|
||||||
|
lockout: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super("./config/usettings.json", {defaultContent: defaultSettings, name: "user settings", project: ['cleanThings', 'keyrooms', 'menu', 'rooms', 'security']})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,8 +65,8 @@ cleanRouter.delete("/:id", async (req, res) => {
|
|||||||
|
|
||||||
cleanRouter.get('/config', (req, res) => {
|
cleanRouter.get('/config', (req, res) => {
|
||||||
res.send({
|
res.send({
|
||||||
rooms: usettings.settings.rooms,
|
rooms: usettings.value.rooms,
|
||||||
things: usettings.settings.cleanThings
|
things: usettings.value.cleanThings
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ cleanRouter.delete('/attendence/:room', async (req, res) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
cleanRouter.get('/attendenceSummary', 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 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 ungraded = allRooms.filter(x => !graded.includes(x))
|
||||||
var summary = attendence.summary()
|
var summary = attendence.summary()
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ keysRouter.post("/", async (req, res) => {
|
|||||||
keysRouter.get("/available", async (req, res) => {
|
keysRouter.get("/available", async (req, res) => {
|
||||||
var taken = await Key.find({tb: {$exists: false}}, {}, {sort: {borrow: -1}})
|
var taken = await Key.find({tb: {$exists: false}}, {}, {sort: {borrow: -1}})
|
||||||
var occ = Array.from(new Set(taken.map((v) => v.room)))
|
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))
|
var free = all.filter(x => !occ.includes(x))
|
||||||
res.send(free)
|
res.send(free)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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 meals = await Menu.find({day: {$gte: start, $lte: end}}, undefined, {sort: {day: 1}})
|
||||||
var doc = meals.map(s => `<tr>
|
var doc = meals.map(s => `<tr>
|
||||||
<th>${dayName(s.day)}<br>${s.day.getDate()}.${s.day.getMonth()}.${s.day.getFullYear()}r.<br>${s.dayTitle}</th>
|
<th>${dayName(s.day)}<br>${s.day.getDate()}.${s.day.getMonth()}.${s.day.getFullYear()}r.<br>${s.dayTitle}</th>
|
||||||
<td>${usettings.settings.menu.defaultItems.sn.join('<br>')}<br>${s.sn.fancy.join('<br>')}<br>${s.sn.second}</td>
|
<td>${usettings.value.menu.defaultItems.sn.join('<br>')}<br>${s.sn.fancy.join('<br>')}<br>${s.sn.second}</td>
|
||||||
<td>
|
<td>
|
||||||
<b>Z:</b> ${s.ob.soup}<br>
|
<b>Z:</b> ${s.ob.soup}<br>
|
||||||
<b>V:</b> ${s.ob.vege}<br>
|
<b>V:</b> ${s.ob.vege}<br>
|
||||||
@@ -71,7 +71,7 @@ menuRouter.get('/print', async (req, res) => {
|
|||||||
${s.ob.drink}<br>
|
${s.ob.drink}<br>
|
||||||
${s.ob.other.join('<br>')}
|
${s.ob.other.join('<br>')}
|
||||||
</td>
|
</td>
|
||||||
<td>${s.day.getUTCDay() == 5 ? "<b>Kolacja w domu!</b>" : `${usettings.settings.menu.defaultItems.kol.join('<br>')}<br>${s.kol}`}</td>
|
<td>${s.day.getUTCDay() == 5 ? "<b>Kolacja w domu!</b>" : `${usettings.value.menu.defaultItems.kol.join('<br>')}<br>${s.kol}`}</td>
|
||||||
</tr>`)
|
</tr>`)
|
||||||
var html = `<html><head><meta charset="UTF-8"><style>table,th,td{border: 0.4ch solid;}td{line-height: 1;}</style></head><body><table><caption>Jadłospis dekadowy</caption><thead><tr><th>Dzień</th><th>Śniadanie</th><th>Obiad</th><th>Kolacja</th></tr></thead><tbody>${doc.join('\n')}</tbody></table></body></html>`
|
var html = `<html><head><meta charset="UTF-8"><style>table,th,td{border: 0.4ch solid;}td{line-height: 1;}</style></head><body><table><caption>Jadłospis dekadowy</caption><thead><tr><th>Dzień</th><th>Śniadanie</th><th>Obiad</th><th>Kolacja</th></tr></thead><tbody>${doc.join('\n')}</tbody></table></body></html>`
|
||||||
res.type('html').send(html)
|
res.type('html').send(html)
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ export const settingsRouter = Router()
|
|||||||
settingsRouter.use(adminPerm(Perms.Superadmin))
|
settingsRouter.use(adminPerm(Perms.Superadmin))
|
||||||
|
|
||||||
settingsRouter.get('/', (req, res) => {
|
settingsRouter.get('/', (req, res) => {
|
||||||
res.send(usettings.settings)
|
res.send(usettings.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
settingsRouter.post('/', (req, res) => {
|
settingsRouter.post('/', (req, res) => {
|
||||||
usettings.settings = req.body
|
usettings.value = req.body
|
||||||
res.send({status: 200})
|
res.send({status: 200})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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) => {
|
appRouter.get("/keys", capability.mw(Features.Key), async (req, res) => {
|
||||||
var keys = await Key.find<Pick<IKey, "room">>({tb: {$exists: false}}, {room: 1}, {sort: {room: 1}})
|
var keys = await Key.find<Pick<IKey, "room">>({tb: {$exists: false}}, {room: 1}, {sort: {room: 1}})
|
||||||
var occ = keys.map(x=>x.room)
|
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 => {
|
var free = all.filter(x=>!occ.includes(x)).sort().map(x => {
|
||||||
return { room: x }
|
return { room: x }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ authRouter.get("/check", islogged, (req, res, next) => {
|
|||||||
res.status(401).send({status: 401, message: "Your account has been locked."})
|
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) => {
|
authRouter.put("/redirect", islogged, async (req, res) => {
|
||||||
|
|||||||
@@ -38,18 +38,22 @@ var adminCond = (adminInt = 0, perm: Perms) => {
|
|||||||
return (adminInt & perm) == perm
|
return (adminInt & perm) == perm
|
||||||
}
|
}
|
||||||
|
|
||||||
export function project<T extends object>(obj: T | any, projection: (keyof T)[] | { [key in keyof T]: any}): Partial<T> {
|
export function project<T extends object>(obj: T | any, projection?: (keyof T)[] | { [key in keyof T]: any}): Partial<T> {
|
||||||
let obj2: Partial<T> = {}
|
let obj2: Partial<T> = {}
|
||||||
if (projection instanceof Array) {
|
if (projection) {
|
||||||
for (let key of projection) {
|
if (projection instanceof Array) {
|
||||||
if (key in obj) obj2[key] = obj[key]
|
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 {
|
} else {
|
||||||
for (let key in projection) {
|
return obj
|
||||||
if (key in obj) obj2[key] = obj[key]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return obj2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export {islogged, isadmin, adminPerm, Perms, adminCond};
|
export {islogged, isadmin, adminPerm, Perms, adminCond};
|
||||||
@@ -20,11 +20,22 @@ class VapidKeysSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reload() {
|
reload() {
|
||||||
this._keys = JSON.parse(readFileSync("./config/keys.json", {encoding: "utf-8"}))
|
try {
|
||||||
if (!(this._keys.privateKey && this._keys.publicKey)) {
|
this._keys = JSON.parse(readFileSync("./config/keys.json", {encoding: "utf-8"}))
|
||||||
this.keys = generateVAPIDKeys()
|
} 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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user