diff --git a/main.ts b/main.ts index 5147443..b83fee4 100644 --- a/main.ts +++ b/main.ts @@ -43,7 +43,6 @@ const CSSPlugin: Plugin = { }; }, }; -await prepareSecretKey(); async function startServer(manifest: Manifest, options: StartOptions = {}) { const ctx = await ServerContext.fromManifest(manifest, options); @@ -86,7 +85,10 @@ if (import.meta.main) { .option("--db-path ", "The path to the database file.", { default: ":memory:", }) - .option("--id-password ", "The password to use. (Not recommended). id:password format.") + .option( + "--id-password ", + "The password to use. (Not recommended). id:password format.", + ) .arguments("[hostname:string]") .action(async ({ debug, port, auth, dbPath, idPassword }, hostname) => { hostname ??= "localhost"; @@ -98,7 +100,7 @@ if (import.meta.main) { } if (idPassword) { Deno.env.set("AUTH_REQUIRED", "true"); - const db = connectDB(); + const db = await connectDB(); const [username, password] = idPassword.split(":"); const new_user = await users.createUser(username, password); await users.addUser(db, new_user); @@ -106,6 +108,7 @@ if (import.meta.main) { if (debug) { console.log("Debug mode enabled."); } + await prepareSecretKey(); await prepareDocs(); await start({ port: port, diff --git a/routes/_middleware.ts b/routes/_middleware.ts index 8184a8f..63c7eb5 100644 --- a/routes/_middleware.ts +++ b/routes/_middleware.ts @@ -3,12 +3,11 @@ import { getCookies } from "http/cookie.ts"; import { verify } from "djwt"; import { prepareSecretKey } from "../util/secret.ts"; -const secret_key = await prepareSecretKey(); - export const handler = async ( req: Request, ctx: MiddlewareHandlerContext>, ) => { + const secret_key = await prepareSecretKey(); const cookies = getCookies(req.headers); const jwt = cookies["auth"]; try { diff --git a/routes/api/login.ts b/routes/api/login.ts index 8a8a0a6..0d9c3f1 100644 --- a/routes/api/login.ts +++ b/routes/api/login.ts @@ -6,17 +6,16 @@ import { getUser, verifyUser } from "../../src/user/user.ts"; import { create as createJWT } from "djwt"; import { prepareSecretKey } from "../../util/secret.ts"; -const SECRET_KEY = await prepareSecretKey(); - -async function POST(req: Request, ctx: HandlerContext): Promise { +async function POST(req: Request, _ctx: HandlerContext): Promise { const url = new URL(req.url); const form = await req.formData(); const username = form.get("username"); const password = form.get("password"); if (username && password) { - const DB = connectDB(); + const DB = await connectDB(); const user = await getUser(DB, username.toString()); if (user) { + const SECRET_KEY = await prepareSecretKey(); if (await verifyUser(user, password.toString())) { const headers = new Headers(); const jwt = await createJWT({ alg: "HS512", typ: "JWT" }, { diff --git a/src/user/db.ts b/src/user/db.ts index 4dec24d..38032a8 100644 --- a/src/user/db.ts +++ b/src/user/db.ts @@ -1,13 +1,31 @@ import { DB } from "sqlite"; import { createSchema } from "./user.ts"; -export function connectDB(): DB { +let inmemoryDB: DB | undefined; +let previousPath = ""; +let dbCache: DB | undefined; + +export async function connectDB(): Promise { let DB_path = Deno.env.get("DB_PATH"); - if (DB_path === undefined) { - Deno.env.set("DB_PATH", "./db.sqlite"); - DB_path = "./db.sqlite"; + if (DB_path === ":memory:") { + if (inmemoryDB === undefined) { + inmemoryDB = new DB(":memory:"); + await createSchema(inmemoryDB); + } + return inmemoryDB; + } else if (DB_path === undefined) { + Deno.env.set("DB_PATH", "db.sqlite"); + DB_path = "db.sqlite"; } - let db = new DB(DB_path); - createSchema(db); + if (dbCache !== undefined && DB_path === previousPath) { + return dbCache; + } else { + dbCache?.close(); + dbCache = undefined; + } + const db = new DB(DB_path); + previousPath = DB_path; + dbCache = db; + await createSchema(db); return db; } diff --git a/user.ts b/user.ts index 907527f..9473380 100644 --- a/user.ts +++ b/user.ts @@ -24,7 +24,7 @@ export const user_command = new Command() Deno.exit(1); } } - const db = connectDB(); + const db = await connectDB(); const new_user = await users.createUser(username, password); await users.addUser(db, new_user); if (!quiet) { @@ -35,7 +35,7 @@ export const user_command = new Command() .arguments("") .option("-q, --quiet", "Quiet output.") .action(async ({ quiet }, username) => { - const db = connectDB(); + const db = await connectDB(); await users.deleteUser(db, username); if (!quiet) { console.log(`Deleting user ${username}`); @@ -43,7 +43,7 @@ export const user_command = new Command() }) .command("list", "list all users") .action(async () => { - const db = connectDB(); + const db = await connectDB(); const all_users = await users.getAllUsers(db); for (const user of all_users) { console.log(`${user.name}`); @@ -53,7 +53,7 @@ export const user_command = new Command() .arguments(" ") .option("-q, --quiet", "quiet output.") .action(async ({ quiet }, [username, password]) => { - const db = connectDB(); + const db = await connectDB(); const new_user = await users.createUser(username, password); await users.updateUser(db, new_user); if (!quiet) { diff --git a/util/secret.ts b/util/secret.ts index 5292c9d..5696186 100644 --- a/util/secret.ts +++ b/util/secret.ts @@ -8,10 +8,17 @@ export async function generateSecretKey() { return key; } +let cacheKey: CryptoKey | undefined; +let previousKey = "invalid"; + export async function prepareSecretKey() { const key = Deno.env.get("SECRET_KEY"); + if (key === previousKey) { + return cacheKey!; + } if (key) { const jwk = JSON.parse(key) as JsonWebKey; + previousKey = key; { const key = await crypto.subtle.importKey( "jwk", @@ -20,12 +27,16 @@ export async function prepareSecretKey() { true, ["sign", "verify"], ); + cacheKey = key; return key; } } else { const key = await generateSecretKey(); const out = await crypto.subtle.exportKey("jwk", key); - Deno.env.set("SECRET_KEY", JSON.stringify(out)); + const outStr = JSON.stringify(out); + Deno.env.set("SECRET_KEY", outStr); + cacheKey = key; + previousKey = outStr; return key; } }