@goweekdays/core 2.6.1 → 2.8.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.
- package/CHANGELOG.md +12 -0
- package/dist/index.d.ts +86 -2
- package/dist/index.js +1887 -1035
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2011 -1158
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -491,25 +491,25 @@ function useAuthService() {
|
|
|
491
491
|
}
|
|
492
492
|
|
|
493
493
|
// src/resources/auth/auth.controller.ts
|
|
494
|
-
import
|
|
494
|
+
import Joi36 from "joi";
|
|
495
495
|
import {
|
|
496
|
-
AppError as
|
|
497
|
-
BadRequestError as
|
|
498
|
-
InternalServerError as
|
|
499
|
-
logger as
|
|
496
|
+
AppError as AppError19,
|
|
497
|
+
BadRequestError as BadRequestError43,
|
|
498
|
+
InternalServerError as InternalServerError23,
|
|
499
|
+
logger as logger22
|
|
500
500
|
} from "@goweekdays/utils";
|
|
501
501
|
|
|
502
502
|
// src/resources/verification/verification.service.ts
|
|
503
503
|
import {
|
|
504
504
|
useMailer,
|
|
505
505
|
compileHandlebar,
|
|
506
|
-
logger as
|
|
506
|
+
logger as logger21,
|
|
507
507
|
getDirectory,
|
|
508
|
-
BadRequestError as
|
|
508
|
+
BadRequestError as BadRequestError42,
|
|
509
509
|
NotFoundError as NotFoundError3,
|
|
510
|
-
InternalServerError as
|
|
511
|
-
useAtlas as
|
|
512
|
-
AppError as
|
|
510
|
+
InternalServerError as InternalServerError22,
|
|
511
|
+
useAtlas as useAtlas19,
|
|
512
|
+
AppError as AppError18
|
|
513
513
|
} from "@goweekdays/utils";
|
|
514
514
|
|
|
515
515
|
// src/resources/verification/verification.model.ts
|
|
@@ -532,7 +532,11 @@ var schemaVerification = Joi2.object({
|
|
|
532
532
|
roleName: Joi2.string().optional().allow("", null),
|
|
533
533
|
referralCode: Joi2.string().optional().allow("", null),
|
|
534
534
|
org: Joi2.string().hex().optional().allow("", null),
|
|
535
|
-
orgName: Joi2.string().optional().allow("", null)
|
|
535
|
+
orgName: Joi2.string().optional().allow("", null),
|
|
536
|
+
contact: Joi2.string().optional().allow("", null),
|
|
537
|
+
seats: Joi2.number().optional().allow("", null),
|
|
538
|
+
createdBy: Joi2.string().optional().allow("", null),
|
|
539
|
+
amount: Joi2.number().optional().allow("", null)
|
|
536
540
|
}).optional(),
|
|
537
541
|
expireAt: Joi2.date().optional().allow("", null)
|
|
538
542
|
});
|
|
@@ -914,15 +918,15 @@ var APP_ORG = process.env.APP_ORG || "http://localhost/organizations/create";
|
|
|
914
918
|
|
|
915
919
|
// src/resources/user/user.service.ts
|
|
916
920
|
import {
|
|
917
|
-
AppError as
|
|
918
|
-
BadRequestError as
|
|
919
|
-
InternalServerError as
|
|
921
|
+
AppError as AppError16,
|
|
922
|
+
BadRequestError as BadRequestError40,
|
|
923
|
+
InternalServerError as InternalServerError20,
|
|
920
924
|
NotFoundError as NotFoundError2,
|
|
921
925
|
hashPassword,
|
|
922
|
-
logger as
|
|
923
|
-
makeCacheKey as
|
|
924
|
-
useAtlas as
|
|
925
|
-
useCache as
|
|
926
|
+
logger as logger20,
|
|
927
|
+
makeCacheKey as makeCacheKey14,
|
|
928
|
+
useAtlas as useAtlas18,
|
|
929
|
+
useCache as useCache15,
|
|
926
930
|
useS3
|
|
927
931
|
} from "@goweekdays/utils";
|
|
928
932
|
|
|
@@ -1538,9 +1542,6 @@ function useMemberRepo() {
|
|
|
1538
1542
|
{
|
|
1539
1543
|
$match: query
|
|
1540
1544
|
},
|
|
1541
|
-
{
|
|
1542
|
-
$sort: { _id: -1 }
|
|
1543
|
-
},
|
|
1544
1545
|
{
|
|
1545
1546
|
$limit: limit
|
|
1546
1547
|
},
|
|
@@ -4379,6 +4380,7 @@ import {
|
|
|
4379
4380
|
useCache as useCache10
|
|
4380
4381
|
} from "@goweekdays/utils";
|
|
4381
4382
|
import { ObjectId as ObjectId14 } from "mongodb";
|
|
4383
|
+
import Joi16 from "joi";
|
|
4382
4384
|
function useOrgRepo() {
|
|
4383
4385
|
const db = useAtlas11.getDb();
|
|
4384
4386
|
if (!db) {
|
|
@@ -4554,10 +4556,9 @@ function useOrgRepo() {
|
|
|
4554
4556
|
});
|
|
4555
4557
|
return cached;
|
|
4556
4558
|
}
|
|
4557
|
-
const result = await collection.findOne({
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
}
|
|
4559
|
+
const result = await collection.findOne({
|
|
4560
|
+
name: { $regex: name, $options: "i" }
|
|
4561
|
+
});
|
|
4561
4562
|
setCache(cacheKey, result, 300).then(() => {
|
|
4562
4563
|
logger14.log({
|
|
4563
4564
|
level: "info",
|
|
@@ -4578,6 +4579,42 @@ function useOrgRepo() {
|
|
|
4578
4579
|
}
|
|
4579
4580
|
}
|
|
4580
4581
|
}
|
|
4582
|
+
async function getByEmail(email) {
|
|
4583
|
+
const { error } = Joi16.string().email().required().validate(email);
|
|
4584
|
+
if (error) {
|
|
4585
|
+
throw new BadRequestError21(error.message);
|
|
4586
|
+
}
|
|
4587
|
+
const cacheKey = makeCacheKey9(namespace_collection, { email });
|
|
4588
|
+
try {
|
|
4589
|
+
const cached = await getCache(cacheKey);
|
|
4590
|
+
if (cached) {
|
|
4591
|
+
logger14.log({
|
|
4592
|
+
level: "info",
|
|
4593
|
+
message: `Cache hit for getByEmail organization: ${cacheKey}`
|
|
4594
|
+
});
|
|
4595
|
+
return cached;
|
|
4596
|
+
}
|
|
4597
|
+
const result = await collection.findOne({ email });
|
|
4598
|
+
setCache(cacheKey, result, 300).then(() => {
|
|
4599
|
+
logger14.log({
|
|
4600
|
+
level: "info",
|
|
4601
|
+
message: `Cache set for organization by email: ${cacheKey}`
|
|
4602
|
+
});
|
|
4603
|
+
}).catch((err) => {
|
|
4604
|
+
logger14.log({
|
|
4605
|
+
level: "error",
|
|
4606
|
+
message: `Failed to set cache for organization by email: ${err.message}`
|
|
4607
|
+
});
|
|
4608
|
+
});
|
|
4609
|
+
return result;
|
|
4610
|
+
} catch (error2) {
|
|
4611
|
+
if (error2 instanceof AppError8) {
|
|
4612
|
+
throw error2;
|
|
4613
|
+
} else {
|
|
4614
|
+
throw new InternalServerError11("Failed to get organization.");
|
|
4615
|
+
}
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4581
4618
|
async function updateFieldById({ _id, field, value } = {}, session) {
|
|
4582
4619
|
const allowedFields = ["name", "description"];
|
|
4583
4620
|
if (!allowedFields.includes(field)) {
|
|
@@ -4652,15 +4689,16 @@ function useOrgRepo() {
|
|
|
4652
4689
|
updateFieldById,
|
|
4653
4690
|
deleteById,
|
|
4654
4691
|
getByName,
|
|
4692
|
+
getByEmail,
|
|
4655
4693
|
updateById
|
|
4656
4694
|
};
|
|
4657
4695
|
}
|
|
4658
4696
|
|
|
4659
4697
|
// src/resources/organization/organization.service.ts
|
|
4660
|
-
import { BadRequestError as
|
|
4698
|
+
import { BadRequestError as BadRequestError38, useAtlas as useAtlas17 } from "@goweekdays/utils";
|
|
4661
4699
|
|
|
4662
4700
|
// src/resources/member/member.controller.ts
|
|
4663
|
-
import
|
|
4701
|
+
import Joi19 from "joi";
|
|
4664
4702
|
import { BadRequestError as BadRequestError25 } from "@goweekdays/utils";
|
|
4665
4703
|
|
|
4666
4704
|
// src/resources/member/member.service.ts
|
|
@@ -4700,7 +4738,7 @@ function useRoleService() {
|
|
|
4700
4738
|
}
|
|
4701
4739
|
|
|
4702
4740
|
// src/resources/role/role.controller.ts
|
|
4703
|
-
import
|
|
4741
|
+
import Joi17 from "joi";
|
|
4704
4742
|
import { BadRequestError as BadRequestError23 } from "@goweekdays/utils";
|
|
4705
4743
|
function useRoleController() {
|
|
4706
4744
|
const {
|
|
@@ -4733,12 +4771,12 @@ function useRoleController() {
|
|
|
4733
4771
|
const limit = parseInt(req.query.limit ?? "10");
|
|
4734
4772
|
const app = req.query.app ?? "";
|
|
4735
4773
|
const org = req.query.org ?? "";
|
|
4736
|
-
const validation =
|
|
4737
|
-
search:
|
|
4738
|
-
page:
|
|
4739
|
-
limit:
|
|
4740
|
-
app:
|
|
4741
|
-
org:
|
|
4774
|
+
const validation = Joi17.object({
|
|
4775
|
+
search: Joi17.string().optional().allow("", null),
|
|
4776
|
+
page: Joi17.number().required(),
|
|
4777
|
+
limit: Joi17.number().required(),
|
|
4778
|
+
app: Joi17.string().optional().allow("", null),
|
|
4779
|
+
org: Joi17.string().hex().optional().allow("", null)
|
|
4742
4780
|
});
|
|
4743
4781
|
const { error } = validation.validate({ search, page, limit, app, org });
|
|
4744
4782
|
if (error) {
|
|
@@ -4755,8 +4793,8 @@ function useRoleController() {
|
|
|
4755
4793
|
}
|
|
4756
4794
|
async function getRoleByUserId(req, res, next) {
|
|
4757
4795
|
const userId = req.params.userId;
|
|
4758
|
-
const validation =
|
|
4759
|
-
userId:
|
|
4796
|
+
const validation = Joi17.object({
|
|
4797
|
+
userId: Joi17.string().required()
|
|
4760
4798
|
});
|
|
4761
4799
|
const { error } = validation.validate({ userId });
|
|
4762
4800
|
if (error) {
|
|
@@ -4773,8 +4811,8 @@ function useRoleController() {
|
|
|
4773
4811
|
}
|
|
4774
4812
|
async function getRoleById(req, res, next) {
|
|
4775
4813
|
const _id = req.params.id;
|
|
4776
|
-
const validation =
|
|
4777
|
-
_id:
|
|
4814
|
+
const validation = Joi17.object({
|
|
4815
|
+
_id: Joi17.string().hex().required()
|
|
4778
4816
|
});
|
|
4779
4817
|
const { error } = validation.validate({ _id });
|
|
4780
4818
|
if (error) {
|
|
@@ -4793,10 +4831,10 @@ function useRoleController() {
|
|
|
4793
4831
|
const _id = req.params.id;
|
|
4794
4832
|
const name = req.body.name ?? "";
|
|
4795
4833
|
const permissions = req.body.permissions ?? [];
|
|
4796
|
-
const validation =
|
|
4797
|
-
_id:
|
|
4798
|
-
name:
|
|
4799
|
-
permissions:
|
|
4834
|
+
const validation = Joi17.object({
|
|
4835
|
+
_id: Joi17.string().required(),
|
|
4836
|
+
name: Joi17.string().required(),
|
|
4837
|
+
permissions: Joi17.array().items(Joi17.string()).required()
|
|
4800
4838
|
});
|
|
4801
4839
|
const { error } = validation.validate({ _id, name, permissions });
|
|
4802
4840
|
if (error) {
|
|
@@ -4814,9 +4852,9 @@ function useRoleController() {
|
|
|
4814
4852
|
async function updatePermissionsById(req, res, next) {
|
|
4815
4853
|
const _id = req.params.id;
|
|
4816
4854
|
const permissions = req.body.permissions ?? [];
|
|
4817
|
-
const validation =
|
|
4818
|
-
_id:
|
|
4819
|
-
permissions:
|
|
4855
|
+
const validation = Joi17.object({
|
|
4856
|
+
_id: Joi17.string().required(),
|
|
4857
|
+
permissions: Joi17.array().items(Joi17.string()).required()
|
|
4820
4858
|
});
|
|
4821
4859
|
const { error } = validation.validate({ _id, permissions });
|
|
4822
4860
|
if (error) {
|
|
@@ -4833,8 +4871,8 @@ function useRoleController() {
|
|
|
4833
4871
|
}
|
|
4834
4872
|
async function deleteRole(req, res, next) {
|
|
4835
4873
|
const _id = req.params.id;
|
|
4836
|
-
const validation =
|
|
4837
|
-
_id:
|
|
4874
|
+
const validation = Joi17.object({
|
|
4875
|
+
_id: Joi17.string().required()
|
|
4838
4876
|
});
|
|
4839
4877
|
const { error } = validation.validate({ _id });
|
|
4840
4878
|
if (error) {
|
|
@@ -4861,7 +4899,7 @@ function useRoleController() {
|
|
|
4861
4899
|
}
|
|
4862
4900
|
|
|
4863
4901
|
// src/resources/member/member.service.ts
|
|
4864
|
-
import
|
|
4902
|
+
import Joi18 from "joi";
|
|
4865
4903
|
function useMemberService() {
|
|
4866
4904
|
const { getById: getRoleById } = useRoleRepo();
|
|
4867
4905
|
const {
|
|
@@ -4914,7 +4952,7 @@ function useMemberService() {
|
|
|
4914
4952
|
}
|
|
4915
4953
|
}
|
|
4916
4954
|
async function deleteById(id) {
|
|
4917
|
-
const { error } =
|
|
4955
|
+
const { error } = Joi18.string().hex().required().validate(id);
|
|
4918
4956
|
if (error) {
|
|
4919
4957
|
throw new BadRequestError24(error.message);
|
|
4920
4958
|
}
|
|
@@ -4957,8 +4995,8 @@ function useMemberController() {
|
|
|
4957
4995
|
} = useMemberService();
|
|
4958
4996
|
async function getByUserId(req, res, next) {
|
|
4959
4997
|
const userId = req.params.id;
|
|
4960
|
-
const validation =
|
|
4961
|
-
id:
|
|
4998
|
+
const validation = Joi19.object({
|
|
4999
|
+
id: Joi19.string().hex().required()
|
|
4962
5000
|
});
|
|
4963
5001
|
const { error } = validation.validate({ id: userId });
|
|
4964
5002
|
if (error) {
|
|
@@ -4977,10 +5015,10 @@ function useMemberController() {
|
|
|
4977
5015
|
}
|
|
4978
5016
|
}
|
|
4979
5017
|
async function getByUserType(req, res, next) {
|
|
4980
|
-
const validation =
|
|
4981
|
-
org:
|
|
4982
|
-
user:
|
|
4983
|
-
app:
|
|
5018
|
+
const validation = Joi19.object({
|
|
5019
|
+
org: Joi19.string().hex().optional().allow("", null),
|
|
5020
|
+
user: Joi19.string().hex().required(),
|
|
5021
|
+
app: Joi19.string().required()
|
|
4984
5022
|
});
|
|
4985
5023
|
const { error } = validation.validate({ ...req.params, ...req.query });
|
|
4986
5024
|
if (error) {
|
|
@@ -5002,10 +5040,10 @@ function useMemberController() {
|
|
|
5002
5040
|
}
|
|
5003
5041
|
}
|
|
5004
5042
|
async function getByApp(req, res, next) {
|
|
5005
|
-
const validation =
|
|
5006
|
-
org:
|
|
5007
|
-
user:
|
|
5008
|
-
app:
|
|
5043
|
+
const validation = Joi19.object({
|
|
5044
|
+
org: Joi19.string().hex().optional().allow("", null),
|
|
5045
|
+
user: Joi19.string().hex().required(),
|
|
5046
|
+
app: Joi19.string().required()
|
|
5009
5047
|
});
|
|
5010
5048
|
const app = req.params.app ?? "";
|
|
5011
5049
|
const org = req.query.org;
|
|
@@ -5034,14 +5072,14 @@ function useMemberController() {
|
|
|
5034
5072
|
const org = req.query.org ?? "";
|
|
5035
5073
|
const app = req.params.app ?? "";
|
|
5036
5074
|
const status = req.query.status ?? "active";
|
|
5037
|
-
const validation =
|
|
5038
|
-
limit:
|
|
5039
|
-
search:
|
|
5040
|
-
page:
|
|
5041
|
-
user:
|
|
5042
|
-
org:
|
|
5043
|
-
app:
|
|
5044
|
-
status:
|
|
5075
|
+
const validation = Joi19.object({
|
|
5076
|
+
limit: Joi19.number().min(10).max(50).required(),
|
|
5077
|
+
search: Joi19.string().optional().allow("", null),
|
|
5078
|
+
page: Joi19.number().required(),
|
|
5079
|
+
user: Joi19.string().hex().optional().allow("", null),
|
|
5080
|
+
org: Joi19.string().hex().optional().allow("", null),
|
|
5081
|
+
app: Joi19.string().required(),
|
|
5082
|
+
status: Joi19.string().required()
|
|
5045
5083
|
});
|
|
5046
5084
|
const { error } = validation.validate({
|
|
5047
5085
|
search,
|
|
@@ -5078,12 +5116,12 @@ function useMemberController() {
|
|
|
5078
5116
|
const page = Number(req.query.page) ?? 1;
|
|
5079
5117
|
const user = req.params.user ?? "";
|
|
5080
5118
|
const app = req.params.app ?? "";
|
|
5081
|
-
const validation =
|
|
5082
|
-
limit:
|
|
5083
|
-
search:
|
|
5084
|
-
page:
|
|
5085
|
-
app:
|
|
5086
|
-
user:
|
|
5119
|
+
const validation = Joi19.object({
|
|
5120
|
+
limit: Joi19.number().min(10).max(200).allow(null, ""),
|
|
5121
|
+
search: Joi19.string().optional().allow("", null),
|
|
5122
|
+
page: Joi19.number().optional().allow(null, ""),
|
|
5123
|
+
app: Joi19.string().required(),
|
|
5124
|
+
user: Joi19.string().hex().required()
|
|
5087
5125
|
});
|
|
5088
5126
|
const { error } = validation.validate({
|
|
5089
5127
|
search,
|
|
@@ -5115,11 +5153,11 @@ function useMemberController() {
|
|
|
5115
5153
|
const search = req.query.search ?? "";
|
|
5116
5154
|
const page = Number(req.query.page) ?? 1;
|
|
5117
5155
|
const user = req.query.user ?? "";
|
|
5118
|
-
const validation =
|
|
5119
|
-
limit:
|
|
5120
|
-
search:
|
|
5121
|
-
page:
|
|
5122
|
-
user:
|
|
5156
|
+
const validation = Joi19.object({
|
|
5157
|
+
limit: Joi19.number().min(10).max(50).required(),
|
|
5158
|
+
search: Joi19.string().optional().allow("", null),
|
|
5159
|
+
page: Joi19.number().required(),
|
|
5160
|
+
user: Joi19.string().hex().optional().allow("", null)
|
|
5123
5161
|
});
|
|
5124
5162
|
const { error } = validation.validate({
|
|
5125
5163
|
search,
|
|
@@ -5144,9 +5182,9 @@ function useMemberController() {
|
|
|
5144
5182
|
}
|
|
5145
5183
|
}
|
|
5146
5184
|
async function updateStatusByUserId(req, res, next) {
|
|
5147
|
-
const validation =
|
|
5148
|
-
id:
|
|
5149
|
-
status:
|
|
5185
|
+
const validation = Joi19.object({
|
|
5186
|
+
id: Joi19.string().hex().required(),
|
|
5187
|
+
status: Joi19.string().valid("active", "suspended", "deleted").required()
|
|
5150
5188
|
});
|
|
5151
5189
|
const { error } = validation.validate(req.params);
|
|
5152
5190
|
if (error) {
|
|
@@ -5195,7 +5233,7 @@ function useMemberController() {
|
|
|
5195
5233
|
async function deleteById(req, res, next) {
|
|
5196
5234
|
const payload = req.body;
|
|
5197
5235
|
const _id = req.params.id ?? "";
|
|
5198
|
-
const { error } =
|
|
5236
|
+
const { error } = Joi19.string().hex().required().validate(_id);
|
|
5199
5237
|
if (error) {
|
|
5200
5238
|
next(new BadRequestError25(error.message));
|
|
5201
5239
|
return;
|
|
@@ -5223,30 +5261,30 @@ function useMemberController() {
|
|
|
5223
5261
|
|
|
5224
5262
|
// src/resources/subscription/subscription.model.ts
|
|
5225
5263
|
import { BadRequestError as BadRequestError26 } from "@goweekdays/utils";
|
|
5226
|
-
import
|
|
5264
|
+
import Joi20 from "joi";
|
|
5227
5265
|
import { ObjectId as ObjectId15 } from "mongodb";
|
|
5228
5266
|
var schema2 = {
|
|
5229
|
-
seats:
|
|
5230
|
-
paidSeats:
|
|
5231
|
-
amount:
|
|
5232
|
-
promoCode:
|
|
5233
|
-
nextBillingDate:
|
|
5267
|
+
seats: Joi20.number().integer().min(1).required(),
|
|
5268
|
+
paidSeats: Joi20.number().integer().min(0).required(),
|
|
5269
|
+
amount: Joi20.number().positive().required(),
|
|
5270
|
+
promoCode: Joi20.string().optional().allow("", null),
|
|
5271
|
+
nextBillingDate: Joi20.date().optional().allow("", null)
|
|
5234
5272
|
};
|
|
5235
|
-
var schemaSubscription =
|
|
5273
|
+
var schemaSubscription = Joi20.object({
|
|
5236
5274
|
...schema2,
|
|
5237
|
-
org:
|
|
5238
|
-
currency:
|
|
5239
|
-
billingCycle:
|
|
5275
|
+
org: Joi20.string().hex().length(24).required(),
|
|
5276
|
+
currency: Joi20.string().length(3).required(),
|
|
5277
|
+
billingCycle: Joi20.string().valid("monthly", "yearly").required()
|
|
5240
5278
|
});
|
|
5241
|
-
var schemaSubscriptionUpdate =
|
|
5279
|
+
var schemaSubscriptionUpdate = Joi20.object({
|
|
5242
5280
|
...schema2,
|
|
5243
|
-
status:
|
|
5281
|
+
status: Joi20.string().optional().allow("", null)
|
|
5244
5282
|
});
|
|
5245
|
-
var schemaSubscriptionSeats =
|
|
5246
|
-
id:
|
|
5247
|
-
seats:
|
|
5248
|
-
amount:
|
|
5249
|
-
user:
|
|
5283
|
+
var schemaSubscriptionSeats = Joi20.object({
|
|
5284
|
+
id: Joi20.string().hex().length(24).required(),
|
|
5285
|
+
seats: Joi20.number().integer().min(1).required(),
|
|
5286
|
+
amount: Joi20.number().positive().optional().allow(null, 0),
|
|
5287
|
+
user: Joi20.string().hex().length(24).required()
|
|
5250
5288
|
});
|
|
5251
5289
|
function modelSubscription(data) {
|
|
5252
5290
|
const { error } = schemaSubscription.validate(data);
|
|
@@ -5292,7 +5330,7 @@ import {
|
|
|
5292
5330
|
useAtlas as useAtlas12,
|
|
5293
5331
|
useCache as useCache11
|
|
5294
5332
|
} from "@goweekdays/utils";
|
|
5295
|
-
import
|
|
5333
|
+
import Joi21 from "joi";
|
|
5296
5334
|
import { ObjectId as ObjectId16 } from "mongodb";
|
|
5297
5335
|
function useSubscriptionRepo() {
|
|
5298
5336
|
const db = useAtlas12.getDb();
|
|
@@ -5402,7 +5440,7 @@ function useSubscriptionRepo() {
|
|
|
5402
5440
|
}
|
|
5403
5441
|
}
|
|
5404
5442
|
async function getByOrg(org) {
|
|
5405
|
-
const { error } =
|
|
5443
|
+
const { error } = Joi21.string().hex().length(24).required().validate(org);
|
|
5406
5444
|
if (error) {
|
|
5407
5445
|
throw new Error(`Invalid org ID: ${error.message}`);
|
|
5408
5446
|
}
|
|
@@ -5441,7 +5479,7 @@ function useSubscriptionRepo() {
|
|
|
5441
5479
|
}
|
|
5442
5480
|
}
|
|
5443
5481
|
async function getById(_id) {
|
|
5444
|
-
const { error } =
|
|
5482
|
+
const { error } = Joi21.string().hex().length(24).required().validate(_id);
|
|
5445
5483
|
if (error) {
|
|
5446
5484
|
throw new Error(`Invalid subscription ID: ${error.message}`);
|
|
5447
5485
|
}
|
|
@@ -5480,7 +5518,7 @@ function useSubscriptionRepo() {
|
|
|
5480
5518
|
}
|
|
5481
5519
|
}
|
|
5482
5520
|
async function deleteById(_id) {
|
|
5483
|
-
const { error } =
|
|
5521
|
+
const { error } = Joi21.string().hex().length(24).required().validate(_id);
|
|
5484
5522
|
if (error) {
|
|
5485
5523
|
throw new Error(`Invalid subscription ID: ${error.message}`);
|
|
5486
5524
|
}
|
|
@@ -5507,7 +5545,7 @@ function useSubscriptionRepo() {
|
|
|
5507
5545
|
}
|
|
5508
5546
|
}
|
|
5509
5547
|
async function updateById(_id, options, session) {
|
|
5510
|
-
const { error: errorId } =
|
|
5548
|
+
const { error: errorId } = Joi21.string().hex().length(24).required().validate(_id);
|
|
5511
5549
|
if (errorId) {
|
|
5512
5550
|
throw new Error(`Invalid subscription ID: ${errorId.message}`);
|
|
5513
5551
|
}
|
|
@@ -5552,7 +5590,7 @@ function useSubscriptionRepo() {
|
|
|
5552
5590
|
}
|
|
5553
5591
|
|
|
5554
5592
|
// src/resources/subscription/subscription.controller.ts
|
|
5555
|
-
import
|
|
5593
|
+
import Joi23 from "joi";
|
|
5556
5594
|
import { BadRequestError as BadRequestError30 } from "@goweekdays/utils";
|
|
5557
5595
|
|
|
5558
5596
|
// src/resources/subscription/subscription.service.ts
|
|
@@ -5571,16 +5609,16 @@ import {
|
|
|
5571
5609
|
|
|
5572
5610
|
// src/resources/subscription/subscription.transaction.model.ts
|
|
5573
5611
|
import { BadRequestError as BadRequestError28 } from "@goweekdays/utils";
|
|
5574
|
-
import
|
|
5612
|
+
import Joi22 from "joi";
|
|
5575
5613
|
import { ObjectId as ObjectId17 } from "mongodb";
|
|
5576
|
-
var schemaSubscriptionTransaction =
|
|
5577
|
-
subscription:
|
|
5578
|
-
amount:
|
|
5579
|
-
currency:
|
|
5580
|
-
type:
|
|
5581
|
-
description:
|
|
5582
|
-
createdBy:
|
|
5583
|
-
createdByName:
|
|
5614
|
+
var schemaSubscriptionTransaction = Joi22.object({
|
|
5615
|
+
subscription: Joi22.string().hex().length(24).required(),
|
|
5616
|
+
amount: Joi22.number().positive().required(),
|
|
5617
|
+
currency: Joi22.string().length(3).required(),
|
|
5618
|
+
type: Joi22.string().valid("initiate", "add-seat", "remove-seat", "renewal").required(),
|
|
5619
|
+
description: Joi22.string().max(255).optional().allow("", null),
|
|
5620
|
+
createdBy: Joi22.string().hex().length(24).required(),
|
|
5621
|
+
createdByName: Joi22.string().optional().allow("", null)
|
|
5584
5622
|
});
|
|
5585
5623
|
function modelSubscriptionTransaction(data) {
|
|
5586
5624
|
const { error } = schemaSubscriptionTransaction.validate(data);
|
|
@@ -5836,10 +5874,10 @@ function useSubscriptionController() {
|
|
|
5836
5874
|
} = useSubscriptionRepo();
|
|
5837
5875
|
const { updateSeats: _updateSeats } = useSubscriptionService();
|
|
5838
5876
|
async function getAll(req, res, next) {
|
|
5839
|
-
const validation =
|
|
5840
|
-
page:
|
|
5841
|
-
limit:
|
|
5842
|
-
status:
|
|
5877
|
+
const validation = Joi23.object({
|
|
5878
|
+
page: Joi23.number().min(1).max(100).optional().allow(null, "").default(1),
|
|
5879
|
+
limit: Joi23.number().min(1).max(100).optional().allow(null, "").default(10),
|
|
5880
|
+
status: Joi23.string().valid("active", "suspended").optional().default("active")
|
|
5843
5881
|
});
|
|
5844
5882
|
const query = req.query;
|
|
5845
5883
|
const { error, value } = validation.validate(query);
|
|
@@ -5857,8 +5895,8 @@ function useSubscriptionController() {
|
|
|
5857
5895
|
}
|
|
5858
5896
|
async function getById(req, res, next) {
|
|
5859
5897
|
const id = req.params.id ?? "";
|
|
5860
|
-
const validation =
|
|
5861
|
-
id:
|
|
5898
|
+
const validation = Joi23.object({
|
|
5899
|
+
id: Joi23.string().hex().length(24).required()
|
|
5862
5900
|
});
|
|
5863
5901
|
const { error, value } = validation.validate({ id });
|
|
5864
5902
|
if (error) {
|
|
@@ -5875,8 +5913,8 @@ function useSubscriptionController() {
|
|
|
5875
5913
|
}
|
|
5876
5914
|
async function getByOrg(req, res, next) {
|
|
5877
5915
|
const org = req.params.org ?? "";
|
|
5878
|
-
const validation =
|
|
5879
|
-
org:
|
|
5916
|
+
const validation = Joi23.object({
|
|
5917
|
+
org: Joi23.string().hex().length(24).required()
|
|
5880
5918
|
});
|
|
5881
5919
|
const { error, value } = validation.validate({ org });
|
|
5882
5920
|
if (error) {
|
|
@@ -5919,15 +5957,15 @@ function useSubscriptionController() {
|
|
|
5919
5957
|
}
|
|
5920
5958
|
|
|
5921
5959
|
// src/resources/subscription/subscription.transaction.controller.ts
|
|
5922
|
-
import
|
|
5960
|
+
import Joi24 from "joi";
|
|
5923
5961
|
import { BadRequestError as BadRequestError31 } from "@goweekdays/utils";
|
|
5924
5962
|
function useSubscriptionTransactionController() {
|
|
5925
5963
|
const { getAll: _getAll } = useSubscriptionTransactionRepo();
|
|
5926
5964
|
async function getAll(req, res, next) {
|
|
5927
|
-
const validation =
|
|
5928
|
-
id:
|
|
5929
|
-
page:
|
|
5930
|
-
limit:
|
|
5965
|
+
const validation = Joi24.object({
|
|
5966
|
+
id: Joi24.string().hex().length(24).required(),
|
|
5967
|
+
page: Joi24.number().min(1).max(100).optional().allow(null, "").default(1),
|
|
5968
|
+
limit: Joi24.number().min(1).max(100).optional().allow(null, "").default(10)
|
|
5931
5969
|
});
|
|
5932
5970
|
const query = req.query;
|
|
5933
5971
|
const id = req.params.id ?? "";
|
|
@@ -5950,16 +5988,16 @@ function useSubscriptionTransactionController() {
|
|
|
5950
5988
|
}
|
|
5951
5989
|
|
|
5952
5990
|
// src/resources/plan/plan.model.ts
|
|
5953
|
-
import
|
|
5991
|
+
import Joi25 from "joi";
|
|
5954
5992
|
var currencies = ["USD", "PHP"];
|
|
5955
|
-
var schemaPlan =
|
|
5956
|
-
name:
|
|
5957
|
-
description:
|
|
5958
|
-
features:
|
|
5959
|
-
price:
|
|
5960
|
-
currency:
|
|
5961
|
-
default:
|
|
5962
|
-
billingCycle:
|
|
5993
|
+
var schemaPlan = Joi25.object({
|
|
5994
|
+
name: Joi25.string().min(3).max(100).required(),
|
|
5995
|
+
description: Joi25.string().max(255).optional().allow("", null),
|
|
5996
|
+
features: Joi25.array().items(Joi25.string().max(100)).optional(),
|
|
5997
|
+
price: Joi25.number().positive().required(),
|
|
5998
|
+
currency: Joi25.string().length(3).allow(...currencies).required(),
|
|
5999
|
+
default: Joi25.boolean().optional().allow(null, ""),
|
|
6000
|
+
billingCycle: Joi25.string().valid("monthly", "yearly").required()
|
|
5963
6001
|
});
|
|
5964
6002
|
function modelPlan(data) {
|
|
5965
6003
|
const { error } = schemaPlan.validate(data);
|
|
@@ -5992,7 +6030,7 @@ import {
|
|
|
5992
6030
|
useAtlas as useAtlas15,
|
|
5993
6031
|
useCache as useCache13
|
|
5994
6032
|
} from "@goweekdays/utils";
|
|
5995
|
-
import
|
|
6033
|
+
import Joi26 from "joi";
|
|
5996
6034
|
import { ObjectId as ObjectId19 } from "mongodb";
|
|
5997
6035
|
function usePlanRepo() {
|
|
5998
6036
|
const db = useAtlas15.getDb();
|
|
@@ -6112,7 +6150,7 @@ function usePlanRepo() {
|
|
|
6112
6150
|
}
|
|
6113
6151
|
}
|
|
6114
6152
|
async function getById(_id) {
|
|
6115
|
-
const { error } =
|
|
6153
|
+
const { error } = Joi26.string().hex().length(24).required().validate(_id);
|
|
6116
6154
|
if (error) {
|
|
6117
6155
|
throw new Error(`Invalid plan ID: ${error.message}`);
|
|
6118
6156
|
}
|
|
@@ -6181,7 +6219,7 @@ function usePlanRepo() {
|
|
|
6181
6219
|
}
|
|
6182
6220
|
}
|
|
6183
6221
|
async function deleteById(_id) {
|
|
6184
|
-
const { error } =
|
|
6222
|
+
const { error } = Joi26.string().hex().length(24).required().validate(_id);
|
|
6185
6223
|
if (error) {
|
|
6186
6224
|
throw new Error(`Invalid plan ID: ${error.message}`);
|
|
6187
6225
|
}
|
|
@@ -6240,7 +6278,7 @@ function usePlanService() {
|
|
|
6240
6278
|
}
|
|
6241
6279
|
|
|
6242
6280
|
// src/resources/plan/plan.controller.ts
|
|
6243
|
-
import
|
|
6281
|
+
import Joi27 from "joi";
|
|
6244
6282
|
import { BadRequestError as BadRequestError33 } from "@goweekdays/utils";
|
|
6245
6283
|
function usePlanController() {
|
|
6246
6284
|
const {
|
|
@@ -6252,13 +6290,13 @@ function usePlanController() {
|
|
|
6252
6290
|
} = usePlanRepo();
|
|
6253
6291
|
async function add(req, res, next) {
|
|
6254
6292
|
const value = req.body;
|
|
6255
|
-
const validation =
|
|
6256
|
-
name:
|
|
6257
|
-
description:
|
|
6258
|
-
features:
|
|
6259
|
-
price:
|
|
6260
|
-
currency:
|
|
6261
|
-
billingCycle:
|
|
6293
|
+
const validation = Joi27.object({
|
|
6294
|
+
name: Joi27.string().min(3).max(100).required(),
|
|
6295
|
+
description: Joi27.string().max(255).optional().allow("", null),
|
|
6296
|
+
features: Joi27.array().items(Joi27.string().max(100)).optional(),
|
|
6297
|
+
price: Joi27.number().positive().required(),
|
|
6298
|
+
currency: Joi27.string().length(3).required(),
|
|
6299
|
+
billingCycle: Joi27.string().valid("monthly", "yearly").required()
|
|
6262
6300
|
});
|
|
6263
6301
|
const { error } = validation.validate(value);
|
|
6264
6302
|
if (error) {
|
|
@@ -6278,11 +6316,11 @@ function usePlanController() {
|
|
|
6278
6316
|
const search = req.query.search ?? "";
|
|
6279
6317
|
const page = Number(req.query.page) ?? 1;
|
|
6280
6318
|
const limit = Number(req.query.limit) ?? 10;
|
|
6281
|
-
const validation =
|
|
6282
|
-
status:
|
|
6283
|
-
search:
|
|
6284
|
-
page:
|
|
6285
|
-
limit:
|
|
6319
|
+
const validation = Joi27.object({
|
|
6320
|
+
status: Joi27.string().required(),
|
|
6321
|
+
search: Joi27.string().optional().allow("", null),
|
|
6322
|
+
page: Joi27.number().required(),
|
|
6323
|
+
limit: Joi27.number().required()
|
|
6286
6324
|
});
|
|
6287
6325
|
const { error } = validation.validate({ status, search, page, limit });
|
|
6288
6326
|
if (error) {
|
|
@@ -6299,7 +6337,7 @@ function usePlanController() {
|
|
|
6299
6337
|
}
|
|
6300
6338
|
async function getById(req, res, next) {
|
|
6301
6339
|
const id = req.params.id;
|
|
6302
|
-
const validation =
|
|
6340
|
+
const validation = Joi27.string().hex().length(24).required();
|
|
6303
6341
|
const { error } = validation.validate(id);
|
|
6304
6342
|
if (error) {
|
|
6305
6343
|
next(new BadRequestError33(error.message));
|
|
@@ -6315,7 +6353,7 @@ function usePlanController() {
|
|
|
6315
6353
|
}
|
|
6316
6354
|
async function deleteById(req, res, next) {
|
|
6317
6355
|
const id = req.params.id;
|
|
6318
|
-
const validation =
|
|
6356
|
+
const validation = Joi27.string().hex().length(24).required();
|
|
6319
6357
|
const { error } = validation.validate(id);
|
|
6320
6358
|
if (error) {
|
|
6321
6359
|
next(new BadRequestError33(error.message));
|
|
@@ -6357,6 +6395,7 @@ import {
|
|
|
6357
6395
|
} from "@paypal/paypal-server-sdk";
|
|
6358
6396
|
import crypto3 from "crypto";
|
|
6359
6397
|
import crc32 from "buffer-crc32";
|
|
6398
|
+
import { logger as logger18 } from "@goweekdays/utils";
|
|
6360
6399
|
var certCache = /* @__PURE__ */ new Map();
|
|
6361
6400
|
function usePaypalService() {
|
|
6362
6401
|
const paypalClient = new Client({
|
|
@@ -6420,11 +6459,14 @@ function usePaypalService() {
|
|
|
6420
6459
|
const certUrl = headers["paypal-cert-url"];
|
|
6421
6460
|
const transmissionSig = headers["paypal-transmission-sig"];
|
|
6422
6461
|
if (!transmissionId || !timeStamp || !certUrl || !transmissionSig) {
|
|
6462
|
+
logger18.log({
|
|
6463
|
+
level: "error",
|
|
6464
|
+
message: "Missing required PayPal webhook headers"
|
|
6465
|
+
});
|
|
6423
6466
|
return false;
|
|
6424
6467
|
}
|
|
6425
|
-
const
|
|
6426
|
-
const
|
|
6427
|
-
const message = `${transmissionId}|${timeStamp}|${webhookId}|${crcValue}`;
|
|
6468
|
+
const crc = parseInt("0x" + crc32(rawBody).toString("hex"));
|
|
6469
|
+
const message = `${transmissionId}|${timeStamp}|${webhookId}|${crc}`;
|
|
6428
6470
|
try {
|
|
6429
6471
|
const certPem = await downloadAndCacheCert(certUrl);
|
|
6430
6472
|
const signatureBuffer = Buffer.from(transmissionSig, "base64");
|
|
@@ -6432,7 +6474,10 @@ function usePaypalService() {
|
|
|
6432
6474
|
verifier.update(message);
|
|
6433
6475
|
return verifier.verify(certPem, signatureBuffer);
|
|
6434
6476
|
} catch (error) {
|
|
6435
|
-
|
|
6477
|
+
logger18.log({
|
|
6478
|
+
level: "error",
|
|
6479
|
+
message: `PayPal webhook verification error: ${error}`
|
|
6480
|
+
});
|
|
6436
6481
|
throw new Error("Failed to verify PayPal webhook signature.");
|
|
6437
6482
|
}
|
|
6438
6483
|
}
|
|
@@ -6444,330 +6489,1077 @@ function usePaypalService() {
|
|
|
6444
6489
|
}
|
|
6445
6490
|
|
|
6446
6491
|
// src/resources/organization/organization.service.ts
|
|
6447
|
-
|
|
6448
|
-
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6492
|
+
import Joi32 from "joi";
|
|
6493
|
+
|
|
6494
|
+
// src/resources/verification/verification.controller.ts
|
|
6495
|
+
import {
|
|
6496
|
+
AppError as AppError14,
|
|
6497
|
+
BadRequestError as BadRequestError34,
|
|
6498
|
+
InternalServerError as InternalServerError18
|
|
6499
|
+
} from "@goweekdays/utils";
|
|
6500
|
+
import Joi28 from "joi";
|
|
6501
|
+
function useVerificationController() {
|
|
6502
|
+
const {
|
|
6503
|
+
createUserInvite: _createUserInvite,
|
|
6504
|
+
createForgetPassword: _createForgetPassword,
|
|
6505
|
+
cancelUserInvitation: _cancelUserInvitation,
|
|
6506
|
+
verify: _verify,
|
|
6507
|
+
inviteMember: _inviteMember,
|
|
6508
|
+
signUp: _signUp,
|
|
6509
|
+
cancelInviteMember: _cancelInviteMember,
|
|
6510
|
+
forgetPassword: _forgetPassword,
|
|
6511
|
+
orgSetupFee: _orgSetupFee
|
|
6512
|
+
} = useVerificationService();
|
|
6513
|
+
const { getVerifications: _getVerifications } = useVerificationRepo();
|
|
6514
|
+
async function createUserInvite(req, res, next) {
|
|
6515
|
+
const validation = Joi28.object({
|
|
6516
|
+
email: Joi28.string().email().required(),
|
|
6517
|
+
app: Joi28.string().required(),
|
|
6518
|
+
role: Joi28.string().hex().required(),
|
|
6519
|
+
roleName: Joi28.string().required(),
|
|
6520
|
+
org: Joi28.string().hex().optional().optional().allow("", null),
|
|
6521
|
+
orgName: Joi28.string().optional().optional().allow("", null)
|
|
6522
|
+
});
|
|
6523
|
+
const { error } = validation.validate(req.body);
|
|
6459
6524
|
if (error) {
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
const session = useAtlas16.getClient()?.startSession();
|
|
6463
|
-
if (!session) {
|
|
6464
|
-
throw new BadRequestError34("Unable to start database session.");
|
|
6525
|
+
next(new BadRequestError34(error.message));
|
|
6526
|
+
return;
|
|
6465
6527
|
}
|
|
6528
|
+
const email = req.body.email ?? "";
|
|
6529
|
+
const app = req.body.app ?? "";
|
|
6530
|
+
const role = req.body.role ?? "";
|
|
6531
|
+
const roleName = req.body.roleName ?? "";
|
|
6532
|
+
const org = req.body.org ?? "";
|
|
6533
|
+
const orgName = req.body.orgName ?? "";
|
|
6466
6534
|
try {
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
{
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
);
|
|
6477
|
-
const plan = await getDefault();
|
|
6478
|
-
if (!plan) {
|
|
6479
|
-
throw new BadRequestError34(
|
|
6480
|
-
"Failed to create organization, plan not found."
|
|
6481
|
-
);
|
|
6482
|
-
}
|
|
6483
|
-
const currentDate = /* @__PURE__ */ new Date();
|
|
6484
|
-
const nextBillingDate = new Date(currentDate);
|
|
6485
|
-
nextBillingDate.setMonth(currentDate.getMonth() + 1);
|
|
6486
|
-
const amount = plan.price * value.seats;
|
|
6487
|
-
const subscriptionId = await addSubscription(
|
|
6488
|
-
{
|
|
6489
|
-
amount,
|
|
6490
|
-
org: String(org),
|
|
6491
|
-
seats: value.seats,
|
|
6492
|
-
paidSeats: value.seats,
|
|
6493
|
-
currency: plan.currency,
|
|
6494
|
-
billingCycle: plan.billingCycle,
|
|
6495
|
-
nextBillingDate
|
|
6496
|
-
},
|
|
6497
|
-
session
|
|
6498
|
-
);
|
|
6499
|
-
const createdBy = String(value.createdBy);
|
|
6500
|
-
const user = await getUserById(createdBy);
|
|
6501
|
-
if (!user) {
|
|
6502
|
-
throw new BadRequestError34("User is required to create org member.");
|
|
6503
|
-
}
|
|
6504
|
-
await addSubscriptionTransaction(
|
|
6505
|
-
{
|
|
6506
|
-
subscription: subscriptionId,
|
|
6507
|
-
type: "initiate",
|
|
6508
|
-
amount,
|
|
6509
|
-
currency: plan.currency,
|
|
6510
|
-
description: "Initial subscription transaction",
|
|
6511
|
-
createdBy: value.createdBy,
|
|
6512
|
-
createdByName: `${user.firstName} ${user.lastName}`
|
|
6513
|
-
},
|
|
6514
|
-
session
|
|
6515
|
-
);
|
|
6516
|
-
const allPermissions = await getAllPermission({
|
|
6517
|
-
app: "org",
|
|
6518
|
-
limit: 100
|
|
6519
|
-
});
|
|
6520
|
-
let permissions = [];
|
|
6521
|
-
if (allPermissions && allPermissions.items && allPermissions.items.length) {
|
|
6522
|
-
permissions = allPermissions.items.map((perm) => perm.key);
|
|
6523
|
-
}
|
|
6524
|
-
if (permissions.length === 0) {
|
|
6525
|
-
throw new Error("No permissions found for the organization type.");
|
|
6526
|
-
}
|
|
6527
|
-
const roleData = {
|
|
6528
|
-
org: String(org),
|
|
6529
|
-
name: "Owner",
|
|
6530
|
-
description: "Owner of the organization",
|
|
6531
|
-
permissions,
|
|
6532
|
-
createdBy,
|
|
6533
|
-
app: "org"
|
|
6534
|
-
};
|
|
6535
|
-
const role = await addRole(roleData, session);
|
|
6536
|
-
if (!role) {
|
|
6537
|
-
throw new BadRequestError34("Role is required to create org member.");
|
|
6538
|
-
}
|
|
6539
|
-
await addMember(
|
|
6540
|
-
{
|
|
6541
|
-
role: String(role),
|
|
6542
|
-
roleName: roleData.name,
|
|
6543
|
-
org: String(org),
|
|
6544
|
-
orgName: value.name,
|
|
6545
|
-
name: `${user.firstName} ${user.lastName}`,
|
|
6546
|
-
user: createdBy,
|
|
6547
|
-
app: "org"
|
|
6548
|
-
},
|
|
6549
|
-
session
|
|
6550
|
-
);
|
|
6551
|
-
const order = await addPaypalOrder({
|
|
6552
|
-
amount,
|
|
6553
|
-
currency: plan.currency,
|
|
6554
|
-
customId: subscriptionId,
|
|
6555
|
-
returnUrl: `${APP_ORG}/organizations/success`,
|
|
6556
|
-
cancelUrl: `${APP_ORG}/organizations/cancel`,
|
|
6557
|
-
action: "pay"
|
|
6535
|
+
await _createUserInvite({
|
|
6536
|
+
email,
|
|
6537
|
+
metadata: {
|
|
6538
|
+
app,
|
|
6539
|
+
role,
|
|
6540
|
+
roleName,
|
|
6541
|
+
org,
|
|
6542
|
+
orgName
|
|
6543
|
+
}
|
|
6558
6544
|
});
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
(link) => link.rel === "approve"
|
|
6562
|
-
);
|
|
6563
|
-
return {
|
|
6564
|
-
org: String(org),
|
|
6565
|
-
paypalOrderLink: paypalOrderLink ? paypalOrderLink.href : ""
|
|
6566
|
-
};
|
|
6545
|
+
res.json({ message: "Successfully invited user." });
|
|
6546
|
+
return;
|
|
6567
6547
|
} catch (error2) {
|
|
6568
|
-
|
|
6569
|
-
throw error2;
|
|
6570
|
-
} finally {
|
|
6571
|
-
await session?.endSession();
|
|
6548
|
+
next(error2);
|
|
6572
6549
|
}
|
|
6573
6550
|
}
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
}
|
|
6578
|
-
|
|
6579
|
-
// src/resources/organization/organization.controller.ts
|
|
6580
|
-
import { BadRequestError as BadRequestError35 } from "@goweekdays/utils";
|
|
6581
|
-
import Joi27 from "joi";
|
|
6582
|
-
function useOrgController() {
|
|
6583
|
-
const { add: _add } = useOrgService();
|
|
6584
|
-
const { getOrgsByMembership } = useMemberRepo();
|
|
6585
|
-
const {
|
|
6586
|
-
getByName: _getByName,
|
|
6587
|
-
getAll: getAllOrg,
|
|
6588
|
-
getById: _getById,
|
|
6589
|
-
updateById: _updateById
|
|
6590
|
-
} = useOrgRepo();
|
|
6591
|
-
async function add(req, res, next) {
|
|
6592
|
-
const value = req.body;
|
|
6593
|
-
const { error } = schemaOrgAdd.validate(value);
|
|
6551
|
+
async function createForgetPassword(req, res, next) {
|
|
6552
|
+
const email = req.body.email || "";
|
|
6553
|
+
const validation = Joi28.string().email().required();
|
|
6554
|
+
const { error } = validation.validate(email);
|
|
6594
6555
|
if (error) {
|
|
6595
|
-
next(new
|
|
6556
|
+
next(new BadRequestError34(error.message));
|
|
6596
6557
|
return;
|
|
6597
6558
|
}
|
|
6598
6559
|
try {
|
|
6599
|
-
|
|
6560
|
+
await _createForgetPassword(email);
|
|
6600
6561
|
res.json({
|
|
6601
|
-
message: "
|
|
6602
|
-
data
|
|
6562
|
+
message: "Check your email to verify it before resetting your password."
|
|
6603
6563
|
});
|
|
6604
6564
|
return;
|
|
6605
6565
|
} catch (error2) {
|
|
6606
|
-
|
|
6566
|
+
if (error2 instanceof AppError14) {
|
|
6567
|
+
next(error2);
|
|
6568
|
+
} else {
|
|
6569
|
+
next(new InternalServerError18("An unexpected error occurred"));
|
|
6570
|
+
}
|
|
6607
6571
|
}
|
|
6608
6572
|
}
|
|
6609
|
-
async function
|
|
6610
|
-
const
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6573
|
+
async function getVerifications(req, res, next) {
|
|
6574
|
+
const validation = Joi28.object({
|
|
6575
|
+
status: Joi28.string().required(),
|
|
6576
|
+
search: Joi28.string().optional().allow("", null),
|
|
6577
|
+
page: Joi28.number().required(),
|
|
6578
|
+
type: Joi28.string().optional().allow("", null),
|
|
6579
|
+
email: Joi28.string().optional().allow("", null),
|
|
6580
|
+
app: Joi28.string().optional().allow("", null),
|
|
6581
|
+
org: Joi28.string().optional().allow("", null)
|
|
6582
|
+
});
|
|
6583
|
+
const { error } = validation.validate(req.query);
|
|
6584
|
+
if (error) {
|
|
6585
|
+
next(new BadRequestError34(error.message));
|
|
6617
6586
|
return;
|
|
6618
6587
|
}
|
|
6619
|
-
const
|
|
6620
|
-
|
|
6621
|
-
|
|
6588
|
+
const status = req.query.status ?? "";
|
|
6589
|
+
const search = req.query.search ?? "";
|
|
6590
|
+
const page = Number(req.query.page) ?? 1;
|
|
6591
|
+
let type = req.query.type ?? "";
|
|
6592
|
+
const email = req.query.email ?? "";
|
|
6593
|
+
const app = req.query.app ?? "";
|
|
6594
|
+
const org = req.query.org ?? "";
|
|
6595
|
+
const hasMultipleTypes = type.includes(",");
|
|
6596
|
+
let splitType = [];
|
|
6597
|
+
if (hasMultipleTypes) {
|
|
6598
|
+
splitType = type.split(",");
|
|
6599
|
+
}
|
|
6600
|
+
try {
|
|
6601
|
+
const items = await _getVerifications({
|
|
6602
|
+
status,
|
|
6603
|
+
search,
|
|
6604
|
+
page,
|
|
6605
|
+
type: hasMultipleTypes ? splitType : type,
|
|
6606
|
+
email,
|
|
6607
|
+
app,
|
|
6608
|
+
org
|
|
6609
|
+
});
|
|
6610
|
+
res.json(items);
|
|
6622
6611
|
return;
|
|
6612
|
+
} catch (error2) {
|
|
6613
|
+
next(error2);
|
|
6623
6614
|
}
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
});
|
|
6630
|
-
const { error } = validation.validate({ user, page, limit, search });
|
|
6615
|
+
}
|
|
6616
|
+
async function verify(req, res, next) {
|
|
6617
|
+
const id = req.params.id || "";
|
|
6618
|
+
const validation = Joi28.string().hex().required();
|
|
6619
|
+
const { error } = validation.validate(id);
|
|
6631
6620
|
if (error) {
|
|
6632
|
-
next(new
|
|
6621
|
+
next(new BadRequestError34(error.message));
|
|
6633
6622
|
return;
|
|
6634
6623
|
}
|
|
6635
6624
|
try {
|
|
6636
|
-
const
|
|
6637
|
-
res.json(
|
|
6625
|
+
const message = await _verify(id);
|
|
6626
|
+
res.json(message);
|
|
6638
6627
|
return;
|
|
6639
6628
|
} catch (error2) {
|
|
6640
6629
|
next(error2);
|
|
6641
6630
|
}
|
|
6642
6631
|
}
|
|
6643
|
-
async function
|
|
6644
|
-
const
|
|
6645
|
-
const validation =
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
status: Joi27.string().valid("active", "suspended", "inactive", "deleted").optional()
|
|
6650
|
-
});
|
|
6651
|
-
const { error } = validation.validate(query);
|
|
6652
|
-
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
6653
|
-
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
6654
|
-
const search = req.query.search ?? "";
|
|
6655
|
-
const status = req.query.status ?? "active";
|
|
6656
|
-
const isPageNumber = isFinite(page);
|
|
6657
|
-
if (!isPageNumber) {
|
|
6658
|
-
next(new BadRequestError35("Invalid page number."));
|
|
6632
|
+
async function cancelUserInvitation(req, res, next) {
|
|
6633
|
+
const otpId = req.params.id || "";
|
|
6634
|
+
const validation = Joi28.string().hex().required();
|
|
6635
|
+
const { error } = validation.validate(otpId);
|
|
6636
|
+
if (error) {
|
|
6637
|
+
next(new BadRequestError34(error.message));
|
|
6659
6638
|
return;
|
|
6660
6639
|
}
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6640
|
+
try {
|
|
6641
|
+
await _cancelUserInvitation(otpId);
|
|
6642
|
+
return res.json({
|
|
6643
|
+
message: "User invite has been cancelled."
|
|
6644
|
+
});
|
|
6645
|
+
} catch (error2) {
|
|
6646
|
+
throw error2;
|
|
6665
6647
|
}
|
|
6648
|
+
}
|
|
6649
|
+
async function signUp(req, res, next) {
|
|
6650
|
+
const validation = Joi28.string().email().required();
|
|
6651
|
+
const { error } = validation.validate(req.body.email);
|
|
6666
6652
|
if (error) {
|
|
6667
|
-
next(new
|
|
6653
|
+
next(new BadRequestError34(error.message));
|
|
6668
6654
|
return;
|
|
6669
6655
|
}
|
|
6656
|
+
const email = req.body.email ?? "";
|
|
6670
6657
|
try {
|
|
6671
|
-
|
|
6672
|
-
res.json(
|
|
6658
|
+
await _signUp({ email });
|
|
6659
|
+
res.json({ message: "Successfully signed up user." });
|
|
6673
6660
|
return;
|
|
6674
6661
|
} catch (error2) {
|
|
6675
6662
|
next(error2);
|
|
6676
6663
|
}
|
|
6677
6664
|
}
|
|
6678
|
-
async function
|
|
6679
|
-
const
|
|
6680
|
-
const validation = Joi27.object({
|
|
6681
|
-
name: Joi27.string().required()
|
|
6682
|
-
});
|
|
6683
|
-
const { error } = validation.validate({ name });
|
|
6665
|
+
async function inviteMember(req, res, next) {
|
|
6666
|
+
const { error } = schemaInviteMember.validate(req.body);
|
|
6684
6667
|
if (error) {
|
|
6685
|
-
next(new
|
|
6668
|
+
next(new BadRequestError34(error.message));
|
|
6686
6669
|
return;
|
|
6687
6670
|
}
|
|
6688
6671
|
try {
|
|
6689
|
-
|
|
6690
|
-
res.json(
|
|
6672
|
+
await _inviteMember(req.body);
|
|
6673
|
+
res.json({ message: "Successfully invited member." });
|
|
6691
6674
|
return;
|
|
6692
6675
|
} catch (error2) {
|
|
6693
6676
|
next(error2);
|
|
6694
6677
|
}
|
|
6695
6678
|
}
|
|
6696
|
-
async function
|
|
6697
|
-
const id = req.params.id;
|
|
6698
|
-
const validation =
|
|
6699
|
-
|
|
6700
|
-
});
|
|
6701
|
-
const { error } = validation.validate({ id });
|
|
6679
|
+
async function cancelInviteMember(req, res, next) {
|
|
6680
|
+
const id = req.params.id || "";
|
|
6681
|
+
const validation = Joi28.string().hex().required();
|
|
6682
|
+
const { error } = validation.validate(id);
|
|
6702
6683
|
if (error) {
|
|
6703
|
-
next(new
|
|
6684
|
+
next(new BadRequestError34(error.message));
|
|
6704
6685
|
return;
|
|
6705
6686
|
}
|
|
6706
6687
|
try {
|
|
6707
|
-
const
|
|
6708
|
-
res.json(
|
|
6688
|
+
const message = await _cancelInviteMember(id);
|
|
6689
|
+
res.json({ message });
|
|
6709
6690
|
return;
|
|
6710
6691
|
} catch (error2) {
|
|
6711
6692
|
next(error2);
|
|
6712
6693
|
}
|
|
6713
6694
|
}
|
|
6714
|
-
async function
|
|
6715
|
-
const
|
|
6716
|
-
const
|
|
6717
|
-
const { error } =
|
|
6695
|
+
async function forgetPassword(req, res, next) {
|
|
6696
|
+
const email = req.body.email ?? "";
|
|
6697
|
+
const validation = Joi28.string().email().required();
|
|
6698
|
+
const { error } = validation.validate(email);
|
|
6718
6699
|
if (error) {
|
|
6719
|
-
next(new
|
|
6700
|
+
next(new BadRequestError34(error.message));
|
|
6720
6701
|
return;
|
|
6721
6702
|
}
|
|
6722
6703
|
try {
|
|
6723
|
-
const message = await
|
|
6724
|
-
res.json({
|
|
6704
|
+
const message = await _forgetPassword(email);
|
|
6705
|
+
res.json({
|
|
6706
|
+
message
|
|
6707
|
+
});
|
|
6708
|
+
return;
|
|
6709
|
+
} catch (error2) {
|
|
6710
|
+
if (error2 instanceof AppError14) {
|
|
6711
|
+
next(error2);
|
|
6712
|
+
} else {
|
|
6713
|
+
next(new InternalServerError18("An unexpected error occurred"));
|
|
6714
|
+
}
|
|
6715
|
+
}
|
|
6716
|
+
}
|
|
6717
|
+
async function orgSetupFee(req, res, next) {
|
|
6718
|
+
const value = req.body;
|
|
6719
|
+
const { error } = schemaOrgAdd.validate(value);
|
|
6720
|
+
if (error) {
|
|
6721
|
+
next(new BadRequestError34(error.message));
|
|
6722
|
+
return;
|
|
6723
|
+
}
|
|
6724
|
+
try {
|
|
6725
|
+
const data = await _orgSetupFee(value);
|
|
6726
|
+
res.json(data);
|
|
6725
6727
|
return;
|
|
6726
6728
|
} catch (error2) {
|
|
6727
6729
|
next(error2);
|
|
6728
6730
|
}
|
|
6729
6731
|
}
|
|
6730
6732
|
return {
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6733
|
+
getVerifications,
|
|
6734
|
+
createUserInvite,
|
|
6735
|
+
createForgetPassword,
|
|
6736
|
+
verify,
|
|
6737
|
+
cancelUserInvitation,
|
|
6738
|
+
inviteMember,
|
|
6739
|
+
signUp,
|
|
6740
|
+
cancelInviteMember,
|
|
6741
|
+
forgetPassword,
|
|
6742
|
+
orgSetupFee
|
|
6737
6743
|
};
|
|
6738
6744
|
}
|
|
6739
6745
|
|
|
6740
|
-
// src/resources/
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6745
|
-
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6746
|
+
// src/resources/ledger/ledger.billing.model.ts
|
|
6747
|
+
import { BadRequestError as BadRequestError35 } from "@goweekdays/utils";
|
|
6748
|
+
import Joi29 from "joi";
|
|
6749
|
+
import { ObjectId as ObjectId20 } from "mongodb";
|
|
6750
|
+
var ledgerBillTypes = ["setup-fee", "subscription"];
|
|
6751
|
+
var ledgerBillStatuses = ["pending", "paid", "overdue"];
|
|
6752
|
+
var schemaLedgerBill = Joi29.object({
|
|
6753
|
+
org: Joi29.string().hex().length(24).required(),
|
|
6754
|
+
type: Joi29.string().valid(...ledgerBillTypes).required(),
|
|
6755
|
+
description: Joi29.string().max(255).required(),
|
|
6756
|
+
amount: Joi29.number().positive().required(),
|
|
6757
|
+
currency: Joi29.string().length(3).required(),
|
|
6758
|
+
status: Joi29.string().valid(...ledgerBillStatuses).required(),
|
|
6759
|
+
billingFrom: Joi29.date().optional().allow("", null),
|
|
6760
|
+
billingTo: Joi29.date().optional().allow("", null),
|
|
6761
|
+
paidAt: Joi29.date().optional().allow("", null),
|
|
6762
|
+
dueDate: Joi29.date().optional().allow("", null)
|
|
6763
|
+
});
|
|
6764
|
+
var schemaLedgerBillingSummary = Joi29.object({
|
|
6765
|
+
status: Joi29.string().valid(...ledgerBillStatuses).required(),
|
|
6766
|
+
org: Joi29.string().hex().length(24).required()
|
|
6767
|
+
});
|
|
6768
|
+
function modelLedgerBill(value) {
|
|
6769
|
+
const { error } = schemaLedgerBill.validate(value);
|
|
6770
|
+
if (error) {
|
|
6771
|
+
throw new Error(`Ledger bill model validation error: ${error.message}`);
|
|
6772
|
+
}
|
|
6773
|
+
try {
|
|
6774
|
+
value.org = new ObjectId20(value.org);
|
|
6775
|
+
} catch (error2) {
|
|
6776
|
+
throw new BadRequestError35("Invalid org ID.");
|
|
6777
|
+
}
|
|
6778
|
+
return {
|
|
6779
|
+
_id: value._id,
|
|
6780
|
+
org: value.org,
|
|
6781
|
+
type: value.type,
|
|
6782
|
+
description: value.description,
|
|
6783
|
+
amount: value.amount,
|
|
6784
|
+
currency: value.currency,
|
|
6785
|
+
status: value.status,
|
|
6786
|
+
billingFrom: value.billingFrom ?? "",
|
|
6787
|
+
billingTo: value.billingTo ?? "",
|
|
6788
|
+
paidAt: value.paidAt ?? "",
|
|
6789
|
+
dueDate: value.dueDate ?? "",
|
|
6790
|
+
createdAt: value.createdAt ?? /* @__PURE__ */ new Date()
|
|
6791
|
+
};
|
|
6792
|
+
}
|
|
6793
|
+
|
|
6794
|
+
// src/resources/ledger/ledger.billing.repository.ts
|
|
6795
|
+
import {
|
|
6796
|
+
AppError as AppError15,
|
|
6797
|
+
BadRequestError as BadRequestError36,
|
|
6798
|
+
InternalServerError as InternalServerError19,
|
|
6799
|
+
logger as logger19,
|
|
6800
|
+
makeCacheKey as makeCacheKey13,
|
|
6801
|
+
paginate as paginate12,
|
|
6802
|
+
useAtlas as useAtlas16,
|
|
6803
|
+
useCache as useCache14
|
|
6804
|
+
} from "@goweekdays/utils";
|
|
6805
|
+
import { ObjectId as ObjectId21 } from "mongodb";
|
|
6806
|
+
import Joi30 from "joi";
|
|
6807
|
+
function useLedgerBillingRepo() {
|
|
6808
|
+
const db = useAtlas16.getDb();
|
|
6809
|
+
if (!db) {
|
|
6810
|
+
throw new Error("Unable to connect to server.");
|
|
6811
|
+
}
|
|
6812
|
+
const namespace_collection = "ledger.billings";
|
|
6813
|
+
const collection = db.collection(namespace_collection);
|
|
6814
|
+
const { getCache, setCache, delNamespace } = useCache14(namespace_collection);
|
|
6815
|
+
function delCachedData() {
|
|
6816
|
+
delNamespace().then(() => {
|
|
6817
|
+
logger19.log({
|
|
6818
|
+
level: "info",
|
|
6819
|
+
message: `Cache namespace cleared for ${namespace_collection}`
|
|
6820
|
+
});
|
|
6821
|
+
}).catch((err) => {
|
|
6822
|
+
logger19.log({
|
|
6823
|
+
level: "error",
|
|
6824
|
+
message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
|
|
6825
|
+
});
|
|
6826
|
+
});
|
|
6827
|
+
}
|
|
6828
|
+
async function createIndexes() {
|
|
6756
6829
|
try {
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6830
|
+
await collection.createIndexes([
|
|
6831
|
+
{ key: { org: 1 } },
|
|
6832
|
+
{ key: { status: 1 } },
|
|
6833
|
+
{ key: { description: "text" }, name: "text_index" }
|
|
6834
|
+
]);
|
|
6835
|
+
return "Successfully created ledger billing indexes.";
|
|
6836
|
+
} catch (error) {
|
|
6837
|
+
throw new BadRequestError36("Failed to create ledger billing indexes.");
|
|
6838
|
+
}
|
|
6839
|
+
}
|
|
6840
|
+
async function add(value, session) {
|
|
6841
|
+
try {
|
|
6842
|
+
value = modelLedgerBill(value);
|
|
6843
|
+
const result = await collection.insertOne(value, { session });
|
|
6844
|
+
delCachedData();
|
|
6845
|
+
return result.insertedId;
|
|
6846
|
+
} catch (error) {
|
|
6847
|
+
if (error instanceof AppError15) {
|
|
6848
|
+
throw error;
|
|
6763
6849
|
}
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
|
|
6850
|
+
throw new BadRequestError36(
|
|
6851
|
+
`Failed to add ledger billing: ${error.message}`
|
|
6852
|
+
);
|
|
6853
|
+
}
|
|
6854
|
+
}
|
|
6855
|
+
async function getAll({
|
|
6856
|
+
org = "",
|
|
6857
|
+
search = "",
|
|
6858
|
+
page = 1,
|
|
6859
|
+
limit = 10,
|
|
6860
|
+
status = ""
|
|
6861
|
+
} = {}) {
|
|
6862
|
+
page = page > 0 ? page - 1 : 0;
|
|
6863
|
+
const query = { status: { $ne: "deleted" } };
|
|
6864
|
+
const cacheKeyOptions = {
|
|
6865
|
+
search,
|
|
6866
|
+
page,
|
|
6867
|
+
limit,
|
|
6868
|
+
tag: "getAll"
|
|
6869
|
+
};
|
|
6870
|
+
try {
|
|
6871
|
+
query.org = new ObjectId21(org);
|
|
6872
|
+
cacheKeyOptions.org = org;
|
|
6873
|
+
} catch (error) {
|
|
6874
|
+
throw new BadRequestError36("Invalid organization ID.");
|
|
6875
|
+
}
|
|
6876
|
+
if (status) {
|
|
6877
|
+
query.status = status;
|
|
6878
|
+
cacheKeyOptions.status = status;
|
|
6879
|
+
}
|
|
6880
|
+
if (search) {
|
|
6881
|
+
query.$text = { $search: search };
|
|
6882
|
+
}
|
|
6883
|
+
const cacheKey = makeCacheKey13(namespace_collection, cacheKeyOptions);
|
|
6884
|
+
logger19.log({
|
|
6885
|
+
level: "info",
|
|
6886
|
+
message: `Cache key for getAll ledger billings: ${cacheKey}`
|
|
6887
|
+
});
|
|
6888
|
+
try {
|
|
6889
|
+
const cached = await getCache(cacheKey);
|
|
6890
|
+
if (cached) {
|
|
6891
|
+
logger19.log({
|
|
6892
|
+
level: "info",
|
|
6893
|
+
message: `Cache hit for getAll ledger billings: ${cacheKey}`
|
|
6894
|
+
});
|
|
6895
|
+
return cached;
|
|
6896
|
+
}
|
|
6897
|
+
const items = await collection.aggregate([
|
|
6898
|
+
{ $match: query },
|
|
6899
|
+
{ $skip: page * limit },
|
|
6900
|
+
{ $limit: limit }
|
|
6901
|
+
]).toArray();
|
|
6902
|
+
const length = await collection.countDocuments(query);
|
|
6903
|
+
const data = paginate12(items, page, limit, length);
|
|
6904
|
+
setCache(cacheKey, data, 600).then(() => {
|
|
6905
|
+
logger19.log({
|
|
6906
|
+
level: "info",
|
|
6907
|
+
message: `Cache set for getAll ledger billings: ${cacheKey}`
|
|
6908
|
+
});
|
|
6909
|
+
}).catch((err) => {
|
|
6910
|
+
logger19.log({
|
|
6911
|
+
level: "error",
|
|
6912
|
+
message: `Failed to set cache for getAll ledger billings: ${err.message}`
|
|
6913
|
+
});
|
|
6914
|
+
});
|
|
6915
|
+
return data;
|
|
6916
|
+
} catch (error) {
|
|
6917
|
+
logger19.log({ level: "error", message: `${error}` });
|
|
6918
|
+
throw error;
|
|
6919
|
+
}
|
|
6920
|
+
}
|
|
6921
|
+
async function getById(_id) {
|
|
6922
|
+
const { error } = Joi30.string().hex().length(24).required().validate(_id);
|
|
6923
|
+
if (error) {
|
|
6924
|
+
throw new Error(`Invalid ledger billing ID: ${error.message}`);
|
|
6925
|
+
}
|
|
6926
|
+
try {
|
|
6927
|
+
_id = new ObjectId21(_id);
|
|
6928
|
+
} catch (error2) {
|
|
6929
|
+
throw new BadRequestError36("Invalid ledger billing ID.");
|
|
6930
|
+
}
|
|
6931
|
+
try {
|
|
6932
|
+
const cacheKey = makeCacheKey13(namespace_collection, {
|
|
6933
|
+
_id: String(_id),
|
|
6934
|
+
tag: "getById"
|
|
6935
|
+
});
|
|
6936
|
+
const cachedData = await getCache(cacheKey);
|
|
6937
|
+
if (cachedData) {
|
|
6938
|
+
return cachedData;
|
|
6939
|
+
}
|
|
6940
|
+
const data = await collection.findOne({
|
|
6941
|
+
_id,
|
|
6942
|
+
status: { $ne: "deleted" }
|
|
6943
|
+
});
|
|
6944
|
+
setCache(cacheKey, data).then(() => {
|
|
6945
|
+
logger19.log({
|
|
6946
|
+
level: "info",
|
|
6947
|
+
message: `Cache set for getById ledger billing: ${cacheKey}`
|
|
6948
|
+
});
|
|
6949
|
+
}).catch((err) => {
|
|
6950
|
+
logger19.log({
|
|
6951
|
+
level: "error",
|
|
6952
|
+
message: `Failed to set cache for getById ledger billing: ${err.message}`
|
|
6953
|
+
});
|
|
6954
|
+
});
|
|
6955
|
+
return data;
|
|
6956
|
+
} catch (error2) {
|
|
6957
|
+
throw new InternalServerError19("Failed to get ledger billing.");
|
|
6958
|
+
}
|
|
6959
|
+
}
|
|
6960
|
+
async function getSummary(status, org) {
|
|
6961
|
+
const { error } = schemaLedgerBillingSummary.validate({ status, org });
|
|
6962
|
+
if (error) {
|
|
6963
|
+
throw new Error(`Invalid ledger billing ID: ${error.message}`);
|
|
6964
|
+
}
|
|
6965
|
+
try {
|
|
6966
|
+
org = new ObjectId21(org);
|
|
6967
|
+
} catch (error2) {
|
|
6968
|
+
throw new BadRequestError36("Invalid ledger billing org.");
|
|
6969
|
+
}
|
|
6970
|
+
try {
|
|
6971
|
+
const cacheKey = makeCacheKey13(namespace_collection, {
|
|
6972
|
+
org: String(org),
|
|
6973
|
+
status,
|
|
6974
|
+
tag: "getSummaryByOrgStatus"
|
|
6975
|
+
});
|
|
6976
|
+
const cachedData = await getCache(cacheKey);
|
|
6977
|
+
if (cachedData) {
|
|
6978
|
+
return cachedData;
|
|
6979
|
+
}
|
|
6980
|
+
const data = await collection.aggregate([
|
|
6981
|
+
{ $match: { org, status } },
|
|
6982
|
+
{
|
|
6983
|
+
$group: {
|
|
6984
|
+
_id: "$status",
|
|
6985
|
+
totalAmount: { $sum: "$amount" },
|
|
6986
|
+
count: { $sum: 1 }
|
|
6987
|
+
}
|
|
6988
|
+
}
|
|
6989
|
+
]).toArray();
|
|
6990
|
+
setCache(cacheKey, data[0]).then(() => {
|
|
6991
|
+
logger19.log({
|
|
6992
|
+
level: "info",
|
|
6993
|
+
message: `Cache set for ledger billing summary: ${cacheKey}`
|
|
6994
|
+
});
|
|
6995
|
+
}).catch((err) => {
|
|
6996
|
+
logger19.log({
|
|
6997
|
+
level: "error",
|
|
6998
|
+
message: `Failed to set cache for ledger billing summary: ${err.message}`
|
|
6999
|
+
});
|
|
7000
|
+
});
|
|
7001
|
+
return data[0];
|
|
7002
|
+
} catch (error2) {
|
|
7003
|
+
throw new InternalServerError19("Failed to get ledger billing summary.");
|
|
7004
|
+
}
|
|
7005
|
+
}
|
|
7006
|
+
return {
|
|
7007
|
+
delCachedData,
|
|
7008
|
+
createIndexes,
|
|
7009
|
+
add,
|
|
7010
|
+
getAll,
|
|
7011
|
+
getById,
|
|
7012
|
+
getSummary
|
|
7013
|
+
};
|
|
7014
|
+
}
|
|
7015
|
+
|
|
7016
|
+
// src/resources/ledger/ledger.billing.controller.ts
|
|
7017
|
+
import Joi31 from "joi";
|
|
7018
|
+
import { BadRequestError as BadRequestError37 } from "@goweekdays/utils";
|
|
7019
|
+
function useLedgerBillingController() {
|
|
7020
|
+
const {
|
|
7021
|
+
getAll: _getAll,
|
|
7022
|
+
getById: _getById,
|
|
7023
|
+
getSummary: _getSummary
|
|
7024
|
+
} = useLedgerBillingRepo();
|
|
7025
|
+
async function getAll(req, res, next) {
|
|
7026
|
+
const query = req.query;
|
|
7027
|
+
const validation = Joi31.object({
|
|
7028
|
+
org: Joi31.string().hex().optional().allow("", null),
|
|
7029
|
+
page: Joi31.number().min(1).optional().allow("", null),
|
|
7030
|
+
limit: Joi31.number().min(1).optional().allow("", null),
|
|
7031
|
+
search: Joi31.string().optional().allow("", null),
|
|
7032
|
+
status: Joi31.string().optional().allow("", null)
|
|
7033
|
+
});
|
|
7034
|
+
const { error } = validation.validate(query);
|
|
7035
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
7036
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
7037
|
+
const search = req.query.search ?? "";
|
|
7038
|
+
const org = req.query.org ?? "";
|
|
7039
|
+
const status = req.query.status ?? "";
|
|
7040
|
+
const isPageNumber = isFinite(page);
|
|
7041
|
+
if (!isPageNumber) {
|
|
7042
|
+
next(new BadRequestError37("Invalid page number."));
|
|
7043
|
+
return;
|
|
7044
|
+
}
|
|
7045
|
+
const isLimitNumber = isFinite(limit);
|
|
7046
|
+
if (!isLimitNumber) {
|
|
7047
|
+
next(new BadRequestError37("Invalid limit number."));
|
|
7048
|
+
return;
|
|
7049
|
+
}
|
|
7050
|
+
if (error) {
|
|
7051
|
+
next(new BadRequestError37(error.message));
|
|
7052
|
+
return;
|
|
7053
|
+
}
|
|
7054
|
+
try {
|
|
7055
|
+
const orgs = await _getAll({ org, page, limit, search, status });
|
|
7056
|
+
res.json(orgs);
|
|
7057
|
+
return;
|
|
7058
|
+
} catch (error2) {
|
|
7059
|
+
next(error2);
|
|
7060
|
+
}
|
|
7061
|
+
}
|
|
7062
|
+
async function getById(req, res, next) {
|
|
7063
|
+
const id = req.params.id;
|
|
7064
|
+
const validation = Joi31.object({
|
|
7065
|
+
id: Joi31.string().hex().required()
|
|
7066
|
+
});
|
|
7067
|
+
const { error } = validation.validate({ id });
|
|
7068
|
+
if (error) {
|
|
7069
|
+
next(new BadRequestError37(error.message));
|
|
7070
|
+
return;
|
|
7071
|
+
}
|
|
7072
|
+
try {
|
|
7073
|
+
const org = await _getById(id);
|
|
7074
|
+
res.json(org);
|
|
7075
|
+
return;
|
|
7076
|
+
} catch (error2) {
|
|
7077
|
+
next(error2);
|
|
7078
|
+
}
|
|
7079
|
+
}
|
|
7080
|
+
async function getSummary(req, res, next) {
|
|
7081
|
+
const params = req.params;
|
|
7082
|
+
const { error } = schemaLedgerBillingSummary.validate(params);
|
|
7083
|
+
const org = req.params.org ?? "";
|
|
7084
|
+
const status = req.params.status ?? "";
|
|
7085
|
+
if (error) {
|
|
7086
|
+
next(new BadRequestError37(error.message));
|
|
7087
|
+
return;
|
|
7088
|
+
}
|
|
7089
|
+
try {
|
|
7090
|
+
const summary = await _getSummary(status, org);
|
|
7091
|
+
res.json(summary);
|
|
7092
|
+
return;
|
|
7093
|
+
} catch (error2) {
|
|
7094
|
+
next(error2);
|
|
7095
|
+
}
|
|
7096
|
+
}
|
|
7097
|
+
return {
|
|
7098
|
+
getAll,
|
|
7099
|
+
getById,
|
|
7100
|
+
getSummary
|
|
7101
|
+
};
|
|
7102
|
+
}
|
|
7103
|
+
|
|
7104
|
+
// src/resources/organization/organization.service.ts
|
|
7105
|
+
function useOrgService() {
|
|
7106
|
+
const { add: addOrg } = useOrgRepo();
|
|
7107
|
+
const { addRole } = useRoleRepo();
|
|
7108
|
+
const { getAll: getAllPermission } = usePermissionRepo();
|
|
7109
|
+
const { add: addMember, updateRoleById: _updateRoleById } = useMemberRepo();
|
|
7110
|
+
const { getUserById } = useUserRepo();
|
|
7111
|
+
const { getDefault } = usePlanRepo();
|
|
7112
|
+
const { add: addSubscription } = useSubscriptionRepo();
|
|
7113
|
+
const { add: addSubscriptionTransaction } = useSubscriptionTransactionRepo();
|
|
7114
|
+
const { addOrder: addPaypalOrder } = usePaypalService();
|
|
7115
|
+
async function add(value) {
|
|
7116
|
+
const { error } = schemaOrgAdd.validate(value);
|
|
7117
|
+
if (error) {
|
|
7118
|
+
throw new BadRequestError38(error.message);
|
|
7119
|
+
}
|
|
7120
|
+
const session = useAtlas17.getClient()?.startSession();
|
|
7121
|
+
if (!session) {
|
|
7122
|
+
throw new BadRequestError38("Unable to start database session.");
|
|
7123
|
+
}
|
|
7124
|
+
try {
|
|
7125
|
+
session?.startTransaction();
|
|
7126
|
+
const org = await addOrg(
|
|
7127
|
+
{
|
|
7128
|
+
email: value.email,
|
|
7129
|
+
name: value.name,
|
|
7130
|
+
contact: value.contact,
|
|
7131
|
+
createdBy: value.createdBy
|
|
7132
|
+
},
|
|
7133
|
+
session
|
|
7134
|
+
);
|
|
7135
|
+
const plan = await getDefault();
|
|
7136
|
+
if (!plan) {
|
|
7137
|
+
throw new BadRequestError38(
|
|
7138
|
+
"Failed to create organization, plan not found."
|
|
7139
|
+
);
|
|
7140
|
+
}
|
|
7141
|
+
const currentDate = /* @__PURE__ */ new Date();
|
|
7142
|
+
const nextBillingDate = new Date(currentDate);
|
|
7143
|
+
nextBillingDate.setMonth(currentDate.getMonth() + 1);
|
|
7144
|
+
const amount = plan.price * value.seats;
|
|
7145
|
+
const subscriptionId = await addSubscription(
|
|
7146
|
+
{
|
|
7147
|
+
amount,
|
|
7148
|
+
org: String(org),
|
|
7149
|
+
seats: value.seats,
|
|
7150
|
+
paidSeats: value.seats,
|
|
7151
|
+
currency: plan.currency,
|
|
7152
|
+
billingCycle: plan.billingCycle,
|
|
7153
|
+
nextBillingDate
|
|
7154
|
+
},
|
|
7155
|
+
session
|
|
7156
|
+
);
|
|
7157
|
+
const createdBy = String(value.createdBy);
|
|
7158
|
+
const user = await getUserById(createdBy);
|
|
7159
|
+
if (!user) {
|
|
7160
|
+
throw new BadRequestError38("User is required to create org member.");
|
|
7161
|
+
}
|
|
7162
|
+
await addSubscriptionTransaction(
|
|
7163
|
+
{
|
|
7164
|
+
subscription: subscriptionId,
|
|
7165
|
+
type: "initiate",
|
|
7166
|
+
amount,
|
|
7167
|
+
currency: plan.currency,
|
|
7168
|
+
description: "Initial subscription transaction",
|
|
7169
|
+
createdBy: value.createdBy,
|
|
7170
|
+
createdByName: `${user.firstName} ${user.lastName}`
|
|
7171
|
+
},
|
|
7172
|
+
session
|
|
7173
|
+
);
|
|
7174
|
+
const allPermissions = await getAllPermission({
|
|
7175
|
+
app: "org",
|
|
7176
|
+
limit: 100
|
|
7177
|
+
});
|
|
7178
|
+
let permissions = [];
|
|
7179
|
+
if (allPermissions && allPermissions.items && allPermissions.items.length) {
|
|
7180
|
+
permissions = allPermissions.items.map((perm) => perm.key);
|
|
7181
|
+
}
|
|
7182
|
+
if (permissions.length === 0) {
|
|
7183
|
+
throw new Error("No permissions found for the organization type.");
|
|
7184
|
+
}
|
|
7185
|
+
const roleData = {
|
|
7186
|
+
org: String(org),
|
|
7187
|
+
name: "Owner",
|
|
7188
|
+
description: "Owner of the organization",
|
|
7189
|
+
permissions,
|
|
7190
|
+
createdBy,
|
|
7191
|
+
app: "org"
|
|
7192
|
+
};
|
|
7193
|
+
const role = await addRole(roleData, session);
|
|
7194
|
+
if (!role) {
|
|
7195
|
+
throw new BadRequestError38("Role is required to create org member.");
|
|
7196
|
+
}
|
|
7197
|
+
await addMember(
|
|
7198
|
+
{
|
|
7199
|
+
role: String(role),
|
|
7200
|
+
roleName: roleData.name,
|
|
7201
|
+
org: String(org),
|
|
7202
|
+
orgName: value.name,
|
|
7203
|
+
name: `${user.firstName} ${user.lastName}`,
|
|
7204
|
+
user: createdBy,
|
|
7205
|
+
app: "org"
|
|
7206
|
+
},
|
|
7207
|
+
session
|
|
7208
|
+
);
|
|
7209
|
+
const order = await addPaypalOrder({
|
|
7210
|
+
amount,
|
|
7211
|
+
currency: plan.currency,
|
|
7212
|
+
customId: subscriptionId,
|
|
7213
|
+
returnUrl: `${APP_ORG}/organizations/success`,
|
|
7214
|
+
cancelUrl: `${APP_ORG}/organizations/cancel`,
|
|
7215
|
+
action: "pay"
|
|
7216
|
+
});
|
|
7217
|
+
await session?.commitTransaction();
|
|
7218
|
+
const paypalOrderLink = JSON.parse(order.body.toString()).links.find(
|
|
7219
|
+
(link) => link.rel === "approve"
|
|
7220
|
+
);
|
|
7221
|
+
return {
|
|
7222
|
+
org: String(org),
|
|
7223
|
+
paypalOrderLink: paypalOrderLink ? paypalOrderLink.href : ""
|
|
7224
|
+
};
|
|
7225
|
+
} catch (error2) {
|
|
7226
|
+
await session?.abortTransaction();
|
|
7227
|
+
throw error2;
|
|
7228
|
+
} finally {
|
|
7229
|
+
await session?.endSession();
|
|
7230
|
+
}
|
|
7231
|
+
}
|
|
7232
|
+
const { getById, updateStatusById: updateVerificationStatus } = useVerificationRepo();
|
|
7233
|
+
const { add: addLedgerBilling } = useLedgerBillingRepo();
|
|
7234
|
+
async function addWithVerification(id) {
|
|
7235
|
+
const { error } = Joi32.string().hex().required().validate(id);
|
|
7236
|
+
if (error) {
|
|
7237
|
+
throw new BadRequestError38(error.message);
|
|
7238
|
+
}
|
|
7239
|
+
const session = useAtlas17.getClient()?.startSession();
|
|
7240
|
+
if (!session) {
|
|
7241
|
+
throw new BadRequestError38("Unable to start database session.");
|
|
7242
|
+
}
|
|
7243
|
+
try {
|
|
7244
|
+
session?.startTransaction();
|
|
7245
|
+
const verification = await getById(id);
|
|
7246
|
+
await updateVerificationStatus(id, "complete", session);
|
|
7247
|
+
if (!verification) {
|
|
7248
|
+
throw new BadRequestError38("Verification not found.");
|
|
7249
|
+
}
|
|
7250
|
+
if (!verification.metadata?.orgName) {
|
|
7251
|
+
throw new BadRequestError38("Organization name is required.");
|
|
7252
|
+
}
|
|
7253
|
+
if (!verification.metadata?.seats) {
|
|
7254
|
+
throw new BadRequestError38("Number of seats is required.");
|
|
7255
|
+
}
|
|
7256
|
+
if (!verification.metadata?.createdBy) {
|
|
7257
|
+
throw new BadRequestError38("CreatedBy is required.");
|
|
7258
|
+
}
|
|
7259
|
+
if (!verification.metadata?.contact) {
|
|
7260
|
+
throw new BadRequestError38("Contact is required.");
|
|
7261
|
+
}
|
|
7262
|
+
const org = await addOrg(
|
|
7263
|
+
{
|
|
7264
|
+
email: verification.email,
|
|
7265
|
+
name: verification.metadata.orgName,
|
|
7266
|
+
contact: verification.metadata.contact,
|
|
7267
|
+
createdBy: verification.metadata.createdBy
|
|
7268
|
+
},
|
|
7269
|
+
session
|
|
7270
|
+
);
|
|
7271
|
+
const plan = await getDefault();
|
|
7272
|
+
if (!plan) {
|
|
7273
|
+
throw new BadRequestError38(
|
|
7274
|
+
"Failed to create organization, plan not found."
|
|
7275
|
+
);
|
|
7276
|
+
}
|
|
7277
|
+
const currentDate = /* @__PURE__ */ new Date();
|
|
7278
|
+
const nextBillingDate = new Date(currentDate);
|
|
7279
|
+
nextBillingDate.setMonth(currentDate.getMonth() + 1);
|
|
7280
|
+
const amount = plan.price * verification.metadata.seats;
|
|
7281
|
+
const subscriptionId = await addSubscription(
|
|
7282
|
+
{
|
|
7283
|
+
amount,
|
|
7284
|
+
org: String(org),
|
|
7285
|
+
seats: verification.metadata.seats,
|
|
7286
|
+
paidSeats: verification.metadata.seats,
|
|
7287
|
+
currency: plan.currency,
|
|
7288
|
+
billingCycle: plan.billingCycle,
|
|
7289
|
+
nextBillingDate
|
|
7290
|
+
},
|
|
7291
|
+
session
|
|
7292
|
+
);
|
|
7293
|
+
const createdBy = String(verification.metadata.createdBy);
|
|
7294
|
+
const user = await getUserById(createdBy);
|
|
7295
|
+
if (!user) {
|
|
7296
|
+
throw new BadRequestError38("User is required to create org member.");
|
|
7297
|
+
}
|
|
7298
|
+
await addSubscriptionTransaction(
|
|
7299
|
+
{
|
|
7300
|
+
subscription: subscriptionId,
|
|
7301
|
+
type: "initiate",
|
|
7302
|
+
amount,
|
|
7303
|
+
currency: plan.currency,
|
|
7304
|
+
description: "Initial subscription transaction",
|
|
7305
|
+
createdBy,
|
|
7306
|
+
createdByName: `${user.firstName} ${user.lastName}`
|
|
7307
|
+
},
|
|
7308
|
+
session
|
|
7309
|
+
);
|
|
7310
|
+
await addLedgerBilling(
|
|
7311
|
+
{
|
|
7312
|
+
org: String(org),
|
|
7313
|
+
amount: verification.metadata?.amount ?? 0,
|
|
7314
|
+
description: "Setup payment during organization creation",
|
|
7315
|
+
currency: plan.currency,
|
|
7316
|
+
type: "setup-fee",
|
|
7317
|
+
status: "paid"
|
|
7318
|
+
},
|
|
7319
|
+
session
|
|
7320
|
+
);
|
|
7321
|
+
const allPermissions = await getAllPermission({
|
|
7322
|
+
app: "org",
|
|
7323
|
+
limit: 100
|
|
7324
|
+
});
|
|
7325
|
+
let permissions = [];
|
|
7326
|
+
if (allPermissions && allPermissions.items && allPermissions.items.length) {
|
|
7327
|
+
permissions = allPermissions.items.map((perm) => perm.key);
|
|
7328
|
+
}
|
|
7329
|
+
if (permissions.length === 0) {
|
|
7330
|
+
throw new Error("No permissions found for the organization type.");
|
|
7331
|
+
}
|
|
7332
|
+
const roleData = {
|
|
7333
|
+
org: String(org),
|
|
7334
|
+
name: "Owner",
|
|
7335
|
+
description: "Owner of the organization",
|
|
7336
|
+
permissions,
|
|
7337
|
+
createdBy,
|
|
7338
|
+
app: "org"
|
|
7339
|
+
};
|
|
7340
|
+
const role = await addRole(roleData, session);
|
|
7341
|
+
if (!role) {
|
|
7342
|
+
throw new BadRequestError38("Role is required to create org member.");
|
|
7343
|
+
}
|
|
7344
|
+
await addMember(
|
|
7345
|
+
{
|
|
7346
|
+
role: String(role),
|
|
7347
|
+
roleName: roleData.name,
|
|
7348
|
+
org: String(org),
|
|
7349
|
+
orgName: verification.metadata.orgName,
|
|
7350
|
+
name: `${user.firstName} ${user.lastName}`,
|
|
7351
|
+
user: createdBy,
|
|
7352
|
+
app: "org"
|
|
7353
|
+
},
|
|
7354
|
+
session
|
|
7355
|
+
);
|
|
7356
|
+
await session?.commitTransaction();
|
|
7357
|
+
return "Successfully created organization with verification.";
|
|
7358
|
+
} catch (error2) {
|
|
7359
|
+
await session?.abortTransaction();
|
|
7360
|
+
throw error2;
|
|
7361
|
+
} finally {
|
|
7362
|
+
await session?.endSession();
|
|
7363
|
+
}
|
|
7364
|
+
}
|
|
7365
|
+
return {
|
|
7366
|
+
add,
|
|
7367
|
+
addWithVerification
|
|
7368
|
+
};
|
|
7369
|
+
}
|
|
7370
|
+
|
|
7371
|
+
// src/resources/organization/organization.controller.ts
|
|
7372
|
+
import { BadRequestError as BadRequestError39 } from "@goweekdays/utils";
|
|
7373
|
+
import Joi33 from "joi";
|
|
7374
|
+
function useOrgController() {
|
|
7375
|
+
const { add: _add } = useOrgService();
|
|
7376
|
+
const { getOrgsByMembership } = useMemberRepo();
|
|
7377
|
+
const {
|
|
7378
|
+
getByName: _getByName,
|
|
7379
|
+
getAll: getAllOrg,
|
|
7380
|
+
getById: _getById,
|
|
7381
|
+
updateById: _updateById
|
|
7382
|
+
} = useOrgRepo();
|
|
7383
|
+
async function add(req, res, next) {
|
|
7384
|
+
const value = req.body;
|
|
7385
|
+
const { error } = schemaOrgAdd.validate(value);
|
|
7386
|
+
if (error) {
|
|
7387
|
+
next(new BadRequestError39(error.message));
|
|
7388
|
+
return;
|
|
7389
|
+
}
|
|
7390
|
+
try {
|
|
7391
|
+
const data = await _add(value);
|
|
7392
|
+
res.json({
|
|
7393
|
+
message: "Organization created successfully.",
|
|
7394
|
+
data
|
|
7395
|
+
});
|
|
7396
|
+
return;
|
|
7397
|
+
} catch (error2) {
|
|
7398
|
+
next(error2);
|
|
7399
|
+
}
|
|
7400
|
+
}
|
|
7401
|
+
async function getOrgsByUserId(req, res, next) {
|
|
7402
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
7403
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
7404
|
+
const search = req.query.search ?? "";
|
|
7405
|
+
const user = req.params.user ?? "";
|
|
7406
|
+
const isPageNumber = isFinite(page);
|
|
7407
|
+
if (!isPageNumber) {
|
|
7408
|
+
next(new BadRequestError39("Invalid page number."));
|
|
7409
|
+
return;
|
|
7410
|
+
}
|
|
7411
|
+
const isLimitNumber = isFinite(limit);
|
|
7412
|
+
if (!isLimitNumber) {
|
|
7413
|
+
next(new BadRequestError39("Invalid limit number."));
|
|
7414
|
+
return;
|
|
7415
|
+
}
|
|
7416
|
+
const validation = Joi33.object({
|
|
7417
|
+
user: Joi33.string().hex().required(),
|
|
7418
|
+
page: Joi33.number().min(1).optional().allow("", null),
|
|
7419
|
+
limit: Joi33.number().min(1).optional().allow("", null),
|
|
7420
|
+
search: Joi33.string().optional().allow("", null)
|
|
7421
|
+
});
|
|
7422
|
+
const { error } = validation.validate({ user, page, limit, search });
|
|
7423
|
+
if (error) {
|
|
7424
|
+
next(new BadRequestError39(error.message));
|
|
7425
|
+
return;
|
|
7426
|
+
}
|
|
7427
|
+
try {
|
|
7428
|
+
const orgs = await getOrgsByMembership({ user, page, limit, search });
|
|
7429
|
+
res.json(orgs);
|
|
7430
|
+
return;
|
|
7431
|
+
} catch (error2) {
|
|
7432
|
+
next(error2);
|
|
7433
|
+
}
|
|
7434
|
+
}
|
|
7435
|
+
async function getAll(req, res, next) {
|
|
7436
|
+
const query = req.query;
|
|
7437
|
+
const validation = Joi33.object({
|
|
7438
|
+
page: Joi33.number().min(1).optional().allow("", null),
|
|
7439
|
+
limit: Joi33.number().min(1).optional().allow("", null),
|
|
7440
|
+
search: Joi33.string().optional().allow("", null),
|
|
7441
|
+
status: Joi33.string().valid("active", "suspended", "inactive", "deleted").optional()
|
|
7442
|
+
});
|
|
7443
|
+
const { error } = validation.validate(query);
|
|
7444
|
+
const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
|
|
7445
|
+
const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
|
|
7446
|
+
const search = req.query.search ?? "";
|
|
7447
|
+
const status = req.query.status ?? "active";
|
|
7448
|
+
const isPageNumber = isFinite(page);
|
|
7449
|
+
if (!isPageNumber) {
|
|
7450
|
+
next(new BadRequestError39("Invalid page number."));
|
|
7451
|
+
return;
|
|
7452
|
+
}
|
|
7453
|
+
const isLimitNumber = isFinite(limit);
|
|
7454
|
+
if (!isLimitNumber) {
|
|
7455
|
+
next(new BadRequestError39("Invalid limit number."));
|
|
7456
|
+
return;
|
|
7457
|
+
}
|
|
7458
|
+
if (error) {
|
|
7459
|
+
next(new BadRequestError39(error.message));
|
|
7460
|
+
return;
|
|
7461
|
+
}
|
|
7462
|
+
try {
|
|
7463
|
+
const orgs = await getAllOrg({ page, limit, search, status });
|
|
7464
|
+
res.json(orgs);
|
|
7465
|
+
return;
|
|
7466
|
+
} catch (error2) {
|
|
7467
|
+
next(error2);
|
|
7468
|
+
}
|
|
7469
|
+
}
|
|
7470
|
+
async function getByName(req, res, next) {
|
|
7471
|
+
const name = req.params.name;
|
|
7472
|
+
const validation = Joi33.object({
|
|
7473
|
+
name: Joi33.string().required()
|
|
7474
|
+
});
|
|
7475
|
+
const { error } = validation.validate({ name });
|
|
7476
|
+
if (error) {
|
|
7477
|
+
next(new BadRequestError39(error.message));
|
|
7478
|
+
return;
|
|
7479
|
+
}
|
|
7480
|
+
try {
|
|
7481
|
+
const org = await _getByName(name);
|
|
7482
|
+
res.json(org);
|
|
7483
|
+
return;
|
|
7484
|
+
} catch (error2) {
|
|
7485
|
+
next(error2);
|
|
7486
|
+
}
|
|
7487
|
+
}
|
|
7488
|
+
async function getById(req, res, next) {
|
|
7489
|
+
const id = req.params.id;
|
|
7490
|
+
const validation = Joi33.object({
|
|
7491
|
+
id: Joi33.string().hex().required()
|
|
7492
|
+
});
|
|
7493
|
+
const { error } = validation.validate({ id });
|
|
7494
|
+
if (error) {
|
|
7495
|
+
next(new BadRequestError39(error.message));
|
|
7496
|
+
return;
|
|
7497
|
+
}
|
|
7498
|
+
try {
|
|
7499
|
+
const org = await _getById(id);
|
|
7500
|
+
res.json(org);
|
|
7501
|
+
return;
|
|
7502
|
+
} catch (error2) {
|
|
7503
|
+
next(error2);
|
|
7504
|
+
}
|
|
7505
|
+
}
|
|
7506
|
+
async function updateById(req, res, next) {
|
|
7507
|
+
const _id = req.params.id ?? "";
|
|
7508
|
+
const payload = req.body;
|
|
7509
|
+
const { error } = schemaOrgUpdate.validate({ _id, ...payload });
|
|
7510
|
+
if (error) {
|
|
7511
|
+
next(new BadRequestError39(error.message));
|
|
7512
|
+
return;
|
|
7513
|
+
}
|
|
7514
|
+
try {
|
|
7515
|
+
const message = await _updateById(_id, payload);
|
|
7516
|
+
res.json({ message });
|
|
7517
|
+
return;
|
|
7518
|
+
} catch (error2) {
|
|
7519
|
+
next(error2);
|
|
7520
|
+
}
|
|
7521
|
+
}
|
|
7522
|
+
return {
|
|
7523
|
+
add,
|
|
7524
|
+
getOrgsByUserId,
|
|
7525
|
+
getByName,
|
|
7526
|
+
getAll,
|
|
7527
|
+
getById,
|
|
7528
|
+
updateById
|
|
7529
|
+
};
|
|
7530
|
+
}
|
|
7531
|
+
|
|
7532
|
+
// src/resources/user/user.service.ts
|
|
7533
|
+
function useUserService() {
|
|
7534
|
+
const {
|
|
7535
|
+
add: addUser,
|
|
7536
|
+
getUserByEmail,
|
|
7537
|
+
getUserById: _getById,
|
|
7538
|
+
updateName: _updateName,
|
|
7539
|
+
updateBirthday: _updateBirthday,
|
|
7540
|
+
updateUserFieldById: _updateUserFieldById
|
|
7541
|
+
} = useUserRepo();
|
|
7542
|
+
const { addRole, getById: getRoleById } = useRoleRepo();
|
|
7543
|
+
const { add: addMember } = useMemberRepo();
|
|
7544
|
+
const { getAll: getAllPermission } = usePermissionRepo();
|
|
7545
|
+
const { getById: getOrgById } = useOrgRepo();
|
|
7546
|
+
async function createDefaultUser() {
|
|
7547
|
+
const session = useAtlas18.getClient()?.startSession();
|
|
7548
|
+
try {
|
|
7549
|
+
session?.startTransaction();
|
|
7550
|
+
const _user = await getUserByEmail(DEFAULT_USER_EMAIL);
|
|
7551
|
+
if (_user) {
|
|
7552
|
+
throw new BadRequestError40(
|
|
7553
|
+
`User already exists: ${DEFAULT_USER_EMAIL}.`
|
|
7554
|
+
);
|
|
7555
|
+
}
|
|
7556
|
+
const hashedPassword = await hashPassword(DEFAULT_USER_PASSWORD);
|
|
7557
|
+
const userId = await addUser(
|
|
7558
|
+
{
|
|
7559
|
+
email: DEFAULT_USER_EMAIL,
|
|
7560
|
+
password: hashedPassword,
|
|
7561
|
+
firstName: DEFAULT_USER_FIRST_NAME,
|
|
7562
|
+
lastName: DEFAULT_USER_LAST_NAME
|
|
6771
7563
|
},
|
|
6772
7564
|
session
|
|
6773
7565
|
);
|
|
@@ -6807,7 +7599,7 @@ function useUserService() {
|
|
|
6807
7599
|
try {
|
|
6808
7600
|
const _user = await getUserByEmail(value.email);
|
|
6809
7601
|
if (_user) {
|
|
6810
|
-
throw new
|
|
7602
|
+
throw new BadRequestError40(`User already exists: ${value.email}.`);
|
|
6811
7603
|
}
|
|
6812
7604
|
const hashedPassword = await hashPassword(value.password);
|
|
6813
7605
|
const insertedId = await addUser({
|
|
@@ -6819,10 +7611,10 @@ function useUserService() {
|
|
|
6819
7611
|
});
|
|
6820
7612
|
return insertedId;
|
|
6821
7613
|
} catch (error) {
|
|
6822
|
-
if (error instanceof
|
|
7614
|
+
if (error instanceof AppError16) {
|
|
6823
7615
|
throw error;
|
|
6824
7616
|
} else {
|
|
6825
|
-
throw new
|
|
7617
|
+
throw new InternalServerError20(`Error creating user: ${error}`);
|
|
6826
7618
|
}
|
|
6827
7619
|
}
|
|
6828
7620
|
}
|
|
@@ -6833,22 +7625,22 @@ function useUserService() {
|
|
|
6833
7625
|
lastName = "",
|
|
6834
7626
|
password = ""
|
|
6835
7627
|
} = {}) {
|
|
6836
|
-
const session =
|
|
7628
|
+
const session = useAtlas18.getClient()?.startSession();
|
|
6837
7629
|
session?.startTransaction();
|
|
6838
7630
|
try {
|
|
6839
7631
|
const invitation = await _getVerificationById(id);
|
|
6840
7632
|
if (!invitation || !invitation.metadata?.app || !invitation.metadata?.role) {
|
|
6841
|
-
throw new
|
|
7633
|
+
throw new BadRequestError40("Invalid invitation.");
|
|
6842
7634
|
}
|
|
6843
7635
|
if (invitation.status === "complete") {
|
|
6844
|
-
throw new
|
|
7636
|
+
throw new BadRequestError40("Invitation already used.");
|
|
6845
7637
|
}
|
|
6846
7638
|
if (!invitation.expireAt) {
|
|
6847
|
-
throw new
|
|
7639
|
+
throw new BadRequestError40("Expiration date is required.");
|
|
6848
7640
|
}
|
|
6849
7641
|
const expired = new Date(invitation.expireAt) < /* @__PURE__ */ new Date();
|
|
6850
7642
|
if (invitation.status === "expired" || expired) {
|
|
6851
|
-
throw new
|
|
7643
|
+
throw new BadRequestError40("Invitation expired.");
|
|
6852
7644
|
}
|
|
6853
7645
|
const email = invitation.email;
|
|
6854
7646
|
const user = await getUserByEmail(invitation.email);
|
|
@@ -6899,10 +7691,10 @@ function useUserService() {
|
|
|
6899
7691
|
return userId;
|
|
6900
7692
|
} catch (error) {
|
|
6901
7693
|
await session?.abortTransaction();
|
|
6902
|
-
if (error instanceof
|
|
7694
|
+
if (error instanceof AppError16) {
|
|
6903
7695
|
throw error;
|
|
6904
7696
|
} else {
|
|
6905
|
-
throw new
|
|
7697
|
+
throw new InternalServerError20("Failed to create user by invite.");
|
|
6906
7698
|
}
|
|
6907
7699
|
} finally {
|
|
6908
7700
|
session?.endSession();
|
|
@@ -6914,29 +7706,29 @@ function useUserService() {
|
|
|
6914
7706
|
lastName = "",
|
|
6915
7707
|
password = ""
|
|
6916
7708
|
} = {}) {
|
|
6917
|
-
const session =
|
|
7709
|
+
const session = useAtlas18.getClient()?.startSession();
|
|
6918
7710
|
session?.startTransaction();
|
|
6919
7711
|
try {
|
|
6920
7712
|
const signUp = await _getVerificationById(id);
|
|
6921
7713
|
if (!signUp) {
|
|
6922
|
-
throw new
|
|
7714
|
+
throw new BadRequestError40("Invalid sign up link.");
|
|
6923
7715
|
}
|
|
6924
7716
|
if (signUp.status === "complete") {
|
|
6925
|
-
throw new
|
|
7717
|
+
throw new BadRequestError40(
|
|
6926
7718
|
"You have already an account created using this link."
|
|
6927
7719
|
);
|
|
6928
7720
|
}
|
|
6929
7721
|
if (!signUp.expireAt) {
|
|
6930
|
-
throw new
|
|
7722
|
+
throw new BadRequestError40("Expiration date is required.");
|
|
6931
7723
|
}
|
|
6932
7724
|
const expired = new Date(signUp.expireAt) < /* @__PURE__ */ new Date();
|
|
6933
7725
|
if (signUp.status === "expired" || expired) {
|
|
6934
|
-
throw new
|
|
7726
|
+
throw new BadRequestError40("Sign up link expired.");
|
|
6935
7727
|
}
|
|
6936
7728
|
const email = signUp.email;
|
|
6937
7729
|
const _user = await getUserByEmail(signUp.email);
|
|
6938
7730
|
if (_user) {
|
|
6939
|
-
throw new
|
|
7731
|
+
throw new BadRequestError40(`User already exists: ${email}.`);
|
|
6940
7732
|
}
|
|
6941
7733
|
const hashedPassword = await hashPassword(password);
|
|
6942
7734
|
const userId = await addUser(
|
|
@@ -6962,17 +7754,17 @@ function useUserService() {
|
|
|
6962
7754
|
const { updateStatusById: updateVerificationStatusById } = useVerificationRepo();
|
|
6963
7755
|
async function resetPassword(value) {
|
|
6964
7756
|
if (value.newPassword !== value.confirmPassword) {
|
|
6965
|
-
throw new
|
|
7757
|
+
throw new BadRequestError40("Passwords do not match.");
|
|
6966
7758
|
}
|
|
6967
7759
|
let hashedPassword = "";
|
|
6968
7760
|
try {
|
|
6969
7761
|
hashedPassword = await hashPassword(value.newPassword);
|
|
6970
7762
|
} catch (error) {
|
|
6971
|
-
throw new
|
|
7763
|
+
throw new InternalServerError20(`Error hashing password: ${error}`);
|
|
6972
7764
|
}
|
|
6973
|
-
const session =
|
|
7765
|
+
const session = useAtlas18.getClient()?.startSession();
|
|
6974
7766
|
if (!session) {
|
|
6975
|
-
throw new
|
|
7767
|
+
throw new InternalServerError20("Failed to start database session.");
|
|
6976
7768
|
}
|
|
6977
7769
|
try {
|
|
6978
7770
|
session.startTransaction();
|
|
@@ -6988,7 +7780,7 @@ function useUserService() {
|
|
|
6988
7780
|
throw new NotFoundError2("User ID is invalid.");
|
|
6989
7781
|
}
|
|
6990
7782
|
if (otpDoc.status === "used") {
|
|
6991
|
-
throw new
|
|
7783
|
+
throw new BadRequestError40("This link has already been invalidated.");
|
|
6992
7784
|
}
|
|
6993
7785
|
await updateVerificationStatusById(value.id, "used", session);
|
|
6994
7786
|
await _updateUserFieldById(
|
|
@@ -6999,31 +7791,31 @@ function useUserService() {
|
|
|
6999
7791
|
return "Successfully reset password.";
|
|
7000
7792
|
} catch (error) {
|
|
7001
7793
|
await session.abortTransaction();
|
|
7002
|
-
if (error instanceof
|
|
7794
|
+
if (error instanceof AppError16) {
|
|
7003
7795
|
throw error;
|
|
7004
7796
|
}
|
|
7005
|
-
throw new
|
|
7797
|
+
throw new InternalServerError20("Failed to reset password.");
|
|
7006
7798
|
}
|
|
7007
7799
|
}
|
|
7008
7800
|
const { updateName: updateMemberName } = useMemberRepo();
|
|
7009
7801
|
async function updateName(_id, firstName, lastName) {
|
|
7010
7802
|
if (!_id) {
|
|
7011
|
-
throw new
|
|
7803
|
+
throw new BadRequestError40("Invalid user ID");
|
|
7012
7804
|
}
|
|
7013
7805
|
if (!firstName) {
|
|
7014
|
-
throw new
|
|
7806
|
+
throw new BadRequestError40("Invalid firstName");
|
|
7015
7807
|
}
|
|
7016
7808
|
if (!lastName) {
|
|
7017
|
-
throw new
|
|
7809
|
+
throw new BadRequestError40("Invalid lastName");
|
|
7018
7810
|
}
|
|
7019
|
-
const session =
|
|
7811
|
+
const session = useAtlas18.getClient()?.startSession();
|
|
7020
7812
|
session?.startTransaction();
|
|
7021
|
-
const cacheKey =
|
|
7813
|
+
const cacheKey = makeCacheKey14("users", { user: _id });
|
|
7022
7814
|
try {
|
|
7023
|
-
|
|
7024
|
-
|
|
7815
|
+
useCache15().delCache(cacheKey).then(() => {
|
|
7816
|
+
logger20.info(`Cache cleared for user: ${_id}`);
|
|
7025
7817
|
}).catch((error) => {
|
|
7026
|
-
|
|
7818
|
+
logger20.error(`Failed to clear cache for user: ${_id}`, error);
|
|
7027
7819
|
});
|
|
7028
7820
|
await _updateName({ _id, firstName, lastName }, session);
|
|
7029
7821
|
await updateMemberName(
|
|
@@ -7041,16 +7833,16 @@ function useUserService() {
|
|
|
7041
7833
|
}
|
|
7042
7834
|
async function updateBirthday(_id, month, day, year) {
|
|
7043
7835
|
if (!_id) {
|
|
7044
|
-
throw new
|
|
7836
|
+
throw new BadRequestError40("Invalid user ID");
|
|
7045
7837
|
}
|
|
7046
7838
|
if (!month) {
|
|
7047
|
-
throw new
|
|
7839
|
+
throw new BadRequestError40("Invalid birth month.");
|
|
7048
7840
|
}
|
|
7049
7841
|
if (!day) {
|
|
7050
|
-
throw new
|
|
7842
|
+
throw new BadRequestError40("Invalid birthday.");
|
|
7051
7843
|
}
|
|
7052
7844
|
if (!year) {
|
|
7053
|
-
throw new
|
|
7845
|
+
throw new BadRequestError40("Invalid birth year.");
|
|
7054
7846
|
}
|
|
7055
7847
|
try {
|
|
7056
7848
|
await _updateBirthday({ _id, month, day, year });
|
|
@@ -7075,7 +7867,7 @@ function useUserService() {
|
|
|
7075
7867
|
bucket: SPACES_BUCKET
|
|
7076
7868
|
});
|
|
7077
7869
|
async function updateUserProfile({ file, user, previousProfile } = {}) {
|
|
7078
|
-
const session =
|
|
7870
|
+
const session = useAtlas18.getClient()?.startSession();
|
|
7079
7871
|
session?.startTransaction();
|
|
7080
7872
|
const _file = {
|
|
7081
7873
|
name: file.originalname,
|
|
@@ -7120,11 +7912,11 @@ function useUserService() {
|
|
|
7120
7912
|
|
|
7121
7913
|
// src/resources/user/user.controller.ts
|
|
7122
7914
|
import {
|
|
7123
|
-
AppError as
|
|
7124
|
-
BadRequestError as
|
|
7125
|
-
InternalServerError as
|
|
7915
|
+
AppError as AppError17,
|
|
7916
|
+
BadRequestError as BadRequestError41,
|
|
7917
|
+
InternalServerError as InternalServerError21
|
|
7126
7918
|
} from "@goweekdays/utils";
|
|
7127
|
-
import
|
|
7919
|
+
import Joi34 from "joi";
|
|
7128
7920
|
function useUserController() {
|
|
7129
7921
|
const {
|
|
7130
7922
|
updateName: _updateName,
|
|
@@ -7140,14 +7932,14 @@ function useUserController() {
|
|
|
7140
7932
|
const status = req.query.status ?? "";
|
|
7141
7933
|
const search = req.query.search ?? "";
|
|
7142
7934
|
const page = Number(req.query.page) ?? 1;
|
|
7143
|
-
const validation =
|
|
7144
|
-
status:
|
|
7145
|
-
search:
|
|
7146
|
-
page:
|
|
7935
|
+
const validation = Joi34.object({
|
|
7936
|
+
status: Joi34.string().required(),
|
|
7937
|
+
search: Joi34.string().optional().allow("", null),
|
|
7938
|
+
page: Joi34.number().required()
|
|
7147
7939
|
});
|
|
7148
7940
|
const { error } = validation.validate({ status, search, page });
|
|
7149
7941
|
if (error) {
|
|
7150
|
-
next(new
|
|
7942
|
+
next(new BadRequestError41(error.message));
|
|
7151
7943
|
return;
|
|
7152
7944
|
}
|
|
7153
7945
|
try {
|
|
@@ -7160,14 +7952,14 @@ function useUserController() {
|
|
|
7160
7952
|
}
|
|
7161
7953
|
async function getUserById(req, res, next) {
|
|
7162
7954
|
const id = req.params.id || "";
|
|
7163
|
-
const validation =
|
|
7955
|
+
const validation = Joi34.string().hex().validate(id);
|
|
7164
7956
|
if (validation.error) {
|
|
7165
|
-
throw new
|
|
7957
|
+
throw new BadRequestError41("Invalid id.");
|
|
7166
7958
|
}
|
|
7167
7959
|
try {
|
|
7168
7960
|
const user = await _getUserById(id);
|
|
7169
7961
|
if (!user) {
|
|
7170
|
-
throw new
|
|
7962
|
+
throw new BadRequestError41("User not found.");
|
|
7171
7963
|
}
|
|
7172
7964
|
res.json(user);
|
|
7173
7965
|
} catch (error) {
|
|
@@ -7178,13 +7970,13 @@ function useUserController() {
|
|
|
7178
7970
|
const id = req.headers.user ?? "";
|
|
7179
7971
|
const firstName = req.body.firstName ?? "";
|
|
7180
7972
|
const lastName = req.body.lastName ?? "";
|
|
7181
|
-
const validation =
|
|
7182
|
-
firstName:
|
|
7183
|
-
lastName:
|
|
7973
|
+
const validation = Joi34.object({
|
|
7974
|
+
firstName: Joi34.string().required(),
|
|
7975
|
+
lastName: Joi34.string().required()
|
|
7184
7976
|
});
|
|
7185
7977
|
const { error } = validation.validate({ firstName, lastName });
|
|
7186
7978
|
if (error) {
|
|
7187
|
-
next(new
|
|
7979
|
+
next(new BadRequestError41(error.message));
|
|
7188
7980
|
return;
|
|
7189
7981
|
}
|
|
7190
7982
|
try {
|
|
@@ -7200,14 +7992,14 @@ function useUserController() {
|
|
|
7200
7992
|
const month = req.body.month ?? "";
|
|
7201
7993
|
const day = req.body.day ?? 0;
|
|
7202
7994
|
const year = req.body.year ?? 0;
|
|
7203
|
-
const validation =
|
|
7204
|
-
month:
|
|
7205
|
-
day:
|
|
7206
|
-
year:
|
|
7995
|
+
const validation = Joi34.object({
|
|
7996
|
+
month: Joi34.string().required(),
|
|
7997
|
+
day: Joi34.number().integer().min(1).max(31).required(),
|
|
7998
|
+
year: Joi34.number().integer().min(1900).max((/* @__PURE__ */ new Date()).getFullYear()).required()
|
|
7207
7999
|
});
|
|
7208
8000
|
const { error } = validation.validate({ month, day, year });
|
|
7209
8001
|
if (error) {
|
|
7210
|
-
next(new
|
|
8002
|
+
next(new BadRequestError41(error.message));
|
|
7211
8003
|
return;
|
|
7212
8004
|
}
|
|
7213
8005
|
try {
|
|
@@ -7221,18 +8013,18 @@ function useUserController() {
|
|
|
7221
8013
|
async function updateUserFieldById(req, res, next) {
|
|
7222
8014
|
const _id = req.params.id;
|
|
7223
8015
|
const { field, value } = req.body;
|
|
7224
|
-
const validation =
|
|
7225
|
-
_id:
|
|
7226
|
-
field:
|
|
7227
|
-
value:
|
|
8016
|
+
const validation = Joi34.object({
|
|
8017
|
+
_id: Joi34.string().hex().required(),
|
|
8018
|
+
field: Joi34.string().valid("gender", "email", "contact", "profile").required(),
|
|
8019
|
+
value: Joi34.alternatives().conditional("field", {
|
|
7228
8020
|
is: "email",
|
|
7229
|
-
then:
|
|
7230
|
-
otherwise:
|
|
8021
|
+
then: Joi34.string().email().required(),
|
|
8022
|
+
otherwise: Joi34.string().required()
|
|
7231
8023
|
})
|
|
7232
8024
|
});
|
|
7233
8025
|
const { error } = validation.validate({ _id, field, value });
|
|
7234
8026
|
if (error) {
|
|
7235
|
-
next(new
|
|
8027
|
+
next(new BadRequestError41(error.message));
|
|
7236
8028
|
return;
|
|
7237
8029
|
}
|
|
7238
8030
|
try {
|
|
@@ -7248,12 +8040,12 @@ function useUserController() {
|
|
|
7248
8040
|
return;
|
|
7249
8041
|
}
|
|
7250
8042
|
const previousProfile = req.body.previousProfile ?? "";
|
|
7251
|
-
const validation =
|
|
7252
|
-
previousProfile:
|
|
8043
|
+
const validation = Joi34.object({
|
|
8044
|
+
previousProfile: Joi34.string().hex().optional().allow("", null)
|
|
7253
8045
|
});
|
|
7254
8046
|
const { error } = validation.validate({ previousProfile });
|
|
7255
8047
|
if (error) {
|
|
7256
|
-
next(new
|
|
8048
|
+
next(new BadRequestError41(error.message));
|
|
7257
8049
|
return;
|
|
7258
8050
|
}
|
|
7259
8051
|
const user = req.headers["user"] ?? "";
|
|
@@ -7266,10 +8058,10 @@ function useUserController() {
|
|
|
7266
8058
|
res.json({ message: "Successfully updated profile picture." });
|
|
7267
8059
|
return;
|
|
7268
8060
|
} catch (error2) {
|
|
7269
|
-
if (error2 instanceof
|
|
8061
|
+
if (error2 instanceof AppError17) {
|
|
7270
8062
|
next(error2);
|
|
7271
8063
|
} else {
|
|
7272
|
-
next(new
|
|
8064
|
+
next(new InternalServerError21(error2));
|
|
7273
8065
|
}
|
|
7274
8066
|
}
|
|
7275
8067
|
}
|
|
@@ -7279,12 +8071,12 @@ function useUserController() {
|
|
|
7279
8071
|
const password = req.body.password ?? "";
|
|
7280
8072
|
const id = req.params.id ?? "";
|
|
7281
8073
|
const type = req.body.type ?? "";
|
|
7282
|
-
const validation =
|
|
7283
|
-
firstName:
|
|
7284
|
-
lastName:
|
|
7285
|
-
password:
|
|
7286
|
-
id:
|
|
7287
|
-
type:
|
|
8074
|
+
const validation = Joi34.object({
|
|
8075
|
+
firstName: Joi34.string().required(),
|
|
8076
|
+
lastName: Joi34.string().required(),
|
|
8077
|
+
password: Joi34.string().required(),
|
|
8078
|
+
id: Joi34.string().hex().required(),
|
|
8079
|
+
type: Joi34.string().required()
|
|
7288
8080
|
});
|
|
7289
8081
|
const { error } = validation.validate({
|
|
7290
8082
|
firstName,
|
|
@@ -7294,7 +8086,7 @@ function useUserController() {
|
|
|
7294
8086
|
type
|
|
7295
8087
|
});
|
|
7296
8088
|
if (error) {
|
|
7297
|
-
next(new
|
|
8089
|
+
next(new BadRequestError41(error.message));
|
|
7298
8090
|
return;
|
|
7299
8091
|
}
|
|
7300
8092
|
try {
|
|
@@ -7317,14 +8109,14 @@ function useUserController() {
|
|
|
7317
8109
|
}
|
|
7318
8110
|
async function resetPassword(req, res, next) {
|
|
7319
8111
|
const payload = req.body;
|
|
7320
|
-
const validation =
|
|
7321
|
-
id:
|
|
7322
|
-
newPassword:
|
|
7323
|
-
confirmPassword:
|
|
8112
|
+
const validation = Joi34.object({
|
|
8113
|
+
id: Joi34.string().hex().required(),
|
|
8114
|
+
newPassword: Joi34.string().min(8).required(),
|
|
8115
|
+
confirmPassword: Joi34.string().min(8).required()
|
|
7324
8116
|
});
|
|
7325
8117
|
const { error } = validation.validate(payload);
|
|
7326
8118
|
if (error) {
|
|
7327
|
-
next(new
|
|
8119
|
+
next(new BadRequestError41(error.message));
|
|
7328
8120
|
return;
|
|
7329
8121
|
}
|
|
7330
8122
|
try {
|
|
@@ -7349,7 +8141,7 @@ function useUserController() {
|
|
|
7349
8141
|
}
|
|
7350
8142
|
|
|
7351
8143
|
// src/resources/verification/verification.service.ts
|
|
7352
|
-
import
|
|
8144
|
+
import Joi35 from "joi";
|
|
7353
8145
|
function useVerificationService() {
|
|
7354
8146
|
const MailerConfig = {
|
|
7355
8147
|
host: MAILER_TRANSPORT_HOST,
|
|
@@ -7384,7 +8176,7 @@ function useVerificationService() {
|
|
|
7384
8176
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
7385
8177
|
};
|
|
7386
8178
|
if (!metadata.app) {
|
|
7387
|
-
throw new
|
|
8179
|
+
throw new BadRequestError42("App metadata is required.");
|
|
7388
8180
|
}
|
|
7389
8181
|
try {
|
|
7390
8182
|
const user = await getUserByEmail(email);
|
|
@@ -7407,7 +8199,7 @@ function useVerificationService() {
|
|
|
7407
8199
|
html: emailContent2,
|
|
7408
8200
|
from: "GoWeekdays"
|
|
7409
8201
|
}).catch((error) => {
|
|
7410
|
-
|
|
8202
|
+
logger21.log({
|
|
7411
8203
|
level: "error",
|
|
7412
8204
|
message: `Error sending user invite email: ${error}`
|
|
7413
8205
|
});
|
|
@@ -7429,7 +8221,7 @@ function useVerificationService() {
|
|
|
7429
8221
|
html: emailContent,
|
|
7430
8222
|
from: "GoWeekdays"
|
|
7431
8223
|
}).catch((error) => {
|
|
7432
|
-
|
|
8224
|
+
logger21.log({
|
|
7433
8225
|
level: "error",
|
|
7434
8226
|
message: `Error sending user invite email: ${error}`
|
|
7435
8227
|
});
|
|
@@ -7464,14 +8256,14 @@ function useVerificationService() {
|
|
|
7464
8256
|
from: "GoWeekdays",
|
|
7465
8257
|
html: emailContent
|
|
7466
8258
|
}).catch((error) => {
|
|
7467
|
-
|
|
8259
|
+
logger21.log({
|
|
7468
8260
|
level: "error",
|
|
7469
8261
|
message: `Error sending forget password email: ${error}`
|
|
7470
8262
|
});
|
|
7471
8263
|
});
|
|
7472
8264
|
return "Successfully created a link to reset password. Please check your email.";
|
|
7473
8265
|
} catch (error) {
|
|
7474
|
-
throw new
|
|
8266
|
+
throw new InternalServerError22("Failed to create forget password link.");
|
|
7475
8267
|
}
|
|
7476
8268
|
}
|
|
7477
8269
|
async function getById(id) {
|
|
@@ -7510,34 +8302,34 @@ function useVerificationService() {
|
|
|
7510
8302
|
}
|
|
7511
8303
|
function errorByType(type, status) {
|
|
7512
8304
|
if (type === "user-invite" && status === "expired") {
|
|
7513
|
-
throw new
|
|
8305
|
+
throw new BadRequestError42(
|
|
7514
8306
|
"Invitation has already expired, please contact admin to resend the invitation."
|
|
7515
8307
|
);
|
|
7516
8308
|
}
|
|
7517
8309
|
if (type === "user-sign-up" && status === "expired") {
|
|
7518
|
-
throw new
|
|
8310
|
+
throw new BadRequestError42(
|
|
7519
8311
|
"Sign up verification has expired, please sign up again to get a new verification link."
|
|
7520
8312
|
);
|
|
7521
8313
|
}
|
|
7522
8314
|
if (type === "user-invite" && status === "complete") {
|
|
7523
|
-
throw new
|
|
8315
|
+
throw new BadRequestError42(
|
|
7524
8316
|
"User already registered, please login to continue."
|
|
7525
8317
|
);
|
|
7526
8318
|
}
|
|
7527
8319
|
if (type === "forget-password" && status === "complete") {
|
|
7528
|
-
throw new
|
|
8320
|
+
throw new BadRequestError42(
|
|
7529
8321
|
"Forget password verification has already been used, please request a new one."
|
|
7530
8322
|
);
|
|
7531
8323
|
}
|
|
7532
8324
|
if (type === "forget-password" && status === "expired") {
|
|
7533
|
-
throw new
|
|
8325
|
+
throw new BadRequestError42(
|
|
7534
8326
|
"Forget password verification has expired, please request a new one."
|
|
7535
8327
|
);
|
|
7536
8328
|
}
|
|
7537
|
-
throw new
|
|
8329
|
+
throw new BadRequestError42("Invalid verification.");
|
|
7538
8330
|
}
|
|
7539
8331
|
async function verify(id) {
|
|
7540
|
-
const session =
|
|
8332
|
+
const session = useAtlas19.getClient()?.startSession();
|
|
7541
8333
|
session?.startTransaction();
|
|
7542
8334
|
try {
|
|
7543
8335
|
const _id = await _getById(id);
|
|
@@ -7548,10 +8340,10 @@ function useVerificationService() {
|
|
|
7548
8340
|
errorByType(_id.type, "expired");
|
|
7549
8341
|
}
|
|
7550
8342
|
if (_id.status === "complete") {
|
|
7551
|
-
throw new
|
|
8343
|
+
throw new BadRequestError42("Verification already completed.");
|
|
7552
8344
|
}
|
|
7553
8345
|
if (!_id.expireAt) {
|
|
7554
|
-
throw new
|
|
8346
|
+
throw new BadRequestError42("Expiration date is required.");
|
|
7555
8347
|
}
|
|
7556
8348
|
const expiration = new Date(_id.expireAt).getTime();
|
|
7557
8349
|
const now = (/* @__PURE__ */ new Date()).getTime();
|
|
@@ -7567,12 +8359,12 @@ function useVerificationService() {
|
|
|
7567
8359
|
throw new NotFoundError3("User not found for member invite.");
|
|
7568
8360
|
}
|
|
7569
8361
|
if (!_id.metadata?.app) {
|
|
7570
|
-
throw new
|
|
8362
|
+
throw new BadRequestError42(
|
|
7571
8363
|
"App metadata is required for member invite."
|
|
7572
8364
|
);
|
|
7573
8365
|
}
|
|
7574
8366
|
if (!_id.metadata?.role || !_id.metadata?.roleName) {
|
|
7575
|
-
throw new
|
|
8367
|
+
throw new BadRequestError42(
|
|
7576
8368
|
"Role metadata is required for member invite."
|
|
7577
8369
|
);
|
|
7578
8370
|
}
|
|
@@ -7594,7 +8386,7 @@ function useVerificationService() {
|
|
|
7594
8386
|
return _id;
|
|
7595
8387
|
} catch (error) {
|
|
7596
8388
|
await session?.abortTransaction();
|
|
7597
|
-
|
|
8389
|
+
logger21.log({
|
|
7598
8390
|
level: "info",
|
|
7599
8391
|
message: `Error verifying user invitation: ${error}`
|
|
7600
8392
|
});
|
|
@@ -7607,7 +8399,7 @@ function useVerificationService() {
|
|
|
7607
8399
|
try {
|
|
7608
8400
|
await updateStatusById(id, "cancelled");
|
|
7609
8401
|
} catch (error) {
|
|
7610
|
-
throw new
|
|
8402
|
+
throw new InternalServerError22(
|
|
7611
8403
|
`Error cancelling user invitation: ${error}`
|
|
7612
8404
|
);
|
|
7613
8405
|
}
|
|
@@ -7627,7 +8419,7 @@ function useVerificationService() {
|
|
|
7627
8419
|
try {
|
|
7628
8420
|
const user = await getUserByEmail(email);
|
|
7629
8421
|
if (user) {
|
|
7630
|
-
throw new
|
|
8422
|
+
throw new BadRequestError42(
|
|
7631
8423
|
`Email ${email} is already registered, please login to continue.`
|
|
7632
8424
|
);
|
|
7633
8425
|
}
|
|
@@ -7654,7 +8446,7 @@ function useVerificationService() {
|
|
|
7654
8446
|
html: emailContent,
|
|
7655
8447
|
from: "GoWeekdays"
|
|
7656
8448
|
}).catch((error) => {
|
|
7657
|
-
|
|
8449
|
+
logger21.log({
|
|
7658
8450
|
level: "error",
|
|
7659
8451
|
message: `Error sending user invite email: ${error}`
|
|
7660
8452
|
});
|
|
@@ -7667,7 +8459,7 @@ function useVerificationService() {
|
|
|
7667
8459
|
async function inviteMember(value) {
|
|
7668
8460
|
const { error } = schemaInviteMember.validate(value);
|
|
7669
8461
|
if (error) {
|
|
7670
|
-
throw new
|
|
8462
|
+
throw new BadRequestError42(error.message);
|
|
7671
8463
|
}
|
|
7672
8464
|
const role = await getRoleById(value.role);
|
|
7673
8465
|
if (!role) {
|
|
@@ -7712,7 +8504,7 @@ function useVerificationService() {
|
|
|
7712
8504
|
html: emailContent2,
|
|
7713
8505
|
from: "GoWeekdays"
|
|
7714
8506
|
}).catch((error2) => {
|
|
7715
|
-
|
|
8507
|
+
logger21.log({
|
|
7716
8508
|
level: "error",
|
|
7717
8509
|
message: `Error sending user invite email: ${error2}`
|
|
7718
8510
|
});
|
|
@@ -7734,24 +8526,24 @@ function useVerificationService() {
|
|
|
7734
8526
|
html: emailContent,
|
|
7735
8527
|
from: "GoWeekdays"
|
|
7736
8528
|
}).catch((error2) => {
|
|
7737
|
-
|
|
8529
|
+
logger21.log({
|
|
7738
8530
|
level: "error",
|
|
7739
8531
|
message: `Error sending user invite email: ${error2}`
|
|
7740
8532
|
});
|
|
7741
8533
|
});
|
|
7742
8534
|
return verificationId;
|
|
7743
8535
|
} catch (error2) {
|
|
7744
|
-
if (error2 instanceof
|
|
8536
|
+
if (error2 instanceof AppError18) {
|
|
7745
8537
|
throw error2;
|
|
7746
8538
|
} else {
|
|
7747
|
-
throw new
|
|
8539
|
+
throw new InternalServerError22("Failed to invite member.");
|
|
7748
8540
|
}
|
|
7749
8541
|
}
|
|
7750
8542
|
}
|
|
7751
8543
|
async function cancelInviteMember(id) {
|
|
7752
|
-
const { error } =
|
|
8544
|
+
const { error } = Joi35.string().hex().required().validate(id);
|
|
7753
8545
|
if (error) {
|
|
7754
|
-
throw new
|
|
8546
|
+
throw new BadRequestError42("Invalid verification ID.");
|
|
7755
8547
|
}
|
|
7756
8548
|
try {
|
|
7757
8549
|
const invite = await _getById(id);
|
|
@@ -7759,25 +8551,25 @@ function useVerificationService() {
|
|
|
7759
8551
|
throw new NotFoundError3("Invitation not found.");
|
|
7760
8552
|
}
|
|
7761
8553
|
if (invite.status === "cancelled") {
|
|
7762
|
-
throw new
|
|
8554
|
+
throw new BadRequestError42("Invitation already cancelled.");
|
|
7763
8555
|
}
|
|
7764
8556
|
if (invite.status === "complete") {
|
|
7765
|
-
throw new
|
|
8557
|
+
throw new BadRequestError42("Cannot cancel a completed invitation.");
|
|
7766
8558
|
}
|
|
7767
8559
|
await _updateStatusById(id, "cancelled");
|
|
7768
8560
|
return "Successfully cancelled the invitation.";
|
|
7769
8561
|
} catch (error2) {
|
|
7770
|
-
if (error2 instanceof
|
|
8562
|
+
if (error2 instanceof AppError18) {
|
|
7771
8563
|
throw error2;
|
|
7772
8564
|
} else {
|
|
7773
|
-
throw new
|
|
8565
|
+
throw new InternalServerError22("Failed to cancel the invitation.");
|
|
7774
8566
|
}
|
|
7775
8567
|
}
|
|
7776
8568
|
}
|
|
7777
8569
|
async function forgetPassword(email) {
|
|
7778
|
-
const { error } =
|
|
8570
|
+
const { error } = Joi35.string().email().required().validate(email);
|
|
7779
8571
|
if (error) {
|
|
7780
|
-
throw new
|
|
8572
|
+
throw new BadRequestError42("Invalid email address.");
|
|
7781
8573
|
}
|
|
7782
8574
|
try {
|
|
7783
8575
|
const member = await getUserByEmail(email);
|
|
@@ -7806,20 +8598,76 @@ function useVerificationService() {
|
|
|
7806
8598
|
from: "GoWeekdays",
|
|
7807
8599
|
html: emailContent
|
|
7808
8600
|
}).catch((error2) => {
|
|
7809
|
-
|
|
8601
|
+
logger21.log({
|
|
7810
8602
|
level: "error",
|
|
7811
8603
|
message: `Error sending forget password email: ${error2}`
|
|
7812
8604
|
});
|
|
7813
8605
|
});
|
|
7814
|
-
return "Successfully created a link to reset password. Please check your email.";
|
|
7815
|
-
} catch (error2) {
|
|
7816
|
-
if (error2 instanceof
|
|
7817
|
-
throw error2;
|
|
7818
|
-
} else {
|
|
7819
|
-
throw new
|
|
7820
|
-
"Failed to process forget password request."
|
|
7821
|
-
);
|
|
8606
|
+
return "Successfully created a link to reset password. Please check your email.";
|
|
8607
|
+
} catch (error2) {
|
|
8608
|
+
if (error2 instanceof AppError18) {
|
|
8609
|
+
throw error2;
|
|
8610
|
+
} else {
|
|
8611
|
+
throw new InternalServerError22(
|
|
8612
|
+
"Failed to process forget password request."
|
|
8613
|
+
);
|
|
8614
|
+
}
|
|
8615
|
+
}
|
|
8616
|
+
}
|
|
8617
|
+
const { addOrder: addPaypalOrder } = usePaypalService();
|
|
8618
|
+
const { getByEmail: getOrgByEmail, getByName: getOrgByName } = useOrgRepo();
|
|
8619
|
+
async function orgSetupFee(value) {
|
|
8620
|
+
const session = useAtlas19.getClient()?.startSession();
|
|
8621
|
+
if (!session) {
|
|
8622
|
+
throw new BadRequestError42("Unable to start database session.");
|
|
8623
|
+
}
|
|
8624
|
+
try {
|
|
8625
|
+
session.startTransaction();
|
|
8626
|
+
const orgExistingByName = await getOrgByName(value.name);
|
|
8627
|
+
if (orgExistingByName) {
|
|
8628
|
+
throw new BadRequestError42(`Name ${value.name} is already taken.`);
|
|
8629
|
+
}
|
|
8630
|
+
const orgExistingByEmail = await getOrgByEmail(value.email);
|
|
8631
|
+
if (orgExistingByEmail) {
|
|
8632
|
+
throw new BadRequestError42(`Email ${value.email} is already taken.`);
|
|
8633
|
+
}
|
|
8634
|
+
const amount = 100;
|
|
8635
|
+
const verificationId = await add(
|
|
8636
|
+
{
|
|
8637
|
+
type: "org-setup-fee",
|
|
8638
|
+
email: value.email,
|
|
8639
|
+
metadata: {
|
|
8640
|
+
seats: value.seats,
|
|
8641
|
+
contact: value.contact,
|
|
8642
|
+
orgName: value.name,
|
|
8643
|
+
createdBy: value.createdBy,
|
|
8644
|
+
amount
|
|
8645
|
+
}
|
|
8646
|
+
},
|
|
8647
|
+
session
|
|
8648
|
+
);
|
|
8649
|
+
const order = await addPaypalOrder({
|
|
8650
|
+
amount,
|
|
8651
|
+
currency: "PHP",
|
|
8652
|
+
customId: String(verificationId),
|
|
8653
|
+
returnUrl: `${APP_ORG}/organizations/success`,
|
|
8654
|
+
cancelUrl: `${APP_ORG}/organizations/cancel`,
|
|
8655
|
+
action: "pay"
|
|
8656
|
+
});
|
|
8657
|
+
await session?.commitTransaction();
|
|
8658
|
+
const paypalOrderLink = JSON.parse(order.body.toString()).links.find(
|
|
8659
|
+
(link) => link.rel === "approve"
|
|
8660
|
+
);
|
|
8661
|
+
return {
|
|
8662
|
+
paypalOrderLink: paypalOrderLink ? paypalOrderLink.href : ""
|
|
8663
|
+
};
|
|
8664
|
+
} catch (error) {
|
|
8665
|
+
if (error instanceof AppError18) {
|
|
8666
|
+
throw error;
|
|
7822
8667
|
}
|
|
8668
|
+
throw new InternalServerError22(
|
|
8669
|
+
"Failed to process organization setup fee."
|
|
8670
|
+
);
|
|
7823
8671
|
}
|
|
7824
8672
|
}
|
|
7825
8673
|
return {
|
|
@@ -7833,7 +8681,8 @@ function useVerificationService() {
|
|
|
7833
8681
|
signUp,
|
|
7834
8682
|
inviteMember,
|
|
7835
8683
|
cancelInviteMember,
|
|
7836
|
-
forgetPassword
|
|
8684
|
+
forgetPassword,
|
|
8685
|
+
orgSetupFee
|
|
7837
8686
|
};
|
|
7838
8687
|
}
|
|
7839
8688
|
|
|
@@ -7843,13 +8692,13 @@ function useAuthController() {
|
|
|
7843
8692
|
async function login(req, res, next) {
|
|
7844
8693
|
const email = req.body.email;
|
|
7845
8694
|
const password = req.body.password;
|
|
7846
|
-
const validation =
|
|
7847
|
-
email:
|
|
7848
|
-
password:
|
|
8695
|
+
const validation = Joi36.object({
|
|
8696
|
+
email: Joi36.string().email().required(),
|
|
8697
|
+
password: Joi36.string().required()
|
|
7849
8698
|
});
|
|
7850
8699
|
const { error } = validation.validate({ email, password });
|
|
7851
8700
|
if (error) {
|
|
7852
|
-
next(new
|
|
8701
|
+
next(new BadRequestError43(error.message));
|
|
7853
8702
|
return;
|
|
7854
8703
|
}
|
|
7855
8704
|
try {
|
|
@@ -7865,14 +8714,14 @@ function useAuthController() {
|
|
|
7865
8714
|
res.cookie("sid", session.sid, cookieOptions).cookie("user", session.user, cookieOptions).json({ message: "Login successful" });
|
|
7866
8715
|
return;
|
|
7867
8716
|
} catch (error2) {
|
|
7868
|
-
|
|
8717
|
+
logger22.log({
|
|
7869
8718
|
level: "error",
|
|
7870
8719
|
message: `Error during login: ${error2.message}`
|
|
7871
8720
|
});
|
|
7872
|
-
if (error2 instanceof
|
|
8721
|
+
if (error2 instanceof AppError19) {
|
|
7873
8722
|
next(error2);
|
|
7874
8723
|
} else {
|
|
7875
|
-
next(new
|
|
8724
|
+
next(new InternalServerError23("An unexpected error occurred"));
|
|
7876
8725
|
}
|
|
7877
8726
|
return;
|
|
7878
8727
|
}
|
|
@@ -7880,17 +8729,17 @@ function useAuthController() {
|
|
|
7880
8729
|
async function logout(req, res, next) {
|
|
7881
8730
|
const sid = req.headers["authorization"] ?? "";
|
|
7882
8731
|
if (!sid) {
|
|
7883
|
-
next(new
|
|
8732
|
+
next(new BadRequestError43("Session ID is required"));
|
|
7884
8733
|
return;
|
|
7885
8734
|
}
|
|
7886
8735
|
try {
|
|
7887
8736
|
await useAuthService().logout(sid);
|
|
7888
8737
|
res.json({ message: "Logged out successfully" });
|
|
7889
8738
|
} catch (error) {
|
|
7890
|
-
if (error instanceof
|
|
8739
|
+
if (error instanceof AppError19) {
|
|
7891
8740
|
next(error);
|
|
7892
8741
|
} else {
|
|
7893
|
-
next(new
|
|
8742
|
+
next(new InternalServerError23("An unexpected error occurred"));
|
|
7894
8743
|
}
|
|
7895
8744
|
}
|
|
7896
8745
|
}
|
|
@@ -7901,64 +8750,64 @@ function useAuthController() {
|
|
|
7901
8750
|
}
|
|
7902
8751
|
|
|
7903
8752
|
// src/resources/building/building.model.ts
|
|
7904
|
-
import { BadRequestError as
|
|
7905
|
-
import
|
|
7906
|
-
import { ObjectId as
|
|
7907
|
-
var schemaBuilding =
|
|
7908
|
-
_id:
|
|
7909
|
-
school:
|
|
7910
|
-
serial:
|
|
7911
|
-
name:
|
|
7912
|
-
levels:
|
|
7913
|
-
createdAt:
|
|
7914
|
-
updatedAt:
|
|
7915
|
-
deletedAt:
|
|
7916
|
-
status:
|
|
8753
|
+
import { BadRequestError as BadRequestError44, logger as logger23 } from "@goweekdays/utils";
|
|
8754
|
+
import Joi37 from "joi";
|
|
8755
|
+
import { ObjectId as ObjectId22 } from "mongodb";
|
|
8756
|
+
var schemaBuilding = Joi37.object({
|
|
8757
|
+
_id: Joi37.string().hex().optional(),
|
|
8758
|
+
school: Joi37.string().hex().required(),
|
|
8759
|
+
serial: Joi37.string().optional().allow("", null),
|
|
8760
|
+
name: Joi37.string().required(),
|
|
8761
|
+
levels: Joi37.number().integer().min(1).required(),
|
|
8762
|
+
createdAt: Joi37.date().optional().allow("", null),
|
|
8763
|
+
updatedAt: Joi37.date().optional().allow("", null),
|
|
8764
|
+
deletedAt: Joi37.date().optional().allow("", null),
|
|
8765
|
+
status: Joi37.string().optional().allow("", null)
|
|
7917
8766
|
});
|
|
7918
|
-
var schemaBuildingUnit =
|
|
7919
|
-
_id:
|
|
7920
|
-
school:
|
|
7921
|
-
name:
|
|
7922
|
-
building:
|
|
7923
|
-
buildingName:
|
|
7924
|
-
level:
|
|
7925
|
-
category:
|
|
7926
|
-
type:
|
|
7927
|
-
seating_capacity:
|
|
7928
|
-
standing_capacity:
|
|
7929
|
-
description:
|
|
7930
|
-
unit_of_measurement:
|
|
7931
|
-
area:
|
|
7932
|
-
status:
|
|
8767
|
+
var schemaBuildingUnit = Joi37.object({
|
|
8768
|
+
_id: Joi37.string().hex().optional(),
|
|
8769
|
+
school: Joi37.string().hex().required(),
|
|
8770
|
+
name: Joi37.string().optional().allow("", null),
|
|
8771
|
+
building: Joi37.string().hex().required(),
|
|
8772
|
+
buildingName: Joi37.string().optional().allow("", null),
|
|
8773
|
+
level: Joi37.number().integer().min(1).required(),
|
|
8774
|
+
category: Joi37.string().required(),
|
|
8775
|
+
type: Joi37.string().required(),
|
|
8776
|
+
seating_capacity: Joi37.number().integer().min(0).required(),
|
|
8777
|
+
standing_capacity: Joi37.number().integer().min(0).required(),
|
|
8778
|
+
description: Joi37.string().optional().allow("", null),
|
|
8779
|
+
unit_of_measurement: Joi37.string().valid("sqm").required(),
|
|
8780
|
+
area: Joi37.number().positive().required(),
|
|
8781
|
+
status: Joi37.string().optional().allow("", null)
|
|
7933
8782
|
});
|
|
7934
|
-
var schemaUpdateOptions =
|
|
7935
|
-
name:
|
|
7936
|
-
building:
|
|
7937
|
-
buildingName:
|
|
7938
|
-
level:
|
|
7939
|
-
category:
|
|
7940
|
-
type:
|
|
7941
|
-
seating_capacity:
|
|
7942
|
-
standing_capacity:
|
|
7943
|
-
area:
|
|
8783
|
+
var schemaUpdateOptions = Joi37.object({
|
|
8784
|
+
name: Joi37.string().optional().allow("", null),
|
|
8785
|
+
building: Joi37.string().hex().optional().allow("", null),
|
|
8786
|
+
buildingName: Joi37.string().optional().allow("", null),
|
|
8787
|
+
level: Joi37.number().integer().min(1).optional().allow("", null),
|
|
8788
|
+
category: Joi37.string().optional().allow("", null),
|
|
8789
|
+
type: Joi37.string().optional().allow("", null),
|
|
8790
|
+
seating_capacity: Joi37.number().integer().min(0).optional().allow("", null),
|
|
8791
|
+
standing_capacity: Joi37.number().integer().min(0).optional().allow("", null),
|
|
8792
|
+
area: Joi37.number().positive().optional().allow("", null)
|
|
7944
8793
|
});
|
|
7945
8794
|
function MBuilding(value) {
|
|
7946
8795
|
const { error } = schemaBuilding.validate(value);
|
|
7947
8796
|
if (error) {
|
|
7948
|
-
|
|
7949
|
-
throw new
|
|
8797
|
+
logger23.info(`Building Model: ${error.message}`);
|
|
8798
|
+
throw new BadRequestError44(error.message);
|
|
7950
8799
|
}
|
|
7951
8800
|
if (value._id && typeof value._id === "string") {
|
|
7952
8801
|
try {
|
|
7953
|
-
value._id = new
|
|
8802
|
+
value._id = new ObjectId22(value._id);
|
|
7954
8803
|
} catch (error2) {
|
|
7955
|
-
throw new
|
|
8804
|
+
throw new BadRequestError44("Invalid _id format");
|
|
7956
8805
|
}
|
|
7957
8806
|
}
|
|
7958
8807
|
try {
|
|
7959
|
-
value.school = new
|
|
8808
|
+
value.school = new ObjectId22(value.school);
|
|
7960
8809
|
} catch (error2) {
|
|
7961
|
-
throw new
|
|
8810
|
+
throw new BadRequestError44("Invalid school format");
|
|
7962
8811
|
}
|
|
7963
8812
|
return {
|
|
7964
8813
|
_id: value._id ?? void 0,
|
|
@@ -7975,25 +8824,25 @@ function MBuilding(value) {
|
|
|
7975
8824
|
function MBuildingUnit(value) {
|
|
7976
8825
|
const { error } = schemaBuildingUnit.validate(value);
|
|
7977
8826
|
if (error) {
|
|
7978
|
-
|
|
7979
|
-
throw new
|
|
8827
|
+
logger23.info(`Building Unit Model: ${error.message}`);
|
|
8828
|
+
throw new BadRequestError44(error.message);
|
|
7980
8829
|
}
|
|
7981
8830
|
if (value._id && typeof value._id === "string") {
|
|
7982
8831
|
try {
|
|
7983
|
-
value._id = new
|
|
8832
|
+
value._id = new ObjectId22(value._id);
|
|
7984
8833
|
} catch (error2) {
|
|
7985
|
-
throw new
|
|
8834
|
+
throw new BadRequestError44("Invalid ID");
|
|
7986
8835
|
}
|
|
7987
8836
|
}
|
|
7988
8837
|
try {
|
|
7989
|
-
value.school = new
|
|
8838
|
+
value.school = new ObjectId22(value.school);
|
|
7990
8839
|
} catch (error2) {
|
|
7991
|
-
throw new
|
|
8840
|
+
throw new BadRequestError44("Invalid school ID");
|
|
7992
8841
|
}
|
|
7993
8842
|
try {
|
|
7994
|
-
value.building = new
|
|
8843
|
+
value.building = new ObjectId22(value.building);
|
|
7995
8844
|
} catch (error2) {
|
|
7996
|
-
throw new
|
|
8845
|
+
throw new BadRequestError44("Invalid building ID");
|
|
7997
8846
|
}
|
|
7998
8847
|
return {
|
|
7999
8848
|
_id: value._id ?? void 0,
|
|
@@ -8018,24 +8867,24 @@ function MBuildingUnit(value) {
|
|
|
8018
8867
|
|
|
8019
8868
|
// src/resources/building/building.repository.ts
|
|
8020
8869
|
import {
|
|
8021
|
-
AppError as
|
|
8022
|
-
BadRequestError as
|
|
8023
|
-
InternalServerError as
|
|
8024
|
-
logger as
|
|
8025
|
-
makeCacheKey as
|
|
8026
|
-
paginate as
|
|
8027
|
-
useAtlas as
|
|
8028
|
-
useCache as
|
|
8870
|
+
AppError as AppError20,
|
|
8871
|
+
BadRequestError as BadRequestError45,
|
|
8872
|
+
InternalServerError as InternalServerError24,
|
|
8873
|
+
logger as logger24,
|
|
8874
|
+
makeCacheKey as makeCacheKey15,
|
|
8875
|
+
paginate as paginate13,
|
|
8876
|
+
useAtlas as useAtlas20,
|
|
8877
|
+
useCache as useCache16
|
|
8029
8878
|
} from "@goweekdays/utils";
|
|
8030
|
-
import { ObjectId as
|
|
8879
|
+
import { ObjectId as ObjectId23 } from "mongodb";
|
|
8031
8880
|
function useBuildingRepo() {
|
|
8032
|
-
const db =
|
|
8881
|
+
const db = useAtlas20.getDb();
|
|
8033
8882
|
if (!db) {
|
|
8034
8883
|
throw new Error("Unable to connect to server.");
|
|
8035
8884
|
}
|
|
8036
8885
|
const namespace_collection = "school.buildings";
|
|
8037
8886
|
const collection = db.collection(namespace_collection);
|
|
8038
|
-
const { getCache, setCache, delNamespace } =
|
|
8887
|
+
const { getCache, setCache, delNamespace } = useCache16(namespace_collection);
|
|
8039
8888
|
async function createIndexes() {
|
|
8040
8889
|
try {
|
|
8041
8890
|
await collection.createIndexes([
|
|
@@ -8054,16 +8903,16 @@ function useBuildingRepo() {
|
|
|
8054
8903
|
delCachedData();
|
|
8055
8904
|
return res.insertedId;
|
|
8056
8905
|
} catch (error) {
|
|
8057
|
-
|
|
8906
|
+
logger24.log({
|
|
8058
8907
|
level: "error",
|
|
8059
8908
|
message: error.message
|
|
8060
8909
|
});
|
|
8061
|
-
if (error instanceof
|
|
8910
|
+
if (error instanceof AppError20) {
|
|
8062
8911
|
throw error;
|
|
8063
8912
|
} else {
|
|
8064
8913
|
const isDuplicated = error.message.includes("duplicate");
|
|
8065
8914
|
if (isDuplicated) {
|
|
8066
|
-
throw new
|
|
8915
|
+
throw new BadRequestError45("Building already exists.");
|
|
8067
8916
|
}
|
|
8068
8917
|
throw new Error("Failed to create building.");
|
|
8069
8918
|
}
|
|
@@ -8071,9 +8920,9 @@ function useBuildingRepo() {
|
|
|
8071
8920
|
}
|
|
8072
8921
|
async function updateById(_id, value, session) {
|
|
8073
8922
|
try {
|
|
8074
|
-
_id = new
|
|
8923
|
+
_id = new ObjectId23(_id);
|
|
8075
8924
|
} catch (error) {
|
|
8076
|
-
throw new
|
|
8925
|
+
throw new BadRequestError45("Invalid ID.");
|
|
8077
8926
|
}
|
|
8078
8927
|
try {
|
|
8079
8928
|
const res = await collection.updateOne(
|
|
@@ -8084,11 +8933,11 @@ function useBuildingRepo() {
|
|
|
8084
8933
|
delCachedData();
|
|
8085
8934
|
return res;
|
|
8086
8935
|
} catch (error) {
|
|
8087
|
-
|
|
8936
|
+
logger24.log({
|
|
8088
8937
|
level: "error",
|
|
8089
8938
|
message: error.message
|
|
8090
8939
|
});
|
|
8091
|
-
if (error instanceof
|
|
8940
|
+
if (error instanceof AppError20) {
|
|
8092
8941
|
throw error;
|
|
8093
8942
|
} else {
|
|
8094
8943
|
throw new Error("Failed to update building.");
|
|
@@ -8113,9 +8962,9 @@ function useBuildingRepo() {
|
|
|
8113
8962
|
}
|
|
8114
8963
|
if (school) {
|
|
8115
8964
|
try {
|
|
8116
|
-
query.school = new
|
|
8965
|
+
query.school = new ObjectId23(school);
|
|
8117
8966
|
} catch (error) {
|
|
8118
|
-
throw new
|
|
8967
|
+
throw new BadRequestError45("Invalid school ID.");
|
|
8119
8968
|
}
|
|
8120
8969
|
}
|
|
8121
8970
|
const cacheParams = {
|
|
@@ -8129,15 +8978,15 @@ function useBuildingRepo() {
|
|
|
8129
8978
|
cacheParams.school = school;
|
|
8130
8979
|
if (status !== "active")
|
|
8131
8980
|
cacheParams.status = status;
|
|
8132
|
-
const cacheKey =
|
|
8133
|
-
|
|
8981
|
+
const cacheKey = makeCacheKey15(namespace_collection, cacheParams);
|
|
8982
|
+
logger24.log({
|
|
8134
8983
|
level: "info",
|
|
8135
8984
|
message: `Cache key for getAll buildings: ${cacheKey}`
|
|
8136
8985
|
});
|
|
8137
8986
|
try {
|
|
8138
8987
|
const cached = await getCache(cacheKey);
|
|
8139
8988
|
if (cached) {
|
|
8140
|
-
|
|
8989
|
+
logger24.log({
|
|
8141
8990
|
level: "info",
|
|
8142
8991
|
message: `Cache hit for getAll buildings: ${cacheKey}`
|
|
8143
8992
|
});
|
|
@@ -8150,35 +8999,35 @@ function useBuildingRepo() {
|
|
|
8150
8999
|
{ $limit: limit }
|
|
8151
9000
|
]).toArray();
|
|
8152
9001
|
const length = await collection.countDocuments(query);
|
|
8153
|
-
const data =
|
|
9002
|
+
const data = paginate13(items, page, limit, length);
|
|
8154
9003
|
setCache(cacheKey, data, 600).then(() => {
|
|
8155
|
-
|
|
9004
|
+
logger24.log({
|
|
8156
9005
|
level: "info",
|
|
8157
9006
|
message: `Cache set for getAll buildings: ${cacheKey}`
|
|
8158
9007
|
});
|
|
8159
9008
|
}).catch((err) => {
|
|
8160
|
-
|
|
9009
|
+
logger24.log({
|
|
8161
9010
|
level: "error",
|
|
8162
9011
|
message: `Failed to set cache for getAll buildings: ${err.message}`
|
|
8163
9012
|
});
|
|
8164
9013
|
});
|
|
8165
9014
|
return data;
|
|
8166
9015
|
} catch (error) {
|
|
8167
|
-
|
|
9016
|
+
logger24.log({ level: "error", message: `${error}` });
|
|
8168
9017
|
throw error;
|
|
8169
9018
|
}
|
|
8170
9019
|
}
|
|
8171
9020
|
async function getById(_id) {
|
|
8172
9021
|
try {
|
|
8173
|
-
_id = new
|
|
9022
|
+
_id = new ObjectId23(_id);
|
|
8174
9023
|
} catch (error) {
|
|
8175
|
-
throw new
|
|
9024
|
+
throw new BadRequestError45("Invalid ID.");
|
|
8176
9025
|
}
|
|
8177
|
-
const cacheKey =
|
|
9026
|
+
const cacheKey = makeCacheKey15(namespace_collection, { _id: String(_id) });
|
|
8178
9027
|
try {
|
|
8179
9028
|
const cached = await getCache(cacheKey);
|
|
8180
9029
|
if (cached) {
|
|
8181
|
-
|
|
9030
|
+
logger24.log({
|
|
8182
9031
|
level: "info",
|
|
8183
9032
|
message: `Cache hit for getById building: ${cacheKey}`
|
|
8184
9033
|
});
|
|
@@ -8188,30 +9037,30 @@ function useBuildingRepo() {
|
|
|
8188
9037
|
_id
|
|
8189
9038
|
});
|
|
8190
9039
|
setCache(cacheKey, result, 300).then(() => {
|
|
8191
|
-
|
|
9040
|
+
logger24.log({
|
|
8192
9041
|
level: "info",
|
|
8193
9042
|
message: `Cache set for building by id: ${cacheKey}`
|
|
8194
9043
|
});
|
|
8195
9044
|
}).catch((err) => {
|
|
8196
|
-
|
|
9045
|
+
logger24.log({
|
|
8197
9046
|
level: "error",
|
|
8198
9047
|
message: `Failed to set cache for building by id: ${err.message}`
|
|
8199
9048
|
});
|
|
8200
9049
|
});
|
|
8201
9050
|
return result;
|
|
8202
9051
|
} catch (error) {
|
|
8203
|
-
if (error instanceof
|
|
9052
|
+
if (error instanceof AppError20) {
|
|
8204
9053
|
throw error;
|
|
8205
9054
|
} else {
|
|
8206
|
-
throw new
|
|
9055
|
+
throw new InternalServerError24("Failed to get building.");
|
|
8207
9056
|
}
|
|
8208
9057
|
}
|
|
8209
9058
|
}
|
|
8210
9059
|
async function deleteById(_id, session) {
|
|
8211
9060
|
try {
|
|
8212
|
-
_id = new
|
|
9061
|
+
_id = new ObjectId23(_id);
|
|
8213
9062
|
} catch (error) {
|
|
8214
|
-
throw new
|
|
9063
|
+
throw new BadRequestError45("Invalid ID.");
|
|
8215
9064
|
}
|
|
8216
9065
|
try {
|
|
8217
9066
|
const res = await collection.updateOne(
|
|
@@ -8221,25 +9070,25 @@ function useBuildingRepo() {
|
|
|
8221
9070
|
delCachedData();
|
|
8222
9071
|
return res;
|
|
8223
9072
|
} catch (error) {
|
|
8224
|
-
|
|
9073
|
+
logger24.log({
|
|
8225
9074
|
level: "error",
|
|
8226
9075
|
message: error.message
|
|
8227
9076
|
});
|
|
8228
|
-
if (error instanceof
|
|
9077
|
+
if (error instanceof AppError20) {
|
|
8229
9078
|
throw error;
|
|
8230
9079
|
} else {
|
|
8231
|
-
throw new
|
|
9080
|
+
throw new InternalServerError24("Failed to delete building.");
|
|
8232
9081
|
}
|
|
8233
9082
|
}
|
|
8234
9083
|
}
|
|
8235
9084
|
function delCachedData() {
|
|
8236
9085
|
delNamespace().then(() => {
|
|
8237
|
-
|
|
9086
|
+
logger24.log({
|
|
8238
9087
|
level: "info",
|
|
8239
9088
|
message: `Cache namespace cleared for ${namespace_collection}`
|
|
8240
9089
|
});
|
|
8241
9090
|
}).catch((err) => {
|
|
8242
|
-
|
|
9091
|
+
logger24.log({
|
|
8243
9092
|
level: "error",
|
|
8244
9093
|
message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
|
|
8245
9094
|
});
|
|
@@ -8256,28 +9105,28 @@ function useBuildingRepo() {
|
|
|
8256
9105
|
}
|
|
8257
9106
|
|
|
8258
9107
|
// src/resources/building/building.service.ts
|
|
8259
|
-
import { BadRequestError as
|
|
9108
|
+
import { BadRequestError as BadRequestError47, NotFoundError as NotFoundError4, useAtlas as useAtlas22 } from "@goweekdays/utils";
|
|
8260
9109
|
|
|
8261
9110
|
// src/resources/building/building-unit.repository.ts
|
|
8262
9111
|
import {
|
|
8263
|
-
AppError as
|
|
8264
|
-
BadRequestError as
|
|
8265
|
-
InternalServerError as
|
|
8266
|
-
logger as
|
|
8267
|
-
makeCacheKey as
|
|
8268
|
-
paginate as
|
|
8269
|
-
useAtlas as
|
|
8270
|
-
useCache as
|
|
9112
|
+
AppError as AppError21,
|
|
9113
|
+
BadRequestError as BadRequestError46,
|
|
9114
|
+
InternalServerError as InternalServerError25,
|
|
9115
|
+
logger as logger25,
|
|
9116
|
+
makeCacheKey as makeCacheKey16,
|
|
9117
|
+
paginate as paginate14,
|
|
9118
|
+
useAtlas as useAtlas21,
|
|
9119
|
+
useCache as useCache17
|
|
8271
9120
|
} from "@goweekdays/utils";
|
|
8272
|
-
import { ObjectId as
|
|
9121
|
+
import { ObjectId as ObjectId24 } from "mongodb";
|
|
8273
9122
|
function useBuildingUnitRepo() {
|
|
8274
|
-
const db =
|
|
9123
|
+
const db = useAtlas21.getDb();
|
|
8275
9124
|
if (!db) {
|
|
8276
9125
|
throw new Error("Unable to connect to server.");
|
|
8277
9126
|
}
|
|
8278
9127
|
const namespace_collection = "school.building-units";
|
|
8279
9128
|
const collection = db.collection(namespace_collection);
|
|
8280
|
-
const { getCache, setCache, delNamespace } =
|
|
9129
|
+
const { getCache, setCache, delNamespace } = useCache17(namespace_collection);
|
|
8281
9130
|
async function createIndexes() {
|
|
8282
9131
|
try {
|
|
8283
9132
|
await collection.createIndexes([
|
|
@@ -8305,12 +9154,12 @@ function useBuildingUnitRepo() {
|
|
|
8305
9154
|
}
|
|
8306
9155
|
function delCachedData() {
|
|
8307
9156
|
delNamespace().then(() => {
|
|
8308
|
-
|
|
9157
|
+
logger25.log({
|
|
8309
9158
|
level: "info",
|
|
8310
9159
|
message: `Cache namespace cleared for ${namespace_collection}`
|
|
8311
9160
|
});
|
|
8312
9161
|
}).catch((err) => {
|
|
8313
|
-
|
|
9162
|
+
logger25.log({
|
|
8314
9163
|
level: "error",
|
|
8315
9164
|
message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
|
|
8316
9165
|
});
|
|
@@ -8323,11 +9172,11 @@ function useBuildingUnitRepo() {
|
|
|
8323
9172
|
delCachedData();
|
|
8324
9173
|
return res.insertedId;
|
|
8325
9174
|
} catch (error) {
|
|
8326
|
-
|
|
9175
|
+
logger25.log({
|
|
8327
9176
|
level: "error",
|
|
8328
9177
|
message: error.message
|
|
8329
9178
|
});
|
|
8330
|
-
if (error instanceof
|
|
9179
|
+
if (error instanceof AppError21) {
|
|
8331
9180
|
throw error;
|
|
8332
9181
|
} else {
|
|
8333
9182
|
throw new Error("Failed to create building unit.");
|
|
@@ -8337,12 +9186,12 @@ function useBuildingUnitRepo() {
|
|
|
8337
9186
|
async function updateById(_id, value, session) {
|
|
8338
9187
|
const { error } = schemaUpdateOptions.validate(value);
|
|
8339
9188
|
if (error) {
|
|
8340
|
-
throw new
|
|
9189
|
+
throw new BadRequestError46(error.message);
|
|
8341
9190
|
}
|
|
8342
9191
|
try {
|
|
8343
|
-
_id = new
|
|
9192
|
+
_id = new ObjectId24(_id);
|
|
8344
9193
|
} catch (error2) {
|
|
8345
|
-
throw new
|
|
9194
|
+
throw new BadRequestError46("Invalid ID.");
|
|
8346
9195
|
}
|
|
8347
9196
|
try {
|
|
8348
9197
|
const res = await collection.updateOne(
|
|
@@ -8353,11 +9202,11 @@ function useBuildingUnitRepo() {
|
|
|
8353
9202
|
delCachedData();
|
|
8354
9203
|
return res;
|
|
8355
9204
|
} catch (error2) {
|
|
8356
|
-
|
|
9205
|
+
logger25.log({
|
|
8357
9206
|
level: "error",
|
|
8358
9207
|
message: error2.message
|
|
8359
9208
|
});
|
|
8360
|
-
if (error2 instanceof
|
|
9209
|
+
if (error2 instanceof AppError21) {
|
|
8361
9210
|
throw error2;
|
|
8362
9211
|
} else {
|
|
8363
9212
|
throw new Error("Failed to create building unit.");
|
|
@@ -8367,12 +9216,12 @@ function useBuildingUnitRepo() {
|
|
|
8367
9216
|
async function updateByBuildingId(building, value, session) {
|
|
8368
9217
|
const { error } = schemaUpdateOptions.validate(value);
|
|
8369
9218
|
if (error) {
|
|
8370
|
-
throw new
|
|
9219
|
+
throw new BadRequestError46(error.message);
|
|
8371
9220
|
}
|
|
8372
9221
|
try {
|
|
8373
|
-
building = new
|
|
9222
|
+
building = new ObjectId24(building);
|
|
8374
9223
|
} catch (error2) {
|
|
8375
|
-
throw new
|
|
9224
|
+
throw new BadRequestError46("Invalid building ID.");
|
|
8376
9225
|
}
|
|
8377
9226
|
try {
|
|
8378
9227
|
const res = await collection.updateMany(
|
|
@@ -8383,11 +9232,11 @@ function useBuildingUnitRepo() {
|
|
|
8383
9232
|
delCachedData();
|
|
8384
9233
|
return res;
|
|
8385
9234
|
} catch (error2) {
|
|
8386
|
-
|
|
9235
|
+
logger25.log({
|
|
8387
9236
|
level: "error",
|
|
8388
9237
|
message: error2.message
|
|
8389
9238
|
});
|
|
8390
|
-
if (error2 instanceof
|
|
9239
|
+
if (error2 instanceof AppError21) {
|
|
8391
9240
|
throw error2;
|
|
8392
9241
|
} else {
|
|
8393
9242
|
throw new Error("Failed to update building unit.");
|
|
@@ -8414,16 +9263,16 @@ function useBuildingUnitRepo() {
|
|
|
8414
9263
|
}
|
|
8415
9264
|
if (school) {
|
|
8416
9265
|
try {
|
|
8417
|
-
query.school = new
|
|
9266
|
+
query.school = new ObjectId24(school);
|
|
8418
9267
|
} catch (error) {
|
|
8419
|
-
throw new
|
|
9268
|
+
throw new BadRequestError46("Invalid school ID.");
|
|
8420
9269
|
}
|
|
8421
9270
|
}
|
|
8422
9271
|
if (building) {
|
|
8423
9272
|
try {
|
|
8424
|
-
query.building = new
|
|
9273
|
+
query.building = new ObjectId24(building);
|
|
8425
9274
|
} catch (error) {
|
|
8426
|
-
throw new
|
|
9275
|
+
throw new BadRequestError46("Invalid building ID.");
|
|
8427
9276
|
}
|
|
8428
9277
|
}
|
|
8429
9278
|
const cacheParams = {
|
|
@@ -8439,15 +9288,15 @@ function useBuildingUnitRepo() {
|
|
|
8439
9288
|
cacheParams.building = building;
|
|
8440
9289
|
if (status !== "active")
|
|
8441
9290
|
cacheParams.status = status;
|
|
8442
|
-
const cacheKey =
|
|
8443
|
-
|
|
9291
|
+
const cacheKey = makeCacheKey16(namespace_collection, cacheParams);
|
|
9292
|
+
logger25.log({
|
|
8444
9293
|
level: "info",
|
|
8445
9294
|
message: `Cache key for getAll building units: ${cacheKey}`
|
|
8446
9295
|
});
|
|
8447
9296
|
try {
|
|
8448
9297
|
const cached = await getCache(cacheKey);
|
|
8449
9298
|
if (cached) {
|
|
8450
|
-
|
|
9299
|
+
logger25.log({
|
|
8451
9300
|
level: "info",
|
|
8452
9301
|
message: `Cache hit for getAll building units: ${cacheKey}`
|
|
8453
9302
|
});
|
|
@@ -8460,35 +9309,35 @@ function useBuildingUnitRepo() {
|
|
|
8460
9309
|
{ $limit: limit }
|
|
8461
9310
|
]).toArray();
|
|
8462
9311
|
const length = await collection.countDocuments(query);
|
|
8463
|
-
const data =
|
|
9312
|
+
const data = paginate14(items, page, limit, length);
|
|
8464
9313
|
setCache(cacheKey, data, 600).then(() => {
|
|
8465
|
-
|
|
9314
|
+
logger25.log({
|
|
8466
9315
|
level: "info",
|
|
8467
9316
|
message: `Cache set for getAll building units: ${cacheKey}`
|
|
8468
9317
|
});
|
|
8469
9318
|
}).catch((err) => {
|
|
8470
|
-
|
|
9319
|
+
logger25.log({
|
|
8471
9320
|
level: "error",
|
|
8472
9321
|
message: `Failed to set cache for getAll building units: ${err.message}`
|
|
8473
9322
|
});
|
|
8474
9323
|
});
|
|
8475
9324
|
return data;
|
|
8476
9325
|
} catch (error) {
|
|
8477
|
-
|
|
9326
|
+
logger25.log({ level: "error", message: `${error}` });
|
|
8478
9327
|
throw error;
|
|
8479
9328
|
}
|
|
8480
9329
|
}
|
|
8481
9330
|
async function getById(_id) {
|
|
8482
9331
|
try {
|
|
8483
|
-
_id = new
|
|
9332
|
+
_id = new ObjectId24(_id);
|
|
8484
9333
|
} catch (error) {
|
|
8485
|
-
throw new
|
|
9334
|
+
throw new BadRequestError46("Invalid ID.");
|
|
8486
9335
|
}
|
|
8487
|
-
const cacheKey =
|
|
9336
|
+
const cacheKey = makeCacheKey16(namespace_collection, { _id: String(_id) });
|
|
8488
9337
|
try {
|
|
8489
9338
|
const cached = await getCache(cacheKey);
|
|
8490
9339
|
if (cached) {
|
|
8491
|
-
|
|
9340
|
+
logger25.log({
|
|
8492
9341
|
level: "info",
|
|
8493
9342
|
message: `Cache hit for getById building unit: ${cacheKey}`
|
|
8494
9343
|
});
|
|
@@ -8499,42 +9348,42 @@ function useBuildingUnitRepo() {
|
|
|
8499
9348
|
deletedAt: { $in: ["", null] }
|
|
8500
9349
|
});
|
|
8501
9350
|
if (!result) {
|
|
8502
|
-
throw new
|
|
9351
|
+
throw new BadRequestError46("Building unit not found.");
|
|
8503
9352
|
}
|
|
8504
9353
|
setCache(cacheKey, result, 300).then(() => {
|
|
8505
|
-
|
|
9354
|
+
logger25.log({
|
|
8506
9355
|
level: "info",
|
|
8507
9356
|
message: `Cache set for building unit by id: ${cacheKey}`
|
|
8508
9357
|
});
|
|
8509
9358
|
}).catch((err) => {
|
|
8510
|
-
|
|
9359
|
+
logger25.log({
|
|
8511
9360
|
level: "error",
|
|
8512
9361
|
message: `Failed to set cache for building unit by id: ${err.message}`
|
|
8513
9362
|
});
|
|
8514
9363
|
});
|
|
8515
9364
|
return result;
|
|
8516
9365
|
} catch (error) {
|
|
8517
|
-
if (error instanceof
|
|
9366
|
+
if (error instanceof AppError21) {
|
|
8518
9367
|
throw error;
|
|
8519
9368
|
} else {
|
|
8520
|
-
throw new
|
|
9369
|
+
throw new InternalServerError25("Failed to get building unit.");
|
|
8521
9370
|
}
|
|
8522
9371
|
}
|
|
8523
9372
|
}
|
|
8524
9373
|
async function getByBuildingLevel(building, level) {
|
|
8525
9374
|
try {
|
|
8526
|
-
building = new
|
|
9375
|
+
building = new ObjectId24(building);
|
|
8527
9376
|
} catch (error) {
|
|
8528
|
-
throw new
|
|
9377
|
+
throw new BadRequestError46("Invalid building ID.");
|
|
8529
9378
|
}
|
|
8530
|
-
const cacheKey =
|
|
9379
|
+
const cacheKey = makeCacheKey16(namespace_collection, {
|
|
8531
9380
|
building: String(building),
|
|
8532
9381
|
level
|
|
8533
9382
|
});
|
|
8534
9383
|
try {
|
|
8535
9384
|
const cached = await getCache(cacheKey);
|
|
8536
9385
|
if (cached) {
|
|
8537
|
-
|
|
9386
|
+
logger25.log({
|
|
8538
9387
|
level: "info",
|
|
8539
9388
|
message: `Cache hit for getById building unit: ${cacheKey}`
|
|
8540
9389
|
});
|
|
@@ -8546,38 +9395,38 @@ function useBuildingUnitRepo() {
|
|
|
8546
9395
|
status: "active"
|
|
8547
9396
|
});
|
|
8548
9397
|
setCache(cacheKey, result, 300).then(() => {
|
|
8549
|
-
|
|
9398
|
+
logger25.log({
|
|
8550
9399
|
level: "info",
|
|
8551
9400
|
message: `Cache set for building unit by id: ${cacheKey}`
|
|
8552
9401
|
});
|
|
8553
9402
|
}).catch((err) => {
|
|
8554
|
-
|
|
9403
|
+
logger25.log({
|
|
8555
9404
|
level: "error",
|
|
8556
9405
|
message: `Failed to set cache for building unit by id: ${err.message}`
|
|
8557
9406
|
});
|
|
8558
9407
|
});
|
|
8559
9408
|
return result;
|
|
8560
9409
|
} catch (error) {
|
|
8561
|
-
if (error instanceof
|
|
9410
|
+
if (error instanceof AppError21) {
|
|
8562
9411
|
throw error;
|
|
8563
9412
|
} else {
|
|
8564
|
-
throw new
|
|
9413
|
+
throw new InternalServerError25("Failed to get building unit.");
|
|
8565
9414
|
}
|
|
8566
9415
|
}
|
|
8567
9416
|
}
|
|
8568
9417
|
async function getByBuilding(building) {
|
|
8569
9418
|
try {
|
|
8570
|
-
building = new
|
|
9419
|
+
building = new ObjectId24(building);
|
|
8571
9420
|
} catch (error) {
|
|
8572
|
-
throw new
|
|
9421
|
+
throw new BadRequestError46("Invalid building ID.");
|
|
8573
9422
|
}
|
|
8574
|
-
const cacheKey =
|
|
9423
|
+
const cacheKey = makeCacheKey16(namespace_collection, {
|
|
8575
9424
|
building: String(building)
|
|
8576
9425
|
});
|
|
8577
9426
|
try {
|
|
8578
9427
|
const cached = await getCache(cacheKey);
|
|
8579
9428
|
if (cached) {
|
|
8580
|
-
|
|
9429
|
+
logger25.log({
|
|
8581
9430
|
level: "info",
|
|
8582
9431
|
message: `Cache hit for getById building unit: ${cacheKey}`
|
|
8583
9432
|
});
|
|
@@ -8588,30 +9437,30 @@ function useBuildingUnitRepo() {
|
|
|
8588
9437
|
status: "active"
|
|
8589
9438
|
});
|
|
8590
9439
|
setCache(cacheKey, result, 300).then(() => {
|
|
8591
|
-
|
|
9440
|
+
logger25.log({
|
|
8592
9441
|
level: "info",
|
|
8593
9442
|
message: `Cache set for building unit by id: ${cacheKey}`
|
|
8594
9443
|
});
|
|
8595
9444
|
}).catch((err) => {
|
|
8596
|
-
|
|
9445
|
+
logger25.log({
|
|
8597
9446
|
level: "error",
|
|
8598
9447
|
message: `Failed to set cache for building unit by id: ${err.message}`
|
|
8599
9448
|
});
|
|
8600
9449
|
});
|
|
8601
9450
|
return result;
|
|
8602
9451
|
} catch (error) {
|
|
8603
|
-
if (error instanceof
|
|
9452
|
+
if (error instanceof AppError21) {
|
|
8604
9453
|
throw error;
|
|
8605
9454
|
} else {
|
|
8606
|
-
throw new
|
|
9455
|
+
throw new InternalServerError25("Failed to get building unit.");
|
|
8607
9456
|
}
|
|
8608
9457
|
}
|
|
8609
9458
|
}
|
|
8610
9459
|
async function deleteById(_id, session) {
|
|
8611
9460
|
try {
|
|
8612
|
-
_id = new
|
|
9461
|
+
_id = new ObjectId24(_id);
|
|
8613
9462
|
} catch (error) {
|
|
8614
|
-
throw new
|
|
9463
|
+
throw new BadRequestError46("Invalid ID.");
|
|
8615
9464
|
}
|
|
8616
9465
|
try {
|
|
8617
9466
|
const res = await collection.updateOne(
|
|
@@ -8622,11 +9471,11 @@ function useBuildingUnitRepo() {
|
|
|
8622
9471
|
delCachedData();
|
|
8623
9472
|
return "Room/Facility deleted successfully.";
|
|
8624
9473
|
} catch (error) {
|
|
8625
|
-
|
|
9474
|
+
logger25.log({
|
|
8626
9475
|
level: "error",
|
|
8627
9476
|
message: error.message
|
|
8628
9477
|
});
|
|
8629
|
-
if (error instanceof
|
|
9478
|
+
if (error instanceof AppError21) {
|
|
8630
9479
|
throw error;
|
|
8631
9480
|
} else {
|
|
8632
9481
|
throw new Error("Failed to deleted room/facility.");
|
|
@@ -8656,7 +9505,7 @@ function useBuildingService() {
|
|
|
8656
9505
|
const { getByBuildingLevel, getByBuilding, updateByBuildingId } = useBuildingUnitRepo();
|
|
8657
9506
|
async function updateById(id, data) {
|
|
8658
9507
|
data.levels = Number(data.levels);
|
|
8659
|
-
const session =
|
|
9508
|
+
const session = useAtlas22.getClient()?.startSession();
|
|
8660
9509
|
try {
|
|
8661
9510
|
const building = await _getById(id);
|
|
8662
9511
|
if (!building) {
|
|
@@ -8665,7 +9514,7 @@ function useBuildingService() {
|
|
|
8665
9514
|
if (data.levels < building.levels) {
|
|
8666
9515
|
const unit = await getByBuildingLevel(id, building.levels);
|
|
8667
9516
|
if (unit) {
|
|
8668
|
-
throw new
|
|
9517
|
+
throw new BadRequestError47(
|
|
8669
9518
|
"Cannot reduce floors, there are existing building units at higher floors."
|
|
8670
9519
|
);
|
|
8671
9520
|
}
|
|
@@ -8687,7 +9536,7 @@ function useBuildingService() {
|
|
|
8687
9536
|
async function deleteById(id) {
|
|
8688
9537
|
const building = await getByBuilding(id);
|
|
8689
9538
|
if (building) {
|
|
8690
|
-
throw new
|
|
9539
|
+
throw new BadRequestError47(
|
|
8691
9540
|
"Cannot delete building with existing room/facility. Please delete room/facility first."
|
|
8692
9541
|
);
|
|
8693
9542
|
}
|
|
@@ -8705,24 +9554,24 @@ function useBuildingService() {
|
|
|
8705
9554
|
}
|
|
8706
9555
|
|
|
8707
9556
|
// src/resources/building/building.controller.ts
|
|
8708
|
-
import { BadRequestError as
|
|
8709
|
-
import
|
|
9557
|
+
import { BadRequestError as BadRequestError48, logger as logger26 } from "@goweekdays/utils";
|
|
9558
|
+
import Joi38 from "joi";
|
|
8710
9559
|
function useBuildingController() {
|
|
8711
9560
|
const { getAll: _getAll, getById: _getById, add: _add } = useBuildingRepo();
|
|
8712
9561
|
const { updateById: _updateById, deleteById: _deleteById } = useBuildingService();
|
|
8713
9562
|
async function createBuilding(req, res, next) {
|
|
8714
9563
|
const value = req.body;
|
|
8715
|
-
const validation =
|
|
8716
|
-
name:
|
|
8717
|
-
school:
|
|
8718
|
-
levels:
|
|
8719
|
-
serial:
|
|
8720
|
-
status:
|
|
9564
|
+
const validation = Joi38.object({
|
|
9565
|
+
name: Joi38.string().required(),
|
|
9566
|
+
school: Joi38.string().hex().required(),
|
|
9567
|
+
levels: Joi38.number().integer().min(1).required(),
|
|
9568
|
+
serial: Joi38.string().optional().allow("", null),
|
|
9569
|
+
status: Joi38.string().optional().allow("", null)
|
|
8721
9570
|
});
|
|
8722
9571
|
const { error } = validation.validate(value);
|
|
8723
9572
|
if (error) {
|
|
8724
|
-
next(new
|
|
8725
|
-
|
|
9573
|
+
next(new BadRequestError48(error.message));
|
|
9574
|
+
logger26.info(`Controller: ${error.message}`);
|
|
8726
9575
|
return;
|
|
8727
9576
|
}
|
|
8728
9577
|
try {
|
|
@@ -8736,18 +9585,18 @@ function useBuildingController() {
|
|
|
8736
9585
|
async function updateById(req, res, next) {
|
|
8737
9586
|
const value = req.body;
|
|
8738
9587
|
const id = req.params.id ?? "";
|
|
8739
|
-
const validation =
|
|
8740
|
-
id:
|
|
8741
|
-
value:
|
|
8742
|
-
name:
|
|
8743
|
-
serial:
|
|
8744
|
-
levels:
|
|
9588
|
+
const validation = Joi38.object({
|
|
9589
|
+
id: Joi38.string().hex().required(),
|
|
9590
|
+
value: Joi38.object({
|
|
9591
|
+
name: Joi38.string().required(),
|
|
9592
|
+
serial: Joi38.string().optional().allow("", null),
|
|
9593
|
+
levels: Joi38.number().integer().min(1).required()
|
|
8745
9594
|
})
|
|
8746
9595
|
});
|
|
8747
9596
|
const { error } = validation.validate({ id, value });
|
|
8748
9597
|
if (error) {
|
|
8749
|
-
next(new
|
|
8750
|
-
|
|
9598
|
+
next(new BadRequestError48(error.message));
|
|
9599
|
+
logger26.info(`Controller: ${error.message}`);
|
|
8751
9600
|
return;
|
|
8752
9601
|
}
|
|
8753
9602
|
try {
|
|
@@ -8760,16 +9609,16 @@ function useBuildingController() {
|
|
|
8760
9609
|
}
|
|
8761
9610
|
async function getAll(req, res, next) {
|
|
8762
9611
|
const query = req.query;
|
|
8763
|
-
const validation =
|
|
8764
|
-
page:
|
|
8765
|
-
limit:
|
|
8766
|
-
search:
|
|
8767
|
-
school:
|
|
8768
|
-
status:
|
|
9612
|
+
const validation = Joi38.object({
|
|
9613
|
+
page: Joi38.number().min(1).optional().allow("", null),
|
|
9614
|
+
limit: Joi38.number().min(1).optional().allow("", null),
|
|
9615
|
+
search: Joi38.string().optional().allow("", null),
|
|
9616
|
+
school: Joi38.string().hex().optional().allow("", null),
|
|
9617
|
+
status: Joi38.string().optional().allow("", null)
|
|
8769
9618
|
});
|
|
8770
9619
|
const { error } = validation.validate(query);
|
|
8771
9620
|
if (error) {
|
|
8772
|
-
next(new
|
|
9621
|
+
next(new BadRequestError48(error.message));
|
|
8773
9622
|
return;
|
|
8774
9623
|
}
|
|
8775
9624
|
const page = parseInt(req.query.page) ?? 1;
|
|
@@ -8803,12 +9652,12 @@ function useBuildingController() {
|
|
|
8803
9652
|
}
|
|
8804
9653
|
async function getById(req, res, next) {
|
|
8805
9654
|
const id = req.params.id;
|
|
8806
|
-
const validation =
|
|
8807
|
-
id:
|
|
9655
|
+
const validation = Joi38.object({
|
|
9656
|
+
id: Joi38.string().hex().required()
|
|
8808
9657
|
});
|
|
8809
9658
|
const { error } = validation.validate({ id });
|
|
8810
9659
|
if (error) {
|
|
8811
|
-
next(new
|
|
9660
|
+
next(new BadRequestError48(error.message));
|
|
8812
9661
|
return;
|
|
8813
9662
|
}
|
|
8814
9663
|
try {
|
|
@@ -8824,12 +9673,12 @@ function useBuildingController() {
|
|
|
8824
9673
|
}
|
|
8825
9674
|
async function deleteById(req, res, next) {
|
|
8826
9675
|
const id = req.params.id;
|
|
8827
|
-
const validation =
|
|
8828
|
-
id:
|
|
9676
|
+
const validation = Joi38.object({
|
|
9677
|
+
id: Joi38.string().hex().required()
|
|
8829
9678
|
});
|
|
8830
9679
|
const { error } = validation.validate({ id });
|
|
8831
9680
|
if (error) {
|
|
8832
|
-
next(new
|
|
9681
|
+
next(new BadRequestError48(error.message));
|
|
8833
9682
|
return;
|
|
8834
9683
|
}
|
|
8835
9684
|
try {
|
|
@@ -8850,11 +9699,11 @@ function useBuildingController() {
|
|
|
8850
9699
|
}
|
|
8851
9700
|
|
|
8852
9701
|
// src/resources/building/building-unit.service.ts
|
|
8853
|
-
import { useAtlas as
|
|
9702
|
+
import { useAtlas as useAtlas23 } from "@goweekdays/utils";
|
|
8854
9703
|
function useBuildingUnitService() {
|
|
8855
9704
|
const { add: _add } = useBuildingUnitRepo();
|
|
8856
9705
|
async function add(value) {
|
|
8857
|
-
const session =
|
|
9706
|
+
const session = useAtlas23.getClient()?.startSession();
|
|
8858
9707
|
if (!session) {
|
|
8859
9708
|
throw new Error("Unable to start session for building unit service.");
|
|
8860
9709
|
}
|
|
@@ -8881,8 +9730,8 @@ function useBuildingUnitService() {
|
|
|
8881
9730
|
}
|
|
8882
9731
|
|
|
8883
9732
|
// src/resources/building/building-unit.controller.ts
|
|
8884
|
-
import { BadRequestError as
|
|
8885
|
-
import
|
|
9733
|
+
import { BadRequestError as BadRequestError49 } from "@goweekdays/utils";
|
|
9734
|
+
import Joi39 from "joi";
|
|
8886
9735
|
function useBuildingUnitController() {
|
|
8887
9736
|
const {
|
|
8888
9737
|
getAll: _getAll,
|
|
@@ -8893,27 +9742,27 @@ function useBuildingUnitController() {
|
|
|
8893
9742
|
const { add: _add } = useBuildingUnitService();
|
|
8894
9743
|
async function add(req, res, next) {
|
|
8895
9744
|
const data = req.body;
|
|
8896
|
-
const validation =
|
|
8897
|
-
building:
|
|
8898
|
-
school:
|
|
8899
|
-
name:
|
|
8900
|
-
building:
|
|
8901
|
-
buildingName:
|
|
8902
|
-
level:
|
|
8903
|
-
category:
|
|
8904
|
-
type:
|
|
8905
|
-
seating_capacity:
|
|
8906
|
-
standing_capacity:
|
|
8907
|
-
description:
|
|
8908
|
-
unit_of_measurement:
|
|
8909
|
-
area:
|
|
8910
|
-
status:
|
|
9745
|
+
const validation = Joi39.object({
|
|
9746
|
+
building: Joi39.object({
|
|
9747
|
+
school: Joi39.string().hex().required(),
|
|
9748
|
+
name: Joi39.string().optional().allow("", null),
|
|
9749
|
+
building: Joi39.string().hex().required(),
|
|
9750
|
+
buildingName: Joi39.string().optional().allow("", null),
|
|
9751
|
+
level: Joi39.number().integer().min(1).required(),
|
|
9752
|
+
category: Joi39.string().required(),
|
|
9753
|
+
type: Joi39.string().required(),
|
|
9754
|
+
seating_capacity: Joi39.number().integer().min(0).required(),
|
|
9755
|
+
standing_capacity: Joi39.number().integer().min(0).required(),
|
|
9756
|
+
description: Joi39.string().optional().allow("", null),
|
|
9757
|
+
unit_of_measurement: Joi39.string().valid("sqm").required(),
|
|
9758
|
+
area: Joi39.number().positive().required(),
|
|
9759
|
+
status: Joi39.string().optional().allow("", null)
|
|
8911
9760
|
}),
|
|
8912
|
-
qty:
|
|
9761
|
+
qty: Joi39.number().integer().min(1).max(20).optional().default(1)
|
|
8913
9762
|
});
|
|
8914
9763
|
const { error } = validation.validate(data);
|
|
8915
9764
|
if (error) {
|
|
8916
|
-
next(new
|
|
9765
|
+
next(new BadRequestError49(error.message));
|
|
8917
9766
|
return;
|
|
8918
9767
|
}
|
|
8919
9768
|
try {
|
|
@@ -8929,13 +9778,13 @@ function useBuildingUnitController() {
|
|
|
8929
9778
|
async function updateById(req, res, next) {
|
|
8930
9779
|
const data = req.body;
|
|
8931
9780
|
const id = req.params.id ?? "";
|
|
8932
|
-
const validation =
|
|
8933
|
-
id:
|
|
9781
|
+
const validation = Joi39.object({
|
|
9782
|
+
id: Joi39.string().hex().required(),
|
|
8934
9783
|
value: schemaUpdateOptions
|
|
8935
9784
|
});
|
|
8936
9785
|
const { error } = validation.validate({ id, value: data });
|
|
8937
9786
|
if (error) {
|
|
8938
|
-
next(new
|
|
9787
|
+
next(new BadRequestError49(error.message));
|
|
8939
9788
|
return;
|
|
8940
9789
|
}
|
|
8941
9790
|
try {
|
|
@@ -8950,17 +9799,17 @@ function useBuildingUnitController() {
|
|
|
8950
9799
|
}
|
|
8951
9800
|
async function getAll(req, res, next) {
|
|
8952
9801
|
const query = req.query;
|
|
8953
|
-
const validation =
|
|
8954
|
-
page:
|
|
8955
|
-
limit:
|
|
8956
|
-
search:
|
|
8957
|
-
school:
|
|
8958
|
-
building:
|
|
8959
|
-
status:
|
|
9802
|
+
const validation = Joi39.object({
|
|
9803
|
+
page: Joi39.number().min(1).optional().allow("", null),
|
|
9804
|
+
limit: Joi39.number().min(1).optional().allow("", null),
|
|
9805
|
+
search: Joi39.string().optional().allow("", null),
|
|
9806
|
+
school: Joi39.string().hex().optional().allow("", null),
|
|
9807
|
+
building: Joi39.string().hex().optional().allow("", null),
|
|
9808
|
+
status: Joi39.string().optional().allow("", null)
|
|
8960
9809
|
});
|
|
8961
9810
|
const { error } = validation.validate(query);
|
|
8962
9811
|
if (error) {
|
|
8963
|
-
next(new
|
|
9812
|
+
next(new BadRequestError49(error.message));
|
|
8964
9813
|
return;
|
|
8965
9814
|
}
|
|
8966
9815
|
const page = parseInt(req.query.page) ?? 1;
|
|
@@ -8996,12 +9845,12 @@ function useBuildingUnitController() {
|
|
|
8996
9845
|
}
|
|
8997
9846
|
async function getById(req, res, next) {
|
|
8998
9847
|
const id = req.params.id;
|
|
8999
|
-
const validation =
|
|
9000
|
-
id:
|
|
9848
|
+
const validation = Joi39.object({
|
|
9849
|
+
id: Joi39.string().hex().required()
|
|
9001
9850
|
});
|
|
9002
9851
|
const { error } = validation.validate({ id });
|
|
9003
9852
|
if (error) {
|
|
9004
|
-
next(new
|
|
9853
|
+
next(new BadRequestError49(error.message));
|
|
9005
9854
|
return;
|
|
9006
9855
|
}
|
|
9007
9856
|
try {
|
|
@@ -9017,12 +9866,12 @@ function useBuildingUnitController() {
|
|
|
9017
9866
|
}
|
|
9018
9867
|
async function deleteById(req, res, next) {
|
|
9019
9868
|
const id = req.params.id;
|
|
9020
|
-
const validation =
|
|
9021
|
-
id:
|
|
9869
|
+
const validation = Joi39.object({
|
|
9870
|
+
id: Joi39.string().hex().required()
|
|
9022
9871
|
});
|
|
9023
9872
|
const { error } = validation.validate({ id });
|
|
9024
9873
|
if (error) {
|
|
9025
|
-
next(new
|
|
9874
|
+
next(new BadRequestError49(error.message));
|
|
9026
9875
|
return;
|
|
9027
9876
|
}
|
|
9028
9877
|
try {
|
|
@@ -9043,14 +9892,14 @@ function useBuildingUnitController() {
|
|
|
9043
9892
|
}
|
|
9044
9893
|
|
|
9045
9894
|
// src/resources/counter/counter.model.ts
|
|
9046
|
-
import { BadRequestError as
|
|
9047
|
-
import { ObjectId as
|
|
9895
|
+
import { BadRequestError as BadRequestError50 } from "@goweekdays/utils";
|
|
9896
|
+
import { ObjectId as ObjectId25 } from "mongodb";
|
|
9048
9897
|
import { z } from "zod";
|
|
9049
9898
|
var TCounter = z.object({
|
|
9050
9899
|
_id: z.union([
|
|
9051
9900
|
z.string().length(24, "Invalid ObjectId hex string"),
|
|
9052
|
-
z.instanceof(
|
|
9053
|
-
]).transform((val) => typeof val === "string" ? new
|
|
9901
|
+
z.instanceof(ObjectId25)
|
|
9902
|
+
]).transform((val) => typeof val === "string" ? new ObjectId25(val) : val).optional(),
|
|
9054
9903
|
count: z.number().int().min(0).default(0),
|
|
9055
9904
|
type: z.string(),
|
|
9056
9905
|
createdAt: z.date().optional().default(() => /* @__PURE__ */ new Date()),
|
|
@@ -9063,7 +9912,7 @@ function useCounterModel(db) {
|
|
|
9063
9912
|
try {
|
|
9064
9913
|
return TCounter.parse(value);
|
|
9065
9914
|
} catch (error) {
|
|
9066
|
-
throw new
|
|
9915
|
+
throw new BadRequestError50(error.issues[0].message);
|
|
9067
9916
|
}
|
|
9068
9917
|
}
|
|
9069
9918
|
function validateCounter(data) {
|
|
@@ -9077,23 +9926,23 @@ function useCounterModel(db) {
|
|
|
9077
9926
|
}
|
|
9078
9927
|
|
|
9079
9928
|
// src/resources/counter/counter.repository.ts
|
|
9080
|
-
import { useAtlas as
|
|
9929
|
+
import { useAtlas as useAtlas24, useCache as useCache18, makeCacheKey as makeCacheKey17, logger as logger27 } from "@goweekdays/utils";
|
|
9081
9930
|
function useCounterRepo() {
|
|
9082
|
-
const db =
|
|
9931
|
+
const db = useAtlas24.getDb();
|
|
9083
9932
|
if (!db) {
|
|
9084
9933
|
throw new Error("Unable to connect to server.");
|
|
9085
9934
|
}
|
|
9086
9935
|
const namespace_collection = "counters";
|
|
9087
9936
|
const { collection, createCounter } = useCounterModel(db);
|
|
9088
|
-
const { getCache, setCache, delNamespace } =
|
|
9937
|
+
const { getCache, setCache, delNamespace } = useCache18(namespace_collection);
|
|
9089
9938
|
function delCachedData() {
|
|
9090
9939
|
delNamespace().then(() => {
|
|
9091
|
-
|
|
9940
|
+
logger27.log({
|
|
9092
9941
|
level: "info",
|
|
9093
9942
|
message: `Cache namespace cleared for ${namespace_collection}`
|
|
9094
9943
|
});
|
|
9095
9944
|
}).catch((err) => {
|
|
9096
|
-
|
|
9945
|
+
logger27.log({
|
|
9097
9946
|
level: "error",
|
|
9098
9947
|
message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
|
|
9099
9948
|
});
|
|
@@ -9137,11 +9986,11 @@ function useCounterRepo() {
|
|
|
9137
9986
|
}
|
|
9138
9987
|
}
|
|
9139
9988
|
async function getByType(type) {
|
|
9140
|
-
const cacheKey =
|
|
9989
|
+
const cacheKey = makeCacheKey17(namespace_collection, { type });
|
|
9141
9990
|
try {
|
|
9142
9991
|
const cached = await getCache(cacheKey);
|
|
9143
9992
|
if (cached) {
|
|
9144
|
-
|
|
9993
|
+
logger27.log({
|
|
9145
9994
|
level: "info",
|
|
9146
9995
|
message: `Cache hit for getByType counter: ${cacheKey}`
|
|
9147
9996
|
});
|
|
@@ -9150,12 +9999,12 @@ function useCounterRepo() {
|
|
|
9150
9999
|
const data = await collection.findOne({ type });
|
|
9151
10000
|
if (data) {
|
|
9152
10001
|
setCache(cacheKey, data, 300).then(() => {
|
|
9153
|
-
|
|
10002
|
+
logger27.log({
|
|
9154
10003
|
level: "info",
|
|
9155
10004
|
message: `Cache set for counter by type: ${cacheKey}`
|
|
9156
10005
|
});
|
|
9157
10006
|
}).catch((err) => {
|
|
9158
|
-
|
|
10007
|
+
logger27.log({
|
|
9159
10008
|
level: "error",
|
|
9160
10009
|
message: `Failed to set cache for counter by type: ${err.message}`
|
|
9161
10010
|
});
|
|
@@ -9175,7 +10024,7 @@ function useCounterRepo() {
|
|
|
9175
10024
|
}
|
|
9176
10025
|
|
|
9177
10026
|
// src/resources/file/file.service.ts
|
|
9178
|
-
import { logger as
|
|
10027
|
+
import { logger as logger28, useS3 as useS32, useAtlas as useAtlas25 } from "@goweekdays/utils";
|
|
9179
10028
|
import cron from "node-cron";
|
|
9180
10029
|
import * as fs from "fs";
|
|
9181
10030
|
function useFileService() {
|
|
@@ -9193,7 +10042,7 @@ function useFileService() {
|
|
|
9193
10042
|
forcePathStyle: true
|
|
9194
10043
|
});
|
|
9195
10044
|
async function createFile(value) {
|
|
9196
|
-
const session =
|
|
10045
|
+
const session = useAtlas25.getClient()?.startSession();
|
|
9197
10046
|
session?.startTransaction();
|
|
9198
10047
|
const file = {
|
|
9199
10048
|
name: value.originalname,
|
|
@@ -9227,7 +10076,7 @@ function useFileService() {
|
|
|
9227
10076
|
}
|
|
9228
10077
|
}
|
|
9229
10078
|
async function deleteFile(id) {
|
|
9230
|
-
const session =
|
|
10079
|
+
const session = useAtlas25.getClient()?.startSession();
|
|
9231
10080
|
session?.startTransaction();
|
|
9232
10081
|
try {
|
|
9233
10082
|
await deleteFileById(id, session);
|
|
@@ -9248,12 +10097,12 @@ function useFileService() {
|
|
|
9248
10097
|
const file = files[index];
|
|
9249
10098
|
try {
|
|
9250
10099
|
await deleteFile(file._id.toString());
|
|
9251
|
-
await
|
|
10100
|
+
await logger28.log({
|
|
9252
10101
|
level: "info",
|
|
9253
10102
|
message: "Successfully deleted draft files."
|
|
9254
10103
|
});
|
|
9255
10104
|
} catch (error) {
|
|
9256
|
-
|
|
10105
|
+
logger28.log({
|
|
9257
10106
|
level: "info",
|
|
9258
10107
|
message: "Successfully deleted draft files."
|
|
9259
10108
|
});
|
|
@@ -9271,11 +10120,11 @@ function useFileService() {
|
|
|
9271
10120
|
|
|
9272
10121
|
// src/resources/file/file.controller.ts
|
|
9273
10122
|
import {
|
|
9274
|
-
AppError as
|
|
9275
|
-
BadRequestError as
|
|
9276
|
-
InternalServerError as
|
|
10123
|
+
AppError as AppError22,
|
|
10124
|
+
BadRequestError as BadRequestError51,
|
|
10125
|
+
InternalServerError as InternalServerError26
|
|
9277
10126
|
} from "@goweekdays/utils";
|
|
9278
|
-
import
|
|
10127
|
+
import Joi40 from "joi";
|
|
9279
10128
|
function useFileController() {
|
|
9280
10129
|
const { createFile, deleteFile: _deleteFile } = useFileService();
|
|
9281
10130
|
async function upload(req, res, next) {
|
|
@@ -9288,29 +10137,29 @@ function useFileController() {
|
|
|
9288
10137
|
res.json({ message: "Successfully uploaded file", id });
|
|
9289
10138
|
return;
|
|
9290
10139
|
} catch (error) {
|
|
9291
|
-
if (error instanceof
|
|
10140
|
+
if (error instanceof AppError22) {
|
|
9292
10141
|
next(error);
|
|
9293
10142
|
} else {
|
|
9294
|
-
next(new
|
|
10143
|
+
next(new InternalServerError26(error));
|
|
9295
10144
|
}
|
|
9296
10145
|
}
|
|
9297
10146
|
}
|
|
9298
10147
|
async function deleteFile(req, res, next) {
|
|
9299
10148
|
const id = req.params.id;
|
|
9300
|
-
const validation =
|
|
10149
|
+
const validation = Joi40.string().required();
|
|
9301
10150
|
const { error } = validation.validate(id);
|
|
9302
10151
|
if (error) {
|
|
9303
|
-
next(new
|
|
10152
|
+
next(new BadRequestError51(error.message));
|
|
9304
10153
|
}
|
|
9305
10154
|
try {
|
|
9306
10155
|
const message = await _deleteFile(id);
|
|
9307
10156
|
res.json({ message });
|
|
9308
10157
|
return;
|
|
9309
10158
|
} catch (error2) {
|
|
9310
|
-
if (error2 instanceof
|
|
10159
|
+
if (error2 instanceof AppError22) {
|
|
9311
10160
|
next(error2);
|
|
9312
10161
|
} else {
|
|
9313
|
-
next(new
|
|
10162
|
+
next(new InternalServerError26(error2));
|
|
9314
10163
|
}
|
|
9315
10164
|
}
|
|
9316
10165
|
}
|
|
@@ -9321,35 +10170,35 @@ function useFileController() {
|
|
|
9321
10170
|
}
|
|
9322
10171
|
|
|
9323
10172
|
// src/resources/promo/promo.model.ts
|
|
9324
|
-
import
|
|
9325
|
-
var schemaPromo =
|
|
9326
|
-
code:
|
|
9327
|
-
description:
|
|
9328
|
-
type:
|
|
9329
|
-
flatRate:
|
|
10173
|
+
import Joi41 from "joi";
|
|
10174
|
+
var schemaPromo = Joi41.object({
|
|
10175
|
+
code: Joi41.string().min(3).max(50).required(),
|
|
10176
|
+
description: Joi41.string().max(255).optional().allow("", null),
|
|
10177
|
+
type: Joi41.string().valid("flat", "fixed", "tiered").required(),
|
|
10178
|
+
flatRate: Joi41.number().positive().when("type", {
|
|
9330
10179
|
is: "flat",
|
|
9331
|
-
then:
|
|
9332
|
-
otherwise:
|
|
10180
|
+
then: Joi41.required(),
|
|
10181
|
+
otherwise: Joi41.forbidden()
|
|
9333
10182
|
}).optional().allow(null, 0),
|
|
9334
|
-
fixedRate:
|
|
10183
|
+
fixedRate: Joi41.number().positive().when("type", {
|
|
9335
10184
|
is: "fixed",
|
|
9336
|
-
then:
|
|
9337
|
-
otherwise:
|
|
10185
|
+
then: Joi41.required(),
|
|
10186
|
+
otherwise: Joi41.forbidden()
|
|
9338
10187
|
}).optional().allow(null, 0),
|
|
9339
|
-
tiers:
|
|
9340
|
-
|
|
9341
|
-
minSeats:
|
|
9342
|
-
maxSeats:
|
|
9343
|
-
rate:
|
|
10188
|
+
tiers: Joi41.array().items(
|
|
10189
|
+
Joi41.object({
|
|
10190
|
+
minSeats: Joi41.number().integer().min(1).required(),
|
|
10191
|
+
maxSeats: Joi41.number().integer().min(Joi41.ref("minSeats")).required(),
|
|
10192
|
+
rate: Joi41.number().positive().required()
|
|
9344
10193
|
})
|
|
9345
10194
|
).when("type", {
|
|
9346
10195
|
is: "tiered",
|
|
9347
|
-
then:
|
|
9348
|
-
otherwise:
|
|
10196
|
+
then: Joi41.required(),
|
|
10197
|
+
otherwise: Joi41.forbidden()
|
|
9349
10198
|
}),
|
|
9350
|
-
currency:
|
|
9351
|
-
startDate:
|
|
9352
|
-
endDate:
|
|
10199
|
+
currency: Joi41.string().length(3).required(),
|
|
10200
|
+
startDate: Joi41.date().required(),
|
|
10201
|
+
endDate: Joi41.date().greater(Joi41.ref("startDate")).optional().allow(null, "")
|
|
9353
10202
|
});
|
|
9354
10203
|
function modelPromo(data) {
|
|
9355
10204
|
const { error } = schemaPromo.validate(data);
|
|
@@ -9378,33 +10227,33 @@ function modelPromo(data) {
|
|
|
9378
10227
|
|
|
9379
10228
|
// src/resources/promo/promo.repository.ts
|
|
9380
10229
|
import {
|
|
9381
|
-
AppError as
|
|
9382
|
-
BadRequestError as
|
|
9383
|
-
InternalServerError as
|
|
9384
|
-
logger as
|
|
9385
|
-
makeCacheKey as
|
|
9386
|
-
paginate as
|
|
9387
|
-
useAtlas as
|
|
9388
|
-
useCache as
|
|
10230
|
+
AppError as AppError23,
|
|
10231
|
+
BadRequestError as BadRequestError52,
|
|
10232
|
+
InternalServerError as InternalServerError27,
|
|
10233
|
+
logger as logger29,
|
|
10234
|
+
makeCacheKey as makeCacheKey18,
|
|
10235
|
+
paginate as paginate15,
|
|
10236
|
+
useAtlas as useAtlas26,
|
|
10237
|
+
useCache as useCache19
|
|
9389
10238
|
} from "@goweekdays/utils";
|
|
9390
|
-
import
|
|
9391
|
-
import { ObjectId as
|
|
10239
|
+
import Joi42 from "joi";
|
|
10240
|
+
import { ObjectId as ObjectId26 } from "mongodb";
|
|
9392
10241
|
function usePromoRepo() {
|
|
9393
|
-
const db =
|
|
10242
|
+
const db = useAtlas26.getDb();
|
|
9394
10243
|
if (!db) {
|
|
9395
|
-
throw new
|
|
10244
|
+
throw new InternalServerError27("Unable to connect to server.");
|
|
9396
10245
|
}
|
|
9397
10246
|
const namespace_collection = "users";
|
|
9398
10247
|
const collection = db.collection(namespace_collection);
|
|
9399
|
-
const { getCache, setCache, delNamespace } =
|
|
10248
|
+
const { getCache, setCache, delNamespace } = useCache19(namespace_collection);
|
|
9400
10249
|
function delCachedData() {
|
|
9401
10250
|
delNamespace().then(() => {
|
|
9402
|
-
|
|
10251
|
+
logger29.log({
|
|
9403
10252
|
level: "info",
|
|
9404
10253
|
message: `Cache namespace cleared for ${namespace_collection}`
|
|
9405
10254
|
});
|
|
9406
10255
|
}).catch((err) => {
|
|
9407
|
-
|
|
10256
|
+
logger29.log({
|
|
9408
10257
|
level: "error",
|
|
9409
10258
|
message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
|
|
9410
10259
|
});
|
|
@@ -9436,7 +10285,7 @@ function usePromoRepo() {
|
|
|
9436
10285
|
delCachedData();
|
|
9437
10286
|
return "Successfully added promo.";
|
|
9438
10287
|
} catch (error) {
|
|
9439
|
-
throw new
|
|
10288
|
+
throw new InternalServerError27("Failed to add promo.");
|
|
9440
10289
|
}
|
|
9441
10290
|
}
|
|
9442
10291
|
async function getAll({
|
|
@@ -9457,7 +10306,7 @@ function usePromoRepo() {
|
|
|
9457
10306
|
if (search) {
|
|
9458
10307
|
query.$text = { $search: search };
|
|
9459
10308
|
}
|
|
9460
|
-
const cacheKey =
|
|
10309
|
+
const cacheKey = makeCacheKey18(namespace_collection, cacheKeyOptions);
|
|
9461
10310
|
try {
|
|
9462
10311
|
const cachedData = await getCache(cacheKey);
|
|
9463
10312
|
if (cachedData) {
|
|
@@ -9469,30 +10318,30 @@ function usePromoRepo() {
|
|
|
9469
10318
|
{ $limit: limit }
|
|
9470
10319
|
]).toArray();
|
|
9471
10320
|
const length = await collection.countDocuments(query);
|
|
9472
|
-
const data =
|
|
10321
|
+
const data = paginate15(items, page, limit, length);
|
|
9473
10322
|
setCache(cacheKey, data).then(() => {
|
|
9474
|
-
|
|
10323
|
+
logger29.log({
|
|
9475
10324
|
level: "info",
|
|
9476
10325
|
message: `Cache set for getAll promo: ${cacheKey}`
|
|
9477
10326
|
});
|
|
9478
10327
|
}).catch((err) => {
|
|
9479
|
-
|
|
10328
|
+
logger29.log({
|
|
9480
10329
|
level: "error",
|
|
9481
10330
|
message: `Failed to set cache for getAll promo: ${err.message}`
|
|
9482
10331
|
});
|
|
9483
10332
|
});
|
|
9484
10333
|
return data;
|
|
9485
10334
|
} catch (error) {
|
|
9486
|
-
throw new
|
|
10335
|
+
throw new InternalServerError27("Failed to get promos.");
|
|
9487
10336
|
}
|
|
9488
10337
|
}
|
|
9489
10338
|
async function getByCode(code) {
|
|
9490
|
-
const { error } =
|
|
10339
|
+
const { error } = Joi42.string().min(3).max(50).required().validate(code);
|
|
9491
10340
|
if (error) {
|
|
9492
10341
|
throw new Error(`Invalid promo code: ${error.message}`);
|
|
9493
10342
|
}
|
|
9494
10343
|
try {
|
|
9495
|
-
const cacheKey =
|
|
10344
|
+
const cacheKey = makeCacheKey18(namespace_collection, {
|
|
9496
10345
|
code,
|
|
9497
10346
|
tag: "getByCode"
|
|
9498
10347
|
});
|
|
@@ -9505,33 +10354,33 @@ function usePromoRepo() {
|
|
|
9505
10354
|
status: { $ne: "deleted" }
|
|
9506
10355
|
});
|
|
9507
10356
|
setCache(cacheKey, data).then(() => {
|
|
9508
|
-
|
|
10357
|
+
logger29.log({
|
|
9509
10358
|
level: "info",
|
|
9510
10359
|
message: `Cache set for getByCode promo: ${cacheKey}`
|
|
9511
10360
|
});
|
|
9512
10361
|
}).catch((err) => {
|
|
9513
|
-
|
|
10362
|
+
logger29.log({
|
|
9514
10363
|
level: "error",
|
|
9515
10364
|
message: `Failed to set cache for getByCode promo: ${err.message}`
|
|
9516
10365
|
});
|
|
9517
10366
|
});
|
|
9518
10367
|
return data;
|
|
9519
10368
|
} catch (error2) {
|
|
9520
|
-
throw new
|
|
10369
|
+
throw new InternalServerError27("Failed to get promo.");
|
|
9521
10370
|
}
|
|
9522
10371
|
}
|
|
9523
10372
|
async function getById(_id) {
|
|
9524
|
-
const { error } =
|
|
10373
|
+
const { error } = Joi42.string().hex().length(24).required().validate(_id);
|
|
9525
10374
|
if (error) {
|
|
9526
10375
|
throw new Error(`Invalid promo ID: ${error.message}`);
|
|
9527
10376
|
}
|
|
9528
10377
|
try {
|
|
9529
|
-
_id = new
|
|
10378
|
+
_id = new ObjectId26(_id);
|
|
9530
10379
|
} catch (error2) {
|
|
9531
|
-
throw new
|
|
10380
|
+
throw new BadRequestError52("Invalid promo ID.");
|
|
9532
10381
|
}
|
|
9533
10382
|
try {
|
|
9534
|
-
const cacheKey =
|
|
10383
|
+
const cacheKey = makeCacheKey18(namespace_collection, {
|
|
9535
10384
|
_id: String(_id),
|
|
9536
10385
|
tag: "getById"
|
|
9537
10386
|
});
|
|
@@ -9544,30 +10393,30 @@ function usePromoRepo() {
|
|
|
9544
10393
|
status: { $ne: "deleted" }
|
|
9545
10394
|
});
|
|
9546
10395
|
setCache(cacheKey, data).then(() => {
|
|
9547
|
-
|
|
10396
|
+
logger29.log({
|
|
9548
10397
|
level: "info",
|
|
9549
10398
|
message: `Cache set for getById promo: ${cacheKey}`
|
|
9550
10399
|
});
|
|
9551
10400
|
}).catch((err) => {
|
|
9552
|
-
|
|
10401
|
+
logger29.log({
|
|
9553
10402
|
level: "error",
|
|
9554
10403
|
message: `Failed to set cache for getById promo: ${err.message}`
|
|
9555
10404
|
});
|
|
9556
10405
|
});
|
|
9557
10406
|
return data;
|
|
9558
10407
|
} catch (error2) {
|
|
9559
|
-
throw new
|
|
10408
|
+
throw new InternalServerError27("Failed to get promo.");
|
|
9560
10409
|
}
|
|
9561
10410
|
}
|
|
9562
10411
|
async function deleteById(_id) {
|
|
9563
|
-
const { error } =
|
|
10412
|
+
const { error } = Joi42.string().hex().length(24).required().validate(_id);
|
|
9564
10413
|
if (error) {
|
|
9565
10414
|
throw new Error(`Invalid promo ID: ${error.message}`);
|
|
9566
10415
|
}
|
|
9567
10416
|
try {
|
|
9568
|
-
_id = new
|
|
10417
|
+
_id = new ObjectId26(_id);
|
|
9569
10418
|
} catch (error2) {
|
|
9570
|
-
throw new
|
|
10419
|
+
throw new BadRequestError52("Invalid promo ID.");
|
|
9571
10420
|
}
|
|
9572
10421
|
try {
|
|
9573
10422
|
const result = await collection.updateOne(
|
|
@@ -9575,15 +10424,15 @@ function usePromoRepo() {
|
|
|
9575
10424
|
{ $set: { status: "deleted" } }
|
|
9576
10425
|
);
|
|
9577
10426
|
if (result.modifiedCount === 0) {
|
|
9578
|
-
throw new
|
|
10427
|
+
throw new InternalServerError27("Failed to delete promo.");
|
|
9579
10428
|
}
|
|
9580
10429
|
delCachedData();
|
|
9581
10430
|
return "Successfully deleted promo.";
|
|
9582
10431
|
} catch (error2) {
|
|
9583
|
-
if (error2 instanceof
|
|
10432
|
+
if (error2 instanceof AppError23) {
|
|
9584
10433
|
throw error2;
|
|
9585
10434
|
}
|
|
9586
|
-
throw new
|
|
10435
|
+
throw new InternalServerError27("Failed to delete promo.");
|
|
9587
10436
|
}
|
|
9588
10437
|
}
|
|
9589
10438
|
return {
|
|
@@ -9597,7 +10446,7 @@ function usePromoRepo() {
|
|
|
9597
10446
|
}
|
|
9598
10447
|
|
|
9599
10448
|
// src/resources/utils/github.service.ts
|
|
9600
|
-
import { AppError as
|
|
10449
|
+
import { AppError as AppError24, BadRequestError as BadRequestError53 } from "@goweekdays/utils";
|
|
9601
10450
|
import { Octokit } from "@octokit/rest";
|
|
9602
10451
|
import _sodium from "libsodium-wrappers";
|
|
9603
10452
|
function useGitHubService() {
|
|
@@ -9611,23 +10460,23 @@ function useGitHubService() {
|
|
|
9611
10460
|
try {
|
|
9612
10461
|
const { data: repoData } = await octokit.repos.get({ owner, repo });
|
|
9613
10462
|
if (!repoData.permissions?.admin) {
|
|
9614
|
-
throw new
|
|
10463
|
+
throw new BadRequestError53(
|
|
9615
10464
|
"You do not have admin access to this repository."
|
|
9616
10465
|
);
|
|
9617
10466
|
}
|
|
9618
10467
|
} catch (error) {
|
|
9619
10468
|
if (error.status === 404) {
|
|
9620
|
-
throw new
|
|
10469
|
+
throw new BadRequestError53(
|
|
9621
10470
|
"Repository not found or you don't have access to it."
|
|
9622
10471
|
);
|
|
9623
10472
|
} else if (error.status === 401) {
|
|
9624
|
-
throw new
|
|
10473
|
+
throw new BadRequestError53(
|
|
9625
10474
|
"Invalid GitHub token or insufficient permissions."
|
|
9626
10475
|
);
|
|
9627
10476
|
} else if (error.message.includes("admin access")) {
|
|
9628
10477
|
throw error;
|
|
9629
10478
|
} else {
|
|
9630
|
-
throw new
|
|
10479
|
+
throw new BadRequestError53(
|
|
9631
10480
|
`Failed to check repository permissions: ${error.message}`
|
|
9632
10481
|
);
|
|
9633
10482
|
}
|
|
@@ -9676,7 +10525,7 @@ function useGitHubService() {
|
|
|
9676
10525
|
key_id: publicKeyRes.key_id
|
|
9677
10526
|
});
|
|
9678
10527
|
} catch (encryptionError) {
|
|
9679
|
-
throw new
|
|
10528
|
+
throw new BadRequestError53(
|
|
9680
10529
|
`Failed to encrypt secret '${key}': ${encryptionError.message}`
|
|
9681
10530
|
);
|
|
9682
10531
|
}
|
|
@@ -9706,22 +10555,22 @@ function useGitHubService() {
|
|
|
9706
10555
|
}
|
|
9707
10556
|
return `Successfully set ${lines.length} ${type} variables/secrets in environment '${environment}'`;
|
|
9708
10557
|
} catch (error) {
|
|
9709
|
-
if (error instanceof
|
|
10558
|
+
if (error instanceof AppError24)
|
|
9710
10559
|
throw error;
|
|
9711
10560
|
if (error.status === 422) {
|
|
9712
|
-
throw new
|
|
10561
|
+
throw new BadRequestError53(
|
|
9713
10562
|
`GitHub API validation error: ${error.message}`
|
|
9714
10563
|
);
|
|
9715
10564
|
} else if (error.status === 404) {
|
|
9716
|
-
throw new
|
|
10565
|
+
throw new BadRequestError53("Environment or repository not found.");
|
|
9717
10566
|
} else if (error.status === 403) {
|
|
9718
|
-
throw new
|
|
10567
|
+
throw new BadRequestError53(
|
|
9719
10568
|
"Forbidden: Insufficient permissions or rate limit exceeded."
|
|
9720
10569
|
);
|
|
9721
10570
|
} else if (error.message.includes("admin access") || error.message.includes("permissions")) {
|
|
9722
10571
|
throw error;
|
|
9723
10572
|
} else {
|
|
9724
|
-
throw new
|
|
10573
|
+
throw new BadRequestError53(
|
|
9725
10574
|
`Failed to set GitHub variables: ${error.message}`
|
|
9726
10575
|
);
|
|
9727
10576
|
}
|
|
@@ -9733,12 +10582,12 @@ function useGitHubService() {
|
|
|
9733
10582
|
}
|
|
9734
10583
|
|
|
9735
10584
|
// src/resources/utils/util.controller.ts
|
|
9736
|
-
import
|
|
10585
|
+
import Joi43 from "joi";
|
|
9737
10586
|
import {
|
|
9738
|
-
AppError as
|
|
9739
|
-
BadRequestError as
|
|
9740
|
-
InternalServerError as
|
|
9741
|
-
logger as
|
|
10587
|
+
AppError as AppError25,
|
|
10588
|
+
BadRequestError as BadRequestError54,
|
|
10589
|
+
InternalServerError as InternalServerError28,
|
|
10590
|
+
logger as logger30
|
|
9742
10591
|
} from "@goweekdays/utils";
|
|
9743
10592
|
function useUtilController() {
|
|
9744
10593
|
async function healthCheck(req, res, next) {
|
|
@@ -9755,32 +10604,32 @@ function useUtilController() {
|
|
|
9755
10604
|
}
|
|
9756
10605
|
});
|
|
9757
10606
|
} catch (error) {
|
|
9758
|
-
|
|
9759
|
-
next(new
|
|
10607
|
+
logger30.error("Health check failed", { error: error.message });
|
|
10608
|
+
next(new InternalServerError28("Health check failed"));
|
|
9760
10609
|
}
|
|
9761
10610
|
}
|
|
9762
10611
|
async function setGitHubVariables(req, res, next) {
|
|
9763
10612
|
try {
|
|
9764
10613
|
const { githubToken, repoUrl, environment, type, keyValues } = req.body;
|
|
9765
|
-
const validation =
|
|
9766
|
-
githubToken:
|
|
10614
|
+
const validation = Joi43.object({
|
|
10615
|
+
githubToken: Joi43.string().required().messages({
|
|
9767
10616
|
"string.empty": "GitHub token is required",
|
|
9768
10617
|
"any.required": "GitHub token is required"
|
|
9769
10618
|
}),
|
|
9770
|
-
repoUrl:
|
|
10619
|
+
repoUrl: Joi43.string().uri().required().messages({
|
|
9771
10620
|
"string.empty": "Repository URL is required",
|
|
9772
10621
|
"string.uri": "Repository URL must be a valid URL",
|
|
9773
10622
|
"any.required": "Repository URL is required"
|
|
9774
10623
|
}),
|
|
9775
|
-
environment:
|
|
10624
|
+
environment: Joi43.string().required().messages({
|
|
9776
10625
|
"string.empty": "Environment name is required",
|
|
9777
10626
|
"any.required": "Environment name is required"
|
|
9778
10627
|
}),
|
|
9779
|
-
type:
|
|
10628
|
+
type: Joi43.string().valid("env", "secret").required().messages({
|
|
9780
10629
|
"any.only": 'Type must be either "env" or "secret"',
|
|
9781
10630
|
"any.required": "Type is required"
|
|
9782
10631
|
}),
|
|
9783
|
-
keyValues:
|
|
10632
|
+
keyValues: Joi43.string().required().messages({
|
|
9784
10633
|
"string.empty": "Key-value pairs are required",
|
|
9785
10634
|
"any.required": "Key-value pairs are required"
|
|
9786
10635
|
})
|
|
@@ -9793,13 +10642,13 @@ function useUtilController() {
|
|
|
9793
10642
|
keyValues
|
|
9794
10643
|
});
|
|
9795
10644
|
if (error) {
|
|
9796
|
-
next(new
|
|
10645
|
+
next(new BadRequestError54(error.message));
|
|
9797
10646
|
return;
|
|
9798
10647
|
}
|
|
9799
10648
|
const repoUrlPattern = /github\.com[:\/]([^\/]+)\/(.+)\.git$/;
|
|
9800
10649
|
if (!repoUrlPattern.test(repoUrl)) {
|
|
9801
10650
|
next(
|
|
9802
|
-
new
|
|
10651
|
+
new BadRequestError54(
|
|
9803
10652
|
"Invalid GitHub repository URL format. Expected format: https://github.com/owner/repo.git"
|
|
9804
10653
|
)
|
|
9805
10654
|
);
|
|
@@ -9811,7 +10660,7 @@ function useUtilController() {
|
|
|
9811
10660
|
);
|
|
9812
10661
|
if (invalidLines.length > 0) {
|
|
9813
10662
|
next(
|
|
9814
|
-
new
|
|
10663
|
+
new BadRequestError54(
|
|
9815
10664
|
"Invalid key-value format. Each pair should be in format: KEY=value. Pairs should be separated by semicolons."
|
|
9816
10665
|
)
|
|
9817
10666
|
);
|
|
@@ -9825,7 +10674,7 @@ function useUtilController() {
|
|
|
9825
10674
|
type,
|
|
9826
10675
|
keyValues
|
|
9827
10676
|
});
|
|
9828
|
-
|
|
10677
|
+
logger30.info(`GitHub variables set successfully`, {
|
|
9829
10678
|
repoUrl,
|
|
9830
10679
|
environment,
|
|
9831
10680
|
type,
|
|
@@ -9836,319 +10685,311 @@ function useUtilController() {
|
|
|
9836
10685
|
message: result,
|
|
9837
10686
|
data: {
|
|
9838
10687
|
repoUrl,
|
|
9839
|
-
environment,
|
|
9840
|
-
type,
|
|
9841
|
-
variablesSet: lines.length
|
|
9842
|
-
}
|
|
9843
|
-
});
|
|
9844
|
-
} catch (error) {
|
|
9845
|
-
logger28.error("Failed to set GitHub variables", {
|
|
9846
|
-
error: error.message,
|
|
9847
|
-
stack: error.stack
|
|
9848
|
-
});
|
|
9849
|
-
if (error instanceof AppError23) {
|
|
9850
|
-
next(error);
|
|
9851
|
-
} else {
|
|
9852
|
-
next(
|
|
9853
|
-
new InternalServerError26(
|
|
9854
|
-
`Failed to set GitHub variables: ${error.message}`
|
|
9855
|
-
)
|
|
9856
|
-
);
|
|
9857
|
-
}
|
|
9858
|
-
}
|
|
9859
|
-
}
|
|
9860
|
-
const { verifySignature } = usePaypalService();
|
|
9861
|
-
async function paypalWebhook(req, res, next) {
|
|
9862
|
-
try {
|
|
9863
|
-
const headers = req.headers;
|
|
9864
|
-
const event = req.body;
|
|
9865
|
-
const data = JSON.parse(event);
|
|
9866
|
-
console.log(`headers`, headers);
|
|
9867
|
-
console.log(`parsed json`, JSON.stringify(data, null, 2));
|
|
9868
|
-
console.log(`raw event: ${event}`);
|
|
9869
|
-
const isSignatureValid = await verifySignature(
|
|
9870
|
-
event,
|
|
9871
|
-
headers,
|
|
9872
|
-
PAYPAL_WEBHOOK_ID
|
|
9873
|
-
);
|
|
9874
|
-
if (isSignatureValid) {
|
|
9875
|
-
console.log("Signature is valid.");
|
|
9876
|
-
console.log(`Received event`, JSON.stringify(data, null, 2));
|
|
9877
|
-
} else {
|
|
9878
|
-
console.log(
|
|
9879
|
-
`Signature is not valid for ${data?.id} ${headers?.["correlation-id"]}`
|
|
9880
|
-
);
|
|
9881
|
-
}
|
|
9882
|
-
res.sendStatus(200);
|
|
9883
|
-
} catch (error) {
|
|
9884
|
-
logger28.log({
|
|
9885
|
-
level: "error",
|
|
9886
|
-
message: `${error}`
|
|
9887
|
-
});
|
|
9888
|
-
}
|
|
9889
|
-
}
|
|
9890
|
-
return {
|
|
9891
|
-
healthCheck,
|
|
9892
|
-
setGitHubVariables,
|
|
9893
|
-
paypalWebhook
|
|
9894
|
-
};
|
|
9895
|
-
}
|
|
9896
|
-
|
|
9897
|
-
// src/resources/utils/transaction.schema.ts
|
|
9898
|
-
import Joi38 from "joi";
|
|
9899
|
-
var transactionSchema = Joi38.object({
|
|
9900
|
-
_id: Joi38.string().hex().optional().allow("", null),
|
|
9901
|
-
payment: Joi38.string().required(),
|
|
9902
|
-
user: Joi38.string().hex().optional().allow("", null),
|
|
9903
|
-
org: Joi38.string().hex().optional().allow("", null),
|
|
9904
|
-
type: Joi38.string().required(),
|
|
9905
|
-
amount: Joi38.number().positive().min(0).required(),
|
|
9906
|
-
currency: Joi38.string().required(),
|
|
9907
|
-
description: Joi38.string().optional().allow("", null),
|
|
9908
|
-
metadata: Joi38.object({
|
|
9909
|
-
subscriptionId: Joi38.string().hex().optional().allow("", null),
|
|
9910
|
-
cycle: Joi38.number().optional().allow("", null),
|
|
9911
|
-
seats: Joi38.number().optional().allow("", null),
|
|
9912
|
-
promoCode: Joi38.string().optional().allow("", null)
|
|
9913
|
-
}).optional().allow("", null),
|
|
9914
|
-
status: Joi38.string().optional().allow("", null),
|
|
9915
|
-
createdAt: Joi38.string().optional().allow("", null),
|
|
9916
|
-
updatedAt: Joi38.string().optional().allow("", null),
|
|
9917
|
-
deletedAt: Joi38.string().optional().allow("", null)
|
|
9918
|
-
});
|
|
9919
|
-
|
|
9920
|
-
// src/resources/verification/verification.controller.ts
|
|
9921
|
-
import {
|
|
9922
|
-
AppError as AppError24,
|
|
9923
|
-
BadRequestError as BadRequestError51,
|
|
9924
|
-
InternalServerError as InternalServerError27
|
|
9925
|
-
} from "@goweekdays/utils";
|
|
9926
|
-
import Joi39 from "joi";
|
|
9927
|
-
function useVerificationController() {
|
|
9928
|
-
const {
|
|
9929
|
-
createUserInvite: _createUserInvite,
|
|
9930
|
-
createForgetPassword: _createForgetPassword,
|
|
9931
|
-
cancelUserInvitation: _cancelUserInvitation,
|
|
9932
|
-
verify: _verify,
|
|
9933
|
-
inviteMember: _inviteMember,
|
|
9934
|
-
signUp: _signUp,
|
|
9935
|
-
cancelInviteMember: _cancelInviteMember,
|
|
9936
|
-
forgetPassword: _forgetPassword
|
|
9937
|
-
} = useVerificationService();
|
|
9938
|
-
const { getVerifications: _getVerifications } = useVerificationRepo();
|
|
9939
|
-
async function createUserInvite(req, res, next) {
|
|
9940
|
-
const validation = Joi39.object({
|
|
9941
|
-
email: Joi39.string().email().required(),
|
|
9942
|
-
app: Joi39.string().required(),
|
|
9943
|
-
role: Joi39.string().hex().required(),
|
|
9944
|
-
roleName: Joi39.string().required(),
|
|
9945
|
-
org: Joi39.string().hex().optional().optional().allow("", null),
|
|
9946
|
-
orgName: Joi39.string().optional().optional().allow("", null)
|
|
9947
|
-
});
|
|
9948
|
-
const { error } = validation.validate(req.body);
|
|
9949
|
-
if (error) {
|
|
9950
|
-
next(new BadRequestError51(error.message));
|
|
9951
|
-
return;
|
|
9952
|
-
}
|
|
9953
|
-
const email = req.body.email ?? "";
|
|
9954
|
-
const app = req.body.app ?? "";
|
|
9955
|
-
const role = req.body.role ?? "";
|
|
9956
|
-
const roleName = req.body.roleName ?? "";
|
|
9957
|
-
const org = req.body.org ?? "";
|
|
9958
|
-
const orgName = req.body.orgName ?? "";
|
|
9959
|
-
try {
|
|
9960
|
-
await _createUserInvite({
|
|
9961
|
-
email,
|
|
9962
|
-
metadata: {
|
|
9963
|
-
app,
|
|
9964
|
-
role,
|
|
9965
|
-
roleName,
|
|
9966
|
-
org,
|
|
9967
|
-
orgName
|
|
10688
|
+
environment,
|
|
10689
|
+
type,
|
|
10690
|
+
variablesSet: lines.length
|
|
9968
10691
|
}
|
|
9969
10692
|
});
|
|
9970
|
-
|
|
9971
|
-
|
|
9972
|
-
|
|
9973
|
-
|
|
9974
|
-
}
|
|
9975
|
-
}
|
|
9976
|
-
async function createForgetPassword(req, res, next) {
|
|
9977
|
-
const email = req.body.email || "";
|
|
9978
|
-
const validation = Joi39.string().email().required();
|
|
9979
|
-
const { error } = validation.validate(email);
|
|
9980
|
-
if (error) {
|
|
9981
|
-
next(new BadRequestError51(error.message));
|
|
9982
|
-
return;
|
|
9983
|
-
}
|
|
9984
|
-
try {
|
|
9985
|
-
await _createForgetPassword(email);
|
|
9986
|
-
res.json({
|
|
9987
|
-
message: "Check your email to verify it before resetting your password."
|
|
10693
|
+
} catch (error) {
|
|
10694
|
+
logger30.error("Failed to set GitHub variables", {
|
|
10695
|
+
error: error.message,
|
|
10696
|
+
stack: error.stack
|
|
9988
10697
|
});
|
|
9989
|
-
|
|
9990
|
-
|
|
9991
|
-
if (error2 instanceof AppError24) {
|
|
9992
|
-
next(error2);
|
|
10698
|
+
if (error instanceof AppError25) {
|
|
10699
|
+
next(error);
|
|
9993
10700
|
} else {
|
|
9994
|
-
next(
|
|
10701
|
+
next(
|
|
10702
|
+
new InternalServerError28(
|
|
10703
|
+
`Failed to set GitHub variables: ${error.message}`
|
|
10704
|
+
)
|
|
10705
|
+
);
|
|
9995
10706
|
}
|
|
9996
10707
|
}
|
|
9997
10708
|
}
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
search: Joi39.string().optional().allow("", null),
|
|
10002
|
-
page: Joi39.number().required(),
|
|
10003
|
-
type: Joi39.string().optional().allow("", null),
|
|
10004
|
-
email: Joi39.string().optional().allow("", null),
|
|
10005
|
-
app: Joi39.string().optional().allow("", null),
|
|
10006
|
-
org: Joi39.string().optional().allow("", null)
|
|
10007
|
-
});
|
|
10008
|
-
const { error } = validation.validate(req.query);
|
|
10009
|
-
if (error) {
|
|
10010
|
-
next(new BadRequestError51(error.message));
|
|
10011
|
-
return;
|
|
10012
|
-
}
|
|
10013
|
-
const status = req.query.status ?? "";
|
|
10014
|
-
const search = req.query.search ?? "";
|
|
10015
|
-
const page = Number(req.query.page) ?? 1;
|
|
10016
|
-
let type = req.query.type ?? "";
|
|
10017
|
-
const email = req.query.email ?? "";
|
|
10018
|
-
const app = req.query.app ?? "";
|
|
10019
|
-
const org = req.query.org ?? "";
|
|
10020
|
-
const hasMultipleTypes = type.includes(",");
|
|
10021
|
-
let splitType = [];
|
|
10022
|
-
if (hasMultipleTypes) {
|
|
10023
|
-
splitType = type.split(",");
|
|
10024
|
-
}
|
|
10709
|
+
const { verifySignature, captureOrder } = usePaypalService();
|
|
10710
|
+
const { addWithVerification } = useOrgService();
|
|
10711
|
+
async function paypalWebhook(req, res, next) {
|
|
10025
10712
|
try {
|
|
10026
|
-
const
|
|
10027
|
-
|
|
10028
|
-
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
|
|
10032
|
-
|
|
10033
|
-
|
|
10034
|
-
|
|
10035
|
-
|
|
10713
|
+
const isSignatureValid = await verifySignature(
|
|
10714
|
+
req.body,
|
|
10715
|
+
req.headers,
|
|
10716
|
+
PAYPAL_WEBHOOK_ID
|
|
10717
|
+
);
|
|
10718
|
+
if (isSignatureValid) {
|
|
10719
|
+
const payload = JSON.parse(req.body);
|
|
10720
|
+
const eventType = payload.event_type;
|
|
10721
|
+
const resource = payload.resource;
|
|
10722
|
+
switch (eventType) {
|
|
10723
|
+
case "CHECKOUT.ORDER.APPROVED": {
|
|
10724
|
+
const orderId = resource.id;
|
|
10725
|
+
await captureOrder(orderId);
|
|
10726
|
+
break;
|
|
10727
|
+
}
|
|
10728
|
+
case "PAYMENT.CAPTURE.COMPLETED": {
|
|
10729
|
+
const customId = resource?.custom_id || resource?.purchase_units?.[0]?.custom_id;
|
|
10730
|
+
if (!customId) {
|
|
10731
|
+
throw new Error("Missing PayPal customId");
|
|
10732
|
+
}
|
|
10733
|
+
await addWithVerification(customId);
|
|
10734
|
+
break;
|
|
10735
|
+
}
|
|
10736
|
+
default:
|
|
10737
|
+
break;
|
|
10738
|
+
}
|
|
10739
|
+
} else {
|
|
10740
|
+
next(new BadRequestError54("Invalid PayPal webhook signature."));
|
|
10741
|
+
return;
|
|
10742
|
+
}
|
|
10743
|
+
res.sendStatus(200);
|
|
10036
10744
|
return;
|
|
10037
|
-
} catch (
|
|
10038
|
-
|
|
10745
|
+
} catch (error) {
|
|
10746
|
+
logger30.log({
|
|
10747
|
+
level: "error",
|
|
10748
|
+
message: `${error}`
|
|
10749
|
+
});
|
|
10750
|
+
next(new InternalServerError28(`PayPal webhook error: ${error.message}`));
|
|
10039
10751
|
}
|
|
10040
10752
|
}
|
|
10041
|
-
|
|
10042
|
-
|
|
10043
|
-
|
|
10044
|
-
|
|
10045
|
-
|
|
10046
|
-
|
|
10047
|
-
|
|
10048
|
-
|
|
10753
|
+
return {
|
|
10754
|
+
healthCheck,
|
|
10755
|
+
setGitHubVariables,
|
|
10756
|
+
paypalWebhook
|
|
10757
|
+
};
|
|
10758
|
+
}
|
|
10759
|
+
|
|
10760
|
+
// src/resources/utils/transaction.schema.ts
|
|
10761
|
+
import Joi44 from "joi";
|
|
10762
|
+
var transactionSchema = Joi44.object({
|
|
10763
|
+
_id: Joi44.string().hex().optional().allow("", null),
|
|
10764
|
+
payment: Joi44.string().required(),
|
|
10765
|
+
user: Joi44.string().hex().optional().allow("", null),
|
|
10766
|
+
org: Joi44.string().hex().optional().allow("", null),
|
|
10767
|
+
type: Joi44.string().required(),
|
|
10768
|
+
amount: Joi44.number().positive().min(0).required(),
|
|
10769
|
+
currency: Joi44.string().required(),
|
|
10770
|
+
description: Joi44.string().optional().allow("", null),
|
|
10771
|
+
metadata: Joi44.object({
|
|
10772
|
+
subscriptionId: Joi44.string().hex().optional().allow("", null),
|
|
10773
|
+
cycle: Joi44.number().optional().allow("", null),
|
|
10774
|
+
seats: Joi44.number().optional().allow("", null),
|
|
10775
|
+
promoCode: Joi44.string().optional().allow("", null)
|
|
10776
|
+
}).optional().allow("", null),
|
|
10777
|
+
status: Joi44.string().optional().allow("", null),
|
|
10778
|
+
createdAt: Joi44.string().optional().allow("", null),
|
|
10779
|
+
updatedAt: Joi44.string().optional().allow("", null),
|
|
10780
|
+
deletedAt: Joi44.string().optional().allow("", null)
|
|
10781
|
+
});
|
|
10782
|
+
|
|
10783
|
+
// src/resources/job-post/job.post.model.ts
|
|
10784
|
+
import { BadRequestError as BadRequestError55 } from "@goweekdays/utils";
|
|
10785
|
+
import Joi45 from "joi";
|
|
10786
|
+
import { ObjectId as ObjectId27 } from "mongodb";
|
|
10787
|
+
var schemaJobPost = Joi45.object({
|
|
10788
|
+
_id: Joi45.string().hex().optional(),
|
|
10789
|
+
org: Joi45.string().hex().optional(),
|
|
10790
|
+
title: Joi45.string().trim().required(),
|
|
10791
|
+
setup: Joi45.string().trim().required(),
|
|
10792
|
+
location: Joi45.string().trim().required(),
|
|
10793
|
+
type: Joi45.string().trim().required(),
|
|
10794
|
+
description: Joi45.string().trim().required(),
|
|
10795
|
+
status: Joi45.string().trim().required(),
|
|
10796
|
+
createdAt: Joi45.date().optional(),
|
|
10797
|
+
updatedAt: Joi45.date().optional(),
|
|
10798
|
+
deletedAt: Joi45.date().optional()
|
|
10799
|
+
});
|
|
10800
|
+
function modelJobPost(value) {
|
|
10801
|
+
const { error } = schemaJobPost.validate(value);
|
|
10802
|
+
if (error) {
|
|
10803
|
+
throw new BadRequestError55(`Invalid job post: ${error.message}`);
|
|
10804
|
+
}
|
|
10805
|
+
if (!value._id) {
|
|
10049
10806
|
try {
|
|
10050
|
-
|
|
10051
|
-
res.json(message);
|
|
10052
|
-
return;
|
|
10807
|
+
value._id = new ObjectId27();
|
|
10053
10808
|
} catch (error2) {
|
|
10054
|
-
|
|
10809
|
+
throw new BadRequestError55("Invalid job post ID.");
|
|
10055
10810
|
}
|
|
10056
10811
|
}
|
|
10057
|
-
|
|
10058
|
-
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10812
|
+
try {
|
|
10813
|
+
value.org = new ObjectId27(value.org);
|
|
10814
|
+
} catch (error2) {
|
|
10815
|
+
throw new BadRequestError55("Invalid Org ID");
|
|
10816
|
+
}
|
|
10817
|
+
return {
|
|
10818
|
+
_id: value._id,
|
|
10819
|
+
org: value.org,
|
|
10820
|
+
title: value.title,
|
|
10821
|
+
setup: value.setup,
|
|
10822
|
+
location: value.location,
|
|
10823
|
+
type: value.type,
|
|
10824
|
+
description: value.description,
|
|
10825
|
+
status: value.status ?? "active",
|
|
10826
|
+
createdAt: value.createdAt ?? /* @__PURE__ */ new Date(),
|
|
10827
|
+
updatedAt: value.updatedAt,
|
|
10828
|
+
deletedAt: value.deletedAt
|
|
10829
|
+
};
|
|
10830
|
+
}
|
|
10831
|
+
|
|
10832
|
+
// src/resources/job-post/job.post.controller.ts
|
|
10833
|
+
import { BadRequestError as BadRequestError58, logger as logger32 } from "@goweekdays/utils";
|
|
10834
|
+
|
|
10835
|
+
// src/resources/job-post/job.post.repository.ts
|
|
10836
|
+
import { BadRequestError as BadRequestError56, useAtlas as useAtlas27, useCache as useCache20, logger as logger31, InternalServerError as InternalServerError29 } from "@goweekdays/utils";
|
|
10837
|
+
import { ObjectId as ObjectId28 } from "mongodb";
|
|
10838
|
+
function useJobPostRepo() {
|
|
10839
|
+
const db = useAtlas27.getDb();
|
|
10840
|
+
if (!db) {
|
|
10841
|
+
throw new BadRequestError56("Unable to connect to server.");
|
|
10842
|
+
}
|
|
10843
|
+
const namespace_collection = "job.posts";
|
|
10844
|
+
const collection = db.collection(namespace_collection);
|
|
10845
|
+
const { delNamespace } = useCache20(namespace_collection);
|
|
10846
|
+
function delCachedData() {
|
|
10847
|
+
delNamespace().then(() => {
|
|
10848
|
+
logger31.log({
|
|
10849
|
+
level: "info",
|
|
10850
|
+
message: `Cache namespace cleared for ${namespace_collection}`
|
|
10851
|
+
});
|
|
10852
|
+
}).catch((err) => {
|
|
10853
|
+
logger31.log({
|
|
10854
|
+
level: "error",
|
|
10855
|
+
message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
|
|
10856
|
+
});
|
|
10857
|
+
});
|
|
10858
|
+
}
|
|
10859
|
+
async function createIndexes() {
|
|
10860
|
+
try {
|
|
10861
|
+
await collection.createIndexes([
|
|
10862
|
+
{ key: { title: 1 } },
|
|
10863
|
+
{ key: { setup: 1 } },
|
|
10864
|
+
{ key: { location: 1 } },
|
|
10865
|
+
{ key: { type: 1 } },
|
|
10866
|
+
{
|
|
10867
|
+
key: {
|
|
10868
|
+
title: "text",
|
|
10869
|
+
setup: "text",
|
|
10870
|
+
location: "text",
|
|
10871
|
+
type: "text"
|
|
10872
|
+
},
|
|
10873
|
+
name: "jobpost_text_search"
|
|
10874
|
+
}
|
|
10875
|
+
]);
|
|
10876
|
+
return "Successfully created job post indexes.";
|
|
10877
|
+
} catch (error) {
|
|
10878
|
+
throw new BadRequestError56("Failed to create job post indexes.");
|
|
10064
10879
|
}
|
|
10880
|
+
}
|
|
10881
|
+
async function add(value, session) {
|
|
10065
10882
|
try {
|
|
10066
|
-
|
|
10067
|
-
|
|
10068
|
-
|
|
10883
|
+
value = modelJobPost(value);
|
|
10884
|
+
const res = await collection.insertOne(value, { session });
|
|
10885
|
+
delCachedData();
|
|
10886
|
+
return res.insertedId;
|
|
10887
|
+
} catch (error) {
|
|
10888
|
+
logger31.log({
|
|
10889
|
+
level: "error",
|
|
10890
|
+
message: error.message
|
|
10069
10891
|
});
|
|
10070
|
-
|
|
10071
|
-
throw error2;
|
|
10892
|
+
throw new BadRequestError56(`Failed to create job post: ${error.message}`);
|
|
10072
10893
|
}
|
|
10073
10894
|
}
|
|
10074
|
-
async function
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
return;
|
|
10895
|
+
async function deleteById(_id, session) {
|
|
10896
|
+
try {
|
|
10897
|
+
_id = new ObjectId28(_id);
|
|
10898
|
+
} catch (error) {
|
|
10899
|
+
throw new BadRequestError56("Invalid ID.");
|
|
10080
10900
|
}
|
|
10081
|
-
const email = req.body.email ?? "";
|
|
10082
10901
|
try {
|
|
10083
|
-
await
|
|
10084
|
-
|
|
10085
|
-
|
|
10086
|
-
|
|
10087
|
-
|
|
10902
|
+
await collection.updateOne(
|
|
10903
|
+
{ _id },
|
|
10904
|
+
{
|
|
10905
|
+
$set: {
|
|
10906
|
+
status: "deleted",
|
|
10907
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
10908
|
+
deletedAt: /* @__PURE__ */ new Date()
|
|
10909
|
+
}
|
|
10910
|
+
},
|
|
10911
|
+
{ session }
|
|
10912
|
+
);
|
|
10913
|
+
delCachedData();
|
|
10914
|
+
return "Successfully deleted job post.";
|
|
10915
|
+
} catch (error) {
|
|
10916
|
+
throw new InternalServerError29("Failed to delete job post.");
|
|
10088
10917
|
}
|
|
10089
10918
|
}
|
|
10090
|
-
|
|
10091
|
-
|
|
10919
|
+
return {
|
|
10920
|
+
createIndexes,
|
|
10921
|
+
add,
|
|
10922
|
+
deleteById
|
|
10923
|
+
};
|
|
10924
|
+
}
|
|
10925
|
+
|
|
10926
|
+
// src/resources/job-post/job.post.service.ts
|
|
10927
|
+
import {
|
|
10928
|
+
AppError as AppError26,
|
|
10929
|
+
BadRequestError as BadRequestError57,
|
|
10930
|
+
InternalServerError as InternalServerError30
|
|
10931
|
+
} from "@goweekdays/utils";
|
|
10932
|
+
import Joi46 from "joi";
|
|
10933
|
+
function useJobPostService() {
|
|
10934
|
+
const { deleteById: _deleteById } = useJobPostRepo();
|
|
10935
|
+
async function deleteById(id) {
|
|
10936
|
+
const { error } = Joi46.string().hex().required().validate(id);
|
|
10092
10937
|
if (error) {
|
|
10093
|
-
|
|
10094
|
-
return;
|
|
10938
|
+
throw new BadRequestError57(error.message);
|
|
10095
10939
|
}
|
|
10096
10940
|
try {
|
|
10097
|
-
await
|
|
10098
|
-
|
|
10099
|
-
return;
|
|
10941
|
+
await _deleteById(id);
|
|
10942
|
+
return "Successfully deleted job post.";
|
|
10100
10943
|
} catch (error2) {
|
|
10101
|
-
|
|
10944
|
+
if (error2 instanceof AppError26) {
|
|
10945
|
+
throw error2;
|
|
10946
|
+
} else {
|
|
10947
|
+
throw new InternalServerError30("Failed to delete job post.");
|
|
10948
|
+
}
|
|
10102
10949
|
}
|
|
10103
10950
|
}
|
|
10104
|
-
|
|
10105
|
-
|
|
10106
|
-
|
|
10107
|
-
|
|
10951
|
+
return {
|
|
10952
|
+
deleteById
|
|
10953
|
+
};
|
|
10954
|
+
}
|
|
10955
|
+
|
|
10956
|
+
// src/resources/job-post/job.post.controller.ts
|
|
10957
|
+
function useJobPostController() {
|
|
10958
|
+
const { add: _add } = useJobPostRepo();
|
|
10959
|
+
const { deleteById: _deleteById } = useJobPostService();
|
|
10960
|
+
async function add(req, res, next) {
|
|
10961
|
+
const value = req.body;
|
|
10962
|
+
const { error } = schemaJobPost.validate(value);
|
|
10108
10963
|
if (error) {
|
|
10109
|
-
next(new
|
|
10964
|
+
next(new BadRequestError58(error.message));
|
|
10965
|
+
logger32.info(`Controller: ${error.message}`);
|
|
10110
10966
|
return;
|
|
10111
10967
|
}
|
|
10112
10968
|
try {
|
|
10113
|
-
const
|
|
10114
|
-
res.json({ message });
|
|
10969
|
+
const result = await _add(value);
|
|
10970
|
+
res.json({ message: "Successfully created job post.", data: { result } });
|
|
10115
10971
|
return;
|
|
10116
10972
|
} catch (error2) {
|
|
10117
10973
|
next(error2);
|
|
10118
10974
|
}
|
|
10119
10975
|
}
|
|
10120
|
-
async function
|
|
10121
|
-
const
|
|
10122
|
-
|
|
10123
|
-
|
|
10124
|
-
if (error) {
|
|
10125
|
-
next(new BadRequestError51(error.message));
|
|
10976
|
+
async function deleteById(req, res, next) {
|
|
10977
|
+
const id = req.params.id;
|
|
10978
|
+
if (!id) {
|
|
10979
|
+
next(new BadRequestError58("Job Post ID is required."));
|
|
10126
10980
|
return;
|
|
10127
10981
|
}
|
|
10128
10982
|
try {
|
|
10129
|
-
const message = await
|
|
10130
|
-
res.json(
|
|
10131
|
-
message
|
|
10132
|
-
});
|
|
10983
|
+
const message = await _deleteById(id);
|
|
10984
|
+
res.json(message);
|
|
10133
10985
|
return;
|
|
10134
|
-
} catch (
|
|
10135
|
-
|
|
10136
|
-
next(error2);
|
|
10137
|
-
} else {
|
|
10138
|
-
next(new InternalServerError27("An unexpected error occurred"));
|
|
10139
|
-
}
|
|
10986
|
+
} catch (error) {
|
|
10987
|
+
next(error);
|
|
10140
10988
|
}
|
|
10141
10989
|
}
|
|
10142
10990
|
return {
|
|
10143
|
-
|
|
10144
|
-
|
|
10145
|
-
createForgetPassword,
|
|
10146
|
-
verify,
|
|
10147
|
-
cancelUserInvitation,
|
|
10148
|
-
inviteMember,
|
|
10149
|
-
signUp,
|
|
10150
|
-
cancelInviteMember,
|
|
10151
|
-
forgetPassword
|
|
10991
|
+
add,
|
|
10992
|
+
deleteById
|
|
10152
10993
|
};
|
|
10153
10994
|
}
|
|
10154
10995
|
export {
|
|
@@ -10194,7 +11035,11 @@ export {
|
|
|
10194
11035
|
XENDIT_SECRET_KEY,
|
|
10195
11036
|
currencies,
|
|
10196
11037
|
isDev,
|
|
11038
|
+
ledgerBillStatuses,
|
|
11039
|
+
ledgerBillTypes,
|
|
10197
11040
|
modelApp,
|
|
11041
|
+
modelJobPost,
|
|
11042
|
+
modelLedgerBill,
|
|
10198
11043
|
modelMember,
|
|
10199
11044
|
modelOrg,
|
|
10200
11045
|
modelPermission,
|
|
@@ -10211,6 +11056,9 @@ export {
|
|
|
10211
11056
|
schemaBuilding,
|
|
10212
11057
|
schemaBuildingUnit,
|
|
10213
11058
|
schemaInviteMember,
|
|
11059
|
+
schemaJobPost,
|
|
11060
|
+
schemaLedgerBill,
|
|
11061
|
+
schemaLedgerBillingSummary,
|
|
10214
11062
|
schemaMember,
|
|
10215
11063
|
schemaMemberRole,
|
|
10216
11064
|
schemaMemberStatus,
|
|
@@ -10250,6 +11098,11 @@ export {
|
|
|
10250
11098
|
useFileRepo,
|
|
10251
11099
|
useFileService,
|
|
10252
11100
|
useGitHubService,
|
|
11101
|
+
useJobPostController,
|
|
11102
|
+
useJobPostRepo,
|
|
11103
|
+
useJobPostService,
|
|
11104
|
+
useLedgerBillingController,
|
|
11105
|
+
useLedgerBillingRepo,
|
|
10253
11106
|
useMemberController,
|
|
10254
11107
|
useMemberRepo,
|
|
10255
11108
|
useOrgController,
|