@axium/server 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/auth.js CHANGED
@@ -6,7 +6,7 @@ import { Login, Registration } from '@axium/core/schemas';
6
6
  import { genSaltSync, hashSync } from 'bcryptjs';
7
7
  import { randomBytes } from 'node:crypto';
8
8
  import { omit } from 'utilium';
9
- import * as config from './config.js';
9
+ import config from './config.js';
10
10
  import * as db from './database.js';
11
11
  import { logger } from './io.js';
12
12
  export let adapter;
package/dist/cli.js CHANGED
@@ -3,9 +3,10 @@ import { Argument, Option, program } from 'commander';
3
3
  import { styleText } from 'node:util';
4
4
  import { getByString, isJSON, setByString } from 'utilium';
5
5
  import $pkg from '../package.json' with { type: 'json' };
6
- import * as config from './config.js';
6
+ import config from './config.js';
7
7
  import * as db from './database.js';
8
8
  import { _portActions, _portMethods, exit, handleError, output, restrictedPorts } from './io.js';
9
+ import { loadDefaultPlugins, plugins, pluginText, resolvePlugin } from './plugins.js';
9
10
  program
10
11
  .version($pkg.version)
11
12
  .name('axium')
@@ -16,8 +17,9 @@ program
16
17
  .option('-c, --config <path>', 'path to the config file');
17
18
  program.on('option:debug', () => config.set({ debug: true }));
18
19
  program.on('option:config', () => config.load(program.opts().config));
19
- program.hook('preAction', function (_, action) {
20
+ program.hook('preAction', async function (_, action) {
20
21
  config.loadDefaults();
22
+ await loadDefaultPlugins();
21
23
  const opt = action.optsWithGlobals();
22
24
  opt.force && output.warn('--force: Protections disabled.');
23
25
  if (opt.debug === false)
@@ -32,25 +34,26 @@ const opts = {
32
34
  config.db.port = port && Number.isSafeInteger(parseInt(port)) ? parseInt(port) : config.db.port;
33
35
  }),
34
36
  force: new Option('-f, --force', 'force the operation').default(false),
37
+ global: new Option('-g, --global', 'apply the operation globally').default(false),
35
38
  };
36
39
  const axiumDB = program
37
40
  .command('db')
38
41
  .alias('database')
39
- .description('manage the database')
42
+ .description('Manage the database')
40
43
  .option('-t, --timeout <ms>', 'how long to wait for commands to complete.', '1000')
41
44
  .addOption(opts.host);
42
45
  axiumDB
43
46
  .command('init')
44
- .description('initialize the database')
47
+ .description('Initialize the database')
45
48
  .addOption(opts.force)
46
- .option('-s, --skip', 'Skip existing database and/or user')
49
+ .option('-s, --skip', 'If the user, database, or schema already exists, skip trying to create it.')
47
50
  .action(async (opt) => {
48
51
  await db.init(opt).catch(handleError);
49
52
  });
50
53
  axiumDB
51
54
  .command('status')
52
55
  .alias('stats')
53
- .description('check the status of the database')
56
+ .description('Check the status of the database')
54
57
  .action(async () => {
55
58
  try {
56
59
  console.log(await db.statusText());
@@ -65,7 +68,7 @@ axiumDB
65
68
  });
66
69
  axiumDB
67
70
  .command('drop')
68
- .description('drop the database')
71
+ .description('Drop the Axium database and user')
69
72
  .addOption(opts.force)
70
73
  .action(async (opt) => {
71
74
  const stats = await db.status().catch(exit);
@@ -81,7 +84,7 @@ axiumDB
81
84
  });
82
85
  axiumDB
83
86
  .command('wipe')
84
- .description('wipe the database')
87
+ .description('Wipe the database')
85
88
  .addOption(opts.force)
86
89
  .action(async (opt) => {
87
90
  const stats = await db.status().catch(exit);
@@ -95,21 +98,17 @@ axiumDB
95
98
  await db.wipe(opt).catch(exit);
96
99
  await db.database.destroy();
97
100
  });
98
- const axiumConfig = program
99
- .command('config')
100
- .description('manage the configuration')
101
- .option('-j, --json', 'values are JSON encoded')
102
- .option('-g, --global', 'apply to the global config');
101
+ const axiumConfig = program.command('config').description('Manage the configuration').addOption(opts.global).option('-j, --json', 'values are JSON encoded');
103
102
  axiumConfig
104
103
  .command('dump')
105
104
  .description('Output the entire current configuration')
106
105
  .action(() => {
107
- const value = config.get();
106
+ const value = config;
108
107
  console.log(axiumConfig.optsWithGlobals().json ? JSON.stringify(value) : value);
109
108
  });
110
109
  axiumConfig
111
110
  .command('get')
112
- .description('get a config value')
111
+ .description('Get a config value')
113
112
  .argument('<key>', 'the key to get')
114
113
  .action((key) => {
115
114
  const value = getByString(config, key);
@@ -137,16 +136,53 @@ axiumConfig
137
136
  for (const path of config.files.keys())
138
137
  console.log(path);
139
138
  });
139
+ const axiumPlugin = program
140
+ .command('plugins')
141
+ .description('Manage plugins')
142
+ .addOption(opts.global)
143
+ .option('--safe', 'do not perform actions that would execute code from plugins.');
144
+ axiumPlugin
145
+ .command('list')
146
+ .alias('ls')
147
+ .description('List loaded plugins')
148
+ .option('-l, --long', 'use the long listing format')
149
+ .option('--no-versions', 'do not show plugin versions')
150
+ .action((opt) => {
151
+ if (!plugins.size) {
152
+ console.log('No plugins loaded.');
153
+ return;
154
+ }
155
+ if (!opt.long) {
156
+ console.log(Array.from(plugins)
157
+ .map(plugin => plugin.name)
158
+ .join(', '));
159
+ return;
160
+ }
161
+ console.log(styleText('whiteBright', plugins.size + ' plugin(s) loaded:'));
162
+ for (const plugin of plugins) {
163
+ console.log(plugin.name, styleText('dim', `(${plugin.id})`), opt.versions ? plugin.version : '');
164
+ }
165
+ });
166
+ axiumPlugin
167
+ .command('info')
168
+ .description('Get information about a plugin')
169
+ .argument('<plugin>', 'the plugin to get information about')
170
+ .action((search) => {
171
+ const plugin = resolvePlugin(search);
172
+ if (!plugin)
173
+ exit(`Can't find a plugin matching "${search}"`);
174
+ console.log(pluginText(plugin));
175
+ });
140
176
  program
141
177
  .command('status')
142
178
  .alias('stats')
143
- .description('get information about the server')
179
+ .description('Get information about the server')
144
180
  .addOption(opts.host)
145
181
  .action(async () => {
146
182
  console.log('Axium Server v' + program.version());
147
- console.log('Debug mode:', config.debug ? styleText('yellow', 'enabled') : 'disabled');
148
- console.log('Loaded config files:', config.files.keys().toArray().join(', '));
149
- process.stdout.write('Database: ');
183
+ console.log(styleText('whiteBright', 'Debug mode:'), config.debug ? styleText('yellow', 'enabled') : 'disabled');
184
+ console.log(styleText('whiteBright', 'Loaded config files:'), config.files.keys().toArray().join(', '));
185
+ process.stdout.write(styleText('whiteBright', 'Database: '));
150
186
  try {
151
187
  console.log(await db.statusText());
152
188
  }
@@ -154,7 +190,16 @@ program
154
190
  output.error('Unavailable');
155
191
  }
156
192
  await db.database.destroy();
157
- console.log('Credentials authentication:', config.auth.credentials ? styleText('yellow', 'enabled') : 'disabled');
193
+ console.log(styleText('whiteBright', 'Credentials authentication:'), config.auth.credentials ? styleText('yellow', 'enabled') : 'disabled');
194
+ console.log(styleText('whiteBright', 'Loaded plugins:'), Array.from(plugins)
195
+ .map(plugin => plugin.id)
196
+ .join(', ') || styleText('dim', '(none)'));
197
+ for (const plugin of plugins) {
198
+ if (!plugin.statusText)
199
+ continue;
200
+ console.log(styleText('bold', plugin.name + ':'));
201
+ console.log(plugin.statusText());
202
+ }
158
203
  });
159
204
  program
160
205
  .command('ports')
package/dist/config.d.ts CHANGED
@@ -1,170 +1,144 @@
1
1
  import { type PartialRecursive } from 'utilium';
2
2
  import * as z from 'zod';
3
- export declare const Database: z.ZodObject<{
4
- host: z.ZodString;
5
- port: z.ZodNumber;
6
- password: z.ZodString;
7
- user: z.ZodString;
8
- database: z.ZodString;
9
- }, "strip", z.ZodTypeAny, {
10
- host: string;
11
- port: number;
12
- password: string;
13
- user: string;
14
- database: string;
15
- }, {
16
- host: string;
17
- port: number;
18
- password: string;
19
- user: string;
20
- database: string;
21
- }>;
22
- export type Database = z.infer<typeof Database>;
23
- export declare const db: Database;
24
- export declare const Auth: z.ZodObject<{
25
- credentials: z.ZodBoolean;
26
- debug: z.ZodOptional<z.ZodBoolean>;
27
- secret: z.ZodString;
28
- secure_cookies: z.ZodBoolean;
29
- }, "strip", z.ZodTypeAny, {
30
- credentials: boolean;
31
- secret: string;
32
- secure_cookies: boolean;
33
- debug?: boolean | undefined;
34
- }, {
35
- credentials: boolean;
36
- secret: string;
37
- secure_cookies: boolean;
38
- debug?: boolean | undefined;
39
- }>;
40
- export type Auth = z.infer<typeof Auth>;
41
- export declare const auth: Auth;
42
- export declare const Log: z.ZodObject<{
43
- level: z.ZodEnum<["error", "warn", "notice", "info", "debug"]>;
44
- console: z.ZodBoolean;
45
- }, "strip", z.ZodTypeAny, {
46
- level: "warn" | "error" | "debug" | "notice" | "info";
47
- console: boolean;
48
- }, {
49
- level: "warn" | "error" | "debug" | "notice" | "info";
50
- console: boolean;
51
- }>;
52
- export type Log = z.infer<typeof Log>;
53
- export declare const log: Log;
54
- export declare const Web: z.ZodObject<{
55
- prefix: z.ZodString;
56
- }, "strip", z.ZodTypeAny, {
57
- prefix: string;
58
- }, {
59
- prefix: string;
60
- }>;
61
- export type Web = z.infer<typeof Web>;
62
- export declare const web: Web;
63
- export declare const Config: z.ZodObject<{
3
+ export declare const Schema: z.ZodObject<{
64
4
  auth: z.ZodObject<{
65
- credentials: z.ZodOptional<z.ZodBoolean>;
66
- debug: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
67
- secret: z.ZodOptional<z.ZodString>;
68
- secure_cookies: z.ZodOptional<z.ZodBoolean>;
5
+ credentials: z.ZodBoolean;
6
+ debug: z.ZodBoolean;
7
+ secret: z.ZodString;
8
+ secure_cookies: z.ZodBoolean;
69
9
  }, "strip", z.ZodTypeAny, {
70
- debug?: boolean | undefined;
71
- credentials?: boolean | undefined;
72
- secret?: string | undefined;
73
- secure_cookies?: boolean | undefined;
10
+ debug: boolean;
11
+ credentials: boolean;
12
+ secret: string;
13
+ secure_cookies: boolean;
74
14
  }, {
75
- debug?: boolean | undefined;
76
- credentials?: boolean | undefined;
77
- secret?: string | undefined;
78
- secure_cookies?: boolean | undefined;
15
+ debug: boolean;
16
+ credentials: boolean;
17
+ secret: string;
18
+ secure_cookies: boolean;
79
19
  }>;
80
- debug: z.ZodBoolean;
81
20
  db: z.ZodObject<{
82
- host: z.ZodOptional<z.ZodString>;
83
- port: z.ZodOptional<z.ZodNumber>;
84
- password: z.ZodOptional<z.ZodString>;
85
- user: z.ZodOptional<z.ZodString>;
86
- database: z.ZodOptional<z.ZodString>;
21
+ host: z.ZodString;
22
+ port: z.ZodNumber;
23
+ password: z.ZodString;
24
+ user: z.ZodString;
25
+ database: z.ZodString;
87
26
  }, "strip", z.ZodTypeAny, {
88
- host?: string | undefined;
89
- port?: number | undefined;
90
- password?: string | undefined;
91
- user?: string | undefined;
92
- database?: string | undefined;
27
+ host: string;
28
+ port: number;
29
+ password: string;
30
+ user: string;
31
+ database: string;
93
32
  }, {
94
- host?: string | undefined;
95
- port?: number | undefined;
96
- password?: string | undefined;
97
- user?: string | undefined;
98
- database?: string | undefined;
33
+ host: string;
34
+ port: number;
35
+ password: string;
36
+ user: string;
37
+ database: string;
99
38
  }>;
39
+ debug: z.ZodBoolean;
100
40
  log: z.ZodObject<{
101
- level: z.ZodOptional<z.ZodEnum<["error", "warn", "notice", "info", "debug"]>>;
102
- console: z.ZodOptional<z.ZodBoolean>;
41
+ level: z.ZodEnum<["error", "warn", "notice", "info", "debug"]>;
42
+ console: z.ZodBoolean;
103
43
  }, "strip", z.ZodTypeAny, {
104
- level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
105
- console?: boolean | undefined;
44
+ level: "debug" | "info" | "warn" | "error" | "notice";
45
+ console: boolean;
106
46
  }, {
107
- level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
108
- console?: boolean | undefined;
47
+ level: "debug" | "info" | "warn" | "error" | "notice";
48
+ console: boolean;
109
49
  }>;
110
50
  web: z.ZodObject<{
111
- prefix: z.ZodOptional<z.ZodString>;
51
+ prefix: z.ZodString;
112
52
  }, "strip", z.ZodTypeAny, {
113
- prefix?: string | undefined;
53
+ prefix: string;
114
54
  }, {
115
- prefix?: string | undefined;
55
+ prefix: string;
116
56
  }>;
117
57
  }, "strip", z.ZodTypeAny, {
118
- log: {
119
- level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
120
- console?: boolean | undefined;
121
- };
122
58
  debug: boolean;
123
59
  auth: {
124
- debug?: boolean | undefined;
125
- credentials?: boolean | undefined;
126
- secret?: string | undefined;
127
- secure_cookies?: boolean | undefined;
60
+ debug: boolean;
61
+ credentials: boolean;
62
+ secret: string;
63
+ secure_cookies: boolean;
128
64
  };
129
65
  db: {
130
- host?: string | undefined;
131
- port?: number | undefined;
132
- password?: string | undefined;
133
- user?: string | undefined;
134
- database?: string | undefined;
66
+ host: string;
67
+ port: number;
68
+ password: string;
69
+ user: string;
70
+ database: string;
71
+ };
72
+ log: {
73
+ level: "debug" | "info" | "warn" | "error" | "notice";
74
+ console: boolean;
135
75
  };
136
76
  web: {
137
- prefix?: string | undefined;
77
+ prefix: string;
138
78
  };
139
79
  }, {
140
- log: {
141
- level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
142
- console?: boolean | undefined;
143
- };
144
80
  debug: boolean;
145
81
  auth: {
146
- debug?: boolean | undefined;
147
- credentials?: boolean | undefined;
148
- secret?: string | undefined;
149
- secure_cookies?: boolean | undefined;
82
+ debug: boolean;
83
+ credentials: boolean;
84
+ secret: string;
85
+ secure_cookies: boolean;
150
86
  };
151
87
  db: {
152
- host?: string | undefined;
153
- port?: number | undefined;
154
- password?: string | undefined;
155
- user?: string | undefined;
156
- database?: string | undefined;
88
+ host: string;
89
+ port: number;
90
+ password: string;
91
+ user: string;
92
+ database: string;
93
+ };
94
+ log: {
95
+ level: "debug" | "info" | "warn" | "error" | "notice";
96
+ console: boolean;
157
97
  };
158
98
  web: {
159
- prefix?: string | undefined;
99
+ prefix: string;
160
100
  };
161
101
  }>;
102
+ export interface Config extends Record<string, unknown>, z.infer<typeof Schema> {
103
+ }
104
+ export declare const configFiles: Map<string, PartialRecursive<Config>>;
105
+ export declare const config: {
106
+ auth: {
107
+ credentials: false;
108
+ debug: false;
109
+ secret: string;
110
+ secure_cookies: true;
111
+ };
112
+ db: {
113
+ database: string;
114
+ host: string;
115
+ password: string;
116
+ port: number;
117
+ user: string;
118
+ };
119
+ debug: false;
120
+ log: {
121
+ console: true;
122
+ level: "info";
123
+ };
124
+ web: {
125
+ prefix: string;
126
+ };
127
+ findPath: typeof findConfigPath;
128
+ load: typeof loadConfig;
129
+ loadDefaults: typeof loadDefaultConfigs;
130
+ save: typeof saveConfig;
131
+ saveTo: typeof saveConfigTo;
132
+ set: typeof setConfig;
133
+ files: Map<string, PartialRecursive<Config>>;
134
+ };
135
+ export default config;
162
136
  export declare const File: z.ZodObject<z.objectUtil.extendShape<{
163
137
  auth: z.ZodOptional<z.ZodObject<{
164
- credentials: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
165
- debug: z.ZodOptional<z.ZodOptional<z.ZodOptional<z.ZodBoolean>>>;
166
- secret: z.ZodOptional<z.ZodOptional<z.ZodString>>;
167
- secure_cookies: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
138
+ credentials: z.ZodOptional<z.ZodBoolean>;
139
+ debug: z.ZodOptional<z.ZodBoolean>;
140
+ secret: z.ZodOptional<z.ZodString>;
141
+ secure_cookies: z.ZodOptional<z.ZodBoolean>;
168
142
  }, "strip", z.ZodTypeAny, {
169
143
  debug?: boolean | undefined;
170
144
  credentials?: boolean | undefined;
@@ -176,13 +150,12 @@ export declare const File: z.ZodObject<z.objectUtil.extendShape<{
176
150
  secret?: string | undefined;
177
151
  secure_cookies?: boolean | undefined;
178
152
  }>>;
179
- debug: z.ZodOptional<z.ZodBoolean>;
180
153
  db: z.ZodOptional<z.ZodObject<{
181
- host: z.ZodOptional<z.ZodOptional<z.ZodString>>;
182
- port: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
183
- password: z.ZodOptional<z.ZodOptional<z.ZodString>>;
184
- user: z.ZodOptional<z.ZodOptional<z.ZodString>>;
185
- database: z.ZodOptional<z.ZodOptional<z.ZodString>>;
154
+ host: z.ZodOptional<z.ZodString>;
155
+ port: z.ZodOptional<z.ZodNumber>;
156
+ password: z.ZodOptional<z.ZodString>;
157
+ user: z.ZodOptional<z.ZodString>;
158
+ database: z.ZodOptional<z.ZodString>;
186
159
  }, "strip", z.ZodTypeAny, {
187
160
  host?: string | undefined;
188
161
  port?: number | undefined;
@@ -196,18 +169,19 @@ export declare const File: z.ZodObject<z.objectUtil.extendShape<{
196
169
  user?: string | undefined;
197
170
  database?: string | undefined;
198
171
  }>>;
172
+ debug: z.ZodOptional<z.ZodBoolean>;
199
173
  log: z.ZodOptional<z.ZodObject<{
200
- level: z.ZodOptional<z.ZodOptional<z.ZodEnum<["error", "warn", "notice", "info", "debug"]>>>;
201
- console: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
174
+ level: z.ZodOptional<z.ZodEnum<["error", "warn", "notice", "info", "debug"]>>;
175
+ console: z.ZodOptional<z.ZodBoolean>;
202
176
  }, "strip", z.ZodTypeAny, {
203
- level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
177
+ level?: "debug" | "info" | "warn" | "error" | "notice" | undefined;
204
178
  console?: boolean | undefined;
205
179
  }, {
206
- level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
180
+ level?: "debug" | "info" | "warn" | "error" | "notice" | undefined;
207
181
  console?: boolean | undefined;
208
182
  }>>;
209
183
  web: z.ZodOptional<z.ZodObject<{
210
- prefix: z.ZodOptional<z.ZodOptional<z.ZodString>>;
184
+ prefix: z.ZodOptional<z.ZodString>;
211
185
  }, "strip", z.ZodTypeAny, {
212
186
  prefix?: string | undefined;
213
187
  }, {
@@ -216,10 +190,6 @@ export declare const File: z.ZodObject<z.objectUtil.extendShape<{
216
190
  }, {
217
191
  include: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
218
192
  }>, "strip", z.ZodTypeAny, {
219
- log?: {
220
- level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
221
- console?: boolean | undefined;
222
- } | undefined;
223
193
  debug?: boolean | undefined;
224
194
  auth?: {
225
195
  debug?: boolean | undefined;
@@ -234,15 +204,15 @@ export declare const File: z.ZodObject<z.objectUtil.extendShape<{
234
204
  user?: string | undefined;
235
205
  database?: string | undefined;
236
206
  } | undefined;
207
+ log?: {
208
+ level?: "debug" | "info" | "warn" | "error" | "notice" | undefined;
209
+ console?: boolean | undefined;
210
+ } | undefined;
237
211
  web?: {
238
212
  prefix?: string | undefined;
239
213
  } | undefined;
240
214
  include?: string[] | undefined;
241
215
  }, {
242
- log?: {
243
- level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
244
- console?: boolean | undefined;
245
- } | undefined;
246
216
  debug?: boolean | undefined;
247
217
  auth?: {
248
218
  debug?: boolean | undefined;
@@ -257,21 +227,21 @@ export declare const File: z.ZodObject<z.objectUtil.extendShape<{
257
227
  user?: string | undefined;
258
228
  database?: string | undefined;
259
229
  } | undefined;
230
+ log?: {
231
+ level?: "debug" | "info" | "warn" | "error" | "notice" | undefined;
232
+ console?: boolean | undefined;
233
+ } | undefined;
260
234
  web?: {
261
235
  prefix?: string | undefined;
262
236
  } | undefined;
263
237
  include?: string[] | undefined;
264
238
  }>;
265
- export type File = z.infer<typeof File>;
266
- export interface Config extends z.infer<typeof Config> {
239
+ export interface File extends PartialRecursive<Config>, z.infer<typeof File> {
267
240
  }
268
- export declare let debug: boolean;
269
- export declare function get(): Config;
270
241
  /**
271
242
  * Update the current config
272
243
  */
273
- export declare function set(config: PartialRecursive<Config>): void;
274
- export declare const files: Map<string, PartialRecursive<Config>>;
244
+ export declare function setConfig(other: PartialRecursive<Config>): void;
275
245
  export interface LoadOptions {
276
246
  /**
277
247
  * If enabled, the config file will be not be loaded if it does not match the schema.
@@ -289,17 +259,17 @@ export interface LoadOptions {
289
259
  /**
290
260
  * Load the config from the provided path
291
261
  */
292
- export declare function load(path: string, options?: LoadOptions): void;
293
- export declare function loadDefaults(): void;
262
+ export declare function loadConfig(path: string, options?: LoadOptions): void;
263
+ export declare function loadDefaultConfigs(): void;
294
264
  /**
295
265
  * Update the current config and write the updated config to the appropriate file
296
266
  */
297
- export declare function save(changed: PartialRecursive<Config>, global?: boolean): void;
267
+ export declare function saveConfig(changed: PartialRecursive<Config>, global?: boolean): void;
298
268
  /**
299
269
  * Update the current config and write the updated config to the provided path
300
270
  */
301
- export declare function saveTo(path: string, changed: PartialRecursive<Config>): void;
271
+ export declare function saveConfigTo(path: string, changed: PartialRecursive<Config>): void;
302
272
  /**
303
273
  * Find the path to the config file
304
274
  */
305
- export declare function findPath(global: boolean): string;
275
+ export declare function findConfigPath(global: boolean): string;
package/dist/config.js CHANGED
@@ -2,87 +2,81 @@
2
2
  import { levelText } from 'logzen';
3
3
  import { readFileSync, writeFileSync } from 'node:fs';
4
4
  import { dirname, join } from 'node:path/posix';
5
- import { assignWithDefaults } from 'utilium';
5
+ import { deepAssign } from 'utilium';
6
6
  import * as z from 'zod';
7
7
  import { findDir, logger, output } from './io.js';
8
- export const Database = z.object({
9
- host: z.string(),
10
- port: z.number(),
11
- password: z.string(),
12
- user: z.string(),
13
- database: z.string(),
14
- });
15
- export const db = {
16
- host: process.env.PGHOST || 'localhost',
17
- port: process.env.PGPORT && Number.isSafeInteger(parseInt(process.env.PGPORT)) ? parseInt(process.env.PGPORT) : 5432,
18
- password: process.env.PGPASSWORD || '',
19
- user: process.env.PGUSER || 'axium',
20
- database: process.env.PGDATABASE || 'axium',
21
- };
22
- export const Auth = z.object({
23
- credentials: z.boolean(),
24
- debug: z.boolean().optional(),
25
- secret: z.string(),
26
- secure_cookies: z.boolean(),
27
- });
28
- export const auth = {
29
- credentials: false,
30
- secret: '',
31
- secure_cookies: false,
32
- };
33
- export const Log = z.object({
34
- level: z.enum(levelText),
35
- console: z.boolean(),
36
- });
37
- export const log = {
38
- level: 'info',
39
- console: true,
40
- };
41
- export const Web = z.object({
42
- prefix: z.string(),
43
- });
44
- export const web = {
45
- prefix: '',
46
- };
47
- export const Config = z.object({
48
- auth: Auth.partial(),
8
+ export const Schema = z.object({
9
+ auth: z.object({
10
+ credentials: z.boolean(),
11
+ debug: z.boolean(),
12
+ secret: z.string(),
13
+ secure_cookies: z.boolean(),
14
+ }),
15
+ db: z.object({
16
+ host: z.string(),
17
+ port: z.number(),
18
+ password: z.string(),
19
+ user: z.string(),
20
+ database: z.string(),
21
+ }),
49
22
  debug: z.boolean(),
50
- db: Database.partial(),
51
- log: Log.partial(),
52
- web: Web.partial(),
23
+ log: z.object({
24
+ level: z.enum(levelText),
25
+ console: z.boolean(),
26
+ }),
27
+ web: z.object({
28
+ prefix: z.string(),
29
+ }),
53
30
  });
31
+ export const configFiles = new Map();
32
+ export const config = {
33
+ auth: {
34
+ credentials: false,
35
+ debug: false,
36
+ secret: '',
37
+ secure_cookies: true,
38
+ },
39
+ db: {
40
+ database: process.env.PGDATABASE || 'axium',
41
+ host: process.env.PGHOST || 'localhost',
42
+ password: process.env.PGPASSWORD || '',
43
+ port: process.env.PGPORT && Number.isSafeInteger(parseInt(process.env.PGPORT)) ? parseInt(process.env.PGPORT) : 5432,
44
+ user: process.env.PGUSER || 'axium',
45
+ },
46
+ debug: false,
47
+ log: {
48
+ console: true,
49
+ level: 'info',
50
+ },
51
+ web: {
52
+ prefix: '',
53
+ },
54
+ findPath: findConfigPath,
55
+ load: loadConfig,
56
+ loadDefaults: loadDefaultConfigs,
57
+ save: saveConfig,
58
+ saveTo: saveConfigTo,
59
+ set: setConfig,
60
+ files: configFiles,
61
+ };
62
+ export default config;
54
63
  // config from file
55
- export const File = Config.deepPartial().extend({
64
+ export const File = Schema.deepPartial().extend({
56
65
  include: z.array(z.string()).optional(),
57
66
  });
58
- export let debug = false;
59
- export function get() {
60
- return {
61
- auth,
62
- db,
63
- debug,
64
- log,
65
- web,
66
- };
67
- }
68
67
  /**
69
68
  * Update the current config
70
69
  */
71
- export function set(config) {
72
- assignWithDefaults(auth, config.auth ?? {});
73
- debug = config.debug ?? debug;
74
- assignWithDefaults(db, config.db ?? {});
75
- assignWithDefaults(log, config.log ?? {});
76
- assignWithDefaults(web, config.web ?? {});
70
+ export function setConfig(other) {
71
+ deepAssign(config, other);
77
72
  logger.detach(output);
78
- if (log.console)
79
- logger.attach(output, { output: log.level });
73
+ if (config.log.console)
74
+ logger.attach(output, { output: config.log.level });
80
75
  }
81
- export const files = new Map();
82
76
  /**
83
77
  * Load the config from the provided path
84
78
  */
85
- export function load(path, options = {}) {
79
+ export function loadConfig(path, options = {}) {
86
80
  let json;
87
81
  try {
88
82
  json = JSON.parse(readFileSync(path, 'utf8'));
@@ -90,42 +84,42 @@ export function load(path, options = {}) {
90
84
  catch (e) {
91
85
  if (!options.optional)
92
86
  throw e;
93
- debug && output.debug(`Skipping config at ${path} (${e.message})`);
87
+ config.debug && output.debug(`Skipping config at ${path} (${e.message})`);
94
88
  return;
95
89
  }
96
- const config = options.strict ? File.parse(json) : json;
97
- files.set(path, config);
98
- set(config);
99
- for (const include of config.include ?? [])
100
- load(join(dirname(path), include), { optional: true });
90
+ const file = options.strict ? File.parse(json) : json;
91
+ configFiles.set(path, file);
92
+ setConfig(file);
93
+ for (const include of file.include ?? [])
94
+ loadConfig(join(dirname(path), include), { optional: true });
101
95
  }
102
- export function loadDefaults() {
103
- load(findPath(true), { optional: true });
104
- load(findPath(false), { optional: true });
96
+ export function loadDefaultConfigs() {
97
+ loadConfig(findConfigPath(true), { optional: true });
98
+ loadConfig(findConfigPath(false), { optional: true });
105
99
  }
106
100
  /**
107
101
  * Update the current config and write the updated config to the appropriate file
108
102
  */
109
- export function save(changed, global = false) {
110
- saveTo(process.env.AXIUM_CONFIG ?? findPath(global), changed);
103
+ export function saveConfig(changed, global = false) {
104
+ saveConfigTo(process.env.AXIUM_CONFIG ?? findConfigPath(global), changed);
111
105
  }
112
106
  /**
113
107
  * Update the current config and write the updated config to the provided path
114
108
  */
115
- export function saveTo(path, changed) {
116
- set(changed);
117
- const config = files.get(path) ?? {};
109
+ export function saveConfigTo(path, changed) {
110
+ setConfig(changed);
111
+ const config = configFiles.get(path) ?? {};
118
112
  Object.assign(config, { ...changed, db: { ...config.db, ...changed.db } });
119
- debug && output.debug(`Wrote config to ${path}`);
113
+ config.debug && output.debug(`Wrote config to ${path}`);
120
114
  writeFileSync(path, JSON.stringify(config));
121
115
  }
122
116
  /**
123
117
  * Find the path to the config file
124
118
  */
125
- export function findPath(global) {
119
+ export function findConfigPath(global) {
126
120
  if (process.env.AXIUM_CONFIG)
127
121
  return process.env.AXIUM_CONFIG;
128
122
  return join(findDir(global), 'config.json');
129
123
  }
130
124
  if (process.env.AXIUM_CONFIG)
131
- load(process.env.AXIUM_CONFIG);
125
+ loadConfig(process.env.AXIUM_CONFIG);
@@ -1,7 +1,6 @@
1
1
  import type { AdapterAccountType as db } from '@auth/core/adapters';
2
2
  import { Kysely, type GeneratedAlways } from 'kysely';
3
3
  import type { Preferences } from './auth.js';
4
- import * as config from './config.js';
5
4
  import { type MaybeOutput, type WithOutput } from './io.js';
6
5
  export interface Schema {
7
6
  User: {
@@ -67,7 +66,7 @@ export interface InitOptions extends OpOptions {
67
66
  skip: boolean;
68
67
  }
69
68
  export declare function shouldRecreate(opt: InitOptions & WithOutput): boolean;
70
- export declare function init(opt: InitOptions): Promise<config.Database>;
69
+ export declare function init(opt: InitOptions): Promise<void>;
71
70
  /**
72
71
  * Completely remove Axium from the database.
73
72
  */
package/dist/database.js CHANGED
@@ -53,8 +53,9 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
53
53
  import { Kysely, PostgresDialect, sql } from 'kysely';
54
54
  import { randomBytes } from 'node:crypto';
55
55
  import pg from 'pg';
56
- import * as config from './config.js';
56
+ import config from './config.js';
57
57
  import { _fixOutput, run, someWarnings } from './io.js';
58
+ import { plugins } from './plugins.js';
58
59
  export let database;
59
60
  export function connect() {
60
61
  if (database)
@@ -107,7 +108,7 @@ export async function init(opt) {
107
108
  opt.output('debug', 'Generated password and wrote to global config');
108
109
  }
109
110
  const _sql = (command, message) => run(opt, message, `sudo -u postgres psql -c "${command}"`);
110
- const relationExists = someWarnings(opt.output, [/relation "\w+" already exists/, 'already exists.']);
111
+ const warnExists = someWarnings(opt.output, [/(schema|relation) "\w+" already exists/, 'already exists.']);
111
112
  const done = () => opt.output('done');
112
113
  await _sql('CREATE DATABASE axium', 'Creating database').catch(async (error) => {
113
114
  if (error != 'database "axium" already exists')
@@ -145,7 +146,7 @@ export async function init(opt) {
145
146
  .addColumn('preferences', 'jsonb', col => col.notNull().defaultTo(sql `'{}'::jsonb`))
146
147
  .execute()
147
148
  .then(done)
148
- .catch(relationExists);
149
+ .catch(warnExists);
149
150
  opt.output('start', 'Creating table Account');
150
151
  await db.schema
151
152
  .createTable('Account')
@@ -163,9 +164,9 @@ export async function init(opt) {
163
164
  .addColumn('session_state', 'text')
164
165
  .execute()
165
166
  .then(done)
166
- .catch(relationExists);
167
+ .catch(warnExists);
167
168
  opt.output('start', 'Creating index for Account.userId');
168
- await db.schema.createIndex('Account_userId_index').on('Account').column('userId').execute().then(done).catch(relationExists);
169
+ await db.schema.createIndex('Account_userId_index').on('Account').column('userId').execute().then(done).catch(warnExists);
169
170
  opt.output('start', 'Creating table Session');
170
171
  await db.schema
171
172
  .createTable('Session')
@@ -175,9 +176,9 @@ export async function init(opt) {
175
176
  .addColumn('expires', 'timestamptz', col => col.notNull())
176
177
  .execute()
177
178
  .then(done)
178
- .catch(relationExists);
179
+ .catch(warnExists);
179
180
  opt.output('start', 'Creating index for Session.userId');
180
- await db.schema.createIndex('Session_userId_index').on('Session').column('userId').execute().then(done).catch(relationExists);
181
+ await db.schema.createIndex('Session_userId_index').on('Session').column('userId').execute().then(done).catch(warnExists);
181
182
  opt.output('start', 'Creating table VerificationToken');
182
183
  await db.schema
183
184
  .createTable('VerificationToken')
@@ -186,7 +187,7 @@ export async function init(opt) {
186
187
  .addColumn('expires', 'timestamptz', col => col.notNull())
187
188
  .execute()
188
189
  .then(done)
189
- .catch(relationExists);
190
+ .catch(warnExists);
190
191
  opt.output('start', 'Creating table Authenticator');
191
192
  await db.schema
192
193
  .createTable('Authenticator')
@@ -200,10 +201,15 @@ export async function init(opt) {
200
201
  .addColumn('transports', 'text')
201
202
  .execute()
202
203
  .then(done)
203
- .catch(relationExists);
204
+ .catch(warnExists);
204
205
  opt.output('start', 'Creating index for Authenticator.credentialID');
205
- await db.schema.createIndex('Authenticator_credentialID_key').on('Authenticator').column('credentialID').execute().then(done).catch(relationExists);
206
- return config.db;
206
+ await db.schema.createIndex('Authenticator_credentialID_key').on('Authenticator').column('credentialID').execute().then(done).catch(warnExists);
207
+ for (const plugin of plugins) {
208
+ if (!plugin.db)
209
+ continue;
210
+ opt.output('plugin', plugin.name);
211
+ await plugin.db.init(opt, db, { warnExists, done });
212
+ }
207
213
  }
208
214
  catch (e_1) {
209
215
  env_1.error = e_1;
@@ -220,6 +226,14 @@ export async function init(opt) {
220
226
  */
221
227
  export async function uninstall(opt) {
222
228
  _fixOutput(opt);
229
+ const db = connect();
230
+ for (const plugin of plugins) {
231
+ if (!plugin.db)
232
+ continue;
233
+ opt.output('plugin', plugin.name);
234
+ await plugin.db.remove(opt, db);
235
+ }
236
+ await db.destroy();
223
237
  const _sql = (command, message) => run(opt, message, `sudo -u postgres psql -c "${command}"`);
224
238
  await _sql('DROP DATABASE axium', 'Dropping database');
225
239
  await _sql('REVOKE ALL PRIVILEGES ON SCHEMA public FROM axium', 'Revoking schema privileges');
@@ -231,6 +245,12 @@ export async function uninstall(opt) {
231
245
  export async function wipe(opt) {
232
246
  _fixOutput(opt);
233
247
  const db = connect();
248
+ for (const plugin of plugins) {
249
+ if (!plugin.db)
250
+ continue;
251
+ opt.output('plugin', plugin.name);
252
+ await plugin.db.wipe(opt, db);
253
+ }
234
254
  for (const table of ['User', 'Account', 'Session', 'VerificationToken', 'Authenticator']) {
235
255
  opt.output('start', `Removing data from ${table}`);
236
256
  await db.deleteFrom(table).execute();
package/dist/io.d.ts CHANGED
@@ -25,10 +25,10 @@ export declare function run(opts: WithOutput & {
25
25
  /** Yet another convenience function */
26
26
  export declare function exit(message: string | Error, code?: number): never;
27
27
  export declare function handleError(e: number | string | Error): void;
28
- export type OutputState = 'done' | 'log' | 'warn' | 'error' | 'start' | 'debug';
28
+ export type OutputTag = 'debug' | 'info' | 'warn' | 'error' | 'start' | 'done' | 'plugin';
29
29
  export interface Output {
30
- (state: 'done'): void;
31
- (state: Exclude<OutputState, 'done'>, message: string): void;
30
+ (tag: 'done'): void;
31
+ (tag: Exclude<OutputTag, 'done'>, message: string): void;
32
32
  }
33
33
  export interface MaybeOutput {
34
34
  output?: Output | null | false;
@@ -36,8 +36,8 @@ export interface MaybeOutput {
36
36
  export interface WithOutput {
37
37
  output: Output;
38
38
  }
39
- export declare function defaultOutput(state: 'done'): void;
40
- export declare function defaultOutput(state: Exclude<OutputState, 'done'>, message: string): void;
39
+ export declare function defaultOutput(tag: 'done'): void;
40
+ export declare function defaultOutput(tag: Exclude<OutputTag, 'done'>, message: string): void;
41
41
  /**
42
42
  * TS can't tell when we do this inline
43
43
  * @internal
package/dist/io.js CHANGED
@@ -4,7 +4,7 @@ import * as fs from 'node:fs';
4
4
  import { homedir } from 'node:os';
5
5
  import { join } from 'node:path/posix';
6
6
  import { styleText } from 'node:util';
7
- import { debug } from './config.js';
7
+ import config from './config.js';
8
8
  /**
9
9
  * Find the Axium directory.
10
10
  * This directory includes things like config files, secrets, etc.
@@ -81,15 +81,12 @@ export function handleError(e) {
81
81
  else
82
82
  exit(e);
83
83
  }
84
- export function defaultOutput(state, message = '') {
85
- switch (state) {
86
- case 'start':
87
- process.stdout.write(message + '... ');
88
- break;
84
+ export function defaultOutput(tag, message = '') {
85
+ switch (tag) {
89
86
  case 'debug':
90
- debug && output.debug(message);
87
+ config.debug && output.debug(message);
91
88
  break;
92
- case 'log':
89
+ case 'info':
93
90
  console.log(message);
94
91
  break;
95
92
  case 'warn':
@@ -98,9 +95,14 @@ export function defaultOutput(state, message = '') {
98
95
  case 'error':
99
96
  console.error(styleText('red', message));
100
97
  break;
98
+ case 'start':
99
+ process.stdout.write(message + '... ');
100
+ break;
101
101
  case 'done':
102
102
  console.log('done.');
103
103
  break;
104
+ case 'plugin':
105
+ console.log(styleText('whiteBright', 'Running plugin: ' + message));
104
106
  }
105
107
  }
106
108
  /**
@@ -0,0 +1,51 @@
1
+ import * as z from 'zod';
2
+ export declare const Plugin: z.ZodObject<{
3
+ id: z.ZodString;
4
+ name: z.ZodString;
5
+ version: z.ZodString;
6
+ description: z.ZodOptional<z.ZodString>;
7
+ statusText: z.ZodOptional<z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnion<[z.ZodString, z.ZodPromise<z.ZodString>]>>>;
8
+ db: z.ZodOptional<z.ZodObject<{
9
+ init: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
10
+ remove: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
11
+ wipe: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodUnknown>;
12
+ }, "strip", z.ZodTypeAny, {
13
+ init: (...args: unknown[]) => unknown;
14
+ remove: (...args: unknown[]) => unknown;
15
+ wipe: (...args: unknown[]) => unknown;
16
+ }, {
17
+ init: (...args: unknown[]) => unknown;
18
+ remove: (...args: unknown[]) => unknown;
19
+ wipe: (...args: unknown[]) => unknown;
20
+ }>>;
21
+ }, "strip", z.ZodTypeAny, {
22
+ name: string;
23
+ id: string;
24
+ version: string;
25
+ db?: {
26
+ init: (...args: unknown[]) => unknown;
27
+ remove: (...args: unknown[]) => unknown;
28
+ wipe: (...args: unknown[]) => unknown;
29
+ } | undefined;
30
+ description?: string | undefined;
31
+ statusText?: ((...args: unknown[]) => string | Promise<string>) | undefined;
32
+ }, {
33
+ name: string;
34
+ id: string;
35
+ version: string;
36
+ db?: {
37
+ init: (...args: unknown[]) => unknown;
38
+ remove: (...args: unknown[]) => unknown;
39
+ wipe: (...args: unknown[]) => unknown;
40
+ } | undefined;
41
+ description?: string | undefined;
42
+ statusText?: ((...args: unknown[]) => string | Promise<string>) | undefined;
43
+ }>;
44
+ export interface Plugin extends z.infer<typeof Plugin> {
45
+ }
46
+ export declare const plugins: Set<Plugin>;
47
+ export declare function resolvePlugin(search: string): Plugin | undefined;
48
+ export declare function pluginText(plugin: Plugin): string;
49
+ export declare function loadPlugin(path: string): Promise<void>;
50
+ export declare function loadPlugins(dir: string): Promise<void>;
51
+ export declare function loadDefaultPlugins(): Promise<void>;
@@ -0,0 +1,68 @@
1
+ import * as fs from 'node:fs';
2
+ import { join, resolve } from 'node:path/posix';
3
+ import { styleText } from 'node:util';
4
+ import * as z from 'zod';
5
+ import { findDir, output } from './io.js';
6
+ import { fromZodError } from 'zod-validation-error';
7
+ export const Plugin = z.object({
8
+ id: z.string(),
9
+ name: z.string(),
10
+ version: z.string(),
11
+ description: z.string().optional(),
12
+ statusText: z
13
+ .function()
14
+ .args()
15
+ .returns(z.union([z.string(), z.promise(z.string())]))
16
+ .optional(),
17
+ db: z
18
+ .object({
19
+ init: z.function(),
20
+ remove: z.function(),
21
+ wipe: z.function(),
22
+ })
23
+ .optional(),
24
+ });
25
+ export const plugins = new Set();
26
+ export function resolvePlugin(search) {
27
+ for (const plugin of plugins) {
28
+ if (plugin.name.startsWith(search) || plugin.id.startsWith(search))
29
+ return plugin;
30
+ }
31
+ }
32
+ export function pluginText(plugin) {
33
+ return [
34
+ styleText('whiteBright', plugin.name),
35
+ plugin.id,
36
+ `Version: ${plugin.version}`,
37
+ `Description: ${plugin.description ?? styleText('dim', '(none)')}`,
38
+ `Status text integration: ${plugin.statusText ? styleText('whiteBright', 'yes') : styleText('yellow', 'no')}`,
39
+ `Database integration: ${plugin.db ? 'yes' : 'no'}`,
40
+ ].join('\n');
41
+ }
42
+ export async function loadPlugin(path) {
43
+ path = resolve(path);
44
+ const stats = fs.statSync(path);
45
+ if (stats.isDirectory() || !['.js', '.mjs'].some(ext => path.endsWith(ext)))
46
+ return;
47
+ try {
48
+ const plugin = await Plugin.parseAsync(await import(path)).catch(e => {
49
+ throw fromZodError(e);
50
+ });
51
+ plugins.add(plugin);
52
+ output.debug(`Loaded plugin: "${plugin.name}" (${plugin.id}) ${plugin.version}`);
53
+ }
54
+ catch (e) {
55
+ output.debug(`Failed to load plugin from ${path}: ${e}`);
56
+ }
57
+ }
58
+ export async function loadPlugins(dir) {
59
+ fs.mkdirSync(dir, { recursive: true });
60
+ const files = fs.readdirSync(dir);
61
+ for (const file of files) {
62
+ await loadPlugin(join(dir, file));
63
+ }
64
+ }
65
+ export async function loadDefaultPlugins() {
66
+ await loadPlugins(join(findDir(true), 'plugins'));
67
+ await loadPlugins(join(findDir(false), 'plugins'));
68
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axium/server",
3
- "version": "0.5.3",
3
+ "version": "0.6.0",
4
4
  "author": "James Prevett <axium@jamespre.dev> (https://jamespre.dev)",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -46,7 +46,7 @@
46
46
  "kysely": "^0.27.5",
47
47
  "logzen": "^0.6.2",
48
48
  "pg": "^8.14.1",
49
- "utilium": "^2.2.3",
49
+ "utilium": "^2.3.0",
50
50
  "zod-validation-error": "^3.4.0"
51
51
  },
52
52
  "devDependencies": {