@danielcok17/prisma-db 1.13.3 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danielcok17/prisma-db",
3
- "version": "1.13.3",
3
+ "version": "1.14.0",
4
4
  "description": "Shared Prisma schema for Legal AI applications",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -15,7 +15,7 @@
15
15
  "scripts": {
16
16
  "build": "npm run db:generate && tsc",
17
17
  "dev": "tsc --watch",
18
- "db:generate": "npx prisma generate --schema ./prisma/app.prisma && npx prisma generate --schema ./prisma/law.prisma",
18
+ "db:generate": "prisma generate --schema ./prisma/app.prisma && prisma generate --schema ./prisma/law.prisma",
19
19
  "db:push": "prisma db push --schema ./prisma/app.prisma",
20
20
  "db:migrate": "prisma migrate dev --schema ./prisma/app.prisma",
21
21
  "db:migrate:prod": "prisma migrate deploy --schema ./prisma/app.prisma",
package/prisma/app.prisma CHANGED
@@ -23,91 +23,96 @@ model Account {
23
23
  scope String?
24
24
  id_token String?
25
25
  session_state String?
26
+ brand String @default("smartlex") // Multi-brand: "smartlex" | "justi"
26
27
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
27
28
 
28
- @@unique([provider, providerAccountId])
29
+ @@unique([provider, providerAccountId, brand])
29
30
  }
30
31
 
31
32
  model User {
32
- id String @id @default(cuid())
33
- name String?
34
- email String? @unique
35
- emailVerified DateTime?
36
- image String?
33
+ id String @id @default(cuid())
34
+ brand String @default("smartlex") // Multi-brand: "smartlex" | "justi"
35
+ name String?
36
+ email String?
37
+ emailVerified DateTime?
38
+ image String?
37
39
  // Credentials authentication fields
38
- password String? // Hashed password pre credentials login
39
- createdAt DateTime @default(now())
40
- agreedToTerms Boolean @default(false)
41
- practiceArea String[]
42
- lawFirm String?
43
- yearsOfExperience Int?
40
+ password String? // Hashed password pre credentials login
41
+ createdAt DateTime @default(now())
42
+ agreedToTerms Boolean @default(false)
43
+ practiceArea String[]
44
+ lawFirm String?
45
+ yearsOfExperience Int?
44
46
  // Nové polia pre schvalovanie používateľov
45
- isApproved Boolean @default(false) // Či je používateľ schválený
46
- isRejected Boolean @default(false) // Či je používateľ zamietnutý
47
- approvedAt DateTime? // Kedy bol schválený
48
- rejectedAt DateTime? // Kedy bol zamietnutý
49
- approvedBy String? // ID admina, ktorý schválil
50
- rejectedBy String? // ID admina, ktorý zamietol
51
- rejectionReason String? // Dôvod zamietnutia
47
+ isApproved Boolean @default(false) // Či je používateľ schválený
48
+ isRejected Boolean @default(false) // Či je používateľ zamietnutý
49
+ approvedAt DateTime? // Kedy bol schválený
50
+ rejectedAt DateTime? // Kedy bol zamietnutý
51
+ approvedBy String? // ID admina, ktorý schválil
52
+ rejectedBy String? // ID admina, ktorý zamietol
53
+ rejectionReason String? // Dôvod zamietnutia
52
54
  // Nové polia pre tracking a žiadosť
53
- referralSource String? // Odkiaľ sa o nás dozvedel (Google, Facebook, LinkedIn, etc.)
54
- applicationText String? // Text žiadosti o prihlásenie
55
+ referralSource String? // Odkiaľ sa o nás dozvedel (Google, Facebook, LinkedIn, etc.)
56
+ applicationText String? // Text žiadosti o prihlásenie
55
57
  // ✨ STRIPE FIELDS (B2C - len pre individuálnych používateľov)
56
- subscriptionTier SubscriptionTier @default(FREE) // Aktuálny subscription tier (cache)
57
- messageCount Int @default(10) // ✅ OPRAVENÉ z 100 na 10
58
- messageCountResetAt DateTime? // Kedy sa má resetovať message count
59
- adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
58
+ subscriptionTier SubscriptionTier @default(FREE) // Aktuálny subscription tier (cache)
59
+ messageCount Int @default(10) // ✅ OPRAVENÉ z 100 na 10
60
+ messageCountResetAt DateTime? // Kedy sa má resetovať message count
61
+ adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
60
62
  // ✨ COMPANY FIELDS (pre SZČO - samostatne zárobkovo činná osoba)
61
- customerType CustomerType @default(INDIVIDUAL) // Typ zákazníka: fyzická osoba alebo SZČO
62
- companyName String? // Obchodné meno (pre SZČO)
63
- companyNumber String? // IČO (povinné pre SZČO, 8 číslic)
64
- vatNumber String? // IČ DPH (SK + 10 číslic, nepovinné)
65
- taxNumber String? // DIČ (10 číslic, nepovinné)
66
- companyStreet String? // Ulica a číslo (pre SZČO)
67
- companyCity String? // Mesto (pre SZČO)
68
- companyPostalCode String? // PSČ (pre SZČO, formát: XXX XX)
69
- companyCountry String? @default("SK") // Krajina (ISO kód, default SK)
63
+ customerType CustomerType @default(INDIVIDUAL) // Typ zákazníka: fyzická osoba alebo SZČO
64
+ companyName String? // Obchodné meno (pre SZČO)
65
+ companyNumber String? // IČO (povinné pre SZČO, 8 číslic)
66
+ vatNumber String? // IČ DPH (SK + 10 číslic, nepovinné)
67
+ taxNumber String? // DIČ (10 číslic, nepovinné)
68
+ companyStreet String? // Ulica a číslo (pre SZČO)
69
+ companyCity String? // Mesto (pre SZČO)
70
+ companyPostalCode String? // PSČ (pre SZČO, formát: XXX XX)
71
+ companyCountry String? @default("SK") // Krajina (ISO kód, default SK)
70
72
  // ✨ UTM TRACKING & ATTRIBUTION FIELDS
71
73
  // First-visit attribution (celý lifecycle)
72
- firstUtmSource String? // Prvý UTM source
73
- firstUtmMedium String? // Prvý UTM medium
74
- firstUtmCampaign String? // Prvá kampaň
75
- firstUtmContent String? // Prvý content/variant
76
- firstUtmTerm String? // Prvé keywords
77
- firstVisitAt DateTime? // Kedy prvýkrát navštívil web
74
+ firstUtmSource String? // Prvý UTM source
75
+ firstUtmMedium String? // Prvý UTM medium
76
+ firstUtmCampaign String? // Prvá kampaň
77
+ firstUtmContent String? // Prvý content/variant
78
+ firstUtmTerm String? // Prvé keywords
79
+ firstVisitAt DateTime? // Kedy prvýkrát navštívil web
78
80
  // Registration attribution
79
- registrationUtmSource String? // UTM source pri registrácii
80
- registrationUtmMedium String? // UTM medium pri registrácii
81
- registrationUtmCampaign String? // Kampaň pri registrácii
82
- registrationUtmContent String? // Content/variant pri registrácii
83
- registrationUtmTerm String? // Keywords pri registrácii
81
+ registrationUtmSource String? // UTM source pri registrácii
82
+ registrationUtmMedium String? // UTM medium pri registrácii
83
+ registrationUtmCampaign String? // Kampaň pri registrácii
84
+ registrationUtmContent String? // Content/variant pri registrácii
85
+ registrationUtmTerm String? // Keywords pri registrácii
84
86
  // Relations
85
- approvalRequest UserApprovalRequest?
86
- stripeCustomer StripeCustomer? // ✨ B2C Stripe customer
87
- ownedOrganizations Organization[] @relation("OrganizationOwner")
88
- organizationMembers OrganizationMember[]
89
- createdInvites OrganizationInvite[] @relation("InviteCreator")
90
- accounts Account[]
91
- answers Answer[]
92
- conversations Conversation[]
93
- feedbacks Feedback[]
94
- pageViews PageView[]
95
- sessions Session[]
96
- workflowLogs WorkflowLog[]
97
- verificationTokens VerificationToken[]
98
- passwordResetTokens PasswordResetToken[]
99
- canvasDocuments CanvasDocument[]
100
- canvasDocumentVersions CanvasDocumentVersion[]
87
+ approvalRequest UserApprovalRequest?
88
+ stripeCustomer StripeCustomer? // ✨ B2C Stripe customer
89
+ ownedOrganizations Organization[] @relation("OrganizationOwner")
90
+ organizationMembers OrganizationMember[]
91
+ createdInvites OrganizationInvite[] @relation("InviteCreator")
92
+ accounts Account[]
93
+ answers Answer[]
94
+ conversations Conversation[]
95
+ feedbacks Feedback[]
96
+ pageViews PageView[]
97
+ sessions Session[]
98
+ workflowLogs WorkflowLog[]
99
+ verificationTokens VerificationToken[]
100
+ passwordResetTokens PasswordResetToken[]
101
+ canvasDocuments CanvasDocument[]
102
+ canvasDocumentVersions CanvasDocumentVersion[]
101
103
  // Folder system relations
102
- ownedFolders Folder[] @relation("FolderOwner")
103
- addedFolderItems FolderItem[] @relation("FolderItemAdder")
104
- folderActivities FolderActivity[] @relation("FolderActivityUser")
105
- folderSharesReceived FolderShare[] @relation("FolderShareUser")
106
- createdFolderShares FolderShare[] @relation("FolderShareCreator")
104
+ ownedFolders Folder[] @relation("FolderOwner")
105
+ addedFolderItems FolderItem[] @relation("FolderItemAdder")
106
+ folderActivities FolderActivity[] @relation("FolderActivityUser")
107
+ folderSharesReceived FolderShare[] @relation("FolderShareUser")
108
+ createdFolderShares FolderShare[] @relation("FolderShareCreator")
107
109
  // UTM tracking relations
108
- touchPoints TouchPoint[]
109
- conversions Conversion[]
110
+ touchPoints TouchPoint[]
111
+ conversions Conversion[]
110
112
 
113
+ // Multi-brand: compound unique allows same email across brands
114
+ @@unique([email, brand])
115
+ @@index([brand])
111
116
  @@index([customerType])
112
117
  @@index([companyNumber])
113
118
  @@index([email, customerType])
@@ -141,43 +146,43 @@ model UserApprovalRequest {
141
146
 
142
147
  // Organization = Právna kancelária / Firma
143
148
  model Organization {
144
- id String @id @default(cuid())
145
- name String // "Advokátska kancelária XYZ s.r.o."
149
+ id String @id @default(cuid())
150
+ name String // "Advokátska kancelária XYZ s.r.o."
146
151
 
147
152
  // Billing & Legal Information
148
- companyNumber String? // IČO (Identifikačné číslo organizácie)
149
- vatNumber String? // IČ DPH (VAT Number)
150
- taxNumber String? // DIČ (Daňové identifikačné číslo)
151
- legalForm String? // "s.r.o.", "a.s.", "v.o.s.", "k.s.", "SZČO"
152
- billingEmail String
153
+ companyNumber String? // IČO (Identifikačné číslo organizácie)
154
+ vatNumber String? // IČ DPH (VAT Number)
155
+ taxNumber String? // DIČ (Daňové identifikačné číslo)
156
+ legalForm String? // "s.r.o.", "a.s.", "v.o.s.", "k.s.", "SZČO"
157
+ billingEmail String
153
158
 
154
159
  // Address
155
- street String?
156
- city String?
157
- postalCode String?
158
- country String @default("SK")
160
+ street String?
161
+ city String?
162
+ postalCode String?
163
+ country String @default("SK")
159
164
 
160
165
  // Subscription (B2B)
161
166
  subscriptionTier SubscriptionTier @default(FREE) // Tier organizácie (cache)
162
- messageCount Int @default(0) // Spoločný pool správ pre všetkých členov
163
- messageLimit Int @default(100) // Limit správ podľa tieru
164
- messageCountResetAt DateTime? // Kedy sa má resetovať pool
165
- adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
167
+ messageCount Int @default(0) // Spoločný pool správ pre všetkých členov
168
+ messageLimit Int @default(100) // Limit správ podľa tieru
169
+ messageCountResetAt DateTime? // Kedy sa má resetovať pool
170
+ adminGrantExpiresAt DateTime? // Kedy admin grant expiruje
166
171
 
167
172
  // Settings
168
- isActive Boolean @default(true)
169
- maxMembers Int @default(5) // Max počet členov podľa tieru
173
+ isActive Boolean @default(true)
174
+ maxMembers Int @default(5) // Max počet členov podľa tieru
170
175
 
171
176
  // Relations
172
- owner User @relation("OrganizationOwner", fields: [ownerId], references: [id])
177
+ owner User @relation("OrganizationOwner", fields: [ownerId], references: [id])
173
178
  ownerId String
174
179
  members OrganizationMember[]
175
180
  invites OrganizationInvite[]
176
181
  stripeCustomer StripeCustomer?
177
182
  folderShares FolderShare[]
178
183
 
179
- createdAt DateTime @default(now())
180
- updatedAt DateTime @updatedAt
184
+ createdAt DateTime @default(now())
185
+ updatedAt DateTime @updatedAt
181
186
 
182
187
  @@index([ownerId])
183
188
  @@index([vatNumber])
@@ -188,14 +193,14 @@ model Organization {
188
193
 
189
194
  // Many-to-Many: User ↔ Organization
190
195
  model OrganizationMember {
191
- id String @id @default(cuid())
196
+ id String @id @default(cuid())
192
197
  organizationId String
193
198
  userId String
194
- role MemberRole @default(MEMBER)
195
- joinedAt DateTime @default(now())
199
+ role MemberRole @default(MEMBER)
200
+ joinedAt DateTime @default(now())
196
201
 
197
- organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
198
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
202
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
203
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
199
204
 
200
205
  @@unique([organizationId, userId])
201
206
  @@index([organizationId])
@@ -205,23 +210,23 @@ model OrganizationMember {
205
210
 
206
211
  // Organization Invite System
207
212
  model OrganizationInvite {
208
- id String @id @default(cuid())
213
+ id String @id @default(cuid())
209
214
  organizationId String
210
- token String @unique
211
- email String? // Optional: specific email invite
212
- role MemberRole @default(MEMBER)
215
+ token String @unique
216
+ email String? // Optional: specific email invite
217
+ role MemberRole @default(MEMBER)
213
218
  createdBy String
214
219
  expiresAt DateTime
215
- maxUses Int @default(1) // For multi-use invites
216
- currentUses Int @default(0)
217
- isActive Boolean @default(true)
218
- usedAt DateTime? // Last usage timestamp
219
- usedBy String? // Last user who used it
220
- createdAt DateTime @default(now())
221
- updatedAt DateTime @updatedAt
220
+ maxUses Int @default(1) // For multi-use invites
221
+ currentUses Int @default(0)
222
+ isActive Boolean @default(true)
223
+ usedAt DateTime? // Last usage timestamp
224
+ usedBy String? // Last user who used it
225
+ createdAt DateTime @default(now())
226
+ updatedAt DateTime @updatedAt
222
227
 
223
- organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
224
- creator User @relation("InviteCreator", fields: [createdBy], references: [id])
228
+ organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
229
+ creator User @relation("InviteCreator", fields: [createdBy], references: [id])
225
230
 
226
231
  @@index([token])
227
232
  @@index([organizationId])
@@ -230,26 +235,27 @@ model OrganizationInvite {
230
235
  }
231
236
 
232
237
  enum MemberRole {
233
- OWNER // Zakladateľ/vlastník, plný prístup k billing a settings
234
- ADMIN // Administrátor, môže pridávať/odoberať členov
235
- MEMBER // Bežný člen, len používa systém
238
+ OWNER // Zakladateľ/vlastník, plný prístup k billing a settings
239
+ ADMIN // Administrátor, môže pridávať/odoberať členov
240
+ MEMBER // Bežný člen, len používa systém
236
241
  }
237
242
 
238
243
  // ZJEDNODUŠENÝ Conversation - len metadata, bez duplikátov
239
244
  model Conversation {
240
- id String @id @default(cuid())
245
+ id String @id @default(cuid())
241
246
  name String
242
247
  userId String
243
- isShareable Boolean @default(false)
244
- shareUrl String? @unique
248
+ isShareable Boolean @default(false)
249
+ shareUrl String? @unique
245
250
  sharedAt DateTime?
246
- createdAt DateTime @default(now())
247
- updatedAt DateTime @updatedAt
251
+ createdAt DateTime @default(now())
252
+ updatedAt DateTime @updatedAt
248
253
  summary String?
249
- messagesSinceLastSummary Int? @default(0)
254
+ messagesSinceLastSummary Int? @default(0)
255
+ integrityHash String? // HMAC-SHA256 hash chain - posledný chainHash pre verifikáciu integrity
250
256
  // Relácie
251
257
  answers Answer[]
252
- user User @relation(fields: [userId], references: [id])
258
+ user User @relation(fields: [userId], references: [id])
253
259
  workflowLogs WorkflowLog[]
254
260
  canvasDocuments CanvasDocument[]
255
261
 
@@ -259,29 +265,31 @@ model Conversation {
259
265
 
260
266
  // Hlavný model - všetky správy, bez duplikátov
261
267
  model Answer {
262
- id String @id @default(cuid())
263
- conversationId String
264
- messageId String @unique
265
- role Role
266
- content String
267
- question String?
268
- answer String?
269
- evaluation String?
270
- isWelcome Boolean @default(false)
271
- processingTime Int?
272
- model String?
273
- userId String?
274
- createdAt DateTime @default(now())
275
- updatedAt DateTime @updatedAt
268
+ id String @id @default(cuid())
269
+ conversationId String
270
+ messageId String @unique
271
+ role Role
272
+ content String
273
+ question String?
274
+ answer String?
275
+ evaluation String?
276
+ isWelcome Boolean @default(false)
277
+ processingTime Int?
278
+ model String?
279
+ userId String?
280
+ contentHash String? // HMAC-SHA256 hash obsahu správy (content + role + messageId)
281
+ chainHash String? // Hash chain - zahŕňa predchádzajúci chainHash pre integritu sekvencie
282
+ createdAt DateTime @default(now())
283
+ updatedAt DateTime @updatedAt
276
284
  // Relácie
277
- conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
278
- user User? @relation(fields: [userId], references: [id])
285
+ conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
286
+ user User? @relation(fields: [userId], references: [id])
279
287
  feedback Feedback?
280
288
  references Reference[]
281
289
  metrics AnswerMetrics?
282
290
  WorkflowLog WorkflowLog[]
283
291
  files MessageFile[]
284
- canvasDocumentId String? // No FK constraint - flexible document management
292
+ canvasDocumentId String? // No FK constraint - flexible document management
285
293
 
286
294
  @@index([conversationId])
287
295
  @@index([messageId])
@@ -409,12 +417,12 @@ model PageView {
409
417
  }
410
418
 
411
419
  model Session {
412
- id String @id @default(cuid())
413
- sessionId String @unique
414
- userId String?
415
- startedAt DateTime @default(now())
416
- endedAt DateTime?
417
- duration Int?
420
+ id String @id @default(cuid())
421
+ sessionId String @unique
422
+ userId String?
423
+ startedAt DateTime @default(now())
424
+ endedAt DateTime?
425
+ duration Int?
418
426
 
419
427
  // ============================================
420
428
  // FIRST-TOUCH ATTRIBUTION
@@ -427,7 +435,7 @@ model Session {
427
435
  firstUtmCampaign String?
428
436
  firstUtmContent String?
429
437
  firstUtmTerm String?
430
- firstTouchAt DateTime? // Timestamp prvého UTM touchpoint
438
+ firstTouchAt DateTime? // Timestamp prvého UTM touchpoint
431
439
 
432
440
  // ============================================
433
441
  // LAST-TOUCH ATTRIBUTION
@@ -440,14 +448,14 @@ model Session {
440
448
  lastUtmCampaign String?
441
449
  lastUtmContent String?
442
450
  lastUtmTerm String?
443
- lastTouchAt DateTime? // Timestamp posledného UTM touchpoint
451
+ lastTouchAt DateTime? // Timestamp posledného UTM touchpoint
444
452
 
445
453
  // ============================================
446
454
  // RELATIONS
447
455
  // ============================================
448
456
 
449
- touchPoints TouchPoint[] // Všetky touchpointy v session
450
- conversions Conversion[] // Všetky konverzie v session
457
+ touchPoints TouchPoint[] // Všetky touchpointy v session
458
+ conversions Conversion[] // Všetky konverzie v session
451
459
 
452
460
  // Existujúce relations
453
461
  pageViews PageView[]
@@ -462,7 +470,6 @@ model Session {
462
470
  @@index([lastUtmCampaign])
463
471
  @@index([firstUtmSource, firstUtmMedium])
464
472
  @@index([lastUtmSource, lastUtmMedium])
465
-
466
473
  // Existujúce indexes
467
474
  @@index([userId])
468
475
  @@index([startedAt])
@@ -475,47 +482,47 @@ model Session {
475
482
  // TouchPoint - každý click s UTM parametrami
476
483
  // Umožňuje multi-touch attribution a customer journey analysis
477
484
  model TouchPoint {
478
- id String @id @default(cuid())
479
- userId String? // Pre prihlásených používateľov
480
- sessionId String // Session tracking (required)
485
+ id String @id @default(cuid())
486
+ userId String? // Pre prihlásených používateľov
487
+ sessionId String // Session tracking (required)
481
488
 
482
489
  // ============================================
483
490
  // UTM PARAMETERS (všetky lowercase!)
484
491
  // ============================================
485
492
  // Best practice: Vždy lowercase, validované pred uložením
486
493
 
487
- utmSource String? // Odkiaľ: "email", "facebook", "google", "linkedin"
488
- utmMedium String? // Typ média: "email", "cpc", "social", "organic", "paid-social"
489
- utmCampaign String? // Názov kampane: "smizany-2026-01", "spring-sale-2026"
490
- utmContent String? // Variant/link identifier: "cta-primary", "banner-top", "hero-button"
491
- utmTerm String? // Keywords (pre paid search): "pravny-asistent", "advokat-ai"
494
+ utmSource String? // Odkiaľ: "email", "facebook", "google", "linkedin"
495
+ utmMedium String? // Typ média: "email", "cpc", "social", "organic", "paid-social"
496
+ utmCampaign String? // Názov kampane: "smizany-2026-01", "spring-sale-2026"
497
+ utmContent String? // Variant/link identifier: "cta-primary", "banner-top", "hero-button"
498
+ utmTerm String? // Keywords (pre paid search): "pravny-asistent", "advokat-ai"
492
499
 
493
500
  // ============================================
494
501
  // CONTEXT & METADATA
495
502
  // ============================================
496
503
 
497
- page String // Ktorá stránka: "/", "/pricing", "/contact"
498
- path String // Celý path s params: "/?utm_source=email&utm_campaign=..."
499
- referrer String? // HTTP Referrer (odkiaľ prišiel)
504
+ page String // Ktorá stránka: "/", "/pricing", "/contact"
505
+ path String // Celý path s params: "/?utm_source=email&utm_campaign=..."
506
+ referrer String? // HTTP Referrer (odkiaľ prišiel)
500
507
 
501
508
  // Device & Browser info (denormalizované pre rýchle queries)
502
- deviceType String? // "mobile", "tablet", "desktop"
503
- browser String? // "Chrome", "Firefox", "Safari"
504
- os String? // "Windows", "macOS", "iOS", "Android"
509
+ deviceType String? // "mobile", "tablet", "desktop"
510
+ browser String? // "Chrome", "Firefox", "Safari"
511
+ os String? // "Windows", "macOS", "iOS", "Android"
505
512
 
506
513
  // Geolocation (anonymizované)
507
- country String? // "SK", "CZ", "US"
508
- city String? // "Bratislava", "Prague"
514
+ country String? // "SK", "CZ", "US"
515
+ city String? // "Bratislava", "Prague"
509
516
 
510
517
  // Timestamp
511
- timestamp DateTime @default(now())
518
+ timestamp DateTime @default(now())
512
519
 
513
520
  // ============================================
514
521
  // RELATIONS
515
522
  // ============================================
516
523
 
517
- session Session @relation(fields: [sessionId], references: [sessionId], onDelete: Cascade)
518
- user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
524
+ session Session @relation(fields: [sessionId], references: [sessionId], onDelete: Cascade)
525
+ user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
519
526
 
520
527
  // ============================================
521
528
  // INDEXES (optimalizované pre analytics queries)
@@ -531,38 +538,37 @@ model TouchPoint {
531
538
  @@index([timestamp])
532
539
  @@index([sessionId, timestamp])
533
540
  @@index([userId, timestamp])
534
-
535
541
  @@map("touch_points")
536
542
  }
537
543
 
538
544
  // Conversion - sledovanie konverzií s attribution
539
545
  // Podporuje first-touch, last-touch aj multi-touch attribution
540
546
  model Conversion {
541
- id String @id @default(cuid())
542
- userId String? // Kto konvertoval (nullable pre anonymous conversions)
543
- sessionId String // V ktorej session
547
+ id String @id @default(cuid())
548
+ userId String? // Kto konvertoval (nullable pre anonymous conversions)
549
+ sessionId String // V ktorej session
544
550
 
545
551
  // ============================================
546
552
  // CONVERSION METADATA
547
553
  // ============================================
548
554
 
549
- type ConversionType // Typ konverzie
550
- value Decimal? // Revenue (pre e-commerce/subscriptions)
551
- currency String? @default("EUR")
555
+ type ConversionType // Typ konverzie
556
+ value Decimal? // Revenue (pre e-commerce/subscriptions)
557
+ currency String? @default("EUR")
552
558
 
553
559
  // Optional metadata
554
- metadata Json? // Stripe subscription ID, product info, etc.
560
+ metadata Json? // Stripe subscription ID, product info, etc.
555
561
 
556
562
  // ============================================
557
563
  // FIRST-TOUCH ATTRIBUTION (40% credit)
558
564
  // ============================================
559
565
  // Prvý UTM touchpoint v session = awareness channel
560
566
 
561
- firstUtmSource String? // Odkiaľ prišiel prvýkrát
562
- firstUtmMedium String? // Akým médiom prvýkrát
563
- firstUtmCampaign String? // Ktorá kampaň priniesla awareness
564
- firstUtmContent String? // Ktorý konkrétny link/banner
565
- firstUtmTerm String? // Ktoré keywords (pre paid search)
567
+ firstUtmSource String? // Odkiaľ prišiel prvýkrát
568
+ firstUtmMedium String? // Akým médiom prvýkrát
569
+ firstUtmCampaign String? // Ktorá kampaň priniesla awareness
570
+ firstUtmContent String? // Ktorý konkrétny link/banner
571
+ firstUtmTerm String? // Ktoré keywords (pre paid search)
566
572
  firstTouchAt DateTime? // Kedy bol prvý touchpoint
567
573
 
568
574
  // ============================================
@@ -570,11 +576,11 @@ model Conversion {
570
576
  // ============================================
571
577
  // Posledný UTM touchpoint pred konverziou = conversion channel
572
578
 
573
- lastUtmSource String? // Odkiaľ prišiel naposledy
574
- lastUtmMedium String? // Akým médiom naposledy
575
- lastUtmCampaign String? // Ktorá kampaň priniesla konverziu
576
- lastUtmContent String? // Ktorý konkrétny link/banner
577
- lastUtmTerm String? // Ktoré keywords (pre paid search)
579
+ lastUtmSource String? // Odkiaľ prišiel naposledy
580
+ lastUtmMedium String? // Akým médiom naposledy
581
+ lastUtmCampaign String? // Ktorá kampaň priniesla konverziu
582
+ lastUtmContent String? // Ktorý konkrétny link/banner
583
+ lastUtmTerm String? // Ktoré keywords (pre paid search)
578
584
  lastTouchAt DateTime? // Kedy bol posledný touchpoint
579
585
 
580
586
  // ============================================
@@ -582,24 +588,24 @@ model Conversion {
582
588
  // ============================================
583
589
  // Všetky touchpointy medzi prvým a posledným
584
590
 
585
- touchPointIds String[] // Array of TouchPoint IDs (pre detailnú analýzu)
586
- touchPointCount Int? // Počet touchpoints v customer journey
591
+ touchPointIds String[] // Array of TouchPoint IDs (pre detailnú analýzu)
592
+ touchPointCount Int? // Počet touchpoints v customer journey
587
593
 
588
594
  // ============================================
589
595
  // CALCULATED FIELDS
590
596
  // ============================================
591
597
 
592
- journeyDuration Int? // Čas od prvého touchpoint po konverziu (sekundy)
598
+ journeyDuration Int? // Čas od prvého touchpoint po konverziu (sekundy)
593
599
 
594
600
  // Timestamp
595
- timestamp DateTime @default(now())
601
+ timestamp DateTime @default(now())
596
602
 
597
603
  // ============================================
598
604
  // RELATIONS
599
605
  // ============================================
600
606
 
601
- session Session @relation(fields: [sessionId], references: [sessionId], onDelete: Cascade)
602
- user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
607
+ session Session @relation(fields: [sessionId], references: [sessionId], onDelete: Cascade)
608
+ user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
603
609
 
604
610
  // ============================================
605
611
  // INDEXES
@@ -609,25 +615,21 @@ model Conversion {
609
615
  @@index([sessionId])
610
616
  @@index([type])
611
617
  @@index([timestamp])
612
-
613
618
  // First-touch attribution indexes
614
619
  @@index([firstUtmSource])
615
620
  @@index([firstUtmMedium])
616
621
  @@index([firstUtmCampaign])
617
622
  @@index([firstUtmCampaign, type])
618
623
  @@index([firstUtmSource, firstUtmMedium])
619
-
620
624
  // Last-touch attribution indexes
621
625
  @@index([lastUtmSource])
622
626
  @@index([lastUtmMedium])
623
627
  @@index([lastUtmCampaign])
624
628
  @@index([lastUtmCampaign, type])
625
629
  @@index([lastUtmSource, lastUtmMedium])
626
-
627
630
  // Analytics indexes
628
631
  @@index([type, timestamp])
629
632
  @@index([value])
630
-
631
633
  @@map("conversions")
632
634
  }
633
635
 
@@ -637,28 +639,28 @@ model Conversion {
637
639
 
638
640
  // Stripe Customer - polymorphic relation (User OR Organization)
639
641
  model StripeCustomer {
640
- id String @id @default(cuid())
642
+ id String @id @default(cuid())
641
643
 
642
644
  // ✅ Polymorphic relation - buď User (B2C) alebo Organization (B2B)
643
- userId String? @unique // B2C: Individuálny používateľ
644
- organizationId String? @unique // B2B: Organizácia
645
+ userId String? @unique // B2C: Individuálny používateľ
646
+ organizationId String? @unique // B2B: Organizácia
645
647
 
646
- stripeCustomerId String @unique
647
- email String
648
- name String?
648
+ stripeCustomerId String @unique
649
+ email String
650
+ name String?
649
651
 
650
652
  // Billing details (Stripe Address & Tax IDs)
651
- billingAddress Json? // Stripe Address object
652
- taxIds Json? // Stripe Tax IDs array (VAT, etc.)
653
+ billingAddress Json? // Stripe Address object
654
+ taxIds Json? // Stripe Tax IDs array (VAT, etc.)
653
655
 
654
- createdAt DateTime @default(now())
655
- updatedAt DateTime @updatedAt
656
+ createdAt DateTime @default(now())
657
+ updatedAt DateTime @updatedAt
656
658
 
657
659
  // Relations
658
- user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
659
- organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
660
- subscriptions StripeSubscription[]
661
- payments StripePayment[]
660
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
661
+ organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
662
+ subscriptions StripeSubscription[]
663
+ payments StripePayment[]
662
664
 
663
665
  @@index([stripeCustomerId])
664
666
  @@index([userId])
@@ -668,37 +670,37 @@ model StripeCustomer {
668
670
 
669
671
  // Stripe Subscription - sledovanie predplatného (User alebo Organization)
670
672
  model StripeSubscription {
671
- id String @id @default(cuid())
673
+ id String @id @default(cuid())
672
674
 
673
675
  // ✅ FIX: customerId = FK na StripeCustomer.id (nie stripeCustomerId!)
674
- customerId String // FK na StripeCustomer.id
675
- stripeCustomerId String // Stripe ID (pre logging/redundancia)
676
- stripeSubscriptionId String @unique
676
+ customerId String // FK na StripeCustomer.id
677
+ stripeCustomerId String // Stripe ID (pre logging/redundancia)
678
+ stripeSubscriptionId String @unique
677
679
  stripePriceId String
678
680
  stripeProductId String
679
681
 
680
- status SubscriptionStatus
681
- tier SubscriptionTier @default(FREE)
682
- billingInterval BillingInterval @default(MONTHLY) // Monthly or Yearly billing
682
+ status SubscriptionStatus
683
+ tier SubscriptionTier @default(FREE)
684
+ billingInterval BillingInterval @default(MONTHLY) // Monthly or Yearly billing
683
685
 
684
- currentPeriodStart DateTime
685
- currentPeriodEnd DateTime
686
- cancelAtPeriodEnd Boolean @default(false)
687
- canceledAt DateTime?
686
+ currentPeriodStart DateTime
687
+ currentPeriodEnd DateTime
688
+ cancelAtPeriodEnd Boolean @default(false)
689
+ canceledAt DateTime?
688
690
 
689
- trialStart DateTime?
690
- trialEnd DateTime?
691
+ trialStart DateTime?
692
+ trialEnd DateTime?
691
693
 
692
694
  // Additional fields
693
- quantity Int @default(1) // Počet seats (pre LAW_FIRM/ENTERPRISE tier)
694
- defaultPaymentMethodId String? // Stripe payment method ID
695
+ quantity Int @default(1) // Počet seats (pre LAW_FIRM/ENTERPRISE tier)
696
+ defaultPaymentMethodId String? // Stripe payment method ID
695
697
 
696
- metadata Json? // Dodatočné Stripe metadata
697
- createdAt DateTime @default(now())
698
- updatedAt DateTime @updatedAt
698
+ metadata Json? // Dodatočné Stripe metadata
699
+ createdAt DateTime @default(now())
700
+ updatedAt DateTime @updatedAt
699
701
 
700
702
  // ✅ FIX: Relácia na customerId (interné ID), nie stripeCustomerId (Stripe ID)
701
- customer StripeCustomer @relation(fields: [customerId], references: [id], onDelete: Cascade)
703
+ customer StripeCustomer @relation(fields: [customerId], references: [id], onDelete: Cascade)
702
704
 
703
705
  @@index([stripeSubscriptionId])
704
706
  @@index([customerId])
@@ -711,14 +713,14 @@ model StripeSubscription {
711
713
 
712
714
  // Stripe Event - prevencia duplicitného spracovania webhookov (idempotencia)
713
715
  model StripeEvent {
714
- id String @id @default(cuid())
715
- eventId String @unique // Stripe event ID
716
- type String // Typ eventu (napr. 'checkout.session.completed')
717
- data Json // Úplné event data pre debugging
718
- processed Boolean @default(false)
716
+ id String @id @default(cuid())
717
+ eventId String @unique // Stripe event ID
718
+ type String // Typ eventu (napr. 'checkout.session.completed')
719
+ data Json // Úplné event data pre debugging
720
+ processed Boolean @default(false)
719
721
  processedAt DateTime?
720
- error String? // Uloženie chýb pri spracovaní
721
- createdAt DateTime @default(now())
722
+ error String? // Uloženie chýb pri spracovaní
723
+ createdAt DateTime @default(now())
722
724
 
723
725
  @@index([eventId])
724
726
  @@index([processed])
@@ -728,28 +730,28 @@ model StripeEvent {
728
730
 
729
731
  // Stripe Payment - sledovanie jednotlivých platieb (voliteľné, pre detailnú analytiku)
730
732
  model StripePayment {
731
- id String @id @default(cuid())
733
+ id String @id @default(cuid())
732
734
 
733
735
  // ✅ FIX: Pridaná relácia na StripeCustomer
734
- customerId String
735
- stripePaymentId String @unique
736
- stripeCustomerId String // Redundantné, ale OK pre logging
736
+ customerId String
737
+ stripePaymentId String @unique
738
+ stripeCustomerId String // Redundantné, ale OK pre logging
737
739
 
738
- amount Int // Suma v centoch
739
- currency String @default("eur")
740
- status String // succeeded, failed, pending
741
- paymentMethod String? // card, sepa_debit, atď.
742
- description String?
740
+ amount Int // Suma v centoch
741
+ currency String @default("eur")
742
+ status String // succeeded, failed, pending
743
+ paymentMethod String? // card, sepa_debit, atď.
744
+ description String?
743
745
 
744
746
  // Invoice info
745
- invoiceId String? // Stripe Invoice ID
746
- invoiceUrl String? // URL na faktúru
747
+ invoiceId String? // Stripe Invoice ID
748
+ invoiceUrl String? // URL na faktúru
747
749
 
748
- metadata Json?
749
- createdAt DateTime @default(now())
750
+ metadata Json?
751
+ createdAt DateTime @default(now())
750
752
 
751
753
  // ✅ Relácia na StripeCustomer
752
- customer StripeCustomer @relation(fields: [customerId], references: [id], onDelete: Cascade)
754
+ customer StripeCustomer @relation(fields: [customerId], references: [id], onDelete: Cascade)
753
755
 
754
756
  @@index([stripePaymentId])
755
757
  @@index([customerId])
@@ -791,35 +793,35 @@ enum ApprovalStatus {
791
793
 
792
794
  // Stripe: Status predplatného
793
795
  enum SubscriptionStatus {
794
- ACTIVE // Predplatné je aktívne
795
- CANCELED // Predplatné zrušené
796
- INCOMPLETE // Iniciálna platba zlyhala
796
+ ACTIVE // Predplatné je aktívne
797
+ CANCELED // Predplatné zrušené
798
+ INCOMPLETE // Iniciálna platba zlyhala
797
799
  INCOMPLETE_EXPIRED // Neúplné predplatné expirované
798
- PAST_DUE // Platba zlyhala, retry prebieha
799
- TRIALING // V skúšobnom období
800
- UNPAID // Platba zlyhala, žiadny retry
801
- PAUSED // Predplatné pozastavené (Stripe feature)
800
+ PAST_DUE // Platba zlyhala, retry prebieha
801
+ TRIALING // V skúšobnom období
802
+ UNPAID // Platba zlyhala, žiadny retry
803
+ PAUSED // Predplatné pozastavené (Stripe feature)
802
804
  }
803
805
 
804
806
  // Customer type: Individuálny vs SZČO
805
807
  enum CustomerType {
806
- INDIVIDUAL // Fyzická osoba (bez IČO)
807
- SELF_EMPLOYED // SZČO - Samostatne zárobkovo činná osoba (s IČO)
808
+ INDIVIDUAL // Fyzická osoba (bez IČO)
809
+ SELF_EMPLOYED // SZČO - Samostatne zárobkovo činná osoba (s IČO)
808
810
  }
809
811
 
810
812
  // Stripe: Tier predplatného
811
813
  enum SubscriptionTier {
812
- FREE // Free tier (10 messages/month)
813
- LAWYER // Lawyer tier (1000 messages/month, €29/month, 1 user)
814
- LAWYER_PRO // Lawyer Pro tier (3000 messages/month, €49/month, 1 user) - RECOMMENDED
815
- LAW_FIRM // Law Firm tier (10000 messages/month, €129/month, up to 5 users)
816
- ENTERPRISE // Enterprise tier (unlimited messages, unlimited users, custom pricing)
814
+ FREE // Free tier (10 messages/month)
815
+ LAWYER // Lawyer tier (1000 messages/month, €29/month, 1 user)
816
+ LAWYER_PRO // Lawyer Pro tier (3000 messages/month, €49/month, 1 user) - RECOMMENDED
817
+ LAW_FIRM // Law Firm tier (10000 messages/month, €129/month, up to 5 users)
818
+ ENTERPRISE // Enterprise tier (unlimited messages, unlimited users, custom pricing)
817
819
  }
818
820
 
819
821
  // Stripe: Billing interval
820
822
  enum BillingInterval {
821
- MONTHLY // Monthly billing
822
- YEARLY // Yearly billing (17% discount)
823
+ MONTHLY // Monthly billing
824
+ YEARLY // Yearly billing (17% discount)
823
825
  }
824
826
 
825
827
  // Canvas Document Status
@@ -831,16 +833,16 @@ enum DocumentStatus {
831
833
 
832
834
  // Conversion types for UTM attribution tracking
833
835
  enum ConversionType {
834
- REGISTRATION // Nová registrácia
835
- SUBSCRIPTION // Začiatok predplatného (FREE → PAID)
836
- UPGRADE // Upgrade tieru (LAWYER → LAWYER_PRO)
837
- RENEWAL // Obnovenie predplatného
838
- TRIAL_START // Začiatok trial periodu
839
- PURCHASE // Jednorazový nákup
840
- LEAD // Vyplnenie kontaktného formulára
841
- DEMO_REQUEST // Žiadosť o demo
842
- DOWNLOAD // Stiahnutie resourceu
843
- FORM_SUBMIT // Iné formuláre
836
+ REGISTRATION // Nová registrácia
837
+ SUBSCRIPTION // Začiatok predplatného (FREE → PAID)
838
+ UPGRADE // Upgrade tieru (LAWYER → LAWYER_PRO)
839
+ RENEWAL // Obnovenie predplatného
840
+ TRIAL_START // Začiatok trial periodu
841
+ PURCHASE // Jednorazový nákup
842
+ LEAD // Vyplnenie kontaktného formulára
843
+ DEMO_REQUEST // Žiadosť o demo
844
+ DOWNLOAD // Stiahnutie resourceu
845
+ FORM_SUBMIT // Iné formuláre
844
846
  }
845
847
 
846
848
  // ============================================
@@ -967,12 +969,12 @@ enum StepType {
967
969
 
968
970
  // Email verification tokens
969
971
  model VerificationToken {
970
- id String @id @default(cuid())
971
- userId String
972
- token String @unique
973
- expires DateTime
974
- createdAt DateTime @default(now())
975
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
972
+ id String @id @default(cuid())
973
+ userId String
974
+ token String @unique
975
+ expires DateTime
976
+ createdAt DateTime @default(now())
977
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
976
978
 
977
979
  @@index([token])
978
980
  @@index([userId])
@@ -981,12 +983,12 @@ model VerificationToken {
981
983
 
982
984
  // Password reset tokens
983
985
  model PasswordResetToken {
984
- id String @id @default(cuid())
985
- userId String
986
- token String @unique
987
- expires DateTime
988
- createdAt DateTime @default(now())
989
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
986
+ id String @id @default(cuid())
987
+ userId String
988
+ token String @unique
989
+ expires DateTime
990
+ createdAt DateTime @default(now())
991
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
990
992
 
991
993
  @@index([token])
992
994
  @@index([userId])
@@ -998,21 +1000,21 @@ model PasswordResetToken {
998
1000
  // ============================================
999
1001
 
1000
1002
  model CanvasDocument {
1001
- id String @id @default(cuid())
1003
+ id String @id @default(cuid())
1002
1004
  userId String
1003
1005
  title String
1004
- status DocumentStatus @default(DRAFT)
1005
- createdAt DateTime @default(now())
1006
- updatedAt DateTime @updatedAt
1006
+ status DocumentStatus @default(DRAFT)
1007
+ createdAt DateTime @default(now())
1008
+ updatedAt DateTime @updatedAt
1007
1009
  originConversationId String?
1008
- originAnswerId String? // No FK constraint - async save reference
1009
- currentVersionId String? @unique
1010
+ originAnswerId String? // No FK constraint - async save reference
1011
+ currentVersionId String? @unique
1010
1012
 
1011
1013
  // Relations
1012
- user User @relation(fields: [userId], references: [id], onDelete: Restrict)
1013
- originConversation Conversation? @relation(fields: [originConversationId], references: [id], onDelete: SetNull)
1014
- currentVersion CanvasDocumentVersion? @relation("CurrentVersion", fields: [currentVersionId], references: [id], onDelete: SetNull)
1015
- versions CanvasDocumentVersion[] @relation("DocumentVersions")
1014
+ user User @relation(fields: [userId], references: [id], onDelete: Restrict)
1015
+ originConversation Conversation? @relation(fields: [originConversationId], references: [id], onDelete: SetNull)
1016
+ currentVersion CanvasDocumentVersion? @relation("CurrentVersion", fields: [currentVersionId], references: [id], onDelete: SetNull)
1017
+ versions CanvasDocumentVersion[] @relation("DocumentVersions")
1016
1018
 
1017
1019
  @@index([userId])
1018
1020
  @@index([originConversationId])
@@ -1021,12 +1023,12 @@ model CanvasDocument {
1021
1023
  }
1022
1024
 
1023
1025
  model CanvasDocumentVersion {
1024
- id String @id @default(cuid())
1026
+ id String @id @default(cuid())
1025
1027
  documentId String
1026
1028
  versionNumber Int
1027
1029
  title String
1028
1030
  markdownContent String
1029
- createdAt DateTime @default(now())
1031
+ createdAt DateTime @default(now())
1030
1032
  createdBy String
1031
1033
  regenerationPrompt String?
1032
1034
  parentVersionId String?
@@ -1051,34 +1053,34 @@ model CanvasDocumentVersion {
1051
1053
  // Main folder entity with hierarchy support via parentId
1052
1054
  // Uses materialized path pattern for efficient hierarchy queries
1053
1055
  model Folder {
1054
- id String @id @default(cuid())
1056
+ id String @id @default(cuid())
1055
1057
  name String
1056
1058
  description String?
1057
- color String? @default("#6366f1")
1058
- icon String? @default("folder")
1059
+ color String? @default("#6366f1")
1060
+ icon String? @default("folder")
1059
1061
 
1060
1062
  // Ownership (always user-owned)
1061
- ownerId String
1063
+ ownerId String
1062
1064
 
1063
1065
  // Hierarchy
1064
1066
  parentId String?
1065
- rootFolderId String? // NULL for root folders, points to top-level ancestor
1066
- path String @default("/") // Materialized path: "/parentId/grandparentId/..."
1067
- depth Int @default(0)
1067
+ rootFolderId String? // NULL for root folders, points to top-level ancestor
1068
+ path String @default("/") // Materialized path: "/parentId/grandparentId/..."
1069
+ depth Int @default(0)
1068
1070
 
1069
1071
  // Metadata
1070
- sortOrder Int @default(0)
1071
- isArchived Boolean @default(false)
1072
- createdAt DateTime @default(now())
1073
- updatedAt DateTime @updatedAt
1072
+ sortOrder Int @default(0)
1073
+ isArchived Boolean @default(false)
1074
+ createdAt DateTime @default(now())
1075
+ updatedAt DateTime @updatedAt
1074
1076
 
1075
1077
  // Relations
1076
- owner User @relation("FolderOwner", fields: [ownerId], references: [id], onDelete: Cascade)
1077
- parent Folder? @relation("FolderHierarchy", fields: [parentId], references: [id], onDelete: Cascade)
1078
- children Folder[] @relation("FolderHierarchy")
1079
- items FolderItem[]
1080
- activities FolderActivity[]
1081
- shares FolderShare[]
1078
+ owner User @relation("FolderOwner", fields: [ownerId], references: [id], onDelete: Cascade)
1079
+ parent Folder? @relation("FolderHierarchy", fields: [parentId], references: [id], onDelete: Cascade)
1080
+ children Folder[] @relation("FolderHierarchy")
1081
+ items FolderItem[]
1082
+ activities FolderActivity[]
1083
+ shares FolderShare[]
1082
1084
 
1083
1085
  @@index([ownerId])
1084
1086
  @@index([parentId])
@@ -1091,12 +1093,12 @@ model Folder {
1091
1093
  // Links entities (conversations, documents, etc.) to folders
1092
1094
  // Polymorphic: entityType + entityId point to any supported entity
1093
1095
  model FolderItem {
1094
- id String @id @default(cuid())
1095
- folderId String
1096
+ id String @id @default(cuid())
1097
+ folderId String
1096
1098
 
1097
1099
  // Polymorphic reference
1098
- entityType FolderItemType
1099
- entityId String
1100
+ entityType FolderItemType
1101
+ entityId String
1100
1102
 
1101
1103
  // Optional metadata override
1102
1104
  displayName String?
@@ -1106,14 +1108,14 @@ model FolderItem {
1106
1108
  rootFolderId String?
1107
1109
 
1108
1110
  // Tracking
1109
- addedById String
1110
- sortOrder Int @default(0)
1111
- createdAt DateTime @default(now())
1112
- updatedAt DateTime @updatedAt
1111
+ addedById String
1112
+ sortOrder Int @default(0)
1113
+ createdAt DateTime @default(now())
1114
+ updatedAt DateTime @updatedAt
1113
1115
 
1114
1116
  // Relations
1115
- folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1116
- addedBy User @relation("FolderItemAdder", fields: [addedById], references: [id], onDelete: Restrict)
1117
+ folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1118
+ addedBy User @relation("FolderItemAdder", fields: [addedById], references: [id], onDelete: Restrict)
1117
1119
 
1118
1120
  @@unique([folderId, entityType, entityId])
1119
1121
  @@index([folderId])
@@ -1125,25 +1127,25 @@ model FolderItem {
1125
1127
  // Timeline entries for folders (system events + user messages)
1126
1128
  // Opening ANY folder shows ALL activities from the entire folder tree
1127
1129
  model FolderActivity {
1128
- id String @id @default(cuid())
1129
- folderId String
1130
+ id String @id @default(cuid())
1131
+ folderId String
1130
1132
 
1131
1133
  // Denormalized for cross-folder timeline queries
1132
- rootFolderId String?
1134
+ rootFolderId String?
1133
1135
 
1134
1136
  // Activity details
1135
1137
  activityType FolderActivityType
1136
1138
  userId String
1137
- content String? // For USER_MESSAGE, USER_NOTE types
1138
- metadata Json? // For system event details
1139
- relatedItemId String? // Optional reference to FolderItem
1139
+ content String? // For USER_MESSAGE, USER_NOTE types
1140
+ metadata Json? // For system event details
1141
+ relatedItemId String? // Optional reference to FolderItem
1140
1142
 
1141
1143
  // Timestamp
1142
- createdAt DateTime @default(now())
1144
+ createdAt DateTime @default(now())
1143
1145
 
1144
1146
  // Relations
1145
- folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1146
- user User @relation("FolderActivityUser", fields: [userId], references: [id], onDelete: Restrict)
1147
+ folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1148
+ user User @relation("FolderActivityUser", fields: [userId], references: [id], onDelete: Restrict)
1147
1149
 
1148
1150
  @@index([folderId])
1149
1151
  @@index([rootFolderId])
@@ -1156,26 +1158,26 @@ model FolderActivity {
1156
1158
  // Sharing permissions for folders
1157
1159
  // Can share with individual user OR entire organization (not both)
1158
1160
  model FolderShare {
1159
- id String @id @default(cuid())
1160
- folderId String
1161
+ id String @id @default(cuid())
1162
+ folderId String
1161
1163
 
1162
1164
  // Share target (either user OR organization, enforced by application logic)
1163
1165
  userId String?
1164
1166
  organizationId String?
1165
1167
 
1166
1168
  // Permission level
1167
- permission FolderPermission @default(VIEW)
1169
+ permission FolderPermission @default(VIEW)
1168
1170
 
1169
1171
  // Sharing metadata
1170
- sharedById String
1171
- sharedAt DateTime @default(now())
1172
- expiresAt DateTime?
1172
+ sharedById String
1173
+ sharedAt DateTime @default(now())
1174
+ expiresAt DateTime?
1173
1175
 
1174
1176
  // Relations
1175
- folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1176
- user User? @relation("FolderShareUser", fields: [userId], references: [id], onDelete: Cascade)
1177
- organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
1178
- sharedBy User @relation("FolderShareCreator", fields: [sharedById], references: [id], onDelete: Restrict)
1177
+ folder Folder @relation(fields: [folderId], references: [id], onDelete: Cascade)
1178
+ user User? @relation("FolderShareUser", fields: [userId], references: [id], onDelete: Cascade)
1179
+ organization Organization? @relation(fields: [organizationId], references: [id], onDelete: Cascade)
1180
+ sharedBy User @relation("FolderShareCreator", fields: [sharedById], references: [id], onDelete: Restrict)
1179
1181
 
1180
1182
  // Partial unique indexes for nullable columns
1181
1183
  @@unique([folderId, userId])
@@ -0,0 +1,23 @@
1
+ -- Multi-Brand Support: Allow same email to exist across different brands (smartlex, justi)
2
+ -- Existing data gets brand = 'smartlex' via DEFAULT value
3
+
4
+ -- DropIndex
5
+ DROP INDEX "Account_provider_providerAccountId_key";
6
+
7
+ -- DropIndex
8
+ DROP INDEX "User_email_key";
9
+
10
+ -- AlterTable: Add brand column to Account with default 'smartlex'
11
+ ALTER TABLE "Account" ADD COLUMN "brand" TEXT NOT NULL DEFAULT 'smartlex';
12
+
13
+ -- AlterTable: Add brand column to User with default 'smartlex'
14
+ ALTER TABLE "User" ADD COLUMN "brand" TEXT NOT NULL DEFAULT 'smartlex';
15
+
16
+ -- CreateIndex: Compound unique on Account (provider + providerAccountId + brand)
17
+ CREATE UNIQUE INDEX "Account_provider_providerAccountId_brand_key" ON "Account"("provider", "providerAccountId", "brand");
18
+
19
+ -- CreateIndex: Index on User.brand for fast filtering
20
+ CREATE INDEX "User_brand_idx" ON "User"("brand");
21
+
22
+ -- CreateIndex: Compound unique on User (email + brand) - allows same email in different brands
23
+ CREATE UNIQUE INDEX "User_email_brand_key" ON "User"("email", "brand");
@@ -0,0 +1,9 @@
1
+ -- AlterTable: Add integrity hashing fields for HMAC-SHA256 conversation verification
2
+ -- contentHash: HMAC-SHA256 hash of message content (content + role + messageId)
3
+ -- chainHash: Hash chain linking to previous message for sequence integrity
4
+ -- integrityHash: Last chainHash for full conversation integrity verification
5
+
6
+ ALTER TABLE "Answer" ADD COLUMN "contentHash" TEXT;
7
+ ALTER TABLE "Answer" ADD COLUMN "chainHash" TEXT;
8
+
9
+ ALTER TABLE "Conversation" ADD COLUMN "integrityHash" TEXT;