arcway 0.1.3 → 0.1.5
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/package.json +1 -1
- package/server/context.js +54 -2
- package/server/rate-limit/index.js +2 -0
- package/server/router/api-router.js +26 -15
package/package.json
CHANGED
package/server/context.js
CHANGED
|
@@ -1,15 +1,67 @@
|
|
|
1
|
+
function trackDb(db) {
|
|
2
|
+
if (!db) return { db, stats: { queries: 0, durationMs: 0 } };
|
|
3
|
+
const stats = { queries: 0, durationMs: 0 };
|
|
4
|
+
const handler = {
|
|
5
|
+
get(target, prop, receiver) {
|
|
6
|
+
const value = Reflect.get(target, prop, receiver);
|
|
7
|
+
if (typeof value !== 'function') return value;
|
|
8
|
+
if (prop === 'raw' || prop === 'select' || prop === 'insert' || prop === 'update' ||
|
|
9
|
+
prop === 'delete' || prop === 'del' || prop === 'from' || prop === 'into' ||
|
|
10
|
+
prop === 'table' || prop === 'with' || prop === 'withRecursive') {
|
|
11
|
+
return (...args) => {
|
|
12
|
+
const builder = value.apply(target, args);
|
|
13
|
+
return wrapBuilder(builder, stats);
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return value.bind ? value.bind(target) : value;
|
|
17
|
+
},
|
|
18
|
+
apply(target, thisArg, args) {
|
|
19
|
+
const builder = Reflect.apply(target, thisArg, args);
|
|
20
|
+
if (builder && typeof builder.then === 'function' && typeof builder.select === 'function') {
|
|
21
|
+
return wrapBuilder(builder, stats);
|
|
22
|
+
}
|
|
23
|
+
return builder;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
const proxy = new Proxy(db, handler);
|
|
27
|
+
return { db: proxy, stats };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function wrapBuilder(builder, stats) {
|
|
31
|
+
const origThen = builder.then.bind(builder);
|
|
32
|
+
builder.then = (resolve, reject) => {
|
|
33
|
+
const start = performance.now();
|
|
34
|
+
return origThen(
|
|
35
|
+
(result) => {
|
|
36
|
+
stats.queries++;
|
|
37
|
+
stats.durationMs += performance.now() - start;
|
|
38
|
+
return resolve?.(result);
|
|
39
|
+
},
|
|
40
|
+
(err) => {
|
|
41
|
+
stats.queries++;
|
|
42
|
+
stats.durationMs += performance.now() - start;
|
|
43
|
+
if (reject) return reject(err);
|
|
44
|
+
throw err;
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
return builder;
|
|
49
|
+
}
|
|
50
|
+
|
|
1
51
|
function buildContext(appContext, extras) {
|
|
52
|
+
const tracked = trackDb(appContext.db);
|
|
2
53
|
const ctx = {
|
|
3
|
-
db:
|
|
54
|
+
db: tracked.db,
|
|
4
55
|
log: appContext.log,
|
|
5
56
|
events: appContext.events,
|
|
6
57
|
cache: appContext.cache,
|
|
7
58
|
queue: appContext.queue,
|
|
8
59
|
files: appContext.files,
|
|
9
60
|
mail: appContext.mail,
|
|
61
|
+
_dbStats: tracked.stats,
|
|
10
62
|
...extras,
|
|
11
63
|
};
|
|
12
64
|
return ctx;
|
|
13
65
|
}
|
|
14
66
|
|
|
15
|
-
export { buildContext };
|
|
67
|
+
export { buildContext, trackDb };
|
|
@@ -146,6 +146,26 @@ class ApiRouter {
|
|
|
146
146
|
);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
_emitCanonicalLog(ctx, { method, path, route, status, startTime, middlewareNames, error, session }) {
|
|
150
|
+
const durationMs = Date.now() - startTime;
|
|
151
|
+
const dbStats = ctx._dbStats ?? { queries: 0, durationMs: 0 };
|
|
152
|
+
const line = {
|
|
153
|
+
method,
|
|
154
|
+
path,
|
|
155
|
+
route,
|
|
156
|
+
status,
|
|
157
|
+
durationMs,
|
|
158
|
+
dbQueries: dbStats.queries,
|
|
159
|
+
dbDurationMs: Math.round(dbStats.durationMs),
|
|
160
|
+
middleware: middlewareNames,
|
|
161
|
+
};
|
|
162
|
+
if (session?.userId ?? session?.id) {
|
|
163
|
+
line.userId = session.userId ?? session.id;
|
|
164
|
+
}
|
|
165
|
+
if (error) line.error = error;
|
|
166
|
+
ctx.log.info('request', line);
|
|
167
|
+
}
|
|
168
|
+
|
|
149
169
|
async init() {
|
|
150
170
|
if (this._config.enabled === false) {
|
|
151
171
|
this._log?.info('API: disabled');
|
|
@@ -214,14 +234,9 @@ class ApiRouter {
|
|
|
214
234
|
} catch (err) {
|
|
215
235
|
const errorMsg = toErrorMessage(err);
|
|
216
236
|
ctx.log.error(`Handler error in ${route.method} ${route.pattern}`, { error: errorMsg });
|
|
217
|
-
|
|
218
|
-
method,
|
|
219
|
-
|
|
220
|
-
route: route.pattern,
|
|
221
|
-
status: 500,
|
|
222
|
-
durationMs: Date.now() - startTime,
|
|
223
|
-
middleware: middlewareNames,
|
|
224
|
-
error: errorMsg,
|
|
237
|
+
this._emitCanonicalLog(ctx, {
|
|
238
|
+
method, path: pathname, route: route.pattern, status: 500,
|
|
239
|
+
startTime, middlewareNames, error: errorMsg, session: reqInfo.session,
|
|
225
240
|
});
|
|
226
241
|
sendJson(res, 500, {
|
|
227
242
|
error: { code: ErrorCodes.HANDLER_ERROR, message: 'An internal error occurred' },
|
|
@@ -248,13 +263,9 @@ class ApiRouter {
|
|
|
248
263
|
// ── Serialize + send ──
|
|
249
264
|
const statusCode = response.status ?? (response.error ? 400 : 200);
|
|
250
265
|
await serializeResponse(res, response, responseHeaders, statusCode);
|
|
251
|
-
|
|
252
|
-
method,
|
|
253
|
-
|
|
254
|
-
route: route.pattern,
|
|
255
|
-
status: statusCode,
|
|
256
|
-
durationMs: Date.now() - startTime,
|
|
257
|
-
middleware: middlewareNames,
|
|
266
|
+
this._emitCanonicalLog(ctx, {
|
|
267
|
+
method, path: pathname, route: route.pattern, status: statusCode,
|
|
268
|
+
startTime, middlewareNames, session: response.session !== void 0 ? response.session : reqInfo.session,
|
|
258
269
|
});
|
|
259
270
|
|
|
260
271
|
return true;
|