@axium/server 0.7.6 → 0.8.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/apps.d.ts +15 -0
- package/dist/apps.js +20 -0
- package/dist/auth.d.ts +63 -30
- package/dist/auth.js +110 -129
- package/dist/cli.js +33 -6
- package/dist/config.d.ts +241 -61
- package/dist/config.js +26 -2
- package/dist/database.d.ts +28 -37
- package/dist/database.js +124 -50
- package/dist/io.js +6 -2
- package/dist/plugins.d.ts +7 -24
- package/dist/plugins.js +9 -14
- package/dist/routes.d.ts +55 -0
- package/dist/routes.js +54 -0
- package/package.json +7 -15
- package/web/api/index.ts +7 -0
- package/web/api/metadata.ts +35 -0
- package/web/api/passkeys.ts +56 -0
- package/web/api/readme.md +1 -0
- package/web/api/register.ts +83 -0
- package/web/api/schemas.ts +22 -0
- package/web/api/session.ts +33 -0
- package/web/api/users.ts +340 -0
- package/web/api/utils.ts +66 -0
- package/web/auth.ts +1 -5
- package/web/hooks.server.ts +6 -1
- package/web/index.server.ts +0 -1
- package/web/lib/Dialog.svelte +3 -6
- package/web/lib/FormDialog.svelte +53 -14
- package/web/lib/Toast.svelte +8 -1
- package/web/lib/UserCard.svelte +1 -1
- package/web/lib/auth.ts +12 -0
- package/web/lib/icons/Icon.svelte +5 -7
- package/web/lib/index.ts +0 -2
- package/web/lib/styles.css +12 -1
- package/web/routes/+layout.svelte +1 -1
- package/web/routes/[...path]/+page.server.ts +13 -0
- package/web/routes/[appId]/[...page]/+page.server.ts +14 -0
- package/web/routes/_axium/default/+page.svelte +11 -0
- package/web/routes/account/+page.svelte +224 -0
- package/web/routes/api/[...path]/+server.ts +49 -0
- package/web/routes/login/+page.svelte +25 -0
- package/web/routes/logout/+page.svelte +13 -0
- package/web/routes/register/+page.svelte +21 -0
- package/web/tsconfig.json +2 -1
- package/web/utils.ts +9 -15
- package/web/actions.ts +0 -58
- package/web/lib/Account.svelte +0 -76
- package/web/lib/SignUp.svelte +0 -20
- package/web/lib/account.css +0 -36
- package/web/routes/+page.server.ts +0 -16
- package/web/routes/+page.svelte +0 -10
- package/web/routes/name/+page.server.ts +0 -5
- package/web/routes/name/+page.svelte +0 -20
- package/web/routes/signup/+page.server.ts +0 -10
- package/web/routes/signup/+page.svelte +0 -15
package/dist/database.d.ts
CHANGED
|
@@ -1,52 +1,41 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Preferences } from '@axium/core';
|
|
2
2
|
import { Kysely, type GeneratedAlways } from 'kysely';
|
|
3
|
-
import type {
|
|
4
|
-
import {
|
|
3
|
+
import type { VerificationRole } from './auth.js';
|
|
4
|
+
import type { MaybeOutput, WithOutput } from './io.js';
|
|
5
|
+
import type { AuthenticatorTransportFuture, CredentialDeviceType } from '@simplewebauthn/server';
|
|
5
6
|
export interface Schema {
|
|
6
|
-
|
|
7
|
-
id: GeneratedAlways<string
|
|
8
|
-
name: string | null;
|
|
7
|
+
users: {
|
|
8
|
+
id: GeneratedAlways<string> & string;
|
|
9
9
|
email: string;
|
|
10
|
-
|
|
11
|
-
image
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
preferences: Preferences;
|
|
15
|
-
};
|
|
16
|
-
Account: {
|
|
17
|
-
id: GeneratedAlways<string>;
|
|
18
|
-
userId: string;
|
|
19
|
-
type: db;
|
|
20
|
-
provider: string;
|
|
21
|
-
providerAccountId: string;
|
|
22
|
-
refresh_token?: string;
|
|
23
|
-
access_token?: string;
|
|
24
|
-
expires_at?: number;
|
|
25
|
-
token_type?: Lowercase<string>;
|
|
26
|
-
scope?: string;
|
|
27
|
-
id_token?: string;
|
|
28
|
-
session_state: string | null;
|
|
10
|
+
name: string;
|
|
11
|
+
image?: string | null;
|
|
12
|
+
emailVerified?: Date | null;
|
|
13
|
+
preferences?: Preferences;
|
|
29
14
|
};
|
|
30
|
-
|
|
15
|
+
sessions: {
|
|
31
16
|
id: GeneratedAlways<string>;
|
|
17
|
+
created: GeneratedAlways<Date>;
|
|
32
18
|
userId: string;
|
|
33
|
-
|
|
19
|
+
token: string;
|
|
34
20
|
expires: Date;
|
|
21
|
+
elevated: boolean;
|
|
35
22
|
};
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
verifications: {
|
|
24
|
+
userId: string;
|
|
38
25
|
token: string;
|
|
39
26
|
expires: Date;
|
|
27
|
+
role: VerificationRole;
|
|
40
28
|
};
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
passkeys: {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string | null;
|
|
32
|
+
createdAt: GeneratedAlways<Date>;
|
|
43
33
|
userId: string;
|
|
44
|
-
|
|
45
|
-
credentialPublicKey: string;
|
|
34
|
+
publicKey: Uint8Array;
|
|
46
35
|
counter: number;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
transports:
|
|
36
|
+
deviceType: CredentialDeviceType;
|
|
37
|
+
backedUp: boolean;
|
|
38
|
+
transports: AuthenticatorTransportFuture[];
|
|
50
39
|
};
|
|
51
40
|
}
|
|
52
41
|
export interface Database extends Kysely<Schema>, AsyncDisposable {
|
|
@@ -55,7 +44,7 @@ export declare let database: Database;
|
|
|
55
44
|
export declare function connect(): Database;
|
|
56
45
|
export interface Stats {
|
|
57
46
|
users: number;
|
|
58
|
-
|
|
47
|
+
passkeys: number;
|
|
59
48
|
sessions: number;
|
|
60
49
|
}
|
|
61
50
|
export declare function count(table: keyof Schema): Promise<number>;
|
|
@@ -69,11 +58,13 @@ export interface InitOptions extends OpOptions {
|
|
|
69
58
|
skip: boolean;
|
|
70
59
|
}
|
|
71
60
|
export declare function shouldRecreate(opt: InitOptions & WithOutput): boolean;
|
|
61
|
+
export declare function getHBA(opt: OpOptions & WithOutput): Promise<[content: string, writeBack: (newContent: string) => void]>;
|
|
72
62
|
export interface PluginShortcuts {
|
|
73
63
|
done: () => void;
|
|
74
64
|
warnExists: (error: string | Error) => void;
|
|
75
65
|
}
|
|
76
66
|
export declare function init(opt: InitOptions): Promise<void>;
|
|
67
|
+
export declare function check(opt: OpOptions): Promise<void>;
|
|
77
68
|
/**
|
|
78
69
|
* Completely remove Axium from the database.
|
|
79
70
|
*/
|
package/dist/database.js
CHANGED
|
@@ -52,6 +52,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
|
|
|
52
52
|
});
|
|
53
53
|
import { Kysely, PostgresDialect, sql } from 'kysely';
|
|
54
54
|
import { randomBytes } from 'node:crypto';
|
|
55
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
55
56
|
import pg from 'pg';
|
|
56
57
|
import config from './config.js';
|
|
57
58
|
import { _fixOutput, run, someWarnings } from './io.js';
|
|
@@ -76,15 +77,15 @@ export async function count(table) {
|
|
|
76
77
|
}
|
|
77
78
|
export async function status() {
|
|
78
79
|
return {
|
|
79
|
-
users: await count('
|
|
80
|
-
|
|
81
|
-
sessions: await count('
|
|
80
|
+
users: await count('users'),
|
|
81
|
+
passkeys: await count('passkeys'),
|
|
82
|
+
sessions: await count('sessions'),
|
|
82
83
|
};
|
|
83
84
|
}
|
|
84
85
|
export async function statusText() {
|
|
85
86
|
try {
|
|
86
87
|
const stats = await status();
|
|
87
|
-
return `${stats.users} users, ${stats.
|
|
88
|
+
return `${stats.users} users, ${stats.passkeys} passkeys, ${stats.sessions} sessions`;
|
|
88
89
|
}
|
|
89
90
|
catch (error) {
|
|
90
91
|
throw typeof error == 'object' && 'message' in error ? error.message : error;
|
|
@@ -102,6 +103,30 @@ export function shouldRecreate(opt) {
|
|
|
102
103
|
opt.output('warn', 'already exists. Use --skip to skip or --force to re-create.');
|
|
103
104
|
throw 2;
|
|
104
105
|
}
|
|
106
|
+
export async function getHBA(opt) {
|
|
107
|
+
const hbaShowResult = await run(opt, 'Finding pg_hba.conf', `sudo -u postgres psql -c "SHOW hba_file"`);
|
|
108
|
+
opt.output('start', 'Resolving pg_hba.conf path');
|
|
109
|
+
const hbaPath = hbaShowResult.match(/^\s*(.+\.conf)\s*$/m)?.[1]?.trim();
|
|
110
|
+
if (!hbaPath) {
|
|
111
|
+
throw 'failed. You will need to add password-based auth for the axium user manually.';
|
|
112
|
+
}
|
|
113
|
+
opt.output('done');
|
|
114
|
+
opt.output('debug', `Found pg_hba.conf at ${hbaPath}`);
|
|
115
|
+
opt.output('start', 'Reading HBA configuration');
|
|
116
|
+
const content = readFileSync(hbaPath, 'utf-8');
|
|
117
|
+
opt.output('done');
|
|
118
|
+
const writeBack = (newContent) => {
|
|
119
|
+
opt.output('start', 'Writing HBA configuration');
|
|
120
|
+
writeFileSync(hbaPath, newContent);
|
|
121
|
+
opt.output('done');
|
|
122
|
+
};
|
|
123
|
+
return [content, writeBack];
|
|
124
|
+
}
|
|
125
|
+
const pgHba = `
|
|
126
|
+
local axium axium md5
|
|
127
|
+
host axium axium 127.0.0.1/32 md5
|
|
128
|
+
host axium axium ::1/128 md5
|
|
129
|
+
`;
|
|
105
130
|
export async function init(opt) {
|
|
106
131
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
107
132
|
try {
|
|
@@ -110,8 +135,10 @@ export async function init(opt) {
|
|
|
110
135
|
config.save({ db: { password: randomBytes(32).toString('base64') } }, true);
|
|
111
136
|
opt.output('debug', 'Generated password and wrote to global config');
|
|
112
137
|
}
|
|
138
|
+
await run(opt, 'Checking for sudo', 'which sudo');
|
|
139
|
+
await run(opt, 'Checking for psql', 'which psql');
|
|
113
140
|
const _sql = (command, message) => run(opt, message, `sudo -u postgres psql -c "${command}"`);
|
|
114
|
-
const warnExists = someWarnings(opt.output, [/(schema|relation) "\w+" already exists/, 'already exists.']);
|
|
141
|
+
const warnExists = someWarnings(opt.output, [/(schema|relation) "[\w.]+" already exists/, 'already exists.']);
|
|
115
142
|
const done = () => opt.output('done');
|
|
116
143
|
await _sql('CREATE DATABASE axium', 'Creating database').catch(async (error) => {
|
|
117
144
|
if (error != 'database "axium" already exists')
|
|
@@ -134,79 +161,73 @@ export async function init(opt) {
|
|
|
134
161
|
await _sql('GRANT ALL PRIVILEGES ON DATABASE axium TO axium', 'Granting database privileges');
|
|
135
162
|
await _sql('GRANT ALL PRIVILEGES ON SCHEMA public TO axium', 'Granting schema privileges');
|
|
136
163
|
await _sql('ALTER DATABASE axium OWNER TO axium', 'Setting database owner');
|
|
164
|
+
await getHBA(opt)
|
|
165
|
+
.then(([content, writeBack]) => {
|
|
166
|
+
opt.output('start', 'Checking for Axium HBA configuration');
|
|
167
|
+
if (content.includes(pgHba))
|
|
168
|
+
throw 'already exists.';
|
|
169
|
+
done();
|
|
170
|
+
opt.output('start', 'Adding Axium HBA configuration');
|
|
171
|
+
const newContent = content.replace(/^local\s+all\s+all.*$/m, `$&\n${pgHba}`);
|
|
172
|
+
done();
|
|
173
|
+
writeBack(newContent);
|
|
174
|
+
})
|
|
175
|
+
.catch(e => opt.output('warn', e));
|
|
137
176
|
await _sql('SELECT pg_reload_conf()', 'Reloading configuration');
|
|
138
177
|
const db = __addDisposableResource(env_1, connect(), true);
|
|
139
|
-
opt.output('start', 'Creating table
|
|
178
|
+
opt.output('start', 'Creating table users');
|
|
140
179
|
await db.schema
|
|
141
|
-
.createTable('
|
|
180
|
+
.createTable('users')
|
|
142
181
|
.addColumn('id', 'uuid', col => col.primaryKey().defaultTo(sql `gen_random_uuid()`))
|
|
143
182
|
.addColumn('name', 'text')
|
|
144
183
|
.addColumn('email', 'text', col => col.unique().notNull())
|
|
145
184
|
.addColumn('emailVerified', 'timestamptz')
|
|
146
185
|
.addColumn('image', 'text')
|
|
147
|
-
.addColumn('password', 'text')
|
|
148
|
-
.addColumn('salt', 'text')
|
|
149
186
|
.addColumn('preferences', 'jsonb', col => col.notNull().defaultTo(sql `'{}'::jsonb`))
|
|
150
187
|
.execute()
|
|
151
188
|
.then(done)
|
|
152
189
|
.catch(warnExists);
|
|
153
|
-
opt.output('start', 'Creating table
|
|
154
|
-
await db.schema
|
|
155
|
-
.createTable('Account')
|
|
156
|
-
.addColumn('id', 'uuid', col => col.primaryKey().defaultTo(sql `gen_random_uuid()`))
|
|
157
|
-
.addColumn('userId', 'uuid', col => col.references('User.id').onDelete('cascade').notNull())
|
|
158
|
-
.addColumn('type', 'text', col => col.notNull())
|
|
159
|
-
.addColumn('provider', 'text', col => col.notNull())
|
|
160
|
-
.addColumn('providerAccountId', 'text', col => col.notNull())
|
|
161
|
-
.addColumn('refresh_token', 'text')
|
|
162
|
-
.addColumn('access_token', 'text')
|
|
163
|
-
.addColumn('expires_at', 'bigint')
|
|
164
|
-
.addColumn('token_type', 'text')
|
|
165
|
-
.addColumn('scope', 'text')
|
|
166
|
-
.addColumn('id_token', 'text')
|
|
167
|
-
.addColumn('session_state', 'text')
|
|
168
|
-
.execute()
|
|
169
|
-
.then(done)
|
|
170
|
-
.catch(warnExists);
|
|
171
|
-
opt.output('start', 'Creating index for Account.userId');
|
|
172
|
-
await db.schema.createIndex('Account_userId_index').on('Account').column('userId').execute().then(done).catch(warnExists);
|
|
173
|
-
opt.output('start', 'Creating table Session');
|
|
190
|
+
opt.output('start', 'Creating table sessions');
|
|
174
191
|
await db.schema
|
|
175
|
-
.createTable('
|
|
192
|
+
.createTable('sessions')
|
|
176
193
|
.addColumn('id', 'uuid', col => col.primaryKey().defaultTo(sql `gen_random_uuid()`))
|
|
177
|
-
.addColumn('userId', 'uuid', col => col.references('
|
|
178
|
-
.addColumn('
|
|
194
|
+
.addColumn('userId', 'uuid', col => col.references('users.id').onDelete('cascade').notNull())
|
|
195
|
+
.addColumn('token', 'text', col => col.notNull().unique())
|
|
196
|
+
.addColumn('created', 'timestamptz', col => col.notNull())
|
|
179
197
|
.addColumn('expires', 'timestamptz', col => col.notNull())
|
|
198
|
+
.addColumn('elevated', 'boolean', col => col.notNull())
|
|
180
199
|
.execute()
|
|
181
200
|
.then(done)
|
|
182
201
|
.catch(warnExists);
|
|
183
|
-
opt.output('start', 'Creating index for
|
|
184
|
-
await db.schema.createIndex('
|
|
185
|
-
opt.output('start', 'Creating table
|
|
202
|
+
opt.output('start', 'Creating index for sessions.userId');
|
|
203
|
+
await db.schema.createIndex('sessions_userId_index').on('sessions').column('userId').execute().then(done).catch(warnExists);
|
|
204
|
+
opt.output('start', 'Creating table verifications');
|
|
186
205
|
await db.schema
|
|
187
|
-
.createTable('
|
|
188
|
-
.addColumn('
|
|
206
|
+
.createTable('verifications')
|
|
207
|
+
.addColumn('userId', 'uuid', col => col.references('users.id').onDelete('cascade').notNull())
|
|
189
208
|
.addColumn('token', 'text', col => col.notNull().unique())
|
|
190
209
|
.addColumn('expires', 'timestamptz', col => col.notNull())
|
|
210
|
+
.addColumn('role', 'text', col => col.notNull())
|
|
191
211
|
.execute()
|
|
192
212
|
.then(done)
|
|
193
213
|
.catch(warnExists);
|
|
194
|
-
opt.output('start', 'Creating table
|
|
214
|
+
opt.output('start', 'Creating table passkeys');
|
|
195
215
|
await db.schema
|
|
196
|
-
.createTable('
|
|
197
|
-
.addColumn('
|
|
198
|
-
.addColumn('
|
|
199
|
-
.addColumn('
|
|
200
|
-
.addColumn('
|
|
216
|
+
.createTable('passkeys')
|
|
217
|
+
.addColumn('id', 'text', col => col.primaryKey().notNull())
|
|
218
|
+
.addColumn('name', 'text')
|
|
219
|
+
.addColumn('createdAt', 'timestamptz', col => col.notNull().defaultTo(sql `now()`))
|
|
220
|
+
.addColumn('userId', 'uuid', col => col.notNull().references('users.id').onDelete('cascade').onUpdate('cascade'))
|
|
221
|
+
.addColumn('publicKey', 'bytea', col => col.notNull())
|
|
201
222
|
.addColumn('counter', 'integer', col => col.notNull())
|
|
202
|
-
.addColumn('
|
|
203
|
-
.addColumn('
|
|
204
|
-
.addColumn('transports',
|
|
223
|
+
.addColumn('deviceType', 'text', col => col.notNull())
|
|
224
|
+
.addColumn('backedUp', 'boolean', col => col.notNull())
|
|
225
|
+
.addColumn('transports', sql `text[]`)
|
|
205
226
|
.execute()
|
|
206
227
|
.then(done)
|
|
207
228
|
.catch(warnExists);
|
|
208
|
-
opt.output('start', 'Creating index for
|
|
209
|
-
await db.schema.createIndex('
|
|
229
|
+
opt.output('start', 'Creating index for passkeys.id');
|
|
230
|
+
await db.schema.createIndex('passkeys_id_key').on('passkeys').column('id').execute().then(done).catch(warnExists);
|
|
210
231
|
for (const plugin of plugins) {
|
|
211
232
|
if (!plugin.db_init)
|
|
212
233
|
continue;
|
|
@@ -224,6 +245,47 @@ export async function init(opt) {
|
|
|
224
245
|
await result_1;
|
|
225
246
|
}
|
|
226
247
|
}
|
|
248
|
+
export async function check(opt) {
|
|
249
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
250
|
+
try {
|
|
251
|
+
_fixOutput(opt);
|
|
252
|
+
await run(opt, 'Checking for sudo', 'which sudo');
|
|
253
|
+
await run(opt, 'Checking for psql', 'which psql');
|
|
254
|
+
const _sql = (command, message) => run(opt, message, `sudo -u postgres psql -c "${command}"`);
|
|
255
|
+
const done = () => opt.output('done');
|
|
256
|
+
const throwUnlessRows = (text) => {
|
|
257
|
+
if (text.includes('(0 rows)'))
|
|
258
|
+
throw 'missing.';
|
|
259
|
+
return text;
|
|
260
|
+
};
|
|
261
|
+
await _sql(`SELECT 1 FROM pg_database WHERE datname = 'axium'`, 'Checking for database').then(throwUnlessRows);
|
|
262
|
+
await _sql(`SELECT 1 FROM pg_roles WHERE rolname = 'axium'`, 'Checking for user').then(throwUnlessRows);
|
|
263
|
+
opt.output('start', 'Connecting to database');
|
|
264
|
+
const db = __addDisposableResource(env_2, connect(), true);
|
|
265
|
+
done();
|
|
266
|
+
opt.output('start', `Checking users table`);
|
|
267
|
+
await db.selectFrom('users').select(['id', 'email', 'emailVerified', 'image', 'name', 'preferences']).execute().then(done);
|
|
268
|
+
opt.output('start', `Checking sessions table`);
|
|
269
|
+
await db.selectFrom('sessions').select(['id', 'userId', 'token', 'created', 'expires', 'elevated']).execute().then(done);
|
|
270
|
+
opt.output('start', `Checking verifications table`);
|
|
271
|
+
await db.selectFrom('verifications').select(['userId', 'token', 'expires', 'role']).execute().then(done);
|
|
272
|
+
opt.output('start', `Checking passkeys table`);
|
|
273
|
+
await db
|
|
274
|
+
.selectFrom('passkeys')
|
|
275
|
+
.select(['id', 'name', 'createdAt', 'userId', 'publicKey', 'counter', 'deviceType', 'backedUp', 'transports'])
|
|
276
|
+
.execute()
|
|
277
|
+
.then(done);
|
|
278
|
+
}
|
|
279
|
+
catch (e_2) {
|
|
280
|
+
env_2.error = e_2;
|
|
281
|
+
env_2.hasError = true;
|
|
282
|
+
}
|
|
283
|
+
finally {
|
|
284
|
+
const result_2 = __disposeResources(env_2);
|
|
285
|
+
if (result_2)
|
|
286
|
+
await result_2;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
227
289
|
/**
|
|
228
290
|
* Completely remove Axium from the database.
|
|
229
291
|
*/
|
|
@@ -241,6 +303,18 @@ export async function uninstall(opt) {
|
|
|
241
303
|
await _sql('DROP DATABASE axium', 'Dropping database');
|
|
242
304
|
await _sql('REVOKE ALL PRIVILEGES ON SCHEMA public FROM axium', 'Revoking schema privileges');
|
|
243
305
|
await _sql('DROP USER axium', 'Dropping user');
|
|
306
|
+
await getHBA(opt)
|
|
307
|
+
.then(([content, writeBack]) => {
|
|
308
|
+
opt.output('start', 'Checking for Axium HBA configuration');
|
|
309
|
+
if (!content.includes(pgHba))
|
|
310
|
+
throw 'missing.';
|
|
311
|
+
opt.output('done');
|
|
312
|
+
opt.output('start', 'Removing Axium HBA configuration');
|
|
313
|
+
const newContent = content.replace(pgHba, '');
|
|
314
|
+
opt.output('done');
|
|
315
|
+
writeBack(newContent);
|
|
316
|
+
})
|
|
317
|
+
.catch(e => opt.output('warn', e));
|
|
244
318
|
}
|
|
245
319
|
/**
|
|
246
320
|
* Removes all data from tables.
|
|
@@ -254,7 +328,7 @@ export async function wipe(opt) {
|
|
|
254
328
|
opt.output('plugin', plugin.name);
|
|
255
329
|
await plugin.db_wipe(opt, db);
|
|
256
330
|
}
|
|
257
|
-
for (const table of ['
|
|
331
|
+
for (const table of ['users', 'passkeys', 'sessions', 'verifications']) {
|
|
258
332
|
opt.output('start', `Removing data from ${table}`);
|
|
259
333
|
await db.deleteFrom(table).execute();
|
|
260
334
|
opt.output('done');
|
package/dist/io.js
CHANGED
|
@@ -65,7 +65,11 @@ export async function run(opts, message, command) {
|
|
|
65
65
|
return value;
|
|
66
66
|
}
|
|
67
67
|
catch (error) {
|
|
68
|
-
throw error == '[command]'
|
|
68
|
+
throw error == '[command]'
|
|
69
|
+
? stderr?.slice(0, 100) || 'failed.'
|
|
70
|
+
: typeof error == 'object' && 'message' in error
|
|
71
|
+
? error.message
|
|
72
|
+
: error;
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
75
|
/** Yet another convenience function */
|
|
@@ -149,7 +153,7 @@ export async function restrictedPorts(opt) {
|
|
|
149
153
|
opt.output('done');
|
|
150
154
|
return '/usr/sbin/setcap';
|
|
151
155
|
});
|
|
152
|
-
opt.output('debug', 'Using
|
|
156
|
+
opt.output('debug', 'Using setcap at ' + setcap);
|
|
153
157
|
let { node } = opt;
|
|
154
158
|
node ||= await run(opt, 'Finding node', 'command -v node')
|
|
155
159
|
.then(e => e.trim())
|
package/dist/plugins.d.ts
CHANGED
|
@@ -1,32 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import z from 'zod/v4';
|
|
2
|
+
export declare const fn: z.ZodCustom<(...args: unknown[]) => any, (...args: unknown[]) => any>;
|
|
2
3
|
export declare const Plugin: z.ZodObject<{
|
|
3
4
|
id: z.ZodString;
|
|
4
5
|
name: z.ZodString;
|
|
5
6
|
version: z.ZodString;
|
|
6
7
|
description: z.ZodOptional<z.ZodString>;
|
|
7
|
-
statusText: z.
|
|
8
|
-
db_init: z.ZodOptional<z.
|
|
9
|
-
db_remove: z.ZodOptional<z.
|
|
10
|
-
db_wipe: z.ZodOptional<z.
|
|
11
|
-
},
|
|
12
|
-
name: string;
|
|
13
|
-
id: string;
|
|
14
|
-
version: string;
|
|
15
|
-
description?: string | undefined;
|
|
16
|
-
statusText?: ((...args: unknown[]) => string | Promise<string>) | undefined;
|
|
17
|
-
db_init?: ((...args: unknown[]) => unknown) | undefined;
|
|
18
|
-
db_remove?: ((...args: unknown[]) => unknown) | undefined;
|
|
19
|
-
db_wipe?: ((...args: unknown[]) => unknown) | undefined;
|
|
20
|
-
}, {
|
|
21
|
-
name: string;
|
|
22
|
-
id: string;
|
|
23
|
-
version: string;
|
|
24
|
-
description?: string | undefined;
|
|
25
|
-
statusText?: ((...args: unknown[]) => string | Promise<string>) | undefined;
|
|
26
|
-
db_init?: ((...args: unknown[]) => unknown) | undefined;
|
|
27
|
-
db_remove?: ((...args: unknown[]) => unknown) | undefined;
|
|
28
|
-
db_wipe?: ((...args: unknown[]) => unknown) | undefined;
|
|
29
|
-
}>;
|
|
8
|
+
statusText: z.ZodCustom<z.core.$InferInnerFunctionTypeAsync<z.core.$ZodTuple<[], null>, z.ZodString>, z.core.$InferInnerFunctionTypeAsync<z.core.$ZodTuple<[], null>, z.ZodString>>;
|
|
9
|
+
db_init: z.ZodOptional<z.ZodCustom<(...args: unknown[]) => any, (...args: unknown[]) => any>>;
|
|
10
|
+
db_remove: z.ZodOptional<z.ZodCustom<(...args: unknown[]) => any, (...args: unknown[]) => any>>;
|
|
11
|
+
db_wipe: z.ZodOptional<z.ZodCustom<(...args: unknown[]) => any, (...args: unknown[]) => any>>;
|
|
12
|
+
}, z.core.$strip>;
|
|
30
13
|
export interface Plugin extends z.infer<typeof Plugin> {
|
|
31
14
|
}
|
|
32
15
|
export declare const plugins: Set<Plugin>;
|
package/dist/plugins.js
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import { join, resolve } from 'node:path/posix';
|
|
3
3
|
import { styleText } from 'node:util';
|
|
4
|
-
import
|
|
5
|
-
import { fromZodError } from 'zod-validation-error';
|
|
4
|
+
import z from 'zod/v4';
|
|
6
5
|
import { findDir, output } from './io.js';
|
|
7
|
-
import {
|
|
6
|
+
import { zAsyncFunction } from '@axium/core/schemas';
|
|
7
|
+
export const fn = z.custom(data => typeof data === 'function');
|
|
8
8
|
export const Plugin = z.object({
|
|
9
9
|
id: z.string(),
|
|
10
10
|
name: z.string(),
|
|
11
11
|
version: z.string(),
|
|
12
12
|
description: z.string().optional(),
|
|
13
|
-
statusText: z
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
.optional(),
|
|
18
|
-
db_init: z.function().optional(),
|
|
19
|
-
db_remove: z.function().optional(),
|
|
20
|
-
db_wipe: z.function().optional(),
|
|
13
|
+
statusText: zAsyncFunction(z.function({ input: [], output: z.string() })),
|
|
14
|
+
db_init: fn.optional(),
|
|
15
|
+
db_remove: fn.optional(),
|
|
16
|
+
db_wipe: fn.optional(),
|
|
21
17
|
});
|
|
22
18
|
export const plugins = new Set();
|
|
23
19
|
export function resolvePlugin(search) {
|
|
@@ -32,7 +28,6 @@ export function pluginText(plugin) {
|
|
|
32
28
|
plugin.id,
|
|
33
29
|
`Version: ${plugin.version}`,
|
|
34
30
|
`Description: ${plugin.description ?? styleText('dim', '(none)')}`,
|
|
35
|
-
`Status text integration: ${plugin.statusText ? styleText('whiteBright', 'yes') : styleText('yellow', 'no')}`,
|
|
36
31
|
`Database integration: ${[plugin.db_init, plugin.db_remove, plugin.db_wipe]
|
|
37
32
|
.filter(Boolean)
|
|
38
33
|
.map(fn => fn?.name.slice(3))
|
|
@@ -41,8 +36,8 @@ export function pluginText(plugin) {
|
|
|
41
36
|
}
|
|
42
37
|
export async function loadPlugin(specifier) {
|
|
43
38
|
try {
|
|
44
|
-
const plugin = await Plugin.parseAsync(await import(specifier)).catch(e => {
|
|
45
|
-
throw
|
|
39
|
+
const plugin = await Plugin.parseAsync(await import(/* @vite-ignore */ specifier)).catch(e => {
|
|
40
|
+
throw z.prettifyError(e);
|
|
46
41
|
});
|
|
47
42
|
plugins.add(plugin);
|
|
48
43
|
output.debug(`Loaded plugin: "${plugin.name}" (${plugin.id}) ${plugin.version}`);
|
package/dist/routes.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { RequestMethod } from '@axium/core/requests';
|
|
2
|
+
import type { LoadEvent, RequestEvent } from '@sveltejs/kit';
|
|
3
|
+
import type { Component } from 'svelte';
|
|
4
|
+
import type z from 'zod/v4';
|
|
5
|
+
type _Params = Partial<Record<string, string>>;
|
|
6
|
+
export type EndpointHandlers<Params extends _Params = _Params> = Partial<Record<RequestMethod, (event: RequestEvent<Params>) => object | Promise<object>>>;
|
|
7
|
+
export type RouteParamOptions = z.ZodType;
|
|
8
|
+
export interface CommonRouteOptions<Params extends _Params = _Params> {
|
|
9
|
+
path: string;
|
|
10
|
+
params?: {
|
|
11
|
+
[K in keyof Params]?: RouteParamOptions;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A route with server-side handlers for different HTTP methods.
|
|
16
|
+
*/
|
|
17
|
+
export interface ServerRouteOptions<Params extends _Params = _Params> extends CommonRouteOptions<Params>, EndpointHandlers<Params> {
|
|
18
|
+
}
|
|
19
|
+
export interface WebRouteOptions extends CommonRouteOptions {
|
|
20
|
+
load?(event: RequestEvent): object | Promise<object>;
|
|
21
|
+
/** the Svelte page */
|
|
22
|
+
page?: Component;
|
|
23
|
+
}
|
|
24
|
+
export type RouteOptions = ServerRouteOptions | WebRouteOptions;
|
|
25
|
+
export interface RouteCommon {
|
|
26
|
+
path: string;
|
|
27
|
+
params?: Record<string, RouteParamOptions>;
|
|
28
|
+
[kBuiltin]: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface ServerRoute extends RouteCommon, EndpointHandlers {
|
|
31
|
+
server: true;
|
|
32
|
+
}
|
|
33
|
+
export interface WebRoute extends RouteCommon {
|
|
34
|
+
server: false;
|
|
35
|
+
load?(event: LoadEvent): object | Promise<object>;
|
|
36
|
+
page?: Component;
|
|
37
|
+
}
|
|
38
|
+
export type Route = ServerRoute | WebRoute;
|
|
39
|
+
/**
|
|
40
|
+
* @internal
|
|
41
|
+
*/
|
|
42
|
+
export declare const routes: Map<string, Route>;
|
|
43
|
+
declare const kBuiltin: unique symbol;
|
|
44
|
+
export declare function addRoute(opt: RouteOptions, _routeMap?: Map<string, Route>): void;
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a request URL into a route.
|
|
47
|
+
* This handles parsing of parameters in the URL.
|
|
48
|
+
*/
|
|
49
|
+
export declare function resolveRoute<T extends Route>(event: RequestEvent | LoadEvent, _routeMap?: Map<string, T>): T | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* This function marks all existing routes as built-in.
|
|
52
|
+
* @internal
|
|
53
|
+
*/
|
|
54
|
+
export declare function _markDefaults(): void;
|
|
55
|
+
export {};
|
package/dist/routes.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @internal
|
|
3
|
+
*/
|
|
4
|
+
export const routes = new Map();
|
|
5
|
+
const kBuiltin = Symbol('kBuiltin');
|
|
6
|
+
export function addRoute(opt, _routeMap = routes) {
|
|
7
|
+
const route = { ...opt, server: !('page' in opt), [kBuiltin]: false };
|
|
8
|
+
if (!route.path.startsWith('/')) {
|
|
9
|
+
throw new Error(`Route path must start with a slash: ${route.path}`);
|
|
10
|
+
}
|
|
11
|
+
if (route.path.startsWith('/api/') && !route.server) {
|
|
12
|
+
throw new Error(`API routes cannot have a client page: ${route.path}`);
|
|
13
|
+
}
|
|
14
|
+
_routeMap.set(route.path, route);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a request URL into a route.
|
|
18
|
+
* This handles parsing of parameters in the URL.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveRoute(event, _routeMap = routes) {
|
|
21
|
+
const { pathname } = event.url;
|
|
22
|
+
if (_routeMap.has(pathname) && !pathname.split('/').some(p => p.startsWith(':')))
|
|
23
|
+
return _routeMap.get(pathname);
|
|
24
|
+
// Otherwise we must have a parameterized route
|
|
25
|
+
routes: for (const route of _routeMap.values()) {
|
|
26
|
+
const params = {};
|
|
27
|
+
// Split the path and route into parts, zipped together
|
|
28
|
+
const pathParts = pathname.split('/').filter(Boolean);
|
|
29
|
+
for (const routePart of route.path.split('/').filter(Boolean)) {
|
|
30
|
+
const pathPart = pathParts.shift();
|
|
31
|
+
if (!pathPart)
|
|
32
|
+
continue routes;
|
|
33
|
+
if (pathPart == routePart)
|
|
34
|
+
continue;
|
|
35
|
+
if (!routePart.startsWith(':'))
|
|
36
|
+
continue routes;
|
|
37
|
+
params[routePart.slice(1)] = pathPart;
|
|
38
|
+
}
|
|
39
|
+
// we didn't find a match, since an exact match would have been found already
|
|
40
|
+
if (pathParts.length || !Object.keys(params).length)
|
|
41
|
+
continue;
|
|
42
|
+
event.params = params;
|
|
43
|
+
return route;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* This function marks all existing routes as built-in.
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
export function _markDefaults() {
|
|
51
|
+
for (const route of routes.values()) {
|
|
52
|
+
route[kBuiltin] = true;
|
|
53
|
+
}
|
|
54
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axium/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"author": "James Prevett <axium@jamespre.dev> (https://jamespre.dev)",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -21,8 +21,7 @@
|
|
|
21
21
|
".": "./dist/index.js",
|
|
22
22
|
"./*": "./dist/*",
|
|
23
23
|
"./web": "./web/index.js",
|
|
24
|
-
"./web/*": "./web/*"
|
|
25
|
-
"./web/server": "./web/index.server.js"
|
|
24
|
+
"./web/*": "./web/*"
|
|
26
25
|
},
|
|
27
26
|
"files": [
|
|
28
27
|
"dist",
|
|
@@ -34,28 +33,21 @@
|
|
|
34
33
|
"scripts": {
|
|
35
34
|
"build": "tsc"
|
|
36
35
|
},
|
|
37
|
-
"peerDependencies": {
|
|
38
|
-
"@axium/core": ">=0.0.2"
|
|
39
|
-
},
|
|
40
36
|
"dependencies": {
|
|
41
|
-
"@
|
|
42
|
-
"@
|
|
37
|
+
"@simplewebauthn/server": "^13.1.1",
|
|
38
|
+
"@sveltejs/kit": "^2.20.2",
|
|
43
39
|
"@types/pg": "^8.11.11",
|
|
44
|
-
"bcryptjs": "^3.0.2",
|
|
45
40
|
"commander": "^13.1.0",
|
|
46
41
|
"kysely": "^0.27.5",
|
|
47
42
|
"logzen": "^0.7.0",
|
|
48
43
|
"mime": "^4.0.7",
|
|
49
44
|
"pg": "^8.14.1",
|
|
50
|
-
"utilium": "^2.3.
|
|
51
|
-
"zod
|
|
45
|
+
"utilium": "^2.3.8",
|
|
46
|
+
"zod": "^3.25.61",
|
|
47
|
+
"@axium/core": ">=0.2.0"
|
|
52
48
|
},
|
|
53
49
|
"devDependencies": {
|
|
54
|
-
"@auth/sveltekit": "^1.8.0",
|
|
55
|
-
"@simplewebauthn/browser": "^9.0.1",
|
|
56
|
-
"@simplewebauthn/server": "^9.0.3",
|
|
57
50
|
"@sveltejs/adapter-node": "^5.2.12",
|
|
58
|
-
"@sveltejs/kit": "^2.20.2",
|
|
59
51
|
"svelte": "^5.25.3",
|
|
60
52
|
"vite-plugin-mkcert": "^1.17.8"
|
|
61
53
|
}
|