@axium/server 0.41.1 → 0.42.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/api/admin.js CHANGED
@@ -66,7 +66,7 @@ addRoute({
66
66
  error(404, 'Plugin not found');
67
67
  }
68
68
  if (config) {
69
- const { schema } = serverConfigs.get(name) || {};
69
+ const schema = serverConfigs.get(name) || null;
70
70
  if (!schema)
71
71
  error(400, 'Plugin does not have a configuration schema');
72
72
  if (!plugin._configPath)
@@ -0,0 +1,11 @@
1
+ export interface ImageUploadConfig {
2
+ enabled: boolean;
3
+ /** Max size in KB */
4
+ max_size: number;
5
+ /** Max pixels per dimension */
6
+ max_length: number;
7
+ }
8
+ export declare function checkImageUpload(request: Request, cfg: ImageUploadConfig, userId: string): Promise<{
9
+ data: Uint8Array<ArrayBuffer>;
10
+ type: string;
11
+ }>;
@@ -26,6 +26,31 @@ catch {
26
26
  warn('Can not determine profile picture dimensions because neither image-size or ImageMagick is available');
27
27
  }
28
28
  }
29
+ export async function checkImageUpload(request, cfg, userId) {
30
+ const { enabled, max_size, max_length } = cfg;
31
+ if (!enabled)
32
+ error(503, 'Image uploads are disabled');
33
+ await checkAuthForUser(request, userId);
34
+ const type = request.headers.get('content-type');
35
+ if (!type)
36
+ error(400, 'Missing Content-Type header');
37
+ if (!type.startsWith('image/'))
38
+ error(415, 'Only image files are allowed');
39
+ const size = Number(request.headers.get('content-length'));
40
+ if (!Number.isSafeInteger(size))
41
+ error(400, 'Invalid Content-Length header');
42
+ if (max_size && size / 1000 > max_size)
43
+ error(413, `Image must be smaller than ${max_size} KB`);
44
+ const data = await request.bytes();
45
+ if (data.byteLength != size)
46
+ error(400, 'Content-Length does not match actual data size');
47
+ const { width, height } = imageSize?.(data) || { width: 0, height: 0 };
48
+ if (imageSize && (!width || !height))
49
+ error(400, 'Invalid image dimensions');
50
+ if (max_length && (width > max_length || height > max_length))
51
+ error(413, `Image must be smaller than ${max_length}x${max_length} pixels`);
52
+ return { data, type };
53
+ }
29
54
  addRoute({
30
55
  path: '/raw/pfp/:id',
31
56
  params: { id: z.uuid() },
@@ -76,28 +101,7 @@ addRoute({
76
101
  });
77
102
  },
78
103
  async POST(request, { id: userId }) {
79
- const { enabled, max_size, max_length } = config.user_pfp;
80
- if (!enabled)
81
- error(503, 'Custom profile pictures are disabled');
82
- await checkAuthForUser(request, userId);
83
- const type = request.headers.get('content-type');
84
- if (!type)
85
- error(400, 'Missing Content-Type header');
86
- if (!type.startsWith('image/'))
87
- error(415, 'Only image files are allowed');
88
- const size = Number(request.headers.get('content-length'));
89
- if (!Number.isSafeInteger(size))
90
- error(400, 'Invalid Content-Length header');
91
- if (max_size && size / 1000 > max_size)
92
- error(413, `Profile picture must be smaller than ${max_size} KB`);
93
- const data = await request.bytes();
94
- if (data.byteLength != size)
95
- error(400, 'Content-Length does not match actual data size');
96
- const { width, height } = imageSize?.(data) || { width: 0, height: 0 };
97
- if (imageSize && (!width || !height))
98
- error(400, 'Invalid image dimensions');
99
- if (max_length && (width > max_length || height > max_length))
100
- error(413, `Profile picture must be smaller than ${max_length}x${max_length} pixels`);
104
+ const { data, type } = await checkImageUpload(request, config.user_pfp, userId);
101
105
  const { isInsert } = await db
102
106
  .insertInto('profile_pictures')
103
107
  .values({ userId, data, type })
@@ -2,7 +2,7 @@ import './acl.js';
2
2
  import './admin.js';
3
3
  import './metadata.js';
4
4
  import './passkeys.js';
5
- import './pfp.js';
5
+ import './images.js';
6
6
  import './register.js';
7
7
  import './session.js';
8
8
  import './users.js';
package/dist/api/index.js CHANGED
@@ -2,7 +2,7 @@ import './acl.js';
2
2
  import './admin.js';
3
3
  import './metadata.js';
4
4
  import './passkeys.js';
5
- import './pfp.js';
5
+ import './images.js';
6
6
  import './register.js';
7
7
  import './session.js';
8
8
  import './users.js';
package/dist/auth.js CHANGED
@@ -51,8 +51,8 @@ export async function getSession(sessionId) {
51
51
  export async function requireSession(request, sensitive = false) {
52
52
  const token = getToken(request, sensitive);
53
53
  if (!token)
54
- error(401, 'Missing session token');
55
- const session = await getSessionAndUser(token).catch(withError('Invalid or expired session token', 401));
54
+ error(401, 'Missing session (you are not logged in)');
55
+ const session = await getSessionAndUser(token).catch(withError('Invalid or expired session', 401));
56
56
  if (session.user.isSuspended)
57
57
  error(403, 'User is suspended');
58
58
  return session;
package/dist/config.js CHANGED
@@ -253,7 +253,7 @@ export async function loadConfig(path, options = {}) {
253
253
  if (!existsSync(configPath))
254
254
  continue;
255
255
  try {
256
- const data = io.readJSON(configPath, serverConfig.schema.partial());
256
+ const data = io.readJSON(configPath, serverConfig.partial());
257
257
  deepAssign(plugin.config, data, true);
258
258
  io.debug(`Loaded config for plugin ${plugin.name} from ${configPath}`);
259
259
  }
@@ -11,6 +11,11 @@ export function connect() {
11
11
  return (database = globalThis[sym]);
12
12
  database = new Kysely({
13
13
  dialect: new PostgresDialect({ pool: new pg.Pool(config.db) }),
14
+ log(event) {
15
+ if (event.level != 'error')
16
+ return;
17
+ io.error('Query failed:', event.query.sql);
18
+ },
14
19
  });
15
20
  globalThis[sym] = database;
16
21
  io.debug('Connected to database!');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axium/server",
3
- "version": "0.41.1",
3
+ "version": "0.42.1",
4
4
  "author": "James Prevett <axium@jamespre.dev>",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -48,9 +48,9 @@
48
48
  },
49
49
  "peerDependencies": {
50
50
  "@axium/client": ">=0.21.0",
51
- "@axium/core": ">=0.24.0",
51
+ "@axium/core": ">=0.26.0",
52
52
  "kysely": "^0.28.0",
53
- "utilium": "^2.6.0",
53
+ "utilium": "^3.0.0",
54
54
  "zod": "^4.0.5"
55
55
  },
56
56
  "dependencies": {
@@ -7,7 +7,7 @@
7
7
  import type { PageProps } from './$types';
8
8
  import { toast, toastStatus } from '@axium/client/toast';
9
9
  import { contextMenu } from '@axium/client/attachments';
10
- import { upload } from 'utilium/dom.js';
10
+ import { upload } from 'utilium/dom';
11
11
 
12
12
  const { data }: PageProps = $props();
13
13
  const { canVerify } = data;
@@ -16,7 +16,7 @@
16
16
  <h2>{text('page.admin.plugins.heading')}</h2>
17
17
 
18
18
  {#each data.plugins as plugin}
19
- {@const cfg = serverConfigs.get(plugin.name)}
19
+ {@const schema = serverConfigs.get(plugin.name)}
20
20
  <div class="plugin">
21
21
  <h3>
22
22
  {plugin.name}<Version
@@ -46,14 +46,12 @@
46
46
  {:else}<i>{text('generic.none')}</i>{/if}
47
47
  </p>
48
48
  <p>{plugin.description}</p>
49
- {#if cfg && plugin.config}
49
+ {#if schema && plugin.config}
50
50
  <h4>{text('page.admin.plugins.configuration')}</h4>
51
- {@const { schema, labels } = cfg}
52
51
  <ZodForm
53
52
  rootValue={plugin.config}
54
53
  idPrefix={plugin.name}
55
54
  {schema}
56
- {labels}
57
55
  updateValue={config => fetchAPI('POST', 'admin/plugins', { plugin: plugin.name, config })}
58
56
  />
59
57
  {/if}
package/dist/api/pfp.d.ts DELETED
@@ -1 +0,0 @@
1
- export {};