Voeg tenant beheer toe aan rikx-offerte

- Login pagina met HMAC-sessie authenticatie
- /tenants overzicht: lijst alle rikx-platform accounts
- Nieuw tenant aanmaken (naam, slug, admin gebruiker)
- Tenant verwijderen (cascade via foreign keys)
- Leest rikx-platform SQLite DB via /rikx-platform-data mount
- Homepage redirect naar /tenants of /login

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Maarten Elsenaar
2026-09-15 13:14:54 +02:00
parent 463f6d2c90
commit 8ad282aad8
6 changed files with 374 additions and 69 deletions
+59
View File
@@ -0,0 +1,59 @@
"use server";
import { redirect } from "next/navigation";
import { randomBytes, scryptSync } from "crypto";
import { getPlatformDb } from "../../lib/platform-db";
import { getSession } from "../../lib/auth";
function requireAuth() {
// Called inside server actions — session check is synchronous-safe via cache
}
function hashPassword(password: string): string {
const salt = randomBytes(16).toString("hex");
const hash = scryptSync(password, salt, 64).toString("hex");
return `${salt}:${hash}`;
}
export async function createTenant(formData: FormData) {
const session = await getSession();
if (!session) redirect("/login");
const name = (formData.get("name") as string).trim();
const slug = (formData.get("slug") as string).trim().toLowerCase().replace(/[^a-z0-9-]/g, "-");
const adminEmail = (formData.get("admin_email") as string).trim();
const adminPassword = formData.get("admin_password") as string;
const adminName = (formData.get("admin_name") as string).trim();
if (!name || !slug || !adminEmail || !adminPassword || !adminName) {
redirect("/tenants?error=missing");
}
const db = getPlatformDb();
const existing = db.prepare("SELECT id FROM accounts WHERE slug = ?").get(slug);
if (existing) redirect("/tenants?error=slug");
db.prepare("INSERT INTO accounts (name, slug) VALUES (?, ?)").run(name, slug);
const account = db.prepare("SELECT id FROM accounts WHERE slug = ?").get(slug) as { id: number };
db.prepare(
"INSERT INTO users (account_id, email, password_hash, name, role) VALUES (?, ?, ?, ?, 'admin')"
).run(account.id, adminEmail, hashPassword(adminPassword), adminName);
redirect("/tenants");
}
export async function deleteTenant(formData: FormData) {
const session = await getSession();
if (!session) redirect("/login");
const accountId = Number(formData.get("account_id"));
if (!accountId) redirect("/tenants");
const db = getPlatformDb();
// Cascade via foreign keys: users, participants, measurements are deleted automatically
db.prepare("DELETE FROM accounts WHERE id = ?").run(accountId);
redirect("/tenants");
}