-- Users table (minimal) CREATE TABLE IF NOT EXISTS users ( id UUID PRIMARY KEY, username VARCHAR(255) UNIQUE NOT NULL, password_hashed BYTEA NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Notifications table CREATE TABLE IF NOT EXISTS notifications ( id UUID PRIMARY KEY, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE, discord_user_id VARCHAR(100), discord_enabled BOOLEAN DEFAULT FALSE, ntfy_topic VARCHAR(255), ntfy_enabled BOOLEAN DEFAULT FALSE, web_push_enabled BOOLEAN DEFAULT FALSE, last_message_sent TIMESTAMP, current_notification_status VARCHAR(50) DEFAULT 'inactive', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Push subscriptions for web push notifications CREATE TABLE IF NOT EXISTS push_subscriptions ( id UUID PRIMARY KEY, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE, endpoint TEXT NOT NULL, p256dh TEXT NOT NULL, auth TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- ── Routines ──────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS routines ( id UUID PRIMARY KEY, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, description TEXT, icon VARCHAR(100), location VARCHAR(255), environment_prompts JSON DEFAULT '[]', habit_stack_after VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS routine_steps ( id UUID PRIMARY KEY, routine_id UUID REFERENCES routines(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, duration_minutes INTEGER, position INTEGER NOT NULL, instructions TEXT, step_type VARCHAR(50) DEFAULT 'generic', media_url TEXT ); CREATE TABLE IF NOT EXISTS routine_sessions ( id UUID PRIMARY KEY, routine_id UUID REFERENCES routines(id) ON DELETE CASCADE, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE, status VARCHAR(20) DEFAULT 'active', current_step_index INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, paused_at TIMESTAMP, completed_at TIMESTAMP, abort_reason TEXT, actual_duration_minutes INTEGER ); CREATE TABLE IF NOT EXISTS routine_schedules ( id UUID PRIMARY KEY, routine_id UUID REFERENCES routines(id) ON DELETE CASCADE, days JSON DEFAULT '[]', time VARCHAR(5), remind BOOLEAN DEFAULT FALSE ); CREATE TABLE IF NOT EXISTS routine_session_notes ( id UUID PRIMARY KEY, session_id UUID REFERENCES routine_sessions(id) ON DELETE CASCADE, step_index INTEGER, note TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS routine_templates ( id UUID PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT, icon VARCHAR(100), category VARCHAR(50) DEFAULT 'Other', created_by_admin BOOLEAN DEFAULT FALSE ); CREATE TABLE IF NOT EXISTS routine_template_steps ( id UUID PRIMARY KEY, template_id UUID REFERENCES routine_templates(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, instructions TEXT, step_type VARCHAR(50) DEFAULT 'generic', duration_minutes INTEGER, media_url TEXT, position INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS routine_tags ( id UUID PRIMARY KEY, name VARCHAR(255) NOT NULL, color VARCHAR(20) DEFAULT '#888888' ); CREATE TABLE IF NOT EXISTS routine_routine_tags ( routine_id UUID REFERENCES routines(id) ON DELETE CASCADE, tag_id UUID REFERENCES routine_tags(id) ON DELETE CASCADE, PRIMARY KEY (routine_id, tag_id) ); CREATE TABLE IF NOT EXISTS routine_streaks ( id UUID PRIMARY KEY, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE, routine_id UUID REFERENCES routines(id) ON DELETE CASCADE, current_streak INTEGER DEFAULT 0, longest_streak INTEGER DEFAULT 0, last_completed_date DATE ); -- ── Rewards ─────────────────────────────────────────────── CREATE TABLE IF NOT EXISTS reward_pool ( id UUID PRIMARY KEY, category VARCHAR(50) NOT NULL, content TEXT NOT NULL, emoji VARCHAR(10), rarity VARCHAR(20) DEFAULT 'common' ); CREATE TABLE IF NOT EXISTS user_rewards ( id UUID PRIMARY KEY, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE, reward_id UUID REFERENCES reward_pool(id) ON DELETE CASCADE, earned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, context VARCHAR(50) ); -- ── User Preferences ────────────────────────────────────── CREATE TABLE IF NOT EXISTS user_preferences ( id UUID PRIMARY KEY, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE, sound_enabled BOOLEAN DEFAULT FALSE, haptic_enabled BOOLEAN DEFAULT TRUE, show_launch_screen BOOLEAN DEFAULT TRUE, celebration_style VARCHAR(50) DEFAULT 'standard', timezone_offset INTEGER DEFAULT 0, timezone_name VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- ── Step Results (per-step tracking within sessions) ─────── CREATE TABLE IF NOT EXISTS routine_step_results ( id UUID PRIMARY KEY, session_id UUID REFERENCES routine_sessions(id) ON DELETE CASCADE, step_id UUID REFERENCES routine_steps(id) ON DELETE CASCADE, step_index INTEGER NOT NULL, result VARCHAR(20) NOT NULL, duration_seconds INTEGER, completed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- ── Medications ───────────────────────────────────────────── CREATE TABLE IF NOT EXISTS medications ( id UUID PRIMARY KEY, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE, name VARCHAR(255) NOT NULL, dosage VARCHAR(100) NOT NULL, unit VARCHAR(50) NOT NULL, frequency VARCHAR(50) NOT NULL, times JSON DEFAULT '[]', days_of_week JSON DEFAULT '[]', interval_days INTEGER, start_date DATE, next_dose_date DATE, notes TEXT, active BOOLEAN DEFAULT TRUE, quantity_remaining INTEGER, refill_date DATE, pharmacy_notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS med_logs ( id UUID PRIMARY KEY, medication_id UUID REFERENCES medications(id) ON DELETE CASCADE, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE, action VARCHAR(20) NOT NULL, scheduled_time VARCHAR(5), notes TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- ── Adaptive Medication Settings ───────────────────────────── CREATE TABLE IF NOT EXISTS adaptive_med_settings ( id UUID PRIMARY KEY, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE, adaptive_timing_enabled BOOLEAN DEFAULT FALSE, adaptive_mode VARCHAR(20) DEFAULT 'shift_all', presence_tracking_enabled BOOLEAN DEFAULT FALSE, nagging_enabled BOOLEAN DEFAULT TRUE, nag_interval_minutes INTEGER DEFAULT 15, max_nag_count INTEGER DEFAULT 4, quiet_hours_start TIME, quiet_hours_end TIME, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- ── User Discord Presence Tracking ──────────────────────────── CREATE TABLE IF NOT EXISTS user_presence ( id UUID PRIMARY KEY, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE, discord_user_id VARCHAR(255), last_online_at TIMESTAMP, last_offline_at TIMESTAMP, is_currently_online BOOLEAN DEFAULT FALSE, typical_wake_time TIME, presence_history JSONB DEFAULT '[]', updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- ── Adaptive Medication Schedules (Daily Tracking) ─────────── CREATE TABLE IF NOT EXISTS medication_schedules ( id UUID PRIMARY KEY, medication_id UUID REFERENCES medications(id) ON DELETE CASCADE, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE, base_time TIME NOT NULL, adjusted_time TIME, adjustment_date DATE NOT NULL, adjustment_minutes INTEGER DEFAULT 0, nag_count INTEGER DEFAULT 0, last_nag_at TIMESTAMP, status VARCHAR(20) DEFAULT 'pending', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_med_schedules_user_date ON medication_schedules(user_uuid, adjustment_date); CREATE INDEX IF NOT EXISTS idx_med_schedules_status ON medication_schedules(status); -- ── Snitch System ───────────────────────────────────────────── CREATE TABLE IF NOT EXISTS snitch_settings ( id UUID PRIMARY KEY, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE, snitch_enabled BOOLEAN DEFAULT FALSE, trigger_after_nags INTEGER DEFAULT 4, trigger_after_missed_doses INTEGER DEFAULT 1, max_snitches_per_day INTEGER DEFAULT 2, require_consent BOOLEAN DEFAULT TRUE, consent_given BOOLEAN DEFAULT FALSE, snitch_cooldown_hours INTEGER DEFAULT 4, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS snitch_contacts ( id UUID PRIMARY KEY, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE, contact_name VARCHAR(255) NOT NULL, contact_type VARCHAR(50) NOT NULL, contact_value VARCHAR(255) NOT NULL, priority INTEGER DEFAULT 1, notify_all BOOLEAN DEFAULT FALSE, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS snitch_log ( id UUID PRIMARY KEY, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE, contact_id UUID REFERENCES snitch_contacts(id) ON DELETE SET NULL, medication_id UUID REFERENCES medications(id) ON DELETE SET NULL, trigger_reason VARCHAR(100) NOT NULL, snitch_count_today INTEGER DEFAULT 1, message_content TEXT, sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, delivered BOOLEAN DEFAULT FALSE ); CREATE INDEX IF NOT EXISTS idx_snitch_log_user_date ON snitch_log(user_uuid, DATE(sent_at)); CREATE INDEX IF NOT EXISTS idx_snitch_contacts_user ON snitch_contacts(user_uuid, is_active); -- ── Migrations ────────────────────────────────────────────── -- Add IANA timezone name to user preferences (run once on existing DBs) ALTER TABLE user_preferences ADD COLUMN IF NOT EXISTS timezone_name VARCHAR(100);