@eeplatform/core 1.0.0 → 1.0.2

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/dist/index.mjs CHANGED
@@ -11703,10 +11703,12 @@ var MUser = class {
11703
11703
  this.createdAt = value.createdAt ?? (/* @__PURE__ */ new Date()).toISOString();
11704
11704
  this.updatedAt = value.updatedAt ?? "";
11705
11705
  this.deletedAt = value.deletedAt ?? "";
11706
- try {
11707
- value.defaultOrg = new ObjectId3(value.defaultOrg);
11708
- } catch (error) {
11709
- throw new Error("Invalid defaultOrg ID.");
11706
+ if (value.defaultOrg) {
11707
+ try {
11708
+ value.defaultOrg = new ObjectId3(value.defaultOrg);
11709
+ } catch (error) {
11710
+ throw new Error("Invalid defaultOrg ID.");
11711
+ }
11710
11712
  }
11711
11713
  this.defaultOrg = value.defaultOrg;
11712
11714
  }
@@ -12188,7 +12190,8 @@ function useMemberRepo() {
12188
12190
  await collection.createIndex(
12189
12191
  {
12190
12192
  org: 1,
12191
- user: 1
12193
+ user: 1,
12194
+ type: 1
12192
12195
  },
12193
12196
  { partialFilterExpression: { deletedAt: "" }, unique: true }
12194
12197
  );
@@ -12217,6 +12220,10 @@ function useMemberRepo() {
12217
12220
  delCachedData();
12218
12221
  return "Successfully added member.";
12219
12222
  } catch (error) {
12223
+ logger3.log({
12224
+ level: "error",
12225
+ message: `Failed to create member: ${error}`
12226
+ });
12220
12227
  if (error instanceof AppError) {
12221
12228
  throw error;
12222
12229
  } else {
@@ -12758,9 +12765,6 @@ function useVerificationService() {
12758
12765
  if (!metadata.app) {
12759
12766
  throw new BadRequestError6("App metadata is required.");
12760
12767
  }
12761
- if (!metadata.org || !metadata.orgName) {
12762
- throw new BadRequestError6("Organization metadata is required.");
12763
- }
12764
12768
  try {
12765
12769
  const user = await getUserByEmail(email);
12766
12770
  const dir = __dirname;
@@ -12772,7 +12776,7 @@ function useVerificationService() {
12772
12776
  context: {
12773
12777
  validity: VERIFICATION_USER_INVITE_DURATION,
12774
12778
  link: `${APP_MAIN}/verify/member-invite/${res2}`,
12775
- organization_name: metadata.orgName
12779
+ organization_name: metadata.app === "admin" ? "Admin" : metadata.orgName
12776
12780
  },
12777
12781
  filePath: filePath2
12778
12782
  });
@@ -12943,11 +12947,6 @@ function useVerificationService() {
12943
12947
  "App metadata is required for member invite."
12944
12948
  );
12945
12949
  }
12946
- if (!_id.metadata?.org || !_id.metadata?.orgName) {
12947
- throw new BadRequestError6(
12948
- "Organization metadata is required for member invite."
12949
- );
12950
- }
12951
12950
  if (!_id.metadata?.role || !_id.metadata?.roleName) {
12952
12951
  throw new BadRequestError6(
12953
12952
  "Role metadata is required for member invite."
@@ -12957,8 +12956,8 @@ function useVerificationService() {
12957
12956
  {
12958
12957
  user: user._id?.toString() ?? "",
12959
12958
  type: _id.metadata?.app,
12960
- org: _id.metadata?.org,
12961
- orgName: _id.metadata?.orgName,
12959
+ org: _id.metadata?.org ?? "",
12960
+ orgName: _id.metadata?.orgName ?? "",
12962
12961
  role: _id.metadata?.role,
12963
12962
  roleName: _id.metadata?.roleName,
12964
12963
  name: `${user.firstName} ${user.lastName}`
@@ -13081,8 +13080,8 @@ function useVerificationController() {
13081
13080
  app: Joi2.string().required(),
13082
13081
  role: Joi2.string().hex().required(),
13083
13082
  roleName: Joi2.string().required(),
13084
- org: Joi2.string().required(),
13085
- orgName: Joi2.string().required()
13083
+ org: Joi2.string().hex().optional().optional().allow("", null),
13084
+ orgName: Joi2.string().optional().optional().allow("", null)
13086
13085
  });
13087
13086
  const { error } = validation.validate(req.body);
13088
13087
  if (error) {
@@ -17405,14 +17404,14 @@ var MRole = class {
17405
17404
  throw new Error("Invalid _id.");
17406
17405
  }
17407
17406
  }
17408
- if (typeof value.org === "string" && value.org.length === 24) {
17407
+ if (typeof value.id === "string" && value.id.length === 24) {
17409
17408
  try {
17410
- value.org = new ObjectId12(value.org);
17409
+ value.id = new ObjectId12(value.id);
17411
17410
  } catch (error) {
17412
- throw new Error("Invalid org.");
17411
+ throw new Error("Invalid id.");
17413
17412
  }
17414
17413
  }
17415
- this.org = value.org ?? "";
17414
+ this.id = value.id ?? "";
17416
17415
  this._id = value._id ?? new ObjectId12();
17417
17416
  this.name = value.name ?? "";
17418
17417
  this.description = value.description ?? "";
@@ -17462,6 +17461,7 @@ function useRoleRepo() {
17462
17461
  await collection.createIndex({ name: 1 });
17463
17462
  await collection.createIndex({ type: 1 });
17464
17463
  await collection.createIndex({ status: 1 });
17464
+ await collection.createIndex({ id: 1 });
17465
17465
  } catch (error) {
17466
17466
  throw new InternalServerError8("Failed to create index on role.");
17467
17467
  }
@@ -17476,7 +17476,7 @@ function useRoleRepo() {
17476
17476
  async function createUniqueIndex() {
17477
17477
  try {
17478
17478
  await collection.createIndex(
17479
- { name: 1, type: 1, org: 1 },
17479
+ { name: 1, type: 1, id: 1 },
17480
17480
  { unique: true }
17481
17481
  );
17482
17482
  } catch (error) {
@@ -17607,26 +17607,29 @@ function useRoleRepo() {
17607
17607
  limit = 10,
17608
17608
  sort = {},
17609
17609
  type = "",
17610
- org = ""
17610
+ id = ""
17611
17611
  } = {}) {
17612
17612
  limit = limit > 0 ? limit : 10;
17613
17613
  search = search || "";
17614
17614
  page = page > 0 ? page - 1 : 0;
17615
- if (org && typeof org === "string" && org.length === 24) {
17615
+ if (id && typeof id === "string" && id.length === 24) {
17616
17616
  try {
17617
- org = new ObjectId13(org);
17617
+ id = new ObjectId13(id);
17618
17618
  } catch (error) {
17619
- throw new BadRequestError10("Invalid organization ID.");
17619
+ throw new BadRequestError10("Invalid ID.");
17620
17620
  }
17621
17621
  }
17622
- const query = { status: "active", org };
17622
+ const query = { status: "active" };
17623
+ if (id) {
17624
+ query.id = id;
17625
+ }
17623
17626
  const cacheKeyOptions = {
17624
17627
  status: "active",
17625
17628
  limit,
17626
17629
  page
17627
17630
  };
17628
- if (org) {
17629
- cacheKeyOptions.org = String(org);
17631
+ if (id) {
17632
+ cacheKeyOptions.id = String(id);
17630
17633
  }
17631
17634
  if (type) {
17632
17635
  cacheKeyOptions.type = type;
@@ -17813,7 +17816,7 @@ function useUserService() {
17813
17816
  await _createUser(user, session);
17814
17817
  const roleId = await addRole(
17815
17818
  {
17816
- org: "",
17819
+ id: "",
17817
17820
  name: "Super Admin",
17818
17821
  type: "admin",
17819
17822
  permissions: ["*"],
@@ -17922,9 +17925,6 @@ function useUserService() {
17922
17925
  throw new BadRequestError11("Invitation expired.");
17923
17926
  }
17924
17927
  const email = invitation.email;
17925
- if (!invitation.metadata.org) {
17926
- throw new BadRequestError11("Invitation does not have an organization.");
17927
- }
17928
17928
  const _user = await getUserByEmail(invitation.email);
17929
17929
  let userId = _user?._id ?? "";
17930
17930
  if (!_user) {
@@ -17949,8 +17949,8 @@ function useUserService() {
17949
17949
  role: invitation.metadata?.role,
17950
17950
  roleName: invitation.metadata?.roleName,
17951
17951
  type: invitation.metadata?.app,
17952
- org: invitation.metadata?.org,
17953
- orgName: invitation.metadata?.orgName
17952
+ org: invitation.metadata?.org ?? "",
17953
+ orgName: invitation.metadata?.orgName ?? ""
17954
17954
  },
17955
17955
  session
17956
17956
  );
@@ -18779,8 +18779,8 @@ function useRoleController() {
18779
18779
  const validation = Joi7.object({
18780
18780
  name: Joi7.string().required(),
18781
18781
  permissions: Joi7.array().items(Joi7.string()).required(),
18782
- type: Joi7.string().optional().allow("", null),
18783
- org: Joi7.string().hex().optional().allow("", null)
18782
+ type: Joi7.string().valid("school", "division", "region", "account").required(),
18783
+ id: Joi7.string().hex().optional().allow("", null)
18784
18784
  });
18785
18785
  const { error } = validation.validate(payload);
18786
18786
  if (error) {
@@ -18800,21 +18800,21 @@ function useRoleController() {
18800
18800
  const page = parseInt(req.query.page ?? "1");
18801
18801
  const limit = parseInt(req.query.limit ?? "10");
18802
18802
  const type = req.query.type ?? "";
18803
- const org = req.query.org ?? "";
18803
+ const id = req.query.id ?? "";
18804
18804
  const validation = Joi7.object({
18805
18805
  search: Joi7.string().optional().allow("", null),
18806
18806
  page: Joi7.number().required(),
18807
18807
  limit: Joi7.number().required(),
18808
18808
  type: Joi7.string().optional().allow("", null),
18809
- org: Joi7.string().hex().optional().allow("", null)
18809
+ id: Joi7.string().hex().optional().allow("", null)
18810
18810
  });
18811
- const { error } = validation.validate({ search, page, limit, type, org });
18811
+ const { error } = validation.validate({ search, page, limit, type, id });
18812
18812
  if (error) {
18813
18813
  next(new BadRequestError16(error.message));
18814
18814
  return;
18815
18815
  }
18816
18816
  try {
18817
- const data = await _getRoles({ search, page, limit, type, org });
18817
+ const data = await _getRoles({ search, page, limit, type, id });
18818
18818
  res.json(data);
18819
18819
  return;
18820
18820
  } catch (error2) {
@@ -21731,7 +21731,7 @@ function useSubscriptionService() {
21731
21731
  const roleName = "owner";
21732
21732
  const role = await addRole(
21733
21733
  {
21734
- org: orgId.toString(),
21734
+ id: orgId.toString(),
21735
21735
  name: roleName,
21736
21736
  permissions: ["*"],
21737
21737
  type: "organization",
@@ -21920,7 +21920,7 @@ function useSubscriptionService() {
21920
21920
  }
21921
21921
  const role = await addRole(
21922
21922
  {
21923
- org: orgId.toString(),
21923
+ id: orgId.toString(),
21924
21924
  name: "owner",
21925
21925
  permissions: ["*"],
21926
21926
  type: "organization",
@@ -24174,7 +24174,7 @@ function useOrgService() {
24174
24174
  );
24175
24175
  const role = await addRole(
24176
24176
  {
24177
- org,
24177
+ id: org,
24178
24178
  name: "Owner",
24179
24179
  description: "Owner of the organization",
24180
24180
  permissions: ["all"]
@@ -25335,6 +25335,1704 @@ function usePriceController() {
25335
25335
  getByNameType
25336
25336
  };
25337
25337
  }
25338
+
25339
+ // src/models/region.model.ts
25340
+ import { BadRequestError as BadRequestError50 } from "@eeplatform/nodejs-utils";
25341
+ import Joi27 from "joi";
25342
+ import { ObjectId as ObjectId33 } from "mongodb";
25343
+ var schemaRegion = Joi27.object({
25344
+ _id: Joi27.string().hex().optional().allow(null, ""),
25345
+ name: Joi27.string().min(1).max(100).required(),
25346
+ director: Joi27.string().hex().optional().allow(null, ""),
25347
+ directorName: Joi27.string().min(1).max(100).optional().allow(null, ""),
25348
+ createdAt: Joi27.string().isoDate().optional(),
25349
+ updatedAt: Joi27.string().isoDate().optional(),
25350
+ deletedAt: Joi27.string().isoDate().optional().allow(null, "")
25351
+ });
25352
+ function MRegion(value) {
25353
+ const { error } = schemaRegion.validate(value);
25354
+ if (error) {
25355
+ throw new BadRequestError50(`Invalid region data: ${error.message}`);
25356
+ }
25357
+ if (value._id && typeof value._id === "string") {
25358
+ try {
25359
+ value._id = new ObjectId33(value._id);
25360
+ } catch (error2) {
25361
+ throw new Error("Invalid _id.");
25362
+ }
25363
+ }
25364
+ if (value.director && typeof value.director === "string") {
25365
+ try {
25366
+ value.director = new ObjectId33(value.director);
25367
+ } catch (error2) {
25368
+ throw new Error("Invalid director.");
25369
+ }
25370
+ }
25371
+ return {
25372
+ _id: value._id,
25373
+ name: value.name,
25374
+ director: value.director ?? "",
25375
+ directorName: value.directorName ?? "",
25376
+ createdAt: value.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
25377
+ updatedAt: value.updatedAt ?? "",
25378
+ deletedAt: value.deletedAt ?? ""
25379
+ };
25380
+ }
25381
+
25382
+ // src/repositories/region.repository.ts
25383
+ import {
25384
+ AppError as AppError12,
25385
+ BadRequestError as BadRequestError51,
25386
+ InternalServerError as InternalServerError23,
25387
+ logger as logger24,
25388
+ makeCacheKey as makeCacheKey16,
25389
+ paginate as paginate12,
25390
+ useAtlas as useAtlas25,
25391
+ useCache as useCache16
25392
+ } from "@eeplatform/nodejs-utils";
25393
+ import { ObjectId as ObjectId34 } from "mongodb";
25394
+ function useRegionRepo() {
25395
+ const db = useAtlas25.getDb();
25396
+ if (!db) {
25397
+ throw new Error("Unable to connect to server.");
25398
+ }
25399
+ const namespace_collection = "regions";
25400
+ const collection = db.collection(namespace_collection);
25401
+ const { getCache, setCache, delNamespace } = useCache16();
25402
+ async function createIndex() {
25403
+ try {
25404
+ await collection.createIndex([
25405
+ { name: 1 },
25406
+ { director: 1 },
25407
+ { createdAt: 1 }
25408
+ ]);
25409
+ } catch (error) {
25410
+ throw new Error("Failed to create index on regions.");
25411
+ }
25412
+ }
25413
+ async function createTextIndex() {
25414
+ try {
25415
+ await collection.createIndex({
25416
+ name: "text",
25417
+ directorName: "text"
25418
+ });
25419
+ } catch (error) {
25420
+ throw new Error(
25421
+ "Failed to create text index on region name and director name."
25422
+ );
25423
+ }
25424
+ }
25425
+ async function createUniqueIndex() {
25426
+ try {
25427
+ await collection.createIndex(
25428
+ {
25429
+ name: 1
25430
+ },
25431
+ { unique: true }
25432
+ );
25433
+ } catch (error) {
25434
+ throw new Error("Failed to create unique index on region name.");
25435
+ }
25436
+ }
25437
+ function delCachedData() {
25438
+ delNamespace(namespace_collection).then(() => {
25439
+ logger24.log({
25440
+ level: "info",
25441
+ message: `Cache namespace cleared for ${namespace_collection}`
25442
+ });
25443
+ }).catch((err) => {
25444
+ logger24.log({
25445
+ level: "error",
25446
+ message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
25447
+ });
25448
+ });
25449
+ }
25450
+ async function add(value, session) {
25451
+ try {
25452
+ value = MRegion(value);
25453
+ const res = await collection.insertOne(value, { session });
25454
+ delCachedData();
25455
+ return res.insertedId;
25456
+ } catch (error) {
25457
+ logger24.log({
25458
+ level: "error",
25459
+ message: error.message
25460
+ });
25461
+ if (error instanceof AppError12) {
25462
+ throw error;
25463
+ } else {
25464
+ const isDuplicated = error.message.includes("duplicate");
25465
+ if (isDuplicated) {
25466
+ throw new BadRequestError51("Region already exists.");
25467
+ }
25468
+ throw new Error("Failed to create region.");
25469
+ }
25470
+ }
25471
+ }
25472
+ async function getAll({ search = "", page = 1, limit = 10, sort = {} } = {}) {
25473
+ page = page > 0 ? page - 1 : 0;
25474
+ const query = { deletedAt: { $in: ["", null] } };
25475
+ sort = Object.keys(sort).length > 0 ? sort : { _id: 1 };
25476
+ if (search) {
25477
+ query.$text = { $search: search };
25478
+ }
25479
+ const cacheKey = makeCacheKey16(namespace_collection, {
25480
+ search,
25481
+ page,
25482
+ limit,
25483
+ sort: JSON.stringify(sort)
25484
+ });
25485
+ logger24.log({
25486
+ level: "info",
25487
+ message: `Cache key for getAll regions: ${cacheKey}`
25488
+ });
25489
+ try {
25490
+ const cached = await getCache(cacheKey);
25491
+ if (cached) {
25492
+ logger24.log({
25493
+ level: "info",
25494
+ message: `Cache hit for getAll regions: ${cacheKey}`
25495
+ });
25496
+ return cached;
25497
+ }
25498
+ const items = await collection.aggregate([
25499
+ { $match: query },
25500
+ { $sort: sort },
25501
+ { $skip: page * limit },
25502
+ { $limit: limit },
25503
+ {
25504
+ $project: {
25505
+ _id: 1,
25506
+ name: 1,
25507
+ director: 1,
25508
+ directorName: 1,
25509
+ createdAt: 1,
25510
+ updatedAt: 1
25511
+ }
25512
+ }
25513
+ ]).toArray();
25514
+ const length = await collection.countDocuments(query);
25515
+ const data = paginate12(items, page, limit, length);
25516
+ setCache(cacheKey, data, 600, namespace_collection).then(() => {
25517
+ logger24.log({
25518
+ level: "info",
25519
+ message: `Cache set for getAll regions: ${cacheKey}`
25520
+ });
25521
+ }).catch((err) => {
25522
+ logger24.log({
25523
+ level: "error",
25524
+ message: `Failed to set cache for getAll regions: ${err.message}`
25525
+ });
25526
+ });
25527
+ return data;
25528
+ } catch (error) {
25529
+ logger24.log({ level: "error", message: `${error}` });
25530
+ throw error;
25531
+ }
25532
+ }
25533
+ async function getById(_id) {
25534
+ try {
25535
+ _id = new ObjectId34(_id);
25536
+ } catch (error) {
25537
+ throw new BadRequestError51("Invalid ID.");
25538
+ }
25539
+ const cacheKey = makeCacheKey16(namespace_collection, { _id: String(_id) });
25540
+ try {
25541
+ const cached = await getCache(cacheKey);
25542
+ if (cached) {
25543
+ logger24.log({
25544
+ level: "info",
25545
+ message: `Cache hit for getById region: ${cacheKey}`
25546
+ });
25547
+ return cached;
25548
+ }
25549
+ const result = await collection.findOne({
25550
+ _id,
25551
+ deletedAt: { $in: ["", null] }
25552
+ });
25553
+ if (!result) {
25554
+ throw new BadRequestError51("Region not found.");
25555
+ }
25556
+ setCache(cacheKey, result, 300, namespace_collection).then(() => {
25557
+ logger24.log({
25558
+ level: "info",
25559
+ message: `Cache set for region by id: ${cacheKey}`
25560
+ });
25561
+ }).catch((err) => {
25562
+ logger24.log({
25563
+ level: "error",
25564
+ message: `Failed to set cache for region by id: ${err.message}`
25565
+ });
25566
+ });
25567
+ return result;
25568
+ } catch (error) {
25569
+ if (error instanceof AppError12) {
25570
+ throw error;
25571
+ } else {
25572
+ throw new InternalServerError23("Failed to get region.");
25573
+ }
25574
+ }
25575
+ }
25576
+ async function getByName(name) {
25577
+ const cacheKey = makeCacheKey16(namespace_collection, { name });
25578
+ try {
25579
+ const cached = await getCache(cacheKey);
25580
+ if (cached) {
25581
+ logger24.log({
25582
+ level: "info",
25583
+ message: `Cache hit for getByName region: ${cacheKey}`
25584
+ });
25585
+ return cached;
25586
+ }
25587
+ const result = await collection.findOne({
25588
+ name,
25589
+ deletedAt: { $in: ["", null] }
25590
+ });
25591
+ if (!result) {
25592
+ throw new BadRequestError51("Region not found.");
25593
+ }
25594
+ setCache(cacheKey, result, 300, namespace_collection).then(() => {
25595
+ logger24.log({
25596
+ level: "info",
25597
+ message: `Cache set for region by name: ${cacheKey}`
25598
+ });
25599
+ }).catch((err) => {
25600
+ logger24.log({
25601
+ level: "error",
25602
+ message: `Failed to set cache for region by name: ${err.message}`
25603
+ });
25604
+ });
25605
+ return result;
25606
+ } catch (error) {
25607
+ if (error instanceof AppError12) {
25608
+ throw error;
25609
+ } else {
25610
+ throw new InternalServerError23("Failed to get region.");
25611
+ }
25612
+ }
25613
+ }
25614
+ async function updateFieldById({ _id, field, value } = {}, session) {
25615
+ const allowedFields = ["name", "director", "directorName"];
25616
+ if (!allowedFields.includes(field)) {
25617
+ throw new BadRequestError51(
25618
+ `Field "${field}" is not allowed to be updated.`
25619
+ );
25620
+ }
25621
+ try {
25622
+ _id = new ObjectId34(_id);
25623
+ } catch (error) {
25624
+ throw new BadRequestError51("Invalid ID.");
25625
+ }
25626
+ try {
25627
+ await collection.updateOne(
25628
+ { _id, deletedAt: { $in: ["", null] } },
25629
+ { $set: { [field]: value, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } },
25630
+ { session }
25631
+ );
25632
+ delCachedData();
25633
+ return `Successfully updated region ${field}.`;
25634
+ } catch (error) {
25635
+ throw new InternalServerError23(`Failed to update region ${field}.`);
25636
+ }
25637
+ }
25638
+ async function deleteById(_id) {
25639
+ try {
25640
+ _id = new ObjectId34(_id);
25641
+ } catch (error) {
25642
+ throw new BadRequestError51("Invalid ID.");
25643
+ }
25644
+ try {
25645
+ await collection.updateOne(
25646
+ { _id },
25647
+ { $set: { deletedAt: (/* @__PURE__ */ new Date()).toISOString() } }
25648
+ );
25649
+ delCachedData();
25650
+ return "Successfully deleted region.";
25651
+ } catch (error) {
25652
+ throw new InternalServerError23("Failed to delete region.");
25653
+ }
25654
+ }
25655
+ return {
25656
+ createIndex,
25657
+ createTextIndex,
25658
+ createUniqueIndex,
25659
+ add,
25660
+ getAll,
25661
+ getById,
25662
+ updateFieldById,
25663
+ deleteById,
25664
+ getByName
25665
+ };
25666
+ }
25667
+
25668
+ // src/controllers/region.controller.ts
25669
+ import { BadRequestError as BadRequestError52 } from "@eeplatform/nodejs-utils";
25670
+ import Joi28 from "joi";
25671
+
25672
+ // src/services/region.service.ts
25673
+ import { useAtlas as useAtlas26 } from "@eeplatform/nodejs-utils";
25674
+ function useRegionService() {
25675
+ const { add: _add } = useRegionRepo();
25676
+ const { addRole } = useRoleRepo();
25677
+ async function add(value) {
25678
+ const session = useAtlas26.getClient()?.startSession();
25679
+ if (!session) {
25680
+ throw new Error("Unable to start session for region service.");
25681
+ }
25682
+ try {
25683
+ session.startTransaction();
25684
+ const region = await _add(value, session);
25685
+ await addRole(
25686
+ {
25687
+ id: region.toString(),
25688
+ name: "Admin",
25689
+ type: "region",
25690
+ permissions: ["*"],
25691
+ status: "active",
25692
+ default: true
25693
+ },
25694
+ session
25695
+ );
25696
+ await session.commitTransaction();
25697
+ return "Region and admin role created successfully.";
25698
+ } catch (error) {
25699
+ await session.abortTransaction();
25700
+ throw error;
25701
+ } finally {
25702
+ session.endSession();
25703
+ }
25704
+ }
25705
+ return {
25706
+ add
25707
+ };
25708
+ }
25709
+
25710
+ // src/controllers/region.controller.ts
25711
+ function useRegionController() {
25712
+ const {
25713
+ getAll: _getAll,
25714
+ getById: _getById,
25715
+ getByName: _getByName,
25716
+ updateFieldById: _updateFieldById,
25717
+ deleteById: _deleteById
25718
+ } = useRegionRepo();
25719
+ const { add: _createRegion } = useRegionService();
25720
+ async function createRegion(req, res, next) {
25721
+ const value = req.body;
25722
+ const validation = Joi28.object({
25723
+ name: Joi28.string().min(1).max(100).required(),
25724
+ director: Joi28.string().hex().optional().allow("", null),
25725
+ directorName: Joi28.string().min(1).max(100).optional().allow("", null)
25726
+ });
25727
+ const { error } = validation.validate(value);
25728
+ if (error) {
25729
+ next(new BadRequestError52(error.message));
25730
+ return;
25731
+ }
25732
+ try {
25733
+ const regionId = await _createRegion(value);
25734
+ res.json({
25735
+ message: "Successfully created region.",
25736
+ data: { regionId }
25737
+ });
25738
+ return;
25739
+ } catch (error2) {
25740
+ next(error2);
25741
+ }
25742
+ }
25743
+ async function getAll(req, res, next) {
25744
+ const query = req.query;
25745
+ const validation = Joi28.object({
25746
+ page: Joi28.number().min(1).optional().allow("", null),
25747
+ limit: Joi28.number().min(1).optional().allow("", null),
25748
+ search: Joi28.string().optional().allow("", null)
25749
+ });
25750
+ const { error } = validation.validate(query);
25751
+ const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
25752
+ const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
25753
+ const search = req.query.search ?? "";
25754
+ const isPageNumber = isFinite(page);
25755
+ if (!isPageNumber) {
25756
+ next(new BadRequestError52("Invalid page number."));
25757
+ return;
25758
+ }
25759
+ const isLimitNumber = isFinite(limit);
25760
+ if (!isLimitNumber) {
25761
+ next(new BadRequestError52("Invalid limit number."));
25762
+ return;
25763
+ }
25764
+ if (error) {
25765
+ next(new BadRequestError52(error.message));
25766
+ return;
25767
+ }
25768
+ try {
25769
+ const regions = await _getAll({ page, limit, search });
25770
+ res.json(regions);
25771
+ return;
25772
+ } catch (error2) {
25773
+ next(error2);
25774
+ }
25775
+ }
25776
+ async function getById(req, res, next) {
25777
+ const id = req.params.id;
25778
+ const validation = Joi28.object({
25779
+ id: Joi28.string().hex().required()
25780
+ });
25781
+ const { error } = validation.validate({ id });
25782
+ if (error) {
25783
+ next(new BadRequestError52(error.message));
25784
+ return;
25785
+ }
25786
+ try {
25787
+ const region = await _getById(id);
25788
+ res.json({
25789
+ message: "Successfully retrieved region.",
25790
+ data: { region }
25791
+ });
25792
+ return;
25793
+ } catch (error2) {
25794
+ next(error2);
25795
+ }
25796
+ }
25797
+ async function getByName(req, res, next) {
25798
+ const name = req.params.name;
25799
+ const validation = Joi28.object({
25800
+ name: Joi28.string().required()
25801
+ });
25802
+ const { error } = validation.validate({ name });
25803
+ if (error) {
25804
+ next(new BadRequestError52(error.message));
25805
+ return;
25806
+ }
25807
+ try {
25808
+ const region = await _getByName(name);
25809
+ res.json({
25810
+ message: "Successfully retrieved region.",
25811
+ data: { region }
25812
+ });
25813
+ return;
25814
+ } catch (error2) {
25815
+ next(error2);
25816
+ }
25817
+ }
25818
+ async function updateField(req, res, next) {
25819
+ const _id = req.params.id;
25820
+ const { field, value } = req.body;
25821
+ const validation = Joi28.object({
25822
+ _id: Joi28.string().hex().required(),
25823
+ field: Joi28.string().valid("name", "director", "directorName").required(),
25824
+ value: Joi28.string().required()
25825
+ });
25826
+ const { error } = validation.validate({ _id, field, value });
25827
+ if (error) {
25828
+ next(new BadRequestError52(error.message));
25829
+ return;
25830
+ }
25831
+ try {
25832
+ const message = await _updateFieldById({ _id, field, value });
25833
+ res.json({ message });
25834
+ return;
25835
+ } catch (error2) {
25836
+ next(error2);
25837
+ }
25838
+ }
25839
+ async function deleteRegion(req, res, next) {
25840
+ const _id = req.params.id;
25841
+ const validation = Joi28.object({
25842
+ _id: Joi28.string().hex().required()
25843
+ });
25844
+ const { error } = validation.validate({ _id });
25845
+ if (error) {
25846
+ next(new BadRequestError52(error.message));
25847
+ return;
25848
+ }
25849
+ try {
25850
+ const message = await _deleteById(_id);
25851
+ res.json({ message });
25852
+ return;
25853
+ } catch (error2) {
25854
+ next(error2);
25855
+ }
25856
+ }
25857
+ return {
25858
+ createRegion,
25859
+ getAll,
25860
+ getById,
25861
+ getByName,
25862
+ updateField,
25863
+ deleteRegion
25864
+ };
25865
+ }
25866
+
25867
+ // src/models/division.model.ts
25868
+ import { BadRequestError as BadRequestError53 } from "@eeplatform/nodejs-utils";
25869
+ import Joi29 from "joi";
25870
+ import { ObjectId as ObjectId35 } from "mongodb";
25871
+ var schemaDivision = Joi29.object({
25872
+ _id: Joi29.string().hex().optional().allow(null, ""),
25873
+ name: Joi29.string().min(1).max(100).required(),
25874
+ region: Joi29.string().hex().optional().allow(null, ""),
25875
+ regionName: Joi29.string().min(1).max(100).optional().allow(null, ""),
25876
+ superintendent: Joi29.string().hex().optional().allow(null, ""),
25877
+ superintendentName: Joi29.string().min(1).max(100).optional().allow(null, ""),
25878
+ createdAt: Joi29.string().isoDate().optional(),
25879
+ updatedAt: Joi29.string().isoDate().optional(),
25880
+ deletedAt: Joi29.string().isoDate().optional().allow(null, "")
25881
+ });
25882
+ function MDivision(value) {
25883
+ const { error } = schemaDivision.validate(value);
25884
+ if (error) {
25885
+ throw new BadRequestError53(`Invalid division data: ${error.message}`);
25886
+ }
25887
+ if (value._id && typeof value._id === "string") {
25888
+ try {
25889
+ value._id = new ObjectId35(value._id);
25890
+ } catch (error2) {
25891
+ throw new Error("Invalid _id.");
25892
+ }
25893
+ }
25894
+ if (value.region && typeof value.region === "string") {
25895
+ try {
25896
+ value.region = new ObjectId35(value.region);
25897
+ } catch (error2) {
25898
+ throw new Error("Invalid region.");
25899
+ }
25900
+ }
25901
+ if (value.superintendent && typeof value.superintendent === "string") {
25902
+ try {
25903
+ value.superintendent = new ObjectId35(value.superintendent);
25904
+ } catch (error2) {
25905
+ throw new Error("Invalid superintendent.");
25906
+ }
25907
+ }
25908
+ return {
25909
+ _id: value._id,
25910
+ name: value.name,
25911
+ region: value.region ?? "",
25912
+ regionName: value.regionName ?? "",
25913
+ superintendent: value.superintendent ?? "",
25914
+ superintendentName: value.superintendentName ?? "",
25915
+ createdAt: value.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
25916
+ updatedAt: value.updatedAt ?? "",
25917
+ deletedAt: value.deletedAt ?? ""
25918
+ };
25919
+ }
25920
+
25921
+ // src/repositories/division.repository.ts
25922
+ import {
25923
+ AppError as AppError13,
25924
+ BadRequestError as BadRequestError54,
25925
+ InternalServerError as InternalServerError24,
25926
+ logger as logger25,
25927
+ makeCacheKey as makeCacheKey17,
25928
+ paginate as paginate13,
25929
+ useAtlas as useAtlas27,
25930
+ useCache as useCache17
25931
+ } from "@eeplatform/nodejs-utils";
25932
+ import { ObjectId as ObjectId36 } from "mongodb";
25933
+ function useDivisionRepo() {
25934
+ const db = useAtlas27.getDb();
25935
+ if (!db) {
25936
+ throw new Error("Unable to connect to server.");
25937
+ }
25938
+ const namespace_collection = "divisions";
25939
+ const collection = db.collection(namespace_collection);
25940
+ const { getCache, setCache, delNamespace } = useCache17();
25941
+ async function createIndex() {
25942
+ try {
25943
+ await collection.createIndex([
25944
+ { name: 1 },
25945
+ { region: 1 },
25946
+ { superintendent: 1 },
25947
+ { createdAt: 1 }
25948
+ ]);
25949
+ } catch (error) {
25950
+ throw new Error("Failed to create index on divisions.");
25951
+ }
25952
+ }
25953
+ async function createTextIndex() {
25954
+ try {
25955
+ await collection.createIndex({
25956
+ name: "text",
25957
+ regionName: "text",
25958
+ superintendentName: "text"
25959
+ });
25960
+ } catch (error) {
25961
+ throw new Error(
25962
+ "Failed to create text index on division name and superintendent name."
25963
+ );
25964
+ }
25965
+ }
25966
+ async function createUniqueIndex() {
25967
+ try {
25968
+ await collection.createIndex(
25969
+ {
25970
+ name: 1
25971
+ },
25972
+ { unique: true }
25973
+ );
25974
+ } catch (error) {
25975
+ throw new Error("Failed to create unique index on division name.");
25976
+ }
25977
+ }
25978
+ function delCachedData() {
25979
+ delNamespace(namespace_collection).then(() => {
25980
+ logger25.log({
25981
+ level: "info",
25982
+ message: `Cache namespace cleared for ${namespace_collection}`
25983
+ });
25984
+ }).catch((err) => {
25985
+ logger25.log({
25986
+ level: "error",
25987
+ message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
25988
+ });
25989
+ });
25990
+ }
25991
+ async function add(value, session) {
25992
+ try {
25993
+ value = MDivision(value);
25994
+ const res = await collection.insertOne(value, { session });
25995
+ delCachedData();
25996
+ return res.insertedId;
25997
+ } catch (error) {
25998
+ logger25.log({
25999
+ level: "error",
26000
+ message: error.message
26001
+ });
26002
+ if (error instanceof AppError13) {
26003
+ throw error;
26004
+ } else {
26005
+ const isDuplicated = error.message.includes("duplicate");
26006
+ if (isDuplicated) {
26007
+ throw new BadRequestError54("Division already exists.");
26008
+ }
26009
+ throw new Error("Failed to create division.");
26010
+ }
26011
+ }
26012
+ }
26013
+ async function getAll({
26014
+ search = "",
26015
+ page = 1,
26016
+ limit = 10,
26017
+ sort = {},
26018
+ region = ""
26019
+ } = {}) {
26020
+ page = page > 0 ? page - 1 : 0;
26021
+ const query = { deletedAt: { $in: ["", null] } };
26022
+ sort = Object.keys(sort).length > 0 ? sort : { _id: -1 };
26023
+ if (search) {
26024
+ query.$text = { $search: search };
26025
+ }
26026
+ if (region) {
26027
+ try {
26028
+ query.region = new ObjectId36(region);
26029
+ } catch (error) {
26030
+ throw new BadRequestError54("Invalid region ID.");
26031
+ }
26032
+ }
26033
+ const cacheKey = makeCacheKey17(namespace_collection, {
26034
+ search,
26035
+ page,
26036
+ limit,
26037
+ sort: JSON.stringify(sort),
26038
+ region
26039
+ });
26040
+ logger25.log({
26041
+ level: "info",
26042
+ message: `Cache key for getAll divisions: ${cacheKey}`
26043
+ });
26044
+ try {
26045
+ const cached = await getCache(cacheKey);
26046
+ if (cached) {
26047
+ logger25.log({
26048
+ level: "info",
26049
+ message: `Cache hit for getAll divisions: ${cacheKey}`
26050
+ });
26051
+ return cached;
26052
+ }
26053
+ const items = await collection.aggregate([
26054
+ { $match: query },
26055
+ { $sort: sort },
26056
+ { $skip: page * limit },
26057
+ { $limit: limit },
26058
+ {
26059
+ $project: {
26060
+ _id: 1,
26061
+ name: 1,
26062
+ region: 1,
26063
+ regionName: 1,
26064
+ superintendent: 1,
26065
+ superintendentName: 1,
26066
+ createdAt: 1,
26067
+ updatedAt: 1
26068
+ }
26069
+ }
26070
+ ]).toArray();
26071
+ const length = await collection.countDocuments(query);
26072
+ const data = paginate13(items, page, limit, length);
26073
+ setCache(cacheKey, data, 600, namespace_collection).then(() => {
26074
+ logger25.log({
26075
+ level: "info",
26076
+ message: `Cache set for getAll divisions: ${cacheKey}`
26077
+ });
26078
+ }).catch((err) => {
26079
+ logger25.log({
26080
+ level: "error",
26081
+ message: `Failed to set cache for getAll divisions: ${err.message}`
26082
+ });
26083
+ });
26084
+ return data;
26085
+ } catch (error) {
26086
+ logger25.log({ level: "error", message: `${error}` });
26087
+ throw error;
26088
+ }
26089
+ }
26090
+ async function getById(_id) {
26091
+ try {
26092
+ _id = new ObjectId36(_id);
26093
+ } catch (error) {
26094
+ throw new BadRequestError54("Invalid ID.");
26095
+ }
26096
+ const cacheKey = makeCacheKey17(namespace_collection, { _id: String(_id) });
26097
+ try {
26098
+ const cached = await getCache(cacheKey);
26099
+ if (cached) {
26100
+ logger25.log({
26101
+ level: "info",
26102
+ message: `Cache hit for getById division: ${cacheKey}`
26103
+ });
26104
+ return cached;
26105
+ }
26106
+ const result = await collection.findOne({
26107
+ _id,
26108
+ deletedAt: { $in: ["", null] }
26109
+ });
26110
+ if (!result) {
26111
+ throw new BadRequestError54("Division not found.");
26112
+ }
26113
+ setCache(cacheKey, result, 300, namespace_collection).then(() => {
26114
+ logger25.log({
26115
+ level: "info",
26116
+ message: `Cache set for division by id: ${cacheKey}`
26117
+ });
26118
+ }).catch((err) => {
26119
+ logger25.log({
26120
+ level: "error",
26121
+ message: `Failed to set cache for division by id: ${err.message}`
26122
+ });
26123
+ });
26124
+ return result;
26125
+ } catch (error) {
26126
+ if (error instanceof AppError13) {
26127
+ throw error;
26128
+ } else {
26129
+ throw new InternalServerError24("Failed to get division.");
26130
+ }
26131
+ }
26132
+ }
26133
+ async function getByName(name) {
26134
+ const cacheKey = makeCacheKey17(namespace_collection, { name });
26135
+ try {
26136
+ const cached = await getCache(cacheKey);
26137
+ if (cached) {
26138
+ logger25.log({
26139
+ level: "info",
26140
+ message: `Cache hit for getByName division: ${cacheKey}`
26141
+ });
26142
+ return cached;
26143
+ }
26144
+ const result = await collection.findOne({
26145
+ name,
26146
+ deletedAt: { $in: ["", null] }
26147
+ });
26148
+ if (!result) {
26149
+ throw new BadRequestError54("Division not found.");
26150
+ }
26151
+ setCache(cacheKey, result, 300, namespace_collection).then(() => {
26152
+ logger25.log({
26153
+ level: "info",
26154
+ message: `Cache set for division by name: ${cacheKey}`
26155
+ });
26156
+ }).catch((err) => {
26157
+ logger25.log({
26158
+ level: "error",
26159
+ message: `Failed to set cache for division by name: ${err.message}`
26160
+ });
26161
+ });
26162
+ return result;
26163
+ } catch (error) {
26164
+ if (error instanceof AppError13) {
26165
+ throw error;
26166
+ } else {
26167
+ throw new InternalServerError24("Failed to get division.");
26168
+ }
26169
+ }
26170
+ }
26171
+ async function updateFieldById({ _id, field, value } = {}, session) {
26172
+ const allowedFields = [
26173
+ "name",
26174
+ "region",
26175
+ "regionName",
26176
+ "superintendent",
26177
+ "superintendentName"
26178
+ ];
26179
+ if (!allowedFields.includes(field)) {
26180
+ throw new BadRequestError54(
26181
+ `Field "${field}" is not allowed to be updated.`
26182
+ );
26183
+ }
26184
+ try {
26185
+ _id = new ObjectId36(_id);
26186
+ } catch (error) {
26187
+ throw new BadRequestError54("Invalid ID.");
26188
+ }
26189
+ try {
26190
+ await collection.updateOne(
26191
+ { _id, deletedAt: { $in: ["", null] } },
26192
+ { $set: { [field]: value, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } },
26193
+ { session }
26194
+ );
26195
+ delCachedData();
26196
+ return `Successfully updated division ${field}.`;
26197
+ } catch (error) {
26198
+ throw new InternalServerError24(`Failed to update division ${field}.`);
26199
+ }
26200
+ }
26201
+ async function deleteById(_id) {
26202
+ try {
26203
+ _id = new ObjectId36(_id);
26204
+ } catch (error) {
26205
+ throw new BadRequestError54("Invalid ID.");
26206
+ }
26207
+ try {
26208
+ await collection.updateOne(
26209
+ { _id },
26210
+ { $set: { deletedAt: (/* @__PURE__ */ new Date()).toISOString() } }
26211
+ );
26212
+ delCachedData();
26213
+ return "Successfully deleted division.";
26214
+ } catch (error) {
26215
+ throw new InternalServerError24("Failed to delete division.");
26216
+ }
26217
+ }
26218
+ return {
26219
+ createIndex,
26220
+ createTextIndex,
26221
+ createUniqueIndex,
26222
+ add,
26223
+ getAll,
26224
+ getById,
26225
+ updateFieldById,
26226
+ deleteById,
26227
+ getByName
26228
+ };
26229
+ }
26230
+
26231
+ // src/controllers/division.controller.ts
26232
+ import { BadRequestError as BadRequestError55 } from "@eeplatform/nodejs-utils";
26233
+ import Joi30 from "joi";
26234
+
26235
+ // src/services/division.service.ts
26236
+ import { useAtlas as useAtlas28 } from "@eeplatform/nodejs-utils";
26237
+ function useDivisionService() {
26238
+ const { add: _add } = useDivisionRepo();
26239
+ const { addRole } = useRoleRepo();
26240
+ async function add(value) {
26241
+ const session = useAtlas28.getClient()?.startSession();
26242
+ if (!session) {
26243
+ throw new Error("Unable to start session for division service.");
26244
+ }
26245
+ try {
26246
+ session.startTransaction();
26247
+ const division = await _add(value, session);
26248
+ await addRole(
26249
+ {
26250
+ id: division.toString(),
26251
+ name: "Admin",
26252
+ type: "division",
26253
+ permissions: ["*"],
26254
+ status: "active",
26255
+ default: true
26256
+ },
26257
+ session
26258
+ );
26259
+ await session.commitTransaction();
26260
+ return "Division and admin role created successfully.";
26261
+ } catch (error) {
26262
+ session.abortTransaction();
26263
+ throw error;
26264
+ } finally {
26265
+ session.endSession();
26266
+ }
26267
+ }
26268
+ return {
26269
+ add
26270
+ };
26271
+ }
26272
+
26273
+ // src/controllers/division.controller.ts
26274
+ function useDivisionController() {
26275
+ const {
26276
+ getAll: _getAll,
26277
+ getById: _getById,
26278
+ getByName: _getByName,
26279
+ updateFieldById: _updateFieldById,
26280
+ deleteById: _deleteById
26281
+ } = useDivisionRepo();
26282
+ const { add: _createDivision } = useDivisionService();
26283
+ async function createDivision(req, res, next) {
26284
+ const value = req.body;
26285
+ const validation = Joi30.object({
26286
+ name: Joi30.string().min(1).max(100).required(),
26287
+ region: Joi30.string().hex().optional().allow("", null),
26288
+ regionName: Joi30.string().min(1).max(100).optional().allow("", null),
26289
+ superintendent: Joi30.string().hex().optional().allow("", null),
26290
+ superintendentName: Joi30.string().min(1).max(100).optional().allow("", null)
26291
+ });
26292
+ const { error } = validation.validate(value);
26293
+ if (error) {
26294
+ next(new BadRequestError55(error.message));
26295
+ return;
26296
+ }
26297
+ try {
26298
+ const divisionId = await _createDivision(value);
26299
+ res.json({
26300
+ message: "Successfully created division.",
26301
+ data: { divisionId }
26302
+ });
26303
+ return;
26304
+ } catch (error2) {
26305
+ next(error2);
26306
+ }
26307
+ }
26308
+ async function getAll(req, res, next) {
26309
+ const query = req.query;
26310
+ const validation = Joi30.object({
26311
+ page: Joi30.number().min(1).optional().allow("", null),
26312
+ limit: Joi30.number().min(1).optional().allow("", null),
26313
+ search: Joi30.string().optional().allow("", null),
26314
+ region: Joi30.string().hex().optional().allow("", null)
26315
+ });
26316
+ const { error } = validation.validate(query);
26317
+ const page = typeof req.query.page === "string" ? Number(req.query.page) : 1;
26318
+ const limit = typeof req.query.limit === "string" ? Number(req.query.limit) : 10;
26319
+ const search = req.query.search ?? "";
26320
+ const region = req.query.region ?? "";
26321
+ const isPageNumber = isFinite(page);
26322
+ if (!isPageNumber) {
26323
+ next(new BadRequestError55("Invalid page number."));
26324
+ return;
26325
+ }
26326
+ const isLimitNumber = isFinite(limit);
26327
+ if (!isLimitNumber) {
26328
+ next(new BadRequestError55("Invalid limit number."));
26329
+ return;
26330
+ }
26331
+ if (error) {
26332
+ next(new BadRequestError55(error.message));
26333
+ return;
26334
+ }
26335
+ try {
26336
+ const divisions = await _getAll({ page, limit, search, region });
26337
+ res.json(divisions);
26338
+ return;
26339
+ } catch (error2) {
26340
+ next(error2);
26341
+ }
26342
+ }
26343
+ async function getById(req, res, next) {
26344
+ const id = req.params.id;
26345
+ const validation = Joi30.object({
26346
+ id: Joi30.string().hex().required()
26347
+ });
26348
+ const { error } = validation.validate({ id });
26349
+ if (error) {
26350
+ next(new BadRequestError55(error.message));
26351
+ return;
26352
+ }
26353
+ try {
26354
+ const division = await _getById(id);
26355
+ res.json({
26356
+ message: "Successfully retrieved division.",
26357
+ data: { division }
26358
+ });
26359
+ return;
26360
+ } catch (error2) {
26361
+ next(error2);
26362
+ }
26363
+ }
26364
+ async function getByName(req, res, next) {
26365
+ const name = req.params.name;
26366
+ const validation = Joi30.object({
26367
+ name: Joi30.string().required()
26368
+ });
26369
+ const { error } = validation.validate({ name });
26370
+ if (error) {
26371
+ next(new BadRequestError55(error.message));
26372
+ return;
26373
+ }
26374
+ try {
26375
+ const division = await _getByName(name);
26376
+ res.json({
26377
+ message: "Successfully retrieved division.",
26378
+ data: { division }
26379
+ });
26380
+ return;
26381
+ } catch (error2) {
26382
+ next(error2);
26383
+ }
26384
+ }
26385
+ async function updateField(req, res, next) {
26386
+ const _id = req.params.id;
26387
+ const { field, value } = req.body;
26388
+ const validation = Joi30.object({
26389
+ _id: Joi30.string().hex().required(),
26390
+ field: Joi30.string().valid(
26391
+ "name",
26392
+ "region",
26393
+ "regionName",
26394
+ "superintendent",
26395
+ "superintendentName"
26396
+ ).required(),
26397
+ value: Joi30.string().required()
26398
+ });
26399
+ const { error } = validation.validate({ _id, field, value });
26400
+ if (error) {
26401
+ next(new BadRequestError55(error.message));
26402
+ return;
26403
+ }
26404
+ try {
26405
+ const message = await _updateFieldById({ _id, field, value });
26406
+ res.json({ message });
26407
+ return;
26408
+ } catch (error2) {
26409
+ next(error2);
26410
+ }
26411
+ }
26412
+ async function deleteDivision(req, res, next) {
26413
+ const _id = req.params.id;
26414
+ const validation = Joi30.object({
26415
+ _id: Joi30.string().hex().required()
26416
+ });
26417
+ const { error } = validation.validate({ _id });
26418
+ if (error) {
26419
+ next(new BadRequestError55(error.message));
26420
+ return;
26421
+ }
26422
+ try {
26423
+ const message = await _deleteById(_id);
26424
+ res.json({ message });
26425
+ return;
26426
+ } catch (error2) {
26427
+ next(error2);
26428
+ }
26429
+ }
26430
+ return {
26431
+ createDivision,
26432
+ getAll,
26433
+ getById,
26434
+ getByName,
26435
+ updateField,
26436
+ deleteDivision
26437
+ };
26438
+ }
26439
+
26440
+ // src/models/school.model.ts
26441
+ import Joi31 from "joi";
26442
+ import { ObjectId as ObjectId37 } from "mongodb";
26443
+ var schemaSchool = Joi31.object({
26444
+ _id: Joi31.string().hex().optional().allow("", null),
26445
+ id: Joi31.string().required(),
26446
+ name: Joi31.string().required(),
26447
+ country: Joi31.string().required(),
26448
+ address: Joi31.string().required(),
26449
+ continuedAddress: Joi31.string().optional().allow("", null),
26450
+ city: Joi31.string().required(),
26451
+ province: Joi31.string().required(),
26452
+ postalCode: Joi31.string().required(),
26453
+ courses: Joi31.array().items(Joi31.string()).required(),
26454
+ principalName: Joi31.string().required(),
26455
+ principalEmail: Joi31.string().email().optional().allow("", null),
26456
+ principalNumber: Joi31.string().optional().allow("", null),
26457
+ region: Joi31.string().hex().required(),
26458
+ regionName: Joi31.string().optional().allow("", null),
26459
+ division: Joi31.string().hex().required(),
26460
+ divisionName: Joi31.string().optional().allow("", null),
26461
+ status: Joi31.string().optional().allow(null, ""),
26462
+ createdAt: Joi31.date().optional().allow("", null),
26463
+ updatedAt: Joi31.date().optional().allow("", null),
26464
+ createdBy: Joi31.string().hex().required()
26465
+ });
26466
+ function MSchool(value) {
26467
+ const { error } = schemaSchool.validate(value);
26468
+ if (error) {
26469
+ throw new Error(`Validation error: ${error.message}`);
26470
+ }
26471
+ if (value._id) {
26472
+ try {
26473
+ value._id = new ObjectId37(value._id);
26474
+ } catch (error2) {
26475
+ throw new Error("Invalid _id.");
26476
+ }
26477
+ }
26478
+ if (value.region) {
26479
+ try {
26480
+ value.region = new ObjectId37(value.region);
26481
+ } catch (error2) {
26482
+ throw new Error("Invalid region.");
26483
+ }
26484
+ }
26485
+ if (value.division) {
26486
+ try {
26487
+ value.division = new ObjectId37(value.division);
26488
+ } catch (error2) {
26489
+ throw new Error("Invalid division.");
26490
+ }
26491
+ }
26492
+ if (value.createdBy) {
26493
+ try {
26494
+ value.createdBy = new ObjectId37(value.createdBy);
26495
+ } catch (error2) {
26496
+ throw new Error("Invalid createdBy.");
26497
+ }
26498
+ }
26499
+ return {
26500
+ _id: value._id ? value._id : new ObjectId37(),
26501
+ id: value.id,
26502
+ name: value.name,
26503
+ country: value.country,
26504
+ address: value.address,
26505
+ continuedAddress: value.continuedAddress ?? "",
26506
+ city: value.city,
26507
+ province: value.province,
26508
+ postalCode: value.postalCode,
26509
+ courses: value.courses || [],
26510
+ principalName: value.principalName,
26511
+ principalEmail: value.principalEmail,
26512
+ principalNumber: value.principalNumber ?? "",
26513
+ region: value.region,
26514
+ regionName: value.regionName ?? "",
26515
+ division: value.division,
26516
+ divisionName: value.divisionName ?? "",
26517
+ createdAt: value.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
26518
+ updatedAt: value.updatedAt ?? "",
26519
+ status: value.status ?? "pending",
26520
+ createdBy: value.createdBy
26521
+ };
26522
+ }
26523
+
26524
+ // src/repositories/school.repository.ts
26525
+ import {
26526
+ BadRequestError as BadRequestError56,
26527
+ logger as logger26,
26528
+ makeCacheKey as makeCacheKey18,
26529
+ paginate as paginate14,
26530
+ useAtlas as useAtlas29,
26531
+ useCache as useCache18
26532
+ } from "@eeplatform/nodejs-utils";
26533
+ import { ObjectId as ObjectId38 } from "mongodb";
26534
+ function useSchoolRepo() {
26535
+ const db = useAtlas29.getDb();
26536
+ if (!db) {
26537
+ throw new BadRequestError56("Unable to connect to server.");
26538
+ }
26539
+ const namespace_collection = "schools";
26540
+ const collection = db.collection(namespace_collection);
26541
+ const { getCache, setCache, delNamespace } = useCache18();
26542
+ function delCachedData() {
26543
+ delNamespace(namespace_collection).then(() => {
26544
+ logger26.log({
26545
+ level: "info",
26546
+ message: `Cache namespace cleared for ${namespace_collection}`
26547
+ });
26548
+ }).catch((err) => {
26549
+ logger26.log({
26550
+ level: "error",
26551
+ message: `Failed to clear cache namespace for ${namespace_collection}: ${err.message}`
26552
+ });
26553
+ });
26554
+ }
26555
+ async function createIndex() {
26556
+ try {
26557
+ await collection.createIndexes([
26558
+ { key: { name: 1 } },
26559
+ { key: { id: 1 }, unique: true },
26560
+ { key: { region: 1 } },
26561
+ { key: { division: 1 } },
26562
+ {
26563
+ key: {
26564
+ name: "text",
26565
+ address: "text",
26566
+ continuedAddress: "text",
26567
+ city: "text",
26568
+ province: "text",
26569
+ postalCode: "text",
26570
+ regionName: "text",
26571
+ divisionName: "text"
26572
+ }
26573
+ }
26574
+ ]);
26575
+ } catch (error) {
26576
+ throw new BadRequestError56("Failed to create index on school.");
26577
+ }
26578
+ }
26579
+ async function add(value, session) {
26580
+ try {
26581
+ value = MSchool(value);
26582
+ const res = await collection.insertOne(value, { session });
26583
+ delCachedData();
26584
+ return res.insertedId;
26585
+ } catch (error) {
26586
+ logger26.log({
26587
+ level: "error",
26588
+ message: `Failed to add school: ${error}`
26589
+ });
26590
+ const isDuplicated = error.message.includes("duplicate");
26591
+ if (isDuplicated) {
26592
+ throw new BadRequestError56("School already exist.");
26593
+ }
26594
+ throw error;
26595
+ }
26596
+ }
26597
+ async function getAll({
26598
+ page = 1,
26599
+ limit = 20,
26600
+ sort = {},
26601
+ status = "active",
26602
+ org = "",
26603
+ app = "admin",
26604
+ search = ""
26605
+ } = {}) {
26606
+ page = Math.max(0, page - 1);
26607
+ if (sort && Object.keys(sort).length === 0) {
26608
+ sort = { name: 1 };
26609
+ }
26610
+ const query = { status };
26611
+ const cacheKeyOptions = {
26612
+ page,
26613
+ limit,
26614
+ sort: JSON.stringify(sort),
26615
+ status
26616
+ };
26617
+ if (search) {
26618
+ query.$text = { $search: search };
26619
+ cacheKeyOptions.search = search;
26620
+ }
26621
+ if (org) {
26622
+ try {
26623
+ query[app] = new ObjectId38(org);
26624
+ cacheKeyOptions[app] = org;
26625
+ } catch (error) {
26626
+ throw new BadRequestError56("Invalid org.");
26627
+ }
26628
+ }
26629
+ try {
26630
+ const cacheKey = makeCacheKey18(namespace_collection, cacheKeyOptions);
26631
+ const cachedData = await getCache(cacheKey);
26632
+ if (cachedData) {
26633
+ return cachedData;
26634
+ }
26635
+ const items = await collection.aggregate([
26636
+ { $match: query },
26637
+ { $sort: sort },
26638
+ { $skip: page * limit },
26639
+ { $limit: limit }
26640
+ ]).toArray();
26641
+ const length = await collection.countDocuments(query);
26642
+ const data = paginate14(items, page, limit, length);
26643
+ setCache(cacheKey, data, 600, namespace_collection).then(() => {
26644
+ logger26.log({
26645
+ level: "info",
26646
+ message: `Cache set for key ${cacheKey}`
26647
+ });
26648
+ }).catch((err) => {
26649
+ logger26.log({
26650
+ level: "error",
26651
+ message: `Failed to set cache for key ${cacheKey}: ${err.message}`
26652
+ });
26653
+ });
26654
+ return data;
26655
+ } catch (error) {
26656
+ logger26.log({
26657
+ level: "error",
26658
+ message: `Failed to get all schools: ${error}`
26659
+ });
26660
+ throw error;
26661
+ }
26662
+ }
26663
+ async function updateStatusById(_id, status, session) {
26664
+ try {
26665
+ _id = new ObjectId38(_id);
26666
+ } catch (error) {
26667
+ throw new BadRequestError56("Invalid school ID.");
26668
+ }
26669
+ const result = await collection.updateOne(
26670
+ { _id },
26671
+ { $set: { status, updatedAt: /* @__PURE__ */ new Date() } },
26672
+ { session }
26673
+ );
26674
+ delCachedData();
26675
+ return result;
26676
+ }
26677
+ async function updateFieldById({ _id, field, value } = {}, session) {
26678
+ const allowedFields = [
26679
+ "name",
26680
+ "country",
26681
+ "address",
26682
+ "continuedAddress",
26683
+ "city",
26684
+ "province",
26685
+ "postalCode",
26686
+ "courses",
26687
+ "email",
26688
+ "principalName",
26689
+ "principalEmail",
26690
+ "principalNumber",
26691
+ "region",
26692
+ "regionName",
26693
+ "division",
26694
+ "divisionName"
26695
+ ];
26696
+ if (!allowedFields.includes(field)) {
26697
+ throw new BadRequestError56(
26698
+ `Field "${field}" is not allowed to be updated.`
26699
+ );
26700
+ }
26701
+ try {
26702
+ _id = new ObjectId38(_id);
26703
+ } catch (error) {
26704
+ throw new BadRequestError56("Invalid ID.");
26705
+ }
26706
+ try {
26707
+ const result = await collection.updateOne(
26708
+ { _id },
26709
+ { $set: { [field]: value } },
26710
+ { session }
26711
+ );
26712
+ delCachedData();
26713
+ return result;
26714
+ } catch (error) {
26715
+ throw new BadRequestError56(`Failed to update school ${field}.`);
26716
+ }
26717
+ }
26718
+ async function getPendingByCreatedBy(createdBy) {
26719
+ try {
26720
+ createdBy = new ObjectId38(createdBy);
26721
+ } catch (error) {
26722
+ throw new BadRequestError56("Invalid createdBy ID.");
26723
+ }
26724
+ const cacheKey = makeCacheKey18(namespace_collection, {
26725
+ createdBy,
26726
+ status: "pending"
26727
+ });
26728
+ const cachedData = await getCache(cacheKey);
26729
+ if (cachedData) {
26730
+ return cachedData;
26731
+ }
26732
+ try {
26733
+ const school = await collection.findOne({ createdBy, status: "pending" });
26734
+ setCache(cacheKey, school, 600, namespace_collection).then(() => {
26735
+ logger26.log({
26736
+ level: "info",
26737
+ message: `Cache set for school by createdBy: ${cacheKey}`
26738
+ });
26739
+ }).catch((err) => {
26740
+ logger26.log({
26741
+ level: "error",
26742
+ message: `Failed to set cache for school by createdBy: ${err.message}`
26743
+ });
26744
+ });
26745
+ return school;
26746
+ } catch (error) {
26747
+ throw error;
26748
+ }
26749
+ }
26750
+ async function getPendingById(_id) {
26751
+ try {
26752
+ _id = new ObjectId38(_id);
26753
+ } catch (error) {
26754
+ throw new BadRequestError56("Invalid ID.");
26755
+ }
26756
+ const cacheKey = makeCacheKey18(namespace_collection, {
26757
+ _id,
26758
+ status: "pending"
26759
+ });
26760
+ const cachedData = await getCache(cacheKey);
26761
+ if (cachedData) {
26762
+ return cachedData;
26763
+ }
26764
+ try {
26765
+ const school = await collection.findOne({ _id, status: "pending" });
26766
+ setCache(cacheKey, school, 600, namespace_collection).then(() => {
26767
+ logger26.log({
26768
+ level: "info",
26769
+ message: `Cache set for school by ID: ${cacheKey}`
26770
+ });
26771
+ }).catch((err) => {
26772
+ logger26.log({
26773
+ level: "error",
26774
+ message: `Failed to set cache for school by ID: ${err.message}`
26775
+ });
26776
+ });
26777
+ return school;
26778
+ } catch (error) {
26779
+ throw error;
26780
+ }
26781
+ }
26782
+ return {
26783
+ createIndex,
26784
+ add,
26785
+ getAll,
26786
+ updateStatusById,
26787
+ updateFieldById,
26788
+ getPendingByCreatedBy,
26789
+ getPendingById
26790
+ };
26791
+ }
26792
+
26793
+ // src/services/school.service.ts
26794
+ import { BadRequestError as BadRequestError57, useAtlas as useAtlas30, logger as logger27 } from "@eeplatform/nodejs-utils";
26795
+ function useSchoolService() {
26796
+ const { add, getPendingByCreatedBy, updateStatusById, getPendingById } = useSchoolRepo();
26797
+ const { addRole } = useRoleRepo();
26798
+ const { getUserById } = useUserRepo();
26799
+ const { add: addMember } = useMemberRepo();
26800
+ async function register(value) {
26801
+ const { error } = schemaSchool.validate(value);
26802
+ if (error) {
26803
+ throw new BadRequestError57(error.message);
26804
+ }
26805
+ const existingSchool = await getPendingByCreatedBy(value.createdBy ?? "");
26806
+ if (existingSchool) {
26807
+ throw new BadRequestError57(
26808
+ "You already have a pending school registration."
26809
+ );
26810
+ }
26811
+ try {
26812
+ value.status = "pending";
26813
+ await add(value);
26814
+ return "Request to register school has been sent successfully. Please wait for approval.";
26815
+ } catch (error2) {
26816
+ throw error2;
26817
+ }
26818
+ }
26819
+ async function approve(id) {
26820
+ const school = await getPendingById(id);
26821
+ if (!school) {
26822
+ throw new BadRequestError57("School registration not found.");
26823
+ }
26824
+ const session = useAtlas30.getClient()?.startSession();
26825
+ if (!session) {
26826
+ throw new Error("Unable to start session for school service.");
26827
+ }
26828
+ try {
26829
+ session.startTransaction();
26830
+ school.status = "approved";
26831
+ await updateStatusById(id, "active", session);
26832
+ const roleType = "school";
26833
+ const roleName = "Admin";
26834
+ const roleId = await addRole(
26835
+ {
26836
+ id,
26837
+ type: roleType,
26838
+ name: roleName,
26839
+ permissions: ["*"],
26840
+ status: "active",
26841
+ default: true
26842
+ },
26843
+ session
26844
+ );
26845
+ if (!school.createdBy) {
26846
+ throw new BadRequestError57("School must have a creator.");
26847
+ }
26848
+ const user = await getUserById(school.createdBy ?? "");
26849
+ if (!user) {
26850
+ throw new BadRequestError57("User not found for the school creator.");
26851
+ }
26852
+ await addMember(
26853
+ {
26854
+ org: id,
26855
+ orgName: school.name,
26856
+ user: school.createdBy.toString(),
26857
+ name: `${user.firstName} ${user.lastName}`,
26858
+ role: roleId.toString(),
26859
+ roleName,
26860
+ type: roleType
26861
+ },
26862
+ session
26863
+ );
26864
+ await session.commitTransaction();
26865
+ return "School registration has been approved.";
26866
+ } catch (error) {
26867
+ logger27.log({
26868
+ level: "error",
26869
+ message: `Error approving school registration: ${error.message}`
26870
+ });
26871
+ await session.abortTransaction();
26872
+ throw error;
26873
+ } finally {
26874
+ await session.endSession();
26875
+ }
26876
+ }
26877
+ return {
26878
+ register,
26879
+ approve
26880
+ };
26881
+ }
26882
+
26883
+ // src/controllers/school.controller.ts
26884
+ import { BadRequestError as BadRequestError58 } from "@eeplatform/nodejs-utils";
26885
+ import Joi32 from "joi";
26886
+ function useSchoolController() {
26887
+ const {
26888
+ add: _add,
26889
+ getAll: _getAll,
26890
+ getPendingByCreatedBy: _getPendingByCreatedBy,
26891
+ updateStatusById: _updateStatusById
26892
+ } = useSchoolRepo();
26893
+ async function add(req, res, next) {
26894
+ const payload = req.body;
26895
+ const { error } = schemaSchool.validate(payload);
26896
+ if (error) {
26897
+ next(new BadRequestError58(`Validation error: ${error.message}`));
26898
+ return;
26899
+ }
26900
+ try {
26901
+ const school = await _add(payload);
26902
+ res.status(201).json(school);
26903
+ return;
26904
+ } catch (error2) {
26905
+ next(error2);
26906
+ return;
26907
+ }
26908
+ }
26909
+ async function getAll(req, res, next) {
26910
+ const validation = Joi32.object({
26911
+ page: Joi32.number().optional().allow(null, ""),
26912
+ limit: Joi32.number().optional().allow(null, ""),
26913
+ sort: Joi32.string().optional().allow(null, ""),
26914
+ sortOrder: Joi32.string().optional().allow(null, ""),
26915
+ status: Joi32.string().optional().allow(null, ""),
26916
+ org: Joi32.string().hex().optional().allow(null, ""),
26917
+ app: Joi32.string().optional().allow(null, ""),
26918
+ search: Joi32.string().optional().allow(null, "")
26919
+ });
26920
+ const { error } = validation.validate(req.query);
26921
+ if (error) {
26922
+ next(new BadRequestError58(`Validation error: ${error.message}`));
26923
+ return;
26924
+ }
26925
+ const page = parseInt(req.query.page) ?? 1;
26926
+ let limit = parseInt(req.query.limit) ?? 20;
26927
+ limit = isNaN(limit) ? 20 : limit;
26928
+ const sort = req.query.sort ? String(req.query.sort).split(",") : "";
26929
+ const sortOrder = req.query.sortOrder ? String(req.query.sortOrder).split(",") : "";
26930
+ const sortObj = {};
26931
+ if (sort && Array.isArray(sort) && sort.length && sortOrder && Array.isArray(sortOrder) && sortOrder.length) {
26932
+ sort.forEach((field, index) => {
26933
+ sortObj[field] = sortOrder[index] === "desc" ? -1 : 1;
26934
+ });
26935
+ }
26936
+ const status = req.query.status ?? "active";
26937
+ const org = req.query.org ?? "";
26938
+ const app = req.query.app ?? "admin";
26939
+ const search = req.query.search ?? "";
26940
+ try {
26941
+ const schools = await _getAll({
26942
+ page,
26943
+ limit,
26944
+ sort: sortObj,
26945
+ status,
26946
+ org,
26947
+ app,
26948
+ search
26949
+ });
26950
+ res.status(200).json(schools);
26951
+ return;
26952
+ } catch (error2) {
26953
+ next(error2);
26954
+ }
26955
+ }
26956
+ async function getByCreatedBy(req, res, next) {
26957
+ const createdBy = req.params.createdBy;
26958
+ const validation = Joi32.string().hex().required();
26959
+ const { error } = validation.validate(createdBy);
26960
+ if (error) {
26961
+ next(new BadRequestError58(`Validation error: ${error.message}`));
26962
+ return;
26963
+ }
26964
+ try {
26965
+ const school = await _getPendingByCreatedBy(createdBy);
26966
+ res.status(200).json(school);
26967
+ return;
26968
+ } catch (error2) {
26969
+ next(error2);
26970
+ }
26971
+ }
26972
+ async function updateStatusById(req, res, next) {
26973
+ const schoolId = req.params.id;
26974
+ const status = req.params.status;
26975
+ const validation = Joi32.object({
26976
+ id: Joi32.string().hex().required(),
26977
+ status: Joi32.string().valid("active", "deleted", "suspended").required()
26978
+ });
26979
+ const { error } = validation.validate({ id: schoolId, status });
26980
+ if (error) {
26981
+ next(new BadRequestError58(`Validation error: ${error.message}`));
26982
+ return;
26983
+ }
26984
+ try {
26985
+ const updatedSchool = await _updateStatusById(schoolId, status);
26986
+ res.status(200).json(updatedSchool);
26987
+ return;
26988
+ } catch (error2) {
26989
+ next(error2);
26990
+ }
26991
+ }
26992
+ const { register: _registerSchool, approve } = useSchoolService();
26993
+ async function registerSchool(req, res, next) {
26994
+ const payload = req.body;
26995
+ const { error } = schemaSchool.validate(payload);
26996
+ if (error) {
26997
+ next(new BadRequestError58(`Validation error: ${error.message}`));
26998
+ return;
26999
+ }
27000
+ try {
27001
+ const schoolId = await _registerSchool(payload);
27002
+ res.status(201).json({ schoolId });
27003
+ return;
27004
+ } catch (error2) {
27005
+ next(error2);
27006
+ return;
27007
+ }
27008
+ }
27009
+ async function approveSchool(req, res, next) {
27010
+ const schoolId = req.params.id;
27011
+ const validation = Joi32.object({
27012
+ id: Joi32.string().hex().required()
27013
+ });
27014
+ const { error } = validation.validate({ id: schoolId });
27015
+ if (error) {
27016
+ next(new BadRequestError58(`Validation error: ${error.message}`));
27017
+ return;
27018
+ }
27019
+ try {
27020
+ const updatedSchool = await approve(schoolId);
27021
+ res.status(200).json(updatedSchool);
27022
+ return;
27023
+ } catch (error2) {
27024
+ next(error2);
27025
+ }
27026
+ }
27027
+ return {
27028
+ add,
27029
+ getAll,
27030
+ getByCreatedBy,
27031
+ updateStatusById,
27032
+ registerSchool,
27033
+ approveSchool
27034
+ };
27035
+ }
25338
27036
  export {
25339
27037
  ACCESS_TOKEN_EXPIRY,
25340
27038
  ACCESS_TOKEN_SECRET,
@@ -25353,6 +27051,7 @@ export {
25353
27051
  MAILER_TRANSPORT_PORT,
25354
27052
  MAILER_TRANSPORT_SECURE,
25355
27053
  MAddress,
27054
+ MDivision,
25356
27055
  MEntity,
25357
27056
  MFile,
25358
27057
  MMember,
@@ -25362,7 +27061,9 @@ export {
25362
27061
  MOrg,
25363
27062
  MPaymentMethod,
25364
27063
  MPromoCode,
27064
+ MRegion,
25365
27065
  MRole,
27066
+ MSchool,
25366
27067
  MSubscription,
25367
27068
  MToken,
25368
27069
  MUser,
@@ -25390,12 +27091,17 @@ export {
25390
27091
  addressSchema,
25391
27092
  isDev,
25392
27093
  schema,
27094
+ schemaDivision,
27095
+ schemaRegion,
27096
+ schemaSchool,
25393
27097
  useAddressController,
25394
27098
  useAddressRepo,
25395
27099
  useAuthController,
25396
27100
  useAuthService,
25397
27101
  useCounterModel,
25398
27102
  useCounterRepo,
27103
+ useDivisionController,
27104
+ useDivisionRepo,
25399
27105
  useEntityController,
25400
27106
  useEntityRepo,
25401
27107
  useFileController,
@@ -25424,8 +27130,13 @@ export {
25424
27130
  usePriceRepo,
25425
27131
  usePromoCodeController,
25426
27132
  usePromoCodeRepo,
27133
+ useRegionController,
27134
+ useRegionRepo,
25427
27135
  useRoleController,
25428
27136
  useRoleRepo,
27137
+ useSchoolController,
27138
+ useSchoolRepo,
27139
+ useSchoolService,
25429
27140
  useSubscriptionController,
25430
27141
  useSubscriptionRepo,
25431
27142
  useSubscriptionService,