Files
setrip/prisma/schema.prisma
T
2026-05-07 18:47:45 +07:00

200 lines
5.3 KiB
Plaintext

generator client {
provider = "prisma-client"
output = "../app/generated/prisma"
}
datasource db {
provider = "postgresql"
}
model User {
id String @id @default(cuid())
name String
email String @unique
/// Hash bcrypt. Null untuk user yang sign-in via OAuth (mis. Google).
password String?
image String?
/// Diisi PrismaAdapter NextAuth saat email diverifikasi provider OAuth (Google selalu sudah verified).
emailVerified DateTime?
/// Apakah user telah menyetujui Syarat & Ketentuan dan Kebijakan Privasi
acceptedTermsAndPrivacy Boolean @default(false)
/// Waktu user menyetujui Syarat & Ketentuan dan Kebijakan Privasi
acceptedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
accounts Account[]
trips Trip[]
participations TripParticipant[]
tripReviews TripReview[]
organizerVerification OrganizerVerification? @relation("OrganizerVerificationOwner")
reviewedVerifications OrganizerVerification[] @relation("OrganizerVerificationReviewer")
}
/// Tabel link akun OAuth pihak ketiga (Google, dst). Diisi oleh PrismaAdapter NextAuth.
/// Session tidak pakai DB — kita pakai JWT, jadi Session/VerificationToken tidak perlu.
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String?
access_token String?
expires_at Int?
token_type String?
scope String?
id_token String?
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model OrganizerVerification {
id String @id @default(cuid())
userId String @unique
user User @relation("OrganizerVerificationOwner", fields: [userId], references: [id], onDelete: Cascade)
/// Nama lengkap sesuai KTP
fullName String
/// NIK terenkripsi (AES-256-GCM, base64). Plaintext tidak disimpan.
nikEncrypted String
/// HMAC-SHA256(NIK + pepper) untuk uniqueness lookup tanpa membuka plaintext.
nikHash String @unique
birthDate DateTime
address String
/// Storage key foto KTP (mis. `ktp/<id>.jpg`). File disimpan terenkripsi di luar /public.
ktpImageKey String
/// Storage key selfie memegang KTP.
selfieKey String
bankName String
bankAccountNumber String
bankAccountName String
status VerificationStatus @default(PENDING)
rejectionReason String?
reviewedAt DateTime?
reviewedById String?
reviewedBy User? @relation("OrganizerVerificationReviewer", fields: [reviewedById], references: [id])
verifiedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
enum VerificationStatus {
PENDING
APPROVED
REJECTED
}
model Trip {
id String @id @default(cuid())
title String
description String?
/// Kategori aktivitas.
category ActivityCategory @default(HIKING)
/// Destinasi/subjek aktivitas — interpretasinya tergantung kategori (gunung untuk HIKING, spot untuk DIVING, venue untuk CONCERT, tema untuk WORKSHOP, dst).
destination String
location String
/// Titik kumpul / meeting point (teks bebas)
meetingPoint String?
/// Itinerary hari per hari (teks bebas, bullet OK)
itinerary String?
/// Yang termasuk harga (teks bebas)
whatsIncluded String?
/// Yang tidak termasuk (teks bebas)
whatsExcluded String?
date DateTime
endDate DateTime?
maxParticipants Int
price Int
status TripStatus @default(OPEN)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
organizerId String
organizer User @relation(fields: [organizerId], references: [id])
participants TripParticipant[]
images TripImage[]
reviews TripReview[]
@@index([category, status, date])
}
model TripReview {
id String @id @default(cuid())
rating Int
comment String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tripId String
trip Trip @relation(fields: [tripId], references: [id], onDelete: Cascade)
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([tripId, userId])
}
model TripImage {
id String @id @default(cuid())
url String
caption String?
order Int @default(0)
tripId String
trip Trip @relation(fields: [tripId], references: [id], onDelete: Cascade)
}
model TripParticipant {
id String @id @default(cuid())
status ParticipantStatus @default(PENDING)
createdAt DateTime @default(now())
/// Peserta menekan "Saya sudah bayar" (pembayaran manual)
markedPaidAt DateTime?
/// Organizer mengonfirmasi uang sudah masuk
paymentConfirmedAt DateTime?
tripId String
trip Trip @relation(fields: [tripId], references: [id])
userId String
user User @relation(fields: [userId], references: [id])
@@unique([tripId, userId])
}
enum TripStatus {
OPEN
FULL
CLOSED
COMPLETED
}
enum ActivityCategory {
HIKING
CAMPING
SNORKELING
DIVING
ISLAND_HOPPING
CITY_TRIP
CULINARY
CONCERT
WORKSHOP
RETREAT
}
enum ParticipantStatus {
PENDING
CONFIRMED
CANCELLED
}