import { query } from "@/db/mod.ts"; const id = "id uuid primary key default uuid_generate_v4()"; interface TableSpec { columns: string[]; additionalTableStatements?: string[]; additionalStatements?: string[]; prepStatements?: string[]; } const createdAtTimestamp = "created_at timestamptz not null default now()"; const updatedAtTimestamp = "updated_at timestamptz not null default now()"; const timestamps = [createdAtTimestamp, updatedAtTimestamp]; const tables: Record = { "note": { columns: [id, "content text not null", ...timestamps], }, "user": { prepStatements: [ "drop type if exists user_status", "create type user_status as enum ('unverified', 'verified', 'owner', 'superadmin')", ], columns: [ id, "username text not null unique", "password_digest text not null", "status user_status not null", "display_name text", ...timestamps, ], additionalTableStatements: [ "constraint valid_username check (username ~* '^[a-z\d\\-_]{2,38}$')", ], }, "user_token": { prepStatements: [ "drop type if exists user_token_type", "create type user_token_type as enum ('session', 'reset')", ], columns: [ id, "token_digest bytea not null unique", "type user_token_type not null", "sent_to text not null", createdAtTimestamp, ], additionalStatements: [ "create index team_user_type on user_token (type)", "create index team_user_sent_to on user_token (sent_to)", ], }, "team": { columns: [ id, "display_name text not null", ...timestamps, ], additionalStatements: [ 'create index display_name_idx on team ("display_name")', ], }, "team_user": { prepStatements: [ "drop type if exists team_user_status", "create type team_user_status as enum ('invited', 'accepted', 'owner')", ], columns: [ "team_id uuid", "user_id uuid", "status team_user_status not null", ...timestamps, ], additionalTableStatements: [ 'constraint fk_team foreign key(team_id) references "team"(id) on delete cascade', 'constraint fk_user foreign key(user_id) references "user"(id) on delete cascade', ], additionalStatements: [ "create index team_user_idx on team_user (team_id) include (user_id)", "create index team_idx on team_user (team_id)", "create index user_idx on team_user (user_id)", "create index status_idx on team_user (status)", ], }, }; const dropTables = Object.entries(tables).reverse().map(([name, _meta]) => `drop table if exists "${name}";` ).join("\n"); const createTables = Object.entries(tables).map(([name, meta]) => ` -- CREATE TABLE ${name} ${(meta.prepStatements || []).map((s) => `${s};`).join("\n")} create table "${name}" ( ${meta.columns.concat(meta.additionalTableStatements || []).join(",\n ")} ); ${(meta.additionalStatements || []).map((s) => `${s};`).join("\n")} `).map((s) => s.trim()).join("\n\n"); const queryString = ` begin; ${dropTables} create extension if not exists "uuid-ossp"; ${createTables} commit; `; const setupResult = await query(queryString); console.debug(setupResult); console.log(queryString); const seedQuery = ` -- TODO: create reserved usernames? with new_user as ( insert into "user" (username, password_digest, status) values ('lytedev', '$2a$10$9fyDAOz6H4a393KHyjbvIe1WFxbhCJhq/CZmlXcEg4d1bE9Ey25WW', 'superadmin') returning id as user_id ), new_team as ( insert into "team" (display_name) values ('superadmins') returning id as team_id ) insert into "team_user" (user_id, team_id, status) values ( (select user_id from new_user), (select team_id from new_team), 'owner' ) returning user_id; `; const seedResult = await query(seedQuery); console.debug(seedResult); console.log(seedQuery);