#!/usr/bin/env node import fs from 'node:fs' import { randomBytes, randomUUID, scryptSync } from 'node:crypto' import path from 'node:path' import { createRequire } from 'node:module' import { fileURLToPath } from 'node:url' import { devMysqlUrl } from '@drama/db' const DEFAULT_DATABASE_URL = devMysqlUrl('voko') const here = path.dirname(fileURLToPath(import.meta.url)) const repoRoot = path.resolve(here, '../..') const envFile = path.resolve(repoRoot, 'apps/voko-server/.env') const requireFromVokoServer = createRequire(new URL('../../apps/voko-server/package.json', import.meta.url)) const { createDb } = await import(requireFromVokoServer.resolve('@drama/db')) function hashPassword(password) { const salt = randomBytes(16) const derived = scryptSync(String(password), salt, 64) return `scrypt$${salt.toString('hex')}$${derived.toString('hex')}` } async function main() { const email = process.argv[2] || 'admin@example.test' const password = process.argv[3] || 'admin123' const displayName = process.argv[4] || 'VOKO Admin' const role = process.argv[5] || 'admin' if (!['admin', 'operator', 'user'].includes(role)) { throw new Error(`voko dev user role 必须是 admin、operator 或 user: ${role}`) } const databaseUrl = process.env.DATABASE_URL || readDotenv(envFile).DATABASE_URL || DEFAULT_DATABASE_URL const db = createDb({ url: databaseUrl, pool: { min: 0, max: 1 } }) const normalizedEmail = email.trim().toLowerCase() const now = new Date() const user = { id: randomUUID(), email: normalizedEmail, display_name: displayName, status: 'active', password_hash: hashPassword(password), locale: 'zh', email_verified_at: now, role, created_at: now, updated_at: now, } try { await db('users').insert(user).onConflict('email').merge({ display_name: user.display_name, status: user.status, password_hash: user.password_hash, locale: user.locale, email_verified_at: db.raw('COALESCE(email_verified_at, ?)', [now]), role: user.role, updated_at: now, }) console.log(`voko dev user ready: ${normalizedEmail}, role=${role}`) } finally { await db.destroy() } } function readDotenv(file) { if (!fs.existsSync(file)) return {} const env = {} for (const raw of fs.readFileSync(file, 'utf8').split(/\r?\n/)) { const line = raw.trim() if (!line || line.startsWith('#')) continue const separator = line.indexOf('=') if (separator < 0) continue const key = line.slice(0, separator).trim() let value = line.slice(separator + 1).trim() if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) { value = value.slice(1, -1) } env[key] = value } return env } main().catch(error => { console.error(error) process.exit(1) })