Skip to main content

Database Schema

Complete database structure for the NS Software Solutions website.

Database: PostgreSQL 15+ via Supabase
Last Updated: April 29, 2026

All tables have Row Level Security (RLS) enabled for data isolation.

Tables Overview

1. profiles

Stores user profile data. Auto-created on signup via handle_new_user() trigger.

ColumnTypeNullableDefaultNotes
iduuidNOgen_random_uuid()Primary key
user_iduuidNOFK → auth.users.id
nametextYESDisplay name
emailtextYESCopied from auth on signup
roletextYES'user''user' or 'admin' (RBAC)
is_onlinebooleanYESfalseReal-time online status
last_active_attimestamptzYESLast activity ping
is_activebooleanYEStrueAccount active flag
admin_notestextYESInternal admin notes
created_attimestamptzNOnow()
updated_attimestamptzNOnow()Auto-updated via trigger

Indexes:

  • idx_profiles_user_id on user_id
  • idx_profiles_role on role
  • idx_profiles_is_online on is_online
  • idx_profiles_last_active_at on last_active_at
  • idx_profiles_role_active on (role, is_active) (composite)

RLS Policies:

  • Users can read/update own profile
  • Admins can read/update all profiles

2. projects

The project catalog. Publicly readable (active + published only).

ColumnTypeNullableDefaultNotes
iduuidNOgen_random_uuid()Primary key
titletextNOProject title (unique)
descriptiontextNOShort display description
short_descriptiontextYESCard subtitle
full_descriptiontextYESFull detail page content (HTML)
technologiestext[]NO'{}'Tech tags array (React, Django, etc)
screenshotstext[]YESArray of Supabase Storage URLs
pricenumeric(10,2)YESPrice in INR
show_pricebooleanYEStrueToggle price visibility
slugtextYESURL-friendly identifier (unique)
featuredbooleanYESfalseFeatured badge on catalog
ieeebooleanYESfalseIEEE certification badge
documentation_addonbooleanYESfalseDocumentation available flag
statustextYES'active''active' or 'inactive'
visibilitytextYES'published''published' or 'draft'
meta_descriptiontextYESSEO meta description
meta_keywordstext[]YESSEO keywords array
created_attimestamptzNOnow()
updated_attimestamptzNOnow()Auto-updated via trigger

Indexes:

  • idx_projects_status on status
  • idx_projects_featured on featured
  • idx_projects_slug on slug
  • idx_projects_created_at on created_at

RLS Policies:

  • Anonymous users read active + published only
  • Authenticated users read all active projects
  • Admins read/write all

3. purchases

Tracks project purchases assigned by admin to users.

ColumnTypeNullableDefaultNotes
iduuidNOgen_random_uuid()Primary key
user_iduuidNOFK → auth.users.id
project_iduuidNOFK → projects.id
amountnumeric(10,2)NOAmount paid in INR
statustextNO'pending'pending / confirmed / in_progress / completed / cancelled
payment_statustextNO'pending'pending / paid / failed
progressintegerNO00–100 completion percentage
notestextYESAdmin notes
created_attimestamptzNOnow()
updated_attimestamptzNOnow()Auto-updated via trigger

Foreign Keys:

  • purchases_project_id_fkeyprojects(id)

Indexes:

  • idx_purchases_user_id on user_id
  • idx_purchases_project_id on project_id
  • idx_purchases_status on status
  • idx_purchases_payment_status on payment_status
  • idx_purchases_user_status on (user_id, status) (composite)

RLS Policies:

  • Users read/create own purchases
  • Admins read/write all purchases

4. purchase_files

Files delivered to users as part of a purchase.

ColumnTypeNullableDefaultNotes
iduuidNOgen_random_uuid()Primary key
purchase_iduuidNOFK → purchases.id
filenametextNODisplay filename
file_nametextYESAlternate name field
file_urltextNODirect download URL
drive_linktextYESGoogle Drive / cloud URL
file_typetextNOsource_code / documentation / demo_video / other
created_attimestamptzNOnow()

Foreign Keys:

  • purchase_files_purchase_id_fkeypurchases(id) ON DELETE CASCADE

Indexes:

  • idx_purchase_files_purchase_id on purchase_id

RLS Policies:

  • Users read own purchase files
  • Admins read/write all

5. project_requests

Requests submitted from the public project catalog (WhatsApp/Email buttons).

ColumnTypeNullableDefaultNotes
iduuidNOgen_random_uuid()Primary key
project_iduuidYESFK → projects.id
customer_nametextNOSubmitter name
customer_emailtextNOContact email
customer_phonetextYESContact number
messagetextYESCustom message
request_typetextYES'whatsapp' or 'email'
statustextYES'pending'pending / in_progress / responded / completed
admin_notestextYESInternal notes
handled_byuuidYESFK → auth.users.id (admin)
handled_attimestamptzYESWhen admin responded
created_attimestamptzNOnow()

Foreign Keys:

  • project_requests_project_id_fkeyprojects(id) ON DELETE SET NULL

Indexes:

  • idx_project_requests_status on status
  • idx_project_requests_project_id on project_id
  • idx_project_requests_created_at on created_at

RLS Policies:

  • Anonymous users insert (create requests)
  • Admins read/update all
  • Prevent user read access (guest data)

6. service_requests

Requests submitted from the Services page.

ColumnTypeNullableDefaultNotes
iduuidNOgen_random_uuid()Primary key
user_iduuidYESFK → auth.users.id (if logged in)
nametextNOSubmitter name
emailtextNOContact email
service_typetextNOSelected service category
messagetextYESRequest details
statustextYES'pending'pending / in_progress / responded / completed
admin_notestextYESInternal notes
handled_byuuidYESFK → auth.users.id (admin)
handled_attimestamptzYESWhen admin responded
created_attimestamptzNOnow()

Foreign Keys:

  • None (optional user_id)

Indexes:

  • idx_service_requests_status on status
  • idx_service_requests_user_id on user_id
  • idx_service_requests_created_at on created_at

RLS Policies:

  • Anonymous users insert
  • Admins read/update all

7. contact_messages

Submissions from the Contact form.

ColumnTypeNullableDefaultNotes
iduuidNOgen_random_uuid()Primary key
nametextNOSubmitter name
emailtextNOContact email
messagetextNOInquiry message
statustextYES'pending'pending / in_progress / responded / completed
admin_notestextYESInternal notes
handled_byuuidYESFK → auth.users.id (admin)
handled_attimestamptzYESWhen admin responded
created_attimestamptzNOnow()

Indexes:

  • idx_contact_messages_status on status
  • idx_contact_messages_created_at on created_at

RLS Policies:

  • Anonymous users insert
  • Admins read/update all

8. custom_requests

Custom project requests from logged-in users.

ColumnTypeNullableDefaultNotes
iduuidNOgen_random_uuid()Primary key
user_iduuidYESFK → auth.users.id
nametextNOSubmitter name
emailtextNOContact email
project_descriptiontextNODetailed request
budget_rangetextYESBudget estimate
timelinetextYESExpected delivery
statustextYES'pending'pending / contacted / completed
created_attimestamptzNOnow()

Indexes:

  • idx_custom_requests_user_id on user_id
  • idx_custom_requests_status on status

RLS Policies:

  • Users read/create own requests
  • Admins read all

9. notifications

Admin-facing notification feed.

ColumnTypeNullableDefaultNotes
iduuidNOgen_random_uuid()Primary key
titletextNONotification title
messagetextNONotification body
typetextNOinfo / warning / success / error
is_readbooleanNOfalseRead state
related_iduuidYESOptional FK to related record
created_attimestamptzNOnow()

Indexes:

  • idx_notifications_is_read on is_read
  • idx_notifications_created_at on created_at

RLS Policies:

  • Admins read/update all notifications

10. user_sessions

Tracks active user sessions for admin visibility.

ColumnTypeNullableDefaultNotes
iduuidNOgen_random_uuid()Primary key
user_iduuidNOFK → auth.users.id
session_tokentextYESOptional session identifier
ip_addressinetYESUser IP address
user_agenttextYESBrowser/device info
is_activebooleanYEStrueSession active flag
last_activitytimestamptzYESLast user activity
expires_attimestamptzYESSession expiration time
created_attimestamptzNOnow()

Indexes:

  • idx_user_sessions_user_id on user_id
  • idx_user_sessions_is_active on is_active
  • idx_user_sessions_expires_at on expires_at

RLS Policies:

  • Users read own sessions
  • Admins read all sessions

11. admin_actions

Audit log for admin operations.

ColumnTypeNullableDefaultNotes
iduuidNOgen_random_uuid()Primary key
admin_iduuidYESFK → auth.users.id
action_typetextNOcreate / update / delete / export
resource_typetextNOproject / purchase / user / request
resource_iduuidYESID of affected record
descriptiontextYESHuman-readable action
ip_addressinetYESAdmin IP
user_agenttextYESBrowser info
created_attimestamptzNOnow()

Indexes:

  • idx_admin_actions_admin_id on admin_id
  • idx_admin_actions_resource_type on resource_type
  • idx_admin_actions_created_at on created_at

RLS Policies:

  • Admins read all actions
  • Users cannot access this table

12. admin_settings

Certificate and verification records.

ColumnTypeNullableDefaultNotes
iduuidNOgen_random_uuid()Primary key
certificate_idtextNOUnique certificate ID
student_nametextNOStudent name
project_titletextNOCompleted project
issue_datedateNOCertificate issue date
validbooleanYEStrueCertificate validity
created_attimestamptzNOnow()

RLS Policies:

  • Public can verify certificate
  • Admins read/write all

Database Functions & Triggers

handle_new_user()

Trigger: AFTER INSERT ON auth.users

Auto-creates a profiles record when a new user signs up. Copies name and email from auth metadata.

CREATE FUNCTION public.handle_new_user()
RETURNS trigger AS $$
BEGIN
INSERT INTO public.profiles (user_id, email, name, role)
VALUES (new.id, new.email, new.raw_user_meta_data->>'name', 'user');
RETURN new;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

update_updated_at_column()

Trigger: BEFORE UPDATE on profiles, projects, purchases

Sets updated_at = now() automatically.

CREATE FUNCTION public.update_updated_at_column()
RETURNS trigger AS $$
BEGIN
new.updated_at = now();
RETURN new;
END;
$$ LANGUAGE plpgsql;

update_user_activity(user_uuid uuid)

Called by: useUserActivity hook every 2 minutes

Updates user's last activity timestamp and online status.

CREATE FUNCTION public.update_user_activity(user_uuid uuid)
RETURNS void AS $$
BEGIN
UPDATE public.profiles
SET last_active_at = now(), is_online = true
WHERE user_id = user_uuid;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

mark_user_offline(user_uuid uuid)

Called by: useUserActivity hook on page unload

Sets is_online = false for user.

CREATE FUNCTION public.mark_user_offline(user_uuid uuid)
RETURNS void AS $$
BEGIN
UPDATE public.profiles
SET is_online = false
WHERE user_id = user_uuid;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

Entity Relationship Diagram