@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.
- package/.github/workflows/publish.yml +40 -0
- package/dist/auth/auth.d.ts +19 -0
- package/dist/auth/auth.js +227 -0
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth.util.d.ts +19 -0
- package/dist/auth.util.js +183 -0
- package/dist/auth.util.js.map +1 -0
- package/dist/commands/cli.d.ts +1 -0
- package/dist/commands/cli.js +78 -0
- package/dist/commands/cli.js.map +1 -0
- package/dist/commands/make/basic-controller.d.ts +2 -0
- package/dist/commands/make/basic-controller.js +40 -0
- package/dist/commands/make/basic-controller.js.map +1 -0
- package/dist/commands/make/basic-migration.d.ts +5 -0
- package/dist/commands/make/basic-migration.js +60 -0
- package/dist/commands/make/basic-migration.js.map +1 -0
- package/dist/commands/make/basic-model.d.ts +2 -0
- package/dist/commands/make/basic-model.js +25 -0
- package/dist/commands/make/basic-model.js.map +1 -0
- package/dist/commands/make/basic-seeder.d.ts +3 -0
- package/dist/commands/make/basic-seeder.js +32 -0
- package/dist/commands/make/basic-seeder.js.map +1 -0
- package/dist/commands/make/blueprint.d.ts +2 -0
- package/dist/commands/make/blueprint.js +29 -0
- package/dist/commands/make/blueprint.js.map +1 -0
- package/dist/commands/make/da-migration.d.ts +5 -0
- package/dist/commands/make/da-migration.js +60 -0
- package/dist/commands/make/da-migration.js.map +1 -0
- package/dist/commands/make/light-controller.d.ts +3 -0
- package/dist/commands/make/light-controller.js +54 -0
- package/dist/commands/make/light-controller.js.map +1 -0
- package/dist/commands/make/light-model.d.ts +3 -0
- package/dist/commands/make/light-model.js +50 -0
- package/dist/commands/make/light-model.js.map +1 -0
- package/dist/commands/make/mail.d.ts +2 -0
- package/dist/commands/make/mail.js +41 -0
- package/dist/commands/make/mail.js.map +1 -0
- package/dist/commands/make/notification.d.ts +2 -0
- package/dist/commands/make/notification.js +33 -0
- package/dist/commands/make/notification.js.map +1 -0
- package/dist/commands/make/queue.d.ts +2 -0
- package/dist/commands/make/queue.js +35 -0
- package/dist/commands/make/queue.js.map +1 -0
- package/dist/commands/runner/barrels.d.ts +3 -0
- package/dist/commands/runner/barrels.js +78 -0
- package/dist/commands/runner/barrels.js.map +1 -0
- package/dist/commands/runner/blueprint/controller-generation.d.ts +1 -0
- package/dist/commands/runner/blueprint/controller-generation.js +147 -0
- package/dist/commands/runner/blueprint/controller-generation.js.map +1 -0
- package/dist/commands/runner/blueprint/documentation-generation.d.ts +6 -0
- package/dist/commands/runner/blueprint/documentation-generation.js +337 -0
- package/dist/commands/runner/blueprint/documentation-generation.js.map +1 -0
- package/dist/commands/runner/blueprint/migration-generation.d.ts +1 -0
- package/dist/commands/runner/blueprint/migration-generation.js +120 -0
- package/dist/commands/runner/blueprint/migration-generation.js.map +1 -0
- package/dist/commands/runner/blueprint/model-generation.d.ts +1 -0
- package/dist/commands/runner/blueprint/model-generation.js +122 -0
- package/dist/commands/runner/blueprint/model-generation.js.map +1 -0
- package/dist/commands/runner/blueprint/runner.d.ts +23 -0
- package/dist/commands/runner/blueprint/runner.js +139 -0
- package/dist/commands/runner/blueprint/runner.js.map +1 -0
- package/dist/commands/runner/blueprint/seeder-generation.d.ts +1 -0
- package/dist/commands/runner/blueprint/seeder-generation.js +40 -0
- package/dist/commands/runner/blueprint/seeder-generation.js.map +1 -0
- package/dist/commands/runner/da-migration.d.ts +39 -0
- package/dist/commands/runner/da-migration.js +262 -0
- package/dist/commands/runner/da-migration.js.map +1 -0
- package/dist/commands/runner/migration.d.ts +11 -0
- package/dist/commands/runner/migration.js +188 -0
- package/dist/commands/runner/migration.js.map +1 -0
- package/dist/commands/runner/seeder.d.ts +3 -0
- package/dist/commands/runner/seeder.js +40 -0
- package/dist/commands/runner/seeder.js.map +1 -0
- package/dist/commands/stubs/index.d.ts +14 -0
- package/dist/commands/stubs/index.js +277 -0
- package/dist/commands/stubs/index.js.map +1 -0
- package/dist/context/context.d.ts +7 -0
- package/dist/context/context.js +11 -0
- package/dist/context/context.js.map +1 -0
- package/dist/context/index.d.ts +1 -0
- package/dist/context/index.js +2 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context.util.d.ts +7 -0
- package/dist/context.util.js +11 -0
- package/dist/context.util.js.map +1 -0
- package/dist/controller/controller.d.ts +118 -0
- package/dist/controller/controller.js +147 -0
- package/dist/controller/controller.js.map +1 -0
- package/dist/controller/index.d.ts +1 -0
- package/dist/controller/index.js +2 -0
- package/dist/controller/index.js.map +1 -0
- package/dist/controller.util.d.ts +118 -0
- package/dist/controller.util.js +144 -0
- package/dist/controller.util.js.map +1 -0
- package/dist/conversion/conversion.d.ts +8 -0
- package/dist/conversion/conversion.js +52 -0
- package/dist/conversion/conversion.js.map +1 -0
- package/dist/conversion/index.d.ts +1 -0
- package/dist/conversion/index.js +2 -0
- package/dist/conversion/index.js.map +1 -0
- package/dist/conversion.util.d.ts +8 -0
- package/dist/conversion.util.js +52 -0
- package/dist/conversion.util.js.map +1 -0
- package/dist/db/db.d.ts +84 -0
- package/dist/db/db.js +177 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/index.d.ts +1 -0
- package/dist/db/index.js +2 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db.util.d.ts +84 -0
- package/dist/db.util.js +177 -0
- package/dist/db.util.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/logger/index.d.ts +1 -0
- package/dist/logger/index.js +2 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/logger.d.ts +30 -0
- package/dist/logger/logger.js +126 -0
- package/dist/logger/logger.js.map +1 -0
- package/dist/logger.util.d.ts +30 -0
- package/dist/logger.util.js +126 -0
- package/dist/logger.util.js.map +1 -0
- package/dist/mail/index.d.ts +1 -0
- package/dist/mail/index.js +2 -0
- package/dist/mail/index.js.map +1 -0
- package/dist/mail/mail.d.ts +21 -0
- package/dist/mail/mail.js +53 -0
- package/dist/mail/mail.js.map +1 -0
- package/dist/mail.util.d.ts +21 -0
- package/dist/mail.util.js +53 -0
- package/dist/mail.util.js.map +1 -0
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.js +2 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/middleware.d.ts +263 -0
- package/dist/middleware/middleware.js +233 -0
- package/dist/middleware/middleware.js.map +1 -0
- package/dist/middleware.util.d.ts +263 -0
- package/dist/middleware.util.js +233 -0
- package/dist/middleware.util.js.map +1 -0
- package/dist/model/index.d.ts +3 -0
- package/dist/model/index.js +4 -0
- package/dist/model/index.js.map +1 -0
- package/dist/model/model.d.ts +204 -0
- package/dist/model/model.js +1495 -0
- package/dist/model/model.js.map +1 -0
- package/dist/model.util.d.ts +204 -0
- package/dist/model.util.js +1495 -0
- package/dist/model.util.js.map +1 -0
- package/dist/permission/index.d.ts +1 -0
- package/dist/permission/index.js +2 -0
- package/dist/permission/index.js.map +1 -0
- package/dist/permission/permission.d.ts +38 -0
- package/dist/permission/permission.js +91 -0
- package/dist/permission/permission.js.map +1 -0
- package/dist/permission.util.d.ts +38 -0
- package/dist/permission.util.js +91 -0
- package/dist/permission.util.js.map +1 -0
- package/dist/registry/index.d.ts +1 -0
- package/dist/registry/index.js +2 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/registry.d.ts +28 -0
- package/dist/registry/registry.js +19 -0
- package/dist/registry/registry.js.map +1 -0
- package/dist/registry.util.d.ts +28 -0
- package/dist/registry.util.js +19 -0
- package/dist/registry.util.js.map +1 -0
- package/dist/route/index.d.ts +1 -0
- package/dist/route/index.js +2 -0
- package/dist/route/index.js.map +1 -0
- package/dist/route/route.d.ts +1 -0
- package/dist/route/route.js +12 -0
- package/dist/route/route.js.map +1 -0
- package/dist/route.util.d.ts +1 -0
- package/dist/route.util.js +12 -0
- package/dist/route.util.js.map +1 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/storage.d.ts +56 -0
- package/dist/storage/storage.js +86 -0
- package/dist/storage/storage.js.map +1 -0
- package/dist/storage.util.d.ts +56 -0
- package/dist/storage.util.js +82 -0
- package/dist/storage.util.js.map +1 -0
- package/dist/validation/index.d.ts +1 -0
- package/dist/validation/index.js +2 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/validation.d.ts +7 -0
- package/dist/validation/validation.js +245 -0
- package/dist/validation/validation.js.map +1 -0
- package/dist/validation.util.d.ts +7 -0
- package/dist/validation.util.js +237 -0
- package/dist/validation.util.js.map +1 -0
- package/package.json +34 -0
- package/src/auth/auth.ts +282 -0
- package/src/auth/index.ts +1 -0
- package/src/commands/cli.ts +89 -0
- package/src/commands/make/basic-controller.ts +49 -0
- package/src/commands/make/basic-migration.ts +89 -0
- package/src/commands/make/basic-model.ts +32 -0
- package/src/commands/make/basic-seeder.ts +38 -0
- package/src/commands/make/blueprint.ts +36 -0
- package/src/commands/make/da-migration.ts +90 -0
- package/src/commands/make/light-controller.ts +67 -0
- package/src/commands/make/light-model.ts +61 -0
- package/src/commands/make/mail.ts +51 -0
- package/src/commands/make/notification.ts +43 -0
- package/src/commands/make/queue.ts +45 -0
- package/src/commands/runner/barrels.ts +85 -0
- package/src/commands/runner/blueprint/controller-generation.ts +194 -0
- package/src/commands/runner/blueprint/documentation-generation.ts +463 -0
- package/src/commands/runner/blueprint/migration-generation.ts +153 -0
- package/src/commands/runner/blueprint/model-generation.ts +149 -0
- package/src/commands/runner/blueprint/runner.ts +181 -0
- package/src/commands/runner/blueprint/seeder-generation.ts +55 -0
- package/src/commands/runner/da-migration.ts +333 -0
- package/src/commands/runner/migration.ts +245 -0
- package/src/commands/runner/seeder.ts +44 -0
- package/src/commands/stubs/index.ts +289 -0
- package/src/context/context.ts +17 -0
- package/src/context/index.ts +1 -0
- package/src/controller/controller.ts +240 -0
- package/src/controller/index.ts +1 -0
- package/src/conversion/conversion.ts +65 -0
- package/src/conversion/index.ts +1 -0
- package/src/index.ts +22 -0
- package/src/logger/index.ts +1 -0
- package/src/logger/logger.ts +177 -0
- package/src/mail/index.ts +1 -0
- package/src/mail/mail.ts +86 -0
- package/src/middleware/index.ts +1 -0
- package/src/middleware/middleware.ts +289 -0
- package/src/permission/index.ts +1 -0
- package/src/permission/permission.ts +136 -0
- package/src/registry/index.ts +1 -0
- package/src/registry/registry.ts +37 -0
- package/src/route/index.ts +1 -0
- package/src/route/route.ts +12 -0
- package/src/storage/index.ts +1 -0
- package/src/storage/storage.ts +107 -0
- package/src/validation/index.ts +1 -0
- package/src/validation/validation.ts +346 -0
- 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
|
+
}
|
package/src/auth/auth.ts
ADDED
|
@@ -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
|
+
}
|