@skalfa/skalfa-api-core 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.
Files changed (249) hide show
  1. package/.github/workflows/publish.yml +40 -0
  2. package/dist/auth/auth.d.ts +19 -0
  3. package/dist/auth/auth.js +227 -0
  4. package/dist/auth/auth.js.map +1 -0
  5. package/dist/auth/index.d.ts +1 -0
  6. package/dist/auth/index.js +2 -0
  7. package/dist/auth/index.js.map +1 -0
  8. package/dist/auth.util.d.ts +19 -0
  9. package/dist/auth.util.js +183 -0
  10. package/dist/auth.util.js.map +1 -0
  11. package/dist/commands/cli.d.ts +1 -0
  12. package/dist/commands/cli.js +78 -0
  13. package/dist/commands/cli.js.map +1 -0
  14. package/dist/commands/make/basic-controller.d.ts +2 -0
  15. package/dist/commands/make/basic-controller.js +40 -0
  16. package/dist/commands/make/basic-controller.js.map +1 -0
  17. package/dist/commands/make/basic-migration.d.ts +5 -0
  18. package/dist/commands/make/basic-migration.js +60 -0
  19. package/dist/commands/make/basic-migration.js.map +1 -0
  20. package/dist/commands/make/basic-model.d.ts +2 -0
  21. package/dist/commands/make/basic-model.js +25 -0
  22. package/dist/commands/make/basic-model.js.map +1 -0
  23. package/dist/commands/make/basic-seeder.d.ts +3 -0
  24. package/dist/commands/make/basic-seeder.js +32 -0
  25. package/dist/commands/make/basic-seeder.js.map +1 -0
  26. package/dist/commands/make/blueprint.d.ts +2 -0
  27. package/dist/commands/make/blueprint.js +29 -0
  28. package/dist/commands/make/blueprint.js.map +1 -0
  29. package/dist/commands/make/da-migration.d.ts +5 -0
  30. package/dist/commands/make/da-migration.js +60 -0
  31. package/dist/commands/make/da-migration.js.map +1 -0
  32. package/dist/commands/make/light-controller.d.ts +3 -0
  33. package/dist/commands/make/light-controller.js +54 -0
  34. package/dist/commands/make/light-controller.js.map +1 -0
  35. package/dist/commands/make/light-model.d.ts +3 -0
  36. package/dist/commands/make/light-model.js +50 -0
  37. package/dist/commands/make/light-model.js.map +1 -0
  38. package/dist/commands/make/mail.d.ts +2 -0
  39. package/dist/commands/make/mail.js +41 -0
  40. package/dist/commands/make/mail.js.map +1 -0
  41. package/dist/commands/make/notification.d.ts +2 -0
  42. package/dist/commands/make/notification.js +33 -0
  43. package/dist/commands/make/notification.js.map +1 -0
  44. package/dist/commands/make/queue.d.ts +2 -0
  45. package/dist/commands/make/queue.js +35 -0
  46. package/dist/commands/make/queue.js.map +1 -0
  47. package/dist/commands/runner/barrels.d.ts +3 -0
  48. package/dist/commands/runner/barrels.js +78 -0
  49. package/dist/commands/runner/barrels.js.map +1 -0
  50. package/dist/commands/runner/blueprint/controller-generation.d.ts +1 -0
  51. package/dist/commands/runner/blueprint/controller-generation.js +147 -0
  52. package/dist/commands/runner/blueprint/controller-generation.js.map +1 -0
  53. package/dist/commands/runner/blueprint/documentation-generation.d.ts +6 -0
  54. package/dist/commands/runner/blueprint/documentation-generation.js +337 -0
  55. package/dist/commands/runner/blueprint/documentation-generation.js.map +1 -0
  56. package/dist/commands/runner/blueprint/migration-generation.d.ts +1 -0
  57. package/dist/commands/runner/blueprint/migration-generation.js +120 -0
  58. package/dist/commands/runner/blueprint/migration-generation.js.map +1 -0
  59. package/dist/commands/runner/blueprint/model-generation.d.ts +1 -0
  60. package/dist/commands/runner/blueprint/model-generation.js +122 -0
  61. package/dist/commands/runner/blueprint/model-generation.js.map +1 -0
  62. package/dist/commands/runner/blueprint/runner.d.ts +23 -0
  63. package/dist/commands/runner/blueprint/runner.js +139 -0
  64. package/dist/commands/runner/blueprint/runner.js.map +1 -0
  65. package/dist/commands/runner/blueprint/seeder-generation.d.ts +1 -0
  66. package/dist/commands/runner/blueprint/seeder-generation.js +40 -0
  67. package/dist/commands/runner/blueprint/seeder-generation.js.map +1 -0
  68. package/dist/commands/runner/da-migration.d.ts +39 -0
  69. package/dist/commands/runner/da-migration.js +262 -0
  70. package/dist/commands/runner/da-migration.js.map +1 -0
  71. package/dist/commands/runner/migration.d.ts +11 -0
  72. package/dist/commands/runner/migration.js +188 -0
  73. package/dist/commands/runner/migration.js.map +1 -0
  74. package/dist/commands/runner/seeder.d.ts +3 -0
  75. package/dist/commands/runner/seeder.js +40 -0
  76. package/dist/commands/runner/seeder.js.map +1 -0
  77. package/dist/commands/stubs/index.d.ts +14 -0
  78. package/dist/commands/stubs/index.js +277 -0
  79. package/dist/commands/stubs/index.js.map +1 -0
  80. package/dist/context/context.d.ts +7 -0
  81. package/dist/context/context.js +11 -0
  82. package/dist/context/context.js.map +1 -0
  83. package/dist/context/index.d.ts +1 -0
  84. package/dist/context/index.js +2 -0
  85. package/dist/context/index.js.map +1 -0
  86. package/dist/context.util.d.ts +7 -0
  87. package/dist/context.util.js +11 -0
  88. package/dist/context.util.js.map +1 -0
  89. package/dist/controller/controller.d.ts +118 -0
  90. package/dist/controller/controller.js +147 -0
  91. package/dist/controller/controller.js.map +1 -0
  92. package/dist/controller/index.d.ts +1 -0
  93. package/dist/controller/index.js +2 -0
  94. package/dist/controller/index.js.map +1 -0
  95. package/dist/controller.util.d.ts +118 -0
  96. package/dist/controller.util.js +144 -0
  97. package/dist/controller.util.js.map +1 -0
  98. package/dist/conversion/conversion.d.ts +8 -0
  99. package/dist/conversion/conversion.js +52 -0
  100. package/dist/conversion/conversion.js.map +1 -0
  101. package/dist/conversion/index.d.ts +1 -0
  102. package/dist/conversion/index.js +2 -0
  103. package/dist/conversion/index.js.map +1 -0
  104. package/dist/conversion.util.d.ts +8 -0
  105. package/dist/conversion.util.js +52 -0
  106. package/dist/conversion.util.js.map +1 -0
  107. package/dist/db/db.d.ts +84 -0
  108. package/dist/db/db.js +177 -0
  109. package/dist/db/db.js.map +1 -0
  110. package/dist/db/index.d.ts +1 -0
  111. package/dist/db/index.js +2 -0
  112. package/dist/db/index.js.map +1 -0
  113. package/dist/db.util.d.ts +84 -0
  114. package/dist/db.util.js +177 -0
  115. package/dist/db.util.js.map +1 -0
  116. package/dist/index.d.ts +21 -0
  117. package/dist/index.js +14 -0
  118. package/dist/index.js.map +1 -0
  119. package/dist/logger/index.d.ts +1 -0
  120. package/dist/logger/index.js +2 -0
  121. package/dist/logger/index.js.map +1 -0
  122. package/dist/logger/logger.d.ts +30 -0
  123. package/dist/logger/logger.js +126 -0
  124. package/dist/logger/logger.js.map +1 -0
  125. package/dist/logger.util.d.ts +30 -0
  126. package/dist/logger.util.js +126 -0
  127. package/dist/logger.util.js.map +1 -0
  128. package/dist/mail/index.d.ts +1 -0
  129. package/dist/mail/index.js +2 -0
  130. package/dist/mail/index.js.map +1 -0
  131. package/dist/mail/mail.d.ts +21 -0
  132. package/dist/mail/mail.js +53 -0
  133. package/dist/mail/mail.js.map +1 -0
  134. package/dist/mail.util.d.ts +21 -0
  135. package/dist/mail.util.js +53 -0
  136. package/dist/mail.util.js.map +1 -0
  137. package/dist/middleware/index.d.ts +1 -0
  138. package/dist/middleware/index.js +2 -0
  139. package/dist/middleware/index.js.map +1 -0
  140. package/dist/middleware/middleware.d.ts +263 -0
  141. package/dist/middleware/middleware.js +233 -0
  142. package/dist/middleware/middleware.js.map +1 -0
  143. package/dist/middleware.util.d.ts +263 -0
  144. package/dist/middleware.util.js +233 -0
  145. package/dist/middleware.util.js.map +1 -0
  146. package/dist/model/index.d.ts +3 -0
  147. package/dist/model/index.js +4 -0
  148. package/dist/model/index.js.map +1 -0
  149. package/dist/model/model.d.ts +204 -0
  150. package/dist/model/model.js +1495 -0
  151. package/dist/model/model.js.map +1 -0
  152. package/dist/model.util.d.ts +204 -0
  153. package/dist/model.util.js +1495 -0
  154. package/dist/model.util.js.map +1 -0
  155. package/dist/permission/index.d.ts +1 -0
  156. package/dist/permission/index.js +2 -0
  157. package/dist/permission/index.js.map +1 -0
  158. package/dist/permission/permission.d.ts +38 -0
  159. package/dist/permission/permission.js +91 -0
  160. package/dist/permission/permission.js.map +1 -0
  161. package/dist/permission.util.d.ts +38 -0
  162. package/dist/permission.util.js +91 -0
  163. package/dist/permission.util.js.map +1 -0
  164. package/dist/registry/index.d.ts +1 -0
  165. package/dist/registry/index.js +2 -0
  166. package/dist/registry/index.js.map +1 -0
  167. package/dist/registry/registry.d.ts +28 -0
  168. package/dist/registry/registry.js +19 -0
  169. package/dist/registry/registry.js.map +1 -0
  170. package/dist/registry.util.d.ts +28 -0
  171. package/dist/registry.util.js +19 -0
  172. package/dist/registry.util.js.map +1 -0
  173. package/dist/route/index.d.ts +1 -0
  174. package/dist/route/index.js +2 -0
  175. package/dist/route/index.js.map +1 -0
  176. package/dist/route/route.d.ts +1 -0
  177. package/dist/route/route.js +12 -0
  178. package/dist/route/route.js.map +1 -0
  179. package/dist/route.util.d.ts +1 -0
  180. package/dist/route.util.js +12 -0
  181. package/dist/route.util.js.map +1 -0
  182. package/dist/storage/index.d.ts +1 -0
  183. package/dist/storage/index.js +2 -0
  184. package/dist/storage/index.js.map +1 -0
  185. package/dist/storage/storage.d.ts +56 -0
  186. package/dist/storage/storage.js +86 -0
  187. package/dist/storage/storage.js.map +1 -0
  188. package/dist/storage.util.d.ts +56 -0
  189. package/dist/storage.util.js +82 -0
  190. package/dist/storage.util.js.map +1 -0
  191. package/dist/validation/index.d.ts +1 -0
  192. package/dist/validation/index.js +2 -0
  193. package/dist/validation/index.js.map +1 -0
  194. package/dist/validation/validation.d.ts +7 -0
  195. package/dist/validation/validation.js +245 -0
  196. package/dist/validation/validation.js.map +1 -0
  197. package/dist/validation.util.d.ts +7 -0
  198. package/dist/validation.util.js +237 -0
  199. package/dist/validation.util.js.map +1 -0
  200. package/package.json +34 -0
  201. package/src/auth/auth.ts +282 -0
  202. package/src/auth/index.ts +1 -0
  203. package/src/commands/cli.ts +89 -0
  204. package/src/commands/make/basic-controller.ts +49 -0
  205. package/src/commands/make/basic-migration.ts +89 -0
  206. package/src/commands/make/basic-model.ts +32 -0
  207. package/src/commands/make/basic-seeder.ts +38 -0
  208. package/src/commands/make/blueprint.ts +36 -0
  209. package/src/commands/make/da-migration.ts +90 -0
  210. package/src/commands/make/light-controller.ts +67 -0
  211. package/src/commands/make/light-model.ts +61 -0
  212. package/src/commands/make/mail.ts +51 -0
  213. package/src/commands/make/notification.ts +43 -0
  214. package/src/commands/make/queue.ts +45 -0
  215. package/src/commands/runner/barrels.ts +85 -0
  216. package/src/commands/runner/blueprint/controller-generation.ts +194 -0
  217. package/src/commands/runner/blueprint/documentation-generation.ts +463 -0
  218. package/src/commands/runner/blueprint/migration-generation.ts +153 -0
  219. package/src/commands/runner/blueprint/model-generation.ts +149 -0
  220. package/src/commands/runner/blueprint/runner.ts +181 -0
  221. package/src/commands/runner/blueprint/seeder-generation.ts +55 -0
  222. package/src/commands/runner/da-migration.ts +333 -0
  223. package/src/commands/runner/migration.ts +245 -0
  224. package/src/commands/runner/seeder.ts +44 -0
  225. package/src/commands/stubs/index.ts +289 -0
  226. package/src/context/context.ts +17 -0
  227. package/src/context/index.ts +1 -0
  228. package/src/controller/controller.ts +240 -0
  229. package/src/controller/index.ts +1 -0
  230. package/src/conversion/conversion.ts +65 -0
  231. package/src/conversion/index.ts +1 -0
  232. package/src/index.ts +22 -0
  233. package/src/logger/index.ts +1 -0
  234. package/src/logger/logger.ts +177 -0
  235. package/src/mail/index.ts +1 -0
  236. package/src/mail/mail.ts +86 -0
  237. package/src/middleware/index.ts +1 -0
  238. package/src/middleware/middleware.ts +289 -0
  239. package/src/permission/index.ts +1 -0
  240. package/src/permission/permission.ts +136 -0
  241. package/src/registry/index.ts +1 -0
  242. package/src/registry/registry.ts +37 -0
  243. package/src/route/index.ts +1 -0
  244. package/src/route/route.ts +12 -0
  245. package/src/storage/index.ts +1 -0
  246. package/src/storage/storage.ts +107 -0
  247. package/src/validation/index.ts +1 -0
  248. package/src/validation/validation.ts +346 -0
  249. package/tsconfig.json +23 -0
@@ -0,0 +1,237 @@
1
+ import validator from "validator";
2
+ import { db } from "@utils";
3
+ // ==================================>
4
+ // ## Check validate field from rules
5
+ // ==================================>
6
+ export async function validate(data, rules) {
7
+ const errors = {};
8
+ for (const field in rules) {
9
+ const fieldRules = normalizeRules(rules[field]);
10
+ if (field.includes("*")) {
11
+ // const [arrayPath, childPath] = field.split(".*.")
12
+ // const arr = getNestedValue(data, arrayPath)
13
+ // if (!Array.isArray(arr)) {
14
+ // addError(errors, arrayPath, `${arrayPath} harus berupa array`)
15
+ // continue
16
+ // }
17
+ // for (let i = 0; i < arr.length; i++) {
18
+ // const value = childPath
19
+ // ? getNestedValue(arr[i], childPath)
20
+ // : arr[i]
21
+ // const itemField = childPath
22
+ // ? `${arrayPath}.${i}.${childPath}`
23
+ // : `${arrayPath}.${i}`
24
+ // await checkRules({ field: itemField, value, rules: fieldRules, data, errors })
25
+ // }
26
+ const segments = field.split(".");
27
+ await nestedValidation({ value: data, segments, rules: fieldRules, fieldPath: "", data, errors });
28
+ continue;
29
+ }
30
+ const value = getNestedValue(data, field) ?? "";
31
+ await checkRules({ field, value, rules: fieldRules, data, errors });
32
+ }
33
+ return {
34
+ valid: Object.keys(errors).length === 0,
35
+ errors
36
+ };
37
+ }
38
+ async function checkRules({ field, value, rules, data, errors }) {
39
+ for (const rule of rules) {
40
+ const [name, param] = rule.split(":");
41
+ switch (name) {
42
+ // === BASIC ===
43
+ case "required":
44
+ if (validator.isEmpty(String(value).trim())) {
45
+ addError(errors, field, `${field} wajib diisi`);
46
+ }
47
+ break;
48
+ case "string":
49
+ case "text":
50
+ if (typeof value !== "string") {
51
+ addError(errors, field, `${field} harus berupa string`);
52
+ }
53
+ break;
54
+ case "numeric":
55
+ case "number":
56
+ if (!validator.isNumeric(String(value))) {
57
+ addError(errors, field, `${field} harus berupa angka`);
58
+ }
59
+ break;
60
+ case "boolean":
61
+ if (!(value === true || value === false || value === "true" || value === "false" || value === 1 || value === 0)) {
62
+ addError(errors, field, `${field} harus berupa boolean`);
63
+ }
64
+ break;
65
+ case "email":
66
+ if (!validator.isEmail(String(value))) {
67
+ addError(errors, field, `${field} harus berupa email yang valid`);
68
+ }
69
+ break;
70
+ case "url":
71
+ if (!validator.isURL(String(value))) {
72
+ addError(errors, field, `${field} harus berupa URL yang valid`);
73
+ }
74
+ break;
75
+ case "date":
76
+ if (!validator.isDate(String(value))) {
77
+ addError(errors, field, `${field} harus berupa tanggal yang valid`);
78
+ }
79
+ break;
80
+ // === LENGTH ===
81
+ case "min": {
82
+ const min = parseInt(param);
83
+ if (!validator.isLength(String(value), { min })) {
84
+ addError(errors, field, `${field} minimal ${min} karakter`);
85
+ }
86
+ break;
87
+ }
88
+ case "max": {
89
+ const max = parseInt(param);
90
+ if (!validator.isLength(String(value), { max })) {
91
+ addError(errors, field, `${field} maksimal ${max} karakter`);
92
+ }
93
+ break;
94
+ }
95
+ case "between": {
96
+ const [minVal, maxVal] = param.split(",").map(Number);
97
+ if (!validator.isLength(String(value), { min: minVal, max: maxVal })) {
98
+ addError(errors, field, `${field} harus antara ${minVal} - ${maxVal} karakter`);
99
+ }
100
+ break;
101
+ }
102
+ // === SET MEMBERSHIP ===
103
+ case "in": {
104
+ const allowed = param.split(",");
105
+ if (!allowed.includes(String(value))) {
106
+ addError(errors, field, `${field} harus salah satu dari: ${allowed.join(", ")}`);
107
+ }
108
+ break;
109
+ }
110
+ case "not_in": {
111
+ const notAllowed = param.split(",");
112
+ if (notAllowed.includes(String(value))) {
113
+ addError(errors, field, `${field} tidak boleh salah satu dari: ${notAllowed.join(", ")}`);
114
+ }
115
+ break;
116
+ }
117
+ case "array":
118
+ if (!Array.isArray(value)) {
119
+ addError(errors, field, `${field} harus berupa array`);
120
+ }
121
+ break;
122
+ // === RELATIONAL ===
123
+ case "confirmed":
124
+ if (value !== getNestedValue(data, `${field}_confirmation`)) {
125
+ addError(errors, field, `${field} tidak sama dengan konfirmasi`);
126
+ }
127
+ break;
128
+ case "same":
129
+ if (value !== getNestedValue(data, param)) {
130
+ addError(errors, field, `${field} harus sama dengan ${param}`);
131
+ }
132
+ break;
133
+ case "different":
134
+ if (value === getNestedValue(data, param)) {
135
+ addError(errors, field, `${field} harus berbeda dengan ${param}`);
136
+ }
137
+ break;
138
+ // === REGEX ===
139
+ case "regex":
140
+ try {
141
+ const pattern = new RegExp(param);
142
+ if (!pattern.test(String(value))) {
143
+ addError(errors, field, `${field} tidak sesuai format`);
144
+ }
145
+ }
146
+ catch {
147
+ addError(errors, field, `Regex rule untuk ${field} tidak valid`);
148
+ }
149
+ break;
150
+ // === DATABASE VALIDATION ===
151
+ case "unique": {
152
+ const [table, column, exceptId] = param.split(",");
153
+ const query = db.table(table).where(column, value);
154
+ if (exceptId)
155
+ query.whereNot("id", exceptId);
156
+ const existing = await query.first();
157
+ if (existing) {
158
+ addError(errors, field, `${field} sudah digunakan`);
159
+ }
160
+ break;
161
+ }
162
+ case "exists": {
163
+ const [table, column] = param.split(",");
164
+ const existing = await db.table(table).where(column, value).first();
165
+ if (!existing) {
166
+ addError(errors, field, `${field} tidak ditemukan di ${table}`);
167
+ }
168
+ break;
169
+ }
170
+ }
171
+ }
172
+ }
173
+ async function nestedValidation({ value, segments, rules, fieldPath, data, errors }) {
174
+ if (segments.length === 0) {
175
+ await checkRules({
176
+ field: fieldPath,
177
+ value,
178
+ rules,
179
+ data,
180
+ errors
181
+ });
182
+ return;
183
+ }
184
+ const [segment, ...rest] = segments;
185
+ if (segment === "*") {
186
+ if (!Array.isArray(value)) {
187
+ addError(errors, fieldPath, `${fieldPath} harus berupa array`);
188
+ return;
189
+ }
190
+ for (let i = 0; i < value.length; i++) {
191
+ await nestedValidation({
192
+ value: value[i],
193
+ segments: rest,
194
+ rules,
195
+ fieldPath: `${fieldPath}.${i}`,
196
+ data,
197
+ errors
198
+ });
199
+ }
200
+ }
201
+ else {
202
+ await nestedValidation({
203
+ value: value?.[segment],
204
+ segments: rest,
205
+ rules,
206
+ fieldPath: fieldPath ? `${fieldPath}.${segment}` : segment,
207
+ data,
208
+ errors
209
+ });
210
+ }
211
+ }
212
+ // ==================================>
213
+ // ## Validation helpers
214
+ // ==================================>
215
+ function getNestedValue(obj, path) {
216
+ if (!obj || typeof obj !== "object")
217
+ return undefined;
218
+ const normalizedPath = path
219
+ .replace(/\[(\w+)\]/g, '.$1')
220
+ .replace(/\['([^']+)'\]/g, '.$1')
221
+ .replace(/\["([^"]+)"\]/g, '.$1');
222
+ return normalizedPath.split('.').reduce((acc, key) => {
223
+ if (acc && Object.prototype.hasOwnProperty.call(acc, key)) {
224
+ return acc[key];
225
+ }
226
+ return undefined;
227
+ }, obj);
228
+ }
229
+ function normalizeRules(rules) {
230
+ if (Array.isArray(rules))
231
+ return rules;
232
+ return rules.split("|");
233
+ }
234
+ function addError(errors, field, message) {
235
+ errors[field] = [...(errors[field] || []), message];
236
+ }
237
+ //# sourceMappingURL=validation.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.util.js","sourceRoot":"","sources":["../src/validation.util.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@skalfa/skalfa-api-core",
3
+ "version": "1.0.2",
4
+ "description": "Core utility functions for Skalfa API framework.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc --ignoreDeprecations 6.0"
9
+ },
10
+ "keywords": [
11
+ "aluna",
12
+ "core",
13
+ "utility"
14
+ ],
15
+ "author": "",
16
+ "license": "UNLICENSED",
17
+ "dependencies": {
18
+ "@skalfa/skalfa-orm": "file:../skalfa-orm",
19
+ "bcrypt": "^6.0.0",
20
+ "commander": "^12.1.0",
21
+ "dotenv": "^17.2.2",
22
+ "elysia": "latest",
23
+ "nodemailer": "^7.0.9",
24
+ "validator": "^13.15.15"
25
+ },
26
+ "devDependencies": {
27
+ "@types/bcrypt": "^6.0.0",
28
+ "@types/node": "^26.0.0",
29
+ "@types/nodemailer": "^7.0.2",
30
+ "@types/validator": "^13.15.3",
31
+ "bun-types": "latest",
32
+ "typescript": "^6.0.3"
33
+ }
34
+ }
@@ -0,0 +1,282 @@
1
+ import crypto from 'crypto'
2
+ import bcrypt from "bcrypt";
3
+ import { db } from '@skalfa/skalfa-orm'
4
+ import { registry } from '@utils/registry'
5
+
6
+ // =====================================>
7
+ // ## Auth: User Access Token
8
+ // =====================================>
9
+ const TOKEN_PLAIN_LENGTH = 20
10
+ const AUTH_PERMISSION = process.env.AUTH_CACHE === "true"
11
+ const AUTH_CACHE = process.env.AUTH_CACHE === "true"
12
+ const AUTH_CACHE_TTL = Number(process.env.AUTH_CACHE_TTL || 600)
13
+
14
+ export const auth = {
15
+
16
+ // =====================================>
17
+ // ## Auth: create access token with user id
18
+ // =====================================>
19
+ async createAccessToken(userId: number, req: Request, permission: boolean = true) {
20
+ const plain = crypto.randomBytes(TOKEN_PLAIN_LENGTH).toString("hex")
21
+ const hash = await bcrypt.hash(plain, 10)
22
+ const agent = generateAgentId(req)
23
+
24
+ if (!db) {
25
+ // get user from db (fallback / stub for no ORM)
26
+ return {
27
+ token: `1|${plain}`,
28
+ tokenId: 1,
29
+ }
30
+ }
31
+
32
+ let permissions: string[] = []
33
+ if (AUTH_PERMISSION && permission) {
34
+ permissions = await getUserPermissions(userId)
35
+ }
36
+
37
+ const [row] = await db("user_access_tokens").insert({
38
+ user_id : userId,
39
+ token : hash,
40
+ agent : agent,
41
+ permissions : JSON.stringify(permissions),
42
+ created_at : new Date(),
43
+ }).returning(["id"])
44
+
45
+ return {
46
+ token : `${row.id}|${plain}`,
47
+ tokenId : row.id,
48
+ }
49
+ },
50
+
51
+ // =====================================>
52
+ // ## Auth: delete access token with user id
53
+ // =====================================>
54
+ async revokeAccessToken(id: number) {
55
+ if (!db) {
56
+ // delete user access token from db (stub for no ORM)
57
+ return;
58
+ }
59
+ return db.table('user_access_tokens').where("id", id).delete()
60
+ },
61
+
62
+ // =====================================>
63
+ // ## Auth: verify access token
64
+ // =====================================>
65
+ async verifyAccessToken(token: string, req?: Request) {
66
+ if (!token.includes("|")) return null
67
+
68
+ const [tokenId, plain] = token.split("|", 2)
69
+ const agent = req ? generateAgentId(req) : ""
70
+ const ip = req ? getRequestIp(req) : ""
71
+
72
+ const cacheKey = `auth:token:${tokenId}`
73
+
74
+ if (AUTH_CACHE) {
75
+ const redis = registry.get('redis')
76
+ if (redis) {
77
+ const cached = await redis.get(cacheKey)
78
+ if (cached) {
79
+ const session = JSON.parse(cached)
80
+ if (session.agent !== agent) return null
81
+ return session
82
+ }
83
+ }
84
+ }
85
+
86
+ if (!db) {
87
+ // get user and token from db (stub for no ORM)
88
+ const user = { id: 1, name: "Admin", email: "admin@example.com" }
89
+ const tokenRecord = { id: Number(tokenId), agent, permission: [] }
90
+ return { user, token: tokenRecord, permissions: [] }
91
+ }
92
+
93
+ const tokenRecord = await db("user_access_tokens").where("id", tokenId).first()
94
+
95
+ if (!tokenRecord) return null
96
+ if (tokenRecord.agent !== agent) return null
97
+
98
+ const valid = await bcrypt.compare(plain, tokenRecord.token)
99
+ if (!valid) return null
100
+
101
+ await db("user_access_tokens").where("id", tokenRecord.id).update({ last_used_at: new Date(), last_used_ip: ip })
102
+
103
+ const user = await db("users").where("id", tokenRecord.user_id).first()
104
+
105
+ if (AUTH_CACHE) {
106
+ const redis = registry.get('redis')
107
+ if (redis) {
108
+ await redis.setex(
109
+ cacheKey,
110
+ AUTH_CACHE_TTL,
111
+ JSON.stringify({
112
+ user : user,
113
+ agent : tokenRecord.agent,
114
+ permissions : tokenRecord.permission,
115
+ })
116
+ )
117
+ }
118
+ }
119
+
120
+ return { user, token: tokenRecord, permissions: tokenRecord.permission }
121
+ },
122
+
123
+ // =====================================>
124
+ // ## Auth: create user mail token
125
+ // =====================================>
126
+ async createUserMailToken(userId: number) {
127
+ const token = Math.floor(100000 + Math.random() * 900000).toString()
128
+
129
+ if (!db) {
130
+ // create user mail token in db (stub for no ORM)
131
+ return {
132
+ token: token,
133
+ tokenId: 1
134
+ }
135
+ }
136
+
137
+ const hash = crypto.createHash('sha256').update(token).digest('hex')
138
+ const trx = await db.transaction()
139
+
140
+ await trx.table('user_mail_tokens').insert({
141
+ user_id : userId,
142
+ token : hash,
143
+ created_at : new Date(),
144
+ })
145
+
146
+ const record = await trx.table('user_mail_tokens').orderBy('id', 'desc').first()
147
+
148
+ await trx.commit()
149
+
150
+ return {
151
+ token : token,
152
+ tokenId : record.id
153
+ }
154
+ },
155
+
156
+ // =====================================>
157
+ // ## Auth: Verify user mail token
158
+ // =====================================>
159
+ async verifyUserMailToken(userId: number, token: string) {
160
+ if (!db) {
161
+ // verify user mail token in db (stub for no ORM)
162
+ return true
163
+ }
164
+
165
+ const hashedToken = crypto.createHash("sha256").update(token).digest("hex");
166
+
167
+ const record = await db.table("user_mail_tokens")
168
+ .where("user_id", userId)
169
+ .whereNull("used_at")
170
+ .orderBy("id", "desc")
171
+ .first();
172
+
173
+ if (!record) return false
174
+
175
+ if (record.token !== hashedToken) return false;
176
+
177
+ const createdAt = new Date(record.created_at);
178
+ const now = new Date();
179
+ const diffMinutes = (now.getTime() - createdAt.getTime()) / (1000 * 60);
180
+
181
+ if (diffMinutes > 10) return false;
182
+
183
+ return true;
184
+ },
185
+
186
+ // =====================================>
187
+ // ## Auth: list user sessions
188
+ // =====================================>
189
+ async listUserSessions(userId: number, currentTokenId?: number) {
190
+ if (!db) {
191
+ // list user sessions from db (stub for no ORM)
192
+ return []
193
+ }
194
+
195
+ const rows = await db("user_access_tokens").select(["id", "agent", "created_at", "last_used_at", "last_used_ip","expired_at"]).where("user_id", userId).orderBy("last_used_at", "desc")
196
+
197
+ return rows.map((r: any) => ({
198
+ ...r,
199
+ is_active : r.revoked_at === null,
200
+ is_current : r.id === currentTokenId,
201
+ }))
202
+ },
203
+
204
+ // =====================================>
205
+ // ## Auth: revalidate user permission
206
+ // =====================================>
207
+ revalidateUserPermissions: revalidateUserPermissions,
208
+ revalidateUserPermissionsByRole: revalidateUserPermissionsByRole,
209
+ }
210
+
211
+ function generateAgentId(req: Request) {
212
+ const ua = req.headers.get("user-agent") ?? ""
213
+ const acc = req.headers.get("accept") ?? ""
214
+
215
+ return crypto.createHash("sha256").update(ua + acc).digest("hex")
216
+ }
217
+
218
+ function getRequestIp(req: Request) {
219
+ return (req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || req.headers.get("x-real-ip") || "unknown")
220
+ }
221
+
222
+ async function getUserPermissions(userId: number): Promise<string[]> {
223
+ if (!db) {
224
+ // get user permissions from db (stub for no ORM)
225
+ return []
226
+ }
227
+
228
+ const roleIds = await db("user_roles").where("user_id", userId).pluck("role_id")
229
+
230
+ if (roleIds.length === 0) return []
231
+
232
+ const rows = await db("permissions").whereIn("role_id", roleIds).pluck("permissions")
233
+
234
+ return Array.from(
235
+ new Set(
236
+ rows.flatMap((p: any) => p ?? [])
237
+ )
238
+ )
239
+ }
240
+
241
+ async function revalidateUserPermissions(userId: number) {
242
+ if (!db) {
243
+ // revalidate user permissions in db (stub for no ORM)
244
+ return
245
+ }
246
+
247
+ const permissions = await getUserPermissions(userId)
248
+
249
+ const tokenIds = await db("user_access_tokens").where("user_id", userId).pluck("id")
250
+
251
+ if (tokenIds.length === 0) return
252
+
253
+ await db("user_access_tokens").whereIn("id", tokenIds).update({
254
+ permissions : JSON.stringify(permissions),
255
+ updated_at : new Date(),
256
+ })
257
+
258
+ if (AUTH_CACHE) {
259
+ const redis = registry.get('redis')
260
+ if (redis) {
261
+ await Promise.all(
262
+ tokenIds.map((id: any) => redis.del(`auth:token:${id}`))
263
+ )
264
+ }
265
+ }
266
+ }
267
+
268
+ async function revalidateUserPermissionsByRole(roleId: number) {
269
+ if (!db) {
270
+ // revalidate user permissions by role in db (stub for no ORM)
271
+ return
272
+ }
273
+
274
+ const userIds = await db("user_roles").where("role_id", roleId).pluck("user_id")
275
+
276
+ const queue = registry.get('queue')
277
+ if (queue) {
278
+ for (const userId of userIds) {
279
+ await queue.add("auth:revalidate-permission", { userId })
280
+ }
281
+ }
282
+ }
@@ -0,0 +1 @@
1
+ export * from "./auth";
@@ -0,0 +1,89 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { Command } from "commander";
4
+
5
+ // Basic commands (always available)
6
+ import { makeControllerCommand } from "./make/basic-controller";
7
+ import { makeLightControllerCommand } from "./make/light-controller";
8
+ import { barrelsCommand, watchBarrelsCommand } from "./runner/barrels";
9
+
10
+ // ORM commands (optional)
11
+ import { makeModelCommand } from "./make/basic-model";
12
+ import { makeSeederCommand } from "./make/basic-seeder";
13
+ import { makeMigrationCommand } from "./make/basic-migration";
14
+ import { makeLightModelCommand } from "./make/light-model";
15
+ import { makeBlueprintCommand } from "./make/blueprint";
16
+ import { migrateCommand, migrateFreshCommand } from "./runner/migration";
17
+ import { seederCommand } from "./runner/seeder";
18
+ import { blueprintCommand } from "./runner/blueprint/runner";
19
+
20
+ // Extension-specific commands
21
+ import { makeQueueCommand } from "./make/queue";
22
+ import { makeMailCommand } from "./make/mail";
23
+ import { makeNotificationCommand } from "./make/notification";
24
+ import { makeDaMigrationCommand } from "./make/da-migration";
25
+ import { daMigrateCommand, daMigrateFreshCommand } from "./runner/da-migration";
26
+
27
+ export function runCli() {
28
+ // Read package.json to dynamically detect installed extensions
29
+ let dependencies: Record<string, string> = {};
30
+ try {
31
+ const pkgPath = path.join(process.cwd(), "package.json");
32
+ if (fs.existsSync(pkgPath)) {
33
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
34
+ dependencies = pkg.dependencies || {};
35
+ }
36
+ } catch {}
37
+
38
+ const hasOrm = !!dependencies["@skalfa/skalfa-orm"];
39
+ const hasMail = !!dependencies["@skalfa/mail"] || !!dependencies["skalfa-mail"];
40
+ const hasNotification = !!dependencies["@skalfa/notification"] || !!dependencies["skalfa-notification"];
41
+ const hasQueue = !!dependencies["@skalfa/queue"] || !!dependencies["@skalfa/redis"] || !!dependencies["skalfa-queue"] || !!dependencies["skalfa-redis"];
42
+ const hasDa = !!dependencies["@skalfa/da"] || !!dependencies["skalfa-da"] || !!dependencies["@clickhouse/client"];
43
+
44
+ const program = new Command();
45
+ program.name("skalfa").description("Skalfa Local CLI").version("1.0.0");
46
+
47
+ // 1. Add Core / Basic commands
48
+ program.addCommand(makeControllerCommand);
49
+ program.addCommand(makeLightControllerCommand);
50
+ program.addCommand(barrelsCommand);
51
+ program.addCommand(watchBarrelsCommand);
52
+
53
+ // 2. Add ORM commands if installed
54
+ if (hasOrm) {
55
+ program.addCommand(makeModelCommand);
56
+ program.addCommand(makeMigrationCommand);
57
+ program.addCommand(makeSeederCommand);
58
+ program.addCommand(makeLightModelCommand);
59
+ program.addCommand(makeBlueprintCommand);
60
+ program.addCommand(migrateCommand);
61
+ program.addCommand(migrateFreshCommand);
62
+ program.addCommand(seederCommand);
63
+ program.addCommand(blueprintCommand);
64
+ }
65
+
66
+ // 3. Add Mail commands if installed
67
+ if (hasMail) {
68
+ program.addCommand(makeMailCommand);
69
+ }
70
+
71
+ // 4. Add Notification commands if installed
72
+ if (hasNotification) {
73
+ program.addCommand(makeNotificationCommand);
74
+ }
75
+
76
+ // 5. Add Queue commands if installed
77
+ if (hasQueue) {
78
+ program.addCommand(makeQueueCommand);
79
+ }
80
+
81
+ // 6. Add Data Analytics / OLAP commands if installed
82
+ if (hasDa) {
83
+ program.addCommand(makeDaMigrationCommand);
84
+ program.addCommand(daMigrateCommand);
85
+ program.addCommand(daMigrateFreshCommand);
86
+ }
87
+
88
+ program.parse(process.argv);
89
+ }