Files

758 lines
18 KiB
Plaintext

generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum UserRole {
USER
ADMIN
}
enum PlanCode {
FREE
BASIC
STANDARD
PREMIUM
}
enum SubscriptionStatus {
ACTIVE
TRIALING
PAST_DUE
CANCELED
EXPIRED
}
enum UsagePeriod {
DAILY
MONTHLY
LIFETIME
}
enum CreditTransactionType {
ADMIN_CREDIT
ADMIN_DEBIT
PAYMENT
REFUND
USAGE
ADJUSTMENT
}
enum PaymentStatus {
PENDING
PAID
FAILED
REFUNDED
CANCELED
}
enum AdSource {
META
ADSPY
PINTEREST
TIKTOK
}
enum AdStatus {
ACTIVE
INACTIVE
UNKNOWN
}
enum MediaType {
VIDEO
IMAGE
CAROUSEL
DCT
UNKNOWN
}
enum AdScore {
ESTABLISHED
HAS_POTENTIAL
UNESTABLISHED
UNKNOWN
}
enum StorePlatform {
SHOPIFY
WOOCOMMERCE
CUSTOM
UNKNOWN
}
enum ExportStatus {
QUEUED
PROCESSING
COMPLETED
FAILED
}
enum IngestStatus {
QUEUED
RUNNING
COMPLETED
FAILED
}
model User {
id String @id @default(cuid())
email String @unique
passwordHash String?
googleId String? @unique
name String?
avatarUrl String?
role UserRole @default(USER)
locale String @default("tr")
currency String @default("EUR")
theme String @default("system")
creditBalance Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
lastLoginAt DateTime?
sessions AuthSession[]
subscription Subscription?
usageCounters UsageCounter[]
savedFolders SavedFolder[]
savedAds SavedAd[]
filterPresets FilterPreset[]
trackedStores TrackedStore[]
trackedBrands TrackedBrand[]
exportJobs ExportJob[]
apiKeys ApiKey[]
creditTransactions CreditTransaction[] @relation("CreditOwner")
creditActions CreditTransaction[] @relation("CreditActor")
payments Payment[] @relation("PaymentOwner")
manualPayments Payment[] @relation("PaymentActor")
adminAuditLogs AdminAuditLog[] @relation("AuditActor")
aiAnalyses AiAnalysis[]
brandAlerts BrandAlert[]
notifications Notification[]
@@index([email])
}
model AuthSession {
id String @id @default(cuid())
userId String
tokenHash String @unique
expiresAt DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([expiresAt])
}
model Plan {
id String @id @default(cuid())
code PlanCode @unique
name String
monthlyPriceEur Int
quarterlyPriceEur Int?
yearlyPriceEur Int?
description String?
features Json
isActive Boolean @default(true)
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
subscriptions Subscription[]
}
model Subscription {
id String @id @default(cuid())
userId String @unique
planId String
status SubscriptionStatus @default(ACTIVE)
billingInterval String @default("monthly")
currentPeriodStart DateTime?
currentPeriodEnd DateTime?
cancelAtPeriodEnd Boolean @default(false)
provider String?
providerCustomerId String?
providerSubscriptionId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
plan Plan @relation(fields: [planId], references: [id])
@@index([planId])
@@index([status])
}
model UsageCounter {
id String @id @default(cuid())
userId String
metric String
period UsagePeriod
periodKey String
used Int @default(0)
limit Int?
resetAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([userId, metric, periodKey])
@@index([userId, metric])
@@index([periodKey])
}
model BrandPage {
id String @id @default(cuid())
source AdSource @default(META)
externalPageId String?
name String
handle String?
logoUrl String?
pageUrl String?
websiteDomain String?
fbLikes Int?
igFollowers Int?
country String?
niche String?
storeId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
store Store? @relation(fields: [storeId], references: [id], onDelete: SetNull)
ads Ad[]
trackedBy TrackedBrand[]
@@unique([source, externalPageId])
@@index([websiteDomain])
@@index([niche])
@@index([country])
}
model Ad {
id String @id @default(cuid())
source AdSource @default(META)
externalAdId String?
brandPageId String?
status AdStatus @default(UNKNOWN)
mediaType MediaType @default(UNKNOWN)
adScore AdScore @default(UNKNOWN)
primaryText String?
headline String?
description String?
ctaText String?
landingUrl String?
productUrl String?
language String?
countries String[] @default([])
niche String?
firstSeenAt DateTime?
lastSeenAt DateTime?
createdAtSource DateTime?
daysRunning Int?
estimatedReachMin Int?
estimatedReachMax Int?
estimatedSpendMin Int?
estimatedSpendMax Int?
estimatedCpm Float?
rank Float?
rankPercentile Float?
rankGrowth String?
raw Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
brandPage BrandPage? @relation(fields: [brandPageId], references: [id], onDelete: SetNull)
creatives AdCreative[]
metricsDaily AdMetricDaily[]
countryStats AdCountryStat[]
aiTags AITag[]
savedBy SavedAd[]
@@unique([source, externalAdId])
@@index([source, status])
@@index([mediaType])
@@index([niche])
@@index([language])
@@index([firstSeenAt])
@@index([lastSeenAt])
@@index([daysRunning])
@@index([rankPercentile])
}
model AdCreative {
id String @id @default(cuid())
adId String
type MediaType
url String
thumbnailUrl String?
width Int?
height Int?
durationSeconds Int?
position Int @default(0)
createdAt DateTime @default(now())
ad Ad @relation(fields: [adId], references: [id], onDelete: Cascade)
@@index([adId])
@@index([type])
}
model AdMetricDaily {
id String @id @default(cuid())
adId String
date DateTime
reachMin Int?
reachMax Int?
spendMin Int?
spendMax Int?
cpm Float?
rank Float?
rankPercentile Float?
activeCountries Int?
createdAt DateTime @default(now())
ad Ad @relation(fields: [adId], references: [id], onDelete: Cascade)
@@unique([adId, date])
@@index([date])
}
model AdCountryStat {
id String @id @default(cuid())
adId String
countryCode String
reachMin Int?
reachMax Int?
spendMin Int?
spendMax Int?
sharePercent Float?
ad Ad @relation(fields: [adId], references: [id], onDelete: Cascade)
@@unique([adId, countryCode])
@@index([countryCode])
}
model AITag {
id String @id @default(cuid())
adId String
type String
value String
confidence Float?
explanation String?
model String?
promptVersion String?
createdAt DateTime @default(now())
ad Ad @relation(fields: [adId], references: [id], onDelete: Cascade)
@@index([adId])
@@index([type, value])
}
model Store {
id String @id @default(cuid())
domain String @unique
name String
logoUrl String?
platform StorePlatform @default(UNKNOWN)
country String?
currency String?
language String?
niche String?
categoryPath String[] @default([])
shopUrl String?
createdAtSource DateTime?
dataSince DateTime?
shopifyTheme String?
productCount Int?
collectionCount Int?
trustpilotScore Float?
trustpilotReviewCount Int?
monthlyVisits Int?
monthlyVisitGrowth Float?
estRevenue30dMin Int?
estRevenue30dMax Int?
estRevenue24hMin Int?
estRevenue24hMax Int?
trafficCountries Json?
socials Json?
raw Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
brandPages BrandPage[]
products StoreProduct[]
snapshots StoreSnapshot[]
pixels StorePixel[]
apps StoreApp[]
trackedBy TrackedStore[]
@@index([niche])
@@index([country])
@@index([currency])
@@index([monthlyVisits])
@@index([monthlyVisitGrowth])
@@index([estRevenue30dMax])
}
model StoreSnapshot {
id String @id @default(cuid())
storeId String
date DateTime
monthlyVisits Int?
estRevenueMin Int?
estRevenueMax Int?
activeAdsCount Int?
productCount Int?
trafficCountries Json?
createdAt DateTime @default(now())
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
@@unique([storeId, date])
@@index([date])
}
model StoreProduct {
id String @id @default(cuid())
storeId String
externalProductId String?
title String
handle String?
imageUrl String?
productUrl String?
price Float?
compareAtPrice Float?
currency String?
variantCount Int?
isBestSeller Boolean @default(false)
firstSeenAt DateTime?
raw Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
@@index([storeId])
@@index([isBestSeller])
@@index([price])
}
model StorePixel {
id String @id @default(cuid())
storeId String
type String
value String?
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
@@unique([storeId, type, value])
@@index([type])
}
model StoreApp {
id String @id @default(cuid())
storeId String
name String
category String?
detectedAt DateTime @default(now())
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
@@unique([storeId, name])
@@index([name])
}
model SavedFolder {
id String @id @default(cuid())
userId String
name String
color String?
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
savedAds SavedAd[]
@@unique([userId, name])
@@index([userId])
}
model SavedAd {
id String @id @default(cuid())
userId String
adId String
folderId String?
note String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
ad Ad @relation(fields: [adId], references: [id], onDelete: Cascade)
folder SavedFolder? @relation(fields: [folderId], references: [id], onDelete: SetNull)
@@unique([userId, adId])
@@index([userId, folderId])
@@index([adId])
}
model TrackedStore {
id String @id @default(cuid())
userId String
storeId String
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
store Store @relation(fields: [storeId], references: [id], onDelete: Cascade)
@@unique([userId, storeId])
@@index([userId])
@@index([storeId])
}
model TrackedBrand {
id String @id @default(cuid())
userId String
brandPageId String
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
brandPage BrandPage @relation(fields: [brandPageId], references: [id], onDelete: Cascade)
alerts BrandAlert[]
@@unique([userId, brandPageId])
@@index([userId])
@@index([brandPageId])
}
model TikTokProduct {
id String @id @default(cuid())
externalId String @unique
title String
description String?
productUrl String?
imageUrl String?
shopName String?
shopExternalId String?
region String?
currency String?
price Float?
originalPrice Float?
soldCount Int?
rating Float?
reviewCount Int?
category String?
raw Json?
firstSeenAt DateTime @default(now())
lastSeenAt DateTime @default(now())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([region])
@@index([soldCount])
@@index([lastSeenAt])
}
model AiAnalysis {
id String @id @default(cuid())
userId String
query String?
provider String
model String
summary String
insights Json
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, createdAt])
}
model TrendSnapshot {
id String @id @default(cuid())
date DateTime @unique
metrics Json
createdAt DateTime @default(now())
@@index([date])
}
model BrandAlert {
id String @id @default(cuid())
userId String
trackedBrandId String
adId String
type String @default("NEW_AD")
title String
details Json?
readAt DateTime?
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
trackedBrand TrackedBrand @relation(fields: [trackedBrandId], references: [id], onDelete: Cascade)
@@unique([userId, trackedBrandId, adId, type])
@@index([userId, readAt, createdAt])
}
model Notification {
id String @id @default(cuid())
userId String
type String
title String
body String
link String?
readAt DateTime?
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, readAt, createdAt])
}
model FilterPreset {
id String @id @default(cuid())
userId String
module String
name String
filters Json
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([userId, module, name])
@@index([userId, module])
}
model ExportJob {
id String @id @default(cuid())
userId String
module String
filters Json?
status ExportStatus @default(QUEUED)
fileUrl String?
errorMessage String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, status])
@@index([module])
}
model IngestJob {
id String @id @default(cuid())
source String
type String
status IngestStatus @default(QUEUED)
startedAt DateTime?
finishedAt DateTime?
recordsImported Int @default(0)
recordsFailed Int @default(0)
errorMessage String?
metadata Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([source, status])
@@index([createdAt])
}
model ApiKey {
id String @id @default(cuid())
userId String
name String
keyHash String @unique
prefix String
lastUsedAt DateTime?
revokedAt DateTime?
monthlyCreditLimit Int?
usedCreditsThisMonth Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([prefix])
}
model CreditTransaction {
id String @id @default(cuid())
userId String
actorId String?
type CreditTransactionType
amount Int
balanceAfter Int
reason String
metadata Json?
createdAt DateTime @default(now())
user User @relation("CreditOwner", fields: [userId], references: [id], onDelete: Cascade)
actor User? @relation("CreditActor", fields: [actorId], references: [id], onDelete: SetNull)
@@index([userId, createdAt])
@@index([actorId, createdAt])
@@index([type, createdAt])
}
model Payment {
id String @id @default(cuid())
userId String
actorId String?
amountCents Int
currency String @default("EUR")
status PaymentStatus @default(PAID)
provider String @default("MANUAL")
externalId String? @unique
description String?
creditGranted Int @default(0)
paidAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation("PaymentOwner", fields: [userId], references: [id], onDelete: Cascade)
actor User? @relation("PaymentActor", fields: [actorId], references: [id], onDelete: SetNull)
@@index([userId, createdAt])
@@index([status, createdAt])
@@index([provider, createdAt])
}
model AdminAuditLog {
id String @id @default(cuid())
actorId String?
actorEmail String
action String
targetType String
targetId String?
summary String
details Json?
ipAddress String?
userAgent String?
createdAt DateTime @default(now())
actor User? @relation("AuditActor", fields: [actorId], references: [id], onDelete: SetNull)
@@index([actorId, createdAt])
@@index([action, createdAt])
@@index([targetType, targetId])
@@index([createdAt])
}