@axium/server 0.2.2 → 0.3.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/cli.js +22 -32
- package/dist/config.d.ts +30 -30
- package/dist/config.js +2 -2
- package/dist/database.d.ts +2 -7
- package/dist/database.js +7 -36
- package/dist/io.d.ts +47 -2
- package/dist/io.js +118 -12
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { Option, program } from 'commander';
|
|
2
|
+
import { Argument, Option, program } from 'commander';
|
|
3
3
|
import { styleText } from 'node:util';
|
|
4
|
-
import { getByString, isJSON,
|
|
4
|
+
import { getByString, isJSON, setByString } from 'utilium';
|
|
5
5
|
import $pkg from '../package.json' with { type: 'json' };
|
|
6
6
|
import * as config from './config.js';
|
|
7
7
|
import * as db from './database.js';
|
|
8
|
-
import { exit, output } from './io.js';
|
|
8
|
+
import { _portActions, _portMethods, exit, handleError, output, restrictedPorts } from './io.js';
|
|
9
9
|
program
|
|
10
10
|
.version($pkg.version)
|
|
11
11
|
.name('axium')
|
|
12
12
|
.description('Axium server CLI')
|
|
13
13
|
.configureHelp({ showGlobalOptions: true })
|
|
14
|
-
.option('
|
|
14
|
+
.option('--debug', 'override debug mode')
|
|
15
|
+
.option('--no-debug', 'override debug mode')
|
|
15
16
|
.option('-c, --config <path>', 'path to the config file');
|
|
16
|
-
program.on('option:debug', () => config.set(
|
|
17
|
+
program.on('option:debug', () => config.set({ debug: true }));
|
|
17
18
|
program.on('option:config', () => config.load(program.opts().config));
|
|
18
19
|
program.hook('preAction', function (_, action) {
|
|
19
20
|
config.loadDefaults();
|
|
20
21
|
const opt = action.optsWithGlobals();
|
|
21
22
|
opt.force && output.warn('--force: Protections disabled.');
|
|
23
|
+
if (opt.debug === false)
|
|
24
|
+
config.set({ debug: false });
|
|
22
25
|
});
|
|
23
26
|
// Options shared by multiple (sub)commands
|
|
24
27
|
const opts = {
|
|
@@ -36,30 +39,13 @@ const axiumDB = program
|
|
|
36
39
|
.description('manage the database')
|
|
37
40
|
.option('-t, --timeout <ms>', 'how long to wait for commands to complete.', '1000')
|
|
38
41
|
.addOption(opts.host);
|
|
39
|
-
function db_output(state, message = '') {
|
|
40
|
-
switch (state) {
|
|
41
|
-
case 'start':
|
|
42
|
-
process.stdout.write(message + '... ');
|
|
43
|
-
break;
|
|
44
|
-
case 'log':
|
|
45
|
-
case 'warn':
|
|
46
|
-
process.stdout.write(styleText('yellow', message));
|
|
47
|
-
break;
|
|
48
|
-
case 'error':
|
|
49
|
-
process.stdout.write(styleText('red', message));
|
|
50
|
-
break;
|
|
51
|
-
case 'done':
|
|
52
|
-
console.log('done.');
|
|
53
|
-
break;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
42
|
axiumDB
|
|
57
43
|
.command('init')
|
|
58
44
|
.description('initialize the database')
|
|
59
45
|
.addOption(opts.force)
|
|
60
46
|
.option('-s, --skip', 'Skip existing database and/or user')
|
|
61
47
|
.action(async (opt) => {
|
|
62
|
-
await db.init(
|
|
48
|
+
await db.init(opt).catch((e) => {
|
|
63
49
|
if (typeof e == 'number')
|
|
64
50
|
process.exit(e);
|
|
65
51
|
else
|
|
@@ -95,7 +81,7 @@ axiumDB
|
|
|
95
81
|
output.warn(`Database has existing ${key}. Use --force if you really want to drop the database.`);
|
|
96
82
|
process.exit(2);
|
|
97
83
|
}
|
|
98
|
-
await db.uninstall(
|
|
84
|
+
await db.uninstall(opt).catch(exit);
|
|
99
85
|
await db.database.destroy();
|
|
100
86
|
});
|
|
101
87
|
axiumDB
|
|
@@ -111,12 +97,11 @@ axiumDB
|
|
|
111
97
|
output.warn(`Database has existing ${key}. Use --force if you really want to wipe the database.`);
|
|
112
98
|
process.exit(2);
|
|
113
99
|
}
|
|
114
|
-
await db.wipe(
|
|
100
|
+
await db.wipe(opt).catch(exit);
|
|
115
101
|
await db.database.destroy();
|
|
116
102
|
});
|
|
117
103
|
const axiumConfig = program
|
|
118
104
|
.command('config')
|
|
119
|
-
.alias('conf')
|
|
120
105
|
.description('manage the configuration')
|
|
121
106
|
.option('-j, --json', 'values are JSON encoded')
|
|
122
107
|
.option('-g, --global', 'apply to the global config');
|
|
@@ -176,17 +161,22 @@ program
|
|
|
176
161
|
await db.database.destroy();
|
|
177
162
|
console.log('Credentials authentication:', config.auth.credentials ? styleText('yellow', 'enabled') : 'disabled');
|
|
178
163
|
});
|
|
164
|
+
program
|
|
165
|
+
.command('ports')
|
|
166
|
+
.description('Enable or disable use of restricted ports (e.g. 443)')
|
|
167
|
+
.addArgument(new Argument('<action>', 'The action to take').choices(_portActions))
|
|
168
|
+
.addOption(new Option('-m, --method <method>', 'the method to use').choices(_portMethods).default('node-cap'))
|
|
169
|
+
.option('-N, --node <path>', 'the path to the node binary')
|
|
170
|
+
.action(async (action, opt) => {
|
|
171
|
+
await restrictedPorts({ ...opt, action }).catch(handleError);
|
|
172
|
+
});
|
|
179
173
|
program
|
|
180
174
|
.command('init')
|
|
181
175
|
.description('Install Axium server')
|
|
182
176
|
.addOption(opts.force)
|
|
183
177
|
.addOption(opts.host)
|
|
184
178
|
.action(async (opt) => {
|
|
185
|
-
await db.init({ ...opt, skip: opt.dbSkip
|
|
186
|
-
|
|
187
|
-
process.exit(e);
|
|
188
|
-
else
|
|
189
|
-
exit(e);
|
|
190
|
-
});
|
|
179
|
+
await db.init({ ...opt, skip: opt.dbSkip }).catch(handleError);
|
|
180
|
+
await restrictedPorts({ method: 'node-cap', action: 'enable' }).catch(handleError);
|
|
191
181
|
});
|
|
192
182
|
program.parse();
|
package/dist/config.d.ts
CHANGED
|
@@ -43,10 +43,10 @@ export declare const Log: z.ZodObject<{
|
|
|
43
43
|
level: z.ZodEnum<["error", "warn", "notice", "info", "debug"]>;
|
|
44
44
|
console: z.ZodBoolean;
|
|
45
45
|
}, "strip", z.ZodTypeAny, {
|
|
46
|
-
level: "
|
|
46
|
+
level: "warn" | "error" | "debug" | "notice" | "info";
|
|
47
47
|
console: boolean;
|
|
48
48
|
}, {
|
|
49
|
-
level: "
|
|
49
|
+
level: "warn" | "error" | "debug" | "notice" | "info";
|
|
50
50
|
console: boolean;
|
|
51
51
|
}>;
|
|
52
52
|
export type Log = z.infer<typeof Log>;
|
|
@@ -67,13 +67,13 @@ export declare const Config: z.ZodObject<{
|
|
|
67
67
|
secret: z.ZodOptional<z.ZodString>;
|
|
68
68
|
secure_cookies: z.ZodOptional<z.ZodBoolean>;
|
|
69
69
|
}, "strip", z.ZodTypeAny, {
|
|
70
|
-
credentials?: boolean | undefined;
|
|
71
70
|
debug?: boolean | undefined;
|
|
71
|
+
credentials?: boolean | undefined;
|
|
72
72
|
secret?: string | undefined;
|
|
73
73
|
secure_cookies?: boolean | undefined;
|
|
74
74
|
}, {
|
|
75
|
-
credentials?: boolean | undefined;
|
|
76
75
|
debug?: boolean | undefined;
|
|
76
|
+
credentials?: boolean | undefined;
|
|
77
77
|
secret?: string | undefined;
|
|
78
78
|
secure_cookies?: boolean | undefined;
|
|
79
79
|
}>;
|
|
@@ -101,10 +101,10 @@ export declare const Config: z.ZodObject<{
|
|
|
101
101
|
level: z.ZodOptional<z.ZodEnum<["error", "warn", "notice", "info", "debug"]>>;
|
|
102
102
|
console: z.ZodOptional<z.ZodBoolean>;
|
|
103
103
|
}, "strip", z.ZodTypeAny, {
|
|
104
|
-
level?: "
|
|
104
|
+
level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
|
|
105
105
|
console?: boolean | undefined;
|
|
106
106
|
}, {
|
|
107
|
-
level?: "
|
|
107
|
+
level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
|
|
108
108
|
console?: boolean | undefined;
|
|
109
109
|
}>;
|
|
110
110
|
web: z.ZodObject<{
|
|
@@ -115,10 +115,14 @@ export declare const Config: z.ZodObject<{
|
|
|
115
115
|
prefix?: string | undefined;
|
|
116
116
|
}>;
|
|
117
117
|
}, "strip", z.ZodTypeAny, {
|
|
118
|
+
log: {
|
|
119
|
+
level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
|
|
120
|
+
console?: boolean | undefined;
|
|
121
|
+
};
|
|
118
122
|
debug: boolean;
|
|
119
123
|
auth: {
|
|
120
|
-
credentials?: boolean | undefined;
|
|
121
124
|
debug?: boolean | undefined;
|
|
125
|
+
credentials?: boolean | undefined;
|
|
122
126
|
secret?: string | undefined;
|
|
123
127
|
secure_cookies?: boolean | undefined;
|
|
124
128
|
};
|
|
@@ -129,18 +133,18 @@ export declare const Config: z.ZodObject<{
|
|
|
129
133
|
user?: string | undefined;
|
|
130
134
|
database?: string | undefined;
|
|
131
135
|
};
|
|
132
|
-
log: {
|
|
133
|
-
level?: "debug" | "error" | "warn" | "notice" | "info" | undefined;
|
|
134
|
-
console?: boolean | undefined;
|
|
135
|
-
};
|
|
136
136
|
web: {
|
|
137
137
|
prefix?: string | undefined;
|
|
138
138
|
};
|
|
139
139
|
}, {
|
|
140
|
+
log: {
|
|
141
|
+
level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
|
|
142
|
+
console?: boolean | undefined;
|
|
143
|
+
};
|
|
140
144
|
debug: boolean;
|
|
141
145
|
auth: {
|
|
142
|
-
credentials?: boolean | undefined;
|
|
143
146
|
debug?: boolean | undefined;
|
|
147
|
+
credentials?: boolean | undefined;
|
|
144
148
|
secret?: string | undefined;
|
|
145
149
|
secure_cookies?: boolean | undefined;
|
|
146
150
|
};
|
|
@@ -151,10 +155,6 @@ export declare const Config: z.ZodObject<{
|
|
|
151
155
|
user?: string | undefined;
|
|
152
156
|
database?: string | undefined;
|
|
153
157
|
};
|
|
154
|
-
log: {
|
|
155
|
-
level?: "debug" | "error" | "warn" | "notice" | "info" | undefined;
|
|
156
|
-
console?: boolean | undefined;
|
|
157
|
-
};
|
|
158
158
|
web: {
|
|
159
159
|
prefix?: string | undefined;
|
|
160
160
|
};
|
|
@@ -166,13 +166,13 @@ export declare const File: z.ZodObject<z.objectUtil.extendShape<{
|
|
|
166
166
|
secret: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
167
167
|
secure_cookies: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
|
|
168
168
|
}, "strip", z.ZodTypeAny, {
|
|
169
|
-
credentials?: boolean | undefined;
|
|
170
169
|
debug?: boolean | undefined;
|
|
170
|
+
credentials?: boolean | undefined;
|
|
171
171
|
secret?: string | undefined;
|
|
172
172
|
secure_cookies?: boolean | undefined;
|
|
173
173
|
}, {
|
|
174
|
-
credentials?: boolean | undefined;
|
|
175
174
|
debug?: boolean | undefined;
|
|
175
|
+
credentials?: boolean | undefined;
|
|
176
176
|
secret?: string | undefined;
|
|
177
177
|
secure_cookies?: boolean | undefined;
|
|
178
178
|
}>>;
|
|
@@ -200,10 +200,10 @@ export declare const File: z.ZodObject<z.objectUtil.extendShape<{
|
|
|
200
200
|
level: z.ZodOptional<z.ZodOptional<z.ZodEnum<["error", "warn", "notice", "info", "debug"]>>>;
|
|
201
201
|
console: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
|
|
202
202
|
}, "strip", z.ZodTypeAny, {
|
|
203
|
-
level?: "
|
|
203
|
+
level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
|
|
204
204
|
console?: boolean | undefined;
|
|
205
205
|
}, {
|
|
206
|
-
level?: "
|
|
206
|
+
level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
|
|
207
207
|
console?: boolean | undefined;
|
|
208
208
|
}>>;
|
|
209
209
|
web: z.ZodOptional<z.ZodObject<{
|
|
@@ -216,10 +216,14 @@ export declare const File: z.ZodObject<z.objectUtil.extendShape<{
|
|
|
216
216
|
}, {
|
|
217
217
|
include: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
218
218
|
}>, "strip", z.ZodTypeAny, {
|
|
219
|
+
log?: {
|
|
220
|
+
level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
|
|
221
|
+
console?: boolean | undefined;
|
|
222
|
+
} | undefined;
|
|
219
223
|
debug?: boolean | undefined;
|
|
220
224
|
auth?: {
|
|
221
|
-
credentials?: boolean | undefined;
|
|
222
225
|
debug?: boolean | undefined;
|
|
226
|
+
credentials?: boolean | undefined;
|
|
223
227
|
secret?: string | undefined;
|
|
224
228
|
secure_cookies?: boolean | undefined;
|
|
225
229
|
} | undefined;
|
|
@@ -230,19 +234,19 @@ export declare const File: z.ZodObject<z.objectUtil.extendShape<{
|
|
|
230
234
|
user?: string | undefined;
|
|
231
235
|
database?: string | undefined;
|
|
232
236
|
} | undefined;
|
|
233
|
-
log?: {
|
|
234
|
-
level?: "debug" | "error" | "warn" | "notice" | "info" | undefined;
|
|
235
|
-
console?: boolean | undefined;
|
|
236
|
-
} | undefined;
|
|
237
237
|
web?: {
|
|
238
238
|
prefix?: string | undefined;
|
|
239
239
|
} | undefined;
|
|
240
240
|
include?: string[] | undefined;
|
|
241
241
|
}, {
|
|
242
|
+
log?: {
|
|
243
|
+
level?: "warn" | "error" | "debug" | "notice" | "info" | undefined;
|
|
244
|
+
console?: boolean | undefined;
|
|
245
|
+
} | undefined;
|
|
242
246
|
debug?: boolean | undefined;
|
|
243
247
|
auth?: {
|
|
244
|
-
credentials?: boolean | undefined;
|
|
245
248
|
debug?: boolean | undefined;
|
|
249
|
+
credentials?: boolean | undefined;
|
|
246
250
|
secret?: string | undefined;
|
|
247
251
|
secure_cookies?: boolean | undefined;
|
|
248
252
|
} | undefined;
|
|
@@ -253,10 +257,6 @@ export declare const File: z.ZodObject<z.objectUtil.extendShape<{
|
|
|
253
257
|
user?: string | undefined;
|
|
254
258
|
database?: string | undefined;
|
|
255
259
|
} | undefined;
|
|
256
|
-
log?: {
|
|
257
|
-
level?: "debug" | "error" | "warn" | "notice" | "info" | undefined;
|
|
258
|
-
console?: boolean | undefined;
|
|
259
|
-
} | undefined;
|
|
260
260
|
web?: {
|
|
261
261
|
prefix?: string | undefined;
|
|
262
262
|
} | undefined;
|
package/dist/config.js
CHANGED
|
@@ -90,7 +90,7 @@ export function load(path, options = {}) {
|
|
|
90
90
|
catch (e) {
|
|
91
91
|
if (!options.optional)
|
|
92
92
|
throw e;
|
|
93
|
-
|
|
93
|
+
debug && output.debug(`Skipping config at ${path} (${e.message})`);
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
96
|
const config = options.strict ? File.parse(json) : json;
|
|
@@ -116,7 +116,7 @@ export function saveTo(path, changed) {
|
|
|
116
116
|
set(changed);
|
|
117
117
|
const config = files.get(path) ?? {};
|
|
118
118
|
Object.assign(config, { ...changed, db: { ...config.db, ...changed.db } });
|
|
119
|
-
|
|
119
|
+
debug && output.debug(`Wrote config to ${path}`);
|
|
120
120
|
writeFileSync(path, JSON.stringify(config));
|
|
121
121
|
}
|
|
122
122
|
/**
|
package/dist/database.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ 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
4
|
import * as config from './config.js';
|
|
5
|
+
import { type MaybeOutput } from './io.js';
|
|
5
6
|
export interface Schema {
|
|
6
7
|
User: {
|
|
7
8
|
id: GeneratedAlways<string>;
|
|
@@ -58,15 +59,9 @@ export interface Stats {
|
|
|
58
59
|
}
|
|
59
60
|
export declare function status(): Promise<Stats>;
|
|
60
61
|
export declare function statusText(): Promise<string>;
|
|
61
|
-
export
|
|
62
|
-
export type OpOutput = {
|
|
63
|
-
(state: 'done'): void;
|
|
64
|
-
(state: Exclude<OpOutputState, 'done'>, message: string): void;
|
|
65
|
-
};
|
|
66
|
-
export interface OpOptions {
|
|
62
|
+
export interface OpOptions extends MaybeOutput {
|
|
67
63
|
timeout: number;
|
|
68
64
|
force: boolean;
|
|
69
|
-
output?: OpOutput;
|
|
70
65
|
}
|
|
71
66
|
export interface InitOptions extends OpOptions {
|
|
72
67
|
skip: boolean;
|
package/dist/database.js
CHANGED
|
@@ -51,11 +51,10 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
51
51
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
52
|
});
|
|
53
53
|
import { Kysely, PostgresDialect, sql } from 'kysely';
|
|
54
|
-
import { exec } from 'node:child_process';
|
|
55
54
|
import { randomBytes } from 'node:crypto';
|
|
56
55
|
import pg from 'pg';
|
|
57
56
|
import * as config from './config.js';
|
|
58
|
-
import {
|
|
57
|
+
import { _fixOutput, run } from './io.js';
|
|
59
58
|
export let database;
|
|
60
59
|
export function connect() {
|
|
61
60
|
if (database)
|
|
@@ -87,35 +86,6 @@ export async function statusText() {
|
|
|
87
86
|
throw typeof error == 'object' && 'message' in error ? error.message : error;
|
|
88
87
|
}
|
|
89
88
|
}
|
|
90
|
-
/**
|
|
91
|
-
* TS can't tell when we do this inline
|
|
92
|
-
*/
|
|
93
|
-
function _fixOutput(opt) {
|
|
94
|
-
opt.output ??= () => { };
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Convenience function for `sudo -u postgres psql -c "${command}"`, plus `report` coolness.
|
|
98
|
-
* @internal
|
|
99
|
-
*/
|
|
100
|
-
async function execSQL(opts, command, message) {
|
|
101
|
-
let stderr;
|
|
102
|
-
try {
|
|
103
|
-
opts.output('start', message);
|
|
104
|
-
const { promise, resolve, reject } = Promise.withResolvers();
|
|
105
|
-
exec(`sudo -u postgres psql -c "${command}"`, opts, (err, _, _stderr) => {
|
|
106
|
-
stderr = _stderr.startsWith('ERROR:') ? _stderr.slice(6).trim() : _stderr;
|
|
107
|
-
if (err)
|
|
108
|
-
reject('[command]');
|
|
109
|
-
else
|
|
110
|
-
resolve();
|
|
111
|
-
});
|
|
112
|
-
await promise;
|
|
113
|
-
opts.output('done');
|
|
114
|
-
}
|
|
115
|
-
catch (error) {
|
|
116
|
-
throw error == '[command]' ? stderr?.slice(0, 100) || 'failed.' : typeof error == 'object' && 'message' in error ? error.message : error;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
89
|
function shouldRecreate(opt) {
|
|
120
90
|
if (opt.skip) {
|
|
121
91
|
opt.output('warn', 'already exists. (skipped)\n');
|
|
@@ -134,9 +104,9 @@ export async function init(opt) {
|
|
|
134
104
|
_fixOutput(opt);
|
|
135
105
|
if (!config.db.password) {
|
|
136
106
|
config.save({ db: { password: randomBytes(32).toString('base64') } }, true);
|
|
137
|
-
|
|
107
|
+
opt.output('debug', 'Generated password and wrote to global config');
|
|
138
108
|
}
|
|
139
|
-
const _sql = (command, message) =>
|
|
109
|
+
const _sql = (command, message) => run(opt, message, `sudo -u postgres psql -c "${command}"`);
|
|
140
110
|
await _sql('CREATE DATABASE axium', 'Creating database').catch(async (error) => {
|
|
141
111
|
if (error != 'database "axium" already exists')
|
|
142
112
|
throw error;
|
|
@@ -258,9 +228,10 @@ export async function init(opt) {
|
|
|
258
228
|
*/
|
|
259
229
|
export async function uninstall(opt) {
|
|
260
230
|
_fixOutput(opt);
|
|
261
|
-
|
|
262
|
-
await
|
|
263
|
-
await
|
|
231
|
+
const _sql = (command, message) => run(opt, message, `sudo -u postgres psql -c "${command}"`);
|
|
232
|
+
await _sql('DROP DATABASE axium', 'Dropping database');
|
|
233
|
+
await _sql('REVOKE ALL PRIVILEGES ON SCHEMA public FROM axium', 'Revoking schema privileges');
|
|
234
|
+
await _sql('DROP USER axium', 'Dropping user');
|
|
264
235
|
}
|
|
265
236
|
/**
|
|
266
237
|
* Removes all data from tables.
|
package/dist/io.d.ts
CHANGED
|
@@ -15,7 +15,52 @@ export declare const output: {
|
|
|
15
15
|
log(message: string): void;
|
|
16
16
|
debug(message: string): void;
|
|
17
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Run a system command with the fancy "Example... done."
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
export declare function run(opts: WithOutput & {
|
|
23
|
+
timeout?: number;
|
|
24
|
+
}, message: string, command: string): Promise<string>;
|
|
18
25
|
/** Yet another convenience function */
|
|
19
26
|
export declare function exit(message: string | Error, code?: number): never;
|
|
20
|
-
|
|
21
|
-
export
|
|
27
|
+
export declare function handleError(e: number | string | Error): void;
|
|
28
|
+
export type OutputState = 'done' | 'log' | 'warn' | 'error' | 'start' | 'debug';
|
|
29
|
+
export interface Output {
|
|
30
|
+
(state: 'done'): void;
|
|
31
|
+
(state: Exclude<OutputState, 'done'>, message: string): void;
|
|
32
|
+
}
|
|
33
|
+
export interface MaybeOutput {
|
|
34
|
+
output?: Output | null | false;
|
|
35
|
+
}
|
|
36
|
+
export interface WithOutput {
|
|
37
|
+
output: Output;
|
|
38
|
+
}
|
|
39
|
+
export declare function defaultOutput(state: 'done'): void;
|
|
40
|
+
export declare function defaultOutput(state: Exclude<OutputState, 'done'>, message: string): void;
|
|
41
|
+
/**
|
|
42
|
+
* TS can't tell when we do this inline
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
export declare function _fixOutput<T extends MaybeOutput>(opt: T): asserts opt is T & WithOutput;
|
|
46
|
+
/** @internal */
|
|
47
|
+
export declare const _portMethods: readonly ["node-cap"];
|
|
48
|
+
/** @internal */
|
|
49
|
+
export declare const _portActions: readonly ["enable", "disable"];
|
|
50
|
+
/**
|
|
51
|
+
* Options for working with restricted ports.
|
|
52
|
+
*
|
|
53
|
+
* Method:
|
|
54
|
+
* - `node-cap`: Use the `cap_net_bind_service` capability on the node binary.
|
|
55
|
+
*/
|
|
56
|
+
export interface PortOptions extends MaybeOutput {
|
|
57
|
+
method: (typeof _portMethods)[number];
|
|
58
|
+
action: (typeof _portActions)[number];
|
|
59
|
+
node?: string;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* This changes if Axium can use restricted ports (like 80 and 443) without root privileges.
|
|
63
|
+
* Use of these ports is needed so the origin doesn't have a port.
|
|
64
|
+
* If the origin has a port, passkeys do not work correctly with some password managers.
|
|
65
|
+
*/
|
|
66
|
+
export declare function restrictedPorts(opt: PortOptions): Promise<void>;
|
package/dist/io.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Logger } from 'logzen';
|
|
2
|
-
import {
|
|
2
|
+
import { exec, execSync } from 'node:child_process';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
3
4
|
import { homedir } from 'node:os';
|
|
4
5
|
import { join } from 'node:path/posix';
|
|
5
6
|
import { styleText } from 'node:util';
|
|
7
|
+
import { debug } from './config.js';
|
|
6
8
|
/**
|
|
7
9
|
* Find the Axium directory.
|
|
8
10
|
* This directory includes things like config files, secrets, etc.
|
|
@@ -17,8 +19,8 @@ export function findDir(global) {
|
|
|
17
19
|
return '.axium';
|
|
18
20
|
}
|
|
19
21
|
if (process.getuid?.() === 0)
|
|
20
|
-
mkdirSync('/etc/axium', { recursive: true });
|
|
21
|
-
mkdirSync(findDir(false), { recursive: true });
|
|
22
|
+
fs.mkdirSync('/etc/axium', { recursive: true });
|
|
23
|
+
fs.mkdirSync(findDir(false), { recursive: true });
|
|
22
24
|
export const logger = new Logger({
|
|
23
25
|
hideWarningStack: true,
|
|
24
26
|
noGlobalConsole: true,
|
|
@@ -42,6 +44,30 @@ export const output = {
|
|
|
42
44
|
},
|
|
43
45
|
};
|
|
44
46
|
logger.attach(output);
|
|
47
|
+
/**
|
|
48
|
+
* Run a system command with the fancy "Example... done."
|
|
49
|
+
* @internal
|
|
50
|
+
*/
|
|
51
|
+
export async function run(opts, message, command) {
|
|
52
|
+
let stderr;
|
|
53
|
+
try {
|
|
54
|
+
opts.output('start', message);
|
|
55
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
56
|
+
exec(command, opts, (err, stdout, _stderr) => {
|
|
57
|
+
stderr = _stderr.startsWith('ERROR:') ? _stderr.slice(6).trim() : _stderr;
|
|
58
|
+
if (err)
|
|
59
|
+
reject('[command]');
|
|
60
|
+
else
|
|
61
|
+
resolve(stdout);
|
|
62
|
+
});
|
|
63
|
+
const value = await promise;
|
|
64
|
+
opts.output('done');
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
throw error == '[command]' ? stderr?.slice(0, 100) || 'failed.' : typeof error == 'object' && 'message' in error ? error.message : error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
45
71
|
/** Yet another convenience function */
|
|
46
72
|
export function exit(message, code = 1) {
|
|
47
73
|
if (message instanceof Error)
|
|
@@ -49,15 +75,95 @@ export function exit(message, code = 1) {
|
|
|
49
75
|
output.error(message);
|
|
50
76
|
process.exit(code);
|
|
51
77
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
78
|
+
export function handleError(e) {
|
|
79
|
+
if (typeof e == 'number')
|
|
80
|
+
process.exit(e);
|
|
81
|
+
else
|
|
82
|
+
exit(e);
|
|
83
|
+
}
|
|
84
|
+
export function defaultOutput(state, message = '') {
|
|
85
|
+
switch (state) {
|
|
86
|
+
case 'start':
|
|
87
|
+
process.stdout.write(message + '... ');
|
|
88
|
+
break;
|
|
89
|
+
case 'debug':
|
|
90
|
+
debug && output.debug(message);
|
|
91
|
+
break;
|
|
92
|
+
case 'log':
|
|
93
|
+
console.log(message);
|
|
94
|
+
break;
|
|
95
|
+
case 'warn':
|
|
96
|
+
process.stdout.write(styleText('yellow', message));
|
|
97
|
+
break;
|
|
98
|
+
case 'error':
|
|
99
|
+
process.stdout.write(styleText('red', message));
|
|
100
|
+
break;
|
|
101
|
+
case 'done':
|
|
102
|
+
console.log('done.');
|
|
103
|
+
break;
|
|
59
104
|
}
|
|
60
|
-
|
|
61
|
-
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* TS can't tell when we do this inline
|
|
108
|
+
* @internal
|
|
109
|
+
*/
|
|
110
|
+
export function _fixOutput(opt) {
|
|
111
|
+
if (opt.output === false)
|
|
112
|
+
opt.output = () => { };
|
|
113
|
+
else
|
|
114
|
+
opt.output ??= defaultOutput;
|
|
115
|
+
}
|
|
116
|
+
/** @internal */
|
|
117
|
+
export const _portMethods = ['node-cap'];
|
|
118
|
+
/** @internal */
|
|
119
|
+
export const _portActions = ['enable', 'disable'];
|
|
120
|
+
/**
|
|
121
|
+
* This changes if Axium can use restricted ports (like 80 and 443) without root privileges.
|
|
122
|
+
* Use of these ports is needed so the origin doesn't have a port.
|
|
123
|
+
* If the origin has a port, passkeys do not work correctly with some password managers.
|
|
124
|
+
*/
|
|
125
|
+
export async function restrictedPorts(opt) {
|
|
126
|
+
_fixOutput(opt);
|
|
127
|
+
opt.output('start', 'Checking for root privileges');
|
|
128
|
+
if (process.getuid?.() != 0)
|
|
129
|
+
throw 'root privileges are needed to change restricted ports.';
|
|
130
|
+
opt.output('done');
|
|
131
|
+
opt.output('start', 'Checking ports method');
|
|
132
|
+
if (!_portMethods.includes(opt.method))
|
|
133
|
+
throw 'invalid';
|
|
134
|
+
opt.output('done');
|
|
135
|
+
opt.output('start', 'Checking ports action');
|
|
136
|
+
if (!_portActions.includes(opt.action))
|
|
137
|
+
throw 'invalid';
|
|
138
|
+
opt.output('done');
|
|
139
|
+
switch (opt.method) {
|
|
140
|
+
case 'node-cap': {
|
|
141
|
+
const setcap = await run(opt, 'Finding setcap', 'command -v setcap')
|
|
142
|
+
.then(e => e.trim())
|
|
143
|
+
.catch(() => {
|
|
144
|
+
opt.output('warn', 'not in path.');
|
|
145
|
+
opt.output('start', 'Checking for /usr/sbin/setcap');
|
|
146
|
+
fs.accessSync('/usr/sbin/setcap', fs.constants.X_OK);
|
|
147
|
+
opt.output('done');
|
|
148
|
+
return '/usr/sbin/setcap';
|
|
149
|
+
});
|
|
150
|
+
opt.output('debug', 'Using setup at ' + setcap);
|
|
151
|
+
let { node } = opt;
|
|
152
|
+
node ||= await run(opt, 'Finding node', 'command -v node')
|
|
153
|
+
.then(e => e.trim())
|
|
154
|
+
.catch(() => {
|
|
155
|
+
opt.output('warn', 'not in path.');
|
|
156
|
+
opt.output('start', 'Checking for /usr/bin/node');
|
|
157
|
+
fs.accessSync('/usr/bin/node', fs.constants.X_OK);
|
|
158
|
+
opt.output('done');
|
|
159
|
+
return '/usr/bin/node';
|
|
160
|
+
});
|
|
161
|
+
opt.output('start', 'Resolving real path for node');
|
|
162
|
+
node = fs.realpathSync(node);
|
|
163
|
+
opt.output('done');
|
|
164
|
+
opt.output('debug', 'Using node at ' + node);
|
|
165
|
+
await run(opt, 'Setting ports capability', `${setcap} cap_net_bind_service=${opt.action == 'enable' ? '+' : '-'}ep ${node}`);
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
62
168
|
}
|
|
63
169
|
}
|