Spaces:
Sleeping
Sleeping
| -- ThaΓ€t β Supabase schema | |
| -- Run once in Supabase SQL editor. | |
| create table if not exists users ( | |
| id uuid primary key, | |
| display_name text not null, | |
| autism_level text not null check (autism_level in ('Level 1', 'Level 2', 'Level 3')), | |
| age_range text not null check (age_range in ('child', 'teen', 'adult')), | |
| primary_goal text not null check (primary_goal in ('understand_others', 'express_myself', 'both')), | |
| hardest_emotions text[] not null default '{}', | |
| caregiver_name text, | |
| caregiver_email text, | |
| notifications_enabled boolean not null default false, | |
| created_at timestamptz not null default now(), | |
| updated_at timestamptz not null default now() | |
| ); | |
| create or replace function update_updated_at() | |
| returns trigger language plpgsql as $$ | |
| begin | |
| new.updated_at = now(); | |
| return new; | |
| end; | |
| $$; | |
| drop trigger if exists users_updated_at on users; | |
| create trigger users_updated_at | |
| before update on users | |
| for each row execute procedure update_updated_at(); | |
| create table if not exists sessions ( | |
| id uuid primary key default gen_random_uuid(), | |
| user_id uuid not null references users(id) on delete cascade, | |
| mode text not null check (mode in ('live', 'test', 'training')), | |
| overall_emotion text, | |
| duration_seconds int not null default 0, | |
| created_at timestamptz not null default now() | |
| ); | |
| create index if not exists sessions_user_id_idx on sessions(user_id); | |
| create index if not exists sessions_created_at_idx on sessions(created_at desc); | |
| create table if not exists emotion_records ( | |
| id uuid primary key default gen_random_uuid(), | |
| user_id uuid not null references users(id) on delete cascade, | |
| session_id uuid references sessions(id) on delete cascade, | |
| spoken_text text, | |
| text_emotion text, | |
| face_emotion text, | |
| body_emotion text, | |
| fusion_emotion text, | |
| conflict_type text, | |
| insight text, | |
| created_at timestamptz not null default now() | |
| ); | |
| create index if not exists emotion_records_user_id_idx on emotion_records(user_id); | |
| create table if not exists training_logs ( | |
| id uuid primary key default gen_random_uuid(), | |
| user_id uuid not null references users(id) on delete cascade, | |
| target_emotion text not null, | |
| detected_emotion text not null, | |
| score int not null check (score >= 0 and score <= 100), | |
| success boolean not null, | |
| feedback text, | |
| created_at timestamptz not null default now() | |
| ); | |
| create index if not exists training_logs_user_id_idx on training_logs(user_id); | |
| create index if not exists training_logs_emotion_idx on training_logs(target_emotion); | |
| create index if not exists training_logs_created_idx on training_logs(created_at desc); | |
| -- ββ Row Level Security ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| -- IMPORTANT: The backend uses the SERVICE ROLE key (SUPABASE_SERVICE_KEY). | |
| -- Service role bypasses RLS entirely β no policy needed for backend writes. | |
| -- RLS here protects against direct client-side access ONLY. | |
| -- Since the Flutter app writes through the backend (not direct Supabase SDK), | |
| -- we only need RLS if you ever add direct Supabase client access from Flutter. | |
| -- For now: enable RLS but add a permissive policy so backend writes work. | |
| -- When you add Supabase Auth to Flutter, replace these with auth.uid() policies. | |
| alter table users enable row level security; | |
| alter table sessions enable row level security; | |
| alter table emotion_records enable row level security; | |
| alter table training_logs enable row level security; | |
| -- Temporary open policies β backend service role bypasses RLS anyway. | |
| -- Replace with auth.uid() = user_id once you add Supabase Auth to Flutter. | |
| drop policy if exists "users: service write" on users; | |
| create policy "users: service write" on users | |
| for all using (true) with check (true); | |
| drop policy if exists "sessions: service write" on sessions; | |
| create policy "sessions: service write" on sessions | |
| for all using (true) with check (true); | |
| drop policy if exists "emotion_records: service write" on emotion_records; | |
| create policy "emotion_records: service write" on emotion_records | |
| for all using (true) with check (true); | |
| drop policy if exists "training_logs: service write" on training_logs; | |
| create policy "training_logs: service write" on training_logs | |
| for all using (true) with check (true); | |
| -- ββ When you add Supabase Auth, replace the above with: βββββββββββββββββββ | |
| -- drop policy if exists "users: own row" on users; | |
| -- create policy "users: own row" on users | |
| -- for all using (auth.uid() = id) with check (auth.uid() = id); | |
| -- (same pattern for sessions, emotion_records, training_logs) | |
| create or replace view emotion_progress_monthly as | |
| select | |
| user_id, | |
| target_emotion, | |
| date_trunc('month', created_at) as month, | |
| round(avg(score)) as avg_score, | |
| count(*) as attempts, | |
| sum(case when success then 1 else 0 end) as successes | |
| from training_logs | |
| group by user_id, target_emotion, date_trunc('month', created_at) | |
| order by user_id, month desc, target_emotion; | |