@axium/server 0.26.2 → 0.27.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/acl.d.ts +2 -1
- package/dist/acl.js +18 -0
- package/dist/api/acl.js +7 -7
- package/dist/api/admin.js +6 -6
- package/dist/api/passkeys.js +6 -6
- package/dist/api/register.js +1 -1
- package/dist/api/users.js +16 -30
- package/dist/audit.d.ts +3 -0
- package/dist/audit.js +8 -1
- package/dist/cli.js +3 -2
- package/dist/config.d.ts +2 -2
- package/dist/config.js +2 -2
- package/dist/database.d.ts +4 -0
- package/dist/database.js +24 -0
- package/dist/internal_requests.js +1 -1
- package/dist/requests.d.ts +1 -1
- package/dist/requests.js +8 -1
- package/dist/routes.d.ts +20 -20
- package/dist/routes.js +2 -1
- package/dist/serve.js +1 -1
- package/package.json +1 -1
- package/routes/admin/audit/[id]/+page.svelte +6 -1
- package/routes/admin/plugins/+page.svelte +5 -1
- package/svelte.config.js +3 -0
package/dist/acl.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AccessControl, Permission, UserInternal } from '@axium/core';
|
|
1
|
+
import type { AccessControl, AccessMap, Permission, UserInternal } from '@axium/core';
|
|
2
2
|
import type { AliasedRawBuilder, Expression, ExpressionBuilder, Selectable } from 'kysely';
|
|
3
3
|
import * as db from './database.js';
|
|
4
4
|
export interface Target {
|
|
@@ -57,4 +57,5 @@ export interface ACLSelectionOptions {
|
|
|
57
57
|
*/
|
|
58
58
|
export declare function from(table: TargetName, opt?: ACLSelectionOptions): (eb: ExpressionBuilder<db.Schema, any>) => AliasedRawBuilder<Required<AccessControl>[], 'acl'>;
|
|
59
59
|
export declare function get(itemType: TargetName, itemId: string): Promise<Required<AccessControlInternal>[]>;
|
|
60
|
+
export declare function set(itemType: TargetName, itemId: string, data: AccessMap): Promise<AccessControlInternal[]>;
|
|
60
61
|
export {};
|
package/dist/acl.js
CHANGED
|
@@ -64,3 +64,21 @@ export function from(table, opt = {}) {
|
|
|
64
64
|
export async function get(itemType, itemId) {
|
|
65
65
|
return await db.database.selectFrom(`acl.${itemType}`).where('itemId', '=', itemId).selectAll().select(db.userFromId).execute();
|
|
66
66
|
}
|
|
67
|
+
export async function set(itemType, itemId, data) {
|
|
68
|
+
if ('public' in data) {
|
|
69
|
+
// @ts-expect-error 2353 - TS misses the column
|
|
70
|
+
await db.database.updateTable(itemType).set({ publicPermission: data.public }).where('id', '=', itemId).execute();
|
|
71
|
+
delete data.public;
|
|
72
|
+
}
|
|
73
|
+
const entries = Object.entries(data).map(([userId, perm]) => ({ userId, perm }));
|
|
74
|
+
if (!entries.length)
|
|
75
|
+
return [];
|
|
76
|
+
return await db.database
|
|
77
|
+
.updateTable(`acl.${itemType}`)
|
|
78
|
+
.from(db.values(entries, 'data'))
|
|
79
|
+
.set('permission', eb => eb.ref('data.perm'))
|
|
80
|
+
.whereRef(`acl.${itemType}.userId`, '=', 'data.userId')
|
|
81
|
+
.where('itemId', '=', itemId)
|
|
82
|
+
.returningAll()
|
|
83
|
+
.execute();
|
|
84
|
+
}
|
package/dist/api/acl.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AccessMap } from '@axium/core/access';
|
|
2
2
|
import * as z from 'zod';
|
|
3
3
|
import * as acl from '../acl.js';
|
|
4
4
|
import { parseBody, withError } from '../requests.js';
|
|
@@ -9,11 +9,11 @@ addRoute({
|
|
|
9
9
|
itemType: z.string(),
|
|
10
10
|
itemId: z.uuid(),
|
|
11
11
|
},
|
|
12
|
-
async
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
return
|
|
12
|
+
async GET(request, { itemType, itemId }) {
|
|
13
|
+
return await acl.get(itemType, itemId).catch(withError('Failed to get access controls'));
|
|
14
|
+
},
|
|
15
|
+
async POST(request, { itemType, itemId }) {
|
|
16
|
+
const data = await parseBody(request, AccessMap);
|
|
17
|
+
return await acl.set(itemType, itemId, data).catch(withError('Failed to set access controls'));
|
|
18
18
|
},
|
|
19
19
|
});
|
package/dist/api/admin.js
CHANGED
|
@@ -55,9 +55,9 @@ addRoute({
|
|
|
55
55
|
addRoute({
|
|
56
56
|
path: '/api/admin/users/:userId',
|
|
57
57
|
params: { userId: z.uuid() },
|
|
58
|
-
async GET(req,
|
|
58
|
+
async GET(req, { userId }) {
|
|
59
59
|
await assertAdmin(this, req);
|
|
60
|
-
if (!
|
|
60
|
+
if (!userId)
|
|
61
61
|
error(400, 'Missing user ID');
|
|
62
62
|
const user = await db
|
|
63
63
|
.selectFrom('users')
|
|
@@ -65,7 +65,7 @@ addRoute({
|
|
|
65
65
|
.select(eb => jsonArrayFrom(eb.selectFrom('sessions').whereRef('sessions.userId', '=', 'users.id').selectAll())
|
|
66
66
|
.$castTo()
|
|
67
67
|
.as('sessions'))
|
|
68
|
-
.where('id', '=',
|
|
68
|
+
.where('id', '=', userId)
|
|
69
69
|
.executeTakeFirstOrThrow()
|
|
70
70
|
.catch(withError('User not found', 404));
|
|
71
71
|
return {
|
|
@@ -134,9 +134,9 @@ addRoute({
|
|
|
134
134
|
addRoute({
|
|
135
135
|
path: '/api/admin/audit/:eventId',
|
|
136
136
|
params: { eventId: z.uuid() },
|
|
137
|
-
async GET(req,
|
|
137
|
+
async GET(req, { eventId }) {
|
|
138
138
|
await assertAdmin(this, req);
|
|
139
|
-
if (!
|
|
139
|
+
if (!eventId)
|
|
140
140
|
error(400, 'Missing event ID');
|
|
141
141
|
const event = await db
|
|
142
142
|
.selectFrom('audit_log')
|
|
@@ -144,7 +144,7 @@ addRoute({
|
|
|
144
144
|
.select(eb => jsonObjectFrom(eb.selectFrom('users').whereRef('users.id', '=', 'audit_log.userId').selectAll())
|
|
145
145
|
.$castTo()
|
|
146
146
|
.as('user'))
|
|
147
|
-
.where('id', '=',
|
|
147
|
+
.where('id', '=', eventId)
|
|
148
148
|
.executeTakeFirstOrThrow()
|
|
149
149
|
.catch(withError('Audit event not found', 404));
|
|
150
150
|
return event;
|
package/dist/api/passkeys.js
CHANGED
|
@@ -10,14 +10,14 @@ addRoute({
|
|
|
10
10
|
params: {
|
|
11
11
|
id: z.string(),
|
|
12
12
|
},
|
|
13
|
-
async GET(request,
|
|
14
|
-
const passkey = await getPasskey(
|
|
13
|
+
async GET(request, { id }) {
|
|
14
|
+
const passkey = await getPasskey(id);
|
|
15
15
|
await checkAuthForUser(request, passkey.userId);
|
|
16
16
|
return omit(passkey, 'counter', 'publicKey');
|
|
17
17
|
},
|
|
18
|
-
async PATCH(request,
|
|
18
|
+
async PATCH(request, { id }) {
|
|
19
19
|
const body = await parseBody(request, PasskeyChangeable);
|
|
20
|
-
const passkey = await getPasskey(
|
|
20
|
+
const passkey = await getPasskey(id);
|
|
21
21
|
await checkAuthForUser(request, passkey.userId);
|
|
22
22
|
const result = await db
|
|
23
23
|
.updateTable('passkeys')
|
|
@@ -28,8 +28,8 @@ addRoute({
|
|
|
28
28
|
.catch(withError('Could not update passkey'));
|
|
29
29
|
return omit(result, 'counter', 'publicKey');
|
|
30
30
|
},
|
|
31
|
-
async DELETE(request,
|
|
32
|
-
const passkey = await getPasskey(
|
|
31
|
+
async DELETE(request, { id }) {
|
|
32
|
+
const passkey = await getPasskey(id);
|
|
33
33
|
await checkAuthForUser(request, passkey.userId);
|
|
34
34
|
const { count } = await db
|
|
35
35
|
.selectFrom('passkeys')
|
package/dist/api/register.js
CHANGED
|
@@ -48,7 +48,7 @@ async function POST(request) {
|
|
|
48
48
|
const { verified, registrationInfo } = await verifyRegistrationResponse({
|
|
49
49
|
response,
|
|
50
50
|
expectedChallenge,
|
|
51
|
-
expectedOrigin: config.
|
|
51
|
+
expectedOrigin: config.origin,
|
|
52
52
|
}).catch(() => error(400, 'Verification failed'));
|
|
53
53
|
if (!verified || !registrationInfo)
|
|
54
54
|
error(401, 'Verification failed');
|
package/dist/api/users.js
CHANGED
|
@@ -32,14 +32,12 @@ addRoute({
|
|
|
32
32
|
addRoute({
|
|
33
33
|
path: '/api/users/:id',
|
|
34
34
|
params,
|
|
35
|
-
async GET(request,
|
|
36
|
-
const userId = params.id;
|
|
35
|
+
async GET(request, { id: userId }) {
|
|
37
36
|
const auth = await checkAuthForUser(request, userId).catch(() => null);
|
|
38
37
|
const user = auth?.user || (await getUser(userId).catch(withError('User does not exist', 404)));
|
|
39
38
|
return stripUser(user, !!auth);
|
|
40
39
|
},
|
|
41
|
-
async PATCH(request,
|
|
42
|
-
const userId = params.id;
|
|
40
|
+
async PATCH(request, { id: userId }) {
|
|
43
41
|
const body = await parseBody(request, UserChangeable);
|
|
44
42
|
await checkAuthForUser(request, userId);
|
|
45
43
|
if ('email' in body)
|
|
@@ -55,8 +53,7 @@ addRoute({
|
|
|
55
53
|
.catch(withError('Failed to update user'));
|
|
56
54
|
return stripUser(result, true);
|
|
57
55
|
},
|
|
58
|
-
async DELETE(request,
|
|
59
|
-
const userId = params.id;
|
|
56
|
+
async DELETE(request, { id: userId }) {
|
|
60
57
|
await checkAuthForUser(request, userId, true);
|
|
61
58
|
const result = await db
|
|
62
59
|
.deleteFrom('users')
|
|
@@ -71,8 +68,7 @@ addRoute({
|
|
|
71
68
|
addRoute({
|
|
72
69
|
path: '/api/users/:id/full',
|
|
73
70
|
params,
|
|
74
|
-
async GET(request,
|
|
75
|
-
const userId = params.id;
|
|
71
|
+
async GET(request, { id: userId }) {
|
|
76
72
|
const { user } = await checkAuthForUser(request, userId);
|
|
77
73
|
const sessions = await getSessions(userId);
|
|
78
74
|
return {
|
|
@@ -84,8 +80,7 @@ addRoute({
|
|
|
84
80
|
addRoute({
|
|
85
81
|
path: '/api/users/:id/auth',
|
|
86
82
|
params,
|
|
87
|
-
async OPTIONS(request,
|
|
88
|
-
const userId = params.id;
|
|
83
|
+
async OPTIONS(request, { id: userId }) {
|
|
89
84
|
const { type } = await parseBody(request, UserAuthOptions);
|
|
90
85
|
const user = await getUser(userId).catch(withError('User does not exist', 404));
|
|
91
86
|
if (user.isSuspended)
|
|
@@ -100,8 +95,7 @@ addRoute({
|
|
|
100
95
|
challenges.set(userId, { data: options.challenge, type });
|
|
101
96
|
return options;
|
|
102
97
|
},
|
|
103
|
-
async POST(request,
|
|
104
|
-
const userId = params.id;
|
|
98
|
+
async POST(request, { id: userId }) {
|
|
105
99
|
const response = await parseBody(request, PasskeyAuthenticationResponse);
|
|
106
100
|
const auth = challenges.get(userId);
|
|
107
101
|
if (!auth)
|
|
@@ -116,7 +110,7 @@ addRoute({
|
|
|
116
110
|
response,
|
|
117
111
|
credential: passkey,
|
|
118
112
|
expectedChallenge,
|
|
119
|
-
expectedOrigin: config.
|
|
113
|
+
expectedOrigin: config.origin,
|
|
120
114
|
expectedRPID: config.auth.rp_id,
|
|
121
115
|
})
|
|
122
116
|
.catch(withError('Verification failed', 400));
|
|
@@ -143,8 +137,7 @@ addRoute({
|
|
|
143
137
|
/**
|
|
144
138
|
* Get passkey registration options for a user.
|
|
145
139
|
*/
|
|
146
|
-
async OPTIONS(request,
|
|
147
|
-
const userId = params.id;
|
|
140
|
+
async OPTIONS(request, { id: userId }) {
|
|
148
141
|
const existing = await getPasskeysByUserId(userId);
|
|
149
142
|
const { user } = await checkAuthForUser(request, userId);
|
|
150
143
|
const options = await webauthn.generateRegistrationOptions({
|
|
@@ -166,8 +159,7 @@ addRoute({
|
|
|
166
159
|
/**
|
|
167
160
|
* Get passkeys for a user.
|
|
168
161
|
*/
|
|
169
|
-
async GET(request,
|
|
170
|
-
const userId = params.id;
|
|
162
|
+
async GET(request, { id: userId }) {
|
|
171
163
|
await checkAuthForUser(request, userId);
|
|
172
164
|
const passkeys = await getPasskeysByUserId(userId);
|
|
173
165
|
return passkeys.map(p => omit(p, 'publicKey', 'counter'));
|
|
@@ -175,8 +167,7 @@ addRoute({
|
|
|
175
167
|
/**
|
|
176
168
|
* Register a new passkey for an existing user.
|
|
177
169
|
*/
|
|
178
|
-
async PUT(request,
|
|
179
|
-
const userId = params.id;
|
|
170
|
+
async PUT(request, { id: userId }) {
|
|
180
171
|
const response = await parseBody(request, PasskeyRegistration);
|
|
181
172
|
await checkAuthForUser(request, userId);
|
|
182
173
|
const expectedChallenge = registrations.get(userId);
|
|
@@ -187,7 +178,7 @@ addRoute({
|
|
|
187
178
|
.verifyRegistrationResponse({
|
|
188
179
|
response,
|
|
189
180
|
expectedChallenge,
|
|
190
|
-
expectedOrigin: config.
|
|
181
|
+
expectedOrigin: config.origin,
|
|
191
182
|
})
|
|
192
183
|
.catch(withError('Verification failed', 400));
|
|
193
184
|
if (!verified || !registrationInfo)
|
|
@@ -205,13 +196,11 @@ addRoute({
|
|
|
205
196
|
addRoute({
|
|
206
197
|
path: '/api/users/:id/sessions',
|
|
207
198
|
params,
|
|
208
|
-
async GET(request,
|
|
209
|
-
const userId = params.id;
|
|
199
|
+
async GET(request, { id: userId }) {
|
|
210
200
|
await checkAuthForUser(request, userId);
|
|
211
201
|
return (await getSessions(userId).catch(e => error(503, 'Failed to get sessions' + (config.debug ? ': ' + e : '')))).map(s => omit(s, 'token'));
|
|
212
202
|
},
|
|
213
|
-
async DELETE(request,
|
|
214
|
-
const userId = params.id;
|
|
203
|
+
async DELETE(request, { id: userId }) {
|
|
215
204
|
const body = await parseBody(request, LogoutSessions);
|
|
216
205
|
await checkAuthForUser(request, userId, body.confirm_all);
|
|
217
206
|
if (!body.confirm_all && !Array.isArray(body.id))
|
|
@@ -229,8 +218,7 @@ addRoute({
|
|
|
229
218
|
addRoute({
|
|
230
219
|
path: '/api/users/:id/verify_email',
|
|
231
220
|
params,
|
|
232
|
-
async OPTIONS(request,
|
|
233
|
-
const userId = params.id;
|
|
221
|
+
async OPTIONS(request, { id: userId }) {
|
|
234
222
|
if (!config.auth.email_verification)
|
|
235
223
|
return { enabled: false };
|
|
236
224
|
await checkAuthForUser(request, userId);
|
|
@@ -238,16 +226,14 @@ addRoute({
|
|
|
238
226
|
return { enabled: false };
|
|
239
227
|
return { enabled: true };
|
|
240
228
|
},
|
|
241
|
-
async GET(request,
|
|
242
|
-
const userId = params.id;
|
|
229
|
+
async GET(request, { id: userId }) {
|
|
243
230
|
const { user } = await checkAuthForUser(request, userId);
|
|
244
231
|
if (user.emailVerified)
|
|
245
232
|
error(409, 'Email already verified');
|
|
246
233
|
const verification = await createVerification('verify_email', userId, config.auth.verification_timeout * 60);
|
|
247
234
|
return omit(verification, 'token', 'role');
|
|
248
235
|
},
|
|
249
|
-
async POST(request,
|
|
250
|
-
const userId = params.id;
|
|
236
|
+
async POST(request, { id: userId }) {
|
|
251
237
|
const { token } = await parseBody(request, z.object({ token: z.string() }));
|
|
252
238
|
const { user } = await checkAuthForUser(request, userId);
|
|
253
239
|
if (user.emailVerified)
|
package/dist/audit.d.ts
CHANGED
package/dist/audit.js
CHANGED
|
@@ -74,7 +74,7 @@ export async function audit(eventName, userId, extra) {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
export function getEvents(filter) {
|
|
77
|
-
let query = database.selectFrom('audit_log').selectAll();
|
|
77
|
+
let query = database.selectFrom('audit_log').selectAll().orderBy('timestamp', 'desc');
|
|
78
78
|
if ('user' in filter && !filter.user)
|
|
79
79
|
query = query.where('userId', 'is', null);
|
|
80
80
|
else if (filter.user)
|
|
@@ -120,3 +120,10 @@ addEvent({
|
|
|
120
120
|
tags: ['auth'],
|
|
121
121
|
extra: { route: z.string(), session: z.string() },
|
|
122
122
|
});
|
|
123
|
+
addEvent({
|
|
124
|
+
source: '@axium/server',
|
|
125
|
+
name: 'response_error',
|
|
126
|
+
severity: Severity.Error,
|
|
127
|
+
tags: [],
|
|
128
|
+
extra: { stack: z.string().optional() },
|
|
129
|
+
});
|
package/dist/cli.js
CHANGED
|
@@ -529,8 +529,9 @@ try {
|
|
|
529
529
|
.addOption(opts.host)
|
|
530
530
|
.addOption(opts.check)
|
|
531
531
|
.addOption(opts.packagesDir)
|
|
532
|
+
.option('-s, --skip', 'Skip already initialized steps')
|
|
532
533
|
.action(async (opt) => {
|
|
533
|
-
await db.init(
|
|
534
|
+
await db.init(opt).catch(io.handleError);
|
|
534
535
|
await restrictedPorts({ method: 'node-cap', action: 'enable' }).catch(io.handleError);
|
|
535
536
|
});
|
|
536
537
|
program
|
|
@@ -546,7 +547,7 @@ try {
|
|
|
546
547
|
ssl_key: opt.ssl ? join(opt.ssl, 'key.pem') : config.web.ssl_key,
|
|
547
548
|
build: opt.build ? resolve(opt.build) : config.web.build,
|
|
548
549
|
});
|
|
549
|
-
const port = !Number.isNaN(Number.parseInt(opt.port ?? '')) ? Number.parseInt(opt.port) : config.web.port;
|
|
550
|
+
const port = !Number.isNaN(Number.parseInt(opt.port ?? 'NaN')) ? Number.parseInt(opt.port) : config.web.port;
|
|
550
551
|
server.listen(port, () => {
|
|
551
552
|
console.log('Server is listening on port ' + port);
|
|
552
553
|
});
|
package/dist/config.d.ts
CHANGED
|
@@ -13,7 +13,6 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
13
13
|
auto_suspend: z.ZodOptional<z.ZodLiteral<"Emergency" | "Alert" | "Critical" | "Error" | "Warning" | "Notice" | "Info" | "Debug" | "emergency" | "alert" | "critical" | "error" | "warning" | "notice" | "info" | "debug">>;
|
|
14
14
|
}, z.core.$loose>>;
|
|
15
15
|
auth: z.ZodOptional<z.ZodObject<{
|
|
16
|
-
origin: z.ZodOptional<z.ZodString>;
|
|
17
16
|
passkey_probation: z.ZodOptional<z.ZodNumber>;
|
|
18
17
|
rp_id: z.ZodOptional<z.ZodString>;
|
|
19
18
|
rp_name: z.ZodOptional<z.ZodString>;
|
|
@@ -41,6 +40,7 @@ export declare const ConfigSchema: z.ZodObject<{
|
|
|
41
40
|
}>>;
|
|
42
41
|
console: z.ZodOptional<z.ZodBoolean>;
|
|
43
42
|
}, z.core.$loose>>;
|
|
43
|
+
origin: z.ZodOptional<z.ZodString>;
|
|
44
44
|
request_size_limit: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
|
|
45
45
|
show_duplicate_state: z.ZodOptional<z.ZodBoolean>;
|
|
46
46
|
web: z.ZodOptional<z.ZodObject<{
|
|
@@ -87,7 +87,6 @@ export declare const FileSchema: z.ZodObject<{
|
|
|
87
87
|
auto_suspend: z.ZodOptional<z.ZodLiteral<"Emergency" | "Alert" | "Critical" | "Error" | "Warning" | "Notice" | "Info" | "Debug" | "emergency" | "alert" | "critical" | "error" | "warning" | "notice" | "info" | "debug">>;
|
|
88
88
|
}, z.core.$loose>>>;
|
|
89
89
|
auth: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
90
|
-
origin: z.ZodOptional<z.ZodString>;
|
|
91
90
|
passkey_probation: z.ZodOptional<z.ZodNumber>;
|
|
92
91
|
rp_id: z.ZodOptional<z.ZodString>;
|
|
93
92
|
rp_name: z.ZodOptional<z.ZodString>;
|
|
@@ -115,6 +114,7 @@ export declare const FileSchema: z.ZodObject<{
|
|
|
115
114
|
}>>;
|
|
116
115
|
console: z.ZodOptional<z.ZodBoolean>;
|
|
117
116
|
}, z.core.$loose>>>;
|
|
117
|
+
origin: z.ZodOptional<z.ZodOptional<z.ZodString>>;
|
|
118
118
|
request_size_limit: z.ZodOptional<z.ZodOptional<z.ZodOptional<z.ZodNumber>>>;
|
|
119
119
|
show_duplicate_state: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
|
|
120
120
|
web: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
package/dist/config.js
CHANGED
|
@@ -30,7 +30,6 @@ export const ConfigSchema = z
|
|
|
30
30
|
.partial(),
|
|
31
31
|
auth: z
|
|
32
32
|
.looseObject({
|
|
33
|
-
origin: z.string(),
|
|
34
33
|
/** In minutes */
|
|
35
34
|
passkey_probation: z.number(),
|
|
36
35
|
rp_id: z.string(),
|
|
@@ -62,6 +61,7 @@ export const ConfigSchema = z
|
|
|
62
61
|
console: z.boolean(),
|
|
63
62
|
})
|
|
64
63
|
.partial(),
|
|
64
|
+
origin: z.string(),
|
|
65
65
|
request_size_limit: z.number().min(0).optional(),
|
|
66
66
|
show_duplicate_state: z.boolean(),
|
|
67
67
|
web: z
|
|
@@ -95,7 +95,6 @@ export const defaultConfig = {
|
|
|
95
95
|
auto_suspend: 'critical',
|
|
96
96
|
},
|
|
97
97
|
auth: {
|
|
98
|
-
origin: 'https://test.localhost',
|
|
99
98
|
passkey_probation: 60,
|
|
100
99
|
rp_id: 'test.localhost',
|
|
101
100
|
rp_name: 'Axium',
|
|
@@ -122,6 +121,7 @@ export const defaultConfig = {
|
|
|
122
121
|
console: true,
|
|
123
122
|
level: 'info',
|
|
124
123
|
},
|
|
124
|
+
origin: 'https://test.localhost',
|
|
125
125
|
show_duplicate_state: false,
|
|
126
126
|
request_size_limit: 0,
|
|
127
127
|
web: {
|
package/dist/database.d.ts
CHANGED
|
@@ -75,6 +75,10 @@ export type TablesMatching<T> = (string & keyof Schema) & keyof {
|
|
|
75
75
|
export declare function userFromId<TB extends TablesMatching<{
|
|
76
76
|
userId: string;
|
|
77
77
|
}>>(builder: kysely.ExpressionBuilder<Schema, TB>): kysely.AliasedRawBuilder<UserInternal, 'user' | TB>;
|
|
78
|
+
/**
|
|
79
|
+
* Used for `update ... set ... from`
|
|
80
|
+
*/
|
|
81
|
+
export declare function values<R extends Record<string, unknown>, A extends string>(records: R[], alias: A): kysely.AliasedRawBuilder<R, A>;
|
|
78
82
|
export interface Stats {
|
|
79
83
|
users: number;
|
|
80
84
|
passkeys: number;
|
package/dist/database.js
CHANGED
|
@@ -93,6 +93,30 @@ export function userFromId(builder) {
|
|
|
93
93
|
.$castTo()
|
|
94
94
|
.as('user');
|
|
95
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Used for `update ... set ... from`
|
|
98
|
+
*/
|
|
99
|
+
export function values(records, alias) {
|
|
100
|
+
if (!records?.length)
|
|
101
|
+
throw new Error('Can not create values() with empty records array');
|
|
102
|
+
// Assume there's at least one record and all records
|
|
103
|
+
// have the same keys.
|
|
104
|
+
const keys = Object.keys(records[0]);
|
|
105
|
+
// Transform the records into a list of lists such as
|
|
106
|
+
// ($1, $2, $3), ($4, $5, $6)
|
|
107
|
+
const values = sql.join(records.map(r => sql `(${sql.join(keys.map(k => r[k]))})`));
|
|
108
|
+
// Create the alias `v(id, v1, v2)` that specifies the table alias
|
|
109
|
+
// AND a name for each column.
|
|
110
|
+
const wrappedAlias = sql.ref(alias);
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
112
|
+
const wrappedColumns = sql.join(keys.map(sql.ref));
|
|
113
|
+
const aliasSql = sql `${wrappedAlias}(${wrappedColumns})`;
|
|
114
|
+
// Finally create a single `AliasedRawBuilder` instance of the
|
|
115
|
+
// whole thing. Note that we need to explicitly specify
|
|
116
|
+
// the alias type using `.as<A>` because we are using a
|
|
117
|
+
// raw sql snippet as the alias.
|
|
118
|
+
return sql `(values ${values})`.as(aliasSql);
|
|
119
|
+
}
|
|
96
120
|
export async function statText() {
|
|
97
121
|
try {
|
|
98
122
|
const stats = await count('users', 'passkeys', 'sessions');
|
|
@@ -60,7 +60,7 @@ function get_raw_body(req) {
|
|
|
60
60
|
}
|
|
61
61
|
export function convertToRequest(req) {
|
|
62
62
|
const headers = req.headers;
|
|
63
|
-
const request = new Request(config.
|
|
63
|
+
const request = new Request(config.origin + req.url, {
|
|
64
64
|
// @ts-expect-error 2353
|
|
65
65
|
duplex: 'half',
|
|
66
66
|
method: req.method,
|
package/dist/requests.d.ts
CHANGED
package/dist/requests.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import {} from '@axium/core';
|
|
2
|
+
import * as io from '@axium/core/io';
|
|
1
3
|
import { userProtectedFields, userPublicFields } from '@axium/core/user';
|
|
2
4
|
import * as cookie from 'cookie_v1';
|
|
3
5
|
import { pick } from 'utilium';
|
|
4
6
|
import * as z from 'zod';
|
|
7
|
+
import { audit } from './audit.js';
|
|
5
8
|
import { createSession } from './auth.js';
|
|
6
9
|
import { config } from './config.js';
|
|
7
10
|
export function isResponseError(e) {
|
|
@@ -25,7 +28,7 @@ export function redirect(location, status = 302) {
|
|
|
25
28
|
export function json(data, init) {
|
|
26
29
|
const response = Response.json(data, init);
|
|
27
30
|
if (!response.headers.has('content-length')) {
|
|
28
|
-
response.headers.set('content-length', JSON.stringify(data).length.toString());
|
|
31
|
+
response.headers.set('content-length', new TextEncoder().encode(JSON.stringify(data)).length.toString());
|
|
29
32
|
}
|
|
30
33
|
return response;
|
|
31
34
|
}
|
|
@@ -71,6 +74,10 @@ export function withError(text, code = 500) {
|
|
|
71
74
|
return function (e) {
|
|
72
75
|
if (e.name == 'ResponseError')
|
|
73
76
|
throw e;
|
|
77
|
+
if (code == 500) {
|
|
78
|
+
void audit('response_error', undefined, { stack: e.stack });
|
|
79
|
+
io.error('(in response) ' + e.stack);
|
|
80
|
+
}
|
|
74
81
|
error(code, text + (config.debug && e.message ? `: ${e.message}` : ''));
|
|
75
82
|
};
|
|
76
83
|
}
|
package/dist/routes.d.ts
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
1
|
import type { RequestMethod } from '@axium/core/requests';
|
|
2
2
|
import type { Component } from 'svelte';
|
|
3
3
|
import type z from 'zod';
|
|
4
|
-
type
|
|
4
|
+
type RouteParams = Record<string, z.ZodType>;
|
|
5
|
+
type ParamValues<P extends RouteParams> = {
|
|
6
|
+
[K in keyof P]: z.infer<P[K]>;
|
|
7
|
+
};
|
|
5
8
|
export type MaybePromise<T> = T | Promise<T>;
|
|
6
|
-
export type EndpointHandlers<Params
|
|
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
|
-
}
|
|
9
|
+
export type EndpointHandlers<Params, This = unknown> = Partial<Record<RequestMethod, (this: This, request: Request, params: Params) => MaybePromise<object | Response>>>;
|
|
14
10
|
/**
|
|
15
11
|
* A route with server-side handlers for different HTTP methods.
|
|
16
12
|
*/
|
|
17
|
-
export interface
|
|
13
|
+
export interface ServerRouteInit<Params extends RouteParams = RouteParams> extends EndpointHandlers<ParamValues<Params>, RouteCommon<Params>> {
|
|
14
|
+
path: string;
|
|
15
|
+
params?: Params;
|
|
18
16
|
api?: boolean;
|
|
19
17
|
}
|
|
20
|
-
export interface
|
|
18
|
+
export interface WebRouteInit<Params extends RouteParams = RouteParams> {
|
|
19
|
+
path: string;
|
|
20
|
+
params?: Params;
|
|
21
21
|
load?(request: Request): object | Promise<object>;
|
|
22
22
|
/** the Svelte page */
|
|
23
23
|
page?: Component;
|
|
24
24
|
}
|
|
25
|
-
export type
|
|
26
|
-
export interface RouteCommon {
|
|
25
|
+
export type RouteInit<Params extends RouteParams = RouteParams> = ServerRouteInit<Params> | WebRouteInit<Params>;
|
|
26
|
+
export interface RouteCommon<Params extends RouteParams = RouteParams> {
|
|
27
27
|
path: string;
|
|
28
|
-
params?:
|
|
28
|
+
params?: Params;
|
|
29
29
|
}
|
|
30
|
-
export interface ServerRoute extends RouteCommon
|
|
30
|
+
export interface ServerRoute<Params extends RouteParams = RouteParams> extends RouteCommon<Params>, EndpointHandlers<ParamValues<Params>> {
|
|
31
31
|
api: boolean;
|
|
32
32
|
server: true;
|
|
33
33
|
}
|
|
34
|
-
export interface WebRoute extends RouteCommon {
|
|
34
|
+
export interface WebRoute<Params extends RouteParams = RouteParams> extends RouteCommon<Params> {
|
|
35
35
|
server: false;
|
|
36
36
|
load?(request: Request): object | Promise<object>;
|
|
37
37
|
page: Component;
|
|
38
38
|
}
|
|
39
|
-
export type Route = ServerRoute | WebRoute
|
|
39
|
+
export type Route<Params extends RouteParams = RouteParams> = ServerRoute<Params> | WebRoute<Params>;
|
|
40
40
|
/**
|
|
41
41
|
* @internal
|
|
42
42
|
*/
|
|
43
|
-
export declare const routes: Map<string, Route
|
|
43
|
+
export declare const routes: Map<string, Route<any>>;
|
|
44
44
|
/**
|
|
45
45
|
* @category Plugin API
|
|
46
46
|
*/
|
|
47
|
-
export declare function addRoute(opt:
|
|
47
|
+
export declare function addRoute<const P extends RouteParams = RouteParams>(opt: RouteInit<P>): void;
|
|
48
48
|
/**
|
|
49
49
|
* Resolve a request URL into a route.
|
|
50
50
|
* This handles parsing of parameters in the URL.
|
|
51
51
|
*/
|
|
52
|
-
export declare function resolveRoute(url: URL): [Route
|
|
52
|
+
export declare function resolveRoute<P extends RouteParams = RouteParams>(url: URL): [Route<P>, params: ParamValues<P>] | void;
|
|
53
53
|
export {};
|
package/dist/routes.js
CHANGED
|
@@ -27,8 +27,9 @@ export function addRoute(opt) {
|
|
|
27
27
|
*/
|
|
28
28
|
export function resolveRoute(url) {
|
|
29
29
|
const { pathname } = url;
|
|
30
|
-
if (routes.has(pathname) && !pathname.split('/').some(p => p.startsWith(':')))
|
|
30
|
+
if (routes.has(pathname) && !pathname.split('/').some(p => p.startsWith(':'))) {
|
|
31
31
|
return [routes.get(pathname), {}];
|
|
32
|
+
}
|
|
32
33
|
// Otherwise we must have a parameterized route
|
|
33
34
|
_routes: for (const route of routes.values()) {
|
|
34
35
|
const params = {};
|
package/dist/serve.js
CHANGED
|
@@ -131,7 +131,7 @@ async function _runRoute(run, request, params) {
|
|
|
131
131
|
async function _getLinkedBuildHandler(buildPath = '../build/handler.js') {
|
|
132
132
|
const { handler: handleFrontendRequest } = await import(buildPath);
|
|
133
133
|
return function handle(req, res) {
|
|
134
|
-
const url = new URL(req.url, config.
|
|
134
|
+
const url = new URL(req.url, config.origin);
|
|
135
135
|
const [route, params = {}] = resolveRoute(url) ?? [];
|
|
136
136
|
if (!route && url.pathname === '/' && config.debug_home) {
|
|
137
137
|
res.writeHead(303, { Location: '/_axium/default' }).end();
|
package/package.json
CHANGED
|
@@ -15,7 +15,11 @@
|
|
|
15
15
|
<p><strong>Author:</strong> {plugin.author}</p>
|
|
16
16
|
<p>
|
|
17
17
|
<strong>Provided apps:</strong>
|
|
18
|
-
{#if plugin.apps?.length}
|
|
18
|
+
{#if plugin.apps?.length}
|
|
19
|
+
{#each plugin.apps as app, i}
|
|
20
|
+
<a href="/{app.id}">{app.name}</a>{i != plugin.apps.length - 1 ? ', ' : ''}
|
|
21
|
+
{/each}
|
|
22
|
+
{:else}<i>None</i>{/if}
|
|
19
23
|
</p>
|
|
20
24
|
<p>{plugin.description}</p>
|
|
21
25
|
{:else}
|