@axium/server 0.19.10 → 0.20.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.
Files changed (38) hide show
  1. package/build/client/_app/immutable/chunks/{tQDJvcbv.js → BayfsfvP.js} +2 -2
  2. package/build/client/_app/immutable/chunks/BayfsfvP.js.br +0 -0
  3. package/build/client/_app/immutable/chunks/BayfsfvP.js.gz +0 -0
  4. package/build/client/_app/immutable/entry/{app.DZ8N2yxy.js → app.Dq4eWLBl.js} +2 -2
  5. package/build/client/_app/immutable/entry/app.Dq4eWLBl.js.br +0 -0
  6. package/build/client/_app/immutable/entry/app.Dq4eWLBl.js.gz +0 -0
  7. package/build/client/_app/immutable/entry/start.BK9HxVV8.js +1 -0
  8. package/build/client/_app/immutable/entry/start.BK9HxVV8.js.br +2 -0
  9. package/build/client/_app/immutable/entry/start.BK9HxVV8.js.gz +0 -0
  10. package/build/client/_app/immutable/nodes/{1.BXyzO0Uc.js → 1.ByFDHqYu.js} +1 -1
  11. package/build/client/_app/immutable/nodes/1.ByFDHqYu.js.br +0 -0
  12. package/build/client/_app/immutable/nodes/1.ByFDHqYu.js.gz +0 -0
  13. package/build/client/_app/version.json +1 -1
  14. package/build/client/_app/version.json.br +0 -0
  15. package/build/client/_app/version.json.gz +0 -0
  16. package/build/server/chunks/{1-Cm-WeIXj.js → 1-x3XMevDH.js} +2 -2
  17. package/build/server/chunks/{1-Cm-WeIXj.js.map → 1-x3XMevDH.js.map} +1 -1
  18. package/build/server/chunks/hooks.server-7AF2LyfM.js +17144 -0
  19. package/build/server/chunks/hooks.server-7AF2LyfM.js.map +1 -0
  20. package/build/server/index.js +2 -2
  21. package/build/server/index.js.map +1 -1
  22. package/build/server/manifest.js +2 -2
  23. package/build/server/manifest.js.map +1 -1
  24. package/dist/cli.js +24 -11
  25. package/dist/linking.d.ts +11 -4
  26. package/dist/linking.js +48 -52
  27. package/package.json +2 -2
  28. package/build/client/_app/immutable/chunks/tQDJvcbv.js.br +0 -0
  29. package/build/client/_app/immutable/chunks/tQDJvcbv.js.gz +0 -0
  30. package/build/client/_app/immutable/entry/app.DZ8N2yxy.js.br +0 -0
  31. package/build/client/_app/immutable/entry/app.DZ8N2yxy.js.gz +0 -0
  32. package/build/client/_app/immutable/entry/start.B2xl8zGI.js +0 -1
  33. package/build/client/_app/immutable/entry/start.B2xl8zGI.js.br +0 -2
  34. package/build/client/_app/immutable/entry/start.B2xl8zGI.js.gz +0 -0
  35. package/build/client/_app/immutable/nodes/1.BXyzO0Uc.js.br +0 -0
  36. package/build/client/_app/immutable/nodes/1.BXyzO0Uc.js.gz +0 -0
  37. package/build/server/chunks/hooks.server-DxovS-nf.js +0 -1386
  38. package/build/server/chunks/hooks.server-DxovS-nf.js.map +0 -1
@@ -1,1386 +0,0 @@
1
- import * as fs from 'node:fs';
2
- import { readFileSync, existsSync, writeFileSync, createWriteStream } from 'node:fs';
3
- import 'node:http';
4
- import 'node:https';
5
- import { Logger, levelText, allLogLevels } from 'logzen';
6
- import { join, resolve, dirname } from 'node:path/posix';
7
- import './string-CafUlmcI.js';
8
- import 'node:child_process';
9
- import { homedir } from 'node:os';
10
- import { styleText } from 'node:util';
11
- import { _ as _unknown, a as _tuple, b as _array, $ as $ZodUnknown, c as $ZodArray, d as $ZodTuple, p as parse, e as parseAsync, l as literal, o as object, r as record, s as string, f as any, n as number, g as array, h as _enum, u as uuid, i as email, j as boolean, k as custom, m as looseObject, q as partialRecord, t as prettifyError, v as date, w as url } from './schemas-C2VqNPFY.js';
12
- import { serialize } from 'cookie';
13
- import { randomBytes, randomUUID } from 'node:crypto';
14
- import { Kysely, PostgresDialect } from 'kysely';
15
- import { jsonObjectFrom } from 'kysely/helpers/postgres';
16
- import pg from 'pg';
17
- import * as webauthn from '@simplewebauthn/server';
18
- import { verifyRegistrationResponse, generateRegistrationOptions } from '@simplewebauthn/server';
19
- import { render } from 'svelte/server';
20
-
21
- function filterObject(object, predicate) {
22
- const entries = Object.entries(object);
23
- return Object.fromEntries(entries.filter(([key, value]) => predicate(key, value)));
24
- }
25
- function pick(object, ...keys) {
26
- const picked = {};
27
- for (const key of keys.flat()) {
28
- picked[key] = object[key];
29
- }
30
- return picked;
31
- }
32
- function omit(object, ...keys) {
33
- return filterObject(object, key => !keys.flat().includes(key));
34
- }
35
- /**
36
- * Returns whether `value` is not a primitive.
37
- *
38
- * This function is only useful for the type check,
39
- * you can do `Object(v) === v` otherwise.
40
- */
41
- function isObject(value) {
42
- return Object(value) === value;
43
- }
44
- function deepAssign(to, from) {
45
- const keys = new Set([
46
- ...Object.keys(to),
47
- ...Object.keys(from),
48
- ]);
49
- for (const key of keys) {
50
- if (!(key in from))
51
- continue;
52
- const value = from[key];
53
- if (!(key in to)) {
54
- to[key] = value;
55
- continue;
56
- }
57
- if (!isObject(to[key]) && Object(value) !== value) {
58
- to[key] = value;
59
- continue;
60
- }
61
- if (isObject(to[key]) && Object(value) === value) {
62
- deepAssign(to[key], value);
63
- continue;
64
- }
65
- throw new TypeError(!isObject(to[key])
66
- ? 'Can not deeply assign an object to a primitive'
67
- : 'Can not deeply assign a primitive to an object');
68
- }
69
- return to;
70
- }
71
-
72
- class $ZodFunction {
73
- constructor(def) {
74
- this._def = def;
75
- this.def = def;
76
- }
77
- implement(func) {
78
- if (typeof func !== "function") {
79
- throw new Error("implement() must be called with a function");
80
- }
81
- const impl = ((...args) => {
82
- const parsedArgs = this._def.input ? parse(this._def.input, args, undefined, { callee: impl }) : args;
83
- if (!Array.isArray(parsedArgs)) {
84
- throw new Error("Invalid arguments schema: not an array or tuple schema.");
85
- }
86
- const output = func(...parsedArgs);
87
- return this._def.output ? parse(this._def.output, output, undefined, { callee: impl }) : output;
88
- });
89
- return impl;
90
- }
91
- implementAsync(func) {
92
- if (typeof func !== "function") {
93
- throw new Error("implement() must be called with a function");
94
- }
95
- const impl = (async (...args) => {
96
- const parsedArgs = this._def.input ? await parseAsync(this._def.input, args, undefined, { callee: impl }) : args;
97
- if (!Array.isArray(parsedArgs)) {
98
- throw new Error("Invalid arguments schema: not an array or tuple schema.");
99
- }
100
- const output = await func(...parsedArgs);
101
- return this._def.output ? parseAsync(this._def.output, output, undefined, { callee: impl }) : output;
102
- });
103
- return impl;
104
- }
105
- input(...args) {
106
- const F = this.constructor;
107
- if (Array.isArray(args[0])) {
108
- return new F({
109
- type: "function",
110
- input: new $ZodTuple({
111
- type: "tuple",
112
- items: args[0],
113
- rest: args[1],
114
- }),
115
- output: this._def.output,
116
- });
117
- }
118
- return new F({
119
- type: "function",
120
- input: args[0],
121
- output: this._def.output,
122
- });
123
- }
124
- output(output) {
125
- const F = this.constructor;
126
- return new F({
127
- type: "function",
128
- input: this._def.input,
129
- output,
130
- });
131
- }
132
- }
133
- function _function(params) {
134
- return new $ZodFunction({
135
- type: "function",
136
- input: Array.isArray(params?.input)
137
- ? _tuple($ZodTuple, params?.input)
138
- : (params?.input ?? _array($ZodArray, _unknown($ZodUnknown))),
139
- output: params?.output ?? _unknown($ZodUnknown),
140
- });
141
- }
142
-
143
- const sym$1 = Symbol.for('Axium:state');
144
- globalThis[sym$1] ||= Object.create({ _errored: false });
145
- let _doWarnings = false;
146
- function _duplicateStateWarnings(value) {
147
- _doWarnings = value;
148
- }
149
- /**
150
- * Prevent duplicate shared state.
151
- */
152
- function _unique(id, value) {
153
- const state = globalThis[sym$1];
154
- const _err = new Error();
155
- Error.captureStackTrace(_err, _unique);
156
- const stack = _err.stack.slice(6);
157
- if (!(id in state)) {
158
- state[id] = { value, stack };
159
- return value;
160
- }
161
- if (!state._errored) {
162
- console.error(styleText('red', 'Duplicate Axium server state! You might have multiple instances of the same module loaded.'));
163
- state._errored = true;
164
- }
165
- _doWarnings && console.warn(styleText('yellow', `Mitigating duplicate state! (${id})\n${stack}\nFrom original\n${state[id].stack}`));
166
- return state[id].value;
167
- }
168
-
169
- const systemDir = '/etc/axium';
170
- const userDir = join(homedir(), '.axium');
171
- const dirs = _unique('dirs', [systemDir, userDir]);
172
- for (let dir = resolve(process.cwd()); dir !== '/'; dir = dirname(dir)) {
173
- if (fs.existsSync(join(dir, '.axium')))
174
- dirs.push(join(dir, '.axium'));
175
- }
176
- if (process.env.AXIUM_DIR)
177
- dirs.push(process.env.AXIUM_DIR);
178
- try {
179
- fs.mkdirSync(systemDir, { recursive: true });
180
- }
181
- catch {
182
- // Missing permissions
183
- }
184
- fs.mkdirSync(userDir, { recursive: true });
185
- const logger = new Logger({
186
- hideWarningStack: true,
187
- noGlobalConsole: true,
188
- });
189
- /**
190
- * @internal
191
- */
192
- const output = {
193
- constructor: { name: 'Console' },
194
- error(message) {
195
- console.error(message.startsWith('\x1b') ? message : styleText('red', message));
196
- },
197
- warn(message) {
198
- console.warn(message.startsWith('\x1b') ? message : styleText('yellow', message));
199
- },
200
- info(message) {
201
- console.info(message.startsWith('\x1b') ? message : styleText('blue', message));
202
- },
203
- log(message) {
204
- console.log(message);
205
- },
206
- debug(message) {
207
- _debugOutput && console.debug(message.startsWith('\x1b') ? message : styleText('gray', message));
208
- },
209
- };
210
- logger.attach(output);
211
- let _debugOutput = false;
212
- /**
213
- * Enable or disable debug output.
214
- */
215
- function _setDebugOutput(enabled) {
216
- _debugOutput = enabled;
217
- }
218
- function defaultOutput(tag, message = '') {
219
- switch (tag) {
220
- case 'debug':
221
- _debugOutput && output.debug(message);
222
- break;
223
- case 'info':
224
- console.log(message);
225
- break;
226
- case 'warn':
227
- console.warn(styleText('yellow', message));
228
- break;
229
- case 'error':
230
- console.error(styleText('red', message));
231
- break;
232
- case 'start':
233
- process.stdout.write(message + '... ');
234
- break;
235
- case 'done':
236
- console.log('done.');
237
- break;
238
- case 'plugin':
239
- console.log(styleText('whiteBright', 'Running plugin: ' + message));
240
- }
241
- }
242
- let _taggedOutput = defaultOutput;
243
- // Shortcuts for tagged output
244
- function done() {
245
- _taggedOutput?.('done');
246
- }
247
- function start(message) {
248
- _taggedOutput?.('start', message);
249
- }
250
- function plugin(name) {
251
- _taggedOutput?.('plugin', name);
252
- }
253
- function debug(message) {
254
- _taggedOutput?.('debug', message);
255
- }
256
- function warn(message) {
257
- _taggedOutput?.('warn', message);
258
- }
259
- /**
260
- * This is a factory for handling errors when performing operations.
261
- * The handler will allow the parent scope to continue if a relation already exists,
262
- * rather than fatally exiting.
263
- */
264
- function someWarnings(...allowList) {
265
- return (error) => {
266
- error = typeof error == 'object' && 'message' in error ? error.message : error;
267
- for (const [pattern, message = error] of allowList) {
268
- if (!pattern.test(error))
269
- continue;
270
- warn(message);
271
- return;
272
- }
273
- throw error;
274
- };
275
- }
276
-
277
- function zAsyncFunction(schema) {
278
- return custom((fn) => schema.implementAsync(fn));
279
- }
280
- const transports = ['ble', 'cable', 'hybrid', 'internal', 'nfc', 'smart-card', 'usb'];
281
- const authenticatorAttachment = literal(['platform', 'cross-platform']).optional();
282
- const PasskeyRegistration = object({
283
- id: string(),
284
- rawId: string(),
285
- response: object({
286
- clientDataJSON: string(),
287
- attestationObject: string(),
288
- authenticatorData: string().optional(),
289
- transports: array(_enum(transports)).optional(),
290
- publicKeyAlgorithm: number().optional(),
291
- publicKey: string().optional(),
292
- }),
293
- authenticatorAttachment,
294
- clientExtensionResults: record(any(), any()),
295
- type: literal('public-key'),
296
- });
297
- /**
298
- * POSTed to the `/users/:id/login` endpoint.
299
- */
300
- const PasskeyAuthenticationResponse = object({
301
- id: string(),
302
- rawId: string(),
303
- response: object({
304
- clientDataJSON: string(),
305
- authenticatorData: string(),
306
- signature: string(),
307
- userHandle: string().optional(),
308
- }),
309
- authenticatorAttachment,
310
- clientExtensionResults: record(any(), any()),
311
- type: literal('public-key'),
312
- });
313
- const APIUserRegistration = object({
314
- name: string().min(1).max(100),
315
- email: email(),
316
- userId: uuid(),
317
- response: PasskeyRegistration,
318
- });
319
- const PasskeyChangeable = object({ name: string() }).partial();
320
- const UserAuthOptions = object({ type: literal(['login', 'action']) });
321
- const LogoutSessions = object({
322
- id: array(uuid()).optional(),
323
- confirm_all: boolean().optional(),
324
- });
325
-
326
- const PluginMetadata = looseObject({
327
- name: string(),
328
- version: string(),
329
- description: string().optional(),
330
- routes: string().optional(),
331
- });
332
- const hookNames = ['db_init', 'remove', 'db_wipe', 'clean'];
333
- const fn = custom(data => typeof data === 'function');
334
- const Plugin = PluginMetadata.extend({
335
- statusText: zAsyncFunction(_function({ input: [], output: string() })),
336
- hooks: partialRecord(literal(hookNames), fn).optional(),
337
- });
338
- const kSpecifier = Symbol('specifier');
339
- const plugins = _unique('plugins', new Set());
340
- async function loadPlugin(specifier) {
341
- try {
342
- const imported = await import(/* @vite-ignore */ specifier);
343
- const maybePlugin = 'default' in imported ? imported.default : imported;
344
- const plugin = Object.assign({ hooks: {}, [kSpecifier]: specifier }, await Plugin.parseAsync(maybePlugin).catch(e => {
345
- throw prettifyError(e);
346
- }));
347
- if (plugin.name.startsWith('#') || plugin.name.includes(' ')) {
348
- throw 'Invalid plugin name. Plugin names can not start with a hash or contain spaces.';
349
- }
350
- plugins.add(plugin);
351
- output.debug(`Loaded plugin: ${plugin.name} ${plugin.version}`);
352
- }
353
- catch (e) {
354
- output.debug(`Failed to load plugin from ${specifier}: ${e ? (e instanceof Error ? e.message : e.toString()) : e}`);
355
- }
356
- }
357
-
358
- const ConfigSchema = looseObject({
359
- allow_new_users: boolean(),
360
- api: looseObject({
361
- disable_metadata: boolean(),
362
- cookie_auth: boolean(),
363
- })
364
- .partial(),
365
- apps: looseObject({
366
- disabled: array(string()),
367
- })
368
- .partial(),
369
- auth: looseObject({
370
- origin: string(),
371
- /** In minutes */
372
- passkey_probation: number(),
373
- rp_id: string(),
374
- rp_name: string(),
375
- secure_cookies: boolean(),
376
- /** In minutes */
377
- verification_timeout: number(),
378
- /** Whether users can verify emails */
379
- email_verification: boolean(),
380
- })
381
- .partial(),
382
- db: looseObject({
383
- host: string(),
384
- port: number(),
385
- password: string(),
386
- user: string(),
387
- database: string(),
388
- })
389
- .partial(),
390
- debug: boolean(),
391
- log: looseObject({
392
- level: _enum(levelText),
393
- console: boolean(),
394
- })
395
- .partial(),
396
- show_duplicate_state: boolean(),
397
- web: looseObject({
398
- assets: string(),
399
- build: string(),
400
- disable_cache: boolean(),
401
- port: number().min(1).max(65535),
402
- prefix: string(),
403
- routes: string(),
404
- secure: boolean(),
405
- ssl_key: string(),
406
- ssl_cert: string(),
407
- template: string(),
408
- })
409
- .partial(),
410
- })
411
- .partial();
412
- const configFiles = _unique('configFiles', new Map());
413
- function plainConfig() {
414
- return omit(config, Object.keys(configShortcuts));
415
- }
416
- const configShortcuts = {
417
- findPath: findConfigPaths,
418
- load: loadConfig,
419
- loadDefaults: loadDefaultConfigs,
420
- plain: plainConfig,
421
- save: saveConfig,
422
- saveTo: saveConfigTo,
423
- set: setConfig,
424
- files: configFiles,
425
- };
426
- const config = _unique('config', {
427
- ...configShortcuts,
428
- allow_new_users: true,
429
- api: {
430
- disable_metadata: false,
431
- cookie_auth: true,
432
- },
433
- apps: {
434
- disabled: [],
435
- },
436
- auth: {
437
- origin: 'https://test.localhost',
438
- passkey_probation: 60,
439
- rp_id: 'test.localhost',
440
- rp_name: 'Axium',
441
- secure_cookies: true,
442
- verification_timeout: 60,
443
- email_verification: false,
444
- },
445
- db: {
446
- database: process.env.PGDATABASE || 'axium',
447
- host: process.env.PGHOST || 'localhost',
448
- password: process.env.PGPASSWORD || '',
449
- port: process.env.PGPORT && Number.isSafeInteger(parseInt(process.env.PGPORT)) ? parseInt(process.env.PGPORT) : 5432,
450
- user: process.env.PGUSER || 'axium',
451
- },
452
- debug: false,
453
- log: {
454
- console: true,
455
- level: 'info',
456
- },
457
- show_duplicate_state: false,
458
- web: {
459
- assets: '',
460
- build: '../build/handler.js',
461
- disable_cache: false,
462
- port: 443,
463
- prefix: '',
464
- routes: 'routes',
465
- secure: true,
466
- ssl_key: resolve(dirs[0], 'ssl_key.pem'),
467
- ssl_cert: resolve(dirs[0], 'ssl_cert.pem'),
468
- template: join(import.meta.dirname, '../web/template.html'),
469
- },
470
- });
471
- // config from file
472
- const FileSchema = looseObject({
473
- ...ConfigSchema.shape,
474
- include: array(string()),
475
- plugins: array(string()),
476
- })
477
- .partial();
478
- /**
479
- * Update the current config
480
- */
481
- function setConfig(other) {
482
- deepAssign(config, other);
483
- logger.detach(output);
484
- if (config.log.console)
485
- logger.attach(output, { output: config.log.level });
486
- _setDebugOutput(config.debug);
487
- _duplicateStateWarnings(config.show_duplicate_state);
488
- }
489
- /**
490
- * Load the config from the provided path
491
- */
492
- async function loadConfig(path, options = {}) {
493
- if (configFiles.has(path))
494
- return;
495
- let json;
496
- try {
497
- json = JSON.parse(readFileSync(path, 'utf8'));
498
- }
499
- catch (e) {
500
- if (!options.optional)
501
- throw e;
502
- output.debug(`Skipping config at ${path} (${e.message})`);
503
- return;
504
- }
505
- let file;
506
- try {
507
- file = FileSchema.parse(json);
508
- }
509
- catch (e) {
510
- if (!options.loose)
511
- throw e;
512
- output.debug(`Loading invalid config from ${path} (${e.message})`);
513
- file = json;
514
- }
515
- configFiles.set(path, file);
516
- setConfig(file);
517
- output.debug('Loaded config: ' + path);
518
- for (const include of file.include ?? [])
519
- await loadConfig(join(dirname(path), include), { optional: true });
520
- for (const plugin of file.plugins ?? [])
521
- await loadPlugin(plugin.startsWith('.') ? resolve(dirname(path), plugin) : plugin);
522
- }
523
- async function loadDefaultConfigs() {
524
- for (const path of findConfigPaths()) {
525
- if (!existsSync(path)) {
526
- try {
527
- writeFileSync(path, '{}');
528
- }
529
- catch {
530
- continue;
531
- }
532
- }
533
- await loadConfig(path, { optional: true });
534
- }
535
- }
536
- /**
537
- * Update the current config and write the updated config to the appropriate file
538
- */
539
- function saveConfig(changed, global = false) {
540
- saveConfigTo(findConfigPaths().at(global ? 0 : -1), changed);
541
- }
542
- /**
543
- * Update the current config and write the updated config to the provided path
544
- */
545
- function saveConfigTo(path, changed) {
546
- setConfig(changed);
547
- const config = configFiles.get(path) ?? {};
548
- Object.assign(config, { ...changed, db: { ...config.db, ...changed.db } });
549
- output.debug(`Wrote config to ${path}`);
550
- writeFileSync(path, JSON.stringify(config));
551
- }
552
- /**
553
- * Find the path to the config file(s)
554
- * This array should roughly be in the order of most global to most local.
555
- */
556
- function findConfigPaths() {
557
- const paths = dirs.map(dir => join(dir, 'config.json'));
558
- if (process.env.AXIUM_CONFIG)
559
- paths.push(process.env.AXIUM_CONFIG);
560
- return paths;
561
- }
562
- if (process.env.AXIUM_CONFIG)
563
- await loadConfig(process.env.AXIUM_CONFIG);
564
-
565
- const requestMethods = ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD', 'PATCH'];
566
-
567
- var version = "0.19.10";
568
- var pkg = {
569
- version: version};
570
-
571
- const User = object({
572
- id: uuid(),
573
- name: string().min(1, 'Name is required').max(255, 'Name is too long'),
574
- email: email(),
575
- emailVerified: date().nullable().optional(),
576
- image: url().nullable().optional(),
577
- preferences: record(string(), any()),
578
- roles: array(string()),
579
- registeredAt: date(),
580
- });
581
- const userPublicFields = ['id', 'image', 'name', 'registeredAt', 'roles'];
582
- const userProtectedFields = ['email', 'emailVerified', 'preferences'];
583
- const UserChangeable = User.pick({
584
- name: true,
585
- email: true,
586
- image: true,
587
- preferences: true,
588
- }).partial();
589
-
590
- (undefined && undefined.__addDisposableResource) || function (env, value, async) {
591
- if (value !== null && value !== void 0) {
592
- if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
593
- var dispose, inner;
594
- if (async) {
595
- if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
596
- dispose = value[Symbol.asyncDispose];
597
- }
598
- if (dispose === void 0) {
599
- if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
600
- dispose = value[Symbol.dispose];
601
- if (async) inner = dispose;
602
- }
603
- if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
604
- if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
605
- env.stack.push({ value: value, dispose: dispose, async: async });
606
- }
607
- else if (async) {
608
- env.stack.push({ async: true });
609
- }
610
- return value;
611
- };
612
- (undefined && undefined.__disposeResources) || (function (SuppressedError) {
613
- return function (env) {
614
- function fail(e) {
615
- env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
616
- env.hasError = true;
617
- }
618
- var r, s = 0;
619
- function next() {
620
- while (r = env.stack.pop()) {
621
- try {
622
- if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
623
- if (r.dispose) {
624
- var result = r.dispose.call(r.value);
625
- if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
626
- }
627
- else s |= 1;
628
- }
629
- catch (e) {
630
- fail(e);
631
- }
632
- }
633
- if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
634
- if (env.hasError) throw env.error;
635
- }
636
- return next();
637
- };
638
- })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
639
- var e = new Error(message);
640
- return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
641
- });
642
- const sym = Symbol.for('Axium:database');
643
- let database;
644
- function connect() {
645
- if (database)
646
- return database;
647
- if (globalThis[sym])
648
- return (database = globalThis[sym]);
649
- database = new Kysely({
650
- dialect: new PostgresDialect({ pool: new pg.Pool(config.db) }),
651
- });
652
- globalThis[sym] = database;
653
- debug('Connected to database!');
654
- return database;
655
- }
656
- /**
657
- * Select the user with the id from the userId column of a table, placing it in the `user` property.
658
- */
659
- function userFromId(builder) {
660
- const eb = builder;
661
- return jsonObjectFrom(eb.selectFrom('users').selectAll().whereRef('id', '=', 'userId'))
662
- .$notNull()
663
- .$castTo()
664
- .as('user');
665
- }
666
- /** Shortcut to output a warning if an error is thrown because relation already exists */
667
- someWarnings([/\w+ "[\w.]+" already exists/, 'already exists.']);
668
- async function clean(opt) {
669
- const now = new Date();
670
- start('Removing expired sessions');
671
- await database.deleteFrom('sessions').where('sessions.expires', '<', now).execute().then(done);
672
- start('Removing expired verifications');
673
- await database.deleteFrom('verifications').where('verifications.expires', '<', now).execute().then(done);
674
- for (const plugin$1 of plugins) {
675
- if (!plugin$1.hooks.clean)
676
- continue;
677
- plugin(plugin$1.name);
678
- await plugin$1.hooks.clean(opt);
679
- }
680
- }
681
-
682
- async function getUser(id) {
683
- return await database.selectFrom('users').selectAll().where('id', '=', id).executeTakeFirstOrThrow();
684
- }
685
- const in30days = () => new Date(Date.now() + 2592000000);
686
- const in10minutes = () => new Date(Date.now() + 600000);
687
- async function createSession(userId, elevated = false) {
688
- const session = {
689
- id: randomUUID(),
690
- userId,
691
- token: randomBytes(64).toString('base64'),
692
- expires: elevated ? in10minutes() : in30days(),
693
- elevated,
694
- created: new Date(),
695
- };
696
- await database.insertInto('sessions').values(session).execute();
697
- return session;
698
- }
699
- async function getSessionAndUser(token) {
700
- const result = await database
701
- .selectFrom('sessions')
702
- .selectAll()
703
- .select(userFromId)
704
- .where('sessions.token', '=', token)
705
- .where('sessions.expires', '>', new Date())
706
- .executeTakeFirstOrThrow();
707
- if (!result.user)
708
- throw new Error('Session references non-existing user');
709
- return result;
710
- }
711
- async function getSessions(userId) {
712
- return await database.selectFrom('sessions').selectAll().where('userId', '=', userId).where('sessions.expires', '>', new Date()).execute();
713
- }
714
- /**
715
- * Create a verification
716
- * @param expires How long the token should be valid for in seconds
717
- */
718
- async function createVerification(role, userId, expires) {
719
- const token = randomBytes(64).toString('base64url');
720
- const verification = { userId, token, expires: new Date(Date.now() + expires * 1000), role };
721
- await database.insertInto('verifications').values(verification).executeTakeFirstOrThrow();
722
- setTimeout(() => {
723
- void database.deleteFrom('verifications').where('verifications.token', '=', verification.token).execute();
724
- }, expires * 1000);
725
- return verification;
726
- }
727
- async function useVerification(role, userId, token) {
728
- const query = database
729
- .deleteFrom('verifications')
730
- .where('verifications.token', '=', token)
731
- .where('verifications.userId', '=', userId)
732
- .where('verifications.role', '=', role);
733
- return await query.returningAll().executeTakeFirst();
734
- }
735
- async function getPasskey(id) {
736
- return await database.selectFrom('passkeys').selectAll().where('id', '=', id).executeTakeFirstOrThrow();
737
- }
738
- async function createPasskey(passkey) {
739
- const result = await database.insertInto('passkeys').values(passkey).returningAll().executeTakeFirstOrThrow();
740
- return result;
741
- }
742
- async function getPasskeysByUserId(userId) {
743
- return await database.selectFrom('passkeys').selectAll().where('userId', '=', userId).execute();
744
- }
745
- async function checkAuthForUser(event, userId, sensitive = false) {
746
- const token = getToken(event, sensitive);
747
- if (!token)
748
- throw error(401, 'Missing token');
749
- const session = await getSessionAndUser(token).catch(withError('Invalid or expired session', 401));
750
- if (session.userId !== userId) {
751
- if (!session.user?.isAdmin)
752
- error(403, 'User ID mismatch');
753
- // Admins are allowed to manage other users.
754
- const accessor = session.user;
755
- session.user = await getUser(userId).catch(withError('Target user not found', 404));
756
- return Object.assign(session, { accessor });
757
- }
758
- if (!session.elevated && sensitive)
759
- error(403, 'This token can not be used for sensitive actions');
760
- return Object.assign(session, { accessor: session.user });
761
- }
762
-
763
- function isResponseError(e) {
764
- return e instanceof Error && e.name === 'ResponseError' && typeof e.status === 'number';
765
- }
766
- function error(status, message) {
767
- const error = Object.assign(new Error(message), { status });
768
- error.name = 'ResponseError';
769
- throw error;
770
- }
771
- function isRedirect(e) {
772
- return typeof e === 'object' && e !== null && 'location' in e && 'status' in e;
773
- }
774
- function json(data, init) {
775
- const response = Response.json(data, init);
776
- if (!response.headers.has('content-length')) {
777
- response.headers.set('content-length', JSON.stringify(data).length.toString());
778
- }
779
- return response;
780
- }
781
- async function parseBody(event, schema) {
782
- const contentType = event.request.headers.get('content-type');
783
- if (!contentType || !contentType.includes('application/json'))
784
- error(415, 'Invalid content type');
785
- const body = await event.request.json().catch(() => error(415, 'Invalid JSON'));
786
- try {
787
- return schema.parse(body);
788
- }
789
- catch (e) {
790
- error(400, prettifyError(e));
791
- }
792
- }
793
- function getToken(event, sensitive = false) {
794
- const header_token = event.request.headers.get('Authorization')?.replace('Bearer ', '');
795
- if (header_token)
796
- return header_token;
797
- if (config.debug || config.api.cookie_auth) {
798
- return event.cookies.get(sensitive ? 'elevated_token' : 'session_token');
799
- }
800
- }
801
- async function createSessionData(userId, elevated = false) {
802
- const { token, expires } = await createSession(userId, elevated);
803
- const response = json({ userId, token: elevated ? '[[redacted:elevated]]' : token }, { status: 201 });
804
- const cookies = serialize(elevated ? 'elevated_token' : 'session_token', token, {
805
- httpOnly: true,
806
- path: '/',
807
- expires,
808
- secure: config.auth.secure_cookies,
809
- sameSite: 'lax',
810
- });
811
- response.headers.set('Set-Cookie', cookies);
812
- return response;
813
- }
814
- function stripUser(user, includeProtected = false) {
815
- return pick(user, ...userPublicFields, ...(includeProtected ? userProtectedFields : []));
816
- }
817
- function withError(text, code = 500) {
818
- return function (e) {
819
- if (e.name == 'ResponseError')
820
- throw e;
821
- error(code, text + (config.debug && e.message ? `: ${e.message}` : ''));
822
- };
823
- }
824
- async function handleAPIRequest(event, route) {
825
- const method = event.request.method;
826
- const _warnings = [];
827
- if (route.api && !event.request.headers.get('Accept')?.includes('application/json')) {
828
- _warnings.push('Only application/json is supported');
829
- event.request.headers.set('Accept', 'application/json');
830
- }
831
- for (const [key, type] of Object.entries(route.params || {})) {
832
- if (!type)
833
- continue;
834
- try {
835
- event.params[key] = type.parse(event.params[key]);
836
- }
837
- catch (e) {
838
- error(400, `Invalid parameter: ${prettifyError(e)}`);
839
- }
840
- }
841
- if (typeof route[method] != 'function')
842
- error(405, `Method ${method} not allowed for ${route.path}`);
843
- const result = await route[method](event);
844
- if (result instanceof Response)
845
- return result;
846
- result._warnings ||= [];
847
- result._warnings.push(..._warnings);
848
- return json(result);
849
- }
850
- function handleResponseError(e) {
851
- if (isResponseError(e))
852
- return json({ message: e.message }, { status: e.status });
853
- if (isRedirect(e))
854
- return Response.redirect(e.location, e.status);
855
- console.error(e);
856
- return json({ message: 'Internal Error' + (config.debug ? ': ' + e.message : '') }, { status: 500 });
857
- }
858
- const noCacheHeaders = {
859
- 'Content-Type': 'text/html; charset=utf-8',
860
- 'Cache-Control': 'no-cache, no-store, must-revalidate',
861
- Pragma: 'no-cache',
862
- Expires: '0',
863
- };
864
-
865
- const apps = _unique('apps', new Map());
866
- const appDisabledContent = {
867
- head: '<title>App Disabled</title>',
868
- body: '<h1>App Disabled</h1><p>This app is currently disabled.</p>',
869
- };
870
-
871
- /**
872
- * @internal
873
- */
874
- const routes = _unique('routes', new Map());
875
- /**
876
- * @category Plugin API
877
- */
878
- function addRoute(opt) {
879
- const route = { ...opt, server: !('page' in opt) };
880
- if (!route.path.startsWith('/')) {
881
- throw new Error(`Route path must start with a slash: ${route.path}`);
882
- }
883
- if (route.path.startsWith('/api/'))
884
- route.api = true;
885
- if (route.api && !route.server)
886
- throw new Error(`API routes cannot have a client page: ${route.path}`);
887
- routes.set(route.path, route);
888
- output.debug('Added route: ' + route.path);
889
- }
890
- /**
891
- * Resolve a request URL into a route.
892
- * This handles parsing of parameters in the URL.
893
- */
894
- function resolveRoute(event) {
895
- const { pathname } = event.url;
896
- if (routes.has(pathname) && !pathname.split('/').some(p => p.startsWith(':')))
897
- return routes.get(pathname);
898
- // Otherwise we must have a parameterized route
899
- _routes: for (const route of routes.values()) {
900
- const params = {};
901
- // Split the path and route into parts, zipped together
902
- const pathParts = pathname.split('/').filter(Boolean);
903
- // Skips routes in disabled apps
904
- if (apps.has(pathParts[0]) && config.apps.disabled.includes(pathParts[0]))
905
- continue;
906
- for (const routePart of route.path.split('/').filter(Boolean)) {
907
- const pathPart = pathParts.shift();
908
- if (!pathPart)
909
- continue _routes;
910
- if (pathPart == routePart)
911
- continue;
912
- if (!routePart.startsWith(':'))
913
- continue _routes;
914
- params[routePart.slice(1)] = pathPart;
915
- }
916
- // we didn't find a match, since an exact match would have been found already
917
- if (pathParts.length || !Object.keys(params).length)
918
- continue;
919
- event.params = params;
920
- return route;
921
- }
922
- }
923
-
924
- addRoute({
925
- path: '/api/metadata',
926
- async GET() {
927
- if (config.api.disable_metadata)
928
- error(401, 'API metadata is disabled');
929
- return {
930
- version: pkg.version,
931
- routes: Object.fromEntries(routes
932
- .entries()
933
- .filter(([path]) => path.startsWith('/api/'))
934
- .map(([path, route]) => [
935
- path,
936
- {
937
- params: Object.fromEntries(Object.entries(route.params || {}).map(([key, type]) => [key, type ? type.def.type : null])),
938
- methods: requestMethods.filter(m => m in route),
939
- },
940
- ])),
941
- plugins: Object.fromEntries(plugins.values().map(plugin => [plugin.name, plugin.version])),
942
- };
943
- },
944
- });
945
-
946
- addRoute({
947
- path: '/api/passkeys/:id',
948
- params: {
949
- id: string(),
950
- },
951
- async GET(event) {
952
- const passkey = await getPasskey(event.params.id);
953
- await checkAuthForUser(event, passkey.userId);
954
- return omit(passkey, 'counter', 'publicKey');
955
- },
956
- async PATCH(event) {
957
- const body = await parseBody(event, PasskeyChangeable);
958
- const passkey = await getPasskey(event.params.id);
959
- await checkAuthForUser(event, passkey.userId);
960
- const result = await database
961
- .updateTable('passkeys')
962
- .set(body)
963
- .where('id', '=', passkey.id)
964
- .returningAll()
965
- .executeTakeFirstOrThrow()
966
- .catch(withError('Could not update passkey'));
967
- return omit(result, 'counter', 'publicKey');
968
- },
969
- async DELETE(event) {
970
- const passkey = await getPasskey(event.params.id);
971
- await checkAuthForUser(event, passkey.userId);
972
- const { count } = await database
973
- .selectFrom('passkeys')
974
- .select(database.fn.countAll().as('count'))
975
- .where('userId', '=', passkey.userId)
976
- .executeTakeFirstOrThrow();
977
- if (Number(count) <= 1)
978
- error(409, 'At least one passkey is required');
979
- const result = await database
980
- .deleteFrom('passkeys')
981
- .where('id', '=', passkey.id)
982
- .returningAll()
983
- .executeTakeFirstOrThrow()
984
- .catch(withError('Could not delete passkey'));
985
- return omit(result, 'counter', 'publicKey');
986
- },
987
- });
988
-
989
- // Map of user ID => challenge
990
- const registrations$1 = new Map();
991
- async function OPTIONS(event) {
992
- if (!config.allow_new_users)
993
- error(409, 'New user registration is disabled');
994
- const { name, email: email$1 } = await parseBody(event, object({ name: string().optional(), email: email().optional() }));
995
- const userId = randomUUID();
996
- const user = await getUser(userId).catch(() => null);
997
- if (user)
998
- error(409, 'Generated UUID is already in use, please retry.');
999
- const options = await generateRegistrationOptions({
1000
- rpName: config.auth.rp_name,
1001
- rpID: config.auth.rp_id,
1002
- userName: email$1 ?? userId,
1003
- userDisplayName: name,
1004
- attestationType: 'none',
1005
- excludeCredentials: [],
1006
- authenticatorSelection: {
1007
- residentKey: 'preferred',
1008
- userVerification: 'preferred',
1009
- authenticatorAttachment: 'platform',
1010
- },
1011
- });
1012
- registrations$1.set(userId, options.challenge);
1013
- return { userId, options };
1014
- }
1015
- async function POST(event) {
1016
- if (!config.allow_new_users)
1017
- error(409, 'New user registration is disabled');
1018
- const { userId, email, name, response } = await parseBody(event, APIUserRegistration);
1019
- const existing = await database.selectFrom('users').selectAll().where('email', '=', email.toLowerCase()).executeTakeFirst();
1020
- if (existing)
1021
- error(409, 'Email already in use');
1022
- const expectedChallenge = registrations$1.get(userId);
1023
- if (!expectedChallenge)
1024
- error(404, 'No registration challenge found for this user');
1025
- registrations$1.delete(userId);
1026
- const { verified, registrationInfo } = await verifyRegistrationResponse({
1027
- response,
1028
- expectedChallenge,
1029
- expectedOrigin: config.auth.origin,
1030
- }).catch(() => error(400, 'Verification failed'));
1031
- if (!verified || !registrationInfo)
1032
- error(401, 'Verification failed');
1033
- await database
1034
- .insertInto('users')
1035
- .values({ id: userId, name, email: email.toLowerCase() })
1036
- .executeTakeFirstOrThrow()
1037
- .catch(withError('Failed to create user'));
1038
- await createPasskey({
1039
- transports: [],
1040
- ...registrationInfo.credential,
1041
- userId,
1042
- deviceType: registrationInfo.credentialDeviceType,
1043
- backedUp: registrationInfo.credentialBackedUp,
1044
- }).catch(withError('Failed to create passkey', 500));
1045
- return await createSessionData(userId);
1046
- }
1047
- addRoute({
1048
- path: '/api/register',
1049
- params: {},
1050
- OPTIONS,
1051
- POST,
1052
- });
1053
-
1054
- addRoute({
1055
- path: '/api/session',
1056
- async GET(event) {
1057
- const token = getToken(event);
1058
- if (!token)
1059
- error(401, 'Missing token');
1060
- const result = await getSessionAndUser(token).catch(withError('Invalid session', 400));
1061
- return {
1062
- ...omit(result, 'token'),
1063
- user: stripUser(result.user, true),
1064
- };
1065
- },
1066
- async DELETE(event) {
1067
- const token = getToken(event);
1068
- if (!token)
1069
- error(401, 'Missing token');
1070
- const result = await database
1071
- .deleteFrom('sessions')
1072
- .where('sessions.token', '=', token)
1073
- .returningAll()
1074
- .executeTakeFirstOrThrow()
1075
- .catch((e) => (e.message == 'no result' ? error(404, 'Session does not exist') : error(400, 'Invalid session')));
1076
- return omit(result, 'token');
1077
- },
1078
- });
1079
-
1080
- const challenges = new Map();
1081
- const params = { id: uuid() };
1082
- /**
1083
- * Resolve a user's UUID using their email (in the future this might also include handles)
1084
- */
1085
- addRoute({
1086
- path: '/api/user_id',
1087
- async POST(event) {
1088
- const { value } = await parseBody(event, object({ using: literal('email'), value: email() }));
1089
- const { id } = await database
1090
- .selectFrom('users')
1091
- .select('id')
1092
- .where('email', '=', value)
1093
- .executeTakeFirstOrThrow()
1094
- .catch(withError('User not found', 404));
1095
- return { id };
1096
- },
1097
- });
1098
- addRoute({
1099
- path: '/api/users/:id',
1100
- params,
1101
- async GET(event) {
1102
- const userId = event.params.id;
1103
- const auth = await checkAuthForUser(event, userId).catch(() => null);
1104
- const user = auth?.user || (await getUser(userId).catch(withError('User does not exist', 404)));
1105
- return stripUser(user, !!auth);
1106
- },
1107
- async PATCH(event) {
1108
- const userId = event.params.id;
1109
- const body = await parseBody(event, UserChangeable);
1110
- await checkAuthForUser(event, userId);
1111
- if ('email' in body)
1112
- body.emailVerified = null;
1113
- const result = await database
1114
- .updateTable('users')
1115
- .set(body)
1116
- .where('id', '=', userId)
1117
- .returningAll()
1118
- .executeTakeFirstOrThrow()
1119
- .catch(withError('Failed to update user'));
1120
- return stripUser(result, true);
1121
- },
1122
- async DELETE(event) {
1123
- const userId = event.params.id;
1124
- await checkAuthForUser(event, userId, true);
1125
- const result = await database
1126
- .deleteFrom('users')
1127
- .where('id', '=', userId)
1128
- .returningAll()
1129
- .executeTakeFirstOrThrow()
1130
- .catch(withError('Failed to delete user'));
1131
- return result;
1132
- },
1133
- });
1134
- addRoute({
1135
- path: '/api/users/:id/full',
1136
- params,
1137
- async GET(event) {
1138
- const userId = event.params.id;
1139
- const { user } = await checkAuthForUser(event, userId);
1140
- const sessions = await getSessions(userId);
1141
- return {
1142
- ...stripUser(user, true),
1143
- sessions: sessions.map(s => omit(s, 'token')),
1144
- };
1145
- },
1146
- });
1147
- addRoute({
1148
- path: '/api/users/:id/auth',
1149
- params,
1150
- async OPTIONS(event) {
1151
- const userId = event.params.id;
1152
- const { type } = await parseBody(event, UserAuthOptions);
1153
- await getUser(userId).catch(withError('User does not exist', 404));
1154
- const passkeys = await getPasskeysByUserId(userId);
1155
- if (!passkeys)
1156
- error(409, 'No passkeys exists for this user');
1157
- const options = await webauthn.generateAuthenticationOptions({
1158
- rpID: config.auth.rp_id,
1159
- allowCredentials: passkeys.map(passkey => pick(passkey, 'id', 'transports')),
1160
- });
1161
- challenges.set(userId, { data: options.challenge, type });
1162
- return options;
1163
- },
1164
- async POST(event) {
1165
- const userId = event.params.id;
1166
- const response = await parseBody(event, PasskeyAuthenticationResponse);
1167
- const auth = challenges.get(userId);
1168
- if (!auth)
1169
- error(404, 'No challenge');
1170
- const { data: expectedChallenge, type } = auth;
1171
- challenges.delete(userId);
1172
- const passkey = await getPasskey(response.id).catch(withError('Passkey does not exist', 404));
1173
- if (passkey.userId !== userId)
1174
- error(403, 'Passkey does not belong to this user');
1175
- const { verified } = await webauthn
1176
- .verifyAuthenticationResponse({
1177
- response,
1178
- credential: passkey,
1179
- expectedChallenge,
1180
- expectedOrigin: config.auth.origin,
1181
- expectedRPID: config.auth.rp_id,
1182
- })
1183
- .catch(withError('Verification failed', 400));
1184
- if (!verified)
1185
- error(401, 'Verification failed');
1186
- switch (type) {
1187
- case 'login':
1188
- return await createSessionData(userId);
1189
- case 'action':
1190
- if ((Date.now() - passkey.createdAt.getTime()) / 60_000 < config.auth.passkey_probation)
1191
- error(403, 'You can not authorize sensitive actions with a newly created passkey');
1192
- return await createSessionData(userId, true);
1193
- }
1194
- },
1195
- });
1196
- // Map of user ID => challenge
1197
- const registrations = new Map();
1198
- addRoute({
1199
- path: '/api/users/:id/passkeys',
1200
- params,
1201
- /**
1202
- * Get passkey registration options for a user.
1203
- */
1204
- async OPTIONS(event) {
1205
- const userId = event.params.id;
1206
- const existing = await getPasskeysByUserId(userId);
1207
- const { user } = await checkAuthForUser(event, userId);
1208
- const options = await webauthn.generateRegistrationOptions({
1209
- rpName: config.auth.rp_name,
1210
- rpID: config.auth.rp_id,
1211
- userName: userId,
1212
- userDisplayName: user.email,
1213
- attestationType: 'none',
1214
- excludeCredentials: existing.map(passkey => pick(passkey, 'id', 'transports')),
1215
- authenticatorSelection: {
1216
- residentKey: 'preferred',
1217
- userVerification: 'preferred',
1218
- authenticatorAttachment: 'platform',
1219
- },
1220
- });
1221
- registrations.set(userId, options.challenge);
1222
- return options;
1223
- },
1224
- /**
1225
- * Get passkeys for a user.
1226
- */
1227
- async GET(event) {
1228
- const userId = event.params.id;
1229
- await checkAuthForUser(event, userId);
1230
- const passkeys = await getPasskeysByUserId(userId);
1231
- return passkeys.map(p => omit(p, 'publicKey', 'counter'));
1232
- },
1233
- /**
1234
- * Register a new passkey for an existing user.
1235
- */
1236
- async PUT(event) {
1237
- const userId = event.params.id;
1238
- const response = await parseBody(event, PasskeyRegistration);
1239
- await checkAuthForUser(event, userId);
1240
- const expectedChallenge = registrations.get(userId);
1241
- if (!expectedChallenge)
1242
- error(404, 'No registration challenge found for this user');
1243
- registrations.delete(userId);
1244
- const { verified, registrationInfo } = await webauthn
1245
- .verifyRegistrationResponse({
1246
- response,
1247
- expectedChallenge,
1248
- expectedOrigin: config.auth.origin,
1249
- })
1250
- .catch(withError('Verification failed', 400));
1251
- if (!verified || !registrationInfo)
1252
- error(401, 'Verification failed');
1253
- const passkey = await createPasskey({
1254
- transports: [],
1255
- ...registrationInfo.credential,
1256
- userId,
1257
- deviceType: registrationInfo.credentialDeviceType,
1258
- backedUp: registrationInfo.credentialBackedUp,
1259
- }).catch(withError('Failed to create passkey'));
1260
- return omit(passkey, 'publicKey', 'counter');
1261
- },
1262
- });
1263
- addRoute({
1264
- path: '/api/users/:id/sessions',
1265
- params,
1266
- async GET(event) {
1267
- const userId = event.params.id;
1268
- await checkAuthForUser(event, userId);
1269
- return (await getSessions(userId).catch(e => error(503, 'Failed to get sessions' + (config.debug ? ': ' + e : '')))).map(s => omit(s, 'token'));
1270
- },
1271
- async DELETE(event) {
1272
- const userId = event.params.id;
1273
- const body = await parseBody(event, LogoutSessions);
1274
- await checkAuthForUser(event, userId, body.confirm_all);
1275
- if (!body.confirm_all && !Array.isArray(body.id))
1276
- error(400, 'Invalid request body');
1277
- const query = body.confirm_all ? database.deleteFrom('sessions') : database.deleteFrom('sessions').where('sessions.id', 'in', body.id);
1278
- const result = await query
1279
- .where('sessions.userId', '=', userId)
1280
- .returningAll()
1281
- .execute()
1282
- .catch(withError('Failed to delete one or more sessions'));
1283
- return result.map(s => omit(s, 'token'));
1284
- },
1285
- });
1286
- addRoute({
1287
- path: '/api/users/:id/verify_email',
1288
- params,
1289
- async OPTIONS(event) {
1290
- const userId = event.params.id;
1291
- if (!config.auth.email_verification)
1292
- return { enabled: false };
1293
- await checkAuthForUser(event, userId);
1294
- if (!config.auth.email_verification)
1295
- return { enabled: false };
1296
- return { enabled: true };
1297
- },
1298
- async GET(event) {
1299
- const userId = event.params.id;
1300
- const { user } = await checkAuthForUser(event, userId);
1301
- if (user.emailVerified)
1302
- error(409, 'Email already verified');
1303
- const verification = await createVerification('verify_email', userId, config.auth.verification_timeout * 60);
1304
- return omit(verification, 'token', 'role');
1305
- },
1306
- async POST(event) {
1307
- const userId = event.params.id;
1308
- const { token } = await parseBody(event, object({ token: string() }));
1309
- const { user } = await checkAuthForUser(event, userId);
1310
- if (user.emailVerified)
1311
- error(409, 'Email already verified');
1312
- await useVerification('verify_email', userId, token).catch(withError('Invalid or expired verification token', 400));
1313
- return {};
1314
- },
1315
- });
1316
-
1317
- /**
1318
- * Perform initial setup for when the server is serving web pages.
1319
- */
1320
- async function init() {
1321
- logger.attach(createWriteStream(join(dirs.at(-1), 'server.log')), { output: allLogLevels });
1322
- await loadDefaultConfigs();
1323
- connect();
1324
- await clean({});
1325
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
1326
- process.on('beforeExit', () => database.destroy());
1327
- }
1328
-
1329
- let template = null;
1330
- function fillSvelteKitTemplate({ head, body }, env = {}, nonce = '') {
1331
- template ||= readFileSync(config.web.template, 'utf-8');
1332
- return (template
1333
- .replaceAll('%sveltekit.head%', head)
1334
- .replaceAll('%sveltekit.body%', body)
1335
- .replaceAll('%sveltekit.assets%', config.web.assets)
1336
- // Unused for now.
1337
- .replaceAll('%sveltekit.nonce%', nonce)
1338
- .replace(/%sveltekit\.env\.([^%]+)%/g, (_match, key) => env[key] ?? ''));
1339
- }
1340
- /**
1341
- * @internal
1342
- */
1343
- async function handleSvelteKit({ event, resolve, }) {
1344
- const route = resolveRoute(event);
1345
- if (!route && event.url.pathname === '/' && config.debug)
1346
- return new Response(null, { status: 303, headers: { Location: '/_axium/default' } });
1347
- if (config.debug)
1348
- console.log(styleText('blueBright', event.request.method.padEnd(7)), route ? route.path : event.url.pathname);
1349
- if (!route) {
1350
- // Check to see if this is for an app that is disabled.
1351
- const maybeApp = event.url.pathname.split('/')[1];
1352
- if (apps.has(maybeApp) && config.apps.disabled.includes(maybeApp)) {
1353
- const body = fillSvelteKitTemplate(appDisabledContent);
1354
- return new Response(body, { headers: noCacheHeaders, status: 503 });
1355
- }
1356
- return await resolve(event).catch(handleResponseError);
1357
- }
1358
- if (route.server == true) {
1359
- if (route.api)
1360
- return await handleAPIRequest(event, route).catch(handleResponseError);
1361
- const run = route[event.request.method];
1362
- if (typeof run !== 'function') {
1363
- error(405, `Method ${event.request.method} not allowed for ${route.path}`);
1364
- }
1365
- try {
1366
- const result = await run(event);
1367
- if (result instanceof Response)
1368
- return result;
1369
- return json(result);
1370
- }
1371
- catch (e) {
1372
- return handleResponseError(e);
1373
- }
1374
- }
1375
- const data = await route.load?.(event);
1376
- const body = fillSvelteKitTemplate(render(route.page, { props: { data } }));
1377
- return new Response(body, {
1378
- headers: config.web.disable_cache ? noCacheHeaders : {},
1379
- status: 200,
1380
- });
1381
- }
1382
-
1383
- await init();
1384
-
1385
- export { handleSvelteKit as handle };
1386
- //# sourceMappingURL=hooks.server-DxovS-nf.js.map