feat: add auth and database foundation
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
import { createClient } from '@libsql/client';
|
||||
import { hashPassword } from 'better-auth/crypto';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
function readDotEnv() {
|
||||
try {
|
||||
const entries = readFileSync('.env', 'utf8')
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.filter((line) => line && !line.startsWith('#'))
|
||||
.map((line) => {
|
||||
const index = line.indexOf('=');
|
||||
const key = line.slice(0, index);
|
||||
const value = line.slice(index + 1).replace(/^"|"$/g, '');
|
||||
return [key, value];
|
||||
});
|
||||
|
||||
for (const [key, value] of entries) {
|
||||
process.env[key] ??= value;
|
||||
}
|
||||
} catch {
|
||||
// .env is optional in deployed or scripted environments.
|
||||
}
|
||||
}
|
||||
|
||||
readDotEnv();
|
||||
|
||||
const databaseUrl = process.env.DATABASE_URL;
|
||||
const email = process.env.ADMIN_EMAIL?.trim().toLowerCase();
|
||||
const password = process.env.ADMIN_PASSWORD;
|
||||
const name = process.env.ADMIN_NAME?.trim() || 'Administrator';
|
||||
const overwrite = process.env.ADMIN_OVERWRITE === '1';
|
||||
|
||||
if (!databaseUrl) {
|
||||
console.error('DATABASE_URL is required.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!email || !password) {
|
||||
console.error('ADMIN_EMAIL and ADMIN_PASSWORD are required.');
|
||||
console.error('Example: ADMIN_EMAIL=admin@example.com ADMIN_PASSWORD=change-me pnpm admin:create');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const client = createClient({ url: databaseUrl });
|
||||
const now = Date.now();
|
||||
const existing = await client.execute({
|
||||
sql: 'select id from user where email = ? limit 1',
|
||||
args: [email]
|
||||
});
|
||||
|
||||
if (existing.rows.length > 0 && !overwrite) {
|
||||
console.error(`A user with email ${email} already exists. Set ADMIN_OVERWRITE=1 to update the password.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const userId = existing.rows[0]?.id?.toString() ?? randomUUID();
|
||||
const passwordHash = await hashPassword(password);
|
||||
|
||||
if (existing.rows.length === 0) {
|
||||
await client.execute({
|
||||
sql: 'insert into user (id, name, email, email_verified, image, created_at, updated_at) values (?, ?, ?, ?, ?, ?, ?)',
|
||||
args: [userId, name, email, 1, null, now, now]
|
||||
});
|
||||
} else {
|
||||
await client.execute({
|
||||
sql: 'update user set name = ?, email_verified = ?, updated_at = ? where id = ?',
|
||||
args: [name, 1, now, userId]
|
||||
});
|
||||
}
|
||||
|
||||
const account = await client.execute({
|
||||
sql: 'select id from account where user_id = ? and provider_id = ? limit 1',
|
||||
args: [userId, 'credential']
|
||||
});
|
||||
|
||||
if (account.rows.length === 0) {
|
||||
await client.execute({
|
||||
sql: 'insert into account (id, account_id, provider_id, user_id, password, created_at, updated_at) values (?, ?, ?, ?, ?, ?, ?)',
|
||||
args: [randomUUID(), userId, 'credential', userId, passwordHash, now, now]
|
||||
});
|
||||
} else {
|
||||
await client.execute({
|
||||
sql: 'update account set password = ?, updated_at = ? where id = ?',
|
||||
args: [passwordHash, now, account.rows[0].id]
|
||||
});
|
||||
}
|
||||
|
||||
console.log(`Admin user ready: ${email}`);
|
||||
Reference in New Issue
Block a user