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,48 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("user_roles", {
6
+ user_role_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
+ references: { model: "users", key: "user_id" },
16
+ onDelete: "CASCADE",
17
+ onUpdate: "CASCADE",
18
+ },
19
+ role_id: {
20
+ type: Sequelize.INTEGER,
21
+ allowNull: false,
22
+ references: { model: "roles", key: "role_id" },
23
+ onDelete: "CASCADE",
24
+ onUpdate: "CASCADE",
25
+ },
26
+ created_at: {
27
+ type: Sequelize.DATE,
28
+ allowNull: false,
29
+ },
30
+ updated_at: {
31
+ type: Sequelize.DATE,
32
+ allowNull: false,
33
+ },
34
+ });
35
+
36
+ await queryInterface.addIndex("user_roles", ["user_id"]);
37
+ await queryInterface.addIndex("user_roles", ["role_id"]);
38
+ // Prevents assigning the same role to the same user twice
39
+ await queryInterface.addIndex("user_roles", ["user_id", "role_id"], {
40
+ unique: true,
41
+ name: "user_roles_user_id_role_id_unique",
42
+ });
43
+ },
44
+
45
+ async down(queryInterface) {
46
+ await queryInterface.dropTable("user_roles");
47
+ },
48
+ };
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("sessions", {
6
+ session_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
+ references: { model: "users", key: "user_id" },
16
+ onDelete: "CASCADE",
17
+ onUpdate: "CASCADE",
18
+ },
19
+ device_id: {
20
+ type: Sequelize.INTEGER,
21
+ allowNull: true,
22
+ references: { model: "devices", key: "device_id" },
23
+ onDelete: "SET NULL",
24
+ onUpdate: "CASCADE",
25
+ },
26
+ refresh_token: {
27
+ type: Sequelize.TEXT,
28
+ allowNull: false,
29
+ comment: "Hashed before storage",
30
+ },
31
+ expires_at: {
32
+ type: Sequelize.DATE,
33
+ allowNull: false,
34
+ comment: "7 days from issuance",
35
+ },
36
+ revoked: {
37
+ type: Sequelize.BOOLEAN,
38
+ allowNull: false,
39
+ defaultValue: false,
40
+ comment: "Set true on logout",
41
+ },
42
+ created_at: {
43
+ type: Sequelize.DATE,
44
+ allowNull: false,
45
+ },
46
+ });
47
+
48
+ await queryInterface.addIndex("sessions", ["user_id"]);
49
+ await queryInterface.addIndex("sessions", ["device_id"]);
50
+ // Speeds up the lookup in refreshToken/logout controllers
51
+ await queryInterface.addIndex("sessions", [
52
+ "user_id",
53
+ "device_id",
54
+ "revoked",
55
+ ]);
56
+ },
57
+
58
+ async down(queryInterface) {
59
+ await queryInterface.dropTable("sessions");
60
+ },
61
+ };
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+
3
+ const { ESCROW_STATUS } = require("../services/constants");
4
+
5
+ module.exports = {
6
+ async up(queryInterface, Sequelize) {
7
+ await queryInterface.createTable("escrows", {
8
+ escrow_id: {
9
+ type: Sequelize.INTEGER,
10
+ primaryKey: true,
11
+ autoIncrement: true,
12
+ allowNull: false,
13
+ },
14
+ buyer_id: {
15
+ type: Sequelize.INTEGER,
16
+ allowNull: false,
17
+ references: { model: "users", key: "user_id" },
18
+ onDelete: "RESTRICT",
19
+ onUpdate: "CASCADE",
20
+ },
21
+ seller_id: {
22
+ type: Sequelize.INTEGER,
23
+ allowNull: false,
24
+ references: { model: "users", key: "user_id" },
25
+ onDelete: "RESTRICT",
26
+ onUpdate: "CASCADE",
27
+ },
28
+ product_id: {
29
+ type: Sequelize.INTEGER,
30
+ allowNull: false,
31
+ references: { model: "products", key: "product_id" },
32
+ onDelete: "RESTRICT",
33
+ onUpdate: "CASCADE",
34
+ },
35
+ amount: {
36
+ type: Sequelize.DECIMAL(15, 2),
37
+ allowNull: false,
38
+ comment: "Agreed transaction value",
39
+ },
40
+ platform_fee: {
41
+ type: Sequelize.DECIMAL(15, 2),
42
+ allowNull: false,
43
+ defaultValue: 0.0,
44
+ comment: "AutoParts NG escrow fee",
45
+ },
46
+ currency: {
47
+ type: Sequelize.STRING(3),
48
+ allowNull: false,
49
+ defaultValue: "NGN",
50
+ },
51
+ status: {
52
+ type: Sequelize.ENUM(...Object.values(ESCROW_STATUS)),
53
+ allowNull: false,
54
+ defaultValue: ESCROW_STATUS.PENDING,
55
+ },
56
+ delivery_deadline: {
57
+ type: Sequelize.DATEONLY,
58
+ allowNull: true,
59
+ comment: "Auto-release trigger date if buyer does not confirm",
60
+ },
61
+ created_at: {
62
+ type: Sequelize.DATE,
63
+ allowNull: false,
64
+ },
65
+ updated_at: {
66
+ type: Sequelize.DATE,
67
+ allowNull: false,
68
+ },
69
+ });
70
+
71
+ await queryInterface.addIndex("escrows", ["buyer_id"]);
72
+ await queryInterface.addIndex("escrows", ["seller_id"]);
73
+ await queryInterface.addIndex("escrows", ["product_id"]);
74
+ await queryInterface.addIndex("escrows", ["status"]);
75
+ },
76
+
77
+ async down(queryInterface) {
78
+ await queryInterface.dropTable("escrows");
79
+ },
80
+ };
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+
3
+ const {
4
+ WALLET_TRANSACTION_TYPE,
5
+ WALLET_TRANSACTION_STATUS,
6
+ } = require("../services/constants");
7
+
8
+ module.exports = {
9
+ async up(queryInterface, Sequelize) {
10
+ await queryInterface.createTable("wallet_transactions", {
11
+ wallet_transaction_id: {
12
+ type: Sequelize.INTEGER,
13
+ primaryKey: true,
14
+ autoIncrement: true,
15
+ allowNull: false,
16
+ },
17
+ wallet_id: {
18
+ type: Sequelize.INTEGER,
19
+ allowNull: false,
20
+ references: { model: "wallets", key: "wallet_id" },
21
+ onDelete: "CASCADE",
22
+ onUpdate: "CASCADE",
23
+ },
24
+ user_id: {
25
+ type: Sequelize.INTEGER,
26
+ allowNull: false,
27
+ references: { model: "users", key: "user_id" },
28
+ onDelete: "CASCADE",
29
+ onUpdate: "CASCADE",
30
+ comment: "Denormalized for quick queries",
31
+ },
32
+ type: {
33
+ type: Sequelize.ENUM(...Object.values(WALLET_TRANSACTION_TYPE)),
34
+ allowNull: false,
35
+ },
36
+ amount: {
37
+ type: Sequelize.DECIMAL(15, 2),
38
+ allowNull: false,
39
+ },
40
+ currency: {
41
+ type: Sequelize.STRING(3),
42
+ allowNull: false,
43
+ defaultValue: "NGN",
44
+ },
45
+ reference: {
46
+ type: Sequelize.STRING(100),
47
+ allowNull: false,
48
+ unique: true,
49
+ comment: "Flutterwave ref or internal reference",
50
+ },
51
+ status: {
52
+ type: Sequelize.ENUM(...Object.values(WALLET_TRANSACTION_STATUS)),
53
+ allowNull: false,
54
+ defaultValue: WALLET_TRANSACTION_STATUS.PENDING,
55
+ },
56
+ created_at: {
57
+ type: Sequelize.DATE,
58
+ allowNull: false,
59
+ },
60
+ });
61
+
62
+ await queryInterface.addIndex("wallet_transactions", ["wallet_id"]);
63
+ await queryInterface.addIndex("wallet_transactions", ["user_id"]);
64
+ await queryInterface.addIndex("wallet_transactions", ["status"]);
65
+ },
66
+
67
+ async down(queryInterface) {
68
+ await queryInterface.dropTable("wallet_transactions");
69
+ },
70
+ };
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("product_images", {
6
+ product_image_id: {
7
+ type: Sequelize.INTEGER,
8
+ primaryKey: true,
9
+ autoIncrement: true,
10
+ allowNull: false,
11
+ },
12
+ product_id: {
13
+ type: Sequelize.INTEGER,
14
+ allowNull: false,
15
+ references: { model: "products", key: "product_id" },
16
+ onDelete: "CASCADE",
17
+ onUpdate: "CASCADE",
18
+ },
19
+ image_url: {
20
+ type: Sequelize.STRING(500),
21
+ allowNull: false,
22
+ comment: "AWS S3 URL",
23
+ },
24
+ position: {
25
+ type: Sequelize.INTEGER,
26
+ allowNull: false,
27
+ defaultValue: 0,
28
+ comment: "Display order",
29
+ },
30
+ ai_metadata: {
31
+ type: Sequelize.JSON,
32
+ allowNull: true,
33
+ comment: "Brand, model, condition extracted by AI analysis",
34
+ },
35
+ });
36
+
37
+ await queryInterface.addIndex("product_images", ["product_id"]);
38
+ },
39
+
40
+ async down(queryInterface) {
41
+ await queryInterface.dropTable("product_images");
42
+ },
43
+ };
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("escrow_events", {
6
+ escrow_event_id: {
7
+ type: Sequelize.INTEGER,
8
+ primaryKey: true,
9
+ autoIncrement: true,
10
+ allowNull: false,
11
+ },
12
+ escrow_id: {
13
+ type: Sequelize.INTEGER,
14
+ allowNull: false,
15
+ references: { model: "escrows", key: "escrow_id" },
16
+ onDelete: "CASCADE",
17
+ onUpdate: "CASCADE",
18
+ },
19
+ event_type: {
20
+ type: Sequelize.STRING(100),
21
+ allowNull: false,
22
+ comment: "FUNDED | ACCEPTED | DELIVERED | RELEASED etc.",
23
+ },
24
+ description: {
25
+ type: Sequelize.TEXT,
26
+ allowNull: true,
27
+ comment: "Human-readable event detail",
28
+ },
29
+ created_by: {
30
+ type: Sequelize.INTEGER,
31
+ allowNull: true,
32
+ references: { model: "users", key: "user_id" },
33
+ onDelete: "SET NULL",
34
+ onUpdate: "CASCADE",
35
+ comment: "User or system actor (null = system)",
36
+ },
37
+ created_at: {
38
+ type: Sequelize.DATE,
39
+ allowNull: false,
40
+ },
41
+ });
42
+
43
+ await queryInterface.addIndex("escrow_events", ["escrow_id"]);
44
+ await queryInterface.addIndex("escrow_events", ["created_by"]);
45
+ },
46
+
47
+ async down(queryInterface) {
48
+ await queryInterface.dropTable("escrow_events");
49
+ },
50
+ };
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+
3
+ const { LOGISTICS_STATUS } = require("../services/constants");
4
+
5
+ module.exports = {
6
+ async up(queryInterface, Sequelize) {
7
+ await queryInterface.createTable("logistics", {
8
+ logistics_id: {
9
+ type: Sequelize.INTEGER,
10
+ primaryKey: true,
11
+ autoIncrement: true,
12
+ allowNull: false,
13
+ },
14
+ escrow_id: {
15
+ type: Sequelize.INTEGER,
16
+ allowNull: false,
17
+ unique: true,
18
+ references: { model: "escrows", key: "escrow_id" },
19
+ onDelete: "RESTRICT",
20
+ onUpdate: "CASCADE",
21
+ },
22
+ provider: {
23
+ type: Sequelize.STRING(100),
24
+ allowNull: false,
25
+ comment: "GIG | DHL | FedEx",
26
+ },
27
+ tracking_number: {
28
+ type: Sequelize.STRING(100),
29
+ allowNull: false,
30
+ },
31
+ status: {
32
+ type: Sequelize.ENUM(...Object.values(LOGISTICS_STATUS)),
33
+ allowNull: false,
34
+ defaultValue: LOGISTICS_STATUS.SHIPPED,
35
+ },
36
+ delivery_proof_url: {
37
+ type: Sequelize.STRING(500),
38
+ allowNull: true,
39
+ comment: "AWS S3 URL — photo or waybill",
40
+ },
41
+ created_at: {
42
+ type: Sequelize.DATE,
43
+ allowNull: false,
44
+ },
45
+ updated_at: {
46
+ type: Sequelize.DATE,
47
+ allowNull: false,
48
+ },
49
+ });
50
+ },
51
+
52
+ async down(queryInterface) {
53
+ await queryInterface.dropTable("logistics");
54
+ },
55
+ };
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("ratings", {
6
+ rating_id: {
7
+ type: Sequelize.INTEGER,
8
+ primaryKey: true,
9
+ autoIncrement: true,
10
+ allowNull: false,
11
+ },
12
+ escrow_id: {
13
+ type: Sequelize.INTEGER,
14
+ allowNull: false,
15
+ unique: true,
16
+ references: { model: "escrows", key: "escrow_id" },
17
+ onDelete: "RESTRICT",
18
+ onUpdate: "CASCADE",
19
+ comment: "One rating per completed escrow",
20
+ },
21
+ buyer_id: {
22
+ type: Sequelize.INTEGER,
23
+ allowNull: false,
24
+ references: { model: "users", key: "user_id" },
25
+ onDelete: "RESTRICT",
26
+ onUpdate: "CASCADE",
27
+ },
28
+ seller_id: {
29
+ type: Sequelize.INTEGER,
30
+ allowNull: false,
31
+ references: { model: "users", key: "user_id" },
32
+ onDelete: "RESTRICT",
33
+ onUpdate: "CASCADE",
34
+ },
35
+ rating: {
36
+ type: Sequelize.TINYINT,
37
+ allowNull: false,
38
+ },
39
+ comment: {
40
+ type: Sequelize.TEXT,
41
+ allowNull: true,
42
+ },
43
+ created_at: {
44
+ type: Sequelize.DATE,
45
+ allowNull: false,
46
+ },
47
+ });
48
+
49
+ await queryInterface.addIndex("ratings", ["buyer_id"]);
50
+ await queryInterface.addIndex("ratings", ["seller_id"]);
51
+ },
52
+
53
+ async down(queryInterface) {
54
+ await queryInterface.dropTable("ratings");
55
+ },
56
+ };
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+
3
+ const {
4
+ DISPUTE_REASON,
5
+ DISPUTE_STATUS,
6
+ DISPUTE_RESOLUTION,
7
+ } = require("../services/constants");
8
+
9
+ module.exports = {
10
+ async up(queryInterface, Sequelize) {
11
+ await queryInterface.createTable("disputes", {
12
+ dispute_id: {
13
+ type: Sequelize.INTEGER,
14
+ primaryKey: true,
15
+ autoIncrement: true,
16
+ allowNull: false,
17
+ },
18
+ escrow_id: {
19
+ type: Sequelize.INTEGER,
20
+ allowNull: false,
21
+ references: { model: "escrows", key: "escrow_id" },
22
+ onDelete: "RESTRICT",
23
+ onUpdate: "CASCADE",
24
+ },
25
+ opened_by: {
26
+ type: Sequelize.INTEGER,
27
+ allowNull: false,
28
+ references: { model: "users", key: "user_id" },
29
+ onDelete: "RESTRICT",
30
+ onUpdate: "CASCADE",
31
+ comment: "Buyer or seller who opened the dispute",
32
+ },
33
+ reason: {
34
+ type: Sequelize.ENUM(...Object.values(DISPUTE_REASON)),
35
+ allowNull: false,
36
+ },
37
+ description: {
38
+ type: Sequelize.TEXT,
39
+ allowNull: false,
40
+ comment: "Buyer/seller's initial explanation when opening the dispute",
41
+ },
42
+ assigned_admin: {
43
+ type: Sequelize.INTEGER,
44
+ allowNull: true,
45
+ references: { model: "admin_users", key: "admin_id" },
46
+ onDelete: "SET NULL",
47
+ onUpdate: "CASCADE",
48
+ comment: "NULL until assigned by super admin",
49
+ },
50
+ status: {
51
+ type: Sequelize.ENUM(...Object.values(DISPUTE_STATUS)),
52
+ allowNull: false,
53
+ defaultValue: DISPUTE_STATUS.OPEN,
54
+ },
55
+ resolution: {
56
+ type: Sequelize.ENUM(...Object.values(DISPUTE_RESOLUTION)),
57
+ allowNull: true,
58
+ defaultValue: null,
59
+ },
60
+ resolution_note: {
61
+ type: Sequelize.TEXT,
62
+ allowNull: true,
63
+ comment: "Admin explanation of decision",
64
+ },
65
+ resolved_at: {
66
+ type: Sequelize.DATE,
67
+ allowNull: true,
68
+ },
69
+ created_at: {
70
+ type: Sequelize.DATE,
71
+ allowNull: false,
72
+ },
73
+ });
74
+
75
+ await queryInterface.addIndex("disputes", ["escrow_id"]);
76
+ await queryInterface.addIndex("disputes", ["opened_by"]);
77
+ await queryInterface.addIndex("disputes", ["assigned_admin"]);
78
+ await queryInterface.addIndex("disputes", ["status"]);
79
+ },
80
+
81
+ async down(queryInterface) {
82
+ await queryInterface.dropTable("disputes");
83
+ },
84
+ };
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+
3
+ const { EVIDENCE_FILE_TYPE } = require("../services/constants");
4
+
5
+ module.exports = {
6
+ async up(queryInterface, Sequelize) {
7
+ await queryInterface.createTable("dispute_evidence", {
8
+ dispute_evidence_id: {
9
+ type: Sequelize.INTEGER,
10
+ primaryKey: true,
11
+ autoIncrement: true,
12
+ allowNull: false,
13
+ },
14
+ dispute_id: {
15
+ type: Sequelize.INTEGER,
16
+ allowNull: false,
17
+ references: { model: "disputes", key: "dispute_id" },
18
+ onDelete: "CASCADE",
19
+ onUpdate: "CASCADE",
20
+ },
21
+ uploaded_by: {
22
+ type: Sequelize.INTEGER,
23
+ allowNull: false,
24
+ references: { model: "users", key: "user_id" },
25
+ onDelete: "RESTRICT",
26
+ onUpdate: "CASCADE",
27
+ },
28
+ file_url: {
29
+ type: Sequelize.STRING(500),
30
+ allowNull: false,
31
+ comment: "AWS S3 URL",
32
+ },
33
+ file_type: {
34
+ type: Sequelize.ENUM(...Object.values(EVIDENCE_FILE_TYPE)),
35
+ allowNull: false,
36
+ },
37
+ created_at: {
38
+ type: Sequelize.DATE,
39
+ allowNull: false,
40
+ },
41
+ });
42
+
43
+ await queryInterface.addIndex("dispute_evidence", ["dispute_id"]);
44
+ await queryInterface.addIndex("dispute_evidence", ["uploaded_by"]);
45
+ },
46
+
47
+ async down(queryInterface) {
48
+ await queryInterface.dropTable("dispute_evidence");
49
+ },
50
+ };
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("dispute_messages", {
6
+ dispute_message_id: {
7
+ type: Sequelize.INTEGER,
8
+ primaryKey: true,
9
+ autoIncrement: true,
10
+ allowNull: false,
11
+ },
12
+ dispute_id: {
13
+ type: Sequelize.INTEGER,
14
+ allowNull: false,
15
+ references: { model: "disputes", key: "dispute_id" },
16
+ onDelete: "CASCADE",
17
+ onUpdate: "CASCADE",
18
+ },
19
+ sender_id: {
20
+ type: Sequelize.INTEGER,
21
+ allowNull: false,
22
+ references: { model: "users", key: "user_id" },
23
+ onDelete: "RESTRICT",
24
+ onUpdate: "CASCADE",
25
+ comment: "Buyer or seller user id",
26
+ },
27
+ message: {
28
+ type: Sequelize.TEXT,
29
+ allowNull: false,
30
+ },
31
+ created_at: {
32
+ type: Sequelize.DATE,
33
+ allowNull: false,
34
+ },
35
+ });
36
+
37
+ await queryInterface.addIndex("dispute_messages", ["dispute_id"]);
38
+ await queryInterface.addIndex("dispute_messages", ["sender_id"]);
39
+ },
40
+
41
+ async down(queryInterface) {
42
+ await queryInterface.dropTable("dispute_messages");
43
+ },
44
+ };
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ async up(queryInterface, Sequelize) {
5
+ await queryInterface.createTable("notifications", {
6
+ notification_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
+ references: { model: "users", key: "user_id" },
16
+ onDelete: "CASCADE",
17
+ onUpdate: "CASCADE",
18
+ },
19
+ type: {
20
+ type: Sequelize.STRING(100),
21
+ allowNull: false,
22
+ comment: "ESCROW_FUNDED | PAYMENT_RELEASED | DISPUTE_OPENED 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("notifications", ["user_id"]);
44
+ await queryInterface.addIndex("notifications", ["user_id", "read_status"]);
45
+ },
46
+
47
+ async down(queryInterface) {
48
+ await queryInterface.dropTable("notifications");
49
+ },
50
+ };