-- +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;