152 lines
4.1 KiB
TypeScript
152 lines
4.1 KiB
TypeScript
import { query } from "@/db/mod.ts";
|
|
|
|
const id = "id uuid primary key default generate_ulid()";
|
|
|
|
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<string, TableSpec> = {
|
|
"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";
|
|
create extension if not exists "pgcrypto";
|
|
|
|
create or replace function generate_ulid() returns uuid
|
|
as $$
|
|
select (lpad(to_hex(floor(extract(epoch from clock_timestamp()) * 1000)::bigint), 12, '0') || encode(gen_random_bytes(10), 'hex'))::uuid;
|
|
$$ language sql;
|
|
|
|
${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);
|