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:
MaticIvesicandClaude Opus 4.8 committed 2026-08-02 20:54:06 +02:00
1 parent 41410e3079
commit 6bc5fffda2
32 files changed
+3241 -147

No files matched your search

+57
View File
@@ -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}).`);
}