Phase 1: self-hosted backend (adapter-node + SQLite) replacing Supabase
Migrate the gallery/admin off the static + Supabase setup to a self-hosted full-stack app that runs on the home server behind nginx. - Switch adapter-static -> adapter-node - SQLite via Drizzle (users, sessions, gallery_images); migrations run on startup - Server-side session auth (argon2 hashing, httpOnly cookie, SHA-256 stored token, sliding expiry) wired through hooks.server.ts - Admin: server-side login, route guard, gallery upload/delete via form actions - Public gallery is now SSR from the DB; /uploads/[...path] serves images in dev - scripts/create-admin.js + db:generate/db:migrate/create-admin npm scripts - Remove @supabase/supabase-js and the client-side gallery/admin code - Fix pre-existing imageOnRight boolean prop type in Main.svelte - ADMIN_SETUP.md rewritten for local dev + home-server deploy (systemd, nginx, backups) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
41410e3079
commit
6bc5fffda2
32 files changed
+3241
-147
No files matched your search
@@ -0,0 +1,57 @@
|
||||
// Create (or update) a dashboard user directly in the SQLite database.
|
||||
// Run the migrations first (npm run db:migrate), then:
|
||||
// npm run create-admin -- --email you@example.com --password "secret" --name "Ime" --role admin
|
||||
// Values can also come from ADMIN_EMAIL / ADMIN_PASSWORD / ADMIN_NAME / ADMIN_ROLE env vars.
|
||||
import Database from 'better-sqlite3';
|
||||
import { hash } from '@node-rs/argon2';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
function arg(name) {
|
||||
const i = process.argv.indexOf(`--${name}`);
|
||||
return i !== -1 ? process.argv[i + 1] : undefined;
|
||||
}
|
||||
|
||||
const email = (arg('email') ?? process.env.ADMIN_EMAIL ?? '').trim().toLowerCase();
|
||||
const password = arg('password') ?? process.env.ADMIN_PASSWORD ?? '';
|
||||
const name = arg('name') ?? process.env.ADMIN_NAME ?? 'Admin';
|
||||
const role = arg('role') ?? process.env.ADMIN_ROLE ?? 'admin';
|
||||
const dbPath = process.env.DATABASE_PATH ?? './data/app.db';
|
||||
|
||||
if (!email || !password) {
|
||||
console.error(
|
||||
'Usage: npm run create-admin -- --email you@example.com --password "secret" [--name "Ime"] [--role admin|editor]'
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
if (role !== 'admin' && role !== 'editor') {
|
||||
console.error('role must be "admin" or "editor"');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const db = new Database(dbPath);
|
||||
|
||||
const hasUsersTable = db
|
||||
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='users'")
|
||||
.get();
|
||||
if (!hasUsersTable) {
|
||||
console.error('No "users" table found. Run "npm run db:migrate" first.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const passwordHash = await hash(password);
|
||||
const existing = db.prepare('SELECT id FROM users WHERE email = ?').get(email);
|
||||
|
||||
if (existing) {
|
||||
db.prepare('UPDATE users SET password_hash = ?, name = ?, role = ? WHERE email = ?').run(
|
||||
passwordHash,
|
||||
name,
|
||||
role,
|
||||
email
|
||||
);
|
||||
console.log(`Updated existing user ${email} (role: ${role}).`);
|
||||
} else {
|
||||
db.prepare(
|
||||
'INSERT INTO users (id, email, password_hash, name, role, created_at) VALUES (?, ?, ?, ?, ?, ?)'
|
||||
).run(randomUUID(), email, passwordHash, name, role, Date.now());
|
||||
console.log(`Created user ${email} (role: ${role}).`);
|
||||
}
|
||||
Reference in new issue
Block a user