@axova/shared 1.0.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.
Files changed (112) hide show
  1. package/CONFIGURATION_GUIDE.md +1 -0
  2. package/README.md +384 -0
  3. package/SCHEMA_ORGANIZATION.md +209 -0
  4. package/dist/configs/index.d.ts +85 -0
  5. package/dist/configs/index.js +555 -0
  6. package/dist/events/kafka.d.ts +40 -0
  7. package/dist/events/kafka.js +311 -0
  8. package/dist/index.d.ts +13 -0
  9. package/dist/index.js +41 -0
  10. package/dist/interfaces/customer-events.d.ts +85 -0
  11. package/dist/interfaces/customer-events.js +2 -0
  12. package/dist/interfaces/inventory-events.d.ts +453 -0
  13. package/dist/interfaces/inventory-events.js +3 -0
  14. package/dist/interfaces/inventory-types.d.ts +894 -0
  15. package/dist/interfaces/inventory-types.js +3 -0
  16. package/dist/interfaces/order-events.d.ts +320 -0
  17. package/dist/interfaces/order-events.js +3 -0
  18. package/dist/lib/auditLogger.d.ts +162 -0
  19. package/dist/lib/auditLogger.js +626 -0
  20. package/dist/lib/authOrganization.d.ts +24 -0
  21. package/dist/lib/authOrganization.js +110 -0
  22. package/dist/lib/db.d.ts +6 -0
  23. package/dist/lib/db.js +88 -0
  24. package/dist/middleware/serviceAuth.d.ts +60 -0
  25. package/dist/middleware/serviceAuth.js +272 -0
  26. package/dist/middleware/storeOwnership.d.ts +15 -0
  27. package/dist/middleware/storeOwnership.js +156 -0
  28. package/dist/middleware/storeValidationMiddleware.d.ts +44 -0
  29. package/dist/middleware/storeValidationMiddleware.js +180 -0
  30. package/dist/middleware/userAuth.d.ts +27 -0
  31. package/dist/middleware/userAuth.js +218 -0
  32. package/dist/schemas/admin/admin-schema.d.ts +741 -0
  33. package/dist/schemas/admin/admin-schema.js +111 -0
  34. package/dist/schemas/ai-moderation/ai-moderation-schema.d.ts +648 -0
  35. package/dist/schemas/ai-moderation/ai-moderation-schema.js +88 -0
  36. package/dist/schemas/common/common-schemas.d.ts +436 -0
  37. package/dist/schemas/common/common-schemas.js +94 -0
  38. package/dist/schemas/compliance/compliance-schema.d.ts +3388 -0
  39. package/dist/schemas/compliance/compliance-schema.js +472 -0
  40. package/dist/schemas/compliance/kyc-schema.d.ts +2642 -0
  41. package/dist/schemas/compliance/kyc-schema.js +361 -0
  42. package/dist/schemas/customer/customer-schema.d.ts +2727 -0
  43. package/dist/schemas/customer/customer-schema.js +399 -0
  44. package/dist/schemas/index.d.ts +27 -0
  45. package/dist/schemas/index.js +138 -0
  46. package/dist/schemas/inventory/inventory-tables.d.ts +9476 -0
  47. package/dist/schemas/inventory/inventory-tables.js +1470 -0
  48. package/dist/schemas/inventory/lot-tables.d.ts +3281 -0
  49. package/dist/schemas/inventory/lot-tables.js +608 -0
  50. package/dist/schemas/order/order-schema.d.ts +5825 -0
  51. package/dist/schemas/order/order-schema.js +954 -0
  52. package/dist/schemas/product/discount-relations.d.ts +15 -0
  53. package/dist/schemas/product/discount-relations.js +34 -0
  54. package/dist/schemas/product/discount-schema.d.ts +1975 -0
  55. package/dist/schemas/product/discount-schema.js +297 -0
  56. package/dist/schemas/product/product-relations.d.ts +41 -0
  57. package/dist/schemas/product/product-relations.js +133 -0
  58. package/dist/schemas/product/product-schema.d.ts +4544 -0
  59. package/dist/schemas/product/product-schema.js +671 -0
  60. package/dist/schemas/store/store-audit-schema.d.ts +4135 -0
  61. package/dist/schemas/store/store-audit-schema.js +556 -0
  62. package/dist/schemas/store/store-schema.d.ts +3100 -0
  63. package/dist/schemas/store/store-schema.js +381 -0
  64. package/dist/schemas/store/store-settings-schema.d.ts +665 -0
  65. package/dist/schemas/store/store-settings-schema.js +141 -0
  66. package/dist/schemas/types.d.ts +50 -0
  67. package/dist/schemas/types.js +3 -0
  68. package/dist/types/events.d.ts +2396 -0
  69. package/dist/types/events.js +505 -0
  70. package/dist/utils/errorHandler.d.ts +12 -0
  71. package/dist/utils/errorHandler.js +36 -0
  72. package/dist/utils/subdomain.d.ts +6 -0
  73. package/dist/utils/subdomain.js +20 -0
  74. package/nul +8 -0
  75. package/package.json +43 -0
  76. package/src/configs/index.ts +654 -0
  77. package/src/events/kafka.ts +429 -0
  78. package/src/index.ts +26 -0
  79. package/src/interfaces/customer-events.ts +106 -0
  80. package/src/interfaces/inventory-events.ts +545 -0
  81. package/src/interfaces/inventory-types.ts +1004 -0
  82. package/src/interfaces/order-events.ts +381 -0
  83. package/src/lib/auditLogger.ts +1117 -0
  84. package/src/lib/authOrganization.ts +153 -0
  85. package/src/lib/db.ts +64 -0
  86. package/src/middleware/serviceAuth.ts +328 -0
  87. package/src/middleware/storeOwnership.ts +199 -0
  88. package/src/middleware/storeValidationMiddleware.ts +247 -0
  89. package/src/middleware/userAuth.ts +248 -0
  90. package/src/schemas/admin/admin-schema.ts +208 -0
  91. package/src/schemas/ai-moderation/ai-moderation-schema.ts +180 -0
  92. package/src/schemas/common/common-schemas.ts +108 -0
  93. package/src/schemas/compliance/compliance-schema.ts +927 -0
  94. package/src/schemas/compliance/kyc-schema.ts +649 -0
  95. package/src/schemas/customer/customer-schema.ts +576 -0
  96. package/src/schemas/index.ts +189 -0
  97. package/src/schemas/inventory/inventory-tables.ts +1927 -0
  98. package/src/schemas/inventory/lot-tables.ts +799 -0
  99. package/src/schemas/order/order-schema.ts +1400 -0
  100. package/src/schemas/product/discount-relations.ts +44 -0
  101. package/src/schemas/product/discount-schema.ts +464 -0
  102. package/src/schemas/product/product-relations.ts +187 -0
  103. package/src/schemas/product/product-schema.ts +955 -0
  104. package/src/schemas/store/ethiopian_business_api.md.resolved +212 -0
  105. package/src/schemas/store/store-audit-schema.ts +1257 -0
  106. package/src/schemas/store/store-schema.ts +661 -0
  107. package/src/schemas/store/store-settings-schema.ts +231 -0
  108. package/src/schemas/types.ts +67 -0
  109. package/src/types/events.ts +646 -0
  110. package/src/utils/errorHandler.ts +44 -0
  111. package/src/utils/subdomain.ts +19 -0
  112. package/tsconfig.json +21 -0
@@ -0,0 +1,927 @@
1
+ import { createId } from "@paralleldrive/cuid2";
2
+ import { relations } from "drizzle-orm";
3
+ import {
4
+ boolean,
5
+ decimal,
6
+ index,
7
+ integer,
8
+ jsonb,
9
+ pgTable,
10
+ text,
11
+ timestamp,
12
+ unique,
13
+ varchar,
14
+ } from "drizzle-orm/pg-core";
15
+
16
+ // Store Compliance Table - Enhanced
17
+ export const storeCompliance = pgTable(
18
+ "store_compliance",
19
+ {
20
+ id: text("id")
21
+ .primaryKey()
22
+ .$defaultFn(() => createId()),
23
+ storeId: text("store_id").notNull().unique(),
24
+
25
+ // Verification & Compliance Status
26
+ isVerified: boolean("is_verified").notNull().default(false),
27
+ isFeatureStore: boolean("is_feature_store").notNull().default(false),
28
+ verificationStatus: varchar("verification_status", { length: 50 })
29
+ .notNull()
30
+ .default("STANDARD")
31
+ .$type<
32
+ "STANDARD" | "VERIFIED" | "PREMIUM_VERIFIED" | "ENTERPRISE_VERIFIED"
33
+ >(),
34
+
35
+ // Verification Details
36
+ verificationLevel: varchar("verification_level", { length: 30 })
37
+ .$type<"BASIC" | "ENHANCED" | "PREMIUM" | "ENTERPRISE">()
38
+ .default("BASIC"),
39
+ verificationBadges: jsonb("verification_badges")
40
+ .$type<string[]>()
41
+ .default([]),
42
+ verifiedAt: timestamp("verified_at", { withTimezone: true }),
43
+ verificationExpiry: timestamp("verification_expiry", {
44
+ withTimezone: true,
45
+ }),
46
+
47
+ // Store Status
48
+ isBanned: boolean("is_banned").notNull().default(false),
49
+ isSuspended: boolean("is_suspended").notNull().default(false),
50
+ isUnderReview: boolean("is_under_review").notNull().default(false),
51
+
52
+ // Ban/Suspension Details
53
+ banReason: text("ban_reason"),
54
+ banLevel: varchar("ban_level", { length: 20 }).$type<
55
+ "TEMPORARY" | "PERMANENT" | "CONDITIONAL"
56
+ >(),
57
+ banStartDate: timestamp("ban_start_date", { withTimezone: true }),
58
+ banEndDate: timestamp("ban_end_date", { withTimezone: true }),
59
+
60
+ suspensionReason: text("suspension_reason"),
61
+ suspensionLevel: varchar("suspension_level", { length: 20 }).$type<
62
+ "PARTIAL" | "FULL" | "PAYMENT_ONLY" | "LISTING_ONLY"
63
+ >(),
64
+ suspensionStartDate: timestamp("suspension_start_date", {
65
+ withTimezone: true,
66
+ }),
67
+ suspensionEndDate: timestamp("suspension_end_date", { withTimezone: true }),
68
+
69
+ // Compliance Metrics
70
+ warningCount: integer("warning_count").notNull().default(0),
71
+ violationCount: integer("violation_count").notNull().default(0),
72
+ appealCount: integer("appeal_count").notNull().default(0),
73
+ successfulAppealCount: integer("successful_appeal_count")
74
+ .notNull()
75
+ .default(0),
76
+
77
+ lastWarningDate: timestamp("last_warning_date", { withTimezone: true }),
78
+ lastViolationDate: timestamp("last_violation_date", { withTimezone: true }),
79
+
80
+ // Compliance Score (0-100)
81
+ complianceScore: integer("compliance_score").default(100),
82
+ riskLevel: varchar("risk_level", { length: 20 })
83
+ .$type<"LOW" | "MEDIUM" | "HIGH" | "CRITICAL">()
84
+ .default("LOW"),
85
+
86
+ // Trust and Safety
87
+ trustScore: decimal("trust_score", { precision: 5, scale: 2 }).default(
88
+ "100.00",
89
+ ),
90
+ safetyRating: varchar("safety_rating", { length: 10 })
91
+ .$type<"A+" | "A" | "B" | "C" | "D" | "F">()
92
+ .default("A"),
93
+
94
+ // Review and Monitoring
95
+ nextReviewDate: timestamp("next_review_date", { withTimezone: true }),
96
+ reviewFrequency: varchar("review_frequency", { length: 20 })
97
+ .$type<"WEEKLY" | "MONTHLY" | "QUARTERLY" | "ANNUALLY" | "ON_DEMAND">()
98
+ .default("QUARTERLY"),
99
+
100
+ monitoringLevel: varchar("monitoring_level", { length: 20 })
101
+ .$type<"STANDARD" | "ENHANCED" | "INTENSIVE" | "MANUAL_ONLY">()
102
+ .default("STANDARD"),
103
+
104
+ // Compliance Flags
105
+ flags: jsonb("flags")
106
+ .$type<{
107
+ highRisk?: boolean;
108
+ repeatOffender?: boolean;
109
+ escalated?: boolean;
110
+ whitelisted?: boolean;
111
+ blacklisted?: boolean;
112
+ underInvestigation?: boolean;
113
+ requiresManualReview?: boolean;
114
+ autoModerationDisabled?: boolean;
115
+ }>()
116
+ .default({}),
117
+
118
+ // Return and Refund Settings
119
+ returnSettings: jsonb("return_settings")
120
+ .$type<{
121
+ returnWindow: number; // days
122
+ returnReasons: string[];
123
+ returnInstructions: string;
124
+ returnShippingPolicy: string;
125
+ restockingFee?: number;
126
+ returnConditions?: string[];
127
+ exchangePolicy?: string;
128
+ digitalReturnPolicy?: string;
129
+ }>()
130
+ .default({
131
+ returnWindow: 30,
132
+ returnReasons: [],
133
+ returnInstructions: "",
134
+ returnShippingPolicy: "",
135
+ }),
136
+
137
+ createdAt: timestamp("created_at", { withTimezone: true })
138
+ .defaultNow()
139
+ .notNull(),
140
+ updatedAt: timestamp("updated_at", { withTimezone: true })
141
+ .defaultNow()
142
+ .notNull(),
143
+ },
144
+ (table) => ({
145
+ storeIdIndex: index("idx_compliance_store_id").on(table.storeId),
146
+ verificationStatusIndex: index("idx_compliance_verification_status").on(
147
+ table.verificationStatus,
148
+ ),
149
+ complianceScoreIndex: index("idx_compliance_score").on(
150
+ table.complianceScore,
151
+ ),
152
+ riskLevelIndex: index("idx_compliance_risk_level").on(table.riskLevel),
153
+
154
+ // Status monitoring indexes
155
+ bannedStoresIndex: index("idx_compliance_banned").on(
156
+ table.isBanned,
157
+ table.banEndDate,
158
+ ),
159
+ suspendedStoresIndex: index("idx_compliance_suspended").on(
160
+ table.isSuspended,
161
+ table.suspensionEndDate,
162
+ ),
163
+ underReviewIndex: index("idx_compliance_under_review").on(
164
+ table.isUnderReview,
165
+ ),
166
+
167
+ // Review workflow indexes
168
+ nextReviewIndex: index("idx_compliance_next_review").on(
169
+ table.nextReviewDate,
170
+ table.reviewFrequency,
171
+ ),
172
+ monitoringIndex: index("idx_compliance_monitoring").on(
173
+ table.monitoringLevel,
174
+ table.riskLevel,
175
+ ),
176
+ }),
177
+ );
178
+
179
+ // Compliance Violations Table - Enhanced
180
+ export const complianceViolations = pgTable(
181
+ "compliance_violations",
182
+ {
183
+ id: text("id")
184
+ .primaryKey()
185
+ .$defaultFn(() => createId()),
186
+ storeId: text("store_id").notNull(),
187
+
188
+ // Violation Classification
189
+ violationType: varchar("violation_type", { length: 50 })
190
+ .notNull()
191
+ .$type<
192
+ | "CONTENT_VIOLATION"
193
+ | "POLICY_VIOLATION"
194
+ | "BEHAVIOR_VIOLATION"
195
+ | "PAYMENT_VIOLATION"
196
+ | "TECHNICAL_VIOLATION"
197
+ | "LEGAL_VIOLATION"
198
+ >(),
199
+
200
+ violationCategory: varchar("violation_category", { length: 50 }).$type<
201
+ | "PROHIBITED_CONTENT"
202
+ | "MISLEADING_INFO"
203
+ | "COPYRIGHT"
204
+ | "TRADEMARK"
205
+ | "FRAUD"
206
+ | "SPAM"
207
+ | "HARASSMENT"
208
+ | "PRIVACY"
209
+ | "SAFETY"
210
+ | "OTHER"
211
+ >(),
212
+
213
+ severity: varchar("severity", { length: 20 })
214
+ .notNull()
215
+ .$type<"LOW" | "MEDIUM" | "HIGH" | "CRITICAL" | "URGENT">(),
216
+
217
+ priority: varchar("priority", { length: 20 })
218
+ .notNull()
219
+ .default("NORMAL")
220
+ .$type<"LOW" | "NORMAL" | "HIGH" | "URGENT" | "EMERGENCY">(),
221
+
222
+ // Violation Details
223
+ title: varchar("title", { length: 255 }).notNull(),
224
+ description: text("description").notNull(),
225
+ context: text("context"), // Additional context about where/how violation occurred
226
+
227
+ // Evidence and Documentation
228
+ evidence: jsonb("evidence").$type<{
229
+ screenshots?: string[];
230
+ urls?: string[];
231
+ reportedContent?: string;
232
+ systemLogs?: Record<string, unknown>;
233
+ userReports?: Array<{
234
+ reporterId: string;
235
+ reason: string;
236
+ timestamp: string;
237
+ evidence?: string;
238
+ }>;
239
+ additionalData?: Record<string, unknown>;
240
+ }>(),
241
+
242
+ // Detection Information
243
+ detectedBy: varchar("detected_by", { length: 50 })
244
+ .notNull()
245
+ .$type<
246
+ | "SYSTEM"
247
+ | "AI_MODERATION"
248
+ | "USER_REPORT"
249
+ | "ADMIN_REVIEW"
250
+ | "AUTOMATED_SCAN"
251
+ | "COMPLIANCE_AUDIT"
252
+ >(),
253
+
254
+ detectorId: text("detector_id"), // ID of the user, system, or AI that detected it
255
+ detectionMethod: varchar("detection_method", { length: 50 }).$type<
256
+ | "KEYWORD_FILTER"
257
+ | "IMAGE_RECOGNITION"
258
+ | "PATTERN_ANALYSIS"
259
+ | "USER_REPORT"
260
+ | "MANUAL_REVIEW"
261
+ | "AUTOMATED_RULE"
262
+ >(),
263
+
264
+ detectionConfidence: decimal("detection_confidence", {
265
+ precision: 5,
266
+ scale: 2,
267
+ }), // 0-100%
268
+
269
+ // Processing Status
270
+ status: varchar("status", { length: 20 })
271
+ .notNull()
272
+ .default("PENDING")
273
+ .$type<
274
+ | "PENDING"
275
+ | "UNDER_REVIEW"
276
+ | "ESCALATED"
277
+ | "RESOLVED"
278
+ | "DISMISSED"
279
+ | "APPEALED"
280
+ | "CLOSED"
281
+ >(),
282
+
283
+ // Actions and Resolution
284
+ actionTaken: varchar("action_taken", { length: 50 }).$type<
285
+ | "NONE"
286
+ | "WARNING"
287
+ | "CONTENT_REMOVAL"
288
+ | "LISTING_SUSPENSION"
289
+ | "ACCOUNT_SUSPENSION"
290
+ | "ACCOUNT_BAN"
291
+ | "FINE"
292
+ | "EDUCATION_REQUIRED"
293
+ >(),
294
+
295
+ autoActionApplied: boolean("auto_action_applied").notNull().default(false),
296
+ requiresManualReview: boolean("requires_manual_review")
297
+ .notNull()
298
+ .default(false),
299
+
300
+ // Resolution Details
301
+ resolvedBy: text("resolved_by"),
302
+ resolvedAt: timestamp("resolved_at", { withTimezone: true }),
303
+ resolutionNotes: text("resolution_notes"),
304
+ resolutionType: varchar("resolution_type", { length: 30 }).$type<
305
+ "AUTOMATED" | "MANUAL" | "ESCALATED" | "APPEALED" | "ADMIN_OVERRIDE"
306
+ >(),
307
+
308
+ // Impact Assessment
309
+ impactScore: integer("impact_score").default(1), // 1-10 scale
310
+ affectedUsers: integer("affected_users").default(0),
311
+ businessImpact: varchar("business_impact", { length: 20 }).$type<
312
+ "MINIMAL" | "LOW" | "MODERATE" | "HIGH" | "SEVERE"
313
+ >(),
314
+
315
+ // Follow-up and Monitoring
316
+ followUpRequired: boolean("follow_up_required").notNull().default(false),
317
+ followUpDate: timestamp("follow_up_date", { withTimezone: true }),
318
+ monitoringPeriod: integer("monitoring_period"), // days
319
+
320
+ createdAt: timestamp("created_at", { withTimezone: true })
321
+ .defaultNow()
322
+ .notNull(),
323
+ updatedAt: timestamp("updated_at", { withTimezone: true })
324
+ .defaultNow()
325
+ .notNull(),
326
+ },
327
+ (table) => ({
328
+ storeIdIndex: index("idx_violations_store_id").on(table.storeId),
329
+ statusIndex: index("idx_violations_status").on(table.status),
330
+ severityIndex: index("idx_violations_severity").on(table.severity),
331
+ detectedByIndex: index("idx_violations_detected_by").on(table.detectedBy),
332
+
333
+ // Workflow indexes
334
+ pendingReviewIndex: index("idx_violations_pending_review").on(
335
+ table.status,
336
+ table.requiresManualReview,
337
+ ),
338
+
339
+ // Performance monitoring indexes
340
+ violationTypeIndex: index("idx_violations_type_category").on(
341
+ table.violationType,
342
+ table.violationCategory,
343
+ ),
344
+
345
+ // Resolution tracking indexes
346
+ resolutionWorkflowIndex: index("idx_violations_resolution").on(
347
+ table.status,
348
+ table.resolvedAt,
349
+ ),
350
+
351
+ // Follow-up indexes
352
+ followUpIndex: index("idx_violations_follow_up").on(
353
+ table.followUpRequired,
354
+ table.followUpDate,
355
+ ),
356
+ }),
357
+ );
358
+
359
+ // Compliance Actions History - Enhanced
360
+ export const complianceActions = pgTable(
361
+ "compliance_actions",
362
+ {
363
+ id: text("id")
364
+ .primaryKey()
365
+ .$defaultFn(() => createId()),
366
+ storeId: text("store_id").notNull(),
367
+ violationId: text("violation_id").references(() => complianceViolations.id),
368
+
369
+ // Action Classification
370
+ actionType: varchar("action_type", { length: 50 })
371
+ .notNull()
372
+ .$type<
373
+ | "WARNING"
374
+ | "CONTENT_REMOVAL"
375
+ | "LISTING_SUSPENSION"
376
+ | "ACCOUNT_SUSPENSION"
377
+ | "ACCOUNT_BAN"
378
+ | "ACCOUNT_UNBAN"
379
+ | "VERIFICATION"
380
+ | "FINE"
381
+ | "EDUCATION_MANDATE"
382
+ | "MONITORING_INCREASE"
383
+ | "FEATURE_RESTRICTION"
384
+ >(),
385
+
386
+ actionCategory: varchar("action_category", { length: 30 }).$type<
387
+ "PREVENTIVE" | "CORRECTIVE" | "PUNITIVE" | "EDUCATIONAL" | "MONITORING"
388
+ >(),
389
+
390
+ severity: varchar("severity", { length: 20 })
391
+ .notNull()
392
+ .$type<"MINOR" | "MODERATE" | "MAJOR" | "SEVERE" | "CRITICAL">(),
393
+
394
+ // Action Details
395
+ reason: text("reason").notNull(),
396
+ description: text("description"),
397
+ justification: text("justification"),
398
+
399
+ // Duration and Scope
400
+ duration: integer("duration"), // Duration in hours for temporary actions
401
+ durationType: varchar("duration_type", { length: 20 }).$type<
402
+ "TEMPORARY" | "PERMANENT" | "CONDITIONAL" | "UNTIL_COMPLIANCE"
403
+ >(),
404
+
405
+ scope: jsonb("scope").$type<{
406
+ affectedFeatures?: string[];
407
+ affectedRegions?: string[];
408
+ affectedProducts?: string[];
409
+ limitations?: string[];
410
+ }>(),
411
+
412
+ // Execution Details
413
+ performedBy: text("performed_by").notNull(),
414
+ performedByType: varchar("performed_by_type", { length: 20 })
415
+ .notNull()
416
+ .$type<
417
+ | "SYSTEM"
418
+ | "AI_MODERATION"
419
+ | "ADMIN"
420
+ | "COMPLIANCE_TEAM"
421
+ | "LEGAL_TEAM"
422
+ | "ESCALATION_TEAM"
423
+ >(),
424
+
425
+ isAutomated: boolean("is_automated").notNull().default(false),
426
+ automationRule: text("automation_rule"), // Reference to the automation rule used
427
+
428
+ // Impact and Effectiveness
429
+ impactLevel: varchar("impact_level", { length: 20 }).$type<
430
+ "LOW" | "MEDIUM" | "HIGH" | "BUSINESS_CRITICAL"
431
+ >(),
432
+
433
+ effectiveness: varchar("effectiveness", { length: 20 }).$type<
434
+ | "PENDING"
435
+ | "EFFECTIVE"
436
+ | "PARTIALLY_EFFECTIVE"
437
+ | "INEFFECTIVE"
438
+ | "REQUIRES_ESCALATION"
439
+ >(),
440
+
441
+ // Additional Data and Context
442
+ additionalData: jsonb("additional_data").$type<{
443
+ originalValues?: Record<string, unknown>;
444
+ configurationChanges?: Record<string, unknown>;
445
+ systemParameters?: Record<string, unknown>;
446
+ businessMetrics?: Record<string, unknown>;
447
+ }>(),
448
+
449
+ notes: text("notes"),
450
+ internalNotes: text("internal_notes"), // Not visible to store owner
451
+
452
+ // Review and Approval
453
+ requiresApproval: boolean("requires_approval").notNull().default(false),
454
+ approvedBy: text("approved_by"),
455
+ approvedAt: timestamp("approved_at", { withTimezone: true }),
456
+ approvalNotes: text("approval_notes"),
457
+
458
+ // Execution Tracking
459
+ executedAt: timestamp("executed_at", { withTimezone: true }),
460
+ executionStatus: varchar("execution_status", { length: 20 })
461
+ .notNull()
462
+ .default("PENDING")
463
+ .$type<
464
+ | "PENDING"
465
+ | "IN_PROGRESS"
466
+ | "COMPLETED"
467
+ | "FAILED"
468
+ | "CANCELLED"
469
+ | "REVERSED"
470
+ >(),
471
+
472
+ executionNotes: text("execution_notes"),
473
+
474
+ // Reversal and Appeals
475
+ isReversible: boolean("is_reversible").notNull().default(true),
476
+ reversedBy: text("reversed_by"),
477
+ reversedAt: timestamp("reversed_at", { withTimezone: true }),
478
+ reversalReason: text("reversal_reason"),
479
+
480
+ createdAt: timestamp("created_at", { withTimezone: true })
481
+ .defaultNow()
482
+ .notNull(),
483
+ updatedAt: timestamp("updated_at", { withTimezone: true })
484
+ .defaultNow()
485
+ .notNull(),
486
+ },
487
+ (table) => ({
488
+ storeIdIndex: index("idx_actions_store_id").on(table.storeId),
489
+ actionTypeIndex: index("idx_actions_type").on(table.actionType),
490
+ violationIdIndex: index("idx_actions_violation_id").on(table.violationId),
491
+
492
+ // Workflow indexes
493
+ approvalWorkflowIndex: index("idx_actions_approval_workflow").on(
494
+ table.requiresApproval,
495
+ table.approvedAt,
496
+ ),
497
+
498
+ executionStatusIndex: index("idx_actions_execution_status").on(
499
+ table.executionStatus,
500
+ table.executedAt,
501
+ ),
502
+
503
+ // Performance tracking indexes
504
+ automationIndex: index("idx_actions_automation").on(
505
+ table.isAutomated,
506
+ table.actionType,
507
+ ),
508
+
509
+ effectivenessIndex: index("idx_actions_effectiveness").on(
510
+ table.effectiveness,
511
+ table.impactLevel,
512
+ ),
513
+ }),
514
+ );
515
+
516
+ // Store Policies Table - Enhanced
517
+ export const storePolicies = pgTable(
518
+ "store_policies",
519
+ {
520
+ id: text("id")
521
+ .primaryKey()
522
+ .$defaultFn(() => createId()),
523
+ storeId: text("store_id").notNull(),
524
+
525
+ // Policy Classification
526
+ policyType: varchar("policy_type", { length: 50 })
527
+ .notNull()
528
+ .$type<
529
+ | "RETURN_POLICY"
530
+ | "REFUND_POLICY"
531
+ | "TERMS_OF_SERVICE"
532
+ | "PRIVACY_POLICY"
533
+ | "SHIPPING_POLICY"
534
+ | "WARRANTY_POLICY"
535
+ | "CANCELLATION_POLICY"
536
+ | "DISPUTE_RESOLUTION"
537
+ | "DATA_PROTECTION"
538
+ | "COOKIE_POLICY"
539
+ >(),
540
+
541
+ policyCategory: varchar("policy_category", { length: 30 }).$type<
542
+ "CUSTOMER_FACING" | "LEGAL_COMPLIANCE" | "OPERATIONAL" | "REGULATORY"
543
+ >(),
544
+
545
+ // Content and Metadata
546
+ title: varchar("title", { length: 255 }).notNull(),
547
+ content: text("content").notNull(),
548
+ summary: text("summary"), // Brief summary for quick reference
549
+
550
+ // Versioning
551
+ version: varchar("version", { length: 20 }).notNull().default("1.0"),
552
+ versionNotes: text("version_notes"),
553
+ previousVersionId: text("previous_version_id"),
554
+
555
+ // Status and Lifecycle
556
+ status: varchar("status", { length: 20 })
557
+ .notNull()
558
+ .default("DRAFT")
559
+ .$type<
560
+ | "DRAFT"
561
+ | "PENDING_REVIEW"
562
+ | "APPROVED"
563
+ | "ACTIVE"
564
+ | "SUPERSEDED"
565
+ | "ARCHIVED"
566
+ | "DEPRECATED"
567
+ >(),
568
+
569
+ isActive: boolean("is_active").notNull().default(false),
570
+ isMandatory: boolean("is_mandatory").notNull().default(false),
571
+ isPublic: boolean("is_public").notNull().default(true),
572
+
573
+ // Compliance and Legal
574
+ requiresAcceptance: boolean("requires_acceptance").notNull().default(false),
575
+ acceptanceTracking: boolean("acceptance_tracking").notNull().default(false),
576
+ legalReviewRequired: boolean("legal_review_required")
577
+ .notNull()
578
+ .default(false),
579
+
580
+ complianceStandards: jsonb("compliance_standards")
581
+ .$type<string[]>()
582
+ .default([]), // GDPR, CCPA, etc.
583
+ jurisdictions: jsonb("jurisdictions").$type<string[]>().default([]), // Countries/regions where this applies
584
+
585
+ // Approval Workflow
586
+ approvedBy: text("approved_by"),
587
+ approvedAt: timestamp("approved_at", { withTimezone: true }),
588
+ approvalNotes: text("approval_notes"),
589
+
590
+ legalReviewedBy: text("legal_reviewed_by"),
591
+ legalReviewedAt: timestamp("legal_reviewed_at", { withTimezone: true }),
592
+ legalReviewNotes: text("legal_review_notes"),
593
+
594
+ // Effective Dates
595
+ effectiveFrom: timestamp("effective_from", { withTimezone: true }),
596
+ effectiveUntil: timestamp("effective_until", { withTimezone: true }),
597
+ lastReviewDate: timestamp("last_review_date", { withTimezone: true }),
598
+ nextReviewDate: timestamp("next_review_date", { withTimezone: true }),
599
+
600
+ // Localization
601
+ language: varchar("language", { length: 10 }).default("en"),
602
+ localizations: jsonb("localizations")
603
+ .$type<
604
+ Record<
605
+ string,
606
+ {
607
+ title: string;
608
+ content: string;
609
+ summary?: string;
610
+ status: string;
611
+ effectiveFrom?: string;
612
+ }
613
+ >
614
+ >()
615
+ .default({}),
616
+
617
+ // Display and Integration
618
+ displayOrder: integer("display_order").default(0),
619
+ categoryOrder: integer("category_order").default(0),
620
+
621
+ // Templates and Customization
622
+ templateId: text("template_id"), // Reference to policy template
623
+ customFields: jsonb("custom_fields")
624
+ .$type<Record<string, unknown>>()
625
+ .default({}),
626
+
627
+ createdAt: timestamp("created_at", { withTimezone: true })
628
+ .defaultNow()
629
+ .notNull(),
630
+ updatedAt: timestamp("updated_at", { withTimezone: true })
631
+ .defaultNow()
632
+ .notNull(),
633
+ },
634
+ (table) => ({
635
+ storeIdIndex: index("idx_policies_store_id").on(table.storeId),
636
+ policyTypeIndex: index("idx_policies_type").on(table.policyType),
637
+ statusIndex: index("idx_policies_status").on(table.status),
638
+
639
+ // Active policies index for quick filtering
640
+ activePoliciesIndex: index("idx_policies_active").on(
641
+ table.storeId,
642
+ table.isActive,
643
+ table.policyType,
644
+ ),
645
+
646
+ // Compliance workflow indexes
647
+ approvalWorkflowIndex: index("idx_policies_approval_workflow").on(
648
+ table.status,
649
+ table.legalReviewRequired,
650
+ ),
651
+
652
+ // Review scheduling indexes
653
+ reviewScheduleIndex: index("idx_policies_review_schedule").on(
654
+ table.nextReviewDate,
655
+ table.isActive,
656
+ ),
657
+
658
+ // Ensure only one active policy per type per store
659
+ uniqueActivePolicyPerType: unique("unique_active_policy_per_type").on(
660
+ table.storeId,
661
+ table.policyType,
662
+ table.isActive,
663
+ ),
664
+ }),
665
+ );
666
+
667
+ // Appeals Table - Enhanced
668
+ export const appeals = pgTable(
669
+ "appeals",
670
+ {
671
+ id: text("id")
672
+ .primaryKey()
673
+ .$defaultFn(() => createId()),
674
+ storeId: text("store_id").notNull(),
675
+ userId: text("user_id").notNull(),
676
+
677
+ // Appeal Classification
678
+ appealType: varchar("appeal_type", { length: 50 })
679
+ .notNull()
680
+ .$type<
681
+ | "ACCOUNT_BAN_APPEAL"
682
+ | "ACCOUNT_SUSPENSION_APPEAL"
683
+ | "WARNING_APPEAL"
684
+ | "CONTENT_REMOVAL_APPEAL"
685
+ | "POLICY_DISPUTE"
686
+ | "VERIFICATION_APPEAL"
687
+ | "FINE_APPEAL"
688
+ | "FEATURE_RESTRICTION_APPEAL"
689
+ >(),
690
+
691
+ appealCategory: varchar("appeal_category", { length: 30 }).$type<
692
+ | "WRONGFUL_ACTION"
693
+ | "PROCEDURAL_ERROR"
694
+ | "NEW_EVIDENCE"
695
+ | "CHANGED_CIRCUMSTANCES"
696
+ | "POLICY_INTERPRETATION"
697
+ | "TECHNICAL_ISSUE"
698
+ >(),
699
+
700
+ priority: varchar("priority", { length: 20 })
701
+ .notNull()
702
+ .default("NORMAL")
703
+ .$type<"LOW" | "NORMAL" | "HIGH" | "URGENT" | "EMERGENCY">(),
704
+
705
+ // Original Action References
706
+ originalViolationId: text("original_violation_id").references(
707
+ () => complianceViolations.id,
708
+ ),
709
+
710
+ originalActionId: text("original_action_id").references(
711
+ () => complianceActions.id,
712
+ ),
713
+
714
+ // Appeal Content
715
+ title: varchar("title", { length: 255 }).notNull(),
716
+ reason: text("reason").notNull(),
717
+ description: text("description"),
718
+ requestedOutcome: text("requested_outcome"), // What the appellant wants to happen
719
+
720
+ // Evidence and Supporting Documents
721
+ evidence: jsonb("evidence").$type<{
722
+ documents?: Array<{
723
+ filename: string;
724
+ url: string;
725
+ type: string;
726
+ uploadedAt: string;
727
+ }>;
728
+ screenshots?: string[];
729
+ testimonials?: Array<{
730
+ name: string;
731
+ relationship: string;
732
+ statement: string;
733
+ contactInfo?: string;
734
+ }>;
735
+ externalReferences?: Array<{
736
+ source: string;
737
+ url: string;
738
+ description: string;
739
+ }>;
740
+ additionalData?: Record<string, unknown>;
741
+ }>(),
742
+
743
+ // Status and Processing
744
+ status: varchar("status", { length: 20 })
745
+ .notNull()
746
+ .default("SUBMITTED")
747
+ .$type<
748
+ | "SUBMITTED"
749
+ | "UNDER_REVIEW"
750
+ | "PENDING_DOCUMENTS"
751
+ | "ESCALATED"
752
+ | "APPROVED"
753
+ | "PARTIALLY_APPROVED"
754
+ | "REJECTED"
755
+ | "WITHDRAWN"
756
+ | "EXPIRED"
757
+ >(),
758
+
759
+ // Assignment and Review
760
+ assignedTo: text("assigned_to"),
761
+ assignedAt: timestamp("assigned_at", { withTimezone: true }),
762
+ reviewLevel: varchar("review_level", { length: 20 })
763
+ .$type<
764
+ "FIRST_LEVEL" | "SECOND_LEVEL" | "MANAGEMENT" | "LEGAL" | "EXECUTIVE"
765
+ >()
766
+ .default("FIRST_LEVEL"),
767
+
768
+ // Review Process
769
+ reviewedBy: text("reviewed_by"),
770
+ reviewedAt: timestamp("reviewed_at", { withTimezone: true }),
771
+ reviewStartedAt: timestamp("review_started_at", { withTimezone: true }),
772
+
773
+ // Decision and Outcome
774
+ decision: varchar("decision", { length: 20 }).$type<
775
+ | "APPROVED"
776
+ | "PARTIALLY_APPROVED"
777
+ | "REJECTED"
778
+ | "REQUIRES_MORE_INFO"
779
+ | "ESCALATED"
780
+ >(),
781
+
782
+ reasoning: text("reasoning"),
783
+ actionTaken: text("action_taken"),
784
+ compensationOffered: text("compensation_offered"),
785
+
786
+ // Review Notes and Communication
787
+ reviewNotes: text("review_notes"),
788
+ internalNotes: text("internal_notes"), // Not visible to appellant
789
+ communicationHistory: jsonb("communication_history")
790
+ .$type<
791
+ Array<{
792
+ timestamp: string;
793
+ from: string;
794
+ to: string;
795
+ type: "EMAIL" | "PHONE" | "CHAT" | "INTERNAL_NOTE";
796
+ subject?: string;
797
+ content: string;
798
+ attachments?: string[];
799
+ }>
800
+ >()
801
+ .default([]),
802
+
803
+ // Timeline and SLA
804
+ submittedAt: timestamp("submitted_at", { withTimezone: true })
805
+ .defaultNow()
806
+ .notNull(),
807
+ acknowledgedAt: timestamp("acknowledged_at", { withTimezone: true }),
808
+ targetResolutionDate: timestamp("target_resolution_date", {
809
+ withTimezone: true,
810
+ }),
811
+ actualResolutionDate: timestamp("actual_resolution_date", {
812
+ withTimezone: true,
813
+ }),
814
+
815
+ // Escalation
816
+ escalationLevel: integer("escalation_level").default(0),
817
+ escalatedBy: text("escalated_by"),
818
+ escalatedAt: timestamp("escalated_at", { withTimezone: true }),
819
+ escalationReason: text("escalation_reason"),
820
+
821
+ // Follow-up and Monitoring
822
+ requiresFollowUp: boolean("requires_follow_up").notNull().default(false),
823
+ followUpDate: timestamp("follow_up_date", { withTimezone: true }),
824
+ satisfactionRating: integer("satisfaction_rating"), // 1-5 scale
825
+ appellantFeedback: text("appellant_feedback"),
826
+
827
+ createdAt: timestamp("created_at", { withTimezone: true })
828
+ .defaultNow()
829
+ .notNull(),
830
+ updatedAt: timestamp("updated_at", { withTimezone: true })
831
+ .defaultNow()
832
+ .notNull(),
833
+ },
834
+ (table) => ({
835
+ storeIdIndex: index("idx_appeals_store_id").on(table.storeId),
836
+ statusIndex: index("idx_appeals_status").on(table.status),
837
+ appealTypeIndex: index("idx_appeals_type").on(table.appealType),
838
+
839
+ // Assignment and workflow indexes
840
+ assignmentIndex: index("idx_appeals_assignment").on(
841
+ table.assignedTo,
842
+ table.status,
843
+ ),
844
+
845
+ reviewLevelIndex: index("idx_appeals_review_level").on(
846
+ table.reviewLevel,
847
+ table.status,
848
+ ),
849
+
850
+ // SLA and timeline tracking indexes
851
+ slaTrackingIndex: index("idx_appeals_sla_tracking").on(
852
+ table.targetResolutionDate,
853
+ table.status,
854
+ ),
855
+
856
+ // Escalation tracking indexes
857
+ escalationIndex: index("idx_appeals_escalation").on(
858
+ table.escalationLevel,
859
+ table.escalatedAt,
860
+ ),
861
+
862
+ // Performance analytics indexes
863
+ decisionTimeIndex: index("idx_appeals_decision_time").on(
864
+ table.submittedAt,
865
+ table.actualResolutionDate,
866
+ ),
867
+ }),
868
+ );
869
+
870
+ // Relations
871
+ export const storeComplianceRelations = relations(
872
+ storeCompliance,
873
+ ({ many }) => ({
874
+ violations: many(complianceViolations),
875
+ actions: many(complianceActions),
876
+ policies: many(storePolicies),
877
+ appeals: many(appeals),
878
+ }),
879
+ );
880
+
881
+ export const complianceViolationsRelations = relations(
882
+ complianceViolations,
883
+ ({ one, many }) => ({
884
+ compliance: one(storeCompliance, {
885
+ fields: [complianceViolations.storeId],
886
+ references: [storeCompliance.storeId],
887
+ }),
888
+ actions: many(complianceActions),
889
+ appeals: many(appeals),
890
+ }),
891
+ );
892
+
893
+ export const complianceActionsRelations = relations(
894
+ complianceActions,
895
+ ({ one }) => ({
896
+ compliance: one(storeCompliance, {
897
+ fields: [complianceActions.storeId],
898
+ references: [storeCompliance.storeId],
899
+ }),
900
+ violation: one(complianceViolations, {
901
+ fields: [complianceActions.violationId],
902
+ references: [complianceViolations.id],
903
+ }),
904
+ }),
905
+ );
906
+
907
+ export const storePoliciesRelations = relations(storePolicies, ({ one }) => ({
908
+ compliance: one(storeCompliance, {
909
+ fields: [storePolicies.storeId],
910
+ references: [storeCompliance.storeId],
911
+ }),
912
+ }));
913
+
914
+ export const appealsRelations = relations(appeals, ({ one }) => ({
915
+ compliance: one(storeCompliance, {
916
+ fields: [appeals.storeId],
917
+ references: [storeCompliance.storeId],
918
+ }),
919
+ violation: one(complianceViolations, {
920
+ fields: [appeals.originalViolationId],
921
+ references: [complianceViolations.id],
922
+ }),
923
+ action: one(complianceActions, {
924
+ fields: [appeals.originalActionId],
925
+ references: [complianceActions.id],
926
+ }),
927
+ }));