Files
Work/Backend/db/migrations/000002_bootstrap_foundation.sql
2026-06-19 17:39:21 +01:00

181 lines
6.9 KiB
SQL

-- +goose Up
CREATE TYPE instance_mode AS ENUM ('personal', 'organizational');
CREATE TYPE instance_access AS ENUM ('local', 'remote');
CREATE TYPE instance_protocol AS ENUM ('http', 'https');
CREATE TYPE workspace_kind AS ENUM ('organization', 'department', 'team', 'project');
CREATE TYPE membership_role AS ENUM ('owner', 'admin', 'member');
CREATE TABLE IF NOT EXISTS installations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
singleton BOOLEAN NOT NULL DEFAULT TRUE UNIQUE,
mode instance_mode NOT NULL,
access instance_access NOT NULL,
protocol instance_protocol NOT NULL DEFAULT 'http',
host TEXT NOT NULL,
is_bootstrapped BOOLEAN NOT NULL DEFAULT FALSE,
bootstrapped_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT NOT NULL,
display_name TEXT NOT NULL,
password_hash TEXT NOT NULL,
is_instance_admin BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_email_unique ON users (LOWER(email));
CREATE TABLE IF NOT EXISTS user_homes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
ALTER TABLE organizations
ADD COLUMN IF NOT EXISTS created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL;
CREATE TABLE IF NOT EXISTS organization_memberships (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role membership_role NOT NULL DEFAULT 'member',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (organization_id, user_id)
);
CREATE INDEX IF NOT EXISTS idx_organization_memberships_user_id ON organization_memberships (user_id);
CREATE TABLE IF NOT EXISTS departments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
name TEXT NOT NULL,
slug TEXT NOT NULL,
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (organization_id, slug)
);
CREATE INDEX IF NOT EXISTS idx_departments_organization_id ON departments (organization_id);
CREATE TABLE IF NOT EXISTS teams (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
department_id UUID REFERENCES departments(id) ON DELETE SET NULL,
name TEXT NOT NULL,
slug TEXT NOT NULL,
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (organization_id, slug)
);
CREATE INDEX IF NOT EXISTS idx_teams_organization_id ON teams (organization_id);
CREATE INDEX IF NOT EXISTS idx_teams_department_id ON teams (department_id);
CREATE TABLE IF NOT EXISTS team_memberships (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
team_id UUID NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role membership_role NOT NULL DEFAULT 'member',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (team_id, user_id)
);
CREATE INDEX IF NOT EXISTS idx_team_memberships_user_id ON team_memberships (user_id);
CREATE TABLE IF NOT EXISTS projects (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
department_id UUID REFERENCES departments(id) ON DELETE SET NULL,
team_id UUID REFERENCES teams(id) ON DELETE SET NULL,
name TEXT NOT NULL,
slug TEXT NOT NULL,
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (organization_id, slug)
);
CREATE INDEX IF NOT EXISTS idx_projects_organization_id ON projects (organization_id);
CREATE INDEX IF NOT EXISTS idx_projects_department_id ON projects (department_id);
CREATE INDEX IF NOT EXISTS idx_projects_team_id ON projects (team_id);
CREATE TABLE IF NOT EXISTS project_memberships (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role membership_role NOT NULL DEFAULT 'member',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (project_id, user_id)
);
CREATE INDEX IF NOT EXISTS idx_project_memberships_user_id ON project_memberships (user_id);
ALTER TABLE workspaces
ADD COLUMN IF NOT EXISTS kind workspace_kind NOT NULL DEFAULT 'organization',
ADD COLUMN IF NOT EXISTS created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
ADD COLUMN IF NOT EXISTS department_id UUID REFERENCES departments(id) ON DELETE SET NULL,
ADD COLUMN IF NOT EXISTS team_id UUID REFERENCES teams(id) ON DELETE SET NULL,
ADD COLUMN IF NOT EXISTS project_id UUID REFERENCES projects(id) ON DELETE SET NULL;
CREATE INDEX IF NOT EXISTS idx_workspaces_department_id ON workspaces (department_id);
CREATE INDEX IF NOT EXISTS idx_workspaces_team_id ON workspaces (team_id);
CREATE INDEX IF NOT EXISTS idx_workspaces_project_id ON workspaces (project_id);
-- +goose Down
DROP INDEX IF EXISTS idx_workspaces_project_id;
DROP INDEX IF EXISTS idx_workspaces_team_id;
DROP INDEX IF EXISTS idx_workspaces_department_id;
ALTER TABLE workspaces
DROP COLUMN IF EXISTS project_id,
DROP COLUMN IF EXISTS team_id,
DROP COLUMN IF EXISTS department_id,
DROP COLUMN IF EXISTS created_by_user_id,
DROP COLUMN IF EXISTS kind;
DROP INDEX IF EXISTS idx_project_memberships_user_id;
DROP TABLE IF EXISTS project_memberships;
DROP INDEX IF EXISTS idx_projects_team_id;
DROP INDEX IF EXISTS idx_projects_department_id;
DROP INDEX IF EXISTS idx_projects_organization_id;
DROP TABLE IF EXISTS projects;
DROP INDEX IF EXISTS idx_team_memberships_user_id;
DROP TABLE IF EXISTS team_memberships;
DROP INDEX IF EXISTS idx_teams_department_id;
DROP INDEX IF EXISTS idx_teams_organization_id;
DROP TABLE IF EXISTS teams;
DROP INDEX IF EXISTS idx_departments_organization_id;
DROP TABLE IF EXISTS departments;
DROP INDEX IF EXISTS idx_organization_memberships_user_id;
DROP TABLE IF EXISTS organization_memberships;
ALTER TABLE organizations
DROP COLUMN IF EXISTS created_by_user_id;
DROP TABLE IF EXISTS user_homes;
DROP INDEX IF EXISTS idx_users_email_unique;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS installations;
DROP TYPE IF EXISTS membership_role;
DROP TYPE IF EXISTS workspace_kind;
DROP TYPE IF EXISTS instance_protocol;
DROP TYPE IF EXISTS instance_access;
DROP TYPE IF EXISTS instance_mode;