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_iduuidNO—FK → auth.users.id
nametextYES—Display name
emailtextYES—Copied from auth on signup
roletextYES'user''user' or 'admin' (RBAC)
is_onlinebooleanYESfalseReal-time online status
last_active_attimestamptzYES—Last activity ping
is_activebooleanYEStrueAccount active flag
admin_notestextYES—Internal 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
titletextNO—Project title (unique)
descriptiontextNO—Short display description
short_descriptiontextYES—Card subtitle
full_descriptiontextYES—Full detail page content (HTML)
technologiestext[]NO'{}'Tech tags array (React, Django, etc)
screenshotstext[]YES—Array of Supabase Storage URLs
pricenumeric(10,2)YES—Price in INR
show_pricebooleanYEStrueToggle price visibility
slugtextYES—URL-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_descriptiontextYES—SEO meta description
meta_keywordstext[]YES—SEO 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_iduuidNO—FK → auth.users.id
project_iduuidNO—FK → projects.id
amountnumeric(10,2)NO—Amount paid in INR
statustextNO'pending'pending / confirmed / in_progress / completed / cancelled
payment_statustextNO'pending'pending / paid / failed
progressintegerNO00–100 completion percentage
notestextYES—Admin notes
created_attimestamptzNOnow()—
updated_attimestamptzNOnow()Auto-updated via trigger

Foreign Keys:

  • purchases_project_id_fkey → projects(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_iduuidNO—FK → purchases.id
filenametextNO—Display filename
file_nametextYES—Alternate name field
file_urltextNO—Direct download URL
drive_linktextYES—Google Drive / cloud URL
file_typetextNO—source_code / documentation / demo_video / other
created_attimestamptzNOnow()—

Foreign Keys:

  • purchase_files_purchase_id_fkey → purchases(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_iduuidYES—FK → projects.id
customer_nametextNO—Submitter name
customer_emailtextNO—Contact email
customer_phonetextYES—Contact number
messagetextYES—Custom message
request_typetextYES—'whatsapp' or 'email'
statustextYES'pending'pending / in_progress / responded / completed
admin_notestextYES—Internal notes
handled_byuuidYES—FK → auth.users.id (admin)
handled_attimestamptzYES—When admin responded
created_attimestamptzNOnow()—

Foreign Keys:

  • project_requests_project_id_fkey → projects(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_iduuidYES—FK → auth.users.id (if logged in)
nametextNO—Submitter name
emailtextNO—Contact email
service_typetextNO—Selected service category
messagetextYES—Request details
statustextYES'pending'pending / in_progress / responded / completed
admin_notestextYES—Internal notes
handled_byuuidYES—FK → auth.users.id (admin)
handled_attimestamptzYES—When 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
nametextNO—Submitter name
emailtextNO—Contact email
messagetextNO—Inquiry message
statustextYES'pending'pending / in_progress / responded / completed
admin_notestextYES—Internal notes
handled_byuuidYES—FK → auth.users.id (admin)
handled_attimestamptzYES—When 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_iduuidYES—FK → auth.users.id
nametextNO—Submitter name
emailtextNO—Contact email
project_descriptiontextNO—Detailed request
budget_rangetextYES—Budget estimate
timelinetextYES—Expected 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
titletextNO—Notification title
messagetextNO—Notification body
typetextNO—info / warning / success / error
is_readbooleanNOfalseRead state
related_iduuidYES—Optional 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_iduuidNO—FK → auth.users.id
session_tokentextYES—Optional session identifier
ip_addressinetYES—User IP address
user_agenttextYES—Browser/device info
is_activebooleanYEStrueSession active flag
last_activitytimestamptzYES—Last user activity
expires_attimestamptzYES—Session 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_iduuidYES—FK → auth.users.id
action_typetextNO—create / update / delete / export
resource_typetextNO—project / purchase / user / request
resource_iduuidYES—ID of affected record
descriptiontextYES—Human-readable action
ip_addressinetYES—Admin IP
user_agenttextYES—Browser 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_idtextNO—Unique certificate ID
student_nametextNO—Student name
project_titletextNO—Completed project
issue_datedateNO—Certificate 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​