@raketa-cloud/admin 1.3.0 → 1.4.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/package.json +1 -1
- package/src/HonoAdapter.js +303 -167
- package/src/filters.js +17 -0
- package/src/index.js +1 -0
- package/src/routeCollection.js +14 -83
- package/src/routeMember.js +10 -33
- package/src/routeNestedCollection.js +19 -47
- package/src/routeNestedMember.js +14 -40
- package/src/tests/Adapter.test.js +4 -4
- package/src/tests/HonoAdapter.filter.test.js +40 -0
- package/src/tests/HonoAdapter.test.js +110 -9
- package/src/tests/RaketaContext.test.js +65 -0
- package/src/tests/cipher.test.js +73 -0
- package/src/tests/filters.test.js +97 -0
- package/src/tests/routeCollection.test.js +0 -92
- package/src/tests/routeMember.test.js +0 -6
- package/src/tests/routeNestedCollection.test.js +0 -14
- package/src/tests/routeNestedMember.test.js +0 -6
- package/src/tests/utils.test.js +70 -0
- package/src/Adapter.js +0 -265
package/package.json
CHANGED
package/src/HonoAdapter.js
CHANGED
|
@@ -1,51 +1,241 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import ejs from "ejs";
|
|
2
4
|
import Logger from "./Logger";
|
|
5
|
+
import currentPath from "./currentPath.cjs";
|
|
6
|
+
import FormBuilder from "./helpers/FormBuilder";
|
|
7
|
+
import HTMLBuilder from "./helpers/HTMLBuilder";
|
|
8
|
+
import Inflector from "./helpers/Inflector";
|
|
3
9
|
import RaketaContext from "./RaketaContext";
|
|
4
10
|
import setupAuthentication from "./setupAuthentication";
|
|
11
|
+
import form_for from "./helpers/form_for";
|
|
12
|
+
import history from "./helpers/historyUtils";
|
|
13
|
+
import pagination from "./helpers/pagination_helper";
|
|
14
|
+
import sort_button from "./helpers/sort_button";
|
|
15
|
+
import utils from "./helpers/utils";
|
|
16
|
+
|
|
17
|
+
function escapeHtml(string) {
|
|
18
|
+
const escape = {
|
|
19
|
+
'"': '"',
|
|
20
|
+
'&': '&',
|
|
21
|
+
"'": ''',
|
|
22
|
+
'<': '<',
|
|
23
|
+
'>': '>'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
return String(string).replace(/["&'<>]/g, char => escape[char]);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const renderError = (message, stack) => {
|
|
30
|
+
const errorCSS = `
|
|
31
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
32
|
+
html { font-family: sans-serif; font-size: 16px; padding: 2rem; background-color: pink; }
|
|
33
|
+
h1 { margin-bottom: 1rem; color: FireBrick; }
|
|
34
|
+
.fail-squid { font-size: 7rem; }
|
|
35
|
+
pre { padding: 1.5rem 2rem; border-radius: 0.5rem; background-color: black; color: white; line-height: 1.5; font-size: 1.25rem; }
|
|
36
|
+
`;
|
|
37
|
+
return `<style>${errorCSS}</style><div class="fail-squid">🦑</div><h1>${message}</h1><pre>${escapeHtml(stack)}</pre>`;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Convert to array if needed
|
|
41
|
+
const toArray = value => Array.isArray(value) ? value : [value].filter(Boolean);
|
|
42
|
+
|
|
43
|
+
// Handle nested form values
|
|
44
|
+
function putIn(result, key, value) {
|
|
45
|
+
const segments = key.split(/[\[\]]+/).filter(Boolean);
|
|
46
|
+
let current = result;
|
|
47
|
+
|
|
48
|
+
segments.forEach((segment, idx) => {
|
|
49
|
+
const isLastSegment = idx === segments.length - 1;
|
|
50
|
+
|
|
51
|
+
if (isLastSegment) {
|
|
52
|
+
// Set value at final segment
|
|
53
|
+
current[segment] = key.endsWith("[]") || Array.isArray(value)
|
|
54
|
+
? toArray(value)
|
|
55
|
+
: value;
|
|
56
|
+
} else {
|
|
57
|
+
// Create path to next segment
|
|
58
|
+
current[segment] ??= {};
|
|
59
|
+
current = current[segment];
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Normalize paths for consistent handling
|
|
65
|
+
const sanitizePath = filePath => path.normalize(path.join("/", ...filePath.split("/")));
|
|
66
|
+
|
|
67
|
+
// Find template path with fallbacks
|
|
68
|
+
function getPartialPath(partialPath, views, viewsFallback) {
|
|
69
|
+
const paths = [
|
|
70
|
+
sanitizePath(`${process.cwd()}/src/views/${views}/${partialPath}`),
|
|
71
|
+
sanitizePath(`${currentPath}/views/${views}/${partialPath}`),
|
|
72
|
+
viewsFallback && sanitizePath(`${currentPath}/views/${viewsFallback}/${partialPath}`)
|
|
73
|
+
].filter(Boolean);
|
|
74
|
+
|
|
75
|
+
// Return first existing path
|
|
76
|
+
const existingPath = paths.find(path => existsSync(path));
|
|
77
|
+
|
|
78
|
+
if (existingPath) return existingPath;
|
|
79
|
+
|
|
80
|
+
throw new Error(`Template not found: "${partialPath}"\nSearched in:\n${paths.join('\n')}`);
|
|
81
|
+
}
|
|
5
82
|
|
|
83
|
+
// Parse request body based on content type
|
|
6
84
|
async function parseBody(req) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return req.text();
|
|
15
|
-
default:
|
|
16
|
-
// That is an ok default for now
|
|
17
|
-
return req.parseBody();
|
|
18
|
-
}
|
|
85
|
+
const contentType = req.header("Content-type") || "";
|
|
86
|
+
|
|
87
|
+
if (contentType.includes("json")) return req.json();
|
|
88
|
+
if (contentType === "text/plain") return req.text();
|
|
89
|
+
|
|
90
|
+
// Default for form data and other content types
|
|
91
|
+
return req.parseBody();
|
|
19
92
|
}
|
|
20
93
|
|
|
94
|
+
// Convert body to request parameters
|
|
21
95
|
function bodyToParams(req, parsedBody, adapter) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
default:
|
|
27
|
-
return parsedBody;
|
|
96
|
+
const contentType = req.header("Content-type") || "";
|
|
97
|
+
|
|
98
|
+
if (contentType.includes("form-data") || contentType.includes("urlencoded")) {
|
|
99
|
+
return adapter.parseFormBody(parsedBody);
|
|
28
100
|
}
|
|
101
|
+
|
|
102
|
+
return parsedBody;
|
|
29
103
|
}
|
|
30
104
|
|
|
31
|
-
export default class HonoAdapter
|
|
105
|
+
export default class HonoAdapter {
|
|
32
106
|
constructor(router, config) {
|
|
33
|
-
super(router);
|
|
34
107
|
this.globalCtx = (ctx) => ctx;
|
|
35
108
|
this.router = router;
|
|
36
109
|
this.globalConfig = config;
|
|
110
|
+
this.globalBeforeFilters = [];
|
|
111
|
+
this.globalAfterFilters = [];
|
|
37
112
|
|
|
38
113
|
// TODO: Pass in a path to redirect to after login?
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
114
|
+
if (config) {
|
|
115
|
+
setupAuthentication(router, config);
|
|
116
|
+
|
|
117
|
+
router.get("/", (c) => {
|
|
118
|
+
return c.redirect("/en");
|
|
119
|
+
});
|
|
120
|
+
}
|
|
44
121
|
}
|
|
45
122
|
|
|
46
123
|
setGlobalContext(globalCtx) {
|
|
47
124
|
this.globalCtx = globalCtx;
|
|
48
125
|
}
|
|
126
|
+
|
|
127
|
+
setBeforeFilter(pattern, filter) {
|
|
128
|
+
this.globalBeforeFilters.push({ pattern, filter });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
setAfterFilter(pattern, filter) {
|
|
132
|
+
this.globalAfterFilters.push({ pattern, filter });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
getActionPath(views, viewsFallback, action) {
|
|
136
|
+
const packagePath = viewsFallback
|
|
137
|
+
? `${currentPath}/views/${viewsFallback}/${action}.ejs`
|
|
138
|
+
: `${currentPath}/views/${views}/${action}.ejs`;
|
|
139
|
+
|
|
140
|
+
const appPath = `${process.cwd()}/src/views/${views}/${action}.ejs`;
|
|
141
|
+
|
|
142
|
+
return existsSync(appPath) ? appPath : packagePath;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
getHelpers(viewHelpersConfig) {
|
|
146
|
+
const t = new HTMLBuilder();
|
|
147
|
+
const f = new FormBuilder();
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
t,
|
|
151
|
+
f,
|
|
152
|
+
form_for: form_for(viewHelpersConfig),
|
|
153
|
+
history,
|
|
154
|
+
utils,
|
|
155
|
+
sort_button,
|
|
156
|
+
pagination,
|
|
157
|
+
Inflector,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async renderLayout(assigns, body) {
|
|
162
|
+
const layoutTemplate = `${currentPath}/views/layouts/application.ejs`;
|
|
163
|
+
|
|
164
|
+
return await ejs.renderFile(layoutTemplate, {
|
|
165
|
+
...assigns,
|
|
166
|
+
body,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
renderPartial(assigns, views, viewsFallback) {
|
|
171
|
+
return function render(relativePath, localAssigns) {
|
|
172
|
+
const partialPath = getPartialPath(relativePath, views, viewsFallback);
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
// Synchronously read the partial file as ejs does not support async/await
|
|
176
|
+
const partialTemplate = readFileSync(partialPath, "utf8");
|
|
177
|
+
const rendered = ejs.render(
|
|
178
|
+
partialTemplate,
|
|
179
|
+
{
|
|
180
|
+
...assigns,
|
|
181
|
+
...localAssigns,
|
|
182
|
+
},
|
|
183
|
+
{ filename: partialPath }
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
return rendered;
|
|
187
|
+
} catch (e) {
|
|
188
|
+
const error = `Error rendering:\n ${partialPath}`;
|
|
189
|
+
Logger.error(`${error}\n\n${e.stack}`);
|
|
190
|
+
|
|
191
|
+
return renderError(error, e.stack);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async renderAction(
|
|
197
|
+
{ views, viewsFallback, action, assigns },
|
|
198
|
+
viewHelpersConfig = {}
|
|
199
|
+
) {
|
|
200
|
+
// Pick the parent here in the views so we can reuse stuff...
|
|
201
|
+
const fullTemplatePath = this.getActionPath(views, viewsFallback, action);
|
|
202
|
+
const renderAssigns = { ...this.getHelpers(viewHelpersConfig), ...assigns };
|
|
203
|
+
const renderPartialHelper = this.renderPartial(
|
|
204
|
+
renderAssigns,
|
|
205
|
+
views,
|
|
206
|
+
viewsFallback
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
// Synchronously read the full template file
|
|
211
|
+
const template = readFileSync(fullTemplatePath, "utf8");
|
|
212
|
+
|
|
213
|
+
// Render the full template with synchronous partial rendering
|
|
214
|
+
const rendered = ejs.render(
|
|
215
|
+
template,
|
|
216
|
+
{
|
|
217
|
+
...renderAssigns,
|
|
218
|
+
render_partial: renderPartialHelper,
|
|
219
|
+
},
|
|
220
|
+
{ filename: fullTemplatePath }
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
const fullPage = this.renderLayout(renderAssigns, rendered);
|
|
224
|
+
|
|
225
|
+
return fullPage;
|
|
226
|
+
} catch (e) {
|
|
227
|
+
const error = `Error rendering:\n ${fullTemplatePath}`;
|
|
228
|
+
Logger.error(`${error}\n\n${e.stack}`);
|
|
229
|
+
|
|
230
|
+
return renderError(error, e.stack);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
parseFormBody(body) {
|
|
235
|
+
const result = {};
|
|
236
|
+
Object.entries(body).forEach(([key, value]) => putIn(result, key, value));
|
|
237
|
+
return result;
|
|
238
|
+
}
|
|
49
239
|
|
|
50
240
|
async createContext(router, next, verb, resource, action) {
|
|
51
241
|
const body = await parseBody(router.req);
|
|
@@ -71,198 +261,144 @@ export default class HonoAdapter extends Adapter {
|
|
|
71
261
|
});
|
|
72
262
|
}
|
|
73
263
|
|
|
74
|
-
async createAction(
|
|
75
|
-
[verb, path, resource, views, action, handler, before_filter, after_filter],
|
|
76
|
-
resourceConfig
|
|
77
|
-
) {
|
|
264
|
+
async createAction([verb, path, resource, views, action, handler], resourceConfig) {
|
|
78
265
|
const method = this.getMethod(verb);
|
|
79
|
-
|
|
80
|
-
|
|
266
|
+
const { i18n } = this.globalConfig;
|
|
267
|
+
|
|
268
|
+
Object.keys(i18n.locales).forEach((currentLocale) => {
|
|
81
269
|
const completePath = `/${currentLocale}${path}`;
|
|
82
|
-
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
});
|
|
270
|
+
|
|
271
|
+
// Clean up existing routes with same path/verb
|
|
272
|
+
this.router.router.routes = this.router.router.routes.filter(route =>
|
|
273
|
+
route[0] !== verb || route[1] !== completePath
|
|
274
|
+
);
|
|
88
275
|
|
|
89
276
|
method(completePath, async (routerCtx, next) => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
ctx.remoteConfig = { ...this.globalConfig.remoteConfig };
|
|
104
|
-
ctx.logger = Logger;
|
|
277
|
+
// Create context with request data
|
|
278
|
+
const ctx = await this.createContext(routerCtx, next, verb, resource, action);
|
|
279
|
+
|
|
280
|
+
// Set context properties
|
|
281
|
+
Object.assign(ctx, {
|
|
282
|
+
resourceConfig,
|
|
283
|
+
locales: i18n.locales,
|
|
284
|
+
defaultLocale: i18n.defaultLocale,
|
|
285
|
+
maxOpenRevisions: this.globalConfig?.maxOpenRevisions || 3,
|
|
286
|
+
locale: currentLocale,
|
|
287
|
+
remoteConfig: { ...this.globalConfig.remoteConfig },
|
|
288
|
+
logger: Logger
|
|
289
|
+
});
|
|
105
290
|
|
|
291
|
+
// Build context object
|
|
106
292
|
const _context = {
|
|
107
|
-
timezones:
|
|
293
|
+
timezones: i18n.timezones,
|
|
108
294
|
locales: ctx.locales,
|
|
109
295
|
locale: ctx.locale,
|
|
110
|
-
defaultLocale: ctx.defaultLocale,
|
|
296
|
+
defaultLocale: ctx.defaultLocale,
|
|
111
297
|
maxOpenRevisions: ctx.maxOpenRevisions,
|
|
112
298
|
path: ctx.req.path,
|
|
113
299
|
query: ctx.query || {},
|
|
114
|
-
layout: {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
resourceConfig: resourceConfig,
|
|
118
|
-
adminJwt: ctx.authentication.token,
|
|
300
|
+
layout: { ...this.globalConfig.layout },
|
|
301
|
+
resourceConfig,
|
|
302
|
+
adminJwt: ctx.authentication?.token,
|
|
119
303
|
remoteConfig: { ...this.globalConfig.remoteConfig },
|
|
120
|
-
globalConfig: this.globalConfig
|
|
304
|
+
globalConfig: this.globalConfig
|
|
121
305
|
};
|
|
122
306
|
|
|
123
|
-
ctx.logger.info(
|
|
124
|
-
|
|
125
|
-
);
|
|
126
|
-
|
|
307
|
+
ctx.logger.info(`${ctx.method} "${ctx.rawPath}" -> ${resource}/${action} | ${JSON.stringify(ctx.params)}`);
|
|
308
|
+
|
|
127
309
|
const assigns = {
|
|
128
310
|
beforeFilterAssigns: {},
|
|
129
311
|
actionAssigns: {},
|
|
130
|
-
afterFilterAssigns: {}
|
|
312
|
+
afterFilterAssigns: {}
|
|
131
313
|
};
|
|
132
314
|
|
|
133
315
|
// Initialize global context
|
|
134
316
|
ctx.initGlobalContext();
|
|
135
317
|
|
|
136
|
-
// Process before filters
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (json) {
|
|
147
|
-
return routerCtx.json(json);
|
|
318
|
+
// Process global before filters
|
|
319
|
+
for (const { pattern, filter } of this.globalBeforeFilters) {
|
|
320
|
+
if (pattern.test(ctx.path)) {
|
|
321
|
+
const result = filter(ctx) || {};
|
|
322
|
+
|
|
323
|
+
if (result.redirect) return routerCtx.redirect(result.redirect);
|
|
324
|
+
if (result.json) return routerCtx.json(result.json);
|
|
325
|
+
|
|
326
|
+
Object.assign(assigns.beforeFilterAssigns, result.assigns || {});
|
|
148
327
|
}
|
|
149
|
-
|
|
150
|
-
assigns.beforeFilterAssigns = beforeFilterAssigns;
|
|
151
328
|
}
|
|
152
329
|
|
|
153
|
-
// Process action
|
|
154
|
-
const {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
json,
|
|
171
|
-
assigns: afterFilterAssigns = {},
|
|
172
|
-
} = after_filter(ctx) || {};
|
|
173
|
-
|
|
174
|
-
if (redirect) return routerCtx.redirect(redirect);
|
|
175
|
-
|
|
176
|
-
if (json) {
|
|
177
|
-
return routerCtx.json(json);
|
|
330
|
+
// Process action handler
|
|
331
|
+
const result = await handler(ctx, resourceConfig) || {};
|
|
332
|
+
assigns.actionAssigns = result.assigns || {};
|
|
333
|
+
|
|
334
|
+
if (result.headers) this.applyHeaders(result.headers, routerCtx);
|
|
335
|
+
if (result.redirect) return routerCtx.redirect(result.redirect);
|
|
336
|
+
if (result.json) return routerCtx.json(result.json);
|
|
337
|
+
|
|
338
|
+
// Process global after filters
|
|
339
|
+
for (const { pattern, filter } of this.globalAfterFilters) {
|
|
340
|
+
if (pattern.test(ctx.path)) {
|
|
341
|
+
const afterResult = filter(ctx) || {};
|
|
342
|
+
|
|
343
|
+
if (afterResult.redirect) return routerCtx.redirect(afterResult.redirect);
|
|
344
|
+
if (afterResult.json) return routerCtx.json(afterResult.json);
|
|
345
|
+
|
|
346
|
+
Object.assign(assigns.afterFilterAssigns, afterResult.assigns || {});
|
|
178
347
|
}
|
|
179
|
-
|
|
180
|
-
assigns.afterFilterAssigns = afterFilterAssigns;
|
|
181
348
|
}
|
|
182
349
|
|
|
350
|
+
// Merge all assigns
|
|
183
351
|
const fullAssigns = {
|
|
184
352
|
...ctx.assigns,
|
|
185
353
|
...assigns.beforeFilterAssigns,
|
|
186
354
|
...assigns.actionAssigns,
|
|
187
355
|
...assigns.afterFilterAssigns,
|
|
188
|
-
_context
|
|
356
|
+
_context
|
|
189
357
|
};
|
|
190
358
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
viewsFallback: resourceConfig.viewsFallback,
|
|
199
|
-
action,
|
|
200
|
-
assigns: fullAssigns,
|
|
201
|
-
},
|
|
202
|
-
this.globalConfig?.helpers || {}
|
|
203
|
-
);
|
|
359
|
+
// Render the view
|
|
360
|
+
const rendered = this.renderAction({
|
|
361
|
+
views,
|
|
362
|
+
viewsFallback: resourceConfig.viewsFallback,
|
|
363
|
+
action,
|
|
364
|
+
assigns: fullAssigns
|
|
365
|
+
}, this.globalConfig?.helpers || {});
|
|
204
366
|
|
|
205
367
|
return routerCtx.html(rendered);
|
|
206
368
|
});
|
|
207
369
|
});
|
|
208
370
|
}
|
|
209
371
|
|
|
372
|
+
// Single method to handle all route types
|
|
210
373
|
routeCollection(mapping, resourceConfig) {
|
|
211
|
-
mapping.forEach((handlerMap)
|
|
212
|
-
this.createAction(handlerMap, resourceConfig)
|
|
213
|
-
);
|
|
374
|
+
mapping.forEach(handlerMap => this.createAction(handlerMap, resourceConfig));
|
|
214
375
|
}
|
|
215
376
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
routeMember(mapping, resourceConfig) {
|
|
223
|
-
mapping.forEach((handlerMap) =>
|
|
224
|
-
this.createAction(handlerMap, resourceConfig)
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
routeNestedMember(mapping, resourceConfig) {
|
|
229
|
-
mapping.forEach((handlerMap) =>
|
|
230
|
-
this.createAction(handlerMap, resourceConfig)
|
|
231
|
-
);
|
|
232
|
-
}
|
|
377
|
+
// Use the same implementation for all route types
|
|
378
|
+
routeNestedCollection = this.routeCollection;
|
|
379
|
+
routeMember = this.routeCollection;
|
|
380
|
+
routeNestedMember = this.routeCollection;
|
|
233
381
|
|
|
234
382
|
getMethod(verb) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
);
|
|
383
|
+
const methods = {
|
|
384
|
+
GET: this.router.get,
|
|
385
|
+
POST: this.router.post,
|
|
386
|
+
PUT: this.router.put,
|
|
387
|
+
DELETE: this.router.delete,
|
|
388
|
+
};
|
|
389
|
+
return methods[verb] || this.router.get;
|
|
243
390
|
}
|
|
244
391
|
|
|
245
392
|
applyHeaders(headers, router) {
|
|
246
|
-
if (!headers)
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
for (const [key, value] of Object.entries(headers)) {
|
|
251
|
-
router.header(key, value);
|
|
252
|
-
}
|
|
393
|
+
if (!headers) return;
|
|
394
|
+
Object.entries(headers).forEach(([key, value]) => router.header(key, value));
|
|
253
395
|
}
|
|
254
396
|
|
|
255
397
|
formatQuery(queries) {
|
|
256
398
|
const result = {};
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
} else {
|
|
262
|
-
result[key] = value;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
return { ...result };
|
|
399
|
+
Object.entries(queries).forEach(([key, value]) => {
|
|
400
|
+
result[key] = value.length === 1 ? value[0] : value;
|
|
401
|
+
});
|
|
402
|
+
return result;
|
|
267
403
|
}
|
|
268
|
-
}
|
|
404
|
+
}
|
package/src/filters.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sets a global filter to be applied to routes matching the pattern
|
|
3
|
+
* @param {HonoAdapter} adapter - The adapter instance
|
|
4
|
+
* @param {RegExp} pattern - Route pattern to match
|
|
5
|
+
* @param {Function} filter - The filter function
|
|
6
|
+
*/
|
|
7
|
+
export const setBeforeFilter = (adapter, pattern, filter) =>
|
|
8
|
+
adapter.setBeforeFilter(pattern, filter);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Sets a global after filter to be applied to routes matching the pattern
|
|
12
|
+
* @param {HonoAdapter} adapter - The adapter instance
|
|
13
|
+
* @param {RegExp} pattern - Route pattern to match
|
|
14
|
+
* @param {Function} filter - The filter function
|
|
15
|
+
*/
|
|
16
|
+
export const setAfterFilter = (adapter, pattern, filter) =>
|
|
17
|
+
adapter.setAfterFilter(pattern, filter);
|
package/src/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export { default as setupCloud } from "./setupCloud";
|
|
|
12
12
|
export { default as routeStandard } from "./routeStandard";
|
|
13
13
|
export { default as routeCategoriesModule } from "./routeCategoriesModule";
|
|
14
14
|
export { default as buildResourcePath } from "./buildResourcePath";
|
|
15
|
+
export { setBeforeFilter, setAfterFilter } from "./filters";
|
|
15
16
|
|
|
16
17
|
export { default as RaketaClient } from "./cloud/RaketaClient";
|
|
17
18
|
export { default as Redirects } from "./cloud/Redirects";
|