359 lines
16 KiB
SQL
359 lines
16 KiB
SQL
-- =============================================================================
|
|
-- Life OS - Release 1 Schema
|
|
-- Self-hosted PostgreSQL on defiant-01 (Hetzner)
|
|
-- =============================================================================
|
|
|
|
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
|
|
|
-- =============================================================================
|
|
-- SYSTEM LEVEL: Context Types
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE context_types (
|
|
id SERIAL PRIMARY KEY,
|
|
name TEXT NOT NULL UNIQUE,
|
|
label TEXT NOT NULL,
|
|
description TEXT,
|
|
is_system BOOLEAN NOT NULL DEFAULT true,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
-- =============================================================================
|
|
-- ORGANIZATIONAL HIERARCHY
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE domains (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name TEXT NOT NULL,
|
|
color TEXT,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE areas (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
domain_id UUID NOT NULL REFERENCES domains(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
description TEXT,
|
|
status TEXT NOT NULL DEFAULT 'active',
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE projects (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
domain_id UUID NOT NULL REFERENCES domains(id) ON DELETE CASCADE,
|
|
area_id UUID REFERENCES areas(id) ON DELETE SET NULL,
|
|
name TEXT NOT NULL,
|
|
description TEXT,
|
|
status TEXT NOT NULL DEFAULT 'active',
|
|
priority INTEGER NOT NULL DEFAULT 3,
|
|
start_date DATE,
|
|
target_date DATE,
|
|
completed_at TIMESTAMPTZ,
|
|
tags TEXT[],
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE tasks (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
domain_id UUID NOT NULL REFERENCES domains(id) ON DELETE CASCADE,
|
|
area_id UUID REFERENCES areas(id) ON DELETE SET NULL,
|
|
project_id UUID REFERENCES projects(id) ON DELETE SET NULL,
|
|
parent_id UUID REFERENCES tasks(id) ON DELETE SET NULL,
|
|
title TEXT NOT NULL,
|
|
description TEXT,
|
|
priority INTEGER NOT NULL DEFAULT 3,
|
|
status TEXT NOT NULL DEFAULT 'open',
|
|
due_date DATE,
|
|
deadline TIMESTAMPTZ,
|
|
recurrence TEXT,
|
|
tags TEXT[],
|
|
context TEXT,
|
|
is_custom_context BOOLEAN NOT NULL DEFAULT false,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
completed_at TIMESTAMPTZ
|
|
);
|
|
|
|
CREATE TABLE notes (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
domain_id UUID NOT NULL REFERENCES domains(id) ON DELETE CASCADE,
|
|
title TEXT NOT NULL,
|
|
body TEXT,
|
|
content_format TEXT NOT NULL DEFAULT 'rich',
|
|
tags TEXT[],
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE lists (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
domain_id UUID NOT NULL REFERENCES domains(id) ON DELETE CASCADE,
|
|
area_id UUID REFERENCES areas(id) ON DELETE SET NULL,
|
|
project_id UUID REFERENCES projects(id) ON DELETE SET NULL,
|
|
name TEXT NOT NULL,
|
|
list_type TEXT NOT NULL DEFAULT 'checklist',
|
|
description TEXT,
|
|
tags TEXT[],
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE list_items (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
list_id UUID NOT NULL REFERENCES lists(id) ON DELETE CASCADE,
|
|
parent_item_id UUID REFERENCES list_items(id) ON DELETE SET NULL,
|
|
content TEXT NOT NULL,
|
|
completed BOOLEAN NOT NULL DEFAULT false,
|
|
completed_at TIMESTAMPTZ,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE links (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
domain_id UUID NOT NULL REFERENCES domains(id) ON DELETE CASCADE,
|
|
area_id UUID REFERENCES areas(id) ON DELETE SET NULL,
|
|
project_id UUID REFERENCES projects(id) ON DELETE SET NULL,
|
|
label TEXT NOT NULL,
|
|
url TEXT NOT NULL,
|
|
description TEXT,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE files (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
filename TEXT NOT NULL,
|
|
original_filename TEXT NOT NULL,
|
|
storage_path TEXT NOT NULL,
|
|
mime_type TEXT,
|
|
size_bytes INTEGER,
|
|
description TEXT,
|
|
tags TEXT[],
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
-- =============================================================================
|
|
-- SYSTEM LEVEL: Contacts
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE contacts (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
name TEXT NOT NULL,
|
|
company TEXT,
|
|
role TEXT,
|
|
email TEXT,
|
|
phone TEXT,
|
|
notes TEXT,
|
|
tags TEXT[],
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
-- =============================================================================
|
|
-- SYSTEM LEVEL: Appointments
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE appointments (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
title TEXT NOT NULL,
|
|
description TEXT,
|
|
location TEXT,
|
|
start_at TIMESTAMPTZ NOT NULL,
|
|
end_at TIMESTAMPTZ,
|
|
all_day BOOLEAN NOT NULL DEFAULT false,
|
|
recurrence TEXT,
|
|
tags TEXT[],
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
-- =============================================================================
|
|
-- SYSTEM LEVEL: Weblink Directory
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE weblink_folders (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
parent_id UUID REFERENCES weblink_folders(id) ON DELETE CASCADE,
|
|
name TEXT NOT NULL,
|
|
auto_generated BOOLEAN NOT NULL DEFAULT false,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
CREATE TABLE weblinks (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
label TEXT NOT NULL,
|
|
url TEXT NOT NULL,
|
|
description TEXT,
|
|
tags TEXT[],
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
-- =============================================================================
|
|
-- SYSTEM LEVEL: Daily Focus
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE daily_focus (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
focus_date DATE NOT NULL,
|
|
task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
slot INTEGER,
|
|
completed BOOLEAN NOT NULL DEFAULT false,
|
|
note TEXT,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
-- =============================================================================
|
|
-- SYSTEM LEVEL: Capture Queue
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE capture (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
raw_text TEXT NOT NULL,
|
|
processed BOOLEAN NOT NULL DEFAULT false,
|
|
converted_to_type TEXT,
|
|
converted_to_id UUID,
|
|
area_id UUID REFERENCES areas(id) ON DELETE SET NULL,
|
|
project_id UUID REFERENCES projects(id) ON DELETE SET NULL,
|
|
list_id UUID REFERENCES lists(id) ON DELETE SET NULL,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
-- =============================================================================
|
|
-- SYSTEM LEVEL: Reminders
|
|
-- =============================================================================
|
|
|
|
CREATE TABLE reminders (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
remind_at TIMESTAMPTZ NOT NULL,
|
|
delivered BOOLEAN NOT NULL DEFAULT false,
|
|
channel TEXT NOT NULL DEFAULT 'web',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
);
|
|
|
|
-- =============================================================================
|
|
-- JUNCTION TABLES
|
|
-- =============================================================================
|
|
|
|
-- Notes <-> Projects (M2M)
|
|
CREATE TABLE note_projects (
|
|
note_id UUID NOT NULL REFERENCES notes(id) ON DELETE CASCADE,
|
|
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
is_primary BOOLEAN NOT NULL DEFAULT false,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (note_id, project_id)
|
|
);
|
|
|
|
-- Files <-> any entity (polymorphic M2M)
|
|
CREATE TABLE file_mappings (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
file_id UUID NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
|
context_type TEXT NOT NULL,
|
|
context_id UUID NOT NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
UNIQUE (file_id, context_type, context_id)
|
|
);
|
|
|
|
-- Contacts <-> Tasks
|
|
CREATE TABLE contact_tasks (
|
|
contact_id UUID NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
|
|
task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
role TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (contact_id, task_id)
|
|
);
|
|
|
|
-- Contacts <-> Lists
|
|
CREATE TABLE contact_lists (
|
|
contact_id UUID NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
|
|
list_id UUID NOT NULL REFERENCES lists(id) ON DELETE CASCADE,
|
|
role TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (contact_id, list_id)
|
|
);
|
|
|
|
-- Contacts <-> List Items
|
|
CREATE TABLE contact_list_items (
|
|
contact_id UUID NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
|
|
list_item_id UUID NOT NULL REFERENCES list_items(id) ON DELETE CASCADE,
|
|
role TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (contact_id, list_item_id)
|
|
);
|
|
|
|
-- Contacts <-> Projects
|
|
CREATE TABLE contact_projects (
|
|
contact_id UUID NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
|
|
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
|
role TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (contact_id, project_id)
|
|
);
|
|
|
|
-- Contacts <-> Appointments
|
|
CREATE TABLE contact_appointments (
|
|
contact_id UUID NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
|
|
appointment_id UUID NOT NULL REFERENCES appointments(id) ON DELETE CASCADE,
|
|
role TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (contact_id, appointment_id)
|
|
);
|
|
|
|
-- Weblinks <-> Folders (M2M)
|
|
CREATE TABLE folder_weblinks (
|
|
folder_id UUID NOT NULL REFERENCES weblink_folders(id) ON DELETE CASCADE,
|
|
weblink_id UUID NOT NULL REFERENCES weblinks(id) ON DELETE CASCADE,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (folder_id, weblink_id)
|
|
);
|
|
|
|
-- =============================================================================
|
|
-- INDEXES
|
|
-- =============================================================================
|
|
|
|
-- Sort order indexes (used on every list render)
|
|
CREATE INDEX idx_domains_sort ON domains(sort_order);
|
|
CREATE INDEX idx_areas_sort ON areas(domain_id, sort_order);
|
|
CREATE INDEX idx_projects_sort ON projects(domain_id, sort_order);
|
|
CREATE INDEX idx_projects_area_sort ON projects(area_id, sort_order);
|
|
CREATE INDEX idx_tasks_project_sort ON tasks(project_id, sort_order);
|
|
CREATE INDEX idx_tasks_parent_sort ON tasks(parent_id, sort_order);
|
|
CREATE INDEX idx_tasks_domain_sort ON tasks(domain_id, sort_order);
|
|
CREATE INDEX idx_list_items_sort ON list_items(list_id, sort_order);
|
|
CREATE INDEX idx_list_items_parent_sort ON list_items(parent_item_id, sort_order);
|
|
CREATE INDEX idx_weblinks_sort ON weblink_folders(parent_id, sort_order);
|
|
|
|
-- Lookup indexes
|
|
CREATE INDEX idx_tasks_status ON tasks(status);
|
|
CREATE INDEX idx_tasks_due_date ON tasks(due_date);
|
|
CREATE INDEX idx_tasks_priority ON tasks(priority);
|
|
CREATE INDEX idx_projects_status ON projects(status);
|
|
CREATE INDEX idx_daily_focus_date ON daily_focus(focus_date);
|
|
CREATE INDEX idx_appointments_start ON appointments(start_at);
|
|
CREATE INDEX idx_capture_processed ON capture(processed);
|
|
CREATE INDEX idx_file_mappings_context ON file_mappings(context_type, context_id);
|
|
|