@arkstack/common 0.7.7 → 0.7.9
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/README.md +735 -20
- package/dist/helpers-DfQxjBEv.js +215 -0
- package/dist/helpers-DfQxjBEv.js.map +1 -0
- package/dist/helpers-P7NkVB6R.d.ts +33 -0
- package/dist/index.d.ts +1 -30
- package/dist/index.js +3 -212
- package/dist/index.js.map +1 -1
- package/dist/utils/index.d.ts +57 -0
- package/dist/utils/index.js +128 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +4 -3
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { Obj } from "@h3ravel/support";
|
|
2
|
+
import { createJiti } from "jiti";
|
|
3
|
+
import { createRequire } from "module";
|
|
4
|
+
import path, { resolve } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { readdirSync } from "fs";
|
|
7
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
|
|
8
|
+
import { Secret, TOTP } from "otpauth";
|
|
9
|
+
import { compare, genSalt, hash } from "bcryptjs";
|
|
10
|
+
import { getUserConfig } from "arkormx";
|
|
11
|
+
//#region src/system.ts
|
|
12
|
+
/**
|
|
13
|
+
* Read the .env file
|
|
14
|
+
*
|
|
15
|
+
* @param env
|
|
16
|
+
* @param def
|
|
17
|
+
* @returns
|
|
18
|
+
*/
|
|
19
|
+
const env = (env, defaultValue) => {
|
|
20
|
+
let val = process.env[env] ?? "";
|
|
21
|
+
if ([
|
|
22
|
+
true,
|
|
23
|
+
"true",
|
|
24
|
+
"on",
|
|
25
|
+
false,
|
|
26
|
+
"false",
|
|
27
|
+
"off"
|
|
28
|
+
].includes(val)) val = [
|
|
29
|
+
true,
|
|
30
|
+
"true",
|
|
31
|
+
"on"
|
|
32
|
+
].includes(val);
|
|
33
|
+
if (!isNaN(Number(val)) && typeof val !== "boolean" && typeof val !== "undefined" && val !== "") val = Number(val);
|
|
34
|
+
if (val === "") val = void 0;
|
|
35
|
+
if (val === "null") val = null;
|
|
36
|
+
val ??= defaultValue;
|
|
37
|
+
return val;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Build the app url
|
|
41
|
+
*
|
|
42
|
+
* @param link
|
|
43
|
+
* @returns
|
|
44
|
+
*/
|
|
45
|
+
const appUrl = (link) => {
|
|
46
|
+
const port = env("PORT") || "3000";
|
|
47
|
+
const defaultUrl = `http://localhost:${port}`;
|
|
48
|
+
const appUrl = env("APP_URL") ?? defaultUrl;
|
|
49
|
+
try {
|
|
50
|
+
const url = new URL(appUrl);
|
|
51
|
+
if (url.port || url.hostname === "localhost") url.port = port;
|
|
52
|
+
const baseUrl = url.toString().replace(/\/$/, "");
|
|
53
|
+
if (link) return `${baseUrl}${`/${link.replace(/^\/+/, "")}`}`;
|
|
54
|
+
return baseUrl;
|
|
55
|
+
} catch {
|
|
56
|
+
return link ? `${defaultUrl}/${link.replace(/^\/+/, "")}` : defaultUrl;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Gets the application configuration.
|
|
61
|
+
*
|
|
62
|
+
* @param key The configuration key to retrieve.
|
|
63
|
+
* @param defaultValue The default value to return if the key is not found.
|
|
64
|
+
* @returns The configuration value.
|
|
65
|
+
*/
|
|
66
|
+
const config = (key, defaultValue) => {
|
|
67
|
+
const dist = path.relative(process.cwd(), outputDir());
|
|
68
|
+
const require = createRequire(import.meta.url);
|
|
69
|
+
const config = readdirSync(path.join(process.cwd(), `${dist}/config`), { withFileTypes: true }).filter((file) => {
|
|
70
|
+
if (file.name.includes("middleware") && globalThis.arkctx.runtime === "CLI") return false;
|
|
71
|
+
return file.isFile() && (file.name.endsWith(".js") || file.name.endsWith(".ts"));
|
|
72
|
+
}).reduce((configs, file) => {
|
|
73
|
+
const configName = path.basename(file.name, path.extname(file.name));
|
|
74
|
+
configs[configName] = require(path.join(file.parentPath, file.name)).default(globalThis.app());
|
|
75
|
+
return configs;
|
|
76
|
+
}, {});
|
|
77
|
+
if (key) return Obj.get(config, key, defaultValue);
|
|
78
|
+
return config;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Gets the current Node environment (development or production).
|
|
82
|
+
*
|
|
83
|
+
* @returns
|
|
84
|
+
*/
|
|
85
|
+
const nodeEnv = () => {
|
|
86
|
+
let envValue = env("NODE_ENV", "development");
|
|
87
|
+
if (envValue !== "development" && envValue !== "production") envValue = "development";
|
|
88
|
+
return envValue === "production" ? "prod" : "dev";
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* Gets the output directory for the application based on the current environment.
|
|
92
|
+
*
|
|
93
|
+
* @param cwd The current working directory (optional, defaults to process.cwd()).
|
|
94
|
+
* @returns
|
|
95
|
+
*/
|
|
96
|
+
const outputDir = (cwd = process.cwd()) => {
|
|
97
|
+
const NODE_ENV = nodeEnv();
|
|
98
|
+
const output = {
|
|
99
|
+
dev: env("OUTPUT_DIR_DEV", ".arkstack/build"),
|
|
100
|
+
prod: env("OUTPUT_DIR", "dist")
|
|
101
|
+
};
|
|
102
|
+
return path.isAbsolute(output[NODE_ENV] ?? output.dev) ? output[NODE_ENV] ?? output.dev : path.join(cwd, output[NODE_ENV] ?? output.dev);
|
|
103
|
+
};
|
|
104
|
+
const importFile = async (filePath) => {
|
|
105
|
+
const resolvedPath = resolve(filePath);
|
|
106
|
+
return await createJiti(pathToFileURL(resolvedPath).href, {
|
|
107
|
+
interopDefault: false,
|
|
108
|
+
tsconfigPaths: true
|
|
109
|
+
}).import(resolvedPath);
|
|
110
|
+
};
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/utils/encryption.ts
|
|
113
|
+
var Encryption = class {
|
|
114
|
+
static algorithm = "aes-256-gcm";
|
|
115
|
+
static getKey() {
|
|
116
|
+
const secret = env("TWO_FACTOR_ENCRYPTION_KEY");
|
|
117
|
+
if (!secret) throw new Error("TWO_FACTOR_ENCRYPTION_KEY is required to use two-factor authentication");
|
|
118
|
+
return createHash("sha256").update(secret).digest();
|
|
119
|
+
}
|
|
120
|
+
static encrypt(value) {
|
|
121
|
+
const iv = randomBytes(12);
|
|
122
|
+
const cipher = createCipheriv(this.algorithm, this.getKey(), iv);
|
|
123
|
+
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
124
|
+
return [
|
|
125
|
+
iv,
|
|
126
|
+
cipher.getAuthTag(),
|
|
127
|
+
ciphertext
|
|
128
|
+
].map((part) => part.toString("base64url")).join(":");
|
|
129
|
+
}
|
|
130
|
+
static decrypt(payload) {
|
|
131
|
+
const [iv, authTag, ciphertext] = payload.split(":");
|
|
132
|
+
if (!iv || !authTag || !ciphertext) throw new Error("Invalid encrypted payload format");
|
|
133
|
+
const decipher = createDecipheriv(this.algorithm, this.getKey(), Buffer.from(iv, "base64url"));
|
|
134
|
+
decipher.setAuthTag(Buffer.from(authTag, "base64url"));
|
|
135
|
+
return Buffer.concat([decipher.update(Buffer.from(ciphertext, "base64url")), decipher.final()]).toString("utf8");
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/utils/hash.ts
|
|
140
|
+
var Hash = class {
|
|
141
|
+
/**
|
|
142
|
+
* Hash a value using bcrypt
|
|
143
|
+
*
|
|
144
|
+
* @param value
|
|
145
|
+
* @returns
|
|
146
|
+
*/
|
|
147
|
+
static async make(value) {
|
|
148
|
+
return await hash(value, await genSalt(10));
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Verify a value against a hashed value
|
|
152
|
+
*
|
|
153
|
+
* @param value
|
|
154
|
+
* @param hashedValue
|
|
155
|
+
* @returns
|
|
156
|
+
*/
|
|
157
|
+
static async verify(value, hashedValue) {
|
|
158
|
+
return await compare(value, hashedValue);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Generate a one-time password (OTP) using TOTP algorithm
|
|
162
|
+
*
|
|
163
|
+
* @param digits The number of digits for the OTP, default is 6.
|
|
164
|
+
* @param label A label to identify the OTP, can be an email or phone number.
|
|
165
|
+
* @param period Interval of time for which a token is valid, in seconds.
|
|
166
|
+
* @returns
|
|
167
|
+
*/
|
|
168
|
+
static otp(digits = 6, label = "Alice", period = 30) {
|
|
169
|
+
return new TOTP({
|
|
170
|
+
label,
|
|
171
|
+
digits,
|
|
172
|
+
issuer: env("APP_NAME", "Roseed"),
|
|
173
|
+
algorithm: "SHA1",
|
|
174
|
+
period,
|
|
175
|
+
secret: "US3WHSG7X5KAPV27VANWKQHF3SH3HULL"
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
static totp(secret, label, issuer = env("APP_NAME", "Roseed"), period = 30) {
|
|
179
|
+
return new TOTP({
|
|
180
|
+
issuer,
|
|
181
|
+
label,
|
|
182
|
+
algorithm: "SHA1",
|
|
183
|
+
digits: 6,
|
|
184
|
+
period,
|
|
185
|
+
secret: Secret.fromBase32(secret)
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
//#endregion
|
|
190
|
+
//#region src/utils/helpers.ts
|
|
191
|
+
/**
|
|
192
|
+
* Determine the number of items to return per page based on the provided query parameters.
|
|
193
|
+
*
|
|
194
|
+
* @param query
|
|
195
|
+
* @returns
|
|
196
|
+
*/
|
|
197
|
+
const perPage = (query) => {
|
|
198
|
+
const requestedPerPage = Number(query.limit ?? query.perPage ?? 15);
|
|
199
|
+
return Number.isFinite(requestedPerPage) && requestedPerPage > 0 ? Math.min(requestedPerPage, 50) : 15;
|
|
200
|
+
};
|
|
201
|
+
async function getModel(modelName) {
|
|
202
|
+
const resolveModelExport = (module, modelName) => {
|
|
203
|
+
if (!isModelModule(module)) return module;
|
|
204
|
+
return module.default ?? module[modelName] ?? module;
|
|
205
|
+
};
|
|
206
|
+
const isModelModule = (value) => typeof value === "object" && value !== null;
|
|
207
|
+
const modelPath = getUserConfig().paths?.models || "./src/models";
|
|
208
|
+
const model = resolveModelExport(await importFile(path.join(path.isAbsolute(modelPath) ? modelPath : path.join(process.cwd(), modelPath), modelName)), path.basename(modelName, path.extname(modelName)));
|
|
209
|
+
if (typeof model !== "function") throw new Error(`Model "${modelName}" not found`);
|
|
210
|
+
return model;
|
|
211
|
+
}
|
|
212
|
+
//#endregion
|
|
213
|
+
export { appUrl as a, importFile as c, Encryption as i, nodeEnv as l, perPage as n, config as o, Hash as r, env as s, getModel as t, outputDir as u };
|
|
214
|
+
|
|
215
|
+
//# sourceMappingURL=helpers-DfQxjBEv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers-DfQxjBEv.js","names":[],"sources":["../src/system.ts","../src/utils/encryption.ts","../src/utils/hash.ts","../src/utils/helpers.ts"],"sourcesContent":["import { DotPath, Obj } from '@h3ravel/support'\nimport { GlobalConfig, GlobalEnv } from './types'\n\nimport { createJiti } from 'jiti'\nimport { createRequire } from 'module'\nimport path from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { readdirSync } from 'fs'\nimport { resolve } from 'node:path'\n\n/**\n * Read the .env file\n *\n * @param env\n * @param def\n * @returns\n */\nexport const env: GlobalEnv = <X = string, Y = undefined | X> (\n env: string,\n defaultValue?: Y,\n) => {\n let val: string | number | boolean | undefined | null = process.env[env] ?? ''\n\n if ([true, 'true', 'on', false, 'false', 'off'].includes(val)) {\n val = [true, 'true', 'on'].includes(val)\n }\n\n if (!isNaN(Number(val)) && typeof val !== 'boolean' && typeof val !== 'undefined' && val !== '') {\n val = Number(val)\n }\n\n if (val === '') {\n val = undefined\n }\n\n if (val === 'null') {\n val = null\n }\n\n val ??= defaultValue as typeof val\n\n return val as Y extends undefined ? X : Y\n}\n\n/**\n * Build the app url\n *\n * @param link\n * @returns\n */\nexport const appUrl = (link?: string): string => {\n const port = env('PORT') || '3000'\n const defaultUrl = `http://localhost:${port}`\n const appUrl = env('APP_URL') ?? defaultUrl\n\n try {\n const url = new URL(appUrl)\n // Append port only if APP_URL has a port or is localhost\n if (url.port || url.hostname === 'localhost') {\n url.port = port\n }\n // Remove trailing slash from base URL\n const baseUrl = url.toString().replace(/\\/$/, '')\n // Append link with proper path separator\n if (link) {\n // Ensure link starts with '/' and remove duplicate slashes\n const normalizedLink = `/${link.replace(/^\\/+/, '')}`\n\n return `${baseUrl}${normalizedLink}`\n }\n\n return baseUrl\n } catch {\n // Return default URL with link if provided\n return link ? `${defaultUrl}/${link.replace(/^\\/+/, '')}` : defaultUrl\n }\n}\n\n/**\n * Gets the application configuration.\n * \n * @param key The configuration key to retrieve.\n * @param defaultValue The default value to return if the key is not found.\n * @returns The configuration value.\n */\nexport const config: GlobalConfig = <X extends Record<string, any>, P extends DotPath<X> | undefined = undefined> (\n key?: P,\n defaultValue?: any\n) => {\n const dist = path.relative(process.cwd(), outputDir())\n const require = createRequire(import.meta.url)\n\n const files = readdirSync(path.join(process.cwd(), `${dist}/config`), { withFileTypes: true })\n .filter(file => {\n if (file.name.includes('middleware') && globalThis.arkctx.runtime === 'CLI') return false\n\n return file.isFile() && (file.name.endsWith('.js') || file.name.endsWith('.ts'))\n })\n\n const config = files.reduce((configs, file) => {\n const configName = path.basename(file.name, path.extname(file.name))\n\n configs[configName] = require(path.join(file.parentPath, file.name))\n .default((globalThis as any).app())\n\n return configs\n }, {} as Record<string, any>) as X\n\n\n if (key) {\n return Obj.get(config, key, defaultValue)\n }\n\n return config\n}\n\n/**\n * Gets the current Node environment (development or production).\n * \n * @returns \n */\nexport const nodeEnv = () => {\n let envValue = env<'development' | 'production'>('NODE_ENV', 'development')\n\n if (envValue !== 'development' && envValue !== 'production') {\n envValue = 'development'\n }\n\n return envValue === 'production' ? 'prod' : 'dev'\n}\n\n/**\n * Gets the output directory for the application based on the current environment.\n * \n * @param cwd The current working directory (optional, defaults to process.cwd()).\n * @returns \n */\nexport const outputDir = (cwd = process.cwd()) => {\n const NODE_ENV = nodeEnv()\n\n const output = {\n dev: env('OUTPUT_DIR_DEV', '.arkstack/build'),\n prod: env('OUTPUT_DIR', 'dist'),\n }\n\n return path.isAbsolute(output[NODE_ENV] ?? output.dev)\n ? (output[NODE_ENV] ?? output.dev)\n : path.join(cwd, output[NODE_ENV] ?? output.dev)\n}\n\n\nexport const importFile = async <T = unknown> (filePath: string): Promise<T> => {\n const resolvedPath = resolve(filePath)\n const jiti = createJiti(pathToFileURL(resolvedPath).href, {\n interopDefault: false,\n tsconfigPaths: true,\n })\n\n return await jiti.import<T>(resolvedPath)\n}","import { createCipheriv, createDecipheriv, createHash, randomBytes } from 'node:crypto'\n\nimport { env } from '../system'\n\nexport class Encryption {\n private static readonly algorithm = 'aes-256-gcm'\n\n private static getKey () {\n const secret = env('TWO_FACTOR_ENCRYPTION_KEY')\n\n if (!secret) {\n throw new Error('TWO_FACTOR_ENCRYPTION_KEY is required to use two-factor authentication')\n }\n\n return createHash('sha256').update(secret).digest()\n }\n\n static encrypt (value: string) {\n const iv = randomBytes(12)\n const cipher = createCipheriv(this.algorithm, this.getKey(), iv)\n const ciphertext = Buffer.concat([\n cipher.update(value, 'utf8'),\n cipher.final(),\n ])\n const authTag = cipher.getAuthTag()\n\n return [iv, authTag, ciphertext].map((part) => part.toString('base64url')).join(':')\n }\n\n static decrypt (payload: string) {\n const [iv, authTag, ciphertext] = payload.split(':')\n\n if (!iv || !authTag || !ciphertext) {\n throw new Error('Invalid encrypted payload format')\n }\n\n const decipher = createDecipheriv(\n this.algorithm,\n this.getKey(),\n Buffer.from(iv, 'base64url'),\n )\n\n decipher.setAuthTag(Buffer.from(authTag, 'base64url'))\n\n const plaintext = Buffer.concat([\n decipher.update(Buffer.from(ciphertext, 'base64url')),\n decipher.final(),\n ])\n\n return plaintext.toString('utf8')\n }\n}","import { Secret, TOTP } from 'otpauth'\nimport { compare, genSalt, hash } from 'bcryptjs'\n\nimport { env } from '../system'\n\nexport class Hash {\n /**\n * Hash a value using bcrypt\n * \n * @param value \n * @returns \n */\n static async make (value: string): Promise<string> {\n const salt = await genSalt(10)\n\n return await hash(value, salt)\n }\n\n /**\n * Verify a value against a hashed value\n * \n * @param value \n * @param hashedValue \n * @returns \n */\n static async verify (value: string, hashedValue: string): Promise<boolean> {\n return await compare(value, hashedValue)\n }\n\n /**\n * Generate a one-time password (OTP) using TOTP algorithm\n * \n * @param digits The number of digits for the OTP, default is 6.\n * @param label A label to identify the OTP, can be an email or phone number.\n * @param period Interval of time for which a token is valid, in seconds.\n * @returns \n */\n static otp (digits: number = 6, label: string = 'Alice', period: number = 30) {\n return new TOTP({\n label,\n digits,\n issuer: env('APP_NAME', 'Roseed'),\n algorithm: 'SHA1',\n period, // in seconds.\n secret: 'US3WHSG7X5KAPV27VANWKQHF3SH3HULL',\n })\n }\n\n static totp (secret: string, label: string, issuer: string = env('APP_NAME', 'Roseed'), period: number = 30) {\n return new TOTP({\n issuer,\n label,\n algorithm: 'SHA1',\n digits: 6,\n period,\n secret: Secret.fromBase32(secret),\n })\n }\n}","import { getUserConfig, type Model, type ModelStatic } from 'arkormx'\nimport { importFile } from '../system'\nimport path from 'node:path'\n\nexport type AbstractModelConstructor<TModel = unknown> =\n abstract new (attributes?: Record<string, unknown>) => TModel\n\nexport type ModelConstructor<TModel extends Model = Model> =\n AbstractModelConstructor<TModel> &\n Pick<ModelStatic<TModel>, keyof ModelStatic<TModel>>\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ModelRegistry { }\n\ntype ModelName = Extract<keyof ModelRegistry, string>\ntype ModelModule = Record<string, unknown> & {\n default?: unknown;\n}\n\n/**\n * Determine the number of items to return per page based on the provided query parameters.\n * \n * @param query \n * @returns \n */\nexport const perPage = (query: { limit?: number; perPage?: number }) => {\n\n const requestedPerPage = Number(query.limit ?? query.perPage ?? 15)\n\n return Number.isFinite(requestedPerPage) && requestedPerPage > 0\n ? Math.min(requestedPerPage, 50)\n : 15\n}\n\n/**\n * Import an application model by name.\n *\n * Apps can augment `ModelRegistry` to make `getModel('User')` return `typeof User`.\n * Without a registry entry, pass the class type explicitly: `getModel<typeof User>('User')`.\n * \n * @param modelName \n */\nexport async function getModel<TName extends ModelName> (\n modelName: TName\n): Promise<ModelRegistry[TName]>\nexport async function getModel<TModel extends AbstractModelConstructor = ModelConstructor> (\n modelName: string\n): Promise<TModel>\nexport async function getModel (modelName: string) {\n const resolveModelExport = (module: ModelModule | unknown, modelName: string) => {\n if (!isModelModule(module)) {\n return module\n }\n\n return module.default ?? module[modelName] ?? module\n }\n\n const isModelModule = (value: unknown): value is ModelModule => (\n typeof value === 'object' && value !== null\n )\n\n const modelPath = getUserConfig().paths?.models || './src/models'\n const modulePath = path.join(\n path.isAbsolute(modelPath) ? modelPath : path.join(process.cwd(), modelPath),\n modelName\n )\n const module = await importFile<ModelModule | unknown>(modulePath)\n const exportName = path.basename(modelName, path.extname(modelName))\n const model = resolveModelExport(module, exportName)\n\n if (typeof model !== 'function') {\n throw new Error(`Model \"${modelName}\" not found`)\n }\n\n return model\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { TOTP } from "otpauth";
|
|
2
|
+
import { Model, ModelStatic } from "arkormx";
|
|
3
|
+
|
|
4
|
+
//#region src/utils/encryption.d.ts
|
|
5
|
+
declare class Encryption {
|
|
6
|
+
private static readonly algorithm;
|
|
7
|
+
private static getKey;
|
|
8
|
+
static encrypt(value: string): string;
|
|
9
|
+
static decrypt(payload: string): string;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/utils/hash.d.ts
|
|
13
|
+
declare class Hash {
|
|
14
|
+
static make(value: string): Promise<string>;
|
|
15
|
+
static verify(value: string, hashedValue: string): Promise<boolean>;
|
|
16
|
+
static otp(digits?: number, label?: string, period?: number): TOTP;
|
|
17
|
+
static totp(secret: string, label: string, issuer?: string, period?: number): TOTP;
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/utils/helpers.d.ts
|
|
21
|
+
type AbstractModelConstructor<TModel = unknown> = abstract new (attributes?: Record<string, unknown>) => TModel;
|
|
22
|
+
type ModelConstructor<TModel extends Model = Model> = AbstractModelConstructor<TModel> & Pick<ModelStatic<TModel>, keyof ModelStatic<TModel>>;
|
|
23
|
+
interface ModelRegistry {}
|
|
24
|
+
type ModelName = Extract<keyof ModelRegistry, string>;
|
|
25
|
+
declare const perPage: (query: {
|
|
26
|
+
limit?: number;
|
|
27
|
+
perPage?: number;
|
|
28
|
+
}) => number;
|
|
29
|
+
declare function getModel<TName extends ModelName>(modelName: TName): Promise<ModelRegistry[TName]>;
|
|
30
|
+
declare function getModel<TModel extends AbstractModelConstructor = ModelConstructor>(modelName: string): Promise<TModel>;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { perPage as a, getModel as i, ModelConstructor as n, Hash as o, ModelRegistry as r, Encryption as s, AbstractModelConstructor as t };
|
|
33
|
+
//# sourceMappingURL=helpers-P7NkVB6R.d.ts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/// <reference path="./app.d.ts" />
|
|
2
|
+
import { a as perPage, i as getModel, n as ModelConstructor, o as Hash, r as ModelRegistry, s as Encryption, t as AbstractModelConstructor } from "./helpers-P7NkVB6R.js";
|
|
2
3
|
import { DotPath } from "@h3ravel/support";
|
|
3
4
|
import pino from "pino";
|
|
4
5
|
import { ChalkInstance } from "chalk";
|
|
5
|
-
import { TOTP } from "otpauth";
|
|
6
|
-
import { Model, ModelStatic } from "arkormx";
|
|
7
6
|
|
|
8
7
|
//#region src/lifecycle.d.ts
|
|
9
8
|
declare const bindGracefulShutdown: (shutdown: () => Promise<void> | void) => void;
|
|
@@ -171,33 +170,5 @@ declare class Hook {
|
|
|
171
170
|
static clear: () => void;
|
|
172
171
|
}
|
|
173
172
|
//#endregion
|
|
174
|
-
//#region src/utils/encryption.d.ts
|
|
175
|
-
declare class Encryption {
|
|
176
|
-
private static readonly algorithm;
|
|
177
|
-
private static getKey;
|
|
178
|
-
static encrypt(value: string): string;
|
|
179
|
-
static decrypt(payload: string): string;
|
|
180
|
-
}
|
|
181
|
-
//#endregion
|
|
182
|
-
//#region src/utils/hash.d.ts
|
|
183
|
-
declare class Hash {
|
|
184
|
-
static make(value: string): Promise<string>;
|
|
185
|
-
static verify(value: string, hashedValue: string): Promise<boolean>;
|
|
186
|
-
static otp(digits?: number, label?: string, period?: number): TOTP;
|
|
187
|
-
static totp(secret: string, label: string, issuer?: string, period?: number): TOTP;
|
|
188
|
-
}
|
|
189
|
-
//#endregion
|
|
190
|
-
//#region src/utils/helpers.d.ts
|
|
191
|
-
type AbstractModelConstructor<TModel = unknown> = abstract new (attributes?: Record<string, unknown>) => TModel;
|
|
192
|
-
type ModelConstructor<TModel extends Model = Model> = AbstractModelConstructor<TModel> & Pick<ModelStatic<TModel>, keyof ModelStatic<TModel>>;
|
|
193
|
-
interface ModelRegistry {}
|
|
194
|
-
type ModelName = Extract<keyof ModelRegistry, string>;
|
|
195
|
-
declare const perPage: (query: {
|
|
196
|
-
limit?: number;
|
|
197
|
-
perPage?: number;
|
|
198
|
-
}) => number;
|
|
199
|
-
declare function getModel<TName extends ModelName>(modelName: TName): Promise<ModelRegistry[TName]>;
|
|
200
|
-
declare function getModel<TModel extends AbstractModelConstructor = ModelConstructor>(modelName: string): Promise<TModel>;
|
|
201
|
-
//#endregion
|
|
202
173
|
export { AbstractModelConstructor, AppException, ArkstackErrorPayload, ArkstackErrorShape, DotPathValue, Encryption, ErrorHandler, Exception, GlobalConfig, GlobalEnv, Hash, Hook, IHook, Logger, LoggerChalk, LoggerLog, LoggerParseSignature, ModelConstructor, ModelRegistry, RequestException, appUrl, bindGracefulShutdown, bootWithDetectedPort, config, createErrorPayload, env, getErrorLogger, getModel, getPrimaryError, getValidationErrors, importFile, isModelNotFoundError, isValidationError, loadPrototypes, logUnhandledError, nodeEnv, normalizeStatusCode, outputDir, perPage, renderError, serializeError, shouldHideStack, shouldLogError, toErrorShape };
|
|
203
174
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import path, { resolve } from "node:path";
|
|
5
|
-
import { pathToFileURL } from "node:url";
|
|
6
|
-
import { readdirSync } from "fs";
|
|
1
|
+
import { a as appUrl, c as importFile, i as Encryption, l as nodeEnv, n as perPage, o as config, r as Hash, s as env, t as getModel, u as outputDir } from "./helpers-DfQxjBEv.js";
|
|
2
|
+
import { str } from "@h3ravel/support";
|
|
3
|
+
import path from "node:path";
|
|
7
4
|
import { detect } from "detect-port";
|
|
8
5
|
import pino from "pino";
|
|
9
6
|
import chalk from "chalk";
|
|
10
|
-
import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
|
|
11
|
-
import { Secret, TOTP } from "otpauth";
|
|
12
|
-
import { compare, genSalt, hash } from "bcryptjs";
|
|
13
|
-
import { getUserConfig } from "arkormx";
|
|
14
7
|
//#region src/lifecycle.ts
|
|
15
8
|
const bindGracefulShutdown = (shutdown) => {
|
|
16
9
|
[
|
|
@@ -24,107 +17,6 @@ const bindGracefulShutdown = (shutdown) => {
|
|
|
24
17
|
});
|
|
25
18
|
};
|
|
26
19
|
//#endregion
|
|
27
|
-
//#region src/system.ts
|
|
28
|
-
/**
|
|
29
|
-
* Read the .env file
|
|
30
|
-
*
|
|
31
|
-
* @param env
|
|
32
|
-
* @param def
|
|
33
|
-
* @returns
|
|
34
|
-
*/
|
|
35
|
-
const env = (env, defaultValue) => {
|
|
36
|
-
let val = process.env[env] ?? "";
|
|
37
|
-
if ([
|
|
38
|
-
true,
|
|
39
|
-
"true",
|
|
40
|
-
"on",
|
|
41
|
-
false,
|
|
42
|
-
"false",
|
|
43
|
-
"off"
|
|
44
|
-
].includes(val)) val = [
|
|
45
|
-
true,
|
|
46
|
-
"true",
|
|
47
|
-
"on"
|
|
48
|
-
].includes(val);
|
|
49
|
-
if (!isNaN(Number(val)) && typeof val !== "boolean" && typeof val !== "undefined" && val !== "") val = Number(val);
|
|
50
|
-
if (val === "") val = void 0;
|
|
51
|
-
if (val === "null") val = null;
|
|
52
|
-
val ??= defaultValue;
|
|
53
|
-
return val;
|
|
54
|
-
};
|
|
55
|
-
/**
|
|
56
|
-
* Build the app url
|
|
57
|
-
*
|
|
58
|
-
* @param link
|
|
59
|
-
* @returns
|
|
60
|
-
*/
|
|
61
|
-
const appUrl = (link) => {
|
|
62
|
-
const port = env("PORT") || "3000";
|
|
63
|
-
const defaultUrl = `http://localhost:${port}`;
|
|
64
|
-
const appUrl = env("APP_URL") ?? defaultUrl;
|
|
65
|
-
try {
|
|
66
|
-
const url = new URL(appUrl);
|
|
67
|
-
if (url.port || url.hostname === "localhost") url.port = port;
|
|
68
|
-
const baseUrl = url.toString().replace(/\/$/, "");
|
|
69
|
-
if (link) return `${baseUrl}${`/${link.replace(/^\/+/, "")}`}`;
|
|
70
|
-
return baseUrl;
|
|
71
|
-
} catch {
|
|
72
|
-
return link ? `${defaultUrl}/${link.replace(/^\/+/, "")}` : defaultUrl;
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
/**
|
|
76
|
-
* Gets the application configuration.
|
|
77
|
-
*
|
|
78
|
-
* @param key The configuration key to retrieve.
|
|
79
|
-
* @param defaultValue The default value to return if the key is not found.
|
|
80
|
-
* @returns The configuration value.
|
|
81
|
-
*/
|
|
82
|
-
const config = (key, defaultValue) => {
|
|
83
|
-
const dist = path.relative(process.cwd(), outputDir());
|
|
84
|
-
const require = createRequire(import.meta.url);
|
|
85
|
-
const config = readdirSync(path.join(process.cwd(), `${dist}/config`), { withFileTypes: true }).filter((file) => {
|
|
86
|
-
if (file.name.includes("middleware") && globalThis.arkctx.runtime === "CLI") return false;
|
|
87
|
-
return file.isFile() && (file.name.endsWith(".js") || file.name.endsWith(".ts"));
|
|
88
|
-
}).reduce((configs, file) => {
|
|
89
|
-
const configName = path.basename(file.name, path.extname(file.name));
|
|
90
|
-
configs[configName] = require(path.join(file.parentPath, file.name)).default(globalThis.app());
|
|
91
|
-
return configs;
|
|
92
|
-
}, {});
|
|
93
|
-
if (key) return Obj.get(config, key, defaultValue);
|
|
94
|
-
return config;
|
|
95
|
-
};
|
|
96
|
-
/**
|
|
97
|
-
* Gets the current Node environment (development or production).
|
|
98
|
-
*
|
|
99
|
-
* @returns
|
|
100
|
-
*/
|
|
101
|
-
const nodeEnv = () => {
|
|
102
|
-
let envValue = env("NODE_ENV", "development");
|
|
103
|
-
if (envValue !== "development" && envValue !== "production") envValue = "development";
|
|
104
|
-
return envValue === "production" ? "prod" : "dev";
|
|
105
|
-
};
|
|
106
|
-
/**
|
|
107
|
-
* Gets the output directory for the application based on the current environment.
|
|
108
|
-
*
|
|
109
|
-
* @param cwd The current working directory (optional, defaults to process.cwd()).
|
|
110
|
-
* @returns
|
|
111
|
-
*/
|
|
112
|
-
const outputDir = (cwd = process.cwd()) => {
|
|
113
|
-
const NODE_ENV = nodeEnv();
|
|
114
|
-
const output = {
|
|
115
|
-
dev: env("OUTPUT_DIR_DEV", ".arkstack/build"),
|
|
116
|
-
prod: env("OUTPUT_DIR", "dist")
|
|
117
|
-
};
|
|
118
|
-
return path.isAbsolute(output[NODE_ENV] ?? output.dev) ? output[NODE_ENV] ?? output.dev : path.join(cwd, output[NODE_ENV] ?? output.dev);
|
|
119
|
-
};
|
|
120
|
-
const importFile = async (filePath) => {
|
|
121
|
-
const resolvedPath = resolve(filePath);
|
|
122
|
-
return await createJiti(pathToFileURL(resolvedPath).href, {
|
|
123
|
-
interopDefault: false,
|
|
124
|
-
tsconfigPaths: true
|
|
125
|
-
}).import(resolvedPath);
|
|
126
|
-
};
|
|
127
|
-
//#endregion
|
|
128
20
|
//#region src/network.ts
|
|
129
21
|
const bootWithDetectedPort = async (boot, preferredPort = 3e3, app) => {
|
|
130
22
|
if (app && !globalThis.app) globalThis.app = () => app;
|
|
@@ -612,107 +504,6 @@ var Hook = class {
|
|
|
612
504
|
};
|
|
613
505
|
};
|
|
614
506
|
//#endregion
|
|
615
|
-
//#region src/utils/encryption.ts
|
|
616
|
-
var Encryption = class {
|
|
617
|
-
static algorithm = "aes-256-gcm";
|
|
618
|
-
static getKey() {
|
|
619
|
-
const secret = env("TWO_FACTOR_ENCRYPTION_KEY");
|
|
620
|
-
if (!secret) throw new Error("TWO_FACTOR_ENCRYPTION_KEY is required to use two-factor authentication");
|
|
621
|
-
return createHash("sha256").update(secret).digest();
|
|
622
|
-
}
|
|
623
|
-
static encrypt(value) {
|
|
624
|
-
const iv = randomBytes(12);
|
|
625
|
-
const cipher = createCipheriv(this.algorithm, this.getKey(), iv);
|
|
626
|
-
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
627
|
-
return [
|
|
628
|
-
iv,
|
|
629
|
-
cipher.getAuthTag(),
|
|
630
|
-
ciphertext
|
|
631
|
-
].map((part) => part.toString("base64url")).join(":");
|
|
632
|
-
}
|
|
633
|
-
static decrypt(payload) {
|
|
634
|
-
const [iv, authTag, ciphertext] = payload.split(":");
|
|
635
|
-
if (!iv || !authTag || !ciphertext) throw new Error("Invalid encrypted payload format");
|
|
636
|
-
const decipher = createDecipheriv(this.algorithm, this.getKey(), Buffer.from(iv, "base64url"));
|
|
637
|
-
decipher.setAuthTag(Buffer.from(authTag, "base64url"));
|
|
638
|
-
return Buffer.concat([decipher.update(Buffer.from(ciphertext, "base64url")), decipher.final()]).toString("utf8");
|
|
639
|
-
}
|
|
640
|
-
};
|
|
641
|
-
//#endregion
|
|
642
|
-
//#region src/utils/hash.ts
|
|
643
|
-
var Hash = class {
|
|
644
|
-
/**
|
|
645
|
-
* Hash a value using bcrypt
|
|
646
|
-
*
|
|
647
|
-
* @param value
|
|
648
|
-
* @returns
|
|
649
|
-
*/
|
|
650
|
-
static async make(value) {
|
|
651
|
-
return await hash(value, await genSalt(10));
|
|
652
|
-
}
|
|
653
|
-
/**
|
|
654
|
-
* Verify a value against a hashed value
|
|
655
|
-
*
|
|
656
|
-
* @param value
|
|
657
|
-
* @param hashedValue
|
|
658
|
-
* @returns
|
|
659
|
-
*/
|
|
660
|
-
static async verify(value, hashedValue) {
|
|
661
|
-
return await compare(value, hashedValue);
|
|
662
|
-
}
|
|
663
|
-
/**
|
|
664
|
-
* Generate a one-time password (OTP) using TOTP algorithm
|
|
665
|
-
*
|
|
666
|
-
* @param digits The number of digits for the OTP, default is 6.
|
|
667
|
-
* @param label A label to identify the OTP, can be an email or phone number.
|
|
668
|
-
* @param period Interval of time for which a token is valid, in seconds.
|
|
669
|
-
* @returns
|
|
670
|
-
*/
|
|
671
|
-
static otp(digits = 6, label = "Alice", period = 30) {
|
|
672
|
-
return new TOTP({
|
|
673
|
-
label,
|
|
674
|
-
digits,
|
|
675
|
-
issuer: env("APP_NAME", "Roseed"),
|
|
676
|
-
algorithm: "SHA1",
|
|
677
|
-
period,
|
|
678
|
-
secret: "US3WHSG7X5KAPV27VANWKQHF3SH3HULL"
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
static totp(secret, label, issuer = env("APP_NAME", "Roseed"), period = 30) {
|
|
682
|
-
return new TOTP({
|
|
683
|
-
issuer,
|
|
684
|
-
label,
|
|
685
|
-
algorithm: "SHA1",
|
|
686
|
-
digits: 6,
|
|
687
|
-
period,
|
|
688
|
-
secret: Secret.fromBase32(secret)
|
|
689
|
-
});
|
|
690
|
-
}
|
|
691
|
-
};
|
|
692
|
-
//#endregion
|
|
693
|
-
//#region src/utils/helpers.ts
|
|
694
|
-
/**
|
|
695
|
-
* Determine the number of items to return per page based on the provided query parameters.
|
|
696
|
-
*
|
|
697
|
-
* @param query
|
|
698
|
-
* @returns
|
|
699
|
-
*/
|
|
700
|
-
const perPage = (query) => {
|
|
701
|
-
const requestedPerPage = Number(query.limit ?? query.perPage ?? 15);
|
|
702
|
-
return Number.isFinite(requestedPerPage) && requestedPerPage > 0 ? Math.min(requestedPerPage, 50) : 15;
|
|
703
|
-
};
|
|
704
|
-
async function getModel(modelName) {
|
|
705
|
-
const resolveModelExport = (module, modelName) => {
|
|
706
|
-
if (!isModelModule(module)) return module;
|
|
707
|
-
return module.default ?? module[modelName] ?? module;
|
|
708
|
-
};
|
|
709
|
-
const isModelModule = (value) => typeof value === "object" && value !== null;
|
|
710
|
-
const modelPath = getUserConfig().paths?.models || "./src/models";
|
|
711
|
-
const model = resolveModelExport(await importFile(path.join(path.isAbsolute(modelPath) ? modelPath : path.join(process.cwd(), modelPath), modelName)), path.basename(modelName, path.extname(modelName)));
|
|
712
|
-
if (typeof model !== "function") throw new Error(`Model "${modelName}" not found`);
|
|
713
|
-
return model;
|
|
714
|
-
}
|
|
715
|
-
//#endregion
|
|
716
507
|
export { AppException, Encryption, ErrorHandler, Exception, Hash, Hook, Logger, RequestException, appUrl, bindGracefulShutdown, bootWithDetectedPort, config, createErrorPayload, env, getErrorLogger, getModel, getPrimaryError, getValidationErrors, importFile, isModelNotFoundError, isValidationError, loadPrototypes, logUnhandledError, nodeEnv, normalizeStatusCode, outputDir, perPage, renderError, serializeError, shouldHideStack, shouldLogError, toErrorShape };
|
|
717
508
|
|
|
718
509
|
//# sourceMappingURL=index.js.map
|