@axium/server 0.44.0 → 0.44.2

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.
@@ -0,0 +1,102 @@
1
+ import { formatDateRange } from '@axium/core/format';
2
+ import { Argument, Option, program } from 'commander';
3
+ import * as io from 'ioium/node';
4
+ import { styleText } from 'node:util';
5
+ import * as z from 'zod';
6
+ import { audit } from '../audit.js';
7
+ import { diffUpdate, lookupUser, rl, userText } from './common.js';
8
+ import config from '../config.js';
9
+ import * as db from '../db/index.js';
10
+ const argUserLookup = new Argument('<user>', 'the UUID or email of the user to operate on').argParser(lookupUser);
11
+ program
12
+ .command('user')
13
+ .description('Get or change information about a user')
14
+ .addArgument(argUserLookup)
15
+ .option('-S, --sessions', 'show user sessions')
16
+ .option('-P, --passkeys', 'show user passkeys')
17
+ .option('--add-role <role...>', 'add roles to the user')
18
+ .option('--remove-role <role...>', 'remove roles from the user')
19
+ .option('--tag <tag...>', 'Add tags to the user')
20
+ .option('--untag <tag...>', 'Remove tags from the user')
21
+ .option('--delete', 'Delete the user')
22
+ .option('--suspend', 'Suspend the user')
23
+ .addOption(new Option('--unsuspend', 'Un-suspend the user').conflicts('suspend'))
24
+ .action(async (_user, opt) => {
25
+ let user = await _user;
26
+ const [updatedRoles, roles, rolesDiff] = diffUpdate(user.roles, opt.addRole, opt.removeRole);
27
+ const [updatedTags, tags, tagsDiff] = diffUpdate(user.tags, opt.tag, opt.untag);
28
+ const changeSuspend = (opt.suspend || opt.unsuspend) && user.isSuspended != (opt.suspend ?? !opt.unsuspend);
29
+ if (updatedRoles || updatedTags || changeSuspend) {
30
+ user = await db.database
31
+ .updateTable('users')
32
+ .where('id', '=', user.id)
33
+ .set({ roles, tags, isSuspended: !changeSuspend ? user.isSuspended : (opt.suspend ?? !opt.unsuspend) })
34
+ .returningAll()
35
+ .executeTakeFirstOrThrow()
36
+ .then(u => {
37
+ if (updatedRoles && rolesDiff)
38
+ console.log(`> Updated roles: ${rolesDiff}`);
39
+ if (updatedTags && tagsDiff)
40
+ console.log(`> Updated tags: ${tagsDiff}`);
41
+ if (changeSuspend)
42
+ console.log(opt.suspend ? '> Suspended' : '> Un-suspended');
43
+ return u;
44
+ })
45
+ .catch(e => io.exit('Failed to update user: ' + e.message));
46
+ }
47
+ if (opt.delete) {
48
+ const confirmed = await rl
49
+ .question(`Are you sure you want to delete ${userText(user, true)}? (y/N) `)
50
+ .then(v => z.stringbool().parseAsync(v))
51
+ .catch(() => false);
52
+ if (!confirmed)
53
+ console.log(styleText('dim', '> Delete aborted.'));
54
+ else
55
+ await db.database
56
+ .deleteFrom('users')
57
+ .where('id', '=', user.id)
58
+ .executeTakeFirstOrThrow()
59
+ .then(() => console.log(styleText(['red', 'bold'], '> Deleted')))
60
+ .catch(e => io.exit('Failed to delete user: ' + e.message));
61
+ }
62
+ console.log([
63
+ user.isSuspended && styleText('yellowBright', 'Suspended'),
64
+ user.isAdmin && styleText('redBright', 'Administrator'),
65
+ 'UUID: ' + user.id,
66
+ 'Name: ' + user.name,
67
+ `Email: ${user.email}, ${user.emailVerified ? 'verified on ' + formatDateRange(user.emailVerified) : styleText(config.auth.email_verification ? 'yellow' : 'dim', 'not verified')}`,
68
+ 'Registered ' + formatDateRange(user.registeredAt),
69
+ `Roles: ${user.roles.length ? user.roles.join(', ') : styleText('dim', '(none)')}`,
70
+ `Tags: ${user.tags.length ? user.tags.join(', ') : styleText('dim', '(none)')}`,
71
+ ]
72
+ .filter(v => v)
73
+ .join('\n'));
74
+ if (opt.sessions) {
75
+ const sessions = await db.database.selectFrom('sessions').where('userId', '=', user.id).selectAll().execute();
76
+ console.log(styleText('bold', 'Sessions:'));
77
+ if (!sessions.length)
78
+ console.log(styleText('dim', '(none)'));
79
+ else
80
+ for (const session of sessions) {
81
+ console.log(`\t${session.id}\tcreated ${formatDateRange(session.created).padEnd(40)}\texpires ${formatDateRange(session.expires).padEnd(40)}\t${session.elevated ? styleText('yellow', '(elevated)') : ''}`);
82
+ }
83
+ }
84
+ if (opt.passkeys) {
85
+ const passkeys = await db.database.selectFrom('passkeys').where('userId', '=', user.id).selectAll().execute();
86
+ console.log(styleText('bold', 'Passkeys:'));
87
+ for (const passkey of passkeys) {
88
+ console.log(`\t${passkey.id}: created ${formatDateRange(passkey.createdAt).padEnd(40)} used ${passkey.counter} times. ${passkey.deviceType}, ${passkey.backedUp ? '' : 'not '}backed up; transports are [${passkey.transports.join(', ')}], ${passkey.name ? 'named ' + JSON.stringify(passkey.name) : 'unnamed'}.`);
89
+ }
90
+ }
91
+ });
92
+ program
93
+ .command('toggle-admin')
94
+ .description('Toggle whether a user is an administrator')
95
+ .addArgument(argUserLookup)
96
+ .action(async (_user) => {
97
+ const user = await _user;
98
+ const isAdmin = !user.isAdmin;
99
+ await db.database.updateTable('users').set({ isAdmin }).where('id', '=', user.id).executeTakeFirstOrThrow();
100
+ await audit('admin_change', undefined, { user: user.id });
101
+ console.log(`${userText(user)} is ${isAdmin ? 'now' : 'no longer'} an administrator. (${styleText(['whiteBright', 'bold'], isAdmin.toString())})`);
102
+ });
package/dist/io.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { PackageVersionInfo } from '@axium/core/packages';
1
+ import { type PackageVersionInfo } from '@axium/core/packages';
2
2
  import { Logger } from 'logzen';
3
3
  export declare const systemDir = "/etc/axium";
4
4
  export declare const dirs: string[];
package/dist/io.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import * as io from 'ioium/node';
2
+ import { fetchPackageMetadata } from '@axium/core/packages';
2
3
  import { getVersionInfo } from '@axium/core/node/packages';
3
4
  import { plugins } from '@axium/core/plugins';
4
5
  import { Logger } from 'logzen';
5
6
  import * as fs from 'node:fs';
6
7
  import { dirname, join, resolve } from 'node:path/posix';
7
8
  import { _unique } from './state.js';
9
+ import { pick } from 'utilium';
8
10
  export const systemDir = '/etc/axium';
9
11
  export const dirs = _unique('dirs', [systemDir]);
10
12
  for (let dir = resolve(process.cwd()); dir !== '/'; dir = dirname(dir)) {
@@ -81,9 +83,12 @@ export async function restrictedPorts(opt) {
81
83
  }
82
84
  export async function getAllVersions() {
83
85
  return await Array.fromAsync([
84
- ...plugins.values().map(p => getVersionInfo(p.specifier, p.loadedBy)),
85
- getVersionInfo('@axium/server'),
86
- getVersionInfo('@axium/core'),
87
- getVersionInfo('@axium/client'),
86
+ ...plugins.values().map(async (p) => ({
87
+ ...pick(p, 'name', 'version'),
88
+ latest: (p.update_checks ? await fetchPackageMetadata(p.name) : null)?._latest,
89
+ })),
90
+ getVersionInfo('@axium/server', import.meta.dirname),
91
+ getVersionInfo('@axium/core', import.meta.dirname),
92
+ getVersionInfo('@axium/client', import.meta.dirname),
88
93
  ]);
89
94
  }
package/dist/linking.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- interface LinkInfo {
1
+ export interface LinkInfo {
2
2
  id: string;
3
3
  from: string;
4
4
  to: string;
@@ -13,4 +13,3 @@ export declare function listRouteLinks(options?: LinkOptions): Generator<LinkInf
13
13
  export declare function linkRoutes(options?: LinkOptions): void;
14
14
  export declare function writePluginHooks(): void;
15
15
  export declare function unlinkRoutes(options?: LinkOptions): void;
16
- export {};
package/dist/main.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import './db/cli.js';
2
+ import './cli/db.js';
3
+ import './cli/index.js';