ojamoto_models 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 (65) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/LICENSE +21 -0
  3. package/certificates/global-bundle.pem +2736 -0
  4. package/config/config.js +52 -0
  5. package/index.js +3 -0
  6. package/migrations/2026000001-create-users.js +97 -0
  7. package/migrations/2026000002-create-and-seed-roles.js +43 -0
  8. package/migrations/2026000003-create-admin-users.js +83 -0
  9. package/migrations/2026000004-create-devices.js +67 -0
  10. package/migrations/2026000005-create-products.js +87 -0
  11. package/migrations/2026000006-create-addresses.js +74 -0
  12. package/migrations/2026000007-create-wallets.js +44 -0
  13. package/migrations/2026000008-create-kyc-verification.js +51 -0
  14. package/migrations/2026000009-create-seller-profiles.js +80 -0
  15. package/migrations/2026000010-create-user-roles.js +48 -0
  16. package/migrations/2026000011-create-sessions.js +61 -0
  17. package/migrations/2026000012-create-escrows.js +80 -0
  18. package/migrations/2026000013-create-wallet-transactions.js +70 -0
  19. package/migrations/2026000014-create-product-images.js +43 -0
  20. package/migrations/2026000015-create-escrow-events.js +50 -0
  21. package/migrations/2026000016-create-logistics.js +55 -0
  22. package/migrations/2026000017-create-ratings.js +56 -0
  23. package/migrations/2026000018-create-disputes.js +84 -0
  24. package/migrations/2026000019-create-dispute-evidence.js +50 -0
  25. package/migrations/2026000020-create-dispute-messages.js +44 -0
  26. package/migrations/2026000021-create-notifications.js +50 -0
  27. package/migrations/2026000022-create-audit-logs.js +57 -0
  28. package/migrations/2026000023-create-webhook-logs.js +46 -0
  29. package/migrations/2026000024-create-user-otps.js +75 -0
  30. package/migrations/2026000025-create-password-reset-otps.js +43 -0
  31. package/migrations/2026000026-create-seller-badges.js +43 -0
  32. package/migrations/2026000027-add-fulltext-index-for-search.js +15 -0
  33. package/migrations/2026000028-create-admin-notifications.js +53 -0
  34. package/migrations/2026000029-create-pin-reset-otp.js +30 -0
  35. package/models/Address.js +76 -0
  36. package/models/AdminNotification.js +59 -0
  37. package/models/AdminUser.js +115 -0
  38. package/models/AuditLog.js +70 -0
  39. package/models/Device.js +82 -0
  40. package/models/Dispute.js +109 -0
  41. package/models/DisputeEvidence.js +62 -0
  42. package/models/DisputeMessage.js +57 -0
  43. package/models/Escrow.js +110 -0
  44. package/models/EscrowEvent.js +63 -0
  45. package/models/KycVerification.js +62 -0
  46. package/models/Logistics.js +62 -0
  47. package/models/Notification.js +58 -0
  48. package/models/PasswordResetOTP.js +52 -0
  49. package/models/PinResetOTP.js +48 -0
  50. package/models/Product.js +100 -0
  51. package/models/ProductImage.js +54 -0
  52. package/models/Rating.js +74 -0
  53. package/models/Role.js +43 -0
  54. package/models/SellerBadges.js +45 -0
  55. package/models/SellerProfile.js +92 -0
  56. package/models/Session.js +67 -0
  57. package/models/User.js +198 -0
  58. package/models/UserOTP.js +84 -0
  59. package/models/UserRole.js +42 -0
  60. package/models/Wallet.js +63 -0
  61. package/models/WalletTransaction.js +81 -0
  62. package/models/WebhookLog.js +52 -0
  63. package/models/index.js +23 -0
  64. package/package.json +30 -0
  65. package/services/constants.js +162 -0
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("audit_logs", {
6
+ audit_log_id: {
7
+ type: Sequelize.INTEGER,
8
+ primaryKey: true,
9
+ autoIncrement: true,
10
+ allowNull: false,
11
+ },
12
+ // One of user_id or admin_id will be populated — never both
13
+ user_id: {
14
+ type: Sequelize.INTEGER,
15
+ allowNull: true,
16
+ references: { model: "users", key: "user_id" },
17
+ onDelete: "SET NULL",
18
+ onUpdate: "CASCADE",
19
+ comment: "Populated when the actor is a regular user",
20
+ },
21
+ admin_id: {
22
+ type: Sequelize.INTEGER,
23
+ allowNull: true,
24
+ references: { model: "admin_users", key: "admin_id" },
25
+ onDelete: "SET NULL",
26
+ onUpdate: "CASCADE",
27
+ comment: "Populated when the actor is an admin",
28
+ },
29
+ action: {
30
+ type: Sequelize.STRING(255),
31
+ allowNull: false,
32
+ comment: "e.g. ESCROW_RELEASED | USER_BANNED | KYC_APPROVED",
33
+ },
34
+ metadata_json: {
35
+ type: Sequelize.JSON,
36
+ allowNull: true,
37
+ comment: "Contextual data — before/after values, entity IDs etc.",
38
+ },
39
+ ip_address: {
40
+ type: Sequelize.STRING(45),
41
+ allowNull: true,
42
+ },
43
+ created_at: {
44
+ type: Sequelize.DATE,
45
+ allowNull: false,
46
+ },
47
+ });
48
+
49
+ await queryInterface.addIndex("audit_logs", ["user_id"]);
50
+ await queryInterface.addIndex("audit_logs", ["admin_id"]);
51
+ await queryInterface.addIndex("audit_logs", ["action"]);
52
+ },
53
+
54
+ async down(queryInterface) {
55
+ await queryInterface.dropTable("audit_logs");
56
+ },
57
+ };
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+
3
+ const { WEBHOOK_STATUS } = require("../services/constants");
4
+
5
+ module.exports = {
6
+ async up(queryInterface, Sequelize) {
7
+ await queryInterface.createTable("webhook_logs", {
8
+ webhook_log_id: {
9
+ type: Sequelize.INTEGER,
10
+ primaryKey: true,
11
+ autoIncrement: true,
12
+ allowNull: false,
13
+ },
14
+ source: {
15
+ type: Sequelize.STRING(100),
16
+ allowNull: false,
17
+ comment: "flutterwave | twilio | onesignal",
18
+ },
19
+ event_type: {
20
+ type: Sequelize.STRING(100),
21
+ allowNull: true,
22
+ comment: "e.g. charge.completed | delivery.update",
23
+ },
24
+ payload_json: {
25
+ type: Sequelize.JSON,
26
+ allowNull: false,
27
+ comment: "Raw webhook body",
28
+ },
29
+ status: {
30
+ type: Sequelize.ENUM(...Object.values(WEBHOOK_STATUS)),
31
+ allowNull: false,
32
+ },
33
+ created_at: {
34
+ type: Sequelize.DATE,
35
+ allowNull: false,
36
+ },
37
+ });
38
+
39
+ await queryInterface.addIndex("webhook_logs", ["source"]);
40
+ await queryInterface.addIndex("webhook_logs", ["status"]);
41
+ },
42
+
43
+ async down(queryInterface) {
44
+ await queryInterface.dropTable("webhook_logs");
45
+ },
46
+ };
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("user_otps", {
6
+ user_otp_id: {
7
+ type: Sequelize.INTEGER,
8
+ primaryKey: true,
9
+ autoIncrement: true,
10
+ allowNull: false,
11
+ },
12
+ email: {
13
+ type: Sequelize.STRING,
14
+ allowNull: false,
15
+ },
16
+ first_name: {
17
+ type: Sequelize.STRING,
18
+ allowNull: false,
19
+ },
20
+ last_name: {
21
+ type: Sequelize.STRING,
22
+ allowNull: false,
23
+ },
24
+ middle_name: {
25
+ type: Sequelize.STRING,
26
+ allowNull: true,
27
+ },
28
+ phone: {
29
+ type: Sequelize.STRING(10),
30
+ allowNull: false,
31
+ },
32
+ country_calling_code: {
33
+ type: Sequelize.STRING(3),
34
+ allowNull: false,
35
+ },
36
+ password_hash: {
37
+ type: Sequelize.STRING,
38
+ allowNull: false,
39
+ },
40
+ user_type: {
41
+ type: Sequelize.STRING,
42
+ allowNull: false,
43
+ },
44
+ otp: {
45
+ type: Sequelize.STRING,
46
+ allowNull: false,
47
+ },
48
+ purpose: {
49
+ type: Sequelize.STRING,
50
+ allowNull: false,
51
+ },
52
+ expires_at: {
53
+ type: Sequelize.DATE,
54
+ allowNull: false,
55
+ },
56
+ created_at: {
57
+ type: Sequelize.DATE,
58
+ allowNull: false,
59
+ },
60
+ updated_at: {
61
+ type: Sequelize.DATE,
62
+ allowNull: false,
63
+ },
64
+ });
65
+
66
+ await queryInterface.addIndex("user_otps", ["email", "purpose"], {
67
+ unique: true,
68
+ name: "user_otps_email_purpose_unique",
69
+ });
70
+ },
71
+
72
+ async down(queryInterface) {
73
+ await queryInterface.dropTable("user_otps");
74
+ },
75
+ };
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("password_reset_otps", {
6
+ password_reset_otp_id: {
7
+ type: Sequelize.INTEGER,
8
+ primaryKey: true,
9
+ autoIncrement: true,
10
+ allowNull: false,
11
+ },
12
+ email: {
13
+ type: Sequelize.STRING,
14
+ allowNull: false,
15
+ },
16
+ otp: {
17
+ type: Sequelize.STRING,
18
+ allowNull: false,
19
+ },
20
+ expires_at: {
21
+ type: Sequelize.DATE,
22
+ allowNull: false,
23
+ },
24
+ created_at: {
25
+ type: Sequelize.DATE,
26
+ allowNull: false,
27
+ },
28
+ updated_at: {
29
+ type: Sequelize.DATE,
30
+ allowNull: false,
31
+ },
32
+ });
33
+
34
+ await queryInterface.addIndex("password_reset_otps", ["email"], {
35
+ unique: true,
36
+ name: "password_reset_otps_email_unique",
37
+ });
38
+ },
39
+
40
+ async down(queryInterface) {
41
+ await queryInterface.dropTable("password_reset_otps");
42
+ },
43
+ };
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+
3
+ const { SELLER_BADGE } = require("../services/constants");
4
+
5
+ module.exports = {
6
+ async up(queryInterface, Sequelize) {
7
+ await queryInterface.createTable("seller_badges", {
8
+ seller_badge_id: {
9
+ type: Sequelize.INTEGER,
10
+ primaryKey: true,
11
+ autoIncrement: true,
12
+ allowNull: false,
13
+ },
14
+ seller_profile_id: {
15
+ type: Sequelize.INTEGER,
16
+ allowNull: false,
17
+ references: { model: "seller_profiles", key: "seller_profile_id" },
18
+ onDelete: "CASCADE",
19
+ onUpdate: "CASCADE",
20
+ },
21
+ badge: {
22
+ type: Sequelize.ENUM(...Object.values(SELLER_BADGE)),
23
+ allowNull: false,
24
+ },
25
+ awarded_at: {
26
+ type: Sequelize.DATE,
27
+ allowNull: false,
28
+ },
29
+ });
30
+
31
+ await queryInterface.addIndex("seller_badges", ["seller_profile_id"]);
32
+ // One seller can only hold each badge once
33
+ await queryInterface.addIndex(
34
+ "seller_badges",
35
+ ["seller_profile_id", "badge"],
36
+ { unique: true, name: "seller_badges_profile_badge_unique" },
37
+ );
38
+ },
39
+
40
+ async down(queryInterface) {
41
+ await queryInterface.dropTable("seller_badges");
42
+ },
43
+ };
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface) {
5
+ await queryInterface.sequelize.query(
6
+ `ALTER TABLE products ADD FULLTEXT INDEX products_search_fulltext (name, description, brand, model, category)`,
7
+ );
8
+ },
9
+
10
+ async down(queryInterface) {
11
+ await queryInterface.sequelize.query(
12
+ `ALTER TABLE products DROP INDEX products_search_fulltext`,
13
+ );
14
+ },
15
+ };
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("admin_notifications", {
6
+ admin_notification_id: {
7
+ type: Sequelize.INTEGER,
8
+ primaryKey: true,
9
+ autoIncrement: true,
10
+ allowNull: false,
11
+ },
12
+ admin_id: {
13
+ type: Sequelize.INTEGER,
14
+ allowNull: false,
15
+ references: { model: "admin_users", key: "admin_id" },
16
+ onDelete: "CASCADE",
17
+ onUpdate: "CASCADE",
18
+ },
19
+ type: {
20
+ type: Sequelize.STRING(100),
21
+ allowNull: false,
22
+ comment: "DISPUTE_OPENED | KYC_SUBMITTED | WITHDRAWAL_FLAGGED etc.",
23
+ },
24
+ title: {
25
+ type: Sequelize.STRING(255),
26
+ allowNull: false,
27
+ },
28
+ message: {
29
+ type: Sequelize.TEXT,
30
+ allowNull: false,
31
+ },
32
+ read_status: {
33
+ type: Sequelize.BOOLEAN,
34
+ allowNull: false,
35
+ defaultValue: false,
36
+ },
37
+ created_at: {
38
+ type: Sequelize.DATE,
39
+ allowNull: false,
40
+ },
41
+ });
42
+
43
+ await queryInterface.addIndex("admin_notifications", ["admin_id"]);
44
+ await queryInterface.addIndex("admin_notifications", [
45
+ "admin_id",
46
+ "read_status",
47
+ ]);
48
+ },
49
+
50
+ async down(queryInterface) {
51
+ await queryInterface.dropTable("admin_notifications");
52
+ },
53
+ };
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("pin_reset_otps", {
6
+ pin_reset_otp_id: {
7
+ type: Sequelize.INTEGER,
8
+ primaryKey: true,
9
+ autoIncrement: true,
10
+ allowNull: false,
11
+ },
12
+ user_id: {
13
+ type: Sequelize.INTEGER,
14
+ allowNull: false,
15
+ unique: true,
16
+ references: { model: "users", key: "user_id" },
17
+ onDelete: "CASCADE",
18
+ onUpdate: "CASCADE",
19
+ },
20
+ otp: { type: Sequelize.STRING, allowNull: false },
21
+ expires_at: { type: Sequelize.DATE, allowNull: false },
22
+ created_at: { type: Sequelize.DATE, allowNull: false },
23
+ updated_at: { type: Sequelize.DATE, allowNull: false },
24
+ });
25
+ },
26
+
27
+ async down(queryInterface) {
28
+ await queryInterface.dropTable("pin_reset_otps");
29
+ },
30
+ };
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+
3
+ const { Model, DataTypes } = require("sequelize");
4
+ const { ADDRESS_LABEL } = require("../services/constants");
5
+
6
+ module.exports = (sequelize) => {
7
+ class Address extends Model {
8
+ static associate(models) {
9
+ Address.belongsTo(models.User, {
10
+ foreignKey: "user_id",
11
+ as: "user",
12
+ });
13
+ }
14
+ }
15
+
16
+ Address.init(
17
+ {
18
+ address_id: {
19
+ type: DataTypes.INTEGER,
20
+ primaryKey: true,
21
+ autoIncrement: true,
22
+ },
23
+ user_id: {
24
+ type: DataTypes.INTEGER,
25
+ allowNull: false,
26
+ references: { model: "users", key: "user_id" },
27
+ onDelete: "CASCADE",
28
+ },
29
+ label: {
30
+ type: DataTypes.ENUM(...Object.values(ADDRESS_LABEL)),
31
+ allowNull: false,
32
+ },
33
+ address_line1: {
34
+ type: DataTypes.STRING(255),
35
+ allowNull: false,
36
+ },
37
+ address_line2: {
38
+ type: DataTypes.STRING(255),
39
+ allowNull: true,
40
+ },
41
+ city: {
42
+ type: DataTypes.STRING(100),
43
+ allowNull: false,
44
+ },
45
+ state: {
46
+ type: DataTypes.STRING(100),
47
+ allowNull: false,
48
+ },
49
+ country: {
50
+ type: DataTypes.STRING(100),
51
+ allowNull: false,
52
+ },
53
+ postal_code: {
54
+ type: DataTypes.STRING(20),
55
+ allowNull: true,
56
+ },
57
+ is_default: {
58
+ type: DataTypes.BOOLEAN,
59
+ defaultValue: false,
60
+ },
61
+ },
62
+ {
63
+ sequelize,
64
+ modelName: "Address",
65
+ tableName: "addresses",
66
+ timestamps: true,
67
+ paranoid: true,
68
+ createdAt: "created_at",
69
+ updatedAt: "updated_at",
70
+ deletedAt: "deleted_at",
71
+ },
72
+ );
73
+
74
+ Address.removeAttribute("id");
75
+ return Address;
76
+ };
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+
3
+ const { Model, DataTypes } = require("sequelize");
4
+
5
+ module.exports = (sequelize) => {
6
+ class AdminNotification extends Model {
7
+ static associate(models) {
8
+ AdminNotification.belongsTo(models.AdminUser, {
9
+ foreignKey: "admin_id",
10
+ as: "admin",
11
+ });
12
+ }
13
+ }
14
+
15
+ AdminNotification.init(
16
+ {
17
+ admin_notification_id: {
18
+ type: DataTypes.INTEGER,
19
+ primaryKey: true,
20
+ autoIncrement: true,
21
+ },
22
+ admin_id: {
23
+ type: DataTypes.INTEGER,
24
+ allowNull: false,
25
+ references: { model: "admin_users", key: "admin_id" },
26
+ onDelete: "CASCADE",
27
+ },
28
+ type: {
29
+ type: DataTypes.STRING(100),
30
+ allowNull: false,
31
+ comment: "DISPUTE_OPENED | KYC_SUBMITTED | WITHDRAWAL_FLAGGED etc.",
32
+ },
33
+ title: {
34
+ type: DataTypes.STRING(255),
35
+ allowNull: false,
36
+ },
37
+ message: {
38
+ type: DataTypes.TEXT,
39
+ allowNull: false,
40
+ },
41
+ read_status: {
42
+ type: DataTypes.BOOLEAN,
43
+ allowNull: false,
44
+ defaultValue: false,
45
+ },
46
+ },
47
+ {
48
+ sequelize,
49
+ modelName: "AdminNotification",
50
+ tableName: "admin_notifications",
51
+ timestamps: true,
52
+ createdAt: "created_at",
53
+ updatedAt: false,
54
+ },
55
+ );
56
+
57
+ AdminNotification.removeAttribute("id");
58
+ return AdminNotification;
59
+ };
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+
3
+ const { Model, DataTypes } = require("sequelize");
4
+ const { ADMIN_ROLE, ADMIN_STATUS } = require("../services/constants");
5
+
6
+ module.exports = (sequelize) => {
7
+ class AdminUser extends Model {
8
+ static associate(models) {
9
+ // Self-referential: who created this admin account
10
+ AdminUser.belongsTo(models.AdminUser, {
11
+ foreignKey: "created_by",
12
+ as: "createdByAdmin",
13
+ });
14
+
15
+ AdminUser.hasMany(models.AdminUser, {
16
+ foreignKey: "created_by",
17
+ as: "createdAdmins",
18
+ });
19
+
20
+ // Disputes assigned to this admin
21
+ AdminUser.hasMany(models.Dispute, {
22
+ foreignKey: "assigned_admin",
23
+ as: "assignedDisputes",
24
+ });
25
+
26
+ // Audit logs performed by this admin
27
+ AdminUser.hasMany(models.AuditLog, {
28
+ foreignKey: "admin_id",
29
+ as: "auditLogs",
30
+ });
31
+
32
+ // Notifications
33
+ AdminUser.hasMany(models.AdminNotification, {
34
+ foreignKey: "admin_id",
35
+ as: "notifications",
36
+ });
37
+ }
38
+ }
39
+
40
+ AdminUser.init(
41
+ {
42
+ admin_id: {
43
+ type: DataTypes.INTEGER,
44
+ primaryKey: true,
45
+ autoIncrement: true,
46
+ },
47
+ uuid: {
48
+ type: DataTypes.UUID,
49
+ defaultValue: DataTypes.UUIDV4,
50
+ allowNull: false,
51
+ unique: true,
52
+ },
53
+ first_name: {
54
+ type: DataTypes.STRING(100),
55
+ allowNull: false,
56
+ },
57
+ last_name: {
58
+ type: DataTypes.STRING(100),
59
+ allowNull: false,
60
+ },
61
+ email: {
62
+ type: DataTypes.STRING(255),
63
+ allowNull: false,
64
+ unique: true,
65
+ validate: { isEmail: true },
66
+ },
67
+ phone: {
68
+ type: DataTypes.STRING(20),
69
+ allowNull: false,
70
+ unique: true,
71
+ },
72
+ password_hash: {
73
+ type: DataTypes.STRING(255),
74
+ allowNull: false,
75
+ },
76
+ role: {
77
+ type: DataTypes.ENUM(...Object.values(ADMIN_ROLE)),
78
+ allowNull: false,
79
+ },
80
+ status: {
81
+ type: DataTypes.ENUM(...Object.values(ADMIN_STATUS)),
82
+ allowNull: false,
83
+ defaultValue: ADMIN_STATUS.ACTIVE,
84
+ },
85
+ two_factor_enabled: {
86
+ type: DataTypes.BOOLEAN,
87
+ allowNull: false,
88
+ defaultValue: true,
89
+ comment: "Mandatory for all admin accounts",
90
+ },
91
+ last_login_at: {
92
+ type: DataTypes.DATE,
93
+ allowNull: true,
94
+ },
95
+ created_by: {
96
+ type: DataTypes.INTEGER,
97
+ allowNull: true,
98
+ references: { model: "admin_users", key: "admin_id" },
99
+ onDelete: "SET NULL",
100
+ comment: "Which super admin created this account",
101
+ },
102
+ },
103
+ {
104
+ sequelize,
105
+ modelName: "AdminUser",
106
+ tableName: "admin_users",
107
+ timestamps: true,
108
+ createdAt: "created_at",
109
+ updatedAt: "updated_at",
110
+ },
111
+ );
112
+
113
+ AdminUser.removeAttribute("id");
114
+ return AdminUser;
115
+ };
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+
3
+ const { Model, DataTypes } = require("sequelize");
4
+
5
+ module.exports = (sequelize) => {
6
+ class AuditLog extends Model {
7
+ static associate(models) {
8
+ // Actor can be a regular user
9
+ AuditLog.belongsTo(models.User, {
10
+ foreignKey: "user_id",
11
+ as: "user",
12
+ });
13
+
14
+ // Or an admin user — use admin_id to distinguish
15
+ AuditLog.belongsTo(models.AdminUser, {
16
+ foreignKey: "admin_id",
17
+ as: "admin",
18
+ });
19
+ }
20
+ }
21
+
22
+ AuditLog.init(
23
+ {
24
+ audit_log_id: {
25
+ type: DataTypes.INTEGER,
26
+ primaryKey: true,
27
+ autoIncrement: true,
28
+ },
29
+ user_id: {
30
+ type: DataTypes.INTEGER,
31
+ allowNull: true,
32
+ references: { model: "users", key: "user_id" },
33
+ onDelete: "SET NULL",
34
+ comment: "Populated when the actor is a regular user",
35
+ },
36
+ admin_id: {
37
+ type: DataTypes.INTEGER,
38
+ allowNull: true,
39
+ references: { model: "admin_users", key: "admin_id" },
40
+ onDelete: "SET NULL",
41
+ comment: "Populated when the actor is an admin",
42
+ },
43
+ action: {
44
+ type: DataTypes.STRING(255),
45
+ allowNull: false,
46
+ comment: "e.g. ESCROW_RELEASED | USER_BANNED | KYC_APPROVED",
47
+ },
48
+ metadata_json: {
49
+ type: DataTypes.JSON,
50
+ allowNull: true,
51
+ comment: "Contextual data — before/after values, entity IDs etc.",
52
+ },
53
+ ip_address: {
54
+ type: DataTypes.STRING(45),
55
+ allowNull: true,
56
+ },
57
+ },
58
+ {
59
+ sequelize,
60
+ modelName: "AuditLog",
61
+ tableName: "audit_logs",
62
+ timestamps: true,
63
+ createdAt: "created_at",
64
+ updatedAt: false,
65
+ },
66
+ );
67
+
68
+ AuditLog.removeAttribute("id");
69
+ return AuditLog;
70
+ };