@axium/server 0.30.0 → 0.31.1
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/dist/api/admin.js +2 -8
- package/dist/api/users.js +2 -2
- package/dist/database.d.ts +1 -1
- package/dist/database.js +27 -18
- package/dist/linking.js +3 -3
- package/dist/main.js +70 -73
- package/dist/requests.d.ts +2 -1
- package/dist/requests.js +10 -0
- package/package.json +2 -2
package/dist/api/admin.js
CHANGED
|
@@ -8,7 +8,7 @@ import { audit, events, getEvents } from '../audit.js';
|
|
|
8
8
|
import { requireSession } from '../auth.js';
|
|
9
9
|
import { config } from '../config.js';
|
|
10
10
|
import { count, database as db } from '../database.js';
|
|
11
|
-
import { error, withError } from '../requests.js';
|
|
11
|
+
import { error, parseSearch, withError } from '../requests.js';
|
|
12
12
|
import { addRoute } from '../routes.js';
|
|
13
13
|
async function assertAdmin(route, req) {
|
|
14
14
|
const admin = await requireSession(req);
|
|
@@ -122,13 +122,7 @@ addRoute({
|
|
|
122
122
|
async GET(req) {
|
|
123
123
|
await assertAdmin(this, req);
|
|
124
124
|
const filter = { severity: Severity.Info };
|
|
125
|
-
|
|
126
|
-
const search = Object.fromEntries(new URL(req.url).searchParams);
|
|
127
|
-
Object.assign(filter, AuditFilter.parse(search));
|
|
128
|
-
}
|
|
129
|
-
catch (e) {
|
|
130
|
-
error(400, e instanceof z.core.$ZodError ? z.prettifyError(e) : 'invalid body');
|
|
131
|
-
}
|
|
125
|
+
Object.assign(filter, parseSearch(req, AuditFilter));
|
|
132
126
|
return await getEvents(filter)
|
|
133
127
|
.select(eb => jsonObjectFrom(eb.selectFrom('users').whereRef('users.id', '=', 'audit_log.userId').select(['id', 'name'])).as('user'))
|
|
134
128
|
.execute();
|
package/dist/api/users.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Register a new passkey for a new or existing user. */
|
|
2
|
-
import { preferenceDefaults,
|
|
2
|
+
import { preferenceDefaults, Preferences } from '@axium/core';
|
|
3
3
|
import { PasskeyAuthenticationResponse, PasskeyRegistration } from '@axium/core/passkeys';
|
|
4
4
|
import { LogoutSessions, UserAuthOptions, UserChangeable } from '@axium/core/user';
|
|
5
5
|
import * as webauthn from '@simplewebauthn/server';
|
|
@@ -43,7 +43,7 @@ addRoute({
|
|
|
43
43
|
if ('email' in body)
|
|
44
44
|
body.emailVerified = null;
|
|
45
45
|
if ('preferences' in body)
|
|
46
|
-
body.preferences = Object.assign(structuredClone(preferenceDefaults), await
|
|
46
|
+
body.preferences = Object.assign(structuredClone(preferenceDefaults), await Preferences.partial().parseAsync(body.preferences).catch(withError('Invalid preferences', 400)));
|
|
47
47
|
const result = await db
|
|
48
48
|
.updateTable('users')
|
|
49
49
|
.set(body)
|
package/dist/database.d.ts
CHANGED
|
@@ -591,7 +591,7 @@ export interface CheckOptions extends OpOptions {
|
|
|
591
591
|
/**
|
|
592
592
|
* Checks that a table has the expected column types, nullability, and default values.
|
|
593
593
|
*/
|
|
594
|
-
export declare function checkTableTypes<TB extends keyof Schema & string>(tableName: TB, types: Table, opt: CheckOptions): Promise<void>;
|
|
594
|
+
export declare function checkTableTypes<TB extends keyof Schema & string>(tableName: TB, types: Table, opt: CheckOptions, tableMetadata?: kysely.TableMetadata[]): Promise<void>;
|
|
595
595
|
export declare function clean(opt: Partial<OpOptions>): Promise<void>;
|
|
596
596
|
export declare function rotatePassword(): Promise<void>;
|
|
597
597
|
export {};
|
package/dist/database.js
CHANGED
|
@@ -64,6 +64,10 @@ import config from './config.js';
|
|
|
64
64
|
import rawSchema from './db.json' with { type: 'json' };
|
|
65
65
|
import { dirs, systemDir } from './io.js';
|
|
66
66
|
pg.types.setTypeParser(pg.types.builtins.INT8, BigInt);
|
|
67
|
+
// @ts-expect-error 2339
|
|
68
|
+
BigInt.prototype.toJSON = function () {
|
|
69
|
+
return Number(this);
|
|
70
|
+
};
|
|
67
71
|
const sym = Symbol.for('Axium:database');
|
|
68
72
|
export let database;
|
|
69
73
|
export function connect() {
|
|
@@ -365,8 +369,7 @@ export function* getSchemaFiles() {
|
|
|
365
369
|
yield [name, SchemaFile.parse(plugin._db)];
|
|
366
370
|
}
|
|
367
371
|
catch (e) {
|
|
368
|
-
|
|
369
|
-
throw `Invalid database configuration for plugin "${name}":\n${text}`;
|
|
372
|
+
throw `Invalid database configuration for plugin "${name}":\n${io.errorText(e)}`;
|
|
370
373
|
}
|
|
371
374
|
}
|
|
372
375
|
}
|
|
@@ -382,10 +385,16 @@ export function getFullSchema(opt = {}) {
|
|
|
382
385
|
let currentSchema = { tables: {}, indexes: [] };
|
|
383
386
|
fullSchema.versions[pluginName] = file.latest;
|
|
384
387
|
for (const [version, schema] of file.versions.entries()) {
|
|
385
|
-
if (schema.delta)
|
|
386
|
-
applyDeltaToSchema(currentSchema, schema);
|
|
387
|
-
else
|
|
388
|
+
if (!schema.delta)
|
|
388
389
|
currentSchema = schema;
|
|
390
|
+
else {
|
|
391
|
+
try {
|
|
392
|
+
applyDeltaToSchema(currentSchema, schema);
|
|
393
|
+
}
|
|
394
|
+
catch (e) {
|
|
395
|
+
throw `Failed to apply version ${version - 1}->${version} delta to ${pluginName}: ${io.errorText(e)}`;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
389
398
|
if (version === file.latest)
|
|
390
399
|
break;
|
|
391
400
|
}
|
|
@@ -423,20 +432,20 @@ export function setUpgradeInfo(info) {
|
|
|
423
432
|
}
|
|
424
433
|
export function applyTableDeltaToSchema(table, delta) {
|
|
425
434
|
for (const column of delta.drop_columns) {
|
|
426
|
-
if (column in table)
|
|
435
|
+
if (column in table.columns)
|
|
427
436
|
delete table.columns[column];
|
|
428
437
|
else
|
|
429
|
-
throw `
|
|
438
|
+
throw `can't drop column ${column} because it does not exist`;
|
|
430
439
|
}
|
|
431
440
|
for (const [name, column] of Object.entries(delta.add_columns)) {
|
|
432
|
-
if (name in table)
|
|
433
|
-
throw `
|
|
441
|
+
if (name in table.columns)
|
|
442
|
+
throw `can't add column ${name} because it already exists`;
|
|
434
443
|
table.columns[name] = column;
|
|
435
444
|
}
|
|
436
445
|
for (const [name, columnDelta] of Object.entries(delta.alter_columns)) {
|
|
437
446
|
const column = table.columns[name];
|
|
438
447
|
if (!column)
|
|
439
|
-
throw `
|
|
448
|
+
throw `can't modify column ${name} because it does not exist`;
|
|
440
449
|
if (columnDelta.type)
|
|
441
450
|
column.type = columnDelta.type;
|
|
442
451
|
if (columnDelta.default)
|
|
@@ -459,11 +468,11 @@ export function applyTableDeltaToSchema(table, delta) {
|
|
|
459
468
|
if (table.constraints[name])
|
|
460
469
|
delete table.constraints[name];
|
|
461
470
|
else
|
|
462
|
-
throw `
|
|
471
|
+
throw `can't drop constraint ${name} because it does not exist`;
|
|
463
472
|
}
|
|
464
473
|
for (const [name, constraint] of Object.entries(delta.add_constraints)) {
|
|
465
474
|
if (table.constraints[name])
|
|
466
|
-
throw `
|
|
475
|
+
throw `can't add constraint ${name} because it already exists`;
|
|
467
476
|
table.constraints[name] = constraint;
|
|
468
477
|
}
|
|
469
478
|
}
|
|
@@ -472,11 +481,11 @@ export function applyDeltaToSchema(schema, delta) {
|
|
|
472
481
|
if (tableName in schema.tables)
|
|
473
482
|
delete schema.tables[tableName];
|
|
474
483
|
else
|
|
475
|
-
throw `
|
|
484
|
+
throw `can't drop table ${tableName} because it does not exist`;
|
|
476
485
|
}
|
|
477
486
|
for (const [tableName, table] of Object.entries(delta.add_tables)) {
|
|
478
487
|
if (tableName in schema.tables)
|
|
479
|
-
throw `
|
|
488
|
+
throw `can't add table ${tableName} because it already exists`;
|
|
480
489
|
else
|
|
481
490
|
schema.tables[tableName] = table;
|
|
482
491
|
}
|
|
@@ -484,7 +493,7 @@ export function applyDeltaToSchema(schema, delta) {
|
|
|
484
493
|
if (tableName in schema.tables)
|
|
485
494
|
applyTableDeltaToSchema(schema.tables[tableName], tableDelta);
|
|
486
495
|
else
|
|
487
|
-
throw `
|
|
496
|
+
throw `can't modify table ${tableName} because it does not exist`;
|
|
488
497
|
}
|
|
489
498
|
}
|
|
490
499
|
export function validateDelta(delta) {
|
|
@@ -782,10 +791,10 @@ export async function applyDelta(delta, forceAbort = false) {
|
|
|
782
791
|
/**
|
|
783
792
|
* Checks that a table has the expected column types, nullability, and default values.
|
|
784
793
|
*/
|
|
785
|
-
export async function checkTableTypes(tableName, types, opt) {
|
|
794
|
+
export async function checkTableTypes(tableName, types, opt, tableMetadata) {
|
|
786
795
|
io.start(`Checking table ${tableName}`);
|
|
787
|
-
|
|
788
|
-
const table =
|
|
796
|
+
tableMetadata ||= await database.introspection.getTables();
|
|
797
|
+
const table = tableMetadata.find(t => (t.schema == 'public' ? t.name : `${t.schema}.${t.name}`) === tableName);
|
|
789
798
|
if (!table)
|
|
790
799
|
throw 'missing.';
|
|
791
800
|
const columns = Object.fromEntries(table.columns.map(c => [c.name, c]));
|
package/dist/linking.js
CHANGED
|
@@ -40,7 +40,7 @@ export function linkRoutes(options = {}) {
|
|
|
40
40
|
unlinkSync(from);
|
|
41
41
|
}
|
|
42
42
|
catch (e) {
|
|
43
|
-
io.exit(e
|
|
43
|
+
io.exit(e);
|
|
44
44
|
}
|
|
45
45
|
io.done();
|
|
46
46
|
io.start('Re-linking ' + text);
|
|
@@ -50,7 +50,7 @@ export function linkRoutes(options = {}) {
|
|
|
50
50
|
io.done();
|
|
51
51
|
}
|
|
52
52
|
catch (e) {
|
|
53
|
-
io.exit(e
|
|
53
|
+
io.exit(e);
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
}
|
|
@@ -65,7 +65,7 @@ export function unlinkRoutes(options = {}) {
|
|
|
65
65
|
io.done();
|
|
66
66
|
}
|
|
67
67
|
catch (e) {
|
|
68
|
-
io.exit(e
|
|
68
|
+
io.exit(e);
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
}
|
package/dist/main.js
CHANGED
|
@@ -60,17 +60,17 @@ import { Argument, Option, program } from 'commander';
|
|
|
60
60
|
import { access } from 'node:fs/promises';
|
|
61
61
|
import { join, resolve } from 'node:path/posix';
|
|
62
62
|
import { createInterface } from 'node:readline/promises';
|
|
63
|
-
import {
|
|
63
|
+
import { parseArgs, styleText } from 'node:util';
|
|
64
64
|
import { getByString, isJSON, setByString } from 'utilium';
|
|
65
65
|
import * as z from 'zod';
|
|
66
66
|
import $pkg from '../package.json' with { type: 'json' };
|
|
67
67
|
import { audit, getEvents, styleSeverity } from './audit.js';
|
|
68
|
+
import { diffUpdate, lookupUser, userText } from './cli.js';
|
|
68
69
|
import config, { configFiles, FileSchema, saveConfigTo } from './config.js';
|
|
69
70
|
import * as db from './database.js';
|
|
70
71
|
import { _portActions, _portMethods, restrictedPorts } from './io.js';
|
|
71
72
|
import { linkRoutes, listRouteLinks, unlinkRoutes } from './linking.js';
|
|
72
73
|
import { serve } from './serve.js';
|
|
73
|
-
import { diffUpdate, lookupUser, userText } from './cli.js';
|
|
74
74
|
async function rlConfirm(question = 'Is this ok') {
|
|
75
75
|
const { data, error } = z
|
|
76
76
|
.stringbool()
|
|
@@ -122,13 +122,11 @@ try {
|
|
|
122
122
|
.name('axium')
|
|
123
123
|
.description('Axium server CLI')
|
|
124
124
|
.configureHelp({ showGlobalOptions: true })
|
|
125
|
-
.option('--safe', 'do not execute code from plugins')
|
|
125
|
+
.option('--safe', 'do not execute code from plugins', false)
|
|
126
126
|
.option('--debug', 'override debug mode')
|
|
127
127
|
.option('--no-debug', 'override debug mode')
|
|
128
|
-
.option('-c, --config <path>', 'path to the config file')
|
|
129
|
-
|
|
130
|
-
noAutoDB = ['init', 'serve', 'check'];
|
|
131
|
-
program.hook('preAction', (_, action) => {
|
|
128
|
+
.option('-c, --config <path>', 'path to the config file')
|
|
129
|
+
.hook('preAction', (_, action) => {
|
|
132
130
|
const opt = action.optsWithGlobals();
|
|
133
131
|
opt.force && io.warn('--force: Protections disabled.');
|
|
134
132
|
if (typeof opt.debug == 'boolean') {
|
|
@@ -142,23 +140,19 @@ try {
|
|
|
142
140
|
if (!noAutoDB.includes(action.name()))
|
|
143
141
|
throw e;
|
|
144
142
|
}
|
|
145
|
-
})
|
|
146
|
-
|
|
143
|
+
})
|
|
144
|
+
.hook('postAction', async (_, action) => {
|
|
147
145
|
if (!noAutoDB.includes(action.name()))
|
|
148
146
|
await db.database.destroy();
|
|
149
|
-
})
|
|
147
|
+
})
|
|
148
|
+
.on('option:debug', () => config.set({ debug: true }));
|
|
149
|
+
noAutoDB = ['init', 'serve', 'check'];
|
|
150
150
|
// Options shared by multiple (sub)commands
|
|
151
151
|
opts = {
|
|
152
|
-
// database specific
|
|
153
|
-
host: new Option('-H, --host <host>', 'the host of the database.').argParser(value => {
|
|
154
|
-
const [hostname, port] = value?.split(':') ?? [];
|
|
155
|
-
config.db.host = hostname || config.db.host;
|
|
156
|
-
config.db.port = port && Number.isSafeInteger(parseInt(port)) ? parseInt(port) : config.db.port;
|
|
157
|
-
}),
|
|
158
152
|
check: new Option('--check', 'check the database schema after initialization').default(false),
|
|
159
153
|
force: new Option('-f, --force', 'force the operation').default(false),
|
|
160
154
|
global: new Option('-g, --global', 'apply the operation globally').default(false),
|
|
161
|
-
timeout: new Option('-t, --timeout <ms>', 'how long to wait for commands to complete.').default(
|
|
155
|
+
timeout: new Option('-t, --timeout <ms>', 'how long to wait for commands to complete.').default(1000).argParser(value => {
|
|
162
156
|
const timeout = parseInt(value);
|
|
163
157
|
if (!Number.isSafeInteger(timeout) || timeout < 0)
|
|
164
158
|
io.warn('Invalid timeout value, using default.');
|
|
@@ -166,17 +160,17 @@ try {
|
|
|
166
160
|
}),
|
|
167
161
|
packagesDir: new Option('-p, --packages-dir <dir>', 'the directory to look for packages in'),
|
|
168
162
|
};
|
|
169
|
-
axiumDB = program.command('db').alias('database').description('Manage the database').addOption(opts.timeout)
|
|
163
|
+
axiumDB = program.command('db').alias('database').description('Manage the database').addOption(opts.timeout);
|
|
170
164
|
axiumDB
|
|
171
165
|
.command('init')
|
|
172
166
|
.description('Initialize the database')
|
|
173
167
|
.addOption(opts.force)
|
|
174
|
-
.option('-s, --skip', 'If the user, database, or schema already exists, skip trying to create it.')
|
|
168
|
+
.option('-s, --skip', 'If the user, database, or schema already exists, skip trying to create it.', false)
|
|
175
169
|
.addOption(opts.check)
|
|
176
|
-
.action(async (
|
|
177
|
-
const opt =
|
|
178
|
-
await db.init(opt).catch(io.
|
|
179
|
-
await dbInitTables().catch(io.
|
|
170
|
+
.action(async function axium_db_init() {
|
|
171
|
+
const opt = this.optsWithGlobals();
|
|
172
|
+
await db.init(opt).catch(io.exit);
|
|
173
|
+
await dbInitTables().catch(io.exit);
|
|
180
174
|
});
|
|
181
175
|
axiumDB
|
|
182
176
|
.command('status')
|
|
@@ -204,9 +198,9 @@ try {
|
|
|
204
198
|
io.warn(`Database has existing ${key}. Use --force if you really want to drop the database.`);
|
|
205
199
|
process.exit(2);
|
|
206
200
|
}
|
|
207
|
-
await db._sql('DROP DATABASE axium', 'Dropping database').catch(io.
|
|
208
|
-
await db._sql('REVOKE ALL PRIVILEGES ON SCHEMA public FROM axium', 'Revoking schema privileges').catch(io.
|
|
209
|
-
await db._sql('DROP USER axium', 'Dropping user').catch(io.
|
|
201
|
+
await db._sql('DROP DATABASE axium', 'Dropping database').catch(io.exit);
|
|
202
|
+
await db._sql('REVOKE ALL PRIVILEGES ON SCHEMA public FROM axium', 'Revoking schema privileges').catch(io.exit);
|
|
203
|
+
await db._sql('DROP USER axium', 'Dropping user').catch(io.exit);
|
|
210
204
|
await db
|
|
211
205
|
.getHBA(opt)
|
|
212
206
|
.then(([content, writeBack]) => {
|
|
@@ -249,7 +243,7 @@ try {
|
|
|
249
243
|
console.log(table + ' '.repeat(maxTableName - table.length), '|', plugins);
|
|
250
244
|
}
|
|
251
245
|
await rlConfirm('Are you sure you want to wipe these tables and any dependents');
|
|
252
|
-
await db.database.deleteFrom(Array.from(tables.keys())).execute().catch(io.
|
|
246
|
+
await db.database.deleteFrom(Array.from(tables.keys())).execute().catch(io.exit);
|
|
253
247
|
});
|
|
254
248
|
axiumDB
|
|
255
249
|
.command('check')
|
|
@@ -258,23 +252,20 @@ try {
|
|
|
258
252
|
.action(async (opt) => {
|
|
259
253
|
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
260
254
|
try {
|
|
261
|
-
await io.run('Checking for sudo', 'which sudo').catch(io.
|
|
262
|
-
await io.run('Checking for psql', 'which psql').catch(io.
|
|
255
|
+
await io.run('Checking for sudo', 'which sudo').catch(io.exit);
|
|
256
|
+
await io.run('Checking for psql', 'which psql').catch(io.exit);
|
|
263
257
|
const throwUnlessRows = (text) => {
|
|
264
258
|
if (text.includes('(0 rows)'))
|
|
265
259
|
throw 'missing.';
|
|
266
260
|
return text;
|
|
267
261
|
};
|
|
268
|
-
await db
|
|
269
|
-
|
|
270
|
-
.then(throwUnlessRows)
|
|
271
|
-
.catch(io.handleError);
|
|
272
|
-
await db._sql(`SELECT 1 FROM pg_roles WHERE rolname = 'axium'`, 'Checking for user').then(throwUnlessRows).catch(io.handleError);
|
|
262
|
+
await db._sql(`SELECT 1 FROM pg_database WHERE datname = 'axium'`, 'Checking for database').then(throwUnlessRows).catch(io.exit);
|
|
263
|
+
await db._sql(`SELECT 1 FROM pg_roles WHERE rolname = 'axium'`, 'Checking for user').then(throwUnlessRows).catch(io.exit);
|
|
273
264
|
io.start('Connecting to database');
|
|
274
265
|
const _ = __addDisposableResource(env_2, db.connect(), true);
|
|
275
266
|
io.done();
|
|
276
267
|
io.start('Getting schema metadata');
|
|
277
|
-
const schemas = await db.database.introspection.getSchemas().catch(io.
|
|
268
|
+
const schemas = await db.database.introspection.getSchemas().catch(io.exit);
|
|
278
269
|
io.done();
|
|
279
270
|
io.start('Checking for acl schema');
|
|
280
271
|
if (!schemas.find(s => s.name == 'acl'))
|
|
@@ -284,13 +275,21 @@ try {
|
|
|
284
275
|
const tablePromises = await Promise.all([
|
|
285
276
|
db.database.introspection.getTables(),
|
|
286
277
|
db.database.withSchema('acl').introspection.getTables(),
|
|
287
|
-
]).catch(io.
|
|
288
|
-
|
|
289
|
-
const tables = Object.fromEntries(
|
|
278
|
+
]).catch(io.exit);
|
|
279
|
+
const tableMetadata = tablePromises.flat();
|
|
280
|
+
const tables = Object.fromEntries(tableMetadata.map(t => [t.schema == 'public' ? t.name : `${t.schema}.${t.name}`, t]));
|
|
290
281
|
io.done();
|
|
291
|
-
|
|
282
|
+
io.start('Resolving database schemas');
|
|
283
|
+
let schema;
|
|
284
|
+
try {
|
|
285
|
+
schema = db.getFullSchema();
|
|
286
|
+
io.done();
|
|
287
|
+
}
|
|
288
|
+
catch (e) {
|
|
289
|
+
io.exit(e);
|
|
290
|
+
}
|
|
292
291
|
for (const [name, table] of Object.entries(schema.tables)) {
|
|
293
|
-
await db.checkTableTypes(name, table, opt);
|
|
292
|
+
await db.checkTableTypes(name, table, opt, tableMetadata);
|
|
294
293
|
delete tables[name];
|
|
295
294
|
}
|
|
296
295
|
io.start('Checking for extra tables');
|
|
@@ -327,13 +326,13 @@ try {
|
|
|
327
326
|
.command('schema')
|
|
328
327
|
.description('Get the JSON schema for the database configuration file')
|
|
329
328
|
.option('-j, --json', 'values are JSON encoded')
|
|
330
|
-
.action(
|
|
329
|
+
.action(opt => {
|
|
331
330
|
try {
|
|
332
331
|
const schema = z.toJSONSchema(db.SchemaFile, { io: 'input' });
|
|
333
332
|
console.log(opt.json ? JSON.stringify(schema, null, 4) : schema);
|
|
334
333
|
}
|
|
335
334
|
catch (e) {
|
|
336
|
-
io.
|
|
335
|
+
io.exit(e);
|
|
337
336
|
}
|
|
338
337
|
});
|
|
339
338
|
axiumDB
|
|
@@ -342,7 +341,7 @@ try {
|
|
|
342
341
|
.alias('up')
|
|
343
342
|
.description('Upgrade the database to the latest version')
|
|
344
343
|
.option('--abort', 'Rollback changes instead of committing them')
|
|
345
|
-
.action(async (opt)
|
|
344
|
+
.action(async function axium_db_upgrade(opt) {
|
|
346
345
|
const deltas = [];
|
|
347
346
|
const info = db.getUpgradeInfo();
|
|
348
347
|
let empty = true;
|
|
@@ -401,7 +400,7 @@ try {
|
|
|
401
400
|
io.exit(e);
|
|
402
401
|
}
|
|
403
402
|
console.log('Applying delta.');
|
|
404
|
-
await db.applyDelta(delta, opt.abort).catch(io.
|
|
403
|
+
await db.applyDelta(delta, opt.abort).catch(io.exit);
|
|
405
404
|
info.upgrades.push({ timestamp: new Date(), from, to });
|
|
406
405
|
db.setUpgradeInfo(info);
|
|
407
406
|
});
|
|
@@ -449,13 +448,13 @@ try {
|
|
|
449
448
|
.command('config')
|
|
450
449
|
.description('Manage the configuration')
|
|
451
450
|
.addOption(opts.global)
|
|
452
|
-
.option('-j, --json', 'values are JSON encoded')
|
|
453
|
-
.option('-r, --redact', 'Do not output sensitive values');
|
|
451
|
+
.option('-j, --json', 'values are JSON encoded', false)
|
|
452
|
+
.option('-r, --redact', 'Do not output sensitive values', false);
|
|
454
453
|
axiumConfig
|
|
455
454
|
.command('dump')
|
|
456
455
|
.description('Output the entire current configuration')
|
|
457
|
-
.action(()
|
|
458
|
-
const opt =
|
|
456
|
+
.action(function axium_config_dump() {
|
|
457
|
+
const opt = this.optsWithGlobals();
|
|
459
458
|
const value = config.plain();
|
|
460
459
|
console.log(opt.json ? JSON.stringify(value, configReplacer(opt), 4) : value);
|
|
461
460
|
});
|
|
@@ -463,8 +462,8 @@ try {
|
|
|
463
462
|
.command('get')
|
|
464
463
|
.description('Get a config value')
|
|
465
464
|
.argument('<key>', 'the key to get')
|
|
466
|
-
.action((key)
|
|
467
|
-
const opt =
|
|
465
|
+
.action(function axium_config_get(key) {
|
|
466
|
+
const opt = this.optsWithGlobals();
|
|
468
467
|
const value = getByString(config.plain(), key);
|
|
469
468
|
console.log(opt.json ? JSON.stringify(value, configReplacer(opt), 4) : value);
|
|
470
469
|
});
|
|
@@ -473,8 +472,8 @@ try {
|
|
|
473
472
|
.description('Set a config value. Note setting objects is not supported.')
|
|
474
473
|
.argument('<key>', 'the key to set')
|
|
475
474
|
.argument('<value>', 'the value')
|
|
476
|
-
.action((key, value)
|
|
477
|
-
const opt =
|
|
475
|
+
.action(function axium_config_set(key, value) {
|
|
476
|
+
const opt = this.optsWithGlobals();
|
|
478
477
|
if (opt.json && !isJSON(value))
|
|
479
478
|
io.exit('Invalid JSON');
|
|
480
479
|
const obj = {};
|
|
@@ -500,7 +499,7 @@ try {
|
|
|
500
499
|
console.log(opt.json ? JSON.stringify(schema, configReplacer(opt), 4) : schema);
|
|
501
500
|
}
|
|
502
501
|
catch (e) {
|
|
503
|
-
io.
|
|
502
|
+
io.exit(e);
|
|
504
503
|
}
|
|
505
504
|
});
|
|
506
505
|
axiumPlugin = program.command('plugin').alias('plugins').description('Manage plugins').addOption(opts.global);
|
|
@@ -510,7 +509,7 @@ try {
|
|
|
510
509
|
.description('List loaded plugins')
|
|
511
510
|
.option('-l, --long', 'use the long listing format')
|
|
512
511
|
.option('--no-versions', 'do not show plugin versions')
|
|
513
|
-
.action(
|
|
512
|
+
.action(opt => {
|
|
514
513
|
if (!plugins.size) {
|
|
515
514
|
console.log('No plugins loaded.');
|
|
516
515
|
return;
|
|
@@ -557,7 +556,7 @@ try {
|
|
|
557
556
|
.addOption(opts.timeout)
|
|
558
557
|
.addOption(opts.check)
|
|
559
558
|
.argument('<plugin>', 'the plugin to initialize')
|
|
560
|
-
.action(async (search
|
|
559
|
+
.action(async (search) => {
|
|
561
560
|
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
562
561
|
try {
|
|
563
562
|
const plugin = _findPlugin(search);
|
|
@@ -582,7 +581,7 @@ try {
|
|
|
582
581
|
.description('List apps added by plugins')
|
|
583
582
|
.option('-l, --long', 'use the long listing format')
|
|
584
583
|
.option('-b, --builtin', 'include built-in apps')
|
|
585
|
-
.action(
|
|
584
|
+
.action(opt => {
|
|
586
585
|
if (!apps.size) {
|
|
587
586
|
console.log('No apps.');
|
|
588
587
|
return;
|
|
@@ -692,7 +691,6 @@ try {
|
|
|
692
691
|
.command('status')
|
|
693
692
|
.alias('stats')
|
|
694
693
|
.description('Get information about the server')
|
|
695
|
-
.addOption(opts.host)
|
|
696
694
|
.action(async () => {
|
|
697
695
|
console.log('Axium Server v' + $pkg.version);
|
|
698
696
|
console.log(styleText('whiteBright', 'Debug mode:'), config.debug ? styleText('yellow', 'enabled') : 'disabled');
|
|
@@ -721,37 +719,37 @@ try {
|
|
|
721
719
|
.addOption(new Option('-m, --method <method>', 'the method to use').choices(_portMethods).default('node-cap'))
|
|
722
720
|
.option('-N, --node <path>', 'the path to the node binary')
|
|
723
721
|
.action(async (action, opt) => {
|
|
724
|
-
await restrictedPorts({ ...opt, action }).catch(io.
|
|
722
|
+
await restrictedPorts({ ...opt, action }).catch(io.exit);
|
|
725
723
|
});
|
|
726
724
|
program
|
|
727
725
|
.command('init')
|
|
728
726
|
.description('Install Axium server')
|
|
729
727
|
.addOption(opts.force)
|
|
730
|
-
.addOption(opts.host)
|
|
731
728
|
.addOption(opts.check)
|
|
732
729
|
.addOption(opts.packagesDir)
|
|
733
|
-
.option('-s, --skip', 'Skip already initialized steps')
|
|
730
|
+
.option('-s, --skip', 'Skip already initialized steps', false)
|
|
734
731
|
.action(async (opt) => {
|
|
735
|
-
await db.init(opt).catch(io.
|
|
736
|
-
await dbInitTables().catch(io.
|
|
737
|
-
await restrictedPorts({ method: 'node-cap', action: 'enable' }).catch(io.
|
|
732
|
+
await db.init(opt).catch(io.exit);
|
|
733
|
+
await dbInitTables().catch(io.exit);
|
|
734
|
+
await restrictedPorts({ method: 'node-cap', action: 'enable' }).catch(io.exit);
|
|
738
735
|
});
|
|
739
736
|
program
|
|
740
737
|
.command('serve')
|
|
741
738
|
.description('Start the Axium server')
|
|
742
|
-
.option('-p, --port <port>', 'the port to listen on')
|
|
739
|
+
.option('-p, --port <port>', 'the port to listen on', Number.parseInt, config.web.port)
|
|
743
740
|
.option('--ssl <prefix>', 'the prefix for the cert.pem and key.pem SSL files')
|
|
744
741
|
.option('-b, --build <path>', 'the path to the handler build')
|
|
745
742
|
.action(async (opt) => {
|
|
743
|
+
if (opt.port < 1 || opt.port > 65535)
|
|
744
|
+
io.exit('Invalid port');
|
|
746
745
|
const server = await serve({
|
|
747
746
|
secure: opt.ssl ? true : config.web.secure,
|
|
748
747
|
ssl_cert: opt.ssl ? join(opt.ssl, 'cert.pem') : config.web.ssl_cert,
|
|
749
748
|
ssl_key: opt.ssl ? join(opt.ssl, 'key.pem') : config.web.ssl_key,
|
|
750
749
|
build: opt.build ? resolve(opt.build) : config.web.build,
|
|
751
750
|
});
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
console.log('Server is listening on port ' + port);
|
|
751
|
+
server.listen(opt.port, () => {
|
|
752
|
+
console.log('Server is listening on port ' + opt.port);
|
|
755
753
|
});
|
|
756
754
|
});
|
|
757
755
|
program
|
|
@@ -761,12 +759,11 @@ try {
|
|
|
761
759
|
.addOption(new Option('-l, --list', 'list route links').conflicts('delete'))
|
|
762
760
|
.option('-d, --delete', 'delete route links')
|
|
763
761
|
.argument('[name...]', 'List of plugin names to operate on. If not specified, operates on all plugins and built-in routes.')
|
|
764
|
-
.action(async function (names) {
|
|
762
|
+
.action(async function axium_link(names) {
|
|
765
763
|
const opt = this.optsWithGlobals();
|
|
766
|
-
|
|
767
|
-
opt.only = names;
|
|
764
|
+
const linkOpts = { only: names };
|
|
768
765
|
if (opt.list) {
|
|
769
|
-
for (const link of listRouteLinks(
|
|
766
|
+
for (const link of listRouteLinks(linkOpts)) {
|
|
770
767
|
const idText = link.id.startsWith('#') ? `(${link.id.slice(1)})` : link.id;
|
|
771
768
|
const fromColor = await access(link.from)
|
|
772
769
|
.then(() => 'cyanBright')
|
|
@@ -776,10 +773,10 @@ try {
|
|
|
776
773
|
return;
|
|
777
774
|
}
|
|
778
775
|
if (opt.delete) {
|
|
779
|
-
unlinkRoutes(
|
|
776
|
+
unlinkRoutes(linkOpts);
|
|
780
777
|
return;
|
|
781
778
|
}
|
|
782
|
-
linkRoutes(
|
|
779
|
+
linkRoutes(linkOpts);
|
|
783
780
|
});
|
|
784
781
|
program
|
|
785
782
|
.command('audit')
|
package/dist/requests.d.ts
CHANGED
|
@@ -32,7 +32,8 @@ export declare function isRedirect(e: unknown): e is Redirect;
|
|
|
32
32
|
*/
|
|
33
33
|
export declare function redirect(location: string, status?: number): never;
|
|
34
34
|
export declare function json(data: object, init?: ResponseInit): Response;
|
|
35
|
-
export declare function parseBody<const Schema extends z.ZodType
|
|
35
|
+
export declare function parseBody<const Schema extends z.ZodType>(request: Request, schema: Schema): Promise<z.infer<Schema>>;
|
|
36
|
+
export declare function parseSearch<const Schema extends z.ZodType>(request: Request, schema: Schema): z.infer<Schema>;
|
|
36
37
|
export declare function getToken(request: Request, sensitive?: boolean): string | undefined;
|
|
37
38
|
export interface CreateSessionOptions {
|
|
38
39
|
elevated?: boolean;
|
package/dist/requests.js
CHANGED
|
@@ -44,6 +44,16 @@ export async function parseBody(request, schema) {
|
|
|
44
44
|
error(400, e instanceof z.core.$ZodError ? z.prettifyError(e) : 'invalid body');
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
export function parseSearch(request, schema) {
|
|
48
|
+
const url = new URL(request.url);
|
|
49
|
+
const searchParams = Object.fromEntries(url.searchParams.entries());
|
|
50
|
+
try {
|
|
51
|
+
return schema.parse(searchParams);
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
error(400, e instanceof z.core.$ZodError ? z.prettifyError(e) : 'invalid query parameters');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
47
57
|
export function getToken(request, sensitive = false) {
|
|
48
58
|
const header_token = request.headers.get('Authorization')?.replace('Bearer ', '');
|
|
49
59
|
if (header_token)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axium/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.1",
|
|
4
4
|
"author": "James Prevett <axium@jamespre.dev>",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"@axium/client": ">=0.9.5",
|
|
51
|
-
"@axium/core": ">=0.
|
|
51
|
+
"@axium/core": ">=0.15.0",
|
|
52
52
|
"kysely": "^0.28.0",
|
|
53
53
|
"utilium": "^2.6.0",
|
|
54
54
|
"zod": "^4.0.5"
|