wip
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.turbo/
|
||||
.env
|
||||
.env.*
|
||||
secrets.env
|
||||
*.db
|
||||
*.db-*
|
||||
*.sqlite
|
||||
*.sqlite*
|
||||
data/
|
||||
coverage/
|
||||
.DS_Store
|
||||
@@ -5,6 +5,13 @@
|
||||
"type": "module",
|
||||
"license": "AGPL-3.0-only",
|
||||
"packageManager": "pnpm@10.6.3",
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
"argon2",
|
||||
"better-sqlite3",
|
||||
"esbuild"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22"
|
||||
},
|
||||
|
||||
+12
-1
@@ -1,16 +1,27 @@
|
||||
import { existsSync, mkdirSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import Database from "better-sqlite3";
|
||||
|
||||
import type { AdminUserRecord, InviteRecord } from "./types.js";
|
||||
|
||||
// Keep local development aligned with the compose-mounted ./data directory.
|
||||
const workspaceSqlitePath = resolve(
|
||||
dirname(fileURLToPath(import.meta.url)),
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"data",
|
||||
"admin.db"
|
||||
);
|
||||
|
||||
function resolveSqlitePath(): string {
|
||||
if (existsSync("/data")) {
|
||||
return "/data/admin.db";
|
||||
}
|
||||
|
||||
return resolve(process.cwd(), "data", "admin.db");
|
||||
return workspaceSqlitePath;
|
||||
}
|
||||
|
||||
export const sqlitePath = resolveSqlitePath();
|
||||
|
||||
@@ -20,6 +20,7 @@ async function main(): Promise<void> {
|
||||
await connectMongo();
|
||||
|
||||
const app = express();
|
||||
app.set("trust proxy", 1);
|
||||
|
||||
app.use(
|
||||
helmet({
|
||||
|
||||
@@ -7,6 +7,14 @@ import { sqlite } from "../db/sqlite.js";
|
||||
|
||||
const SQLiteStore = connectSqlite3(session);
|
||||
export const SESSION_COOKIE_NAME = "stoat-admin.sid";
|
||||
const SESSION_COOKIE_SECURE =
|
||||
new URL(env.ADMIN_WEB_ORIGIN).protocol === "https:";
|
||||
export const SESSION_COOKIE_OPTIONS = {
|
||||
path: "/",
|
||||
httpOnly: true,
|
||||
sameSite: "strict",
|
||||
secure: SESSION_COOKIE_SECURE
|
||||
} as const;
|
||||
|
||||
export const sessionMiddleware = session({
|
||||
name: SESSION_COOKIE_NAME,
|
||||
@@ -21,10 +29,8 @@ export const sessionMiddleware = session({
|
||||
}
|
||||
}),
|
||||
cookie: {
|
||||
maxAge: 2 * 60 * 60 * 1000,
|
||||
httpOnly: true,
|
||||
sameSite: "strict",
|
||||
secure: false
|
||||
...SESSION_COOKIE_OPTIONS,
|
||||
maxAge: 2 * 60 * 60 * 1000
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@ import { z } from "zod";
|
||||
|
||||
import { statements } from "../db/sqlite.js";
|
||||
import { asyncHandler } from "../lib/async-handler.js";
|
||||
import { requireAuth, SESSION_COOKIE_NAME } from "../middleware/auth.js";
|
||||
import {
|
||||
requireAuth,
|
||||
SESSION_COOKIE_NAME,
|
||||
SESSION_COOKIE_OPTIONS
|
||||
} from "../middleware/auth.js";
|
||||
|
||||
const loginSchema = z.object({
|
||||
username: z.string().min(1),
|
||||
@@ -56,7 +60,7 @@ authRouter.post(
|
||||
});
|
||||
});
|
||||
|
||||
res.clearCookie(SESSION_COOKIE_NAME);
|
||||
res.clearCookie(SESSION_COOKIE_NAME, SESSION_COOKIE_OPTIONS);
|
||||
res.status(200).json({ success: true });
|
||||
})
|
||||
);
|
||||
|
||||
@@ -32,7 +32,11 @@ async function promptForMissing(
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const rawArgs = process.argv.slice(2);
|
||||
const normalizedArgs = rawArgs[0] === "--" ? rawArgs.slice(1) : rawArgs;
|
||||
|
||||
const parsed = parseArgs({
|
||||
args: normalizedArgs,
|
||||
options: {
|
||||
username: {
|
||||
type: "string"
|
||||
|
||||
Reference in New Issue
Block a user