@volcanicminds/backend 3.3.0 → 3.5.0
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 +15 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/config/general.d.ts +3 -0
- package/dist/lib/config/general.d.ts.map +1 -1
- package/dist/lib/config/general.js +3 -0
- package/dist/lib/config/general.js.map +1 -1
- package/dist/lib/config/plugins.d.ts.map +1 -1
- package/dist/lib/config/plugins.js +5 -0
- package/dist/lib/config/plugins.js.map +1 -1
- package/dist/lib/hooks/onError.d.ts.map +1 -1
- package/dist/lib/hooks/onError.js +8 -7
- package/dist/lib/hooks/onError.js.map +1 -1
- package/dist/lib/loader/general.d.ts.map +1 -1
- package/dist/lib/loader/general.js +3 -0
- package/dist/lib/loader/general.js.map +1 -1
- package/dist/lib/loader/router.d.ts.map +1 -1
- package/dist/lib/loader/router.js +29 -5
- package/dist/lib/loader/router.js.map +1 -1
- package/dist/lib/util/cache.d.ts +37 -0
- package/dist/lib/util/cache.d.ts.map +1 -0
- package/dist/lib/util/cache.js +189 -0
- package/dist/lib/util/cache.js.map +1 -0
- package/dist/lib/util/tracker.d.ts +1 -0
- package/dist/lib/util/tracker.d.ts.map +1 -1
- package/dist/lib/util/tracker.js +3 -3
- package/dist/lib/util/tracker.js.map +1 -1
- package/dist/types/global.d.ts +28 -0
- package/lib/config/general.ts +7 -1
- package/lib/config/plugins.ts +8 -0
- package/lib/hooks/onError.ts +12 -7
- package/lib/loader/general.ts +3 -0
- package/lib/loader/router.ts +40 -5
- package/lib/manifest/generator.ts +1 -1
- package/lib/util/cache.ts +258 -0
- package/lib/util/tracker.ts +5 -3
- package/package.json +4 -2
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
const DEFAULTS = { enabled: false, ttl: 3600, maxEntries: 1000 };
|
|
2
|
+
let settings = { ...DEFAULTS };
|
|
3
|
+
const store = new Map();
|
|
4
|
+
let hits = 0;
|
|
5
|
+
let misses = 0;
|
|
6
|
+
let sweepTimer = null;
|
|
7
|
+
const nowMs = () => Date.now();
|
|
8
|
+
function startSweep(intervalMs = 60_000) {
|
|
9
|
+
if (sweepTimer)
|
|
10
|
+
clearInterval(sweepTimer);
|
|
11
|
+
sweepTimer = setInterval(() => {
|
|
12
|
+
const t = nowMs();
|
|
13
|
+
for (const [k, e] of store)
|
|
14
|
+
if (e.expires <= t)
|
|
15
|
+
store.delete(k);
|
|
16
|
+
}, intervalMs);
|
|
17
|
+
sweepTimer.unref?.();
|
|
18
|
+
}
|
|
19
|
+
export function configureCache(opts) {
|
|
20
|
+
settings = {
|
|
21
|
+
enabled: opts?.enabled === true,
|
|
22
|
+
ttl: Number(opts?.ttl) > 0 ? Number(opts?.ttl) : DEFAULTS.ttl,
|
|
23
|
+
maxEntries: Number(opts?.maxEntries) > 0 ? Number(opts?.maxEntries) : DEFAULTS.maxEntries
|
|
24
|
+
};
|
|
25
|
+
store.clear();
|
|
26
|
+
hits = 0;
|
|
27
|
+
misses = 0;
|
|
28
|
+
if (settings.enabled) {
|
|
29
|
+
startSweep();
|
|
30
|
+
if (log?.i)
|
|
31
|
+
log.info(`Cache 🧊 enabled — ttl ${settings.ttl}s, maxEntries ${settings.maxEntries}, strategy LRU+TTL`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
if (sweepTimer) {
|
|
35
|
+
clearInterval(sweepTimer);
|
|
36
|
+
sweepTimer = null;
|
|
37
|
+
}
|
|
38
|
+
if (log?.i)
|
|
39
|
+
log.info('Cache 🧊 disabled');
|
|
40
|
+
}
|
|
41
|
+
return settings;
|
|
42
|
+
}
|
|
43
|
+
export const cacheEnabled = () => settings.enabled;
|
|
44
|
+
export const defaultTtl = () => settings.ttl;
|
|
45
|
+
export function cacheGet(key) {
|
|
46
|
+
const e = store.get(key);
|
|
47
|
+
if (!e) {
|
|
48
|
+
misses++;
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
if (e.expires <= nowMs()) {
|
|
52
|
+
store.delete(key);
|
|
53
|
+
misses++;
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
store.delete(key);
|
|
57
|
+
store.set(key, e);
|
|
58
|
+
hits++;
|
|
59
|
+
return e.value;
|
|
60
|
+
}
|
|
61
|
+
export function cacheSet(key, value, ttlSec) {
|
|
62
|
+
const ttl = Number(ttlSec) > 0 ? Number(ttlSec) : settings.ttl;
|
|
63
|
+
if (store.has(key))
|
|
64
|
+
store.delete(key);
|
|
65
|
+
store.set(key, { value, expires: ttl > 0 ? nowMs() + ttl * 1000 : Infinity });
|
|
66
|
+
while (store.size > settings.maxEntries) {
|
|
67
|
+
const oldest = store.keys().next().value;
|
|
68
|
+
if (oldest === undefined)
|
|
69
|
+
break;
|
|
70
|
+
store.delete(oldest);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function invalidateCache(keyGroups) {
|
|
74
|
+
if (keyGroups == null) {
|
|
75
|
+
const n = store.size;
|
|
76
|
+
store.clear();
|
|
77
|
+
if (log?.d)
|
|
78
|
+
log.debug(`Cache 🧊 flushAll — ${n} entries removed`);
|
|
79
|
+
return n;
|
|
80
|
+
}
|
|
81
|
+
const list = Array.isArray(keyGroups) ? keyGroups : [keyGroups];
|
|
82
|
+
const prefixes = list.map((g) => `${g}::`);
|
|
83
|
+
let n = 0;
|
|
84
|
+
for (const k of [...store.keys()]) {
|
|
85
|
+
if (prefixes.some((p) => k.startsWith(p))) {
|
|
86
|
+
store.delete(k);
|
|
87
|
+
n++;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (log?.d)
|
|
91
|
+
log.debug(`Cache 🧊 invalidate [${list.join(', ')}] — ${n} entries removed`);
|
|
92
|
+
return n;
|
|
93
|
+
}
|
|
94
|
+
export function cacheStats() {
|
|
95
|
+
return { size: store.size, hits, misses, ...settings };
|
|
96
|
+
}
|
|
97
|
+
export const cache = {
|
|
98
|
+
get: cacheGet,
|
|
99
|
+
set: cacheSet,
|
|
100
|
+
del: (key) => store.delete(key),
|
|
101
|
+
invalidate: invalidateCache,
|
|
102
|
+
flushAll: () => invalidateCache(),
|
|
103
|
+
stats: cacheStats,
|
|
104
|
+
enabled: cacheEnabled
|
|
105
|
+
};
|
|
106
|
+
export function normalizeRouteCache(input, defaultKeyGroup) {
|
|
107
|
+
if (input == null || input === false)
|
|
108
|
+
return undefined;
|
|
109
|
+
const obj = input === true ? {} : typeof input === 'number' ? { ttl: input } : input;
|
|
110
|
+
const enabled = obj.enabled !== false;
|
|
111
|
+
const invalidates = obj.invalidates == null ? undefined : Array.isArray(obj.invalidates) ? obj.invalidates : [obj.invalidates];
|
|
112
|
+
if (!enabled && !invalidates?.length)
|
|
113
|
+
return undefined;
|
|
114
|
+
return {
|
|
115
|
+
enabled,
|
|
116
|
+
ttl: typeof obj.ttl === 'number' && obj.ttl > 0 ? obj.ttl : undefined,
|
|
117
|
+
keyGroup: obj.keyGroup || defaultKeyGroup,
|
|
118
|
+
invalidates
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const HIT = Symbol('volcanic.cacheHit');
|
|
122
|
+
const HEADER_ALLOW = /^(content-type|v-)/i;
|
|
123
|
+
function scope(req) {
|
|
124
|
+
const tenant = req.tenant?.id ?? '';
|
|
125
|
+
const subject = req.user?.externalId ?? req.token?.getId?.() ?? 'anon';
|
|
126
|
+
const rolesList = typeof req.roles === 'function' ? req.roles() : [];
|
|
127
|
+
const rolesKey = [...(rolesList || [])].sort().join(',');
|
|
128
|
+
return `${tenant}|${subject}|${rolesKey}`;
|
|
129
|
+
}
|
|
130
|
+
function keyFor(req, keyGroup) {
|
|
131
|
+
return `${keyGroup}::${scope(req)}::${req.method} ${req.url}`;
|
|
132
|
+
}
|
|
133
|
+
function tenantMissing(req) {
|
|
134
|
+
const mt = global.config?.options?.multi_tenant?.enabled;
|
|
135
|
+
const tenantCtx = req.routeOptions?.config?.tenantContext;
|
|
136
|
+
return Boolean(mt && tenantCtx !== false && !req.tenant);
|
|
137
|
+
}
|
|
138
|
+
export function buildCacheHooks(routeCache) {
|
|
139
|
+
const cacheable = routeCache.enabled === true;
|
|
140
|
+
const keyGroup = routeCache.keyGroup;
|
|
141
|
+
const ttl = routeCache.ttl;
|
|
142
|
+
const invalidates = routeCache.invalidates;
|
|
143
|
+
const preHandler = cacheable
|
|
144
|
+
? async (req, reply) => {
|
|
145
|
+
if (!cacheEnabled() || req.method !== 'GET' || tenantMissing(req))
|
|
146
|
+
return;
|
|
147
|
+
const hit = cacheGet(keyFor(req, keyGroup));
|
|
148
|
+
if (hit) {
|
|
149
|
+
for (const [h, v] of Object.entries(hit.headers))
|
|
150
|
+
reply.header(h, v);
|
|
151
|
+
req[HIT] = true;
|
|
152
|
+
reply.code(hit.statusCode);
|
|
153
|
+
if (log?.d)
|
|
154
|
+
log.debug(`Cache 🧊 hit ${keyGroup} ${req.method} ${req.url}`);
|
|
155
|
+
return reply.send(hit.payload);
|
|
156
|
+
}
|
|
157
|
+
if (log?.d)
|
|
158
|
+
log.debug(`Cache 🧊 miss ${keyGroup} ${req.method} ${req.url}`);
|
|
159
|
+
}
|
|
160
|
+
: undefined;
|
|
161
|
+
const onSend = async (req, reply, payload) => {
|
|
162
|
+
if (!cacheEnabled())
|
|
163
|
+
return payload;
|
|
164
|
+
if (cacheable &&
|
|
165
|
+
!req[HIT] &&
|
|
166
|
+
req.method === 'GET' &&
|
|
167
|
+
reply.statusCode >= 200 &&
|
|
168
|
+
reply.statusCode < 300 &&
|
|
169
|
+
typeof payload === 'string' &&
|
|
170
|
+
!tenantMissing(req) &&
|
|
171
|
+
!reply.getHeader('set-cookie')) {
|
|
172
|
+
const headers = {};
|
|
173
|
+
const all = reply.getHeaders();
|
|
174
|
+
for (const h of Object.keys(all))
|
|
175
|
+
if (HEADER_ALLOW.test(h))
|
|
176
|
+
headers[h] = all[h];
|
|
177
|
+
cacheSet(keyFor(req, keyGroup), { payload, headers, statusCode: reply.statusCode }, ttl);
|
|
178
|
+
}
|
|
179
|
+
if (invalidates?.length &&
|
|
180
|
+
req.method !== 'GET' &&
|
|
181
|
+
reply.statusCode >= 200 &&
|
|
182
|
+
reply.statusCode < 300) {
|
|
183
|
+
invalidateCache(invalidates);
|
|
184
|
+
}
|
|
185
|
+
return payload;
|
|
186
|
+
};
|
|
187
|
+
return { preHandler, onSend };
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../lib/util/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FastifyRequest, FastifyReply } from '../../types/global.js';
|
|
2
2
|
export declare function initialize(req: FastifyRequest, _reply: FastifyReply): Promise<void>;
|
|
3
3
|
export declare function track(req: FastifyRequest, _reply: FastifyReply, payload: any): Promise<void>;
|
|
4
|
+
export declare function isFieldChanged(oldValue: any, newValue: any): boolean;
|
|
4
5
|
//# sourceMappingURL=tracker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../../lib/util/tracker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEzE,wBAAsB,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,iBAmBzE;AAED,wBAAsB,KAAK,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,iBAsDlF"}
|
|
1
|
+
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../../lib/util/tracker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEzE,wBAAsB,UAAU,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,iBAmBzE;AAED,wBAAsB,KAAK,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG,iBAsDlF;AAWD,wBAAgB,cAAc,CAAC,QAAQ,KAAA,EAAE,QAAQ,KAAA,WAyBhD"}
|
package/dist/lib/util/tracker.js
CHANGED
|
@@ -78,18 +78,18 @@ function getTrackingConfigIfEnabled(req) {
|
|
|
78
78
|
log.error(error);
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
-
function isFieldChanged(oldValue, newValue) {
|
|
81
|
+
export function isFieldChanged(oldValue, newValue) {
|
|
82
82
|
if ((oldValue instanceof Date || newValue instanceof Date) && oldValue != null && newValue != undefined) {
|
|
83
83
|
return !dayjs(oldValue).isSame(dayjs(newValue));
|
|
84
84
|
}
|
|
85
85
|
if ((oldValue instanceof Object || newValue instanceof Object) && oldValue != null && newValue != undefined) {
|
|
86
86
|
const primaryKey = global.trackingConfig?.primaryKey;
|
|
87
|
-
const oldId = oldValue != null && primaryKey in oldValue
|
|
87
|
+
const oldId = oldValue != null && typeof oldValue === 'object' && primaryKey in oldValue
|
|
88
88
|
? oldValue[primaryKey]
|
|
89
89
|
: typeof oldValue === 'string'
|
|
90
90
|
? oldValue
|
|
91
91
|
: undefined;
|
|
92
|
-
const newId = newValue != null && primaryKey in newValue
|
|
92
|
+
const newId = newValue != null && typeof newValue === 'object' && primaryKey in newValue
|
|
93
93
|
? newValue[primaryKey]
|
|
94
94
|
: typeof newValue === 'string'
|
|
95
95
|
? newValue
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../../lib/util/tracker.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../../lib/util/tracker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAmB,EAAE,MAAoB;IACxE,IAAI,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC;QAClD,MAAM,EAAE,GAAG,0BAA0B,CAAC,GAAG,CAAC,CAAA;QAC1C,MAAM,OAAO,GAAG,EAAE,GAAG,GAAG,CAAC,UAAU,EAAE,EAAE,GAAG,GAAG,CAAC,IAAI,EAAE,EAAE,CAAA;QAEtD,IAAI,EAAE,EAAE,CAAC;YACP,IAAI,CAAC;gBACH,IAAI,OAAO,IAAI,EAAE,CAAC,MAAM,IAAI,EAAE,CAAC,UAAU,IAAI,EAAE,CAAC,UAAU,IAAI,OAAO,EAAE,CAAC;oBACtE,MAAM,GAAG,GAAG,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,CAAA;oBAClC,GAAG,CAAC,YAAY,GAAG,MAAM,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;oBACjF,IAAI,GAAG,CAAC,CAAC;wBACP,GAAG,CAAC,KAAK,CAAC,8BAA8B,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;gBACxG,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,GAAG,CAAC,KAAK,CAAC,8BAA8B,EAAE,CAAC,IAAI,EAAE,CAAC,CAAA;gBAClD,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YAClB,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,KAAK,CAAC,GAAmB,EAAE,MAAoB,EAAE,OAAY;IACjF,IAAI,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC;QAClD,MAAM,EAAE,GAAG,0BAA0B,CAAC,GAAG,CAAC,CAAA;QAC1C,IAAI,EAAE,EAAE,CAAC;YACP,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAU,EAAE,CAAA;gBAC1B,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,CAAA;gBACnC,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAA;gBAChC,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,CAAA;gBAEhC,MAAM,MAAM,GACV,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAA;gBAEhH,MAAM,EAAE,GACN,EAAE,CAAC,UAAU,IAAI,OAAO,IAAI,EAAE,CAAC,UAAU,IAAI,OAAO;oBAClD,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC;oBACxB,CAAC,CAAC,EAAE,CAAC,UAAU,IAAI,OAAO,IAAI,EAAE,CAAC,UAAU,IAAI,OAAO;wBACtD,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC;wBACxB,CAAC,CAAC,SAAS,CAAA;gBAEf,IAAI,CAAC,EAAE,EAAE,CAAC;oBACR,GAAG,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAA;oBACzD,OAAM;gBACR,CAAC;gBAED,IAAI,SAAS,GAAG,KAAK,CAAA;gBACrB,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;oBACxB,SAAS,GAAG,IAAI,CAAA;gBAClB,CAAC;qBAAM,CAAC;oBACN,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,EAAE,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;oBAChE,MAAM,QAAQ,GAAa,EAAE,CAAC,MAAM,EAAE,QAAQ,IAAI,EAAE,CAAA;oBAEpD,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;wBACvB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;4BAC9B,MAAM,QAAQ,GAAG,OAAO,IAAI,IAAI,IAAI,KAAK,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;4BACjF,MAAM,QAAQ,GAAG,OAAO,IAAI,IAAI,IAAI,KAAK,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;4BACjF,IAAI,QAAQ,KAAK,SAAS,IAAI,cAAc,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;gCACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAA;gCAC3D,SAAS,GAAG,IAAI,CAAA;4BAClB,CAAC;wBACH,CAAC;oBACH,CAAC,CAAC,CAAA;gBACJ,CAAC;gBAED,IAAI,SAAS,EAAE,CAAC;oBACd,IAAI,GAAG,CAAC,CAAC;wBAAE,GAAG,CAAC,KAAK,CAAC,yBAAyB,YAAY,QAAQ,MAAM,KAAK,EAAE,KAAK,MAAM,KAAK,MAAM,EAAE,CAAC,CAAA;oBACxG,MAAM,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,CAAC,CAAA;gBACnG,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,GAAG,CAAC,KAAK,CAAC,8BAA8B,EAAE,CAAC,IAAI,EAAE,CAAC,CAAA;gBAClD,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YAClB,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,0BAA0B,CAAC,GAAG;IACrC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,CAAC,WAAW,EAAE,GAAG,IAAI,GAAG,CAAC,GAAG,EAAE,CAAA;QAChH,OAAO,IAAI,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IAC5G,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAClB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAQ,EAAE,QAAQ;IAC/C,IAAI,CAAC,QAAQ,YAAY,IAAI,IAAI,QAAQ,YAAY,IAAI,CAAC,IAAI,QAAQ,IAAI,IAAI,IAAI,QAAQ,IAAI,SAAS,EAAE,CAAC;QACxG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAA;IACjD,CAAC;IAED,IAAI,CAAC,QAAQ,YAAY,MAAM,IAAI,QAAQ,YAAY,MAAM,CAAC,IAAI,QAAQ,IAAI,IAAI,IAAI,QAAQ,IAAI,SAAS,EAAE,CAAC;QAC5G,MAAM,UAAU,GAAG,MAAM,CAAC,cAAc,EAAE,UAAU,CAAA;QAGpD,MAAM,KAAK,GACT,QAAQ,IAAI,IAAI,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,UAAU,IAAI,QAAQ;YACxE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;YACtB,CAAC,CAAC,OAAO,QAAQ,KAAK,QAAQ;gBAC9B,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC,SAAS,CAAA;QACf,MAAM,KAAK,GACT,QAAQ,IAAI,IAAI,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,UAAU,IAAI,QAAQ;YACxE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;YACtB,CAAC,CAAC,OAAO,QAAQ,KAAK,QAAQ;gBAC9B,CAAC,CAAC,QAAQ;gBACV,CAAC,CAAC,SAAS,CAAA;QACf,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAA;IAC5E,CAAC;IAED,OAAO,QAAQ,IAAI,QAAQ,CAAA;AAC7B,CAAC"}
|
package/dist/types/global.d.ts
CHANGED
|
@@ -69,6 +69,25 @@ export interface RouteConfig {
|
|
|
69
69
|
consumes?: any
|
|
70
70
|
rawBody?: boolean
|
|
71
71
|
manifest?: ManifestHints // structural hints for the generated manifest (group + resource)
|
|
72
|
+
cache?: boolean | number | RouteCache // file-level cache default (inherited by every route; per-route `cache` overrides)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Per-route caching (opt-in). Authored on the route: `true` (default TTL), a number
|
|
76
|
+
// (TTL in seconds), or the full object. Only GET responses with a 2xx status are
|
|
77
|
+
// cached; `invalidates` works on any method (typically mutations).
|
|
78
|
+
export interface RouteCache {
|
|
79
|
+
enabled?: boolean // default true when the object is present
|
|
80
|
+
ttl?: number // seconds; default from global config (options.cache.ttl)
|
|
81
|
+
keyGroup?: string // logical key-group for invalidation; default = the api folder (area)
|
|
82
|
+
invalidates?: string | string[] // key-groups to flush after a successful (2xx) response
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// The normalized form threaded onto a ConfiguredRoute (keyGroup always resolved).
|
|
86
|
+
export interface NormalizedRouteCache {
|
|
87
|
+
enabled: boolean
|
|
88
|
+
ttl?: number
|
|
89
|
+
keyGroup: string
|
|
90
|
+
invalidates?: string[]
|
|
72
91
|
}
|
|
73
92
|
|
|
74
93
|
export interface Route {
|
|
@@ -79,6 +98,7 @@ export interface Route {
|
|
|
79
98
|
middlewares: string[]
|
|
80
99
|
config?: RouteConfig
|
|
81
100
|
rateLimit?: any
|
|
101
|
+
cache?: boolean | number | RouteCache
|
|
82
102
|
}
|
|
83
103
|
|
|
84
104
|
export interface GeneralConfig {
|
|
@@ -105,6 +125,12 @@ export interface GeneralConfig {
|
|
|
105
125
|
manifest?: {
|
|
106
126
|
enabled: boolean
|
|
107
127
|
}
|
|
128
|
+
// In-memory per-route response cache (opt-in per route via `cache`).
|
|
129
|
+
cache?: {
|
|
130
|
+
enabled?: boolean // master switch (default true)
|
|
131
|
+
ttl?: number // default TTL in seconds for routes without an explicit ttl
|
|
132
|
+
maxEntries?: number // LRU cap (slots) before least-recently-used eviction
|
|
133
|
+
}
|
|
108
134
|
}
|
|
109
135
|
}
|
|
110
136
|
|
|
@@ -157,6 +183,7 @@ export interface ConfiguredRoute {
|
|
|
157
183
|
}
|
|
158
184
|
group?: string // structural hint (manifest)
|
|
159
185
|
resource?: ResourceHints // structural hints (manifest)
|
|
186
|
+
cache?: NormalizedRouteCache // per-route caching (normalized)
|
|
160
187
|
}
|
|
161
188
|
|
|
162
189
|
export interface TrackChanges {
|
|
@@ -340,6 +367,7 @@ declare global {
|
|
|
340
367
|
var entity: any
|
|
341
368
|
var repository: any
|
|
342
369
|
var routes: ConfiguredRoute[]
|
|
370
|
+
var cache: any
|
|
343
371
|
}
|
|
344
372
|
|
|
345
373
|
export { global }
|
package/lib/config/general.ts
CHANGED
|
@@ -17,7 +17,13 @@ export default {
|
|
|
17
17
|
query_key: 'tid'
|
|
18
18
|
},
|
|
19
19
|
manifest: {
|
|
20
|
-
|
|
20
|
+
// opt-in: exposes GET /admin/manifest (admin-only) for the backoffice engine
|
|
21
|
+
enabled: false
|
|
22
|
+
},
|
|
23
|
+
cache: {
|
|
24
|
+
// opt-in: in-memory LRU+TTL cache for routes that declare `cache:`.
|
|
25
|
+
// When enabled, ttl (default 3600s) and maxEntries (default 1000) can be set here.
|
|
26
|
+
enabled: false
|
|
21
27
|
}
|
|
22
28
|
}
|
|
23
29
|
}
|
package/lib/config/plugins.ts
CHANGED
|
@@ -60,6 +60,14 @@ export default [
|
|
|
60
60
|
enable: false,
|
|
61
61
|
options: {}
|
|
62
62
|
},
|
|
63
|
+
{
|
|
64
|
+
// Serve a static folder (e.g. public uploads in dev; nginx/CDN in prod).
|
|
65
|
+
// Options are @fastify/static's: { root, prefix?, decorateReply? }, or an array
|
|
66
|
+
// of them for multiple mounts. Disabled by default.
|
|
67
|
+
name: 'static',
|
|
68
|
+
enable: false,
|
|
69
|
+
options: {}
|
|
70
|
+
},
|
|
63
71
|
{
|
|
64
72
|
name: 'rawBody',
|
|
65
73
|
enable: false,
|
package/lib/hooks/onError.ts
CHANGED
|
@@ -2,32 +2,37 @@
|
|
|
2
2
|
import { FastifyRequest, FastifyReply } from 'fastify'
|
|
3
3
|
|
|
4
4
|
export default async (_req: FastifyRequest, reply: FastifyReply, error: any) => {
|
|
5
|
-
|
|
5
|
+
// Normalize the message up-front: `error` may be a string, or an object without
|
|
6
|
+
// a `message` — reading `.includes` on an undefined message would crash the
|
|
7
|
+
// error handler itself (and mask the real error as an unhandled 500).
|
|
8
|
+
const message = typeof error?.message === 'string' ? error.message : String(error?.message ?? error ?? '')
|
|
9
|
+
|
|
10
|
+
if (log.e) log.error(message || `${error}`)
|
|
6
11
|
if (log.t) log.trace(error)
|
|
7
12
|
|
|
8
|
-
if (error
|
|
13
|
+
if (error?.statusCode && error.statusCode >= 400) {
|
|
9
14
|
return reply.code(error.statusCode).send(error)
|
|
10
15
|
}
|
|
11
16
|
|
|
12
|
-
if (
|
|
17
|
+
if (message === 'Wrong credentials' || message === 'Unauthorized') {
|
|
13
18
|
return reply.code(403).send({
|
|
14
19
|
statusCode: 403,
|
|
15
20
|
error: 'Forbidden',
|
|
16
|
-
message
|
|
21
|
+
message
|
|
17
22
|
})
|
|
18
23
|
}
|
|
19
24
|
|
|
20
|
-
if (
|
|
25
|
+
if (message.includes('not found')) {
|
|
21
26
|
return reply.code(404).send({
|
|
22
27
|
statusCode: 404,
|
|
23
28
|
error: 'Not Found',
|
|
24
|
-
message
|
|
29
|
+
message
|
|
25
30
|
})
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
return reply.code(500).send({
|
|
29
34
|
statusCode: 500,
|
|
30
35
|
error: 'Internal Server Error',
|
|
31
|
-
message
|
|
36
|
+
message
|
|
32
37
|
})
|
|
33
38
|
}
|
package/lib/loader/general.ts
CHANGED
package/lib/loader/router.ts
CHANGED
|
@@ -3,6 +3,7 @@ import yn from '../util/yn.js'
|
|
|
3
3
|
import type { Role, Route, ConfiguredRoute, RouteConfig } from '../../types/global.js'
|
|
4
4
|
import { FastifyReply, FastifyRequest } from 'fastify'
|
|
5
5
|
import { normalizePatterns } from '../util/path.js'
|
|
6
|
+
import { normalizeRouteCache, buildCacheHooks, cacheEnabled } from '../util/cache.js'
|
|
6
7
|
import { globSync } from 'glob'
|
|
7
8
|
import path from 'path'
|
|
8
9
|
import { fileURLToPath } from 'url'
|
|
@@ -83,7 +84,8 @@ export function processRoute(
|
|
|
83
84
|
roles: rs = [],
|
|
84
85
|
config = {} as RouteConfig,
|
|
85
86
|
middlewares = [],
|
|
86
|
-
rateLimit
|
|
87
|
+
rateLimit,
|
|
88
|
+
cache: cacheInput
|
|
87
89
|
} = route
|
|
88
90
|
|
|
89
91
|
const rsp = !rs.length ? [roles.public] : rs
|
|
@@ -186,6 +188,9 @@ export function processRoute(
|
|
|
186
188
|
tenantContext,
|
|
187
189
|
rawBody,
|
|
188
190
|
rateLimit,
|
|
191
|
+
// Per-route cache (falls back to the file-level `config.cache`); the default
|
|
192
|
+
// key-group is the api folder (`dir`), overridable.
|
|
193
|
+
cache: normalizeRouteCache(cacheInput ?? defaultConfig?.cache, dir),
|
|
189
194
|
base,
|
|
190
195
|
file: path.join(base, defaultConfig.controller || 'controller', handlerParts[0]),
|
|
191
196
|
func: handlerParts[1],
|
|
@@ -245,13 +250,33 @@ async function applyRoutes(server: any, routes: ConfiguredRoute[]): Promise<void
|
|
|
245
250
|
let countRoutes = 0
|
|
246
251
|
for (const route of routes) {
|
|
247
252
|
if (route?.enable) {
|
|
248
|
-
const { handler, method, path, middlewares, roles, rawBody, rateLimit, base, file, func, doc, tenantContext } =
|
|
253
|
+
const { handler, method, path, middlewares, roles, rawBody, rateLimit, base, file, func, doc, tenantContext, cache } =
|
|
249
254
|
route
|
|
250
255
|
|
|
251
256
|
if (log.d) log.debug(`* Add path ${method} ${path} on handle ${handler}`)
|
|
252
257
|
const midds = await loadMiddlewares(base, middlewares)
|
|
253
258
|
|
|
254
|
-
|
|
259
|
+
// Attach the cache read (preHandler, runs after auth) and write (onSend)
|
|
260
|
+
// hooks only when the route opts in — no overhead on uncached routes.
|
|
261
|
+
const cacheHooks = cache && (cache.enabled || cache.invalidates?.length) ? buildCacheHooks(cache) : null
|
|
262
|
+
|
|
263
|
+
// Courtesy log at wiring time: describe each cache-declaring route, and warn
|
|
264
|
+
// when a route opts in while the global master switch is off (hooks are inert).
|
|
265
|
+
if (cacheHooks) {
|
|
266
|
+
if (!cacheEnabled()) {
|
|
267
|
+
if (log.w)
|
|
268
|
+
log.warn(
|
|
269
|
+
`Cache 🧊 route ${method} ${path} declares cache/invalidation but global cache is disabled (options.cache.enabled) — inert`
|
|
270
|
+
)
|
|
271
|
+
} else if (log.d) {
|
|
272
|
+
const parts: string[] = []
|
|
273
|
+
if (cache?.enabled) parts.push(`read+write ttl ${cache.ttl ?? 'default'}s keyGroup '${cache.keyGroup}'`)
|
|
274
|
+
if (cache?.invalidates?.length) parts.push(`invalidates [${cache.invalidates.join(', ')}]`)
|
|
275
|
+
log.debug(`Cache 🧊 route ${method} ${path} — ${parts.join(', ')}`)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const routeDef: any = {
|
|
255
280
|
method: method,
|
|
256
281
|
path: path,
|
|
257
282
|
schema: doc,
|
|
@@ -260,7 +285,8 @@ async function applyRoutes(server: any, routes: ConfiguredRoute[]): Promise<void
|
|
|
260
285
|
requiredRoles: roles || [],
|
|
261
286
|
rawBody: rawBody || false,
|
|
262
287
|
rateLimit: rateLimit || undefined,
|
|
263
|
-
tenantContext: tenantContext
|
|
288
|
+
tenantContext: tenantContext,
|
|
289
|
+
cache: cache || undefined
|
|
264
290
|
},
|
|
265
291
|
handler: async function (req: FastifyRequest, reply: FastifyReply) {
|
|
266
292
|
let module
|
|
@@ -286,7 +312,16 @@ async function applyRoutes(server: any, routes: ConfiguredRoute[]): Promise<void
|
|
|
286
312
|
|
|
287
313
|
return await module[func](req, reply)
|
|
288
314
|
}
|
|
289
|
-
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (cacheHooks?.preHandler) {
|
|
318
|
+
// Cache read runs before the route middlewares; auth/roles are already
|
|
319
|
+
// enforced in the global onRequest hook, so this is safe.
|
|
320
|
+
routeDef.preHandler = [cacheHooks.preHandler, ...((midds as any).preHandler || [])]
|
|
321
|
+
}
|
|
322
|
+
if (cacheHooks?.onSend) routeDef.onSend = cacheHooks.onSend
|
|
323
|
+
|
|
324
|
+
server.route(routeDef)
|
|
290
325
|
|
|
291
326
|
countRoutes++
|
|
292
327
|
}
|
|
@@ -313,7 +313,7 @@ function collectFields(
|
|
|
313
313
|
const kind = crudKind(r.method, rest)
|
|
314
314
|
// Write side: create/update bodies, plus a PUT/PATCH on the BASE path — the singleton
|
|
315
315
|
// update (e.g. PUT /company), which crudKind classifies as 'action'. Restricted to the
|
|
316
|
-
// base path so sub-route action payloads (e.g. /
|
|
316
|
+
// base path so sub-route action payloads (e.g. /posts/:id/attachments/reorder) don't leak in.
|
|
317
317
|
const isSingletonWrite = rest.length === 0 && (r.method === 'PUT' || r.method === 'PATCH')
|
|
318
318
|
if (kind === 'create' || kind === 'update' || isSingletonWrite) addSide(r.doc?.body, 'write')
|
|
319
319
|
if (kind === 'read' || kind === 'list') {
|