-- Calendar & Contacts API Schema CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- Users CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email TEXT NOT NULL, password_hash TEXT NOT NULL, timezone TEXT NOT NULL DEFAULT 'UTC', is_active BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ ); CREATE UNIQUE INDEX idx_users_email ON users (email) WHERE deleted_at IS NULL; -- Refresh Tokens CREATE TABLE refresh_tokens ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id), token_hash TEXT NOT NULL UNIQUE, expires_at TIMESTAMPTZ NOT NULL, revoked_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_refresh_tokens_user_id ON refresh_tokens (user_id); -- API Keys CREATE TABLE api_keys ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id), name TEXT NOT NULL, key_hash TEXT NOT NULL UNIQUE, scopes JSONB NOT NULL DEFAULT '{}', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), revoked_at TIMESTAMPTZ ); CREATE INDEX idx_api_keys_user_id ON api_keys (user_id); -- Calendars CREATE TABLE calendars ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), owner_id UUID NOT NULL REFERENCES users(id), name TEXT NOT NULL, color TEXT NOT NULL DEFAULT '#3B82F6', is_public BOOLEAN NOT NULL DEFAULT false, public_token TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ ); CREATE INDEX idx_calendars_owner_id ON calendars (owner_id); CREATE UNIQUE INDEX idx_calendars_public_token ON calendars (public_token) WHERE public_token IS NOT NULL; -- Calendar Members CREATE TABLE calendar_members ( calendar_id UUID NOT NULL REFERENCES calendars(id), user_id UUID NOT NULL REFERENCES users(id), role TEXT NOT NULL CHECK (role IN ('owner', 'editor', 'viewer')), PRIMARY KEY (calendar_id, user_id) ); -- Events CREATE TABLE events ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), calendar_id UUID NOT NULL REFERENCES calendars(id), title TEXT NOT NULL, description TEXT, location TEXT, start_time TIMESTAMPTZ NOT NULL, end_time TIMESTAMPTZ NOT NULL, timezone TEXT NOT NULL DEFAULT 'UTC', all_day BOOLEAN NOT NULL DEFAULT false, recurrence_rule TEXT, tags TEXT[] NOT NULL DEFAULT '{}', created_by UUID NOT NULL REFERENCES users(id), updated_by UUID NOT NULL REFERENCES users(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ ); CREATE INDEX idx_events_calendar_start ON events (calendar_id, start_time); CREATE INDEX idx_events_start_time ON events (start_time); CREATE INDEX idx_events_tags ON events USING GIN (tags); -- Event Reminders CREATE TABLE event_reminders ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), event_id UUID NOT NULL REFERENCES events(id), minutes_before INTEGER NOT NULL CHECK (minutes_before >= 0 AND minutes_before <= 10080) ); CREATE INDEX idx_event_reminders_event_id ON event_reminders (event_id); -- Event Attendees CREATE TABLE event_attendees ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), event_id UUID NOT NULL REFERENCES events(id), user_id UUID REFERENCES users(id), email TEXT, status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'accepted', 'declined', 'tentative')) ); CREATE INDEX idx_event_attendees_event_id ON event_attendees (event_id); -- Event Exceptions (for recurrence) CREATE TABLE event_exceptions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), event_id UUID NOT NULL REFERENCES events(id), exception_date DATE NOT NULL, action TEXT NOT NULL DEFAULT 'skip' CHECK (action IN ('skip')) ); CREATE INDEX idx_event_exceptions_event_id ON event_exceptions (event_id); -- Event Attachments CREATE TABLE event_attachments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), event_id UUID NOT NULL REFERENCES events(id), file_url TEXT NOT NULL ); CREATE INDEX idx_event_attachments_event_id ON event_attachments (event_id); -- Contacts CREATE TABLE contacts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), owner_id UUID NOT NULL REFERENCES users(id), first_name TEXT, last_name TEXT, email TEXT, phone TEXT, company TEXT, notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ ); CREATE INDEX idx_contacts_owner_id ON contacts (owner_id); CREATE INDEX idx_contacts_search ON contacts (owner_id, first_name, last_name, email, company); -- Booking Links CREATE TABLE booking_links ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), calendar_id UUID NOT NULL REFERENCES calendars(id), token TEXT NOT NULL UNIQUE, duration_minutes INTEGER NOT NULL, buffer_minutes INTEGER NOT NULL DEFAULT 0, timezone TEXT NOT NULL DEFAULT 'UTC', working_hours JSONB NOT NULL DEFAULT '{}', active BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_booking_links_token ON booking_links (token); -- Audit Logs CREATE TABLE audit_logs ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), entity_type TEXT NOT NULL, entity_id UUID NOT NULL, action TEXT NOT NULL, user_id UUID NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_audit_logs_entity ON audit_logs (entity_type, entity_id); -- Calendar Subscriptions (external iCal URL sources) CREATE TABLE calendar_subscriptions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), calendar_id UUID NOT NULL REFERENCES calendars(id), source_url TEXT NOT NULL, last_synced_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_calendar_subscriptions_calendar_id ON calendar_subscriptions (calendar_id);