feat(api): seed dev admin + worker via admin createUser

This commit is contained in:
Bas van Rossem
2026-06-17 17:50:18 +02:00
parent dc8f550665
commit bd2d859e92
2 changed files with 40 additions and 34 deletions

View File

@@ -4,10 +4,23 @@ import { db } from './client';
import { activities, user } from './schema';
import { auth } from '../auth';
// Dev-only test account so `db:seed` yields ready-made login credentials for local
// testing / phone demos. Created through better-auth (password is properly hashed).
// NEVER seeded when NODE_ENV=production — see seedDevUser().
const DEV_USER = { email: 'worker@solelog.local', password: 'werkplaats123', name: 'Test Werker' };
// Dev-only accounts so `db:seed` yields ready-made logins for local testing / phone demos.
// Created through better-auth's admin createUser (hashes the password, sets the role, and works
// even though public sign-up is disabled). NEVER seeded when NODE_ENV=production.
const DEV_ACCOUNTS = [
{
email: 'worker@solelog.local',
password: 'werkplaats123',
name: 'Test Werker',
role: 'worker' as const,
},
{
email: 'admin@solelog.local',
password: 'werkplaats-admin',
name: 'Test Beheerder',
role: 'admin' as const,
},
];
// Reference activities (realistic Dutch handeling names) — see
// docs/reference/legacy-mobile-app.md §6.2 and the Phase 1 plan.
@@ -20,24 +33,22 @@ const REFERENCE_ACTIVITIES: { name: string; insoleTypes: string[] }[] = [
{ name: 'Printen', insoleTypes: ['3D'] },
];
// Idempotent: create the dev account once, and only outside production (it has a
// known password). Uses better-auth's server API so the user + hashed-password
// account rows are created exactly as a real sign-up would.
async function seedDevUser(): Promise<void> {
async function seedDevUsers(): Promise<void> {
if (process.env.NODE_ENV === 'production') return;
const existing = await db.select().from(user).where(eq(user.email, DEV_USER.email));
if (existing.length > 0) return;
await auth.api.signUpEmail({ body: DEV_USER });
console.log(`Seeded dev account: ${DEV_USER.email} / ${DEV_USER.password}`);
for (const acc of DEV_ACCOUNTS) {
const existing = await db.select().from(user).where(eq(user.email, acc.email));
if (existing.length > 0) continue;
await auth.api.createUser({
body: { email: acc.email, password: acc.password, name: acc.name, role: acc.role },
});
console.log(`Seeded dev ${acc.role}: ${acc.email} / ${acc.password}`);
}
}
// Idempotent: insert each reference activity only if no activity with that name exists.
export async function seed(): Promise<void> {
for (const activity of REFERENCE_ACTIVITIES) {
const existing = await db
.select()
.from(activities)
.where(eq(activities.name, activity.name));
const existing = await db.select().from(activities).where(eq(activities.name, activity.name));
if (existing.length === 0) {
await db.insert(activities).values({
name: activity.name,
@@ -45,7 +56,7 @@ export async function seed(): Promise<void> {
});
}
}
await seedDevUser();
await seedDevUsers();
}
// Allow running directly: `tsx src/db/seed.ts` (cross-platform — pathToFileURL