Added auto database creation based on sql scripts and jwt key cache

This commit is contained in:
fedyna-k
2025-01-16 23:13:58 +01:00
parent 914875a3df
commit 46a417f411
8 changed files with 70 additions and 7 deletions
+2
View File
@@ -9,3 +9,5 @@
_fresh/ _fresh/
# npm dependencies # npm dependencies
node_modules/ node_modules/
databases/data/
+28
View File
@@ -0,0 +1,28 @@
import { Database } from "@db/sqlite";
export default async function ensureDatabases() {
for await (const file of Deno.readDir("databases/init")) {
if (!file.isFile) {
console.warn(`[WARN] Path ${file.name} is not a file.`);
continue;
}
const databaseName = file.name.substring(0, file.name.length - 4);
const databasePath = `databases/data/${databaseName}.db`;
try {
await Deno.stat(databasePath);
} catch (error) {
if (!(error instanceof Deno.errors.NotFound)) {
throw error;
}
const sqlInitCode = await Deno.readTextFile(
`databases/init/${file.name}`,
);
const database = new Database(databasePath);
database.run(sqlInitCode);
database.close();
}
}
}
View File
View File
+4 -3
View File
@@ -4,9 +4,9 @@
"check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx", "check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx",
"cli": "echo \"import '\\$fresh/src/dev/cli.ts'\" | deno run --unstable -A -", "cli": "echo \"import '\\$fresh/src/dev/cli.ts'\" | deno run --unstable -A -",
"manifest": "deno task cli manifest $(pwd)", "manifest": "deno task cli manifest $(pwd)",
"start": "deno run -A --watch=static/,routes/ dev.ts", "start": "deno run -A --unstable-ffi --watch=static/,routes/ dev.ts",
"build": "deno run -A dev.ts build", "build": "deno run -A --unstable-ffi dev.ts build",
"preview": "deno run -A main.ts", "preview": "deno run -A --unstable-ffi main.ts",
"update": "deno run -A -r https://fresh.deno.dev/update ." "update": "deno run -A -r https://fresh.deno.dev/update ."
}, },
"lint": { "lint": {
@@ -22,6 +22,7 @@
], ],
"imports": { "imports": {
"$fresh/": "https://deno.land/x/fresh@1.7.3/", "$fresh/": "https://deno.land/x/fresh@1.7.3/",
"@db/sqlite": "jsr:@db/sqlite@^0.12.0",
"@melvdouc/xml-parser": "jsr:@melvdouc/xml-parser@^0.1.1", "@melvdouc/xml-parser": "jsr:@melvdouc/xml-parser@^0.1.1",
"@popov/jwt": "jsr:@popov/jwt@^1.0.1", "@popov/jwt": "jsr:@popov/jwt@^1.0.1",
"preact": "https://esm.sh/preact@10.22.0", "preact": "https://esm.sh/preact@10.22.0",
+2
View File
@@ -1,5 +1,7 @@
import { defineConfig } from "$fresh/server.ts"; import { defineConfig } from "$fresh/server.ts";
import ensureDatabases from "$root/databases/ensure.ts";
await ensureDatabases();
export default defineConfig({ export default defineConfig({
server: { server: {
cert: await Deno.readTextFile("certs/cert.pem"), cert: await Deno.readTextFile("certs/cert.pem"),
+24 -3
View File
@@ -1,6 +1,7 @@
import { FreshContext } from "$fresh/server.ts"; import { FreshContext } from "$fresh/server.ts";
import { getCookies } from "$std/http/cookie.ts"; import { getCookies } from "$std/http/cookie.ts";
import { isJwtValid } from "@popov/jwt"; import { getJwtPayload, isJwtValid } from "@popov/jwt";
import { LoginJWT } from "$root/routes/login.tsx";
const PUBLIC_ROUTES = [ const PUBLIC_ROUTES = [
"/", "/",
@@ -11,6 +12,8 @@ const PUBLIC_ROUTES = [
"/contact", "/contact",
]; ];
const jwtKeyCache: Record<string, string> = {};
export interface State { export interface State {
isAuthenticated: boolean; isAuthenticated: boolean;
} }
@@ -19,15 +22,33 @@ function isRoutePublic(route: string) {
return PUBLIC_ROUTES.includes(route) || route.match(/\..+$/); return PUBLIC_ROUTES.includes(route) || route.match(/\..+$/);
} }
export function getKey(user: string): string {
if (!jwtKeyCache[user]) {
const keyBuffer = new Uint8Array(32);
crypto.getRandomValues(keyBuffer);
jwtKeyCache[user] = new TextDecoder().decode(keyBuffer);
}
return jwtKeyCache[user];
}
export const handler = [ export const handler = [
async function checkAuthentication( async function checkAuthentication(
request: Request, request: Request,
context: FreshContext<State>, context: FreshContext<State>,
) { ) {
const cookies = getCookies(request.headers); const cookies = getCookies(request.headers);
if (!cookies["sessionToken"]) {
context.state.isAuthenticated = false;
return await context.next();
}
const content = getJwtPayload(cookies["sessionToken"]) as LoginJWT;
const key = getKey(content.user.uid as string);
context.state.isAuthenticated = await isJwtValid( context.state.isAuthenticated = await isJwtValid(
cookies["sessionToken"] ?? "", cookies["sessionToken"],
"NEED TO CHANGE THIS KEY FURTHER IN DEV", key,
); );
return await context.next(); return await context.next();
+10 -1
View File
@@ -7,6 +7,7 @@ import {
} from "@melvdouc/xml-parser"; } from "@melvdouc/xml-parser";
import { createJwt } from "@popov/jwt"; import { createJwt } from "@popov/jwt";
import { setCookie } from "$std/http/cookie.ts"; import { setCookie } from "$std/http/cookie.ts";
import { getKey } from "$root/routes/_middleware.ts";
const SERVICE = "https://localhost/login"; const SERVICE = "https://localhost/login";
const CAS = "https://ident.univ-amu.fr/cas"; const CAS = "https://ident.univ-amu.fr/cas";
@@ -23,6 +24,14 @@ interface CasResponse extends RegularTagNode {
children: [TextNode, CasGroupNode]; children: [TextNode, CasGroupNode];
} }
export interface LoginJWT {
iss: "PolyMPR";
iat: number;
exp: number;
aud: "PolyMPR";
user: Record<string, string | string[]>;
}
function getTag(tag: CasTagNode): [string, string] { function getTag(tag: CasTagNode): [string, string] {
return [ return [
tag.tagName.replace("cas:", ""), tag.tagName.replace("cas:", ""),
@@ -55,7 +64,7 @@ function createUserJWT(casResponse: CasResponse): Promise<string> {
user: fullUserInfos, user: fullUserInfos,
}; };
const key = "NEED TO CHANGE THIS KEY FURTHER IN DEV"; const key = getKey(fullUserInfos.uid as string);
return createJwt(payload, key); return createJwt(payload, key);
} }