@axium/server 0.16.2 → 0.17.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/build/client/_app/immutable/chunks/{Dm7VIzXy.js → BIqJFsZ3.js} +1 -1
- package/build/client/_app/immutable/chunks/BIqJFsZ3.js.br +0 -0
- package/build/client/_app/immutable/chunks/BIqJFsZ3.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{DIqfHWch.js → BQEW-soH.js} +1 -1
- package/build/client/_app/immutable/chunks/BQEW-soH.js.br +0 -0
- package/build/client/_app/immutable/chunks/BQEW-soH.js.gz +0 -0
- package/build/client/_app/immutable/chunks/ClV-nJVy.js +31 -0
- package/build/client/_app/immutable/chunks/ClV-nJVy.js.br +0 -0
- package/build/client/_app/immutable/chunks/ClV-nJVy.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{Ccv5p-Lr.js → DQ-d8a5w.js} +2 -2
- package/build/client/_app/immutable/chunks/DQ-d8a5w.js.br +0 -0
- package/build/client/_app/immutable/chunks/DQ-d8a5w.js.gz +0 -0
- package/build/client/_app/immutable/entry/{app.CYeEqzuG.js → app.Bexu3pho.js} +2 -2
- package/build/client/_app/immutable/entry/app.Bexu3pho.js.br +0 -0
- package/build/client/_app/immutable/entry/app.Bexu3pho.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.CkXOtoPI.js +1 -0
- package/build/client/_app/immutable/entry/start.CkXOtoPI.js.br +2 -0
- package/build/client/_app/immutable/entry/start.CkXOtoPI.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{1.BHYgH-pu.js → 1.Di0t29Xx.js} +1 -1
- package/build/client/_app/immutable/nodes/1.Di0t29Xx.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.Di0t29Xx.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{3.C6woCAgZ.js → 3.dB5xKZIR.js} +1 -1
- package/build/client/_app/immutable/nodes/3.dB5xKZIR.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.dB5xKZIR.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{4.CMRdSHhP.js → 4.DC1TCp7U.js} +1 -1
- package/build/client/_app/immutable/nodes/4.DC1TCp7U.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.DC1TCp7U.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.DGB0QJIF.js +1 -0
- package/build/client/_app/immutable/nodes/5.DGB0QJIF.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.DGB0QJIF.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{6.asNffl6m.js → 6.WTfzqOhT.js} +1 -1
- package/build/client/_app/immutable/nodes/6.WTfzqOhT.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.WTfzqOhT.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/server/chunks/{1-B46rUG8d.js → 1-Db3AncWW.js} +2 -2
- package/build/server/chunks/{1-B46rUG8d.js.map → 1-Db3AncWW.js.map} +1 -1
- package/build/server/chunks/{3-CeRz1jLd.js → 3-BzEpIldk.js} +3 -3
- package/build/server/chunks/{3-CeRz1jLd.js.map → 3-BzEpIldk.js.map} +1 -1
- package/build/server/chunks/{4-CPwP8Ikn.js → 4-J7bxt5HT.js} +3 -3
- package/build/server/chunks/{4-CPwP8Ikn.js.map → 4-J7bxt5HT.js.map} +1 -1
- package/build/server/chunks/{5-xNibYAp4.js → 5-uwjgYAk8.js} +3 -3
- package/build/server/chunks/{5-xNibYAp4.js.map → 5-uwjgYAk8.js.map} +1 -1
- package/build/server/chunks/{6-CivW166X.js → 6-Cj4cg3MT.js} +3 -3
- package/build/server/chunks/{6-CivW166X.js.map → 6-Cj4cg3MT.js.map} +1 -1
- package/build/server/chunks/{Logout-jfPnYBz7.js → Logout-CkwmxNTt.js} +2 -2
- package/build/server/chunks/{Logout-jfPnYBz7.js.map → Logout-CkwmxNTt.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-D1Tk1-7i.js → _page.svelte-CVJoQAAB.js} +2 -2
- package/build/server/chunks/{_page.svelte-D1Tk1-7i.js.map → _page.svelte-CVJoQAAB.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-CPmgKX2f.js → _page.svelte-D3kXcWlK.js} +2 -2
- package/build/server/chunks/{_page.svelte-CPmgKX2f.js.map → _page.svelte-D3kXcWlK.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-C5KWK5D-.js → _page.svelte-DPdvFuWg.js} +3 -3
- package/build/server/chunks/{_page.svelte-C5KWK5D-.js.map → _page.svelte-DPdvFuWg.js.map} +1 -1
- package/build/server/chunks/{_page.svelte-BUYPMfbS.js → _page.svelte-whmO3TtB.js} +3 -3
- package/build/server/chunks/{_page.svelte-BUYPMfbS.js.map → _page.svelte-whmO3TtB.js.map} +1 -1
- package/build/server/chunks/user-DW-tjDe0.js +5656 -0
- package/build/server/chunks/user-DW-tjDe0.js.map +1 -0
- package/build/server/index.js +1 -1
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +6 -6
- package/build/server/manifest.js.map +1 -1
- package/dist/acl.d.ts +54 -0
- package/dist/acl.js +63 -0
- package/dist/api/acl.d.ts +1 -0
- package/dist/api/acl.js +21 -0
- package/dist/api/passkeys.js +1 -1
- package/dist/api/register.js +1 -1
- package/dist/api/users.js +17 -13
- package/dist/auth.js +1 -0
- package/dist/cli.js +15 -9
- package/dist/database.d.ts +52 -7
- package/dist/database.js +147 -41
- package/dist/plugins.d.ts +10 -10
- package/dist/plugins.js +1 -1
- package/dist/requests.d.ts +1 -1
- package/dist/requests.js +1 -1
- package/dist/routes.d.ts +3 -0
- package/dist/routes.js +3 -0
- package/dist/sveltekit.js +1 -1
- package/package.json +2 -2
- package/svelte.config.js +3 -0
- package/web/lib/AccessControl.svelte +37 -0
- package/web/lib/AccessControlDialog.svelte +12 -0
- package/web/lib/Popover.svelte +0 -2
- package/web/lib/Upload.svelte +19 -21
- package/web/lib/WithContextMenu.svelte +0 -1
- package/build/client/_app/immutable/chunks/Ccv5p-Lr.js.br +0 -0
- package/build/client/_app/immutable/chunks/Ccv5p-Lr.js.gz +0 -0
- package/build/client/_app/immutable/chunks/DIqfHWch.js.br +0 -0
- package/build/client/_app/immutable/chunks/DIqfHWch.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Dm7VIzXy.js.br +0 -0
- package/build/client/_app/immutable/chunks/Dm7VIzXy.js.gz +0 -0
- package/build/client/_app/immutable/chunks/iuOP1-8s.js +0 -33
- package/build/client/_app/immutable/chunks/iuOP1-8s.js.br +0 -0
- package/build/client/_app/immutable/chunks/iuOP1-8s.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.CYeEqzuG.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CYeEqzuG.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.B8r-M4qY.js +0 -1
- package/build/client/_app/immutable/entry/start.B8r-M4qY.js.br +0 -2
- package/build/client/_app/immutable/entry/start.B8r-M4qY.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.BHYgH-pu.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.BHYgH-pu.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.C6woCAgZ.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.C6woCAgZ.js.gz +0 -0
- package/build/client/_app/immutable/nodes/4.CMRdSHhP.js.br +0 -0
- package/build/client/_app/immutable/nodes/4.CMRdSHhP.js.gz +0 -0
- package/build/client/_app/immutable/nodes/5.DQgmXUpj.js +0 -1
- package/build/client/_app/immutable/nodes/5.DQgmXUpj.js.br +0 -0
- package/build/client/_app/immutable/nodes/5.DQgmXUpj.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.asNffl6m.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.asNffl6m.js.gz +0 -0
- package/build/server/chunks/user-C2RJxOZ2.js +0 -13552
- package/build/server/chunks/user-C2RJxOZ2.js.map +0 -1
- package/schema.json +0 -154
package/dist/database.js
CHANGED
|
@@ -58,6 +58,7 @@ import pg from 'pg';
|
|
|
58
58
|
import config from './config.js';
|
|
59
59
|
import * as io from './io.js';
|
|
60
60
|
import { plugins } from './plugins.js';
|
|
61
|
+
import * as acl from './acl.js';
|
|
61
62
|
const sym = Symbol.for('Axium:database');
|
|
62
63
|
export let database;
|
|
63
64
|
export function connect() {
|
|
@@ -77,11 +78,12 @@ export function connect() {
|
|
|
77
78
|
return database;
|
|
78
79
|
}
|
|
79
80
|
// Helpers
|
|
80
|
-
export async function count(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
.select(
|
|
84
|
-
|
|
81
|
+
export async function count(...tables) {
|
|
82
|
+
return await database
|
|
83
|
+
.selectFrom(tables)
|
|
84
|
+
.select(() => tables.map(t => database.fn.countAll(t).as(t)))
|
|
85
|
+
.$castTo()
|
|
86
|
+
.executeTakeFirstOrThrow();
|
|
85
87
|
}
|
|
86
88
|
/**
|
|
87
89
|
* Select the user with the id from the userId column of a table, placing it in the `user` property.
|
|
@@ -92,16 +94,9 @@ export function userFromId(eb) {
|
|
|
92
94
|
.$castTo()
|
|
93
95
|
.as('user');
|
|
94
96
|
}
|
|
95
|
-
export async function
|
|
96
|
-
return {
|
|
97
|
-
users: await count('users'),
|
|
98
|
-
passkeys: await count('passkeys'),
|
|
99
|
-
sessions: await count('sessions'),
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
export async function statusText() {
|
|
97
|
+
export async function statText() {
|
|
103
98
|
try {
|
|
104
|
-
const stats = await
|
|
99
|
+
const stats = await count('users', 'passkeys', 'sessions');
|
|
105
100
|
return `${stats.users} users, ${stats.passkeys} passkeys, ${stats.sessions} sessions`;
|
|
106
101
|
}
|
|
107
102
|
catch (error) {
|
|
@@ -152,6 +147,13 @@ const throwUnlessRows = (text) => {
|
|
|
152
147
|
throw 'missing.';
|
|
153
148
|
return text;
|
|
154
149
|
};
|
|
150
|
+
export async function createIndex(table, column, mod) {
|
|
151
|
+
io.start(`Creating index for ${table}.${column}`);
|
|
152
|
+
let query = database.schema.createIndex(`${table}_${column}_index`).on(table).column(column);
|
|
153
|
+
if (mod)
|
|
154
|
+
query = mod(query);
|
|
155
|
+
await query.execute().then(io.done).catch(warnExists);
|
|
156
|
+
}
|
|
155
157
|
export async function init(opt) {
|
|
156
158
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
157
159
|
try {
|
|
@@ -195,7 +197,16 @@ export async function init(opt) {
|
|
|
195
197
|
})
|
|
196
198
|
.catch(io.warn);
|
|
197
199
|
await _sql('SELECT pg_reload_conf()', 'Reloading configuration');
|
|
200
|
+
io.start('Connecting to database');
|
|
198
201
|
const db = __addDisposableResource(env_1, connect(), true);
|
|
202
|
+
io.done();
|
|
203
|
+
function maybeCheck(table) {
|
|
204
|
+
return (e) => {
|
|
205
|
+
warnExists(e);
|
|
206
|
+
if (opt.check)
|
|
207
|
+
return checkTableTypes(table, expectedTypes[table], opt);
|
|
208
|
+
};
|
|
209
|
+
}
|
|
199
210
|
io.start('Creating table users');
|
|
200
211
|
await db.schema
|
|
201
212
|
.createTable('users')
|
|
@@ -211,7 +222,7 @@ export async function init(opt) {
|
|
|
211
222
|
.addColumn('registeredAt', 'timestamptz', col => col.notNull().defaultTo(sql `now()`))
|
|
212
223
|
.execute()
|
|
213
224
|
.then(io.done)
|
|
214
|
-
.catch(
|
|
225
|
+
.catch(maybeCheck('users'));
|
|
215
226
|
io.start('Creating table sessions');
|
|
216
227
|
await db.schema
|
|
217
228
|
.createTable('sessions')
|
|
@@ -223,9 +234,8 @@ export async function init(opt) {
|
|
|
223
234
|
.addColumn('elevated', 'boolean', col => col.notNull())
|
|
224
235
|
.execute()
|
|
225
236
|
.then(io.done)
|
|
226
|
-
.catch(
|
|
227
|
-
|
|
228
|
-
await db.schema.createIndex('sessions_userId_index').on('sessions').column('userId').execute().then(io.done).catch(warnExists);
|
|
237
|
+
.catch(maybeCheck('sessions'));
|
|
238
|
+
await createIndex('sessions', 'id');
|
|
229
239
|
io.start('Creating table verifications');
|
|
230
240
|
await db.schema
|
|
231
241
|
.createTable('verifications')
|
|
@@ -235,14 +245,14 @@ export async function init(opt) {
|
|
|
235
245
|
.addColumn('role', 'text', col => col.notNull())
|
|
236
246
|
.execute()
|
|
237
247
|
.then(io.done)
|
|
238
|
-
.catch(
|
|
248
|
+
.catch(maybeCheck('verifications'));
|
|
239
249
|
io.start('Creating table passkeys');
|
|
240
250
|
await db.schema
|
|
241
251
|
.createTable('passkeys')
|
|
242
252
|
.addColumn('id', 'text', col => col.primaryKey().notNull())
|
|
243
253
|
.addColumn('name', 'text')
|
|
244
254
|
.addColumn('createdAt', 'timestamptz', col => col.notNull().defaultTo(sql `now()`))
|
|
245
|
-
.addColumn('userId', 'uuid', col => col.notNull().references('users.id').onDelete('cascade').
|
|
255
|
+
.addColumn('userId', 'uuid', col => col.notNull().references('users.id').onDelete('cascade').notNull())
|
|
246
256
|
.addColumn('publicKey', 'bytea', col => col.notNull())
|
|
247
257
|
.addColumn('counter', 'integer', col => col.notNull())
|
|
248
258
|
.addColumn('deviceType', 'text', col => col.notNull())
|
|
@@ -250,14 +260,15 @@ export async function init(opt) {
|
|
|
250
260
|
.addColumn('transports', sql `text[]`)
|
|
251
261
|
.execute()
|
|
252
262
|
.then(io.done)
|
|
253
|
-
.catch(
|
|
254
|
-
|
|
255
|
-
|
|
263
|
+
.catch(maybeCheck('passkeys'));
|
|
264
|
+
await createIndex('passkeys', 'userId');
|
|
265
|
+
io.start('Creating schema acl');
|
|
266
|
+
await db.schema.createSchema('acl').execute().then(io.done).catch(warnExists);
|
|
256
267
|
for (const plugin of plugins) {
|
|
257
268
|
if (!plugin.hooks.db_init)
|
|
258
269
|
continue;
|
|
259
270
|
io.plugin(plugin.name);
|
|
260
|
-
await plugin.hooks.db_init(opt
|
|
271
|
+
await plugin.hooks.db_init(opt);
|
|
261
272
|
}
|
|
262
273
|
}
|
|
263
274
|
catch (e_1) {
|
|
@@ -270,6 +281,90 @@ export async function init(opt) {
|
|
|
270
281
|
await result_1;
|
|
271
282
|
}
|
|
272
283
|
}
|
|
284
|
+
export const expectedTypes = {
|
|
285
|
+
users: {
|
|
286
|
+
email: { type: 'text', required: true },
|
|
287
|
+
emailVerified: { type: 'timestamptz' },
|
|
288
|
+
id: { type: 'uuid', required: true, hasDefault: true },
|
|
289
|
+
image: { type: 'text' },
|
|
290
|
+
isAdmin: { type: 'bool', required: true, hasDefault: true },
|
|
291
|
+
name: { type: 'text' },
|
|
292
|
+
preferences: { type: 'jsonb', required: true, hasDefault: true },
|
|
293
|
+
registeredAt: { type: 'timestamptz', required: true, hasDefault: true },
|
|
294
|
+
roles: { type: '_text', required: true, hasDefault: true },
|
|
295
|
+
tags: { type: '_text', required: true, hasDefault: true },
|
|
296
|
+
},
|
|
297
|
+
verifications: {
|
|
298
|
+
userId: { type: 'uuid', required: true },
|
|
299
|
+
token: { type: 'text', required: true },
|
|
300
|
+
expires: { type: 'timestamptz', required: true },
|
|
301
|
+
role: { type: 'text', required: true },
|
|
302
|
+
},
|
|
303
|
+
passkeys: {
|
|
304
|
+
id: { type: 'text', required: true },
|
|
305
|
+
name: { type: 'text' },
|
|
306
|
+
createdAt: { type: 'timestamptz', required: true, hasDefault: true },
|
|
307
|
+
userId: { type: 'uuid', required: true },
|
|
308
|
+
publicKey: { type: 'bytea', required: true },
|
|
309
|
+
counter: { type: 'int4', required: true },
|
|
310
|
+
deviceType: { type: 'text', required: true },
|
|
311
|
+
backedUp: { type: 'bool', required: true },
|
|
312
|
+
transports: { type: '_text' },
|
|
313
|
+
},
|
|
314
|
+
sessions: {
|
|
315
|
+
id: { type: 'uuid', required: true, hasDefault: true },
|
|
316
|
+
userId: { type: 'uuid', required: true },
|
|
317
|
+
created: { type: 'timestamptz', required: true },
|
|
318
|
+
token: { type: 'text', required: true },
|
|
319
|
+
expires: { type: 'timestamptz', required: true },
|
|
320
|
+
elevated: { type: 'bool', required: true },
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
/**
|
|
324
|
+
* Checks that a table has the expected column types, nullability, and default values.
|
|
325
|
+
*/
|
|
326
|
+
export async function checkTableTypes(tableName, types, opt) {
|
|
327
|
+
io.start(`Checking for table ${tableName}`);
|
|
328
|
+
const dbTables = opt._metadata || (await database.introspection.getTables());
|
|
329
|
+
const table = dbTables.find(t => t.name === tableName);
|
|
330
|
+
if (!table)
|
|
331
|
+
throw 'missing.';
|
|
332
|
+
io.done();
|
|
333
|
+
const columns = Object.fromEntries(table.columns.map(c => [c.name, c]));
|
|
334
|
+
for (const [key, { type, required = false, hasDefault = false }] of Object.entries(types)) {
|
|
335
|
+
io.start(`Checking column ${tableName}.${key}`);
|
|
336
|
+
const col = columns[key];
|
|
337
|
+
if (!col)
|
|
338
|
+
throw 'missing.';
|
|
339
|
+
try {
|
|
340
|
+
if (col.dataType != type)
|
|
341
|
+
throw `incorrect type "${col.dataType}", expected ${type}`;
|
|
342
|
+
if (col.isNullable != !required)
|
|
343
|
+
throw required ? 'nullable' : 'not nullable';
|
|
344
|
+
if (col.hasDefaultValue != hasDefault)
|
|
345
|
+
throw hasDefault ? 'missing default' : 'has default';
|
|
346
|
+
io.done();
|
|
347
|
+
}
|
|
348
|
+
catch (e) {
|
|
349
|
+
if (opt.strict)
|
|
350
|
+
throw e;
|
|
351
|
+
io.warn(e);
|
|
352
|
+
}
|
|
353
|
+
delete columns[key];
|
|
354
|
+
}
|
|
355
|
+
if (!opt.extra)
|
|
356
|
+
return;
|
|
357
|
+
io.start('Checking for extra columns in ' + tableName);
|
|
358
|
+
const unchecked = Object.keys(columns)
|
|
359
|
+
.map(c => `${tableName}.${c}`)
|
|
360
|
+
.join(', ');
|
|
361
|
+
if (!unchecked.length)
|
|
362
|
+
io.done();
|
|
363
|
+
else if (opt.strict)
|
|
364
|
+
throw unchecked;
|
|
365
|
+
else
|
|
366
|
+
io.warn(unchecked);
|
|
367
|
+
}
|
|
273
368
|
export async function check(opt) {
|
|
274
369
|
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
275
370
|
try {
|
|
@@ -280,18 +375,22 @@ export async function check(opt) {
|
|
|
280
375
|
io.start('Connecting to database');
|
|
281
376
|
const db = __addDisposableResource(env_2, connect(), true);
|
|
282
377
|
io.done();
|
|
283
|
-
io.start('
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
378
|
+
io.start('Getting table metadata');
|
|
379
|
+
opt._metadata = await db.introspection.getTables();
|
|
380
|
+
const tables = Object.fromEntries(opt._metadata.map(t => [t.name, t]));
|
|
381
|
+
io.done();
|
|
382
|
+
for (const table of Object.keys(expectedTypes)) {
|
|
383
|
+
await checkTableTypes(table, expectedTypes[table], opt);
|
|
384
|
+
delete tables[table];
|
|
385
|
+
}
|
|
386
|
+
io.start('Checking for extra tables');
|
|
387
|
+
const unchecked = Object.keys(tables).join(', ');
|
|
388
|
+
if (!unchecked.length)
|
|
389
|
+
io.done();
|
|
390
|
+
else if (opt.strict)
|
|
391
|
+
throw unchecked;
|
|
392
|
+
else
|
|
393
|
+
io.warn(unchecked);
|
|
295
394
|
}
|
|
296
395
|
catch (e_2) {
|
|
297
396
|
env_2.error = e_2;
|
|
@@ -314,7 +413,7 @@ export async function clean(opt) {
|
|
|
314
413
|
if (!plugin.hooks.clean)
|
|
315
414
|
continue;
|
|
316
415
|
io.plugin(plugin.name);
|
|
317
|
-
await plugin.hooks.clean(opt
|
|
416
|
+
await plugin.hooks.clean(opt);
|
|
318
417
|
}
|
|
319
418
|
}
|
|
320
419
|
/**
|
|
@@ -323,12 +422,12 @@ export async function clean(opt) {
|
|
|
323
422
|
export async function uninstall(opt) {
|
|
324
423
|
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
325
424
|
try {
|
|
326
|
-
const
|
|
425
|
+
const _ = __addDisposableResource(env_3, connect(), true);
|
|
327
426
|
for (const plugin of plugins) {
|
|
328
427
|
if (!plugin.hooks.remove)
|
|
329
428
|
continue;
|
|
330
429
|
io.plugin(plugin.name);
|
|
331
|
-
await plugin.hooks.remove(opt
|
|
430
|
+
await plugin.hooks.remove(opt);
|
|
332
431
|
}
|
|
333
432
|
await _sql('DROP DATABASE axium', 'Dropping database');
|
|
334
433
|
await _sql('REVOKE ALL PRIVILEGES ON SCHEMA public FROM axium', 'Revoking schema privileges');
|
|
@@ -365,13 +464,20 @@ export async function wipe(opt) {
|
|
|
365
464
|
if (!plugin.hooks.db_wipe)
|
|
366
465
|
continue;
|
|
367
466
|
io.plugin(plugin.name);
|
|
368
|
-
await plugin.hooks.db_wipe(opt
|
|
467
|
+
await plugin.hooks.db_wipe(opt);
|
|
369
468
|
}
|
|
370
469
|
for (const table of ['users', 'passkeys', 'sessions', 'verifications']) {
|
|
371
|
-
io.start(`
|
|
470
|
+
io.start(`Wiping ${table}`);
|
|
372
471
|
await db.deleteFrom(table).execute();
|
|
373
472
|
io.done();
|
|
374
473
|
}
|
|
474
|
+
for (const table of await database.introspection.getTables()) {
|
|
475
|
+
if (!table.name.startsWith('acl.'))
|
|
476
|
+
continue;
|
|
477
|
+
const name = table.name;
|
|
478
|
+
io.debug(`Wiping ${name}`);
|
|
479
|
+
await db.deleteFrom(name).execute();
|
|
480
|
+
}
|
|
375
481
|
}
|
|
376
482
|
export async function rotatePassword() {
|
|
377
483
|
io.start('Generating new password');
|
package/dist/plugins.d.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import z from 'zod';
|
|
2
|
-
import type {
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
import type { InitOptions, OpOptions } from './database.js';
|
|
3
3
|
export declare const PluginMetadata: z.ZodObject<{
|
|
4
4
|
name: z.ZodString;
|
|
5
5
|
version: z.ZodString;
|
|
6
6
|
description: z.ZodOptional<z.ZodString>;
|
|
7
7
|
routes: z.ZodOptional<z.ZodString>;
|
|
8
|
-
}, z.
|
|
8
|
+
}, z.core.$loose>;
|
|
9
9
|
export declare const Plugin: z.ZodObject<{
|
|
10
10
|
name: z.ZodString;
|
|
11
11
|
version: z.ZodString;
|
|
12
12
|
description: z.ZodOptional<z.ZodString>;
|
|
13
13
|
routes: z.ZodOptional<z.ZodString>;
|
|
14
|
-
statusText: z.ZodCustom<z.
|
|
15
|
-
hooks: z.ZodOptional<z.ZodRecord<z.ZodLiteral<"remove" | "db_init" | "db_wipe" | "clean"> & z.
|
|
16
|
-
}, z.
|
|
14
|
+
statusText: z.ZodCustom<z.core.$InferInnerFunctionTypeAsync<z.core.$ZodTuple<[], null>, z.ZodString>, z.core.$InferInnerFunctionTypeAsync<z.core.$ZodTuple<[], null>, z.ZodString>>;
|
|
15
|
+
hooks: z.ZodOptional<z.ZodRecord<z.ZodLiteral<"remove" | "db_init" | "db_wipe" | "clean"> & z.core.$partial, z.ZodCustom<(...args: any[]) => any, (...args: any[]) => any>>>;
|
|
16
|
+
}, z.core.$loose>;
|
|
17
17
|
declare const kSpecifier: unique symbol;
|
|
18
18
|
export type Plugin = z.infer<typeof Plugin>;
|
|
19
19
|
interface PluginInternal extends Plugin {
|
|
@@ -21,12 +21,12 @@ interface PluginInternal extends Plugin {
|
|
|
21
21
|
hooks: Hooks;
|
|
22
22
|
}
|
|
23
23
|
export interface Hooks {
|
|
24
|
-
db_init?: (opt: InitOptions
|
|
24
|
+
db_init?: (opt: InitOptions) => void | Promise<void>;
|
|
25
25
|
remove?: (opt: {
|
|
26
26
|
force?: boolean;
|
|
27
|
-
}
|
|
28
|
-
db_wipe?: (opt: OpOptions
|
|
29
|
-
clean?: (opt: Partial<OpOptions
|
|
27
|
+
}) => void | Promise<void>;
|
|
28
|
+
db_wipe?: (opt: OpOptions) => void | Promise<void>;
|
|
29
|
+
clean?: (opt: Partial<OpOptions>) => void | Promise<void>;
|
|
30
30
|
}
|
|
31
31
|
export declare const plugins: Set<PluginInternal>;
|
|
32
32
|
export declare function resolvePlugin(search: string): PluginInternal | undefined;
|
package/dist/plugins.js
CHANGED
|
@@ -2,7 +2,7 @@ import { zAsyncFunction } from '@axium/core/schemas';
|
|
|
2
2
|
import * as fs from 'node:fs';
|
|
3
3
|
import { resolve } from 'node:path/posix';
|
|
4
4
|
import { styleText } from 'node:util';
|
|
5
|
-
import z from 'zod';
|
|
5
|
+
import * as z from 'zod';
|
|
6
6
|
import { output } from './io.js';
|
|
7
7
|
import { _unique } from './state.js';
|
|
8
8
|
export const PluginMetadata = z.looseObject({
|
package/dist/requests.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type User } from '@axium/core/user';
|
|
2
2
|
import { type HttpError, type RequestEvent } from '@sveltejs/kit';
|
|
3
|
-
import z from 'zod';
|
|
3
|
+
import * as z from 'zod';
|
|
4
4
|
import { type SessionAndUser, type UserInternal } from './auth.js';
|
|
5
5
|
export declare function parseBody<const Schema extends z.ZodType, const Result extends z.infer<Schema> = z.infer<Schema>>(event: RequestEvent, schema: Schema): Promise<Result>;
|
|
6
6
|
export declare function getToken(event: RequestEvent, sensitive?: boolean): string | undefined;
|
package/dist/requests.js
CHANGED
|
@@ -2,7 +2,7 @@ import { userProtectedFields, userPublicFields } from '@axium/core/user';
|
|
|
2
2
|
import { error, json } from '@sveltejs/kit';
|
|
3
3
|
import { serialize as serializeCookie } from 'cookie';
|
|
4
4
|
import { pick } from 'utilium';
|
|
5
|
-
import z from 'zod';
|
|
5
|
+
import * as z from 'zod';
|
|
6
6
|
import { createSession, getSessionAndUser, getUser } from './auth.js';
|
|
7
7
|
import { config } from './config.js';
|
|
8
8
|
export async function parseBody(event, schema) {
|
package/dist/routes.d.ts
CHANGED
|
@@ -42,6 +42,9 @@ export type Route = ServerRoute | WebRoute;
|
|
|
42
42
|
* @internal
|
|
43
43
|
*/
|
|
44
44
|
export declare const routes: Map<string, Route>;
|
|
45
|
+
/**
|
|
46
|
+
* @category Plugin API
|
|
47
|
+
*/
|
|
45
48
|
export declare function addRoute(opt: RouteOptions): void;
|
|
46
49
|
/**
|
|
47
50
|
* Resolve a request URL into a route.
|
package/dist/routes.js
CHANGED
|
@@ -6,6 +6,9 @@ import { _unique } from './state.js';
|
|
|
6
6
|
* @internal
|
|
7
7
|
*/
|
|
8
8
|
export const routes = _unique('routes', new Map());
|
|
9
|
+
/**
|
|
10
|
+
* @category Plugin API
|
|
11
|
+
*/
|
|
9
12
|
export function addRoute(opt) {
|
|
10
13
|
const route = { ...opt, server: !('page' in opt) };
|
|
11
14
|
if (!route.path.startsWith('/')) {
|
package/dist/sveltekit.js
CHANGED
|
@@ -2,7 +2,7 @@ import { error, json } from '@sveltejs/kit';
|
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { styleText } from 'node:util';
|
|
4
4
|
import { render } from 'svelte/server';
|
|
5
|
-
import z from 'zod';
|
|
5
|
+
import * as z from 'zod';
|
|
6
6
|
import { config } from './config.js';
|
|
7
7
|
import { resolveRoute } from './routes.js';
|
|
8
8
|
async function handleAPIRequest(event, route) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axium/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"author": "James Prevett <axium@jamespre.dev> (https://jamespre.dev)",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"exports": {
|
|
22
22
|
".": "./dist/index.js",
|
|
23
23
|
"./*": "./dist/*.js",
|
|
24
|
+
"./lib": "./web/lib/index.js",
|
|
24
25
|
"./lib/*": "./web/lib/*",
|
|
25
26
|
"./$hooks": "./web/hooks.server.ts",
|
|
26
27
|
"./$routes": "./routes",
|
|
@@ -32,7 +33,6 @@
|
|
|
32
33
|
"build",
|
|
33
34
|
"dist",
|
|
34
35
|
"routes",
|
|
35
|
-
"schema.json",
|
|
36
36
|
"svelte.config.js",
|
|
37
37
|
"web"
|
|
38
38
|
],
|
package/svelte.config.js
CHANGED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import UserCard from './UserCard.svelte';
|
|
3
|
+
import type { Permission, AccessControl } from '@axium/core/access';
|
|
4
|
+
import { permissionNames } from '@axium/core/access';
|
|
5
|
+
import type { Entries } from 'utilium';
|
|
6
|
+
|
|
7
|
+
const { control, editable }: { control: AccessControl; editable: boolean } = $props();
|
|
8
|
+
|
|
9
|
+
const perm = $derived(permissionNames[control.permission as Permission]);
|
|
10
|
+
|
|
11
|
+
const permEntries = Object.entries(permissionNames) as any as Entries<typeof permissionNames>;
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<div class="AccessControl">
|
|
15
|
+
{#if !control.user}<i>Unknown</i>
|
|
16
|
+
{:else}
|
|
17
|
+
<UserCard user={control.user} />
|
|
18
|
+
{#if editable}
|
|
19
|
+
<input type="hidden" name="userId" value={control.user.id} />
|
|
20
|
+
<select name="permission">
|
|
21
|
+
{#each permEntries as [key, name]}
|
|
22
|
+
<option value={key} selected={key == control.permission}>{name}</option>
|
|
23
|
+
{/each}
|
|
24
|
+
</select>
|
|
25
|
+
{:else}
|
|
26
|
+
<span>{perm}</span>
|
|
27
|
+
{/if}
|
|
28
|
+
{/if}
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<style>
|
|
32
|
+
.AccessControl {
|
|
33
|
+
display: flex;
|
|
34
|
+
gap: 1em;
|
|
35
|
+
padding: 1em 2em;
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import FormDialog from './FormDialog.svelte';
|
|
3
|
+
import AccessControl from './AccessControl.svelte';
|
|
4
|
+
|
|
5
|
+
let { item, editable } = $props();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<FormDialog submitText="Save">
|
|
9
|
+
{#each item.acl as control}
|
|
10
|
+
<AccessControl {control} {editable} />
|
|
11
|
+
{/each}
|
|
12
|
+
</FormDialog>
|
package/web/lib/Popover.svelte
CHANGED
|
@@ -7,8 +7,6 @@
|
|
|
7
7
|
}: { children(): any; toggle?(): any; id?: string } = $props();
|
|
8
8
|
</script>
|
|
9
9
|
|
|
10
|
-
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
11
|
-
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
12
10
|
<div onclick={e => e.stopPropagation()}>
|
|
13
11
|
<button style:display="contents" popovertarget={id}>
|
|
14
12
|
{#if toggle}
|
package/web/lib/Upload.svelte
CHANGED
|
@@ -10,27 +10,25 @@
|
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
12
|
<div>
|
|
13
|
-
{
|
|
14
|
-
|
|
15
|
-
{
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
<label for={id}><Icon i="upload" />Upload</label>
|
|
33
|
-
{/if}
|
|
13
|
+
<label for={id} class={[files?.length && 'file']}>
|
|
14
|
+
{#each files as file}
|
|
15
|
+
<Icon i={forMime(file.type)} />
|
|
16
|
+
<span>{file.name}</span>
|
|
17
|
+
<button
|
|
18
|
+
onclick={e => {
|
|
19
|
+
e.preventDefault();
|
|
20
|
+
const dt = new DataTransfer();
|
|
21
|
+
for (let f of files) if (file !== f) dt.items.add(f);
|
|
22
|
+
input.files = files = dt.files;
|
|
23
|
+
}}
|
|
24
|
+
style:display="contents"
|
|
25
|
+
>
|
|
26
|
+
<Icon i="trash" />
|
|
27
|
+
</button>
|
|
28
|
+
{:else}
|
|
29
|
+
<Icon i="upload" /> Upload
|
|
30
|
+
{/each}
|
|
31
|
+
</label>
|
|
34
32
|
|
|
35
33
|
<input bind:this={input} {name} {id} type="file" bind:files {...rest} />
|
|
36
34
|
</div>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|