@churchsoln/dbms 1.0.3 → 1.0.5

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.
package/Dockerfile CHANGED
@@ -9,6 +9,7 @@ RUN apk update && \
9
9
 
10
10
  # Set registry and timeout
11
11
  RUN npm config set registry https://registry.npmmirror.com
12
+ RUN npm rebuild bcrypt --build-from-source
12
13
  RUN npm install --legacy-peer-deps --force --timeout=60000
13
14
  RUN npm cache clean --force
14
15
 
@@ -17,5 +18,5 @@ RUN npm cache clean --force
17
18
  RUN apk del make gcc g++ python3 && \
18
19
  rm -rf /var/cache/apk/* && \
19
20
  npm cache clean --force
20
- EXPOSE 5000
21
+ EXPOSE 5007
21
22
  CMD [ "npm", "run", "start" ]
@@ -1,6 +1,7 @@
1
1
  module.exports = {
2
2
  success: "Success",
3
3
  churchCreated: "Church created successfully",
4
+ churchUpdated: "Church updated successfully",
4
5
  churchAlreadyExists: "Church already exists",
5
6
  migrationInitiated: "Migration initiated successfully",
6
7
  seedInitiated: "Seed initiated successfully",
@@ -1,6 +1,7 @@
1
1
  const { churchService } = require("../services");
2
2
  const errorCodes = require("../config/errorCodes");
3
3
  const response = require("../utils/response");
4
+ const errorMsgs = require("../config/errorMsgs");
4
5
  class ChurchController {
5
6
  async onboardChurch(req, res) {
6
7
  try {
@@ -31,13 +32,28 @@ class ChurchController {
31
32
 
32
33
  async updateChurchById(req, res) {
33
34
  try {
34
- const result = await churchService.updateChurchByIdService({ ...req.params, ...req.query });
35
+ const result = await churchService.updateChurchByIdService({ ...req.params, ...req.body });
35
36
  response.success(req, res, result.code, result.data, result.message);
36
37
  } catch (err) {
37
38
  response.error(req, res, errorCodes.HTTP_INTERNAL_SERVER_ERROR, err);
38
39
  }
39
40
  }
40
-
41
+ async uploadFile(req, res) {
42
+ try {
43
+ const file = req.files[0];
44
+ let file_folder = "documents";
45
+ if (file?.mimetype?.includes("image")) file_folder = "images";
46
+ else if (file?.mimetype?.includes("video")) file_folder = "videos";
47
+ else file_folder = "documents";
48
+ const responseData = {
49
+ url: `${process.env.BASE_URL}/dbms/api/uploads/${file_folder}/${file?.filename}`,
50
+ original_name: file?.originalname,
51
+ };
52
+ response.success(req, res, errorCodes.HTTP_OK, responseData, errorMsgs.success);
53
+ } catch (err) {
54
+ response.error(req, res, errorCodes.HTTP_INTERNAL_SERVER_ERROR, err);
55
+ }
56
+ }
41
57
  async addChurchPermission(req, res) {
42
58
  try {
43
59
  const result = await churchService.addChurchPermissionService({
@@ -11,11 +11,11 @@ http {
11
11
  keepalive_timeout 65;
12
12
 
13
13
  upstream frontend {
14
- server host.docker.internal:5200;
14
+ server host.docker.internal:5010;
15
15
  }
16
16
 
17
17
  upstream backend {
18
- server host.docker.internal:6000;
18
+ server host.docker.internal:5009;
19
19
  }
20
20
 
21
21
  # Redirect HTTP to HTTPS
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ up: async (queryInterface, DataTypes) => {
5
+ await queryInterface
6
+ .createTable(
7
+ "Church",
8
+ {
9
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
10
+ name: { type: DataTypes.STRING },
11
+ address: { type: DataTypes.STRING },
12
+ cityId: { type: DataTypes.INTEGER },
13
+ provinceId: { type: DataTypes.INTEGER },
14
+ country: { type: DataTypes.STRING },
15
+ postalCode: { type: DataTypes.STRING },
16
+ email: { type: DataTypes.STRING },
17
+ phoneNumber: { type: DataTypes.STRING },
18
+ contactPerson: { type: DataTypes.STRING },
19
+ currency: { type: DataTypes.STRING },
20
+ charitable_reg_no: { type: DataTypes.STRING },
21
+ registration_agency: { type: DataTypes.STRING },
22
+ profile_image: { type: DataTypes.STRING },
23
+ status: { type: DataTypes.BOOLEAN, defaultValue: true },
24
+ createdAt: {
25
+ type: DataTypes.DATE,
26
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
27
+ },
28
+ updatedAt: {
29
+ type: DataTypes.DATE,
30
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
31
+ },
32
+ },
33
+ { timestamps: false }
34
+ );
35
+ },
36
+ down: async (queryInterface) => {
37
+ await queryInterface.dropTable("Church");
38
+ },
39
+ };
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ up: async (queryInterface, DataTypes) => {
5
+ await queryInterface
6
+ .createTable(
7
+ "Group",
8
+ {
9
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
10
+ name: { type: DataTypes.STRING },
11
+ description: { type: DataTypes.TEXT },
12
+ isInclude: { type: DataTypes.BOOLEAN, defaultValue: false },
13
+ isPrivate: { type: DataTypes.BOOLEAN, defaultValue: false },
14
+ maxMembers: { type: DataTypes.INTEGER, defaultValue: 50 },
15
+ profileUrl: { type: DataTypes.STRING },
16
+ status: { type: DataTypes.BOOLEAN, defaultValue: true },
17
+ createdAt: {
18
+ type: DataTypes.DATE,
19
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
20
+ },
21
+ updatedAt: {
22
+ type: DataTypes.DATE,
23
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
24
+ },
25
+ createdBy: { type: DataTypes.INTEGER },
26
+ updatedBy: { type: DataTypes.INTEGER },
27
+ deletedAt: { type: DataTypes.DATE },
28
+ deletedBy: { type: DataTypes.INTEGER },
29
+ },
30
+ { timestamps: false }
31
+ )
32
+ .then(() =>
33
+ queryInterface.addConstraint("Group", {
34
+ fields: ["createdBy"],
35
+ type: "foreign key",
36
+ name: "Group_CreatedBy_Fkey",
37
+ references: {
38
+ table: "User",
39
+ field: "id",
40
+ },
41
+ onDelete: "cascade",
42
+ onUpdate: "cascade",
43
+ })
44
+ ).then(() =>
45
+ queryInterface.addConstraint("Group", {
46
+ fields: ["updatedBy"],
47
+ type: "foreign key",
48
+ name: "Group_UpdatedBy_Fkey",
49
+ references: {
50
+ table: "User",
51
+ field: "id",
52
+ },
53
+ onDelete: "cascade",
54
+ onUpdate: "cascade",
55
+ })
56
+ );
57
+ },
58
+ down: async (queryInterface) => {
59
+ await queryInterface.dropTable("Group");
60
+ },
61
+ };
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ up: async (queryInterface, DataTypes) => {
5
+ await queryInterface
6
+ .createTable(
7
+ "GroupMembers",
8
+ {
9
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
10
+ userId: { type: DataTypes.INTEGER },
11
+ groupId: { type: DataTypes.INTEGER },
12
+ isAdmin: { type: DataTypes.BOOLEAN, defaultValue: false },
13
+ status: { type: DataTypes.BOOLEAN, defaultValue: true },
14
+ createdAt: {
15
+ type: DataTypes.DATE,
16
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
17
+ },
18
+ updatedAt: {
19
+ type: DataTypes.DATE,
20
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
21
+ },
22
+ createdBy: { type: DataTypes.INTEGER },
23
+ updatedBy: { type: DataTypes.INTEGER },
24
+ }
25
+ ).then(() =>
26
+ queryInterface.addConstraint("GroupMembers", {
27
+ fields: ["userId"],
28
+ type: "foreign key",
29
+ name: "GroupMembers_UserId_Fkey",
30
+ references: {
31
+ table: "User",
32
+ field: "id",
33
+ },
34
+ onDelete: "cascade",
35
+ onUpdate: "cascade",
36
+ })
37
+ ).then(() =>
38
+ queryInterface.addConstraint("GroupMembers", {
39
+ fields: ["groupId"],
40
+ type: "foreign key",
41
+ name: "GroupMembers_GroupId_Fkey",
42
+ references: {
43
+ table: "Group",
44
+ field: "id",
45
+ },
46
+ onDelete: "cascade",
47
+ onUpdate: "cascade",
48
+ })
49
+ ).then(() =>
50
+ queryInterface.addConstraint("GroupMembers", {
51
+ fields: ["createdBy"],
52
+ type: "foreign key",
53
+ name: "GroupMembers_CreatedBy_Fkey",
54
+ references: {
55
+ table: "User",
56
+ field: "id",
57
+ },
58
+ onDelete: "cascade",
59
+ onUpdate: "cascade",
60
+ })
61
+ ).then(() =>
62
+ queryInterface.addConstraint("GroupMembers", {
63
+ fields: ["updatedBy"],
64
+ type: "foreign key",
65
+ name: "GroupMembers_UpdatedBy_Fkey",
66
+ references: {
67
+ table: "User",
68
+ field: "id",
69
+ },
70
+ onDelete: "cascade",
71
+ onUpdate: "cascade",
72
+ })
73
+ )
74
+ }
75
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ module.exports = {
4
+ up: async (queryInterface, DataTypes) => {
5
+ return Promise.all([
6
+ queryInterface.removeColumn("Church", "profile_image"),
7
+ queryInterface.removeColumn("Church", "registration_agency"),
8
+ queryInterface.removeColumn("Church", "charitable_reg_no"),
9
+ queryInterface.removeColumn("Church", "currency"),
10
+ queryInterface.removeColumn("Church", "postalCode"),
11
+ queryInterface.removeColumn("Church", "country"),
12
+ queryInterface.removeConstraint("Church", "church_provinceId_fk").then(() =>
13
+ queryInterface.removeColumn("Church", "provinceId")
14
+ ),
15
+ queryInterface.removeConstraint("Church", "church_cityId_fk").then(() =>
16
+ queryInterface.removeColumn("Church", "cityId")
17
+ ),
18
+ queryInterface.removeColumn("Church", "address"),
19
+ ]);
20
+ },
21
+
22
+ down: async (queryInterface) => {},
23
+ };
@@ -0,0 +1,47 @@
1
+ const { ONBOARD_STATUS } = require("../../constants");
2
+
3
+ module.exports = (sequelize, DataTypes) => {
4
+ const church = sequelize.define(
5
+ "Church",
6
+ {
7
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
8
+ name: { type: DataTypes.STRING },
9
+ address: { type: DataTypes.STRING },
10
+ cityId: { type: DataTypes.INTEGER },
11
+ provinceId: { type: DataTypes.INTEGER },
12
+ country: { type: DataTypes.STRING },
13
+ postalCode: { type: DataTypes.STRING },
14
+ email: { type: DataTypes.STRING },
15
+ phoneNumber: { type: DataTypes.STRING },
16
+ contactPerson: { type: DataTypes.STRING },
17
+ currency: { type: DataTypes.STRING },
18
+ charitable_reg_no: { type: DataTypes.STRING },
19
+ registration_agency: { type: DataTypes.STRING },
20
+ profile_image: { type: DataTypes.STRING },
21
+ status: { type: DataTypes.BOOLEAN },
22
+ createdAt: { type: DataTypes.DATE },
23
+ updatedAt: { type: DataTypes.DATE },
24
+ },
25
+ { freezeTableName: true, timestamps: false }
26
+ );
27
+ church.selectedFields = [
28
+ "id",
29
+ "name",
30
+ "address",
31
+ "cityId",
32
+ "provinceId",
33
+ "country",
34
+ "postalCode",
35
+ "email",
36
+ "phoneNumber",
37
+ "contactPerson",
38
+ "currency",
39
+ "charitable_reg_no",
40
+ "registration_agency",
41
+ "profile_image",
42
+ "status",
43
+ "createdAt",
44
+ "updatedAt",
45
+ ];
46
+ return church;
47
+ };
@@ -0,0 +1,74 @@
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const group = sequelize.define(
3
+ "Group",
4
+ {
5
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
6
+ name: { type: DataTypes.STRING },
7
+ description: { type: DataTypes.TEXT },
8
+ isInclude: { type: DataTypes.BOOLEAN, defaultValue: false },
9
+ isPrivate: { type: DataTypes.BOOLEAN, defaultValue: false },
10
+ maxMembers: { type: DataTypes.INTEGER, defaultValue: 50 },
11
+ profileUrl: { type: DataTypes.STRING },
12
+ status: { type: DataTypes.BOOLEAN, defaultValue: true },
13
+ createdAt: {
14
+ type: DataTypes.DATE,
15
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
16
+ },
17
+ updatedAt: {
18
+ type: DataTypes.DATE,
19
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
20
+ },
21
+ createdBy: {
22
+ type: DataTypes.INTEGER,
23
+ set(createdBy) {
24
+ this.setDataValue("createdBy", createdBy?.id || createdBy);
25
+ },
26
+ },
27
+ updatedBy: {
28
+ type: DataTypes.INTEGER,
29
+ set(updatedBy) {
30
+ this.setDataValue("updatedBy", updatedBy?.id || updatedBy);
31
+ },
32
+ },
33
+ deletedAt: { type: DataTypes.DATE },
34
+ deletedBy: {
35
+ type: DataTypes.INTEGER,
36
+ set(deletedBy) {
37
+ this.setDataValue("deletedBy", deletedBy?.id || deletedBy);
38
+ },
39
+ },
40
+ },
41
+ { freezeTableName: true, timestamps: false }
42
+ );
43
+ group.associate = function (models) {
44
+ group.belongsTo(models.user, {
45
+ foreignKey: "createdBy",
46
+ as: "createdBy",
47
+ });
48
+ group.belongsTo(models.user, {
49
+ foreignKey: "updatedBy",
50
+ as: "updatedBy",
51
+ });
52
+ group.belongsTo(models.user, {
53
+ foreignKey: "deletedBy",
54
+ as: "deletedBy",
55
+ });
56
+ };
57
+ group.selectedFields = [
58
+ "id",
59
+ "name",
60
+ "description",
61
+ "isInclude",
62
+ "isPrivate",
63
+ "maxMembers",
64
+ "profileUrl",
65
+ "status",
66
+ "createdAt",
67
+ "updatedAt",
68
+ "createdBy",
69
+ "updatedBy",
70
+ "deletedAt",
71
+ "deletedBy",
72
+ ];
73
+ return group;
74
+ };
@@ -0,0 +1,72 @@
1
+ module.exports = (sequelize, DataTypes) => {
2
+ const groupMembers = sequelize.define(
3
+ "GroupMembers",
4
+ {
5
+ id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
6
+ userId: {
7
+ type: DataTypes.INTEGER,
8
+ set(userId) {
9
+ this.setDataValue("userId", userId?.id || userId);
10
+ },
11
+ },
12
+ groupId: {
13
+ type: DataTypes.INTEGER,
14
+ set(groupId) {
15
+ this.setDataValue("groupId", groupId?.id || groupId);
16
+ },
17
+ },
18
+ isAdmin: { type: DataTypes.BOOLEAN, defaultValue: false },
19
+ status: { type: DataTypes.BOOLEAN, defaultValue: true },
20
+ createdAt: {
21
+ type: DataTypes.DATE,
22
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
23
+ },
24
+ updatedAt: {
25
+ type: DataTypes.DATE,
26
+ defaultValue: DataTypes.literal("CURRENT_TIMESTAMP"),
27
+ },
28
+ createdBy: {
29
+ type: DataTypes.INTEGER,
30
+ set(createdBy) {
31
+ this.setDataValue("createdBy", createdBy?.id || createdBy);
32
+ },
33
+ },
34
+ updatedBy: {
35
+ type: DataTypes.INTEGER,
36
+ set(updatedBy) {
37
+ this.setDataValue("updatedBy", updatedBy?.id || updatedBy);
38
+ },
39
+ },
40
+ },
41
+ { freezeTableName: true, timestamps: false }
42
+ );
43
+ groupMembers.associate = function (models) {
44
+ groupMembers.belongsTo(models.user, {
45
+ foreignKey: "userId",
46
+ as: "user",
47
+ });
48
+ groupMembers.belongsTo(models.group, {
49
+ foreignKey: "groupId",
50
+ as: "group",
51
+ });
52
+ groupMembers.belongsTo(models.user, {
53
+ foreignKey: "createdBy",
54
+ as: "createdBy",
55
+ });
56
+ groupMembers.belongsTo(models.user, {
57
+ foreignKey: "updatedBy",
58
+ as: "updatedBy",
59
+ });
60
+ };
61
+ groupMembers.selectedFields = [
62
+ "id",
63
+ "userId",
64
+ "groupId",
65
+ "isAdmin",
66
+ "status",
67
+ "createdAt",
68
+ "updatedAt",
69
+ "createdBy",
70
+ "updatedBy",
71
+ ];
72
+ }
@@ -7,18 +7,9 @@ module.exports = (sequelize, DataTypes) => {
7
7
  id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
8
8
  name: { type: DataTypes.STRING },
9
9
  connectionUrl: { type: DataTypes.STRING },
10
- address: { type: DataTypes.STRING },
11
- cityId: { type: DataTypes.INTEGER },
12
- provinceId: { type: DataTypes.INTEGER },
13
- country: { type: DataTypes.STRING },
14
- postalCode: { type: DataTypes.STRING },
15
10
  email: { type: DataTypes.STRING },
16
11
  phoneNumber: { type: DataTypes.STRING },
17
12
  contactPerson: { type: DataTypes.STRING },
18
- currency: { type: DataTypes.STRING },
19
- charitable_reg_no: { type: DataTypes.STRING },
20
- registration_agency: { type: DataTypes.STRING },
21
- profile_image: { type: DataTypes.STRING },
22
13
  registeredAt: { type: DataTypes.DATEONLY },
23
14
  subscribedAt: { type: DataTypes.DATEONLY },
24
15
  subscriptionPlanId: { type: DataTypes.INTEGER },
@@ -43,25 +34,14 @@ module.exports = (sequelize, DataTypes) => {
43
34
  foreignKey: "subscriptionPlanId",
44
35
  as: "subscriptionPlan",
45
36
  });
46
- church.belongsTo(models.city, { foreignKey: "cityId", as: "city" });
47
- church.belongsTo(models.province, { foreignKey: "provinceId", as: "province" });
48
37
  };
49
38
  church.selectedFields = [
50
39
  "id",
51
40
  "name",
52
41
  "connectionUrl",
53
- "address",
54
- "cityId",
55
- "provinceId",
56
- "country",
57
- "postalCode",
58
42
  "email",
59
43
  "phoneNumber",
60
44
  "contactPerson",
61
- "currency",
62
- "charitable_reg_no",
63
- "registration_agency",
64
- "profile_image",
65
45
  "registeredAt",
66
46
  "subscribedAt",
67
47
  "subscriptionPlanId",
@@ -21,6 +21,6 @@ module.exports = (sequelize, DataTypes) => {
21
21
  as: "church",
22
22
  });
23
23
  };
24
- churchPermission.selectedFields = ["id", "churchId", "permissionId"];
24
+ churchPermission.selectedFields = ["id", "churchId", "permissionId", "createdAt"];
25
25
  return churchPermission;
26
26
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@churchsoln/dbms",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "prestart": "node db-setup.js",
@@ -25,6 +25,7 @@
25
25
  "joi": "^17.1.1",
26
26
  "jsonwebtoken": "^9.0.2",
27
27
  "morgan": "^1.10.0",
28
+ "multer": "^1.4.4",
28
29
  "mysql2": "^3.12.0",
29
30
  "nodemon": "^3.1.4",
30
31
  "pg": "^8.8.0",
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "servers": [
9
9
  {
10
- "url": "http://localhost:5000/dbms/api/v1",
10
+ "url": "http://localhost:5007/dbms/api/v1",
11
11
  "description": "Local"
12
12
  },
13
13
  {
@@ -47,20 +47,9 @@
47
47
  },
48
48
  "example": {
49
49
  "name": "Xavier",
50
- "address": "Address",
51
- "cityId": 1,
52
- "provinceId": 1,
53
- "country": "Country",
54
- "postalCode": "123",
55
50
  "email": "email",
56
51
  "phoneNumber": "1234",
57
52
  "contactPerson": "string",
58
- "currency": "dollar",
59
- "charitable_reg_no": "123",
60
- "registration_agency": "agency",
61
- "profileImage": "http://image.com",
62
- "registeredAt": "2023-08-01",
63
- "subscribedAt": "2023-08-01",
64
53
  "subscriptionPlanId": 1,
65
54
  "churchAdminDetails": {
66
55
  "firstName": "Jose",
@@ -136,8 +125,8 @@
136
125
  },
137
126
  "patch": {
138
127
  "tags": ["Church API's"],
139
- "summary": "Activate/Deactivate Church by id",
140
- "operationId": "Activate/Deactivate Church by id",
128
+ "summary": "Update Church by id",
129
+ "operationId": "Update Church by id",
141
130
  "parameters": [
142
131
  {
143
132
  "name": "id",
@@ -150,19 +139,50 @@
150
139
  "format": "int32",
151
140
  "example": 1
152
141
  }
153
- },
154
- {
155
- "name": "status",
156
- "in": "query",
157
- "required": true,
158
- "style": "form",
159
- "explode": true,
160
- "schema": {
161
- "type": "boolean",
162
- "example": true
163
- }
164
142
  }
165
143
  ],
144
+ "requestBody": {
145
+ "content": {
146
+ "application/json": {
147
+ "schema": {
148
+ "$ref": "#/components/schemas/UpdateChurchDataRequest"
149
+ },
150
+ "example": {
151
+ "name": "Xavier",
152
+ "email": "email",
153
+ "phoneNumber": "1234",
154
+ "contactPerson": "string",
155
+ "subscriptionPlanId": 1,
156
+ "status": true
157
+ }
158
+ }
159
+ }
160
+ },
161
+ "responses": {
162
+ "200": {
163
+ "description": "OK"
164
+ }
165
+ },
166
+ "deprecated": false
167
+ }
168
+ },
169
+ "/church/upload-file": {
170
+ "post": {
171
+ "tags": ["Church API's"],
172
+ "summary": "Upload file",
173
+ "operationId": "Upload file",
174
+ "parameters": [],
175
+ "requestBody": {
176
+ "content": {
177
+ "multipart/form-data": {
178
+ "schema": {
179
+ "$ref": "#/components/schemas/UploadFileDataRequest"
180
+ },
181
+ "encoding": {}
182
+ }
183
+ },
184
+ "required": false
185
+ },
166
186
  "responses": {
167
187
  "200": {
168
188
  "description": "OK"
@@ -197,7 +217,7 @@
197
217
  "$ref": "#/components/schemas/AddChurchPermissionDataRequest"
198
218
  },
199
219
  "example": {
200
- "permissionId": 1
220
+ "permissionId": [1]
201
221
  }
202
222
  }
203
223
  },
@@ -312,20 +332,9 @@
312
332
  "title": "OnboardNewDataRequest",
313
333
  "required": [
314
334
  "name",
315
- "address",
316
- "cityId",
317
- "provinceId",
318
- "country",
319
- "postalCode",
320
335
  "email",
321
336
  "phoneNumber",
322
337
  "contactPerson",
323
- "currency",
324
- "charitable_reg_no",
325
- "registration_agency",
326
- "profile_image",
327
- "registeredAt",
328
- "subscribedAt",
329
338
  "subscriptionPlanId",
330
339
  "churchAdminDetails"
331
340
  ],
@@ -334,23 +343,6 @@
334
343
  "name": {
335
344
  "type": "string"
336
345
  },
337
- "address": {
338
- "type": "string"
339
- },
340
- "cityId": {
341
- "type": "integer",
342
- "format": "int32"
343
- },
344
- "provinceId": {
345
- "type": "integer",
346
- "format": "int32"
347
- },
348
- "country": {
349
- "type": "string"
350
- },
351
- "postalCode": {
352
- "type": "string"
353
- },
354
346
  "email": {
355
347
  "type": "string"
356
348
  },
@@ -360,24 +352,6 @@
360
352
  "contactPerson": {
361
353
  "type": "string"
362
354
  },
363
- "currency": {
364
- "type": "string"
365
- },
366
- "charitable_reg_no": {
367
- "type": "string"
368
- },
369
- "registration_agency": {
370
- "type": "string"
371
- },
372
- "profile_image": {
373
- "type": "string"
374
- },
375
- "registeredAt": {
376
- "type": "string"
377
- },
378
- "subscribedAt": {
379
- "type": "string"
380
- },
381
355
  "subscriptionPlanId": {
382
356
  "type": "integer",
383
357
  "format": "int32"
@@ -389,20 +363,9 @@
389
363
  },
390
364
  "example": {
391
365
  "name": "Xavier",
392
- "address": "Address",
393
- "cityId": 1,
394
- "provinceId": 1,
395
- "country": "Country",
396
- "postalCode": "123",
397
366
  "email": "email",
398
367
  "phoneNumber": "1234",
399
368
  "contactPerson": "string",
400
- "currenncy": "dollar",
401
- "charitable_reg_no": "123",
402
- "registration_agency": "agency",
403
- "profileImage": "http://image.com",
404
- "registeredAt": "2023-08-01",
405
- "subscribedAt": "2023-08-01",
406
369
  "subscriptionPlanId": 1,
407
370
  "churchAdminDetails": {
408
371
  "firstName": "Jose",
@@ -413,6 +376,40 @@
413
376
  }
414
377
  }
415
378
  },
379
+ "UpdateChurchDataRequest": {
380
+ "title": "UpdateChurchDataRequest",
381
+ "required": [],
382
+ "type": "object",
383
+ "properties": {
384
+ "name": {
385
+ "type": "string"
386
+ },
387
+ "email": {
388
+ "type": "string"
389
+ },
390
+ "phoneNumber": {
391
+ "type": "string"
392
+ },
393
+ "contactPerson": {
394
+ "type": "string"
395
+ },
396
+ "subscriptionPlanId": {
397
+ "type": "integer",
398
+ "format": "int32"
399
+ },
400
+ "status": {
401
+ "type": "boolean"
402
+ }
403
+ },
404
+ "example": {
405
+ "name": "Xavier",
406
+ "email": "email",
407
+ "phoneNumber": "1234",
408
+ "contactPerson": "string",
409
+ "subscriptionPlanId": 1,
410
+ "status": true
411
+ }
412
+ },
416
413
  "ChurchAdminDataRequest": {
417
414
  "title": "ChurchAdminDataRequest",
418
415
  "required": ["firstName", "lastName", "email", "phone", "password"],
@@ -465,8 +462,21 @@
465
462
  "type": "object",
466
463
  "properties": {
467
464
  "permissionId": {
468
- "type": "integer",
469
- "format": "int32"
465
+ "type": "array",
466
+ "items": {
467
+ "type": "number"
468
+ }
469
+ }
470
+ }
471
+ },
472
+ "UploadFileDataRequest": {
473
+ "title": "UploadFileDataRequest",
474
+ "required": ["file"],
475
+ "type": "object",
476
+ "properties": {
477
+ "file": {
478
+ "type": "string",
479
+ "format": "binary"
470
480
  }
471
481
  }
472
482
  }
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "servers": [
9
9
  {
10
- "url": "http://localhost:5000/dbms/api/v1",
10
+ "url": "http://localhost:5007/dbms/api/v1",
11
11
  "description": "Local"
12
12
  },
13
13
  {
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "servers": [
9
9
  {
10
- "url": "http://localhost:5000/dbms/api/v1",
10
+ "url": "http://localhost:5007/dbms/api/v1",
11
11
  "description": "Local"
12
12
  },
13
13
  {
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "servers": [
9
9
  {
10
- "url": "http://localhost:5000/dbms/api/v1",
10
+ "url": "http://localhost:5007/dbms/api/v1",
11
11
  "description": "Local"
12
12
  },
13
13
  {
package/redis/config.js CHANGED
@@ -1,4 +1,5 @@
1
+ require('dotenv').config()
1
2
  module.exports = {
2
- host: "redis1",
3
- port: 6379,
3
+ host: process.env.REDIS_HOST,
4
+ port: process.env.REDIS_PORT,
4
5
  };
@@ -3,6 +3,7 @@ const router = express.Router();
3
3
  const { ChurchController } = require("../../controllers");
4
4
  const validators = require("../../validators");
5
5
  const verifyToken = require("../../utils/verifyToken");
6
+ const docUpload = require("../../utils/docUpload");
6
7
 
7
8
  // Church API's
8
9
  router.post("", verifyToken, validators.onboardChurch, ChurchController.onboardChurch);
@@ -19,7 +20,7 @@ router.patch(
19
20
  validators.updateChurch,
20
21
  ChurchController.updateChurchById
21
22
  );
22
-
23
+ router.post("/upload-file", verifyToken, docUpload, ChurchController.uploadFile);
23
24
  // Church Permissions
24
25
  router.post(
25
26
  "/:churchId/permission",
@@ -16,6 +16,7 @@ module.exports = {
16
16
  { name: "Accounts", status: true },
17
17
  { name: "Donations", status: true },
18
18
  { name: "Transfer Type", status: true },
19
+ { name: "Groups", status: true },
19
20
  ];
20
21
 
21
22
  for (const permission of permissions) {
@@ -26,6 +26,8 @@ class ChurchService {
26
26
  const churchData = await commonDb.church.create(
27
27
  {
28
28
  ...params,
29
+ registeredAt: new Date(),
30
+ subscribedAt: new Date(),
29
31
  connectionUrl: process.env.CHURCH_SHADOW_DATABASE_URL.replace(
30
32
  "church_shadow",
31
33
  name.toLowerCase()
@@ -67,6 +69,7 @@ class ChurchService {
67
69
  roleId: ROLES.CHURCH_ADMIN,
68
70
  churchId: churchData.id,
69
71
  });
72
+ await churchDb.church.create(params)
70
73
  console.log(`Church onboarding completed for church: ${name}`);
71
74
  }
72
75
  });
@@ -81,9 +84,14 @@ class ChurchService {
81
84
  const { search = null } = params;
82
85
  const searchCondition = !!search ? { name: { [Op.like]: `%${search}%` } } : {};
83
86
  const data = await commonDb.church.findAll({
84
- where: { status: true, ...searchCondition },
87
+ where: { ...searchCondition },
85
88
  attributes: commonDb.church.selectedFields,
86
- raw: true,
89
+ include: {
90
+ model: commonDb.subscriptionPlan,
91
+ as: "subscriptionPlan",
92
+ attributes: commonDb.subscriptionPlan.selectedFields,
93
+ required: false,
94
+ },
87
95
  });
88
96
  return { code: errorCodes.HTTP_OK, message: errorMsgs.success, data };
89
97
  } catch (err) {
@@ -107,10 +115,12 @@ class ChurchService {
107
115
 
108
116
  async updateChurchByIdService(params) {
109
117
  try {
110
- const { id, status } = params;
111
- await commonDb.church.update({ status }, { where: { id } });
112
- const message = status ? errorMsgs.activated : errorMsgs.deactivated;
113
- return { code: errorCodes.HTTP_OK, message };
118
+ const { id, ...restParams } = params;
119
+ if(restParams.subscriptionPlanId) {
120
+ restParams.subscribedAt = new Date();
121
+ }
122
+ await commonDb.church.update(restParams, { where: { id } });
123
+ return { code: errorCodes.HTTP_OK, message: errorMsgs.churchUpdated };
114
124
  } catch (err) {
115
125
  return { code: errorCodes.HTTP_INTERNAL_SERVER_ERROR, message: err };
116
126
  }
@@ -118,7 +128,7 @@ class ChurchService {
118
128
 
119
129
  async addChurchPermissionService(params) {
120
130
  try {
121
- const { churchId, permissionId } = params;
131
+ const { churchId, permissionId = [] } = params;
122
132
  const existingChurch = await commonDb.church.findOne({
123
133
  where: { id: churchId },
124
134
  raw: true,
@@ -126,26 +136,28 @@ class ChurchService {
126
136
  if (!existingChurch) {
127
137
  return { code: errorCodes.HTTP_BAD_REQUEST, message: errorMsgs.invalidChurchId };
128
138
  }
129
- const existingPermission = await commonDb.permission.findOne({
139
+ const existingPermission = await commonDb.permission.findAll({
130
140
  where: { id: permissionId },
131
141
  raw: true,
132
142
  });
133
- if (!existingPermission) {
143
+ if (existingPermission.length !== permissionId.length) {
134
144
  return {
135
145
  code: errorCodes.HTTP_BAD_REQUEST,
136
146
  message: errorMsgs.invalidPermissionId,
137
147
  };
138
148
  }
139
- const existingChurchPermission = await commonDb.churchPermission.findOne({
149
+ const existingChurchPermission = await commonDb.churchPermission.findAll({
140
150
  where: { churchId, permissionId },
141
151
  raw: true,
142
152
  });
143
- if (existingChurchPermission) {
144
- return { code: errorCodes.HTTP_CONFLICT, message: errorMsgs.permissionExists };
153
+ const excludedPermissions = permissionId.filter(
154
+ (x) => !existingChurchPermission.map((y) => y.permissionId).includes(x)
155
+ );
156
+ if(excludedPermissions.length) {
157
+ const queries = excludedPermissions.map((x) => commonDb.churchPermission.create({ churchId, permissionId: x }))
158
+ await Promise.all(queries);
145
159
  }
146
- const response = await commonDb.churchPermission.create({ churchId, permissionId });
147
- const data = response.get({ plain: true });
148
- return { code: errorCodes.HTTP_OK, message: errorMsgs.success, data };
160
+ return { code: errorCodes.HTTP_OK, message: errorMsgs.success };
149
161
  } catch (err) {
150
162
  return { code: errorCodes.HTTP_INTERNAL_SERVER_ERROR, message: err };
151
163
  }
@@ -0,0 +1,51 @@
1
+ const multer = require("multer");
2
+ const path = require("path");
3
+ const response = require("./response");
4
+ const errorCodes = require("../config/errorCodes");
5
+ const docUpload = async (req, res, next) => {
6
+ const storage = multer.diskStorage({
7
+ destination: function (req, file, cb) {
8
+ let file_folder = "documents";
9
+ if (file.mimetype.includes("image")) file_folder = "images";
10
+ else if (file.mimetype.includes("video")) file_folder = "videos";
11
+ else file_folder = "documents";
12
+ cb(null, `/app/uploads/${file_folder}`);
13
+ },
14
+ filename: function (req, file, cb) {
15
+ const filename =
16
+ file.fieldname +
17
+ "-" +
18
+ Date.now() +
19
+ path.extname(file.originalname).toLocaleLowerCase();
20
+ cb(null, filename);
21
+ },
22
+ });
23
+ const filter = (req, res, file, cb) => {
24
+ if (
25
+ file.mimetype.includes("pdf") ||
26
+ file.mimetype.includes("image") ||
27
+ file.mimetype.includes("video")
28
+ ) {
29
+ cb(null, true);
30
+ } else {
31
+ return cb(new Error("Only images/pdf/videos are allowed"));
32
+ }
33
+ };
34
+ req.files = {};
35
+ multer({
36
+ storage,
37
+ fileFilter: (req, file, cb) => filter(req, res, file, cb),
38
+ limits: { fileSize: 20 * 1024 * 1024 },
39
+ }).any()(req, res, (err) => {
40
+ console.log(err)
41
+ if (err)
42
+ response.invalid(
43
+ req,
44
+ res,
45
+ errorCodes.HTTP_BAD_REQUEST,
46
+ "Only images/videos under 20 MB each are permitted."
47
+ );
48
+ else next();
49
+ });
50
+ };
51
+ module.exports = docUpload;
@@ -6,20 +6,9 @@ const schemas = {
6
6
  .max(20)
7
7
  .regex(/^[a-zA-Z]+$/)
8
8
  .required(),
9
- address: BaseJoi.string().required(),
10
- cityId: BaseJoi.number().required(),
11
- provinceId: BaseJoi.number().required(),
12
- country: BaseJoi.string().required(),
13
- postalCode: BaseJoi.string().required(),
14
9
  email: BaseJoi.string().required(),
15
10
  phoneNumber: BaseJoi.string().required(),
16
- contactPerson: BaseJoi.string().required(),
17
- currency: BaseJoi.string().required(),
18
- charitable_reg_no: BaseJoi.string().required(),
19
- registration_agency: BaseJoi.string().required(),
20
- profileImage: BaseJoi.string().required(),
21
- registeredAt: BaseJoi.string().required(),
22
- subscribedAt: BaseJoi.string().required(),
11
+ contactPerson: BaseJoi.string().optional().allow(null),
23
12
  subscriptionPlanId: BaseJoi.number().required(),
24
13
  churchAdminDetails: BaseJoi.object({
25
14
  firstName: BaseJoi.string().required(),
@@ -30,11 +19,17 @@ const schemas = {
30
19
  }).required(),
31
20
  }),
32
21
  updateChurch: BaseJoi.object({
33
- status: BaseJoi.boolean().required(),
22
+ id: BaseJoi.number().required(),
23
+ name: BaseJoi.string().optional(),
24
+ email: BaseJoi.string().optional(),
25
+ phoneNumber: BaseJoi.string().optional(),
26
+ contactPerson: BaseJoi.string().optional(),
27
+ subscriptionPlanId: BaseJoi.number().optional(),
28
+ status: BaseJoi.boolean().optional(),
34
29
  }),
35
30
  addChurchPermission: BaseJoi.object({
36
31
  churchId: BaseJoi.number().required(),
37
- permissionId: BaseJoi.number().required(),
32
+ permissionId: BaseJoi.array().items(BaseJoi.number().required()),
38
33
  }),
39
34
  getByChurchId: BaseJoi.object({
40
35
  churchId: BaseJoi.number().required(),
@@ -54,14 +49,14 @@ const options = {
54
49
  abortEarly: false,
55
50
  convert: true,
56
51
  allowUnknown: false,
57
- stripUnknown: true,
52
+ stripUnknown: false,
58
53
  },
59
54
  array: {
60
55
  abortEarly: false,
61
56
  convert: true,
62
- allowUnknown: true,
57
+ allowUnknown: false,
63
58
  stripUnknown: {
64
- objects: true,
59
+ objects: false,
65
60
  },
66
61
  },
67
62
  };
@@ -79,7 +74,7 @@ const updateChurch = async (req, res, next) => {
79
74
  const { updateChurch } = schemas;
80
75
  const { basic } = options;
81
76
  try {
82
- await updateChurch.validateAsync({ ...req.params, ...req.query }, basic);
77
+ await updateChurch.validateAsync({ ...req.params, ...req.body }, basic);
83
78
  next();
84
79
  } catch (err) {
85
80
  throwError(req, res, err);
@@ -1,81 +0,0 @@
1
- version: '3'
2
- services:
3
- churchsoln_backend:
4
- container_name: churchsoln_backend
5
- build:
6
- context: ./churchsoln_backend
7
- dockerfile: Dockerfile
8
- restart: always
9
- ports:
10
- - "5009:5009"
11
- depends_on:
12
- - postgres-backend
13
- env_file: ./churchsoln_backend/.env
14
- volumes:
15
- - ./churchsoln_backend/:/app
16
- - image-volume:/app/uploads/images
17
- - document-volume:/app/uploads/documents
18
- - video-volume:/app/uploads/videos
19
- networks:
20
- - app-network
21
- churchsoln_dbms:
22
- container_name: churchsoln_dbms
23
- build:
24
- context: ./churchsoln_dbms
25
- dockerfile: Dockerfile
26
- restart: always
27
- ports:
28
- - "5007:5007"
29
- depends_on:
30
- - postgres
31
- - redis1
32
- env_file: ./churchsoln_dbms/.env
33
- volumes:
34
- - ./churchsoln_dbms/:/app
35
- - image-volume:/app/uploads/images
36
- - document-volume:/app/uploads/documents
37
- - video-volume:/app/uploads/videos
38
- networks:
39
- - app-network
40
-
41
- postgres:
42
- image: postgres:15
43
- container_name: postgres
44
- env_file: ../.env
45
- restart: always
46
- volumes:
47
- - pgdata:/var/lib/postgresql/data
48
- networks:
49
- - app-network
50
-
51
- pgadmin1:
52
- image: dpage/pgadmin4
53
- container_name: pgadmin1
54
- env_file: ../.env
55
- ports:
56
- - "5008:80"
57
- depends_on:
58
- - postgres
59
- networks:
60
- - app-network
61
-
62
- redis1:
63
- image: redis:latest
64
- container_name: redis1
65
- ports:
66
- - "6379:6379"
67
- volumes:
68
- - redis-data:/data
69
- networks:
70
- - app-network
71
-
72
- networks:
73
- app-network:
74
- driver: bridge
75
-
76
- volumes:
77
- pgdata:
78
- redis-data:
79
- image-volume:
80
- document-volume:
81
- video-volume: