7.1 KiB
LifeTimer Database Schema, Supabase
This document expands the schema sketch in project.md into a more detailed proposal for Postgres tables.
Note, naming and exact types can be adjusted when creating migrations.
1. users
Core user record.
- id, uuid, primary key
- auth_provider_id, text or json, optional mapping to external providers
- username, text, unique, required
- email, text, unique, required
- avatar_url, text, nullable
- bio, text, nullable
- is_public_profile, boolean, default false
- countdown_start_date, timestamptz, nullable until bucket list is confirmed
- countdown_end_date, timestamptz, nullable until challenge has started
- created_at, timestamptz, default now
- updated_at, timestamptz, default now
Row Level Security
- Users can select and update only their own row.
- Public profile fields such as username and avatar, plus limited stats, can be exposed to other users only when
is_public_profileis true.
2. goals
Represents one goal in the bucket list.
- id, uuid, primary key
- owner_id, uuid, foreign key to users.id
- title, text, required
- description, text, nullable
- progress, integer, 0 to 100, default 0
- location_lat, double precision, nullable
- location_lng, double precision, nullable
- location_name, text, nullable
- image_url, text, nullable
- completed, boolean, default false
- created_at, timestamptz, default now
- updated_at, timestamptz, default now
Constraints
- Each user can have at most 20 goals in total. The countdown can start once there is at least one goal.
3. goal_steps
Optional more granular checklist per goal.
- id, uuid, primary key
- goal_id, uuid, foreign key to goals.id
- title, text
- is_done, boolean, default false
- order_index, integer
- created_at, timestamptz, default now
4. followers
Social graph between users.
- id, uuid, primary key
- user_id, uuid, the user being followed
- follower_id, uuid, the user who follows
- created_at, timestamptz, default now
Constraint
- unique user_id, follower_id
5. activities
Timeline of events used for feeds and analytics.
- id, uuid, primary key
- user_id, uuid, foreign key to users.id
- type, text, for example goal_created, goal_completed, countdown_started
- payload, jsonb, optional extra data
- created_at, timestamptz, default now
6. notifications
Optional table for storing notifications when server side delivery is needed.
- id, uuid, primary key
- user_id, uuid, foreign key to users.id
- type, text
- title, text
- body, text
- scheduled_for, timestamptz, nullable
- delivered_at, timestamptz, nullable
7. indexes and performance
- Index on goals.owner_id.
- Index on activities.user_id and created_at for feed queries.
- Index on followers.user_id and followers.follower_id.
8. Example Row Level Security (RLS) policies
The following snippets show how to enforce the privacy model using PostgreSQL RLS in Supabase.
8.1 Enable RLS on tables
alter table public.users enable row level security;
alter table public.goals enable row level security;
alter table public.goal_steps enable row level security;
alter table public.followers enable row level security;
alter table public.activities enable row level security;
alter table public.notifications enable row level security;
8.2 users
Users can only see and update their own profile row.
create policy "Users can select their own profile"
on public.users
for select
using ( auth.uid() = id );
create policy "Users can update their own profile"
on public.users
for update
using ( auth.uid() = id )
with check ( auth.uid() = id );
Social and leaderboard queries should not select directly from public.users with broad access. Instead, implement read-only views or RPC functions that expose only non-sensitive fields for public profiles and are called from secure backend code (for example Supabase Edge Functions using the service role key).
8.3 goals
Goals are always private to their owner.
create policy "Users can read their own goals"
on public.goals
for select
using ( auth.uid() = owner_id );
create policy "Users can insert their own goals"
on public.goals
for insert
with check ( auth.uid() = owner_id );
create policy "Users can update their own goals"
on public.goals
for update
using ( auth.uid() = owner_id )
with check ( auth.uid() = owner_id );
create policy "Users can delete their own goals"
on public.goals
for delete
using ( auth.uid() = owner_id );
8.4 goal_steps
Access to goal steps is tied to ownership of the parent goal.
create policy "Users can read steps for their own goals"
on public.goal_steps
for select
using (
exists (
select 1 from public.goals g
where g.id = goal_id and g.owner_id = auth.uid()
)
);
create policy "Users can insert steps for their own goals"
on public.goal_steps
for insert
with check (
exists (
select 1 from public.goals g
where g.id = goal_id and g.owner_id = auth.uid()
)
);
create policy "Users can update steps for their own goals"
on public.goal_steps
for update
using (
exists (
select 1 from public.goals g
where g.id = goal_id and g.owner_id = auth.uid()
)
)
with check (
exists (
select 1 from public.goals g
where g.id = goal_id and g.owner_id = auth.uid()
)
);
create policy "Users can delete steps for their own goals"
on public.goal_steps
for delete
using (
exists (
select 1 from public.goals g
where g.id = goal_id and g.owner_id = auth.uid()
)
);
8.5 followers
Follow relationships are visible only to the users involved. Either side can remove a relationship.
create policy "Users can read their follower relationships"
on public.followers
for select
using ( auth.uid() = user_id or auth.uid() = follower_id );
create policy "Users can follow others"
on public.followers
for insert
with check ( auth.uid() = follower_id );
create policy "Users can unfollow or remove followers"
on public.followers
for delete
using ( auth.uid() = user_id or auth.uid() = follower_id );
8.6 activities
Users see only their own activity timeline. Public feeds and leaderboards should use separate, read-only views or RPC functions that return aggregated activity for public profiles only.
create policy "Users can read their own activity"
on public.activities
for select
using ( auth.uid() = user_id );
create policy "Users can insert their own activity events"
on public.activities
for insert
with check ( auth.uid() = user_id );
8.7 notifications
Notifications are private to the recipient user.
create policy "Users can read their own notifications"
on public.notifications
for select
using ( auth.uid() = user_id );
create policy "Users can receive their own notifications"
on public.notifications
for insert
with check ( auth.uid() = user_id );
create policy "Users can delete their own notifications"
on public.notifications
for delete
using ( auth.uid() = user_id );