@axium/server 0.7.6 → 0.9.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 (57) hide show
  1. package/dist/apps.d.ts +15 -0
  2. package/dist/apps.js +20 -0
  3. package/dist/auth.d.ts +53 -30
  4. package/dist/auth.js +103 -130
  5. package/dist/cli.js +176 -42
  6. package/dist/config.d.ts +57 -312
  7. package/dist/config.js +65 -31
  8. package/dist/database.d.ts +31 -40
  9. package/dist/database.js +165 -62
  10. package/dist/io.js +6 -2
  11. package/dist/plugins.d.ts +8 -24
  12. package/dist/plugins.js +10 -14
  13. package/dist/routes.d.ts +55 -0
  14. package/dist/routes.js +54 -0
  15. package/package.json +11 -16
  16. package/web/api/index.ts +7 -0
  17. package/web/api/metadata.ts +35 -0
  18. package/web/api/passkeys.ts +56 -0
  19. package/web/api/readme.md +1 -0
  20. package/web/api/register.ts +83 -0
  21. package/web/api/schemas.ts +22 -0
  22. package/web/api/session.ts +33 -0
  23. package/web/api/users.ts +351 -0
  24. package/web/api/utils.ts +66 -0
  25. package/web/auth.ts +1 -5
  26. package/web/hooks.server.ts +12 -1
  27. package/web/index.server.ts +0 -1
  28. package/web/lib/ClipboardCopy.svelte +42 -0
  29. package/web/lib/Dialog.svelte +3 -6
  30. package/web/lib/FormDialog.svelte +61 -14
  31. package/web/lib/Toast.svelte +8 -1
  32. package/web/lib/UserCard.svelte +1 -1
  33. package/web/lib/auth.ts +12 -0
  34. package/web/lib/icons/Icon.svelte +7 -13
  35. package/web/lib/index.ts +0 -2
  36. package/web/lib/styles.css +18 -1
  37. package/web/routes/+layout.svelte +1 -1
  38. package/web/routes/[...path]/+page.server.ts +13 -0
  39. package/web/routes/[appId]/[...page]/+page.server.ts +14 -0
  40. package/web/routes/_axium/default/+page.svelte +11 -0
  41. package/web/routes/account/+page.svelte +291 -0
  42. package/web/routes/api/[...path]/+server.ts +49 -0
  43. package/web/routes/login/+page.svelte +25 -0
  44. package/web/routes/logout/+page.svelte +13 -0
  45. package/web/routes/register/+page.svelte +21 -0
  46. package/web/tsconfig.json +2 -1
  47. package/web/utils.ts +9 -15
  48. package/web/actions.ts +0 -58
  49. package/web/lib/Account.svelte +0 -76
  50. package/web/lib/SignUp.svelte +0 -20
  51. package/web/lib/account.css +0 -36
  52. package/web/routes/+page.server.ts +0 -16
  53. package/web/routes/+page.svelte +0 -10
  54. package/web/routes/name/+page.server.ts +0 -5
  55. package/web/routes/name/+page.svelte +0 -20
  56. package/web/routes/signup/+page.server.ts +0 -10
  57. package/web/routes/signup/+page.svelte +0 -15
package/dist/apps.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import type { LoadEvent, RequestEvent } from '@sveltejs/kit';
2
+ import type { WebRoute, WebRouteOptions } from './routes.js';
3
+ export interface CreateAppOptions {
4
+ id: string;
5
+ name?: string;
6
+ }
7
+ export declare const apps: Map<string, App>;
8
+ export declare class App {
9
+ readonly id: string;
10
+ name: string;
11
+ protected readonly routes: Map<string, WebRoute>;
12
+ constructor(opt: CreateAppOptions);
13
+ addRoute(route: WebRouteOptions): void;
14
+ resolveRoute(event: RequestEvent | LoadEvent): WebRoute | undefined;
15
+ }
package/dist/apps.js ADDED
@@ -0,0 +1,20 @@
1
+ import { pick } from 'utilium';
2
+ import { addRoute, resolveRoute } from './routes.js';
3
+ export const apps = new Map();
4
+ export class App {
5
+ id;
6
+ name;
7
+ routes = new Map();
8
+ constructor(opt) {
9
+ if (apps.has(opt.id))
10
+ throw new ReferenceError(`App with ID "${opt.id}" already exists.`);
11
+ Object.assign(this, pick(opt, 'id', 'name'));
12
+ apps.set(this.id, this);
13
+ }
14
+ addRoute(route) {
15
+ addRoute(route, this.routes);
16
+ }
17
+ resolveRoute(event) {
18
+ return resolveRoute(event, this.routes);
19
+ }
20
+ }
package/dist/auth.d.ts CHANGED
@@ -1,35 +1,58 @@
1
- import type { Adapter } from '@auth/core/adapters';
2
- import type { Provider } from '@auth/core/providers';
3
- import type { AuthConfig } from '@auth/core/types';
4
- import { Registration } from '@axium/core/schemas';
5
- /**
6
- * User preferences.
7
- * Modify with `declare module ...`
8
- */
9
- export interface Preferences {
1
+ import type { Passkey, Session, Verification } from '@axium/core/api';
2
+ import type { User } from '@axium/core/user';
3
+ export interface UserInternal extends User {
4
+ password?: string | null;
5
+ salt?: string | null;
10
6
  }
11
- declare module '@auth/core/adapters' {
12
- interface AdapterUser {
13
- password: string | null;
14
- salt: string | null;
15
- preferences: Preferences;
16
- }
7
+ export declare function getUser(id: string): Promise<{
8
+ name: string;
9
+ id: string;
10
+ image: string | null | undefined;
11
+ email: string;
12
+ emailVerified: Date | null | undefined;
13
+ preferences: import("@axium/core/user").Preferences | undefined;
14
+ } | null>;
15
+ export declare function getUserByEmail(email: string): Promise<{
16
+ name: string;
17
+ id: string;
18
+ image: string | null | undefined;
19
+ email: string;
20
+ emailVerified: Date | null | undefined;
21
+ preferences: import("@axium/core/user").Preferences | undefined;
22
+ } | null>;
23
+ export declare function updateUser({ id, ...user }: UserInternal): Promise<{
24
+ name: string;
25
+ id: string;
26
+ image: string | null | undefined;
27
+ email: string;
28
+ emailVerified: Date | null | undefined;
29
+ preferences: import("@axium/core/user").Preferences | undefined;
30
+ }>;
31
+ export interface SessionInternal extends Session {
32
+ token: string;
17
33
  }
18
- export declare let adapter: Adapter;
19
- export declare function createAdapter(): void;
20
- /**
21
- * Login using credentials
22
- */
23
- export declare function register(credentials: Registration): Promise<{
24
- user: import("@auth/core/adapters").AdapterUser;
25
- session: import("@auth/core/adapters").AdapterSession;
34
+ export declare function createSession(userId: string, elevated?: boolean): Promise<SessionInternal>;
35
+ export declare function getSessionAndUser(token: string): Promise<SessionInternal & {
36
+ user: UserInternal | null;
26
37
  }>;
38
+ export declare function getSession(sessionId: string): Promise<SessionInternal>;
39
+ export declare function getSessions(userId: string): Promise<SessionInternal[]>;
40
+ export type VerificationRole = 'verify_email' | 'login';
41
+ export interface VerificationInternal extends Verification {
42
+ token: string;
43
+ role: VerificationRole;
44
+ }
27
45
  /**
28
- * Authorize using credentials
46
+ * Create a verification
47
+ * @param expires How long the token should be valid for in seconds
29
48
  */
30
- export declare function authorize(credentials: Partial<Record<string, unknown>>): Promise<Omit<import("@auth/core/adapters").AdapterUser, "password" | "salt"> | null>;
31
- type Providers = Exclude<Provider, (...args: any[]) => any>[];
32
- export declare function getConfig(): AuthConfig & {
33
- providers: Providers;
34
- };
35
- export {};
49
+ export declare function createVerification(role: VerificationRole, userId: string, expires: number): Promise<VerificationInternal>;
50
+ export declare function useVerification(role: VerificationRole, userId: string, token: string): Promise<VerificationInternal | undefined>;
51
+ export interface PasskeyInternal extends Passkey {
52
+ publicKey: Uint8Array;
53
+ counter: number;
54
+ }
55
+ export declare function getPasskey(id: string): Promise<PasskeyInternal | null>;
56
+ export declare function createPasskey(passkey: Omit<PasskeyInternal, 'createdAt'>): Promise<PasskeyInternal>;
57
+ export declare function getPasskeysByUserId(userId: string): Promise<PasskeyInternal[]>;
58
+ export declare function updatePasskeyCounter(id: PasskeyInternal['id'], newCounter: PasskeyInternal['counter']): Promise<PasskeyInternal>;
package/dist/auth.js CHANGED
@@ -1,137 +1,110 @@
1
- import { CredentialsSignin } from '@auth/core/errors';
2
- import Credentials from '@auth/core/providers/credentials';
3
- import Passkey from '@auth/core/providers/passkey';
4
- import { KyselyAdapter } from '@auth/kysely-adapter';
5
- import { Login, Registration } from '@axium/core/schemas';
6
- import { genSaltSync, hashSync } from 'bcryptjs';
7
- import { randomBytes } from 'node:crypto';
8
- import { omit } from 'utilium';
9
- import config from './config.js';
10
- import * as db from './database.js';
11
- import { logger } from './io.js';
12
- export let adapter;
13
- export function createAdapter() {
14
- if (adapter)
15
- return;
16
- const conn = db.connect();
17
- adapter = Object.assign(KyselyAdapter(conn), {
18
- async getAccount(providerAccountId, provider) {
19
- const result = await conn.selectFrom('Account').selectAll().where('providerAccountId', '=', providerAccountId).where('provider', '=', provider).executeTakeFirst();
20
- return result ?? null;
21
- },
22
- async getAuthenticator(credentialID) {
23
- const result = await conn.selectFrom('Authenticator').selectAll().where('credentialID', '=', credentialID).executeTakeFirst();
24
- return result ?? null;
25
- },
26
- async createAuthenticator(authenticator) {
27
- await conn.insertInto('Authenticator').values(authenticator).executeTakeFirstOrThrow();
28
- return authenticator;
29
- },
30
- async listAuthenticatorsByUserId(userId) {
31
- const result = await conn.selectFrom('Authenticator').selectAll().where('userId', '=', userId).execute();
32
- return result;
33
- },
34
- async updateAuthenticatorCounter(credentialID, newCounter) {
35
- await conn.updateTable('Authenticator').set({ counter: newCounter }).where('credentialID', '=', credentialID).executeTakeFirstOrThrow();
36
- const authenticator = await adapter.getAuthenticator?.(credentialID);
37
- if (!authenticator)
38
- throw new Error('Authenticator not found');
39
- return authenticator;
40
- },
41
- });
1
+ import { jsonObjectFrom } from 'kysely/helpers/postgres';
2
+ import { randomBytes, randomUUID } from 'node:crypto';
3
+ import { connect, database as db } from './database.js';
4
+ export async function getUser(id) {
5
+ connect();
6
+ const result = await db.selectFrom('users').selectAll().where('id', '=', id).executeTakeFirst();
7
+ if (!result)
8
+ return null;
9
+ return result;
42
10
  }
43
- /**
44
- * Login using credentials
45
- */
46
- export async function register(credentials) {
47
- const { email, password, name } = Registration.parse(credentials);
48
- const existing = await adapter.getUserByEmail?.(email);
49
- if (existing)
50
- throw 'User already exists';
51
- let id = crypto.randomUUID();
52
- while (await adapter.getUser?.(id))
53
- id = crypto.randomUUID();
54
- const salt = genSaltSync(10);
55
- const user = await adapter.createUser({
56
- id,
57
- name,
58
- email,
59
- emailVerified: null,
60
- salt: password ? salt : null,
61
- password: password ? hashSync(password, salt) : null,
62
- preferences: {},
63
- });
64
- const expires = new Date();
65
- expires.setMonth(expires.getMonth() + 1);
66
- const session = await adapter.createSession({
67
- sessionToken: randomBytes(64).toString('base64'),
68
- userId: id,
69
- expires,
70
- });
71
- return { user, session };
11
+ export async function getUserByEmail(email) {
12
+ connect();
13
+ const result = await db.selectFrom('users').selectAll().where('email', '=', email).executeTakeFirst();
14
+ if (!result)
15
+ return null;
16
+ return result;
17
+ }
18
+ export async function updateUser({ id, ...user }) {
19
+ connect();
20
+ const query = db.updateTable('users').set(user).where('id', '=', id);
21
+ return await query.returningAll().executeTakeFirstOrThrow();
22
+ }
23
+ const in30days = () => new Date(Date.now() + 2592000000);
24
+ const in10minutes = () => new Date(Date.now() + 600000);
25
+ export async function createSession(userId, elevated = false) {
26
+ connect();
27
+ const session = {
28
+ id: randomUUID(),
29
+ userId,
30
+ token: randomBytes(64).toString('base64'),
31
+ expires: elevated ? in10minutes() : in30days(),
32
+ elevated,
33
+ created: new Date(),
34
+ };
35
+ await db.insertInto('sessions').values(session).execute();
36
+ return session;
37
+ }
38
+ export async function getSessionAndUser(token) {
39
+ connect();
40
+ const result = await db
41
+ .selectFrom('sessions')
42
+ .selectAll()
43
+ .select(eb => jsonObjectFrom(eb.selectFrom('users').selectAll().whereRef('users.id', '=', 'sessions.userId')).as('user'))
44
+ .where('sessions.token', '=', token)
45
+ .where('sessions.expires', '>', new Date())
46
+ .executeTakeFirst();
47
+ if (!result)
48
+ throw new Error('Session not found');
49
+ return result;
50
+ }
51
+ export async function getSession(sessionId) {
52
+ connect();
53
+ return await db
54
+ .selectFrom('sessions')
55
+ .selectAll()
56
+ .where('id', '=', sessionId)
57
+ .where('sessions.expires', '>', new Date())
58
+ .executeTakeFirstOrThrow();
59
+ }
60
+ export async function getSessions(userId) {
61
+ connect();
62
+ return await db.selectFrom('sessions').selectAll().where('userId', '=', userId).where('sessions.expires', '>', new Date()).execute();
72
63
  }
73
64
  /**
74
- * Authorize using credentials
65
+ * Create a verification
66
+ * @param expires How long the token should be valid for in seconds
75
67
  */
76
- export async function authorize(credentials) {
77
- const { success, error, data } = Login.safeParse(credentials);
78
- if (!success)
79
- throw new CredentialsSignin(error);
80
- const user = await adapter.getUserByEmail?.(data.email);
81
- if (!user || !data.password || !user.salt)
82
- return null;
83
- if (user.password !== hashSync(data.password, user.salt))
68
+ export async function createVerification(role, userId, expires) {
69
+ const token = randomBytes(64).toString('base64url');
70
+ const verification = { userId, token, expires: new Date(Date.now() + expires * 1000), role };
71
+ connect();
72
+ await db.insertInto('verifications').values(verification).executeTakeFirstOrThrow();
73
+ setTimeout(() => {
74
+ void db.deleteFrom('verifications').where('verifications.token', '=', verification.token).execute();
75
+ }, expires * 1000);
76
+ return verification;
77
+ }
78
+ export async function useVerification(role, userId, token) {
79
+ connect();
80
+ const query = db
81
+ .deleteFrom('verifications')
82
+ .where('verifications.token', '=', token)
83
+ .where('verifications.userId', '=', userId)
84
+ .where('verifications.role', '=', role);
85
+ return await query.returningAll().executeTakeFirst();
86
+ }
87
+ export async function getPasskey(id) {
88
+ connect();
89
+ const result = await db.selectFrom('passkeys').selectAll().where('id', '=', id).executeTakeFirst();
90
+ if (!result)
84
91
  return null;
85
- return omit(user, 'password', 'salt');
92
+ return result;
86
93
  }
87
- export function getConfig() {
88
- createAdapter();
89
- const providers = [Passkey({})];
90
- if (config.auth.credentials) {
91
- providers.push(Credentials({
92
- credentials: {
93
- email: { label: 'Email', type: 'email' },
94
- password: { label: 'Password', type: 'password' },
95
- },
96
- authorize,
97
- }));
98
- }
99
- const debug = config.auth.debug ?? config.debug;
100
- return {
101
- adapter,
102
- providers,
103
- debug,
104
- experimental: { enableWebAuthn: true },
105
- secret: config.auth.secret,
106
- useSecureCookies: config.auth.secure_cookies,
107
- session: { strategy: 'database' },
108
- logger: {
109
- error(error) {
110
- logger.error('[auth] ' + error.message);
111
- },
112
- warn(code) {
113
- switch (code) {
114
- case 'experimental-webauthn':
115
- case 'debug-enabled':
116
- return;
117
- case 'csrf-disabled':
118
- logger.warn('CSRF protection is disabled.');
119
- break;
120
- case 'env-url-basepath-redundant':
121
- case 'env-url-basepath-mismatch':
122
- default:
123
- logger.warn('[auth] ' + code);
124
- }
125
- },
126
- debug(message, metadata) {
127
- debug && logger.debug('[auth]', message, metadata ? JSON.stringify(metadata, (k, v) => (k && JSON.stringify(v).length > 100 ? '...' : v)) : '');
128
- },
129
- },
130
- callbacks: {
131
- signIn({ user }) {
132
- logger.info('[auth] signin', user.id ?? '', user.email ? `(${user.email})` : '');
133
- return true;
134
- },
135
- },
136
- };
94
+ export async function createPasskey(passkey) {
95
+ connect();
96
+ const result = await db.insertInto('passkeys').values(passkey).returningAll().executeTakeFirstOrThrow();
97
+ return result;
98
+ }
99
+ export async function getPasskeysByUserId(userId) {
100
+ connect();
101
+ return await db.selectFrom('passkeys').selectAll().where('userId', '=', userId).execute();
102
+ }
103
+ export async function updatePasskeyCounter(id, newCounter) {
104
+ connect();
105
+ await db.updateTable('passkeys').set({ counter: newCounter }).where('id', '=', id).executeTakeFirstOrThrow();
106
+ const passkey = await getPasskey(id);
107
+ if (!passkey)
108
+ throw new Error('Passkey not found');
109
+ return passkey;
137
110
  }
package/dist/cli.js CHANGED
@@ -1,6 +1,57 @@
1
1
  #!/usr/bin/env node
2
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
3
+ if (value !== null && value !== void 0) {
4
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
5
+ var dispose, inner;
6
+ if (async) {
7
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
8
+ dispose = value[Symbol.asyncDispose];
9
+ }
10
+ if (dispose === void 0) {
11
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
12
+ dispose = value[Symbol.dispose];
13
+ if (async) inner = dispose;
14
+ }
15
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
16
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
17
+ env.stack.push({ value: value, dispose: dispose, async: async });
18
+ }
19
+ else if (async) {
20
+ env.stack.push({ async: true });
21
+ }
22
+ return value;
23
+ };
24
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
25
+ return function (env) {
26
+ function fail(e) {
27
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
28
+ env.hasError = true;
29
+ }
30
+ var r, s = 0;
31
+ function next() {
32
+ while (r = env.stack.pop()) {
33
+ try {
34
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
35
+ if (r.dispose) {
36
+ var result = r.dispose.call(r.value);
37
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
38
+ }
39
+ else s |= 1;
40
+ }
41
+ catch (e) {
42
+ fail(e);
43
+ }
44
+ }
45
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
46
+ if (env.hasError) throw env.error;
47
+ }
48
+ return next();
49
+ };
50
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
51
+ var e = new Error(message);
52
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
53
+ });
2
54
  import { Argument, Option, program } from 'commander';
3
- import { randomBytes } from 'node:crypto';
4
55
  import { styleText } from 'node:util';
5
56
  import { getByString, isJSON, setByString } from 'utilium';
6
57
  import $pkg from '../package.json' with { type: 'json' };
@@ -8,6 +59,7 @@ import config from './config.js';
8
59
  import * as db from './database.js';
9
60
  import { _portActions, _portMethods, exit, handleError, output, restrictedPorts } from './io.js';
10
61
  import { loadDefaultPlugins, plugins, pluginText, resolvePlugin } from './plugins.js';
62
+ import { apps } from './apps.js';
11
63
  program
12
64
  .version($pkg.version)
13
65
  .name('axium')
@@ -25,10 +77,10 @@ program.hook('preAction', async function (_, action) {
25
77
  opt.force && output.warn('--force: Protections disabled.');
26
78
  if (opt.debug === false)
27
79
  config.set({ debug: false });
28
- if (!config.auth.secret) {
80
+ /* if (!config.auth.secret) {
29
81
  config.save({ auth: { secret: process.env.AUTH_SECRET || randomBytes(32).toString('base64') } }, true);
30
82
  output.debug('Auto-generated a new auth secret');
31
- }
83
+ } */
32
84
  });
33
85
  // Options shared by multiple (sub)commands
34
86
  const opts = {
@@ -76,32 +128,82 @@ axiumDB
76
128
  .description('Drop the Axium database and user')
77
129
  .addOption(opts.force)
78
130
  .action(async (opt) => {
79
- const stats = await db.status().catch(exit);
80
- if (!opt.force)
81
- for (const key of ['users', 'accounts', 'sessions']) {
82
- if (stats[key] == 0)
83
- continue;
84
- output.warn(`Database has existing ${key}. Use --force if you really want to drop the database.`);
85
- process.exit(2);
86
- }
87
- await db.uninstall(opt).catch(exit);
88
- await db.database.destroy();
131
+ const env_1 = { stack: [], error: void 0, hasError: false };
132
+ try {
133
+ const _ = __addDisposableResource(env_1, db.connect(), true);
134
+ const stats = await db.status().catch(exit);
135
+ if (!opt.force)
136
+ for (const key of ['users', 'passkeys', 'sessions']) {
137
+ if (stats[key] == 0)
138
+ continue;
139
+ output.warn(`Database has existing ${key}. Use --force if you really want to drop the database.`);
140
+ process.exit(2);
141
+ }
142
+ await db.uninstall(opt).catch(exit);
143
+ }
144
+ catch (e_1) {
145
+ env_1.error = e_1;
146
+ env_1.hasError = true;
147
+ }
148
+ finally {
149
+ const result_1 = __disposeResources(env_1);
150
+ if (result_1)
151
+ await result_1;
152
+ }
89
153
  });
90
154
  axiumDB
91
155
  .command('wipe')
92
156
  .description('Wipe the database')
93
157
  .addOption(opts.force)
94
158
  .action(async (opt) => {
95
- const stats = await db.status().catch(exit);
96
- if (!opt.force)
97
- for (const key of ['users', 'accounts', 'sessions']) {
98
- if (stats[key] == 0)
99
- continue;
100
- output.warn(`Database has existing ${key}. Use --force if you really want to wipe the database.`);
101
- process.exit(2);
102
- }
103
- await db.wipe(opt).catch(exit);
104
- await db.database.destroy();
159
+ const env_2 = { stack: [], error: void 0, hasError: false };
160
+ try {
161
+ const _ = __addDisposableResource(env_2, db.connect(), true);
162
+ const stats = await db.status().catch(exit);
163
+ if (!opt.force)
164
+ for (const key of ['users', 'passkeys', 'sessions']) {
165
+ if (stats[key] == 0)
166
+ continue;
167
+ output.warn(`Database has existing ${key}. Use --force if you really want to wipe the database.`);
168
+ process.exit(2);
169
+ }
170
+ await db.wipe(opt).catch(exit);
171
+ }
172
+ catch (e_2) {
173
+ env_2.error = e_2;
174
+ env_2.hasError = true;
175
+ }
176
+ finally {
177
+ const result_2 = __disposeResources(env_2);
178
+ if (result_2)
179
+ await result_2;
180
+ }
181
+ });
182
+ axiumDB
183
+ .command('check')
184
+ .description('Check the structure of the database')
185
+ .action(async (opt) => {
186
+ await db.check(opt).catch(exit);
187
+ });
188
+ axiumDB
189
+ .command('clean')
190
+ .description('Remove expired rows')
191
+ .addOption(opts.force)
192
+ .action(async (opt) => {
193
+ const env_3 = { stack: [], error: void 0, hasError: false };
194
+ try {
195
+ const _ = __addDisposableResource(env_3, db.connect(), true);
196
+ await db.clean(opt).catch(exit);
197
+ }
198
+ catch (e_3) {
199
+ env_3.error = e_3;
200
+ env_3.hasError = true;
201
+ }
202
+ finally {
203
+ const result_3 = __disposeResources(env_3);
204
+ if (result_3)
205
+ await result_3;
206
+ }
105
207
  });
106
208
  const axiumConfig = program
107
209
  .command('config')
@@ -111,7 +213,6 @@ const axiumConfig = program
111
213
  .option('-r, --redact', 'Do not output sensitive values');
112
214
  function configReplacer(opt) {
113
215
  return (key, value) => {
114
- // eslint-disable-next-line @typescript-eslint/no-unsafe-return
115
216
  return opt.redact && ['password', 'secret'].includes(key) ? '[redacted]' : value;
116
217
  };
117
218
  }
@@ -191,33 +292,66 @@ axiumPlugin
191
292
  exit(`Can't find a plugin matching "${search}"`);
192
293
  console.log(pluginText(plugin));
193
294
  });
295
+ const axiumApps = program.command('apps').description('Manage Axium apps').addOption(opts.global);
296
+ axiumApps
297
+ .command('list')
298
+ .alias('ls')
299
+ .description('List apps added by plugins')
300
+ .option('-l, --long', 'use the long listing format')
301
+ .option('-b, --builtin', 'include built-in apps')
302
+ .action((opt) => {
303
+ if (!apps.size) {
304
+ console.log('No apps.');
305
+ return;
306
+ }
307
+ if (!opt.long) {
308
+ console.log(Array.from(apps.values().map(app => app.name)).join(', '));
309
+ return;
310
+ }
311
+ console.log(styleText('whiteBright', apps.size + ' app(s) loaded:'));
312
+ for (const app of apps.values()) {
313
+ console.log(app.name, styleText('dim', `(${app.id})`));
314
+ }
315
+ });
194
316
  program
195
317
  .command('status')
196
318
  .alias('stats')
197
319
  .description('Get information about the server')
198
320
  .addOption(opts.host)
199
321
  .action(async () => {
200
- console.log('Axium Server v' + program.version());
201
- console.log(styleText('whiteBright', 'Debug mode:'), config.debug ? styleText('yellow', 'enabled') : 'disabled');
202
- console.log(styleText('whiteBright', 'Loaded config files:'), config.files.keys().toArray().join(', '));
203
- process.stdout.write(styleText('whiteBright', 'Database: '));
322
+ const env_4 = { stack: [], error: void 0, hasError: false };
204
323
  try {
205
- console.log(await db.statusText());
324
+ console.log('Axium Server v' + program.version());
325
+ console.log(styleText('whiteBright', 'Debug mode:'), config.debug ? styleText('yellow', 'enabled') : 'disabled');
326
+ console.log(styleText('whiteBright', 'Loaded config files:'), config.files.keys().toArray().join(', '));
327
+ process.stdout.write(styleText('whiteBright', 'Database: '));
328
+ const _ = __addDisposableResource(env_4, db.connect(), true);
329
+ try {
330
+ console.log(await db.statusText());
331
+ }
332
+ catch {
333
+ output.error('Unavailable');
334
+ }
335
+ console.log(styleText('whiteBright', 'Credentials authentication:'), config.auth.credentials ? styleText('yellow', 'enabled') : 'disabled');
336
+ console.log(styleText('whiteBright', 'Loaded plugins:'), Array.from(plugins)
337
+ .map(plugin => plugin.id)
338
+ .join(', ') || styleText('dim', '(none)'));
339
+ for (const plugin of plugins) {
340
+ if (!plugin.statusText)
341
+ continue;
342
+ console.log(styleText('bold', plugin.name), plugin.version + ':');
343
+ console.log(await plugin.statusText());
344
+ }
206
345
  }
207
- catch {
208
- output.error('Unavailable');
346
+ catch (e_4) {
347
+ env_4.error = e_4;
348
+ env_4.hasError = true;
209
349
  }
210
- console.log(styleText('whiteBright', 'Credentials authentication:'), config.auth.credentials ? styleText('yellow', 'enabled') : 'disabled');
211
- console.log(styleText('whiteBright', 'Loaded plugins:'), Array.from(plugins)
212
- .map(plugin => plugin.id)
213
- .join(', ') || styleText('dim', '(none)'));
214
- for (const plugin of plugins) {
215
- if (!plugin.statusText)
216
- continue;
217
- console.log(styleText('bold', plugin.name), plugin.version + ':');
218
- console.log(await plugin.statusText());
350
+ finally {
351
+ const result_4 = __disposeResources(env_4);
352
+ if (result_4)
353
+ await result_4;
219
354
  }
220
- await db.database.destroy();
221
355
  });
222
356
  program
223
357
  .command('ports')
@@ -234,7 +368,7 @@ program
234
368
  .addOption(opts.force)
235
369
  .addOption(opts.host)
236
370
  .action(async (opt) => {
237
- config.save({ auth: { secret: randomBytes(32).toString('base64') } }, true);
371
+ /* config.save({ auth: { secret: randomBytes(32).toString('base64') } }, true); */
238
372
  await db.init({ ...opt, skip: opt.dbSkip }).catch(handleError);
239
373
  await restrictedPorts({ method: 'node-cap', action: 'enable' }).catch(handleError);
240
374
  });