schema.prisma 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // EKS Support Relay — persistence layer.
  2. // SQLite for now; the schema deliberately avoids SQLite-only features so a
  3. // later move to PostgreSQL only needs a datasource/provider change.
  4. generator client {
  5. provider = "prisma-client-js"
  6. output = "../node_modules/.prisma/client"
  7. }
  8. datasource db {
  9. provider = "sqlite"
  10. url = env("DATABASE_URL")
  11. }
  12. /// One row per inbound source message. Primary idempotency guard for the
  13. /// Chatwoot webhook: (source, messageId) is unique.
  14. model ProcessedMessage {
  15. id String @id @default(cuid())
  16. source String
  17. messageId String
  18. conversationId Int
  19. /// queued | processing | replied | skipped | ticket | failed | spam
  20. status String
  21. reason String?
  22. createdAt DateTime @default(now())
  23. updatedAt DateTime @updatedAt
  24. @@unique([source, messageId])
  25. @@index([conversationId])
  26. @@index([status])
  27. }
  28. /// Async work item. The webhook persists a job and returns 202; the in-process
  29. /// worker picks it up, with retries and exponential backoff.
  30. model Job {
  31. id String @id @default(cuid())
  32. /// chatwoot_message
  33. type String
  34. /// queued | processing | done | failed | dead
  35. status String @default("queued")
  36. attempts Int @default(0)
  37. maxAttempts Int @default(3)
  38. /// Redacted, minimal payload — never full secrets.
  39. payloadJson String
  40. /// Redacted error text.
  41. lastError String?
  42. runAfter DateTime @default(now())
  43. startedAt DateTime?
  44. finishedAt DateTime?
  45. createdAt DateTime @default(now())
  46. updatedAt DateTime @updatedAt
  47. @@index([status, runAfter])
  48. }
  49. /// Handoff record. conversationId is unique so repeated new_ticket calls for
  50. /// the same conversation always resolve to the same ticket number.
  51. model Ticket {
  52. id String @id @default(cuid())
  53. conversationId Int @unique
  54. ticketNumber String
  55. reason String?
  56. createdAt DateTime @default(now())
  57. }
  58. /// Non-secret audit trail of decisions taken per message/conversation.
  59. model AuditEvent {
  60. id String @id @default(cuid())
  61. conversationId Int?
  62. messageId String?
  63. /// Set when the event belongs to a queued job, so the ops panel can join the
  64. /// audit trail to the queue without parsing summary text.
  65. jobId String?
  66. eventType String
  67. summary String
  68. /// Redacted JSON metadata.
  69. metaJson String?
  70. createdAt DateTime @default(now())
  71. @@index([conversationId])
  72. @@index([eventType])
  73. @@index([createdAt])
  74. @@index([jobId])
  75. }
  76. /// Cached AI translations for agent UI. Keyed by text hash rather than raw text
  77. /// to avoid duplicating customer content unnecessarily in lookup keys.
  78. model TranslationCache {
  79. id String @id @default(cuid())
  80. conversationId Int?
  81. messageId String?
  82. sourceHash String
  83. sourceLanguage String?
  84. targetLanguage String
  85. translatedText String
  86. provider String
  87. createdAt DateTime @default(now())
  88. updatedAt DateTime @updatedAt
  89. @@unique([sourceHash, targetLanguage])
  90. @@index([conversationId])
  91. @@index([messageId])
  92. }