diff --git a/source/server/routes/api/v1/admin/mailtest.ts b/source/server/routes/api/v1/admin/mailtest.ts index 44471427..608c0abf 100644 --- a/source/server/routes/api/v1/admin/mailtest.ts +++ b/source/server/routes/api/v1/admin/mailtest.ts @@ -1,7 +1,6 @@ import { Request, Response } from "express"; import sendmail from "../../../../utils/mails/send.js"; -import { getUser } from "../../../../utils/locals.js"; -import config from "../../../../utils/config.js"; +import { getLocals, getUser } from "../../../../utils/locals.js"; import { BadRequestError } from "../../../../utils/errors.js"; /** @@ -10,6 +9,7 @@ import { BadRequestError } from "../../../../utils/errors.js"; * This is a protected route and requires admin privileges */ export default async function handleMailtest(req :Request, res :Response){ + const {config} = getLocals(req); const {username :requester, email:to} = getUser(req); if(!to){ throw new BadRequestError("No email address found for user "+ requester); diff --git a/source/server/routes/api/v1/login.ts b/source/server/routes/api/v1/login.ts index 16326218..f9493a39 100644 --- a/source/server/routes/api/v1/login.ts +++ b/source/server/routes/api/v1/login.ts @@ -2,7 +2,7 @@ import { createHmac } from "crypto"; import { Request, RequestHandler, Response } from "express"; import User, { SafeUser } from "../../../auth/User.js"; import { BadRequestError, ForbiddenError, NotFoundError, UnauthorizedError } from "../../../utils/errors.js"; -import { AppLocals, getHost, getUser, getUserManager } from "../../../utils/locals.js"; +import { AppLocals, getHost, getLocals, getUser, getUserManager } from "../../../utils/locals.js"; import sendmail from "../../../utils/mails/send.js"; /** * @@ -122,7 +122,7 @@ export async function sendLoginLink(req :Request, res :Response){ ); let lang = "fr"; - const mail_content = await (res.app.locals as AppLocals).templates.render(`emails/connection_${lang}`, { + const mail_content = await getLocals(req).templates.render(`emails/connection_${lang}`, { name: user.username, lang: "fr", url: link.toString() diff --git a/source/server/routes/scenes/propfind.ts b/source/server/routes/scenes/propfind.ts index 407e806d..d0e655d6 100644 --- a/source/server/routes/scenes/propfind.ts +++ b/source/server/routes/scenes/propfind.ts @@ -1,7 +1,7 @@ import { Request, RequestHandler, Response } from "express"; import xml from 'xml-js'; import path from "path"; -import { AppLocals, getHost, getUser } from "../../utils/locals.js"; +import { AppLocals, getHost, getLocals, getUser } from "../../utils/locals.js"; import Vfs, { FileProps, ItemProps } from "../../vfs/index.js"; interface ElementProps{ @@ -198,7 +198,7 @@ async function getScenes(vfs :Vfs, rootUrl:URL, recurse :number, user_id ?:numbe export async function handlePropfind(req :Request, res:Response){ let u = getUser(req); const {scene:scene_name} = req.params; - const {vfs} = req.app.locals as AppLocals; + const {vfs} = getLocals(req); let recurse = parseInt(req.get("Depth")??"-1"); if(!Number.isSafeInteger(recurse)) throw new Error("Invalid Depth header : "+req.get("Depth")); diff --git a/source/server/server.ts b/source/server/server.ts index a99f968d..4be0de83 100644 --- a/source/server/server.ts +++ b/source/server/server.ts @@ -48,6 +48,7 @@ export default async function createServer(config = defaultConfig) :Promise)=>AsyncGenerator; var expect :typeof chai["expect"]; - var createIntegrationContext :(c:Mocha.Context)=>Promise; + var createIntegrationContext :(c:Mocha.Context, config_override ?:Partial)=>Promise; var cleanIntegrationContext :(c:Mocha.Context)=>Promise; } @@ -30,11 +30,17 @@ global.dataStream = async function* (src :Array =["foo", "\n"]){ } } -global.createIntegrationContext = async function(c :Mocha.Context){ +global.createIntegrationContext = async function(c :Mocha.Context, config_override :Partial={}){ let {default:createServer} = await import("./server.js"); let titleSlug = c.currentTest?.title.replace(/[^\w]/g, "_") ?? `eThesaurus_integration_test`; c.dir = await fs.mkdtemp(path.join(tmpdir(), titleSlug)); - c.config = parse({ROOT_DIR: c.dir, CLEAN_DATABASE: "false", VERBOSE: "false", HOT_RELOAD: "false"}); + c.config = Object.assign( + parse({ //Common options + ROOT_DIR: c.dir, CLEAN_DATABASE: "false", VERBOSE: "false", HOT_RELOAD: "false", + }), + //Options we might want to customize + config_override + ); c.server = await createServer( c.config ); return c.server.locals; } diff --git a/source/server/utils/config.ts b/source/server/utils/config.ts index ef0e96e8..4f18eb69 100644 --- a/source/server/utils/config.ts +++ b/source/server/utils/config.ts @@ -25,7 +25,7 @@ const values = { type Key = keyof typeof values; type ValueType = ReturnType; -type Config = { +export type Config = { [T in Key]: typeof values[T][0] extends BuildKey? ReturnType : ValueType; } @@ -51,6 +51,9 @@ function toBool(s:string):boolean{ return !(!s || s.toLowerCase() === "false" || s == "0"); } +/** + * Parses a set of environment variables into a configuration object + */ export function parse(env :NodeJS.ProcessEnv = process.env):Config{ let c :Partial = {}; for(let [key, value] of Object.entries(values)){ diff --git a/source/server/utils/locals.ts b/source/server/utils/locals.ts index cf0d0350..484c35ec 100644 --- a/source/server/utils/locals.ts +++ b/source/server/utils/locals.ts @@ -6,6 +6,7 @@ import UserManager, { AccessType, AccessTypes } from "../auth/UserManager.js"; import Vfs, { GetFileParams } from "../vfs/index.js"; import { BadRequestError, ForbiddenError, HTTPError, InternalError, NotFoundError, UnauthorizedError } from "./errors.js"; import Templates from "./templates.js"; +import { Config } from "./config.js"; export interface AppLocals extends Record{ port :number; @@ -13,20 +14,25 @@ export interface AppLocals extends Record{ userManager :UserManager; vfs :Vfs; templates :Templates; + config: Config; +} + +export function getLocals(req :Request){ + return req.app.locals as AppLocals; } /** * @throws {InternalError} if app.locals.userManager is not defined for this request */ export function getUserManager(req :Request) :UserManager { - let userManager :UserManager = (req.app.locals as AppLocals).userManager; + let userManager :UserManager = getLocals(req).userManager; //istanbul ignore if if(!userManager) throw new InternalError("Badly configured app : userManager is not defined in app.locals"); return userManager } export function getFileDir(req :Request) :string{ - let fileDir = (req.app.locals as AppLocals).fileDir; + let fileDir = getLocals(req).fileDir; if(!fileDir) throw new InternalError("Badly configured app : fileDir is not a valid string"); return fileDir; } @@ -145,7 +151,7 @@ export function getFileParams(req :Request):GetFileParams{ } export function getVfs(req :Request){ - let vfs :Vfs = (req.app.locals as AppLocals).vfs; + let vfs :Vfs = getLocals(req).vfs; //istanbul ignore if if(!vfs) throw new InternalError("Badly configured app : vfs is not defined in app.locals"); return vfs;