Add database tables and repository for managing bookings Create API endpoints for booking submission and capacity management Update landing page to persist bookings before WhatsApp redirection
144 lines
3.9 KiB
TypeScript
144 lines
3.9 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)
|
|
`
|
|
|
|
await sql`
|
|
create table if not exists bookings (
|
|
id text primary key,
|
|
confirmation_token text not null unique,
|
|
customer_name text not null,
|
|
customer_phone text not null,
|
|
booking_mode text not null check (booking_mode in ('table', 'pax')),
|
|
quantity integer not null check (quantity >= 1),
|
|
seat_count integer not null check (seat_count >= 1),
|
|
ticket_type text not null check (ticket_type in ('vip', 'supporter')),
|
|
unit_price integer not null check (unit_price >= 0),
|
|
total_price integer not null check (total_price >= 0),
|
|
person_in_charge_id text not null references users(id) on delete restrict,
|
|
person_in_charge_name text not null,
|
|
person_in_charge_phone_number text not null,
|
|
status text not null check (status in ('pending', 'confirmed')) default 'pending',
|
|
confirmed_at timestamptz,
|
|
created_at timestamptz not null default now(),
|
|
updated_at timestamptz not null default now()
|
|
)
|
|
`
|
|
|
|
await sql`
|
|
create table if not exists booking_settings (
|
|
id text primary key,
|
|
total_tables integer,
|
|
total_seats integer,
|
|
updated_at timestamptz not null default now()
|
|
)
|
|
`
|
|
|
|
await sql`
|
|
insert into booking_settings (id)
|
|
values ('default')
|
|
on conflict (id) do nothing
|
|
`
|
|
|
|
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 = '')
|
|
`
|
|
}
|