Files
dticket.tootaio.com/server/utils/db-init.ts
xiaomai 377a9617be feat: implement auth system, passkeys, and user management
Add PostgreSQL and Redis integration for users and sessions
Implement password and WebAuthn passkey login flows
Add Docker stack, super-admin seeding, and protected routes
2026-04-12 20:16:43 +08:00

107 lines
2.6 KiB
TypeScript

import { randomUUID } from 'node:crypto'
import { DEFAULT_USER_PASSWORD } from '~~/shared/auth'
import { hashPassword } from './password'
import { getSqlClient } from './postgres'
let databaseReadyPromise: Promise<void> | null = null
export async function ensureDatabaseReady() {
if (!databaseReadyPromise) {
databaseReadyPromise = initializeDatabase()
}
return databaseReadyPromise
}
async function initializeDatabase() {
const sql = getSqlClient()
await sql`
create table if not exists users (
id text primary key,
username text not null unique,
full_name text not null,
phone_number text,
role text not null check (role in ('super_admin', 'staff')),
password_hash text not null,
must_change_password boolean not null default true,
is_active boolean not null default true,
created_by text references users(id) on delete set null,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
last_login_at timestamptz
)
`
await sql`
alter table users
add column if not exists phone_number text
`
await sql`
create table if not exists user_passkeys (
id text primary key,
user_id text not null references users(id) on delete cascade,
credential_id text not null unique,
public_key text not null,
counter bigint not null default 0,
device_type text not null check (device_type in ('singleDevice', 'multiDevice')),
backed_up boolean not null default false,
transports jsonb not null default '[]'::jsonb,
label text not null,
created_at timestamptz not null default now(),
last_used_at timestamptz
)
`
await sql`
create index if not exists user_passkeys_user_id_idx
on user_passkeys (user_id)
`
const [existingSuperAdmin] = await sql<{ id: string }[]>`
select id
from users
where username = 'xiaomai'
limit 1
`
if (!existingSuperAdmin) {
const passwordHash = await hashPassword(DEFAULT_USER_PASSWORD)
await sql`
insert into users (
id,
username,
full_name,
role,
password_hash,
must_change_password,
is_active,
created_by
)
values (
${randomUUID()},
'xiaomai',
'Xiaomai',
'super_admin',
${passwordHash},
true,
true,
null
)
`
}
await sql`
update users
set
phone_number = '601157753558',
updated_at = now()
where username = 'xiaomai'
and (phone_number is null or phone_number = '')
`
}