@jwn-js/common 2.2.2 → 2.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ApiError.js +37 -1
- package/ApiError.mjs +35 -1
- package/Jwt-B4QZ2Ypb.js +80 -0
- package/Jwt-Be4GWrIO.js +75 -0
- package/Jwt.js +8 -1
- package/Jwt.mjs +2 -1
- package/Memcached.js +166 -9
- package/Memcached.mjs +164 -9
- package/Server.js +316 -1
- package/Server.mjs +313 -1
- package/cookieParse.js +25 -1
- package/cookieParse.mjs +23 -1
- package/cookieString.js +22 -1
- package/cookieString.mjs +20 -1
- package/docs/classes/ApiError.html +2 -2
- package/docs/classes/AsyncJwt.html +2 -2
- package/docs/classes/Controller.html +2 -2
- package/docs/classes/Jwt.html +2 -2
- package/docs/classes/Memcached.html +2 -2
- package/docs/classes/Model.html +2 -2
- package/docs/classes/Server.html +2 -2
- package/docs/classes/Ssr.html +2 -2
- package/docs/classes/Web.html +2 -2
- package/docs/functions/action.html +2 -2
- package/docs/functions/body.html +2 -2
- package/docs/functions/codeToStatus.html +2 -2
- package/docs/functions/config.html +2 -2
- package/docs/functions/connection.html +2 -2
- package/docs/functions/context.html +2 -2
- package/docs/functions/controller-1.html +2 -2
- package/docs/functions/cookies.html +2 -2
- package/docs/functions/db.html +2 -2
- package/docs/functions/headers.html +2 -2
- package/docs/functions/home.html +2 -2
- package/docs/functions/hostname.html +2 -2
- package/docs/functions/http.html +2 -2
- package/docs/functions/init.html +2 -2
- package/docs/functions/json.html +2 -2
- package/docs/functions/logerror.html +2 -2
- package/docs/functions/method.html +2 -2
- package/docs/functions/mixin.html +2 -2
- package/docs/functions/mount.html +2 -2
- package/docs/functions/pool.html +2 -2
- package/docs/functions/protocol.html +2 -2
- package/docs/functions/request.html +2 -2
- package/docs/functions/selectControllersSchema.html +1 -1
- package/docs/functions/stream.html +2 -2
- package/docs/functions/subaction.html +2 -2
- package/docs/functions/url.html +2 -2
- package/docs/functions/xml.html +2 -2
- package/docs/index.html +2 -2
- package/docs/interfaces/ApiErrorMessage.html +2 -2
- package/docs/interfaces/ContextSsr.html +2 -2
- package/docs/interfaces/ContextWeb.html +2 -2
- package/docs/interfaces/OptionsSsr.html +2 -2
- package/docs/interfaces/OptionsWeb.html +2 -2
- package/docs/interfaces/ResponseOptions.html +2 -2
- package/docs/interfaces/Route.html +2 -2
- package/docs/interfaces/Schema.html +2 -2
- package/docs/interfaces/ServerHandler.html +2 -2
- package/docs/interfaces/ServerOptions.html +2 -2
- package/docs/interfaces/ServerWebsocket.html +2 -2
- package/docs/modules.html +2 -2
- package/docs/types/ServerRoutes.html +1 -1
- package/docs/variables/helpers.html +2 -2
- package/index.js +1473 -4
- package/index.mjs +1417 -4
- package/jsonBody.js +25 -1
- package/jsonBody.mjs +23 -1
- package/multipartBody.js +42 -1
- package/multipartBody.mjs +40 -1
- package/package.json +1 -1
- package/readConfig.js +11 -1
- package/readConfig.mjs +9 -1
- package/readConfigSync.js +11 -1
- package/readConfigSync.mjs +9 -1
- package/staticBody.js +205 -1
- package/staticBody.mjs +200 -1
- package/urlencodedBody.js +26 -1
- package/urlencodedBody.mjs +24 -1
- package/Jwt-7tQL-rwa.js +0 -1
- package/Jwt-CDdbxwvH.js +0 -1
package/index.mjs
CHANGED
|
@@ -1,4 +1,222 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import { ApiError } from './ApiError.mjs';
|
|
2
|
+
import { codeToStatus } from './Server.mjs';
|
|
3
|
+
export { Server } from './Server.mjs';
|
|
4
|
+
import { t as toBase64url, f as fromBase64url, u as urlEncode } from './Jwt-Be4GWrIO.js';
|
|
5
|
+
export { J as Jwt } from './Jwt-Be4GWrIO.js';
|
|
6
|
+
import crypto, { webcrypto } from 'crypto';
|
|
7
|
+
import { TextEncoder } from 'util';
|
|
8
|
+
import { Memcached } from './Memcached.mjs';
|
|
9
|
+
import { jsonBody } from './jsonBody.mjs';
|
|
10
|
+
import { PassThrough, Stream } from 'stream';
|
|
11
|
+
import { urlencodedBody } from './urlencodedBody.mjs';
|
|
12
|
+
import { multipartBody } from './multipartBody.mjs';
|
|
13
|
+
import { readConfig } from './readConfig.mjs';
|
|
14
|
+
import { readConfigSync } from './readConfigSync.mjs';
|
|
15
|
+
import { cookieParse } from './cookieParse.mjs';
|
|
16
|
+
import { cookieString } from './cookieString.mjs';
|
|
17
|
+
import { getExt, extensions, staticBody } from './staticBody.mjs';
|
|
18
|
+
import * as fs from 'fs';
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
import path__default from 'path';
|
|
21
|
+
import { isClass, isObject, omit } from 'easy-ash';
|
|
22
|
+
import querystring from 'querystring';
|
|
23
|
+
import xmljs from 'xml-js';
|
|
24
|
+
import * as os from 'os';
|
|
25
|
+
import 'reflect-metadata';
|
|
26
|
+
import 'dns';
|
|
27
|
+
import 'uWebSockets.js';
|
|
28
|
+
import 'formidable';
|
|
29
|
+
|
|
30
|
+
const { subtle } = webcrypto;
|
|
31
|
+
class AsyncJwt {
|
|
32
|
+
/**
|
|
33
|
+
* @constructor
|
|
34
|
+
* @param secret
|
|
35
|
+
* @param opt addition options
|
|
36
|
+
*/
|
|
37
|
+
constructor(secret, opt) {
|
|
38
|
+
this.algorithm = "SHA-256";
|
|
39
|
+
this.secret = secret;
|
|
40
|
+
this.algorithm = opt?.algorithm || this.algorithm;
|
|
41
|
+
this.algorithm = this.algorithm.replace("-", "").replace("SHA", "SHA-");
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Verify jwt token
|
|
45
|
+
* @param jwt token
|
|
46
|
+
* @returns is token valid
|
|
47
|
+
*/
|
|
48
|
+
async verify(jwt) {
|
|
49
|
+
if (!jwt) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const parts = jwt.split(".");
|
|
53
|
+
const signature = await this.signString(`${parts[0]}.${parts[1]}`);
|
|
54
|
+
return signature === parts[2];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Get token
|
|
58
|
+
* @param data - user data
|
|
59
|
+
* @returns jwt
|
|
60
|
+
*/
|
|
61
|
+
async sign(data) {
|
|
62
|
+
const head = toBase64url({
|
|
63
|
+
alg: this.algorithm.replace("-", "").replace("SHA", "HS"),
|
|
64
|
+
typ: "JWT"
|
|
65
|
+
});
|
|
66
|
+
const body = toBase64url(data);
|
|
67
|
+
const signature = await this.signString(`${head}.${body}`);
|
|
68
|
+
return `${head}.${body}.${signature}`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Decode token
|
|
72
|
+
* @param jwt - jwt token
|
|
73
|
+
* @returns {head, body}
|
|
74
|
+
*/
|
|
75
|
+
decode(jwt) {
|
|
76
|
+
const parts = (jwt || "").split(".");
|
|
77
|
+
const head = fromBase64url(parts[0]);
|
|
78
|
+
const body = fromBase64url(parts[1]);
|
|
79
|
+
return { head, body };
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Sign
|
|
83
|
+
* @param str input string
|
|
84
|
+
* @returns base64 sign
|
|
85
|
+
* @private
|
|
86
|
+
*/
|
|
87
|
+
async signString(str) {
|
|
88
|
+
const enc = new TextEncoder();
|
|
89
|
+
const key = await subtle.importKey(
|
|
90
|
+
"raw",
|
|
91
|
+
enc.encode(this.secret),
|
|
92
|
+
{
|
|
93
|
+
name: "HMAC",
|
|
94
|
+
hash: {
|
|
95
|
+
name: this.algorithm
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
false,
|
|
99
|
+
["sign", "verify"]
|
|
100
|
+
);
|
|
101
|
+
return urlEncode(Buffer.from(await subtle.sign(
|
|
102
|
+
"HMAC",
|
|
103
|
+
key,
|
|
104
|
+
enc.encode(str)
|
|
105
|
+
)).toString("base64"));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const jwtDecode = (jwt) => {
|
|
110
|
+
const parts = (jwt || "").split(".");
|
|
111
|
+
const head = fromBase64url(parts[0]);
|
|
112
|
+
const body = fromBase64url(parts[1]);
|
|
113
|
+
return { head, body };
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const rawBody = (res) => new Promise((resolve, reject) => {
|
|
117
|
+
readRaw(res, (obj) => resolve(obj), () => {
|
|
118
|
+
reject(new ApiError({ message: "Can`t parse request", code: 1, statusCode: 404 }));
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
function readRaw(res, cb, err) {
|
|
122
|
+
let buffer = Buffer.from([]);
|
|
123
|
+
res.onData((ab, isLast) => {
|
|
124
|
+
buffer = Buffer.concat([buffer, Buffer.from(ab)]);
|
|
125
|
+
isLast && cb(buffer);
|
|
126
|
+
});
|
|
127
|
+
res.onAborted(err);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const streamBody = (res, req, resume = true) => {
|
|
131
|
+
res.onAborted(() => new ApiError({ message: "Connection aborted", code: 1, statusCode: 404 }));
|
|
132
|
+
const stream = new PassThrough();
|
|
133
|
+
stream.headers = {};
|
|
134
|
+
req.forEach((key, val) => stream.headers[key] = val);
|
|
135
|
+
res.onData((chunk, isLast) => {
|
|
136
|
+
stream.write(Buffer.from(Buffer.from(chunk)));
|
|
137
|
+
resume && stream.resume();
|
|
138
|
+
isLast && stream.end();
|
|
139
|
+
});
|
|
140
|
+
return stream;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const readJsonConfigsSync = (configs, input = {}) => {
|
|
144
|
+
for (const config of configs) {
|
|
145
|
+
const filePath = path.resolve(config);
|
|
146
|
+
Object.assign(input, JSON.parse(fs.readFileSync(filePath).toString()));
|
|
147
|
+
}
|
|
148
|
+
return input;
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
class Ssr {
|
|
152
|
+
/**
|
|
153
|
+
* @constructor
|
|
154
|
+
* @param res response
|
|
155
|
+
* @param req request
|
|
156
|
+
* @param context params
|
|
157
|
+
* @param entry - entry point function
|
|
158
|
+
*/
|
|
159
|
+
constructor(res, req, context, entry) {
|
|
160
|
+
this.res = res;
|
|
161
|
+
this.req = req;
|
|
162
|
+
this.context = context;
|
|
163
|
+
this.entry = entry;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* @param request addition request params
|
|
167
|
+
*/
|
|
168
|
+
async request(request = {}) {
|
|
169
|
+
this.res.onAborted(() => {
|
|
170
|
+
console.log("Abort is SSR handler");
|
|
171
|
+
});
|
|
172
|
+
const query = this.req.getQuery();
|
|
173
|
+
const url = this.req.getUrl() + (query ? `?${query}` : "");
|
|
174
|
+
const extension = getExt(url);
|
|
175
|
+
if (extensions.includes(extension)) {
|
|
176
|
+
await staticBody(this.res, this.req, "./dist/client");
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const contextSsr = {
|
|
180
|
+
hostname: this.req.getHeader("host"),
|
|
181
|
+
protocol: this.req.getHeader("x-forwarded-proto") || "http",
|
|
182
|
+
url: this.req.getUrl(),
|
|
183
|
+
cookies: cookieParse(this.req.getHeader("cookie")),
|
|
184
|
+
ip: this.req.getHeader("x-forwarded-for")?.split(/,\s+/)?.[0],
|
|
185
|
+
memcache: null,
|
|
186
|
+
statusCode: 200,
|
|
187
|
+
headers: {},
|
|
188
|
+
responseHeaders: {
|
|
189
|
+
"content-type": "text/html; charset=utf-8"
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
this.req.forEach((key2, value) => contextSsr.headers[key2] = value);
|
|
193
|
+
const key = `${contextSsr.protocol}://${contextSsr.hostname}${contextSsr.url}`;
|
|
194
|
+
let html, page = null;
|
|
195
|
+
if (this.context.memcached) {
|
|
196
|
+
page = await this.context.memcached.getPage(key);
|
|
197
|
+
}
|
|
198
|
+
if (page) {
|
|
199
|
+
html = page.data.toString();
|
|
200
|
+
contextSsr.headers = page.headers;
|
|
201
|
+
} else {
|
|
202
|
+
({ html } = await this.entry(url, {
|
|
203
|
+
manifest: this.context.manifest,
|
|
204
|
+
res: this.res,
|
|
205
|
+
req: this.req,
|
|
206
|
+
context: contextSsr
|
|
207
|
+
}));
|
|
208
|
+
if (contextSsr.statusCode === 200 && this.context.memcached) {
|
|
209
|
+
await this.context.memcached.setPage(key, contextSsr.headers, html, contextSsr.memcache);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
this.res.writeStatus(codeToStatus(contextSsr.statusCode));
|
|
213
|
+
Object.keys(contextSsr.headers).map((key2) => this.res.writeHeader(key2, contextSsr.headers[key2]));
|
|
214
|
+
this.res.end(html);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const selectRouteController = async (db, name, subaction) => {
|
|
219
|
+
return (await db.poolQuery({ sql: `
|
|
2
220
|
SELECT c.id AS controller_id,
|
|
3
221
|
c.name AS controller_name,
|
|
4
222
|
c.is_active AS controller_is_active,
|
|
@@ -17,7 +235,10 @@ import{ApiError as h}from"./ApiError.mjs";import{codeToStatus as _}from"./Server
|
|
|
17
235
|
INNER JOIN app_subactions s ON s.app_controllers_id = c.id
|
|
18
236
|
LEFT JOIN app_actions a ON a.id = s.app_actions_id
|
|
19
237
|
WHERE c.name = :name AND s.name = :subaction
|
|
20
|
-
`,namedPlaceholders
|
|
238
|
+
`, namedPlaceholders: true }, { name, subaction }))?.[0];
|
|
239
|
+
};
|
|
240
|
+
const selectAll = async (db) => {
|
|
241
|
+
const rows = await db.poolQuery(`
|
|
21
242
|
SELECT c.id AS controller_id,
|
|
22
243
|
c.name AS controller_name,
|
|
23
244
|
c.is_active AS controller_is_active,
|
|
@@ -35,7 +256,113 @@ import{ApiError as h}from"./ApiError.mjs";import{codeToStatus as _}from"./Server
|
|
|
35
256
|
FROM app_controllers c
|
|
36
257
|
INNER JOIN app_subactions s ON s.app_controllers_id = c.id
|
|
37
258
|
LEFT JOIN app_actions a ON a.id = s.app_actions_id;
|
|
38
|
-
`)
|
|
259
|
+
`);
|
|
260
|
+
return rows.reduce((ac, row) => {
|
|
261
|
+
ac[`${row.controller_name}::${row.subaction_name}`] = row;
|
|
262
|
+
return ac;
|
|
263
|
+
}, {});
|
|
264
|
+
};
|
|
265
|
+
const selectRouteControllerMemcached = async (db, name, subaction, memcached, memcachedPrefix = "", expires = 3600) => {
|
|
266
|
+
const key = `${memcachedPrefix}::routes::controllers`;
|
|
267
|
+
const index = `${name}::${subaction}`;
|
|
268
|
+
const schema = await memcached.getValue(key);
|
|
269
|
+
if (schema) {
|
|
270
|
+
const json = JSON.parse(schema.toString());
|
|
271
|
+
return (json || {}).hasOwnProperty(index) ? json[index] : void 0;
|
|
272
|
+
} else {
|
|
273
|
+
const all = await selectAll(db);
|
|
274
|
+
await memcached.setValue(key, Buffer.from(JSON.stringify(all)), expires);
|
|
275
|
+
return (all || {}).hasOwnProperty(index) ? all[index] : void 0;
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
const selectControllerParams = async (db, method, name, subaction, memcached, memcachedPrefix = "", expires = 3600) => {
|
|
279
|
+
let row;
|
|
280
|
+
if (memcached && memcached.isClient() && memcached.isConnectedServers()) {
|
|
281
|
+
row = await selectRouteControllerMemcached(db, name, subaction, memcached, memcachedPrefix, expires);
|
|
282
|
+
} else {
|
|
283
|
+
row = await selectRouteController(db, name, subaction);
|
|
284
|
+
}
|
|
285
|
+
if (!row) {
|
|
286
|
+
throw new ApiError({
|
|
287
|
+
statusCode: 404,
|
|
288
|
+
code: 11,
|
|
289
|
+
message: `Controller ${name} not found in site schema`
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
const allowMethods = (row?.action_method || "").split(",").map((method2) => method2.trim());
|
|
293
|
+
if (row.controller_is_active !== 1) {
|
|
294
|
+
throw new ApiError({
|
|
295
|
+
statusCode: 404,
|
|
296
|
+
code: 11,
|
|
297
|
+
message: `Controller ${name} not active in site schema`
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
if (!row.action_id) {
|
|
301
|
+
throw new ApiError({
|
|
302
|
+
statusCode: 404,
|
|
303
|
+
code: 11,
|
|
304
|
+
message: `Action not found in site schema`
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
if (row.action_is_active !== 1) {
|
|
308
|
+
throw new ApiError({
|
|
309
|
+
statusCode: 404,
|
|
310
|
+
code: 11,
|
|
311
|
+
message: `Action ${row.action_name} not active in site schema`
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
if (!row.subaction_id) {
|
|
315
|
+
throw new ApiError({
|
|
316
|
+
statusCode: 404,
|
|
317
|
+
code: 11,
|
|
318
|
+
message: `Subaction ${subaction} not found in site schema`
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
if (row.subaction_is_active !== 1) {
|
|
322
|
+
throw new ApiError({
|
|
323
|
+
statusCode: 404,
|
|
324
|
+
code: 11,
|
|
325
|
+
message: `Subaction ${subaction} not active in site schema`
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
if (row.subaction_is_check_method !== 0) {
|
|
329
|
+
if (row.action_method !== "any" && !allowMethods.includes(method)) {
|
|
330
|
+
throw new ApiError({
|
|
331
|
+
statusCode: 404,
|
|
332
|
+
code: 11,
|
|
333
|
+
message: `Controller ${name} action ${row.action_name} allow only ${row.action_method} method`
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
controller: {
|
|
339
|
+
id: +row.controller_id,
|
|
340
|
+
name: row.controller_name,
|
|
341
|
+
isActive: !!row.controller_is_active,
|
|
342
|
+
isSitemap: !!row.controller_is_sitemap
|
|
343
|
+
},
|
|
344
|
+
action: {
|
|
345
|
+
id: +row.action_id,
|
|
346
|
+
name: row.action_name,
|
|
347
|
+
isActive: !!row.action_is_active,
|
|
348
|
+
method: row.action_method
|
|
349
|
+
},
|
|
350
|
+
subaction: {
|
|
351
|
+
id: +row.subaction_id,
|
|
352
|
+
name: row.subaction_name,
|
|
353
|
+
isPermission: !!row.subaction_is_permission,
|
|
354
|
+
isCheckMethod: !!row.subaction_is_check_method,
|
|
355
|
+
isLog: !!row.subaction_is_log,
|
|
356
|
+
isActive: !!row.subaction_is_active,
|
|
357
|
+
isSync: false
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
const selectControllersSchema = async (pool) => {
|
|
363
|
+
const actions = {};
|
|
364
|
+
const controllers = {};
|
|
365
|
+
const rows = await pool.poolQuery(`
|
|
39
366
|
SELECT c.id AS controller_id,
|
|
40
367
|
c.name AS controller_name,
|
|
41
368
|
c.is_active AS controller_is_active,
|
|
@@ -54,4 +381,1090 @@ import{ApiError as h}from"./ApiError.mjs";import{codeToStatus as _}from"./Server
|
|
|
54
381
|
FROM app_controllers c
|
|
55
382
|
INNER JOIN app_subactions s ON s.app_controllers_id = c.id
|
|
56
383
|
LEFT JOIN app_actions a ON a.id = s.app_actions_id;
|
|
57
|
-
`)).forEach(s=>{e[s.action_name]={id:s.action_id,name:s.action_name,isActive:!!s.action_is_active,method:s.action_method},t[s.controller_name]=t[s.controller_name]||{id:s.controller_id,name:s.controller_name,isActive:!!s.controller_is_active,isSitemap:!!s.controller_is_sitemap,subactions:[]},t[s.controller_name].subactions.push({id:s.subaction_id,action:s.action_name,name:s.subaction_name,isPermission:!!s.subaction_is_permission,isCheckMethod:!!s.subaction_is_check_method,isLog:!!s.subaction_is_log,isActive:!!s.subaction_is_active,isSync:!!s.subaction_is_sync})}),{controllers:Object.values(t),actions:Object.values(e)}},I=(o,e,t,s)=>{let n=o.controllers.find(d=>d.name===t);if(!n)throw new h({statusCode:404,code:11,message:`Controller ${t} not found is site schema`});if(n=Object.assign({isActive:!0,isSitemap:!1},n),!n.isActive)throw new h({statusCode:404,code:11,message:`Controller ${t} not active is site schema`});let i=n.subactions.find(d=>d.name===s);if(!i)throw new h({statusCode:404,code:11,message:`Subaction ${s} not found is site schema`});if(i=Object.assign({isPermission:!1,isCheckMethod:!1,isLog:!1,isActive:!0},i),!i.isActive)throw new h({statusCode:404,code:11,message:`Subaction ${s} not active is site schema`});let a=o.actions.find(d=>d.name===i.action);if(!a)throw new h({statusCode:404,code:11,message:"Action not found is site schema"});if(a=Object.assign({isActive:!0,method:"any"},a),!a.isActive)throw new h({statusCode:404,code:11,message:`Action ${a.name} not active is site schema`});const r=(a?.method||"").split(",").map(d=>d.trim());if(i.isCheckMethod&&a.method!=="any"&&!r.includes(e))throw new h({statusCode:404,code:11,message:`Controller ${t} action ${a.name} allow only ${a.method} method`});return{controller:{id:n.id,name:n.name,isActive:n.isActive,isSitemap:n.isSitemap},subaction:{id:i.id,name:i.name,isPermission:i.isPermission,isCheckMethod:i.isCheckMethod,isLog:i.isLog,isActive:i.isActive,isSync:!!i.isSync},action:{id:a.id,name:a.name,isActive:a.isActive,method:a.method}}};class ft{constructor(e,t,s){this.defaultRequest={lang:"ru"},this.defaultResponse={statusCode:200,headers:{"content-type":"application/json"},body:{}},this.defaultControllerSubaction={controller:"Index",subaction:"index"},this.res=e,this.req=t,this.res.onAborted(()=>new h({message:"The connection was close",code:1,statusCode:404})),this.context=s}setDefaultRequest(e){return Object.assign(this.defaultRequest,e),this}async request(e={}){let t={};try{const s=this.req.getQuery(),n=Object.assign({},this.defaultRequest,e,ot.parse(this.req.getQuery()));this.contextWeb={config:this.context.config,db:this.context.db,method:this.req.getMethod(),cookies:this.req.cookies,hostname:this.req.getHeader("host"),protocol:this.req.getHeader("x-forwarded-proto")||"http",url:this.req.getUrl()+(s?`?${s}`:""),headers:{},stack:this.context.stack||{},getRequest:()=>n},this.memcachedKey=`${this.contextWeb.protocol}://${this.contextWeb.hostname}${this.contextWeb.url}`,this.req.forEach((c,l)=>this.contextWeb.headers[c]=l);const i=this.contextWeb.headers["content-type"]||"application/json";if(n.controller&&n.subaction&&this.context.schema&&(this.route=this.findRoute(n.controller),Object.defineProperty(this.contextWeb,"$route",{enumerable:!1,configurable:!1,writable:!1,value:this.route}),Object.assign(this.contextWeb,I(this.context.schema,this.contextWeb.method,this.route.name,n.subaction)),this.contextWeb.subaction.isSync)){["get","head"].includes(this.contextWeb.method)||Object.defineProperty(this.contextWeb,"$stream",{enumerable:!1,configurable:!1,writable:!1,value:w(this.res,this.req,!1)});const c=this.importControllerSync(n.controller),l=new c;if(this.injectContext(l),n.subaction in l)t=await l[n.subaction]();else throw new h({statusCode:404,code:13,message:`Method ${n.subaction} not found in ${n.controller} route`});t=Object.assign({},this.defaultResponse,t),this.success(t);return}if(["get","head"].includes(this.contextWeb.method)&&this.contextWeb?.stack?.memcached instanceof $){const c=this.contextWeb.stack.memcached;if(c.isConnectedServers()){const l=await c.getPage(this.memcachedKey);if(l){const{data:b,headers:g}=l;this.success({body:b.toString(),headers:g,statusCode:200})}}}if(n.controller=n.controller||this.defaultControllerSubaction.controller,n.subaction=n.subaction||this.defaultControllerSubaction.subaction,!["get","head"].includes(this.contextWeb.method))if(i.indexOf("application/json")!==-1||i.indexOf("text/json")!==-1)Object.assign(n,await O(this.res));else if(i.indexOf("multipart/form-data")!==-1)Object.assign(n,await R(this.res,this.req)),Object.defineProperty(this.contextWeb,"$files",{enumerable:!1,configurable:!1,writable:!1,value:n.files});else if(i.indexOf("application/x-www-form-urlencoded")!==-1)Object.assign(n,await H(this.res));else if(i.indexOf("application/xml")!==-1||i.indexOf("text/xml")!==-1){const c=(await C(this.res)).toString(),l=S.xml2js(c,{compact:!0,cdataKey:"_value",textKey:"_value"});Object.assign(n,l||{})}else i.indexOf("stream")!==-1?Object.defineProperty(this.contextWeb,"$stream",{enumerable:!1,configurable:!1,writable:!1,value:w(this.res,this.req,!1)}):i.indexOf("binary")!==-1?await new Promise(c=>{const l=w(this.res,this.req),b=nt.tmpdir(),g="./body_"+V.randomBytes(32).toString("hex"),u=tt.resolve(b,g),p=M.createWriteStream(u);l.pipe(p),Object.defineProperty(this.contextWeb,"$files",{enumerable:!1,configurable:!1,writable:!1,value:{body:{path:u}}}),l.on("end",()=>c(!0))}):Object.defineProperty(this.contextWeb,"$body",{enumerable:!1,configurable:!1,writable:!1,value:await C(this.res)});this.route=this.findRoute(n.controller),Object.assign(this.contextWeb,this.context.schema?I(this.context.schema,this.contextWeb.method,this.route.name,n.subaction):await J(this.contextWeb.db.home,this.contextWeb.method,this.route.name,n.subaction,this.context.stack?.memcached,this.context.stack?.memcachedPrefix,this.context.stack?.memcachedExpiry)),Object.defineProperty(this.contextWeb,"$route",{enumerable:!1,configurable:!1,writable:!1,value:this.route});const a=await this.importController(n.controller),r=new a;this.injectContext(r);const d=await this.initComponent(r);if(typeof d>"u")if(n.subaction in r)t=await r[n.subaction]();else throw new h({statusCode:404,code:13,message:`Method ${n.subaction} not found in ${n.controller} route`});else t=d;t=Object.assign({},this.defaultResponse,t),this.success(t)}catch(s){this.error(s)}}async importController(e){let t=this.route.component.default||this.route.component;if(et(t)||(t=(await this.route.component()).default),typeof t!="function")throw new h({statusCode:404,code:12,message:`Class not found for ${e} route`});return t}importControllerSync(e){const t=this.route.component.default||this.route.component;if(typeof t!="function")throw new h({statusCode:404,code:12,message:`Class not found for ${e} route`});return t}findRoute(e){const t=this.context.routes.filter(s=>s.name===e&&(s.method===this.contextWeb.method||s.method==="any"||Array.isArray(s.method)&&s.method.includes(this.contextWeb.method)||typeof s.method>"u"))?.[0];if(!t)throw new h({statusCode:404,code:11,message:`Route ${e} not found`});return t}injectContext(e){"$inject"in e&&e.$inject(this.contextWeb)}async initComponent(e){const t=[...new Set(["init",...e.$inits||[]])];for(const s of t)if(e[s]){const n=await e[s]();if(n)return n}}success(e){const t=e.body instanceof k,s=e.body instanceof Uint8Array,n=e.statusCode||200,i=_(n),a=!s&&!t&&B(e.body)?JSON.stringify(e.body):e.body;!t&&["get","head"].includes(this.contextWeb.method)&&n===200&&e.memcache&&this.contextWeb?.stack?.memcached instanceof $&&this.contextWeb.stack.memcached.setPage(this.memcachedKey,e.headers,a,e.memcache).then(),t?this.res.cork(()=>{this.res.writeStatus(i),this.writeHeaders(e.headers),a.on("data",r=>{this.res.write(r)}).on("end",()=>{this.res.end()})}):this.res.cork(()=>{this.res.writeStatus(i),this.writeHeaders(e.headers),this.res.end(a)})}error(e){const t=e instanceof h&&e.getData()instanceof k;let s=_(404),n=this.defaultResponse.headers,i=e.message;if(e instanceof h){const a={isError:!0,code:e.getCode(),error:e.getMessage()};s=_(e.getStatusCode()),n=e.getHeaders()||this.defaultResponse.headers,t?i=e.getData():i=B(e.getData())&&Object.keys(e.getData()).length>0?JSON.stringify(e.getData()):JSON.stringify(a)}t?this.res.cork(()=>{this.res.writeStatus(s),this.writeHeaders(n),i.on("data",a=>{this.res.write(a)}).on("end",()=>{this.res.end()})}):this.res.cork(()=>{this.res.writeStatus(s),this.writeHeaders(n),this.res.end(i)})}writeHeaders(e){Object.entries(e).forEach(([t,s])=>{Array.isArray(s)?s.forEach(n=>this.res.writeHeader(t,n)):this.res.writeHeader(t,s)})}}const pt=async(o,e,t={})=>{const s=async a=>{let r;return"init"in a&&(r=await a.init()),r},n=(a,r)=>{"$inject"in a&&a.$inject(r)};e=Object.assign({config:{},getRequest:()=>t,method:"get",...t.controller&&t.subaction&&e.db?.home?await J(e.db.home,e.method||"get",t.controller,t.subaction):{},cookies:{},hostname:e.config?.server?.development?.host,protocol:"http",headers:{}},e);const i=new o;if(n(i,e),await s(i))throw new h({statusCode:404,code:13,message:"init method response value"});return i},L={compact:!0,ignoreComment:!0,spaces:4};class bt{constructor(){this.responseHeaders={}}setCookieHeader(e,t,s={}){return P(e,t,s)}success(e={},t){return{headers:Object.assign({"content-type":"application/json"},this.responseHeaders,t?.headers||{}),body:{isError:!1,data:e},memcache:t?.memcache||null,statusCode:t?.statusCode||200}}error(e){const t=e,s=t.code?t.code:0,n=t.message?t.message:"Api request error",i=t.statusCode?t.statusCode:404,a=t.data?t.data:{},r=t.response?{status:t.response.status,headers:t.response.headers,config:{url:t.response.url,method:t.response.method,params:t.response.params,headers:t.response.headers},data:t.response.data}:{};return{headers:Object.assign({"content-type":"application/json"},t.headers),body:{isError:!0,error:n,code:s,data:a,stack:process.env.NODE_ENV!=="production"?t.stack:"",response:process.env.NODE_ENV!=="production"?r:{}},statusCode:i}}successXml(e={},t){const s={_declaration:{_attributes:{version:"1.0",encoding:"utf-8"}}},n=this.success(e,t);return{headers:Object.assign({"content-type":"application/xml"},this.responseHeaders,t?.headers||{}),body:S.js2xml(Object.assign(s,{body:{...n.body}}),L),memcache:n.memcache}}errorXml(e){const t=e,s={_declaration:{_attributes:{version:"1.0",encoding:"utf-8"}}},n=this.error(t);return{headers:Object.assign({"content-type":"application/xml"},t.headers),body:S.js2xml(Object.assign(s,{body:{...n.body}}),L),statusCode:n.statusCode}}async $create(e,...t){const s=new e(...t);"$inject"in s&&s.$inject({method:this.$context.method,getRequest:this.$context.getRequest,config:this.$context.config,db:this.$context.db,stack:this.$context.stack,headers:this.$context.headers,cookies:this.$context.cookies,controller:this.$context.controller,action:this.$context.action,subaction:this.$context.subaction,setResponseHeader:(i,a)=>this.setResponseHeader(i,a),setCookieHeader:this.setCookieHeader});const n=["init",...s.$inits||[]];for(const i of n)s[i]&&await s[i]();return s}async $createAll(e){return Promise.all(e.map(t=>this.$create(...t)))}$inject(e){this.$context=e}getContext(){return this.$context}getRequest(){return this.$context.getRequest()}getBody(){return this.$context.$body||Buffer.from("")}getFiles(){return this.$context.$files||{}}getStream(){return this.$context.$stream||new j().end()}getConfig(){return this.$context.config}getMethod(){return this.$context.method}getCookies(){return this.$context.cookies}getHostname(){return this.$context.hostname}getUrl(){return this.$context.url}getProtocol(){return this.$context.protocol}getDb(){return this.$context.db}getHeaders(){return this.$context.headers}getController(){return this.$context.controller}getAction(){return this.$context.action}getSubaction(){return this.$context.subaction}getHome(){return this.getDb().home}getPool(e="home"){return this.getDb()[e]}getStack(){return this.$context.stack}setResponseHeader(e,t){this.responseHeaders.hasOwnProperty(e)?(this.responseHeaders[e]=Array.isArray(this.responseHeaders[e])?this.responseHeaders[e]:[this.responseHeaders[e]],this.responseHeaders[e].push(t)):this.responseHeaders[e]=t}}class gt{async $create(e,...t){const s=new e(...t);return"$inject"in s&&s.$inject({method:this.$context.method,getRequest:this.$context.getRequest,config:this.$context.config,db:this.$context.db,stack:this.$context.stack,headers:this.$context.headers,cookies:this.$context.cookies,controller:this.$context.controller,action:this.$context.action,subaction:this.$context.subaction,setResponseHeader:this.$context.setResponseHeader,setCookieHeader:this.$context.setCookieHeader}),"init"in s&&await s.init(),s}async $createAll(e){return Promise.all(e.map(t=>this.$create(...t)))}$inject(e){this.$context=e}getContext(){return this.$context}getRequest(){return this.$context.getRequest()}getConfig(){return this.$context.config}getMethod(){return this.$context.method}getHeaders(){return this.$context.headers}getCookies(){return this.$context.cookies}getController(){return this.$context.controller}getAction(){return this.$context.action}getSubaction(){return this.$context.subaction}getDb(){return this.$context.db}getHome(){return this.$context.db.home}getPool(e="home"){return this.getDb()[e]}getStack(){return this.$context.stack}setResponseHeader(e,t){return this.$context.setResponseHeader(e,t)}setCookieHeader(e,t,s={}){return this.$context.setCookieHeader(e,t,s)}}const f=Symbol("arguments"),T=Symbol("connections"),A=Symbol("response"),F=Symbol("init"),K=Symbol("logger"),_t=o=>o.charAt(0).toUpperCase()+o.slice(1);function yt(o,e={}){const t=Reflect.ownKeys(o),s=Reflect.ownKeys(e),n=Symbol("isa");function i(a){for(const r of t)Object.defineProperty(a.prototype,r,{value:o[r]});return Object.defineProperty(a.prototype,n,{value:!0}),a}for(const a of s)Object.defineProperty(i,a,{value:e[a],enumerable:e.propertyIsEnumerable(a)});return Object.defineProperty(i,Symbol.hasInstance,{value:a=>!!a[n]}),i}const m=(o,e,t,s,n,i)=>{const a=Reflect.getOwnMetadata(o,e,t)||{};a[s]={type:n,params:i},Reflect.defineMetadata(o,a,e,t)};function xt(){return function(o,e,t){m(f,o,e,t,"request")}}function wt(){return function(o,e,t){m(f,o,e,t,"context")}}function $t(){return function(o,e,t){m(f,o,e,t,"config")}}function St(){return function(o,e,t){m(f,o,e,t,"method")}}function Ct(){return function(o,e,t){m(f,o,e,t,"cookies")}}function At(){return function(o,e,t){m(f,o,e,t,"hostname")}}function vt(){return function(o,e,t){m(f,o,e,t,"url")}}function Ot(){return function(o,e,t){m(f,o,e,t,"protocol")}}function jt(){return function(o,e,t){m(f,o,e,t,"db")}}function kt(){return function(o,e,t){m(f,o,e,t,"headers")}}function Ht(){return function(o,e,t){m(f,o,e,t,"controller")}}function Rt(){return function(o,e,t){m(f,o,e,t,"action")}}function qt(){return function(o,e,t){m(f,o,e,t,"subaction")}}function Pt(){return function(o,e,t){m(f,o,e,t,"body")}}function Wt(){return function(o,e,t){m(f,o,e,t,"stream")}}function Et(){return function(o,e,t){m(f,o,e,t,"home")}}function Nt(o="home"){return function(e,t,s){m(f,e,t,s,"pool",o)}}function Mt(o="home"){return function(e,t,s){m(T,e,t,s,"connection",o)}}function Bt(o={}){return function(e,t,s){m(A,e,t,0,"json",o)}}function Dt(o={}){return function(e,t,s){m(A,e,t,0,"xml",o)}}function Jt(o){return function(e,t,s){m(K,e,t,o,"logger",o)}}function It(o){return function(e,t,s){const n={format:"json"},i=s.value;s.value=async function(){o&&Object.assign(n,await o.apply(this)||{});const a=[],r=[],d=Reflect.getOwnMetadata(f,e,t)||{},c=Reflect.getOwnMetadata(T,e,t)||{},l=Reflect.getOwnMetadata(K,e,t)||{},b=Reflect.getOwnMetadata(A,e,t)?.[0]||{type:"json",params:{}},g=Reflect.getOwnMetadata(F,e,t)?.[0]||{type:"",params:{}};for(const u in d){if(!d.hasOwnProperty(u))continue;const p=`get${_t(d[u].type)}`;a[+u]=this[p](d[u].params)}for(const u in c)c.hasOwnProperty(u)&&(c[u].type,a[+u]=await this.getDb()[c[u].params].getConnection(),r.push(a[+u]));try{let u=await i.apply(this,a),p;if(Array.isArray(u)&&([u,p]=u),g.type!=="init")return n.format==="xml"||b.type==="xml"?this.successXml(u,Object.assign(b.params,p||{})):this.success(u,Object.assign(b.params,p||{}))}catch(u){await Promise.all([...r.map(y=>y.rollback())]);let p;n.format==="xml"||b.type==="xml"?p=this.errorXml(u):p=this.error(u);for(const y in l){if(!l.hasOwnProperty(y))continue;const Q=l[y].params;await this[Q].error(u,{response:st(p?.body||{},["stack"])})}return p}finally{await Promise.all([...r.map(u=>u.release())])}}}}function Lt(){return function(o,e,t){m(F,o,e,0,"init"),t.value,"$inits"in o||Object.defineProperty(o,"$inits",{value:[]}),o.$inits.push(e)}}const Tt={cookieParse:q,cookieString:P,jsonBody:O,rawBody:C,streamBody:w,urlencodedBody:H,multipartBody:R,readConfig:G,readConfigSync:Y,staticBody:N,extensions:E,getExt:W,jwtDecode:rt,readJsonConfigsSync:ct};export{h as ApiError,it as AsyncJwt,bt as Controller,ge as Jwt,$ as Memcached,gt as Model,pe as Server,ht as Ssr,ft as Web,Rt as action,Pt as body,_ as codeToStatus,$t as config,Mt as connection,wt as context,Ht as controller,Ct as cookies,jt as db,kt as headers,Tt as helpers,Et as home,At as hostname,It as http,Lt as init,Bt as json,Jt as logerror,St as method,yt as mixin,pt as mount,Nt as pool,Ot as protocol,xt as request,lt as selectControllersSchema,Wt as stream,qt as subaction,vt as url,Dt as xml};
|
|
384
|
+
`);
|
|
385
|
+
rows.forEach((row) => {
|
|
386
|
+
actions[row.action_name] = {
|
|
387
|
+
id: row.action_id,
|
|
388
|
+
name: row.action_name,
|
|
389
|
+
isActive: !!row.action_is_active,
|
|
390
|
+
method: row.action_method
|
|
391
|
+
};
|
|
392
|
+
controllers[row.controller_name] = controllers[row.controller_name] || {
|
|
393
|
+
id: row.controller_id,
|
|
394
|
+
name: row.controller_name,
|
|
395
|
+
isActive: !!row.controller_is_active,
|
|
396
|
+
isSitemap: !!row.controller_is_sitemap,
|
|
397
|
+
subactions: []
|
|
398
|
+
};
|
|
399
|
+
controllers[row.controller_name].subactions.push({
|
|
400
|
+
id: row.subaction_id,
|
|
401
|
+
action: row.action_name,
|
|
402
|
+
name: row.subaction_name,
|
|
403
|
+
isPermission: !!row.subaction_is_permission,
|
|
404
|
+
isCheckMethod: !!row.subaction_is_check_method,
|
|
405
|
+
isLog: !!row.subaction_is_log,
|
|
406
|
+
isActive: !!row.subaction_is_active,
|
|
407
|
+
isSync: !!row.subaction_is_sync
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
return {
|
|
411
|
+
controllers: Object.values(controllers),
|
|
412
|
+
actions: Object.values(actions)
|
|
413
|
+
};
|
|
414
|
+
};
|
|
415
|
+
const schemaControllerParams = (schema, method, name, subaction) => {
|
|
416
|
+
let controllerRow = schema.controllers.find((row) => row.name === name);
|
|
417
|
+
if (!controllerRow) {
|
|
418
|
+
throw new ApiError({
|
|
419
|
+
statusCode: 404,
|
|
420
|
+
code: 11,
|
|
421
|
+
message: `Controller ${name} not found is site schema`
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
controllerRow = Object.assign({
|
|
425
|
+
isActive: true,
|
|
426
|
+
isSitemap: false
|
|
427
|
+
}, controllerRow);
|
|
428
|
+
if (!controllerRow.isActive) {
|
|
429
|
+
throw new ApiError({
|
|
430
|
+
statusCode: 404,
|
|
431
|
+
code: 11,
|
|
432
|
+
message: `Controller ${name} not active is site schema`
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
let subactionRow = controllerRow.subactions.find((row) => row.name === subaction);
|
|
436
|
+
if (!subactionRow) {
|
|
437
|
+
throw new ApiError({
|
|
438
|
+
statusCode: 404,
|
|
439
|
+
code: 11,
|
|
440
|
+
message: `Subaction ${subaction} not found is site schema`
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
subactionRow = Object.assign({
|
|
444
|
+
isPermission: false,
|
|
445
|
+
isCheckMethod: false,
|
|
446
|
+
isLog: false,
|
|
447
|
+
isActive: true
|
|
448
|
+
}, subactionRow);
|
|
449
|
+
if (!subactionRow.isActive) {
|
|
450
|
+
throw new ApiError({
|
|
451
|
+
statusCode: 404,
|
|
452
|
+
code: 11,
|
|
453
|
+
message: `Subaction ${subaction} not active is site schema`
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
let actionRow = schema.actions.find((row) => row.name === subactionRow.action);
|
|
457
|
+
if (!actionRow) {
|
|
458
|
+
throw new ApiError({
|
|
459
|
+
statusCode: 404,
|
|
460
|
+
code: 11,
|
|
461
|
+
message: `Action not found is site schema`
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
actionRow = Object.assign({
|
|
465
|
+
isActive: true,
|
|
466
|
+
method: "any"
|
|
467
|
+
}, actionRow);
|
|
468
|
+
if (!actionRow.isActive) {
|
|
469
|
+
throw new ApiError({
|
|
470
|
+
statusCode: 404,
|
|
471
|
+
code: 11,
|
|
472
|
+
message: `Action ${actionRow.name} not active is site schema`
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
const allowMethods = (actionRow?.method || "").split(",").map((method2) => method2.trim());
|
|
476
|
+
if (subactionRow.isCheckMethod) {
|
|
477
|
+
if (actionRow.method !== "any" && !allowMethods.includes(method)) {
|
|
478
|
+
throw new ApiError({
|
|
479
|
+
statusCode: 404,
|
|
480
|
+
code: 11,
|
|
481
|
+
message: `Controller ${name} action ${actionRow.name} allow only ${actionRow.method} method`
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return {
|
|
486
|
+
controller: {
|
|
487
|
+
id: controllerRow.id,
|
|
488
|
+
name: controllerRow.name,
|
|
489
|
+
isActive: controllerRow.isActive,
|
|
490
|
+
isSitemap: controllerRow.isSitemap
|
|
491
|
+
},
|
|
492
|
+
subaction: {
|
|
493
|
+
id: subactionRow.id,
|
|
494
|
+
name: subactionRow.name,
|
|
495
|
+
isPermission: subactionRow.isPermission,
|
|
496
|
+
isCheckMethod: subactionRow.isCheckMethod,
|
|
497
|
+
isLog: subactionRow.isLog,
|
|
498
|
+
isActive: subactionRow.isActive,
|
|
499
|
+
isSync: !!subactionRow.isSync
|
|
500
|
+
},
|
|
501
|
+
action: {
|
|
502
|
+
id: actionRow.id,
|
|
503
|
+
name: actionRow.name,
|
|
504
|
+
isActive: actionRow.isActive,
|
|
505
|
+
method: actionRow.method
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
class Web {
|
|
511
|
+
constructor(res, req, context) {
|
|
512
|
+
this.defaultRequest = {
|
|
513
|
+
lang: "ru"
|
|
514
|
+
};
|
|
515
|
+
this.defaultResponse = {
|
|
516
|
+
statusCode: 200,
|
|
517
|
+
headers: { "content-type": "application/json" },
|
|
518
|
+
body: {}
|
|
519
|
+
};
|
|
520
|
+
this.defaultControllerSubaction = {
|
|
521
|
+
controller: "Index",
|
|
522
|
+
subaction: "index"
|
|
523
|
+
};
|
|
524
|
+
this.res = res;
|
|
525
|
+
this.req = req;
|
|
526
|
+
this.res.onAborted(() => new ApiError({ message: "The connection was close", code: 1, statusCode: 404 }));
|
|
527
|
+
this.context = context;
|
|
528
|
+
}
|
|
529
|
+
setDefaultRequest(params) {
|
|
530
|
+
Object.assign(this.defaultRequest, params);
|
|
531
|
+
return this;
|
|
532
|
+
}
|
|
533
|
+
async request(input = {}) {
|
|
534
|
+
let response = {};
|
|
535
|
+
try {
|
|
536
|
+
const query = this.req.getQuery();
|
|
537
|
+
const request = Object.assign(
|
|
538
|
+
{},
|
|
539
|
+
this.defaultRequest,
|
|
540
|
+
input,
|
|
541
|
+
querystring.parse(this.req.getQuery())
|
|
542
|
+
);
|
|
543
|
+
this.contextWeb = {
|
|
544
|
+
config: this.context.config,
|
|
545
|
+
db: this.context.db,
|
|
546
|
+
method: this.req.getMethod(),
|
|
547
|
+
cookies: this.req.cookies,
|
|
548
|
+
hostname: this.req.getHeader("host"),
|
|
549
|
+
protocol: this.req.getHeader("x-forwarded-proto") || "http",
|
|
550
|
+
url: this.req.getUrl() + (query ? `?${query}` : ""),
|
|
551
|
+
headers: {},
|
|
552
|
+
stack: this.context.stack || {},
|
|
553
|
+
getRequest: () => request
|
|
554
|
+
};
|
|
555
|
+
this.memcachedKey = `${this.contextWeb.protocol}://${this.contextWeb.hostname}${this.contextWeb.url}`;
|
|
556
|
+
this.req.forEach((key, value) => this.contextWeb.headers[key] = value);
|
|
557
|
+
const type = this.contextWeb.headers["content-type"] || "application/json";
|
|
558
|
+
if (request.controller && request.subaction && this.context.schema) {
|
|
559
|
+
this.route = this.findRoute(request.controller);
|
|
560
|
+
Object.defineProperty(this.contextWeb, "$route", {
|
|
561
|
+
enumerable: false,
|
|
562
|
+
configurable: false,
|
|
563
|
+
writable: false,
|
|
564
|
+
value: this.route
|
|
565
|
+
});
|
|
566
|
+
Object.assign(
|
|
567
|
+
this.contextWeb,
|
|
568
|
+
schemaControllerParams(
|
|
569
|
+
this.context.schema,
|
|
570
|
+
this.contextWeb.method,
|
|
571
|
+
this.route.name,
|
|
572
|
+
// Определяет контроллер
|
|
573
|
+
request.subaction
|
|
574
|
+
)
|
|
575
|
+
);
|
|
576
|
+
if (this.contextWeb.subaction.isSync) {
|
|
577
|
+
if (!["get", "head"].includes(this.contextWeb.method)) {
|
|
578
|
+
Object.defineProperty(this.contextWeb, "$stream", {
|
|
579
|
+
enumerable: false,
|
|
580
|
+
configurable: false,
|
|
581
|
+
writable: false,
|
|
582
|
+
value: streamBody(this.res, this.req, false)
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
const controllerClass2 = this.importControllerSync(request.controller);
|
|
586
|
+
const controller2 = new controllerClass2();
|
|
587
|
+
this.injectContext(controller2);
|
|
588
|
+
if (request.subaction in controller2) {
|
|
589
|
+
response = await controller2[request.subaction]();
|
|
590
|
+
} else {
|
|
591
|
+
throw new ApiError(
|
|
592
|
+
{
|
|
593
|
+
statusCode: 404,
|
|
594
|
+
code: 13,
|
|
595
|
+
message: `Method ${request.subaction} not found in ${request.controller} route`
|
|
596
|
+
}
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
response = Object.assign({}, this.defaultResponse, response);
|
|
600
|
+
this.success(response);
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
if (["get", "head"].includes(this.contextWeb.method) && this.contextWeb?.stack?.memcached instanceof Memcached) {
|
|
605
|
+
const memcached = this.contextWeb.stack.memcached;
|
|
606
|
+
if (memcached.isConnectedServers()) {
|
|
607
|
+
const cache = await memcached.getPage(this.memcachedKey);
|
|
608
|
+
if (cache) {
|
|
609
|
+
const { data, headers } = cache;
|
|
610
|
+
this.success({
|
|
611
|
+
body: data.toString(),
|
|
612
|
+
headers,
|
|
613
|
+
statusCode: 200
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
request.controller = request.controller || this.defaultControllerSubaction.controller;
|
|
619
|
+
request.subaction = request.subaction || this.defaultControllerSubaction.subaction;
|
|
620
|
+
if (!["get", "head"].includes(this.contextWeb.method)) {
|
|
621
|
+
if (type.indexOf("application/json") !== -1 || type.indexOf("text/json") !== -1) {
|
|
622
|
+
Object.assign(request, await jsonBody(this.res));
|
|
623
|
+
} else if (type.indexOf("multipart/form-data") !== -1) {
|
|
624
|
+
Object.assign(request, await multipartBody(this.res, this.req));
|
|
625
|
+
Object.defineProperty(this.contextWeb, "$files", {
|
|
626
|
+
enumerable: false,
|
|
627
|
+
configurable: false,
|
|
628
|
+
writable: false,
|
|
629
|
+
value: request.files
|
|
630
|
+
});
|
|
631
|
+
} else if (type.indexOf("application/x-www-form-urlencoded") !== -1) {
|
|
632
|
+
Object.assign(request, await urlencodedBody(this.res));
|
|
633
|
+
} else if (type.indexOf("application/xml") !== -1 || type.indexOf("text/xml") !== -1) {
|
|
634
|
+
const raw = (await rawBody(this.res)).toString();
|
|
635
|
+
const records = xmljs.xml2js(raw, { compact: true, cdataKey: "_value", textKey: "_value" });
|
|
636
|
+
Object.assign(request, records || {});
|
|
637
|
+
} else if (type.indexOf("stream") !== -1) {
|
|
638
|
+
Object.defineProperty(this.contextWeb, "$stream", {
|
|
639
|
+
enumerable: false,
|
|
640
|
+
configurable: false,
|
|
641
|
+
writable: false,
|
|
642
|
+
value: streamBody(this.res, this.req, false)
|
|
643
|
+
});
|
|
644
|
+
} else if (type.indexOf("binary") !== -1) {
|
|
645
|
+
await new Promise((resolve) => {
|
|
646
|
+
const stream = streamBody(this.res, this.req);
|
|
647
|
+
const temp = os.tmpdir();
|
|
648
|
+
const name = "./body_" + crypto.randomBytes(32).toString("hex");
|
|
649
|
+
const pathToFile = path__default.resolve(temp, name);
|
|
650
|
+
const writer = fs.createWriteStream(pathToFile);
|
|
651
|
+
stream.pipe(writer);
|
|
652
|
+
Object.defineProperty(this.contextWeb, "$files", {
|
|
653
|
+
enumerable: false,
|
|
654
|
+
configurable: false,
|
|
655
|
+
writable: false,
|
|
656
|
+
value: { body: { path: pathToFile } }
|
|
657
|
+
});
|
|
658
|
+
stream.on("end", () => resolve(true));
|
|
659
|
+
});
|
|
660
|
+
} else {
|
|
661
|
+
Object.defineProperty(this.contextWeb, "$body", {
|
|
662
|
+
enumerable: false,
|
|
663
|
+
configurable: false,
|
|
664
|
+
writable: false,
|
|
665
|
+
value: await rawBody(this.res)
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
this.route = this.findRoute(request.controller);
|
|
670
|
+
Object.assign(
|
|
671
|
+
this.contextWeb,
|
|
672
|
+
this.context.schema ? schemaControllerParams(
|
|
673
|
+
this.context.schema,
|
|
674
|
+
this.contextWeb.method,
|
|
675
|
+
this.route.name,
|
|
676
|
+
// Определяет контроллер
|
|
677
|
+
request.subaction
|
|
678
|
+
) : await selectControllerParams(
|
|
679
|
+
this.contextWeb.db.home,
|
|
680
|
+
this.contextWeb.method,
|
|
681
|
+
this.route.name,
|
|
682
|
+
request.subaction,
|
|
683
|
+
this.context.stack?.memcached,
|
|
684
|
+
this.context.stack?.memcachedPrefix,
|
|
685
|
+
this.context.stack?.memcachedExpiry
|
|
686
|
+
)
|
|
687
|
+
);
|
|
688
|
+
Object.defineProperty(this.contextWeb, "$route", {
|
|
689
|
+
enumerable: false,
|
|
690
|
+
configurable: false,
|
|
691
|
+
writable: false,
|
|
692
|
+
value: this.route
|
|
693
|
+
});
|
|
694
|
+
const controllerClass = await this.importController(request.controller);
|
|
695
|
+
const controller = new controllerClass();
|
|
696
|
+
this.injectContext(controller);
|
|
697
|
+
const initResponse = await this.initComponent(controller);
|
|
698
|
+
if (typeof initResponse === "undefined") {
|
|
699
|
+
if (request.subaction in controller) {
|
|
700
|
+
response = await controller[request.subaction]();
|
|
701
|
+
} else {
|
|
702
|
+
throw new ApiError(
|
|
703
|
+
{
|
|
704
|
+
statusCode: 404,
|
|
705
|
+
code: 13,
|
|
706
|
+
message: `Method ${request.subaction} not found in ${request.controller} route`
|
|
707
|
+
}
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
} else {
|
|
711
|
+
response = initResponse;
|
|
712
|
+
}
|
|
713
|
+
response = Object.assign({}, this.defaultResponse, response);
|
|
714
|
+
this.success(response);
|
|
715
|
+
} catch (e) {
|
|
716
|
+
this.error(e);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* Import controller class
|
|
721
|
+
*/
|
|
722
|
+
async importController(controllerName) {
|
|
723
|
+
let controllerClass = this.route.component.default || this.route.component;
|
|
724
|
+
if (!isClass(controllerClass)) {
|
|
725
|
+
controllerClass = (await this.route.component()).default;
|
|
726
|
+
}
|
|
727
|
+
if (typeof controllerClass !== "function") {
|
|
728
|
+
throw new ApiError(
|
|
729
|
+
{
|
|
730
|
+
statusCode: 404,
|
|
731
|
+
code: 12,
|
|
732
|
+
message: `Class not found for ${controllerName} route`
|
|
733
|
+
}
|
|
734
|
+
);
|
|
735
|
+
}
|
|
736
|
+
return controllerClass;
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Sync controller
|
|
740
|
+
*/
|
|
741
|
+
importControllerSync(controllerName) {
|
|
742
|
+
const controllerClass = this.route.component.default || this.route.component;
|
|
743
|
+
if (typeof controllerClass !== "function") {
|
|
744
|
+
throw new ApiError(
|
|
745
|
+
{
|
|
746
|
+
statusCode: 404,
|
|
747
|
+
code: 12,
|
|
748
|
+
message: `Class not found for ${controllerName} route`
|
|
749
|
+
}
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
return controllerClass;
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Find route in api routers
|
|
756
|
+
*/
|
|
757
|
+
findRoute(controllerName) {
|
|
758
|
+
const route = this.context.routes.filter(
|
|
759
|
+
(v) => v.name === controllerName && (v.method === this.contextWeb.method || v.method === "any" || Array.isArray(v.method) && v.method.includes(this.contextWeb.method) || typeof v.method === "undefined")
|
|
760
|
+
)?.[0];
|
|
761
|
+
if (!route) {
|
|
762
|
+
throw new ApiError(
|
|
763
|
+
{
|
|
764
|
+
statusCode: 404,
|
|
765
|
+
code: 11,
|
|
766
|
+
message: `Route ${controllerName} not found`
|
|
767
|
+
}
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
return route;
|
|
771
|
+
}
|
|
772
|
+
injectContext(obj) {
|
|
773
|
+
if ("$inject" in obj) {
|
|
774
|
+
obj.$inject(this.contextWeb);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
async initComponent(obj) {
|
|
778
|
+
const inits = [.../* @__PURE__ */ new Set(["init", ...obj.$inits || []])];
|
|
779
|
+
for (const method of inits) {
|
|
780
|
+
if (obj[method]) {
|
|
781
|
+
const out = await obj[method]();
|
|
782
|
+
if (out) {
|
|
783
|
+
return out;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
success(response) {
|
|
789
|
+
const isStream = response.body instanceof Stream;
|
|
790
|
+
const isBuffer = response.body instanceof Uint8Array;
|
|
791
|
+
const code = response.statusCode || 200;
|
|
792
|
+
const status = codeToStatus(code);
|
|
793
|
+
const body = !isBuffer && !isStream && isObject(response.body) ? JSON.stringify(response.body) : response.body;
|
|
794
|
+
if (!isStream && ["get", "head"].includes(this.contextWeb.method) && code === 200 && response.memcache && this.contextWeb?.stack?.memcached instanceof Memcached) {
|
|
795
|
+
const memcached = this.contextWeb.stack.memcached;
|
|
796
|
+
memcached.setPage(this.memcachedKey, response.headers, body, response.memcache).then();
|
|
797
|
+
}
|
|
798
|
+
if (!isStream) {
|
|
799
|
+
this.res.cork(() => {
|
|
800
|
+
this.res.writeStatus(status);
|
|
801
|
+
this.writeHeaders(response.headers);
|
|
802
|
+
this.res.end(body);
|
|
803
|
+
});
|
|
804
|
+
} else {
|
|
805
|
+
this.res.cork(() => {
|
|
806
|
+
this.res.writeStatus(status);
|
|
807
|
+
this.writeHeaders(response.headers);
|
|
808
|
+
body.on("data", (chunk) => {
|
|
809
|
+
this.res.write(chunk);
|
|
810
|
+
}).on("end", () => {
|
|
811
|
+
this.res.end();
|
|
812
|
+
});
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Error response
|
|
818
|
+
*/
|
|
819
|
+
error(e) {
|
|
820
|
+
const isStream = e instanceof ApiError && e.getData() instanceof Stream;
|
|
821
|
+
let status = codeToStatus(404), headers = this.defaultResponse.headers, body = e.message;
|
|
822
|
+
if (e instanceof ApiError) {
|
|
823
|
+
const data = { isError: true, code: e.getCode(), error: e.getMessage() };
|
|
824
|
+
status = codeToStatus(e.getStatusCode());
|
|
825
|
+
headers = e.getHeaders() || this.defaultResponse.headers;
|
|
826
|
+
if (isStream) {
|
|
827
|
+
body = e.getData();
|
|
828
|
+
} else {
|
|
829
|
+
body = isObject(e.getData()) && Object.keys(e.getData()).length > 0 ? JSON.stringify(e.getData()) : JSON.stringify(data);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
if (!isStream) {
|
|
833
|
+
this.res.cork(() => {
|
|
834
|
+
this.res.writeStatus(status);
|
|
835
|
+
this.writeHeaders(headers);
|
|
836
|
+
this.res.end(body);
|
|
837
|
+
});
|
|
838
|
+
} else {
|
|
839
|
+
this.res.cork(() => {
|
|
840
|
+
this.res.writeStatus(status);
|
|
841
|
+
this.writeHeaders(headers);
|
|
842
|
+
body.on("data", (chunk) => {
|
|
843
|
+
this.res.write(chunk);
|
|
844
|
+
}).on("end", () => {
|
|
845
|
+
this.res.end();
|
|
846
|
+
});
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
writeHeaders(headers) {
|
|
851
|
+
Object.entries(headers).forEach(([key, value]) => {
|
|
852
|
+
if (Array.isArray(value)) {
|
|
853
|
+
value.forEach((v) => this.res.writeHeader(key, v));
|
|
854
|
+
} else {
|
|
855
|
+
this.res.writeHeader(key, value);
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
const mountWithContext = async ($class, $context, request = {}) => {
|
|
862
|
+
const initComponent = async (obj2) => {
|
|
863
|
+
let out;
|
|
864
|
+
if ("init" in obj2) {
|
|
865
|
+
out = await obj2.init();
|
|
866
|
+
}
|
|
867
|
+
return out;
|
|
868
|
+
};
|
|
869
|
+
const injectContext = (obj2, $context2) => {
|
|
870
|
+
if ("$inject" in obj2) {
|
|
871
|
+
obj2.$inject($context2);
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
$context = Object.assign({
|
|
875
|
+
config: {},
|
|
876
|
+
getRequest: () => request,
|
|
877
|
+
method: "get",
|
|
878
|
+
...request.controller && request.subaction && $context.db?.home ? await selectControllerParams(
|
|
879
|
+
$context.db.home,
|
|
880
|
+
$context.method || "get",
|
|
881
|
+
request.controller,
|
|
882
|
+
request.subaction
|
|
883
|
+
) : {},
|
|
884
|
+
cookies: {},
|
|
885
|
+
hostname: $context.config?.server?.development?.host,
|
|
886
|
+
protocol: "http",
|
|
887
|
+
headers: {}
|
|
888
|
+
}, $context);
|
|
889
|
+
const obj = new $class();
|
|
890
|
+
injectContext(obj, $context);
|
|
891
|
+
const initResponse = await initComponent(obj);
|
|
892
|
+
if (initResponse) {
|
|
893
|
+
throw new ApiError(
|
|
894
|
+
{
|
|
895
|
+
statusCode: 404,
|
|
896
|
+
code: 13,
|
|
897
|
+
message: `init method response value`
|
|
898
|
+
}
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
return obj;
|
|
902
|
+
};
|
|
903
|
+
|
|
904
|
+
const xmlOptions = { compact: true, ignoreComment: true, spaces: 4 };
|
|
905
|
+
class Controller {
|
|
906
|
+
constructor() {
|
|
907
|
+
// Addition headers that can be add in any place
|
|
908
|
+
this.responseHeaders = {};
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Set cookies
|
|
912
|
+
* @param name
|
|
913
|
+
* @param value
|
|
914
|
+
* @param options
|
|
915
|
+
*/
|
|
916
|
+
setCookieHeader(name, value, options = {}) {
|
|
917
|
+
return cookieString(name, value, options);
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Success request
|
|
921
|
+
* @param data response params
|
|
922
|
+
* @param options
|
|
923
|
+
* @returns
|
|
924
|
+
*/
|
|
925
|
+
success(data = {}, options) {
|
|
926
|
+
return {
|
|
927
|
+
headers: Object.assign({ "content-type": "application/json" }, this.responseHeaders, options?.headers || {}),
|
|
928
|
+
body: {
|
|
929
|
+
isError: false,
|
|
930
|
+
data
|
|
931
|
+
},
|
|
932
|
+
memcache: options?.memcache || null,
|
|
933
|
+
statusCode: options?.statusCode || 200
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Error response
|
|
938
|
+
* @param err
|
|
939
|
+
*/
|
|
940
|
+
error(err) {
|
|
941
|
+
const e = err;
|
|
942
|
+
const code = e.code ? e.code : 0;
|
|
943
|
+
const message = e.message ? e.message : "Api request error";
|
|
944
|
+
const statusCode = e.statusCode ? e.statusCode : 404;
|
|
945
|
+
const data = e.data ? e.data : {};
|
|
946
|
+
const response = e.response ? {
|
|
947
|
+
// @ts-ignore
|
|
948
|
+
status: e.response.status,
|
|
949
|
+
// @ts-ignore
|
|
950
|
+
headers: e.response.headers,
|
|
951
|
+
config: {
|
|
952
|
+
// @ts-ignore
|
|
953
|
+
url: e.response.url,
|
|
954
|
+
// @ts-ignore
|
|
955
|
+
method: e.response.method,
|
|
956
|
+
// @ts-ignore
|
|
957
|
+
params: e.response.params,
|
|
958
|
+
// @ts-ignore
|
|
959
|
+
headers: e.response.headers
|
|
960
|
+
},
|
|
961
|
+
// @ts-ignore
|
|
962
|
+
data: e.response.data
|
|
963
|
+
} : {};
|
|
964
|
+
const headers = Object.assign({ "content-type": "application/json" }, e.headers);
|
|
965
|
+
return {
|
|
966
|
+
headers,
|
|
967
|
+
body: {
|
|
968
|
+
isError: true,
|
|
969
|
+
error: message,
|
|
970
|
+
code,
|
|
971
|
+
data,
|
|
972
|
+
stack: process.env.NODE_ENV !== "production" ? e.stack : "",
|
|
973
|
+
response: process.env.NODE_ENV !== "production" ? response : {}
|
|
974
|
+
},
|
|
975
|
+
statusCode
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
/**
|
|
979
|
+
* Xml response
|
|
980
|
+
* @param data response params
|
|
981
|
+
* @param options addition options
|
|
982
|
+
*/
|
|
983
|
+
successXml(data = {}, options) {
|
|
984
|
+
const defaultXml = {
|
|
985
|
+
_declaration: {
|
|
986
|
+
_attributes: {
|
|
987
|
+
version: "1.0",
|
|
988
|
+
encoding: "utf-8"
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
const response = this.success(data, options);
|
|
993
|
+
return {
|
|
994
|
+
headers: Object.assign({ "content-type": "application/xml" }, this.responseHeaders, options?.headers || {}),
|
|
995
|
+
body: xmljs.js2xml(Object.assign(defaultXml, { body: { ...response.body } }), xmlOptions),
|
|
996
|
+
memcache: response.memcache
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Error xml response
|
|
1001
|
+
* @param err error object
|
|
1002
|
+
*/
|
|
1003
|
+
errorXml(err) {
|
|
1004
|
+
const e = err;
|
|
1005
|
+
const defaultXml = {
|
|
1006
|
+
_declaration: {
|
|
1007
|
+
_attributes: {
|
|
1008
|
+
version: "1.0",
|
|
1009
|
+
encoding: "utf-8"
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
};
|
|
1013
|
+
const response = this.error(e);
|
|
1014
|
+
const headers = Object.assign({ "content-type": "application/xml" }, e.headers);
|
|
1015
|
+
return {
|
|
1016
|
+
headers,
|
|
1017
|
+
body: xmljs.js2xml(Object.assign(defaultXml, { body: { ...response.body } }), xmlOptions),
|
|
1018
|
+
statusCode: response.statusCode
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
// Create model
|
|
1022
|
+
async $create(model, ...args) {
|
|
1023
|
+
const obj = new model(...args);
|
|
1024
|
+
if ("$inject" in obj) {
|
|
1025
|
+
obj.$inject({
|
|
1026
|
+
method: this.$context.method,
|
|
1027
|
+
getRequest: this.$context.getRequest,
|
|
1028
|
+
config: this.$context.config,
|
|
1029
|
+
db: this.$context.db,
|
|
1030
|
+
stack: this.$context.stack,
|
|
1031
|
+
headers: this.$context.headers,
|
|
1032
|
+
cookies: this.$context.cookies,
|
|
1033
|
+
controller: this.$context.controller,
|
|
1034
|
+
action: this.$context.action,
|
|
1035
|
+
subaction: this.$context.subaction,
|
|
1036
|
+
setResponseHeader: (key, value) => this.setResponseHeader(key, value),
|
|
1037
|
+
setCookieHeader: this.setCookieHeader
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
const inits = ["init", ...obj.$inits || []];
|
|
1041
|
+
for (const method of inits) {
|
|
1042
|
+
if (obj[method]) {
|
|
1043
|
+
await obj[method]();
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
return obj;
|
|
1047
|
+
}
|
|
1048
|
+
// Create group of libs
|
|
1049
|
+
async $createAll(models) {
|
|
1050
|
+
return Promise.all(models.map((params) => this.$create(...params)));
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Inject app context
|
|
1054
|
+
* @param context
|
|
1055
|
+
*/
|
|
1056
|
+
$inject(context) {
|
|
1057
|
+
this.$context = context;
|
|
1058
|
+
}
|
|
1059
|
+
getContext() {
|
|
1060
|
+
return this.$context;
|
|
1061
|
+
}
|
|
1062
|
+
getRequest() {
|
|
1063
|
+
return this.$context.getRequest();
|
|
1064
|
+
}
|
|
1065
|
+
getBody() {
|
|
1066
|
+
return this.$context["$body"] || Buffer.from("");
|
|
1067
|
+
}
|
|
1068
|
+
getFiles() {
|
|
1069
|
+
return this.$context["$files"] || {};
|
|
1070
|
+
}
|
|
1071
|
+
getStream() {
|
|
1072
|
+
return this.$context["$stream"] || new PassThrough().end();
|
|
1073
|
+
}
|
|
1074
|
+
getConfig() {
|
|
1075
|
+
return this.$context.config;
|
|
1076
|
+
}
|
|
1077
|
+
getMethod() {
|
|
1078
|
+
return this.$context.method;
|
|
1079
|
+
}
|
|
1080
|
+
getCookies() {
|
|
1081
|
+
return this.$context.cookies;
|
|
1082
|
+
}
|
|
1083
|
+
getHostname() {
|
|
1084
|
+
return this.$context.hostname;
|
|
1085
|
+
}
|
|
1086
|
+
getUrl() {
|
|
1087
|
+
return this.$context.url;
|
|
1088
|
+
}
|
|
1089
|
+
getProtocol() {
|
|
1090
|
+
return this.$context.protocol;
|
|
1091
|
+
}
|
|
1092
|
+
getDb() {
|
|
1093
|
+
return this.$context.db;
|
|
1094
|
+
}
|
|
1095
|
+
getHeaders() {
|
|
1096
|
+
return this.$context.headers;
|
|
1097
|
+
}
|
|
1098
|
+
getController() {
|
|
1099
|
+
return this.$context.controller;
|
|
1100
|
+
}
|
|
1101
|
+
getAction() {
|
|
1102
|
+
return this.$context.action;
|
|
1103
|
+
}
|
|
1104
|
+
getSubaction() {
|
|
1105
|
+
return this.$context.subaction;
|
|
1106
|
+
}
|
|
1107
|
+
getHome() {
|
|
1108
|
+
return this.getDb().home;
|
|
1109
|
+
}
|
|
1110
|
+
getPool(key = "home") {
|
|
1111
|
+
return this.getDb()[key];
|
|
1112
|
+
}
|
|
1113
|
+
getStack() {
|
|
1114
|
+
return this.$context.stack;
|
|
1115
|
+
}
|
|
1116
|
+
setResponseHeader(key, value) {
|
|
1117
|
+
if (this.responseHeaders.hasOwnProperty(key)) {
|
|
1118
|
+
this.responseHeaders[key] = Array.isArray(this.responseHeaders[key]) ? this.responseHeaders[key] : [this.responseHeaders[key]];
|
|
1119
|
+
this.responseHeaders[key].push(value);
|
|
1120
|
+
} else {
|
|
1121
|
+
this.responseHeaders[key] = value;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
class Model {
|
|
1127
|
+
/**
|
|
1128
|
+
* Create model
|
|
1129
|
+
* @param model
|
|
1130
|
+
* @param args
|
|
1131
|
+
*/
|
|
1132
|
+
async $create(model, ...args) {
|
|
1133
|
+
const obj = new model(...args);
|
|
1134
|
+
if ("$inject" in obj) {
|
|
1135
|
+
obj.$inject({
|
|
1136
|
+
method: this.$context.method,
|
|
1137
|
+
getRequest: this.$context.getRequest,
|
|
1138
|
+
config: this.$context.config,
|
|
1139
|
+
db: this.$context.db,
|
|
1140
|
+
stack: this.$context.stack,
|
|
1141
|
+
headers: this.$context.headers,
|
|
1142
|
+
cookies: this.$context.cookies,
|
|
1143
|
+
controller: this.$context.controller,
|
|
1144
|
+
action: this.$context.action,
|
|
1145
|
+
subaction: this.$context.subaction,
|
|
1146
|
+
setResponseHeader: this.$context.setResponseHeader,
|
|
1147
|
+
setCookieHeader: this.$context.setCookieHeader
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
if ("init" in obj) {
|
|
1151
|
+
await obj.init();
|
|
1152
|
+
}
|
|
1153
|
+
return obj;
|
|
1154
|
+
}
|
|
1155
|
+
// Create group of libs
|
|
1156
|
+
async $createAll(models) {
|
|
1157
|
+
return Promise.all(models.map((params) => this.$create(...params)));
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Inject app context
|
|
1161
|
+
* @param context
|
|
1162
|
+
*/
|
|
1163
|
+
$inject(context) {
|
|
1164
|
+
this.$context = context;
|
|
1165
|
+
}
|
|
1166
|
+
getContext() {
|
|
1167
|
+
return this.$context;
|
|
1168
|
+
}
|
|
1169
|
+
getRequest() {
|
|
1170
|
+
return this.$context.getRequest();
|
|
1171
|
+
}
|
|
1172
|
+
getConfig() {
|
|
1173
|
+
return this.$context.config;
|
|
1174
|
+
}
|
|
1175
|
+
getMethod() {
|
|
1176
|
+
return this.$context.method;
|
|
1177
|
+
}
|
|
1178
|
+
getHeaders() {
|
|
1179
|
+
return this.$context.headers;
|
|
1180
|
+
}
|
|
1181
|
+
getCookies() {
|
|
1182
|
+
return this.$context.cookies;
|
|
1183
|
+
}
|
|
1184
|
+
getController() {
|
|
1185
|
+
return this.$context.controller;
|
|
1186
|
+
}
|
|
1187
|
+
getAction() {
|
|
1188
|
+
return this.$context.action;
|
|
1189
|
+
}
|
|
1190
|
+
getSubaction() {
|
|
1191
|
+
return this.$context.subaction;
|
|
1192
|
+
}
|
|
1193
|
+
getDb() {
|
|
1194
|
+
return this.$context.db;
|
|
1195
|
+
}
|
|
1196
|
+
getHome() {
|
|
1197
|
+
return this.$context.db.home;
|
|
1198
|
+
}
|
|
1199
|
+
getPool(key = "home") {
|
|
1200
|
+
return this.getDb()[key];
|
|
1201
|
+
}
|
|
1202
|
+
getStack() {
|
|
1203
|
+
return this.$context.stack;
|
|
1204
|
+
}
|
|
1205
|
+
setResponseHeader(key, value) {
|
|
1206
|
+
return this.$context.setResponseHeader(key, value);
|
|
1207
|
+
}
|
|
1208
|
+
setCookieHeader(name, value, options = {}) {
|
|
1209
|
+
return this.$context.setCookieHeader(name, value, options);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
const argumentsSubactionsKey = Symbol("arguments");
|
|
1214
|
+
const argumentsConnectionsKey = Symbol("connections");
|
|
1215
|
+
const responseSubactionsKey = Symbol("response");
|
|
1216
|
+
const responseInitKey = Symbol("init");
|
|
1217
|
+
const responseLoggerKey = Symbol("logger");
|
|
1218
|
+
const upperCaseFirstLetter = (str) => str.charAt(0).toUpperCase() + str.slice(1);
|
|
1219
|
+
function mixin(behaviour, sharedBehaviour = {}) {
|
|
1220
|
+
const instanceKeys = Reflect.ownKeys(behaviour);
|
|
1221
|
+
const sharedKeys = Reflect.ownKeys(sharedBehaviour);
|
|
1222
|
+
const typeTag = Symbol("isa");
|
|
1223
|
+
function _mixin(clazz) {
|
|
1224
|
+
for (const property of instanceKeys) {
|
|
1225
|
+
Object.defineProperty(clazz.prototype, property, { value: behaviour[property] });
|
|
1226
|
+
}
|
|
1227
|
+
Object.defineProperty(clazz.prototype, typeTag, { value: true });
|
|
1228
|
+
return clazz;
|
|
1229
|
+
}
|
|
1230
|
+
for (const property of sharedKeys) {
|
|
1231
|
+
Object.defineProperty(_mixin, property, {
|
|
1232
|
+
//@ts-ignore
|
|
1233
|
+
value: sharedBehaviour[property],
|
|
1234
|
+
enumerable: sharedBehaviour.propertyIsEnumerable(property)
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
Object.defineProperty(_mixin, Symbol.hasInstance, {
|
|
1238
|
+
value: (i) => !!i[typeTag]
|
|
1239
|
+
});
|
|
1240
|
+
return _mixin;
|
|
1241
|
+
}
|
|
1242
|
+
const defineMetadataArgument = (key, target, propertyKey, parameterIndex, argumentType, params) => {
|
|
1243
|
+
const existingArguments = Reflect.getOwnMetadata(
|
|
1244
|
+
key,
|
|
1245
|
+
target,
|
|
1246
|
+
propertyKey
|
|
1247
|
+
) || {};
|
|
1248
|
+
existingArguments[parameterIndex] = {
|
|
1249
|
+
type: argumentType,
|
|
1250
|
+
params
|
|
1251
|
+
};
|
|
1252
|
+
Reflect.defineMetadata(key, existingArguments, target, propertyKey);
|
|
1253
|
+
};
|
|
1254
|
+
function request() {
|
|
1255
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1256
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "request");
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
function context() {
|
|
1260
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1261
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "context");
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
function config() {
|
|
1265
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1266
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "config");
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
function method() {
|
|
1270
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1271
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "method");
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
function cookies() {
|
|
1275
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1276
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "cookies");
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
function hostname() {
|
|
1280
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1281
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "hostname");
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
function url() {
|
|
1285
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1286
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "url");
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
function protocol() {
|
|
1290
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1291
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "protocol");
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
function db() {
|
|
1295
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1296
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "db");
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
function headers() {
|
|
1300
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1301
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "headers");
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
function controller() {
|
|
1305
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1306
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "controller");
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
function action() {
|
|
1310
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1311
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "action");
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
function subaction() {
|
|
1315
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1316
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "subaction");
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
function body() {
|
|
1320
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1321
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "body");
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
function stream() {
|
|
1325
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1326
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "stream");
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
function home() {
|
|
1330
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1331
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "home");
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
function pool(name = "home") {
|
|
1335
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1336
|
+
defineMetadataArgument(argumentsSubactionsKey, target, propertyKey, parameterIndex, "pool", name);
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
function connection(name = "home") {
|
|
1340
|
+
return function(target, propertyKey, parameterIndex) {
|
|
1341
|
+
defineMetadataArgument(argumentsConnectionsKey, target, propertyKey, parameterIndex, "connection", name);
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
function json(opt = {}) {
|
|
1345
|
+
return function(target, propertyKey, descriptor) {
|
|
1346
|
+
defineMetadataArgument(responseSubactionsKey, target, propertyKey, 0, "json", opt);
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
function xml(opt = {}) {
|
|
1350
|
+
return function(target, propertyKey, descriptor) {
|
|
1351
|
+
defineMetadataArgument(responseSubactionsKey, target, propertyKey, 0, "xml", opt);
|
|
1352
|
+
};
|
|
1353
|
+
}
|
|
1354
|
+
function logerror(name) {
|
|
1355
|
+
return function(target, propertyKey, descriptor) {
|
|
1356
|
+
defineMetadataArgument(responseLoggerKey, target, propertyKey, name, "logger", name);
|
|
1357
|
+
};
|
|
1358
|
+
}
|
|
1359
|
+
function http(beforeCreate) {
|
|
1360
|
+
return function(target, propertyKey, descriptor) {
|
|
1361
|
+
const opts = {
|
|
1362
|
+
format: "json"
|
|
1363
|
+
};
|
|
1364
|
+
const method2 = descriptor.value;
|
|
1365
|
+
descriptor.value = async function() {
|
|
1366
|
+
if (beforeCreate) {
|
|
1367
|
+
Object.assign(opts, await beforeCreate.apply(this) || {});
|
|
1368
|
+
}
|
|
1369
|
+
const args = [];
|
|
1370
|
+
const connections = [];
|
|
1371
|
+
const existingArguments = Reflect.getOwnMetadata(
|
|
1372
|
+
argumentsSubactionsKey,
|
|
1373
|
+
target,
|
|
1374
|
+
propertyKey
|
|
1375
|
+
) || {};
|
|
1376
|
+
const existingConnections = Reflect.getOwnMetadata(
|
|
1377
|
+
argumentsConnectionsKey,
|
|
1378
|
+
target,
|
|
1379
|
+
propertyKey
|
|
1380
|
+
) || {};
|
|
1381
|
+
const existingLoggers = Reflect.getOwnMetadata(
|
|
1382
|
+
responseLoggerKey,
|
|
1383
|
+
target,
|
|
1384
|
+
propertyKey
|
|
1385
|
+
) || {};
|
|
1386
|
+
const response = Reflect.getOwnMetadata(
|
|
1387
|
+
responseSubactionsKey,
|
|
1388
|
+
target,
|
|
1389
|
+
propertyKey
|
|
1390
|
+
)?.[0] || { type: "json", params: {} };
|
|
1391
|
+
const responseInit = Reflect.getOwnMetadata(
|
|
1392
|
+
responseInitKey,
|
|
1393
|
+
target,
|
|
1394
|
+
propertyKey
|
|
1395
|
+
)?.[0] || { type: "", params: {} };
|
|
1396
|
+
for (const key in existingArguments) {
|
|
1397
|
+
if (!existingArguments.hasOwnProperty(key)) {
|
|
1398
|
+
continue;
|
|
1399
|
+
}
|
|
1400
|
+
const getterName = `get${upperCaseFirstLetter(existingArguments[key].type)}`;
|
|
1401
|
+
args[+key] = this[getterName](existingArguments[key].params);
|
|
1402
|
+
}
|
|
1403
|
+
for (const key in existingConnections) {
|
|
1404
|
+
if (!existingConnections.hasOwnProperty(key)) {
|
|
1405
|
+
continue;
|
|
1406
|
+
}
|
|
1407
|
+
existingConnections[key].type;
|
|
1408
|
+
args[+key] = await this.getDb()[existingConnections[key].params].getConnection();
|
|
1409
|
+
connections.push(args[+key]);
|
|
1410
|
+
}
|
|
1411
|
+
try {
|
|
1412
|
+
let out = await method2.apply(this, args), opt;
|
|
1413
|
+
Array.isArray(out) && ([out, opt] = out);
|
|
1414
|
+
if (responseInit.type !== "init") {
|
|
1415
|
+
if (opts.format === "xml" || response.type === "xml") {
|
|
1416
|
+
return this.successXml(out, Object.assign(response.params, opt || {}));
|
|
1417
|
+
}
|
|
1418
|
+
return this.success(out, Object.assign(response.params, opt || {}));
|
|
1419
|
+
}
|
|
1420
|
+
} catch (e) {
|
|
1421
|
+
await Promise.all([...connections.map((connection2) => connection2.rollback())]);
|
|
1422
|
+
let output;
|
|
1423
|
+
if (opts.format === "xml" || response.type === "xml") {
|
|
1424
|
+
output = this.errorXml(e);
|
|
1425
|
+
} else {
|
|
1426
|
+
output = this.error(e);
|
|
1427
|
+
}
|
|
1428
|
+
for (const key in existingLoggers) {
|
|
1429
|
+
if (!existingLoggers.hasOwnProperty(key)) {
|
|
1430
|
+
continue;
|
|
1431
|
+
}
|
|
1432
|
+
const logger = existingLoggers[key].params;
|
|
1433
|
+
await this[logger].error(e, { response: omit(output?.body || {}, ["stack"]) });
|
|
1434
|
+
}
|
|
1435
|
+
return output;
|
|
1436
|
+
} finally {
|
|
1437
|
+
await Promise.all([...connections.map((connection2) => connection2.release())]);
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
function init() {
|
|
1443
|
+
return function(target, propertyKey, descriptor) {
|
|
1444
|
+
defineMetadataArgument(responseInitKey, target, propertyKey, 0, "init");
|
|
1445
|
+
descriptor.value;
|
|
1446
|
+
if (!("$inits" in target)) {
|
|
1447
|
+
Object.defineProperty(target, "$inits", { value: [] });
|
|
1448
|
+
}
|
|
1449
|
+
target.$inits.push(propertyKey);
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
const helpers = {
|
|
1454
|
+
cookieParse,
|
|
1455
|
+
cookieString,
|
|
1456
|
+
jsonBody,
|
|
1457
|
+
rawBody,
|
|
1458
|
+
streamBody,
|
|
1459
|
+
urlencodedBody,
|
|
1460
|
+
multipartBody,
|
|
1461
|
+
readConfig,
|
|
1462
|
+
readConfigSync,
|
|
1463
|
+
staticBody,
|
|
1464
|
+
extensions,
|
|
1465
|
+
getExt,
|
|
1466
|
+
jwtDecode,
|
|
1467
|
+
readJsonConfigsSync
|
|
1468
|
+
};
|
|
1469
|
+
|
|
1470
|
+
export { ApiError, AsyncJwt, Controller, Memcached, Model, Ssr, Web, action, body, codeToStatus, config, connection, context, controller, cookies, db, headers, helpers, home, hostname, http, init, json, logerror, method, mixin, mountWithContext as mount, pool, protocol, request, selectControllersSchema, stream, subaction, url, xml };
|