prow-authority 3.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/.claude/settings.local.json +11 -0
  2. package/.dockerignore +11 -0
  3. package/.github/workflows/_deploy.yml +191 -0
  4. package/.github/workflows/deploy-dev.yml +30 -0
  5. package/.github/workflows/deploy-prod.yml +34 -0
  6. package/CLAUDE.md +114 -0
  7. package/Dockerfile +61 -0
  8. package/Dockerfile.dev +24 -0
  9. package/README.md +1 -0
  10. package/package.json +61 -0
  11. package/src/App.ts +40 -0
  12. package/src/helpers/api-error.ts +21 -0
  13. package/src/helpers/authority.ts +216 -0
  14. package/src/helpers/config.ts +4 -0
  15. package/src/helpers/holded.ts +105 -0
  16. package/src/helpers/mysql.ts +15 -0
  17. package/src/helpers/notifications.ts +55 -0
  18. package/src/helpers/random.ts +18 -0
  19. package/src/helpers/s3.ts +43 -0
  20. package/src/helpers/stripe.ts +8 -0
  21. package/src/index.ts +47 -0
  22. package/src/v1/common-types.ts +12 -0
  23. package/src/v1/controllers/admin/accounting-seat-controller.ts +148 -0
  24. package/src/v1/controllers/admin/billing-cycle-controller.ts +171 -0
  25. package/src/v1/controllers/admin/invoice-controller.ts +222 -0
  26. package/src/v1/controllers/admin/license-controller.ts +190 -0
  27. package/src/v1/controllers/admin/license-limit-controller.ts +160 -0
  28. package/src/v1/controllers/admin/notification-controller.ts +115 -0
  29. package/src/v1/controllers/admin/organization-controller.ts +210 -0
  30. package/src/v1/controllers/admin/organization-license-controller.ts +224 -0
  31. package/src/v1/controllers/admin/organization-license-limit-controller.ts +196 -0
  32. package/src/v1/controllers/admin/organization-usage-controller.ts +167 -0
  33. package/src/v1/controllers/admin/payment-method-controller.ts +173 -0
  34. package/src/v1/controllers/admin/policy-controller.ts +138 -0
  35. package/src/v1/controllers/admin/policy-version-controller.ts +143 -0
  36. package/src/v1/controllers/admin/schedule-controller.ts +181 -0
  37. package/src/v1/controllers/admin/service-controller.ts +206 -0
  38. package/src/v1/controllers/admin/service-notification-controller.ts +166 -0
  39. package/src/v1/controllers/admin/user-controller.ts +282 -0
  40. package/src/v1/controllers/admin/user-organization-controller.ts +172 -0
  41. package/src/v1/controllers/admin/user-policy-controller.ts +164 -0
  42. package/src/v1/controllers/auth/@deprecated oauth-controller.ts +191 -0
  43. package/src/v1/controllers/auth/identity-token-controller.ts +286 -0
  44. package/src/v1/controllers/auth/organization-token-controller.ts +277 -0
  45. package/src/v1/controllers/auth/temp-code-controller.ts +60 -0
  46. package/src/v1/controllers/billing/billing-controller.ts +805 -0
  47. package/src/v1/controllers/organizations/public-organization-controller.ts +1796 -0
  48. package/src/v1/controllers/policies/public-policy-controller.ts +68 -0
  49. package/src/v1/controllers/services/public-service-controller.ts +177 -0
  50. package/src/v1/controllers/services/service-controller.ts +474 -0
  51. package/src/v1/controllers/users/public-user-controller.ts +471 -0
  52. package/src/v1/daos/@deprecated auth-dao.ts +173 -0
  53. package/src/v1/daos/accounting-seat-dao.ts +111 -0
  54. package/src/v1/daos/billing-cycle-dao.ts +107 -0
  55. package/src/v1/daos/invoice-dao.ts +131 -0
  56. package/src/v1/daos/license-dao.ts +152 -0
  57. package/src/v1/daos/license-limit-dao.ts +120 -0
  58. package/src/v1/daos/organization-dao.ts +109 -0
  59. package/src/v1/daos/organization-license-dao.ts +117 -0
  60. package/src/v1/daos/organization-license-limit-dao.ts +157 -0
  61. package/src/v1/daos/organization-usage-dao.ts +115 -0
  62. package/src/v1/daos/payment-method-dao.ts +110 -0
  63. package/src/v1/daos/policy-dao.ts +105 -0
  64. package/src/v1/daos/policy-version-dao.ts +112 -0
  65. package/src/v1/daos/redis-dao.ts +38 -0
  66. package/src/v1/daos/schema.sql +462 -0
  67. package/src/v1/daos/service-dao.ts +118 -0
  68. package/src/v1/daos/service-notification-dao.ts +120 -0
  69. package/src/v1/daos/user-dao.ts +127 -0
  70. package/src/v1/daos/user-organization-dao.ts +123 -0
  71. package/src/v1/daos/user-policy-dao.ts +109 -0
  72. package/src/v1/middlewares/auth/identity-token-middleware.ts +51 -0
  73. package/src/v1/middlewares/auth/organization-token-middleware.ts +73 -0
  74. package/src/v1/middlewares/auth/service-token-middleware.ts +167 -0
  75. package/src/v1/middlewares/authority-middleware.ts +97 -0
  76. package/src/v1/middlewares/organizations/organization-middleware.ts +94 -0
  77. package/src/v1/middlewares/static/static-middleware.ts +41 -0
  78. package/src/v1/middlewares/users/user-middleware.ts +41 -0
  79. package/src/v1/routes/admin/accounting-seat-router.ts +89 -0
  80. package/src/v1/routes/admin/admin-router.ts +61 -0
  81. package/src/v1/routes/admin/billing-cycle-router.ts +89 -0
  82. package/src/v1/routes/admin/invoice-router.ts +89 -0
  83. package/src/v1/routes/admin/license-limit-router.ts +89 -0
  84. package/src/v1/routes/admin/license-router.ts +89 -0
  85. package/src/v1/routes/admin/notification-router.ts +61 -0
  86. package/src/v1/routes/admin/organization-license-limit-router.ts +89 -0
  87. package/src/v1/routes/admin/organization-license-router.ts +89 -0
  88. package/src/v1/routes/admin/organization-router.ts +89 -0
  89. package/src/v1/routes/admin/payment-method-router.ts +89 -0
  90. package/src/v1/routes/admin/policy-router.ts +89 -0
  91. package/src/v1/routes/admin/policy-version-router.ts +89 -0
  92. package/src/v1/routes/admin/schedule-router.ts +75 -0
  93. package/src/v1/routes/admin/service-notification-router.ts +98 -0
  94. package/src/v1/routes/admin/service-router.ts +89 -0
  95. package/src/v1/routes/admin/user-organization-router.ts +89 -0
  96. package/src/v1/routes/admin/user-policy-router.ts +89 -0
  97. package/src/v1/routes/admin/user-router.ts +89 -0
  98. package/src/v1/routes/auth/@deprecated auth-router.ts +199 -0
  99. package/src/v1/routes/auth/identity-token-router.ts +147 -0
  100. package/src/v1/routes/auth/organization-token-router.ts +70 -0
  101. package/src/v1/routes/billing/billing-router.ts +227 -0
  102. package/src/v1/routes/organizations/invoices-router.ts +48 -0
  103. package/src/v1/routes/organizations/payments-router.ts +104 -0
  104. package/src/v1/routes/organizations/public-organization-router.ts +319 -0
  105. package/src/v1/routes/organizations/users-router.ts +139 -0
  106. package/src/v1/routes/policies/public-policy-router.ts +50 -0
  107. package/src/v1/routes/services/public-service-router.ts +89 -0
  108. package/src/v1/routes/services/service-router.ts +197 -0
  109. package/src/v1/routes/users/public-user-router.ts +230 -0
  110. package/src/v1/routes/v1-router.ts +50 -0
  111. package/src/v1/schemas/admin/accounting-seat-schemas.ts +22 -0
  112. package/src/v1/schemas/admin/billing-cycle-schemas.ts +20 -0
  113. package/src/v1/schemas/admin/invoice-schemas.ts +41 -0
  114. package/src/v1/schemas/admin/license-limit-schemas.ts +25 -0
  115. package/src/v1/schemas/admin/license-schemas.ts +30 -0
  116. package/src/v1/schemas/admin/organization-license-limit-schemas.ts +29 -0
  117. package/src/v1/schemas/admin/organization-license-schemas.ts +43 -0
  118. package/src/v1/schemas/admin/organization-schemas.ts +67 -0
  119. package/src/v1/schemas/admin/organization-usage-schemas.ts +15 -0
  120. package/src/v1/schemas/admin/payment-method-schemas.ts +40 -0
  121. package/src/v1/schemas/admin/policy-schemas.ts +31 -0
  122. package/src/v1/schemas/admin/service-notification-schemas.ts +23 -0
  123. package/src/v1/schemas/admin/service-schemas.ts +56 -0
  124. package/src/v1/schemas/admin/user-organization-schemas.ts +24 -0
  125. package/src/v1/schemas/admin/user-policy-schemas.ts +17 -0
  126. package/src/v1/schemas/admin/user-schemas.ts +52 -0
  127. package/src/v1/schemas/auth/auth-schemas.ts +55 -0
  128. package/src/v1/schemas/billing/billing-schemas.ts +32 -0
  129. package/src/v1/schemas/organizations/public-organization-schemas.ts +77 -0
  130. package/src/v1/schemas/payments/payment-method-schemas.ts +0 -0
  131. package/src/v1/schemas/services/public-service-schemas.ts +5 -0
  132. package/src/v1/schemas/services/service-schemas.ts +27 -0
  133. package/src/v1/schemas/users/public-user-schemas.ts +73 -0
  134. package/src/v1/types/admin/accounting-seat-types.ts +16 -0
  135. package/src/v1/types/admin/billing-cycle-types.ts +14 -0
  136. package/src/v1/types/admin/invoice-types.ts +39 -0
  137. package/src/v1/types/admin/license-limit-types.ts +16 -0
  138. package/src/v1/types/admin/license-types.ts +29 -0
  139. package/src/v1/types/admin/organization-license-limit-types.ts +13 -0
  140. package/src/v1/types/admin/organization-license-types.ts +21 -0
  141. package/src/v1/types/admin/organization-types.ts +60 -0
  142. package/src/v1/types/admin/organization-usage-types.ts +17 -0
  143. package/src/v1/types/admin/payment-method-types.ts +30 -0
  144. package/src/v1/types/admin/policy-types.ts +9 -0
  145. package/src/v1/types/admin/policy-version-types.ts +12 -0
  146. package/src/v1/types/admin/service-notification-types.ts +20 -0
  147. package/src/v1/types/admin/service-types.ts +36 -0
  148. package/src/v1/types/admin/user-organization-types.ts +31 -0
  149. package/src/v1/types/admin/user-types.ts +44 -0
  150. package/src/v1/types/auth/@deprecated auth-types.ts +26 -0
  151. package/src/v1/types/auth/common-types.ts +7 -0
  152. package/src/v1/types/auth/confirmation-types.ts +5 -0
  153. package/src/v1/types/auth/identity-token-types.ts +8 -0
  154. package/src/v1/types/auth/organization-token-types.ts +23 -0
  155. package/src/v1/types/auth/otp-types.ts +12 -0
  156. package/src/v1/types/billing/billing-types.ts +37 -0
  157. package/src/v1/types/billing/ue-types.ts +29 -0
  158. package/src/v1/types/organizations/public-organization-types.ts +76 -0
  159. package/src/v1/types/policies/public-policy-types.ts +7 -0
  160. package/src/v1/types/services/public-service-types.ts +20 -0
  161. package/src/v1/types/services/service-types.ts +43 -0
  162. package/src/v1/types/users/public-user-types.ts +15 -0
  163. package/src/v1/types/users/user-policy-types.ts +11 -0
  164. package/static/error.html +55 -0
  165. package/static/grant.html +117 -0
  166. package/static/login.html +54 -0
  167. package/static/oauth-client-logo.webp +0 -0
  168. package/tsconfig.json +20 -0
@@ -0,0 +1,160 @@
1
+ import { JSON2 } from "@beseif-solutions/utility-functions/dist/utils/json-utils";
2
+ import { Where } from "@beseif-solutions/utility-functions/dist/utils/sql-utils";
3
+ import { DeepPartial } from "@beseif-solutions/utility-functions/dist/utils/typing-utils";
4
+ import { Validator } from "@beseif-solutions/utility-functions/dist/utils/validation-utils";
5
+ import APIError, { StatusCodes } from "../../../helpers/api-error";
6
+ import licenseLimitDao from "../../daos/license-limit-dao";
7
+ import { addLicenseLimitSchema, updateLicenseLimitSchema } from "../../schemas/admin/license-limit-schemas";
8
+ import { DBRecord, LicenseLimit } from "../../types/admin/license-limit-types";
9
+ import licenseController from "./license-controller";
10
+ import { Modifiers } from "../../common-types";
11
+
12
+ type Add = Omit<LicenseLimit, `id`>;
13
+ type Update = DeepPartial<Omit<LicenseLimit, `id`>>;
14
+
15
+ class LicenseLimitCtrl {
16
+
17
+ public readonly get = async (id: number): Promise<LicenseLimit[]> => {
18
+ try {
19
+ const db = await licenseLimitDao.get({ id: id });
20
+ const response = await this.parse(db);
21
+ return response;
22
+ } catch (e) { throw e; }
23
+ };
24
+
25
+ public readonly list = async (params: Where<LicenseLimit>, options: Modifiers = {}): Promise<LicenseLimit[]> => {
26
+ try {
27
+ const db = await licenseLimitDao.list({ ...params }, options);
28
+ const response = await this.parse(db);
29
+ return response;
30
+ } catch (e) {
31
+ throw e;
32
+ }
33
+ };
34
+
35
+ public readonly add = async (data: Add): Promise<LicenseLimit[]> => {
36
+ try {
37
+ await Validator.validateAsync(addLicenseLimitSchema, data);
38
+ await this.check(data);
39
+
40
+ const { insertId } = await licenseLimitDao.add({
41
+ license: data.license,
42
+ name: data.name,
43
+ limit: data.limit,
44
+ period: data.period,
45
+ monthly_price_abs: data.monthly_price_abs,
46
+ annual_price_abs: data.annual_price_abs,
47
+ overconsume_monthly_price_per_unit: data.overconsume_monthly_price_per_unit,
48
+ overconsume_annual_price_per_unit: data.overconsume_annual_price_per_unit,
49
+ metadata: data.metadata,
50
+ });
51
+
52
+ return await this.get(insertId);
53
+ } catch (e) { throw e; }
54
+ };
55
+
56
+ public readonly update = async (id: number, data: Update): Promise<LicenseLimit[]> => {
57
+ try {
58
+ await Validator.validateAsync(updateLicenseLimitSchema, data);
59
+
60
+ const old = (await this.get(id))[0];
61
+ if (!old) {
62
+ throw new APIError({
63
+ code: StatusCodes.NOT_FOUND,
64
+ detail: `LicenseLimit with id ${id} not found.`,
65
+ });
66
+ }
67
+
68
+ const merged = JSON2.deepClean(JSON2.deepMerge({}, old, data)) as LicenseLimit;
69
+
70
+ await this.check(merged, old);
71
+
72
+ const { affectedRows } = await licenseLimitDao.update({ id: id }, {
73
+ license: merged.license,
74
+ name: merged.name,
75
+ limit: merged.limit,
76
+ period: merged.period,
77
+ monthly_price_abs: merged.monthly_price_abs,
78
+ annual_price_abs: merged.annual_price_abs,
79
+ overconsume_monthly_price_per_unit: merged.overconsume_monthly_price_per_unit,
80
+ overconsume_annual_price_per_unit: merged.overconsume_annual_price_per_unit,
81
+ metadata: merged.metadata,
82
+ });
83
+
84
+ if (affectedRows <= 0) {
85
+ throw new APIError({
86
+ code: StatusCodes.INTERNAL_SERVER_ERROR,
87
+ detail: `Could not update license with id ${id}`,
88
+ });
89
+ }
90
+
91
+ return await this.get(id);
92
+ } catch (e) { throw e; }
93
+
94
+ };
95
+
96
+ public readonly delete = async (id: number): Promise<{ deleted: boolean }> => {
97
+ try {
98
+ const license = (await this.get(id))[0];
99
+ if (!license) {
100
+ throw new APIError({
101
+ code: StatusCodes.NOT_FOUND,
102
+ detail: `LicenseLimit with id ${id} not found.`,
103
+ });
104
+ }
105
+
106
+ const { affectedRows } = await licenseLimitDao.delete({ id: license.id });
107
+
108
+ if (affectedRows <= 0) {
109
+ throw new APIError({
110
+ code: StatusCodes.INTERNAL_SERVER_ERROR,
111
+ detail: `Could not delete license with id ${id}`,
112
+ });
113
+ }
114
+
115
+ return { deleted: affectedRows > 0 };
116
+ } catch (e) { throw e; }
117
+ };
118
+
119
+ private readonly check = async (data: Add | LicenseLimit, old?: LicenseLimit): Promise<void> => {
120
+ try {
121
+ if (!old || data.license !== old.license) {
122
+ const license = (await licenseController.list({ id: data.license, enabled: true }))[0];
123
+ if (!license) {
124
+ throw new APIError({
125
+ code: StatusCodes.BAD_REQUEST,
126
+ detail: `No enabled license ${data.license} found.`,
127
+ });
128
+ }
129
+ }
130
+ } catch (e) { throw e; }
131
+ };
132
+
133
+ private readonly parse = async (records: DBRecord[]): Promise<LicenseLimit[]> => {
134
+ try {
135
+ const response: LicenseLimit[] = [];
136
+
137
+ for (const record of records) {
138
+ const p: LicenseLimit = {
139
+ id: record.id,
140
+ license: record.license,
141
+ name: record.name,
142
+ limit: record.limit,
143
+ period: record.period,
144
+ monthly_price_abs: parseFloat(record.monthly_price_abs as any),
145
+ annual_price_abs: parseFloat(record.annual_price_abs as any),
146
+ overconsume_monthly_price_per_unit: record.overconsume_monthly_price_per_unit ? parseFloat(record.overconsume_monthly_price_per_unit as any) : null,
147
+ overconsume_annual_price_per_unit: record.overconsume_annual_price_per_unit ? parseFloat(record.overconsume_annual_price_per_unit as any) : null,
148
+ metadata: record.metadata,
149
+ };
150
+
151
+ response.push(p);
152
+ }
153
+
154
+ return response;
155
+ } catch (e) { throw e; }
156
+ };
157
+ }
158
+
159
+ const licenseLimitController = new LicenseLimitCtrl();
160
+ export default licenseLimitController;
@@ -0,0 +1,115 @@
1
+ import APIError, { StatusCodes } from "../../../helpers/api-error";
2
+ import { NotificationEventNames } from "../../../helpers/authority";
3
+ import nconf from "../../../helpers/config";
4
+ import notifications from "../../../helpers/notifications";
5
+ import { Organization } from "../../types/admin/organization-types";
6
+ import { UserOrganization } from "../../types/admin/user-organization-types";
7
+ import { User, UserStatus } from "../../types/admin/user-types";
8
+ import { TempCodeType } from "../../types/auth/otp-types";
9
+ import tempCodeController from "../auth/temp-code-controller";
10
+ import userController from "./user-controller";
11
+
12
+ class NotificationController {
13
+
14
+ public readonly confirmation = async (id: User[`id`]): Promise<{ done: true }> => {
15
+ try {
16
+ const user = (await userController.get(id, { password: false }))[0];
17
+ if (!user) { throw new Error(`No user found with id ${id}`); }
18
+
19
+ if (user.status !== UserStatus.Pending) {
20
+ throw new APIError({
21
+ code: StatusCodes.BAD_REQUEST,
22
+ detail: `User is already confirmed.`,
23
+ });
24
+ }
25
+
26
+ const confirmation = await tempCodeController.generate(TempCodeType.Confirmation, user);
27
+
28
+ await notifications.gmail({
29
+ template: nconf.get(`SERVICES:NOTIFICATIONS:GMAIL:TEMPLATES:CONFIRMATION`),
30
+ locale: user.preferences.language,
31
+ to: [user.email],
32
+ arguments: confirmation,
33
+ });
34
+
35
+ return { done: true };
36
+ } catch (e) { throw e; }
37
+ };
38
+
39
+ public readonly password = async (id: User[`id`]): Promise<{ done: true }> => {
40
+ try {
41
+ const user = (await userController.get(id, { password: false }))[0];
42
+ if (!user) { throw new Error(`No user found with id ${id}`); }
43
+
44
+ if (user.status === UserStatus.Blocked) {
45
+ throw new APIError({
46
+ code: StatusCodes.FORBIDDEN,
47
+ detail: `User is blocked.`,
48
+ });
49
+ }
50
+
51
+ const password = await tempCodeController.generate(TempCodeType.Password, user);
52
+
53
+ await notifications.gmail({
54
+ template: nconf.get(`SERVICES:NOTIFICATIONS:GMAIL:TEMPLATES:PASSWORD`),
55
+ locale: user.preferences.language,
56
+ to: [user.email],
57
+ arguments: password,
58
+ });
59
+
60
+ return { done: true };
61
+ } catch (e) { throw e; }
62
+ };
63
+
64
+ public readonly otp = async (id: User[`id`]): Promise<{ done: true }> => {
65
+ try {
66
+ const user = (await userController.get(id, { password: false }))[0];
67
+ if (!user) { throw new Error(`No user found with id ${id}`); }
68
+
69
+ if (user.status === UserStatus.Blocked) {
70
+ throw new APIError({
71
+ code: StatusCodes.FORBIDDEN,
72
+ detail: `User is blocked.`,
73
+ });
74
+ }
75
+
76
+ const otp = await tempCodeController.generate(TempCodeType.OTP, user);
77
+
78
+ await notifications.gmail({
79
+ template: nconf.get(`SERVICES:NOTIFICATIONS:GMAIL:TEMPLATES:OTP`),
80
+ locale: user.preferences.language,
81
+ to: [user.email],
82
+ arguments: otp,
83
+ });
84
+
85
+ return { done: true };
86
+ } catch (e) { throw e; }
87
+ };
88
+
89
+ public readonly invitation = async (user: User, access: UserOrganization, organization: Organization): Promise<{ done: true }> => {
90
+ try {
91
+ await notifications.gmail({
92
+ template: nconf.get(`SERVICES:NOTIFICATIONS:GMAIL:TEMPLATES:INVITATION`),
93
+ locale: user.preferences.language,
94
+ to: [user.email],
95
+ arguments: {
96
+ event: NotificationEventNames.MemberInvited,
97
+ data: {
98
+ organization: organization,
99
+ role: access.role,
100
+ cta: {
101
+ organizations: nconf.get(`SERVICES:NOTIFICATIONS:CTAS:ORG_LIST`),
102
+ preferences: nconf.get(`SERVICES:NOTIFICATIONS:CTAS:USER_PREF_LIST`),
103
+ },
104
+ },
105
+ },
106
+ });
107
+
108
+ return { done: true };
109
+ } catch (e) { throw e; }
110
+ };
111
+
112
+ }
113
+
114
+ const notificationController = new NotificationController();
115
+ export default notificationController;
@@ -0,0 +1,210 @@
1
+ import { Date2, Formats } from "@beseif-solutions/utility-functions/dist/utils/date-utils";
2
+ import { JSON2 } from "@beseif-solutions/utility-functions/dist/utils/json-utils";
3
+ import { Where } from "@beseif-solutions/utility-functions/dist/utils/sql-utils";
4
+ import { newUUIDv4 } from "@beseif-solutions/utility-functions/dist/utils/string-utils/uuids";
5
+ import { DeepPartial } from "@beseif-solutions/utility-functions/dist/utils/typing-utils";
6
+ import { Schema, Validator } from "@beseif-solutions/utility-functions/dist/utils/validation-utils";
7
+ import organizationDao from "../../daos/organization-dao";
8
+ import { addOrganizationSchema, updateOrganizationSchema } from "../../schemas/admin/organization-schemas";
9
+ import { DBRecord, Organization, OrganizationStatus } from "../../types/admin/organization-types";
10
+ import { uploadFile, deleteFile } from "../../../helpers/s3";
11
+ import { performance } from "perf_hooks";
12
+ import { Modifiers } from "../../common-types";
13
+
14
+ type Add = Omit<Organization, `id` | `uuid` | `created_date` | `modified_date`>;
15
+
16
+ type Update = DeepPartial<Add>;
17
+
18
+ type Options = { timestamp?: true, deletion?: boolean };
19
+
20
+ class OrganizationCtrl {
21
+ public readonly get = async (id: number, options: Options = {}): Promise<Organization[]> => {
22
+ try {
23
+ const db = await organizationDao.get({ id: id });
24
+ const response = await this.parse(db, options);
25
+ return response;
26
+ } catch (e) { throw e; }
27
+ };
28
+
29
+ public readonly list = async (params: Where<Organization>, options: Options & Modifiers = {}): Promise<Organization[]> => {
30
+ try {
31
+ const db = await organizationDao.list({ ...params }, options);
32
+ const response = await this.parse(db, options);
33
+ return response;
34
+ } catch (e) {
35
+ throw e;
36
+ }
37
+ };
38
+
39
+ public readonly add = async (data: Add, options: Options = {}): Promise<Organization[]> => {
40
+ try {
41
+ Validator.validate(addOrganizationSchema, data);
42
+ await this.check(data);
43
+
44
+ const uuid = await this.getFreeUUID();
45
+
46
+ if (data.properties.image) {
47
+ data.properties.image =
48
+ data.properties.image.startsWith(`http`) ?
49
+ data.properties.image :
50
+ await uploadFile({
51
+ entity: `organizations`,
52
+ uuid: uuid,
53
+ name: `avatar`,
54
+ }, data.properties.image);
55
+ }
56
+
57
+ if (data.properties.cover) {
58
+ data.properties.cover =
59
+ data.properties.cover.startsWith(`http`) ?
60
+ data.properties.cover :
61
+ await uploadFile({
62
+ entity: `organizations`,
63
+ uuid: uuid,
64
+ name: `cover`,
65
+ }, data.properties.cover);
66
+ }
67
+
68
+ const { insertId } = await organizationDao.add({
69
+ uuid: uuid,
70
+ properties: data.properties,
71
+ billing: data.billing,
72
+ preferences: data.preferences,
73
+ status: data.status,
74
+ metadata: data.metadata,
75
+ internal: data.internal,
76
+ });
77
+
78
+ return await this.get(insertId, options);
79
+ } catch (e) { throw e; }
80
+ };
81
+
82
+ public readonly update = async (id: number, data: Update, options: Options = {}): Promise<Organization[]> => {
83
+ try {
84
+ Validator.validate(updateOrganizationSchema(options.deletion), data);
85
+
86
+ const old = (await this.get(id, {}))[0];
87
+ if (!old) { throw new Error(`Organization with id ${id} doesn't exist`); }
88
+
89
+ const merged = JSON2.deepClean(JSON2.deepMerge({}, old, data)) as Organization;
90
+
91
+ await this.check(merged, old);
92
+
93
+ if (data.properties?.image === null && old.properties.image) {
94
+ await deleteFile({
95
+ entity: `organizations`,
96
+ uuid: merged.uuid,
97
+ name: `avatar`,
98
+ });
99
+ merged.properties.image = null;
100
+ } else if (data.properties?.image) {
101
+ merged.properties.image =
102
+ data.properties.image.startsWith(`http`) ?
103
+ data.properties.image :
104
+ await uploadFile({
105
+ entity: `organizations`,
106
+ uuid: merged.uuid,
107
+ name: `avatar`,
108
+ }, data.properties.image);
109
+ }
110
+
111
+ if (data.properties?.cover === null && old.properties.cover) {
112
+ await deleteFile({
113
+ entity: `organizations`,
114
+ uuid: merged.uuid,
115
+ name: `cover`,
116
+ });
117
+ merged.properties.cover = null;
118
+ } else if (data.properties?.cover) {
119
+ merged.properties.cover =
120
+ data.properties.cover.startsWith(`http`) ?
121
+ data.properties.cover :
122
+ await uploadFile({
123
+ entity: `organizations`,
124
+ uuid: merged.uuid,
125
+ name: `cover`,
126
+ }, data.properties.cover);
127
+ }
128
+
129
+ const { affectedRows } = await organizationDao.update({ id: id }, {
130
+ uuid: old.uuid,
131
+ properties: merged.properties,
132
+ billing: merged.billing,
133
+ preferences: merged.preferences,
134
+ status: merged.status,
135
+ metadata: merged.metadata,
136
+ internal: merged.internal,
137
+ });
138
+
139
+ if (affectedRows <= 0) { throw new Error(`Could not update organization with id ${id}`); }
140
+
141
+ return await this.get(id, options);
142
+ } catch (e) { throw e; }
143
+
144
+ };
145
+
146
+ public readonly delete = async (id: number): Promise<{ deleted: boolean }> => {
147
+ try {
148
+ Validator.validate(Schema.number(), id);
149
+ const { affectedRows } = await organizationDao.delete({ id: id });
150
+ return { deleted: affectedRows > 0 };
151
+ } catch (e) { throw e; }
152
+ };
153
+
154
+ private readonly getFreeUUID = async (): Promise<string> => {
155
+ try {
156
+ let uuid: string;
157
+ do {
158
+ uuid = newUUIDv4({ case: `lower` });
159
+ const exists = (await organizationDao.list({ uuid: uuid }))[0];
160
+ if (!exists) { break; }
161
+ } while (true);
162
+ return uuid;
163
+ } catch (e) { throw e; }
164
+ };
165
+
166
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
167
+ private readonly check = async (data: Add | Organization, old?: Organization): Promise<void> => {
168
+ try {
169
+ // nothing to check at the moment
170
+ } catch (e) {
171
+ throw e;
172
+ }
173
+ };
174
+
175
+ private readonly parse = async (records: DBRecord[], options: Options = {}): Promise<Organization[]> => {
176
+ try {
177
+ const response: Organization[] = [];
178
+
179
+ for (const record of records) {
180
+ // ignore deleted organizations
181
+ if (!Object.values(OrganizationStatus).includes(record.status)) { continue; }
182
+
183
+ const p: Organization = {
184
+ id: record.id,
185
+ uuid: record.uuid,
186
+ properties: {
187
+ name: record.properties.name,
188
+ activity: record.properties.activity,
189
+ ...record.properties.image ? { image: `${record.properties.image}${options.timestamp ? `?ts=${performance.now()}` : ``}` } : {},
190
+ ...record.properties.cover ? { cover: `${record.properties.cover}${options.timestamp ? `?ts=${performance.now()}` : ``}` } : {},
191
+ },
192
+ billing: record.billing,
193
+ preferences: record.preferences,
194
+ created_date: new Date2(record.created_date).format(Formats.ISO),
195
+ modified_date: new Date2(record.modified_date).format(Formats.ISO),
196
+ status: record.status,
197
+ metadata: record.metadata,
198
+ internal: record.internal,
199
+ };
200
+
201
+ response.push(p);
202
+ }
203
+
204
+ return response;
205
+ } catch (e) { throw e; }
206
+ };
207
+ }
208
+
209
+ const organizationController = new OrganizationCtrl();
210
+ export default organizationController;