Files
1356/database-schema.md
T
2026-01-03 18:35:35 +01:00

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_profile is 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 );