Skip to content

Database Schema (Supabase / Prisma) ​

Prisma Schema ​

prisma
// prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider  = "postgresql"
  url       = env("DATABASE_URL")
  directUrl = env("DIRECT_URL")
}

// ─── Users & Auth ───────────────────────────────────────────

model User {
  id            String   @id @default(uuid())
  email         String   @unique
  phone         String?
  nameEn        String?
  nameBn        String?
  nidArea       String?  // NID region code
  address       String?
  divisionCode  String?
  districtCode  String?
  thanaCode     String?
  avatarUrl     String?
  isVerified    Boolean  @default(false)
  createdAt     DateTime @default(now())
  updatedAt     DateTime @updatedAt

  reports           CivicReport[]
  emergencyContacts EmergencyContact[]
  deviceTokens      DeviceToken[]
  locationHistory   LocationSnapshot[]

  @@map("users")
}

model EmergencyContact {
  id      String @id @default(uuid())
  userId  String
  name    String
  phone   String
  relation String  // পরিবার | বন্ধু | সহকর্মী

  user User @relation(fields: [userId], references: [id])

  @@map("emergency_contacts")
}

model DeviceToken {
  id        String   @id @default(uuid())
  userId    String
  token     String   @unique  // FCM token
  platform  String   // android | ios
  updatedAt DateTime @updatedAt

  user User @relation(fields: [userId], references: [id])

  @@map("device_tokens")
}

// ─── Civic Reports ───────────────────────────────────────────

model CivicReport {
  id          String   @id @default(uuid())
  userId      String
  category    String   // road_damage | drainage | electricity ...
  subCategory String
  location    String   // Human-readable location
  lat         Float?
  lng         Float?
  severity    String   // low | medium | high | critical
  description String
  isEmergency Boolean  @default(false)
  status      String   @default("submitted")
                       // submitted | in_progress | resolved | closed
  ticketId    String?  // External ticket ID if govt API available
  audioUrl    String?  // Recorded voice (Supabase Storage)
  photoUrls   String[] // Report photos
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  user User @relation(fields: [userId], references: [id])

  @@index([userId])
  @@index([lat, lng])
  @@index([status])
  @@map("civic_reports")
}

// ─── Location & Risk ─────────────────────────────────────────

model LocationSnapshot {
  id        String   @id @default(uuid())
  userId    String
  lat       Float
  lng       Float
  accuracy  Float?
  capturedAt DateTime @default(now())

  user User @relation(fields: [userId], references: [id])

  @@index([userId, capturedAt])
  @@map("location_snapshots")
}

model RiskZone {
  id          String   @id @default(uuid())
  name        String
  nameBn      String
  description String?
  level       String   // low | medium | high | critical
  type        String   // flood | crime | accident | fire | construction
  boundary    Json     // GeoJSON polygon coordinates
  division    String?
  district    String?
  thana       String?
  activeFrom  DateTime?
  activeTo    DateTime?
  isActive    Boolean  @default(true)
  createdAt   DateTime @default(now())

  @@map("risk_zones")
}

// ─── Emergency Events ─────────────────────────────────────────

model EmergencyEvent {
  id          String   @id @default(uuid())
  userId      String
  trigger     String   // shake | volume | voice | manual
  lat         Float
  lng         Float
  audioUrl    String?
  contactsNotified Int @default(0)
  policeAlerted    Boolean @default(false)
  resolvedAt  DateTime?
  createdAt   DateTime @default(now())

  @@map("emergency_events")
}

// ─── BD Static Data ───────────────────────────────────────────

model PoliceStation {
  id         String  @id
  nameBn     String
  nameEn     String
  phone      String
  division   String
  district   String
  thana      String
  lat        Float
  lng        Float
  address    String

  @@map("police_stations")
}

model Hospital {
  id         String  @id
  nameBn     String
  nameEn     String
  phone      String
  type       String  // govt | private | clinic
  division   String
  district   String
  thana      String
  lat        Float
  lng        Float
  address    String
  beds       Int?
  emergency  Boolean @default(false)

  @@map("hospitals")
}

// ─── News ─────────────────────────────────────────────────────

model NewsArticle {
  id          String   @id @default(uuid())
  title       String
  titleBn     String?
  summary     String?
  sourceUrl   String   @unique
  source      String   // prothom_alo | bdnews24 | dhaka_tribune
  category    String?  // crime | politics | weather | disaster
  publishedAt DateTime
  fetchedAt   DateTime @default(now())
  isAlert     Boolean  @default(false)  // breaking/emergency news

  @@index([publishedAt])
  @@index([isAlert])
  @@map("news_articles")
}

Supabase Row Level Security (RLS) ​

sql
-- Users can only read/edit their own data
ALTER TABLE users ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users read own profile"
  ON users FOR SELECT
  USING (auth.uid()::text = id);

CREATE POLICY "Users update own profile"
  ON users FOR UPDATE
  USING (auth.uid()::text = id);

-- Reports: users see only their own
ALTER TABLE civic_reports ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users manage own reports"
  ON civic_reports FOR ALL
  USING (auth.uid()::text = user_id);

-- Risk zones: public read
ALTER TABLE risk_zones ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Public risk zones read"
  ON risk_zones FOR SELECT
  TO anon, authenticated
  USING (is_active = true);

-- Police stations: public read
ALTER TABLE police_stations ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Public police read"
  ON police_stations FOR SELECT TO anon, authenticated
  USING (true);
```\n\n

Documentation maintained by Dreams of X Team .