schema.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import {
  2. boolean,
  3. integer,
  4. jsonb,
  5. pgEnum,
  6. pgTable,
  7. serial,
  8. text,
  9. timestamp,
  10. varchar,
  11. } from "drizzle-orm/pg-core";
  12. // ── Enum definitions ─────────────────────────────────────────────────────────
  13. export const roleEnum = pgEnum("role", ["user", "agent", "admin"]);
  14. export const conversationStatusEnum = pgEnum("conversation_status", ["active", "escalated", "resolved", "closed"]);
  15. export const senderEnum = pgEnum("sender", ["visitor", "bot", "agent"]);
  16. export const workflowNodeTypeEnum = pgEnum("workflow_node_type", [
  17. "greeting", "intent", "response", "condition", "escalation",
  18. "action", "end", "customer_data", "sales_order", "guardrail",
  19. ]);
  20. export const inviteStatusEnum = pgEnum("invite_status", ["pending", "accepted", "expired", "revoked"]);
  21. export const suggestionStatusEnum = pgEnum("suggestion_status", ["pending", "approved", "declined", "waiting"]);
  22. export const eventTypeEnum = pgEnum("event_type", [
  23. "session_start", "message_sent", "message_received",
  24. "intent_detected", "flow_triggered", "escalated",
  25. "resolved_by_bot", "resolved_by_agent", "abandoned",
  26. "button_clicked", "feedback_positive", "feedback_negative",
  27. ]);
  28. export const sourceTypeEnum = pgEnum("source_type", ["url", "file", "qa_pair", "api"]);
  29. export const sourceStatusEnum = pgEnum("source_status", ["active", "inactive", "syncing", "error"]);
  30. export const httpMethodEnum = pgEnum("http_method", ["GET", "POST", "PUT", "DELETE"]);
  31. // ── Tables ───────────────────────────────────────────────────────────────────
  32. /**
  33. * Core user table backing auth flow.
  34. */
  35. export const users = pgTable("users", {
  36. id: serial("id").primaryKey(),
  37. openId: varchar("openId", { length: 64 }).notNull().unique(),
  38. name: text("name"),
  39. email: varchar("email", { length: 320 }),
  40. loginMethod: varchar("loginMethod", { length: 64 }),
  41. role: roleEnum("role").default("user").notNull(),
  42. createdAt: timestamp("createdAt").defaultNow().notNull(),
  43. updatedAt: timestamp("updatedAt").defaultNow().notNull(),
  44. lastSignedIn: timestamp("lastSignedIn").defaultNow().notNull(),
  45. passwordHash: varchar("passwordHash", { length: 255 }),
  46. });
  47. export type User = typeof users.$inferSelect;
  48. export type InsertUser = typeof users.$inferInsert;
  49. /**
  50. * Password reset tokens — for forgot password flow.
  51. */
  52. export const passwordResetTokens = pgTable("password_reset_tokens", {
  53. id: serial("id").primaryKey(),
  54. userId: integer("userId").notNull(),
  55. token: varchar("token", { length: 64 }).notNull().unique(),
  56. expiresAt: timestamp("expiresAt").notNull(),
  57. usedAt: timestamp("usedAt"),
  58. createdAt: timestamp("createdAt").defaultNow().notNull(),
  59. });
  60. export type PasswordResetToken = typeof passwordResetTokens.$inferSelect;
  61. export type InsertPasswordResetToken = typeof passwordResetTokens.$inferInsert;
  62. /**
  63. * Chat conversations — each visitor session creates one conversation.
  64. */
  65. export const conversations = pgTable("conversations", {
  66. id: serial("id").primaryKey(),
  67. sessionId: varchar("sessionId", { length: 64 }).notNull().unique(),
  68. visitorName: varchar("visitorName", { length: 255 }),
  69. visitorEmail: varchar("visitorEmail", { length: 320 }),
  70. customerId: varchar("customerId", { length: 64 }),
  71. salesRep: varchar("salesRep", { length: 255 }),
  72. status: conversationStatusEnum("status").default("active").notNull(),
  73. assignedAgentId: integer("assignedAgentId"),
  74. metadata: jsonb("metadata"),
  75. createdAt: timestamp("createdAt").defaultNow().notNull(),
  76. updatedAt: timestamp("updatedAt").defaultNow().notNull(),
  77. });
  78. export type Conversation = typeof conversations.$inferSelect;
  79. export type InsertConversation = typeof conversations.$inferInsert;
  80. /**
  81. * Chat messages — each message belongs to a conversation.
  82. */
  83. export const messages = pgTable("messages", {
  84. id: serial("id").primaryKey(),
  85. conversationId: integer("conversationId").notNull(),
  86. sender: senderEnum("sender").notNull(),
  87. content: text("content").notNull(),
  88. metadata: jsonb("metadata"),
  89. createdAt: timestamp("createdAt").defaultNow().notNull(),
  90. });
  91. export type Message = typeof messages.$inferSelect;
  92. export type InsertMessage = typeof messages.$inferInsert;
  93. /**
  94. * Workflow nodes — each node in the chatbot conversation flow.
  95. */
  96. export const workflowNodes = pgTable("workflow_nodes", {
  97. id: serial("id").primaryKey(),
  98. workflowId: varchar("workflowId", { length: 64 }).notNull(),
  99. nodeId: varchar("nodeId", { length: 64 }).notNull(),
  100. type: workflowNodeTypeEnum("type").notNull(),
  101. label: varchar("label", { length: 255 }).notNull(),
  102. config: jsonb("config"),
  103. positionX: integer("positionX").default(0).notNull(),
  104. positionY: integer("positionY").default(0).notNull(),
  105. createdAt: timestamp("createdAt").defaultNow().notNull(),
  106. updatedAt: timestamp("updatedAt").defaultNow().notNull(),
  107. });
  108. export type WorkflowNode = typeof workflowNodes.$inferSelect;
  109. export type InsertWorkflowNode = typeof workflowNodes.$inferInsert;
  110. /**
  111. * Workflow edges — connections between nodes.
  112. */
  113. export const workflowEdges = pgTable("workflow_edges", {
  114. id: serial("id").primaryKey(),
  115. workflowId: varchar("workflowId", { length: 64 }).notNull(),
  116. sourceNodeId: varchar("sourceNodeId", { length: 64 }).notNull(),
  117. targetNodeId: varchar("targetNodeId", { length: 64 }).notNull(),
  118. label: varchar("label", { length: 255 }),
  119. condition: jsonb("condition"),
  120. createdAt: timestamp("createdAt").defaultNow().notNull(),
  121. });
  122. export type WorkflowEdge = typeof workflowEdges.$inferSelect;
  123. export type InsertWorkflowEdge = typeof workflowEdges.$inferInsert;
  124. /**
  125. * Invitations — track sent invitations and their acceptance status.
  126. */
  127. export const invitations = pgTable("invitations", {
  128. id: serial("id").primaryKey(),
  129. email: varchar("email", { length: 320 }).notNull(),
  130. role: roleEnum("role").default("agent").notNull(),
  131. token: varchar("token", { length: 64 }).notNull().unique(),
  132. status: inviteStatusEnum("status").default("pending").notNull(),
  133. invitedById: integer("invitedById").notNull(),
  134. invitedByName: varchar("invitedByName", { length: 255 }),
  135. acceptedByUserId: integer("acceptedByUserId"),
  136. message: text("message"),
  137. expiresAt: timestamp("expiresAt").notNull(),
  138. acceptedAt: timestamp("acceptedAt"),
  139. createdAt: timestamp("createdAt").defaultNow().notNull(),
  140. updatedAt: timestamp("updatedAt").defaultNow().notNull(),
  141. });
  142. export type Invitation = typeof invitations.$inferSelect;
  143. export type InsertInvitation = typeof invitations.$inferInsert;
  144. /**
  145. * Audit logs — track important user management actions.
  146. */
  147. export const auditLogs = pgTable("audit_logs", {
  148. id: serial("id").primaryKey(),
  149. action: varchar("action", { length: 64 }).notNull(),
  150. actorId: integer("actorId").notNull(),
  151. actorName: varchar("actorName", { length: 255 }),
  152. targetId: integer("targetId"),
  153. targetName: varchar("targetName", { length: 255 }),
  154. details: jsonb("details"),
  155. createdAt: timestamp("createdAt").defaultNow().notNull(),
  156. });
  157. export type AuditLog = typeof auditLogs.$inferSelect;
  158. export type InsertAuditLog = typeof auditLogs.$inferInsert;
  159. /**
  160. * AI workflow suggestions — AI-recommended nodes based on FAQ analysis.
  161. */
  162. export const workflowSuggestions = pgTable("workflow_suggestions", {
  163. id: serial("id").primaryKey(),
  164. workflowId: varchar("workflowId", { length: 64 }).notNull(),
  165. suggestedNodeType: varchar("suggestedNodeType", { length: 64 }).notNull(),
  166. label: varchar("label", { length: 255 }).notNull(),
  167. description: text("description"),
  168. config: jsonb("config"),
  169. faqQuestion: text("faqQuestion"),
  170. frequency: integer("frequency").default(0).notNull(),
  171. status: suggestionStatusEnum("status").default("pending").notNull(),
  172. reviewedById: integer("reviewedById"),
  173. reviewedAt: timestamp("reviewedAt"),
  174. createdAt: timestamp("createdAt").defaultNow().notNull(),
  175. updatedAt: timestamp("updatedAt").defaultNow().notNull(),
  176. });
  177. export type WorkflowSuggestion = typeof workflowSuggestions.$inferSelect;
  178. export type InsertWorkflowSuggestion = typeof workflowSuggestions.$inferInsert;
  179. /**
  180. * Analytics events — track chatbot interactions for resolution rate and metrics.
  181. */
  182. export const analyticsEvents = pgTable("analytics_events", {
  183. id: serial("id").primaryKey(),
  184. conversationId: integer("conversationId"),
  185. sessionId: varchar("sessionId", { length: 64 }),
  186. eventType: eventTypeEnum("eventType").notNull(),
  187. category: varchar("category", { length: 64 }),
  188. metadata: jsonb("metadata"),
  189. createdAt: timestamp("createdAt").defaultNow().notNull(),
  190. });
  191. export type AnalyticsEvent = typeof analyticsEvents.$inferSelect;
  192. export type InsertAnalyticsEvent = typeof analyticsEvents.$inferInsert;
  193. /**
  194. * Data sources — knowledge base for the AI agent.
  195. */
  196. export const dataSources = pgTable("data_sources", {
  197. id: serial("id").primaryKey(),
  198. name: varchar("name", { length: 255 }).notNull(),
  199. type: sourceTypeEnum("type").notNull(),
  200. status: sourceStatusEnum("status").default("active").notNull(),
  201. config: jsonb("config"),
  202. lastSyncedAt: timestamp("lastSyncedAt"),
  203. itemCount: integer("itemCount").default(0).notNull(),
  204. createdById: integer("createdById"),
  205. createdAt: timestamp("createdAt").defaultNow().notNull(),
  206. updatedAt: timestamp("updatedAt").defaultNow().notNull(),
  207. });
  208. export type DataSource = typeof dataSources.$inferSelect;
  209. export type InsertDataSource = typeof dataSources.$inferInsert;
  210. /**
  211. * API connections — external API endpoints for Actions.
  212. */
  213. export const apiConnections = pgTable("api_connections", {
  214. id: serial("id").primaryKey(),
  215. name: varchar("name", { length: 255 }).notNull(),
  216. description: text("description"),
  217. category: varchar("category", { length: 64 }),
  218. method: httpMethodEnum("method").default("GET").notNull(),
  219. endpoint: varchar("endpoint", { length: 1024 }).notNull(),
  220. headers: jsonb("headers"),
  221. inputVariables: jsonb("inputVariables"),
  222. outputVariables: jsonb("outputVariables"),
  223. testPayload: jsonb("testPayload"),
  224. isActive: boolean("isActive").default(true).notNull(),
  225. executionCount: integer("executionCount").default(0).notNull(),
  226. lastExecutedAt: timestamp("lastExecutedAt"),
  227. createdById: integer("createdById"),
  228. createdAt: timestamp("createdAt").defaultNow().notNull(),
  229. updatedAt: timestamp("updatedAt").defaultNow().notNull(),
  230. });
  231. export type ApiConnection = typeof apiConnections.$inferSelect;
  232. export type InsertApiConnection = typeof apiConnections.$inferInsert;