init.ts
Raw
1import { Database } from "bun:sqlite";
2import { mkdirSync, readFileSync } from "node:fs";
3import path from "node:path";
4import * as argon2 from "argon2";
5import config from "../config.ts";
6import { ADMIN_USERNAME, paths } from "../constants.ts";
7
8// Ensure data directories exist
9mkdirSync(config.DATA_DIR, { recursive: true });
10mkdirSync(paths.REPOS_DIR, { recursive: true });
11mkdirSync(paths.AVATARS_DIR, { recursive: true });
12
13const db = new Database(paths.DB_PATH);
14db.run("PRAGMA journal_mode=WAL");
15db.run("PRAGMA foreign_keys=ON");
16
17// Run schema
18const schema = readFileSync(path.join(import.meta.dir, "schema.sql"), "utf-8");
19db.run(schema);
20
21// Seed admin account if not present
22const existing = db
23 .query("SELECT id FROM users WHERE username = ?")
24 .get(ADMIN_USERNAME);
25if (!existing) {
26 const password = process.env.ADMIN_PASSWORD ?? "changeme";
27 const hash = await argon2.hash(password);
28 const now = new Date().toISOString();
29 db.run(
30 "INSERT INTO users (username, password_hash, created_at) VALUES (?, ?, ?)",
31 [ADMIN_USERNAME, hash, now],
32 );
33 console.log(
34 `Created admin account (username: ${ADMIN_USERNAME}, password: ${password})`,
35 );
36 if (password === "changeme") {
37 console.warn(
38 "WARNING: Using default admin password. Set ADMIN_PASSWORD env var before running db:init.",
39 );
40 }
41} else {
42 console.log("Admin account already exists.");
43}
44
45console.log("Database initialized at", paths.DB_PATH);
46db.close();
47