@axium/server 0.32.1 → 0.33.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 +29 -2
- package/dist/config.d.ts +8 -10
- package/dist/config.js +25 -21
- package/dist/database.d.ts +4 -4
- package/dist/database.js +2 -2
- package/dist/linking.d.ts +1 -0
- package/dist/linking.js +16 -2
- package/dist/main.js +15 -5
- package/package.json +3 -3
- package/routes/account/+page.svelte +10 -2
- package/routes/admin/plugins/+page.svelte +19 -1
- package/routes/admin/users/[id]/+page.svelte +9 -2
- package/schemas/config.json +3 -1
- package/schemas/db.json +3 -2
- package/svelte.config.js +1 -0
package/dist/api/admin.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { AuditFilter, Severity } from '@axium/core';
|
|
2
|
+
import { errorText, writeJSON } from '@axium/core/node/io';
|
|
2
3
|
import { getVersionInfo } from '@axium/core/node/packages';
|
|
3
|
-
import { plugins } from '@axium/core/plugins';
|
|
4
|
+
import { _findPlugin, plugins, PluginUpdate, serverConfigs } from '@axium/core/plugins';
|
|
4
5
|
import { jsonObjectFrom } from 'kysely/helpers/postgres';
|
|
5
|
-
import {
|
|
6
|
+
import { mkdirSync } from 'node:fs';
|
|
7
|
+
import { dirname } from 'node:path/posix';
|
|
8
|
+
import { deepAssign, omit } from 'utilium';
|
|
6
9
|
import * as z from 'zod';
|
|
7
10
|
import { audit, events, getEvents } from '../audit.js';
|
|
8
11
|
import { createVerification, requireSession } from '../auth.js';
|
|
@@ -52,6 +55,30 @@ addRoute({
|
|
|
52
55
|
.values()
|
|
53
56
|
.map(async (p) => Object.assign(omit(p, '_hooks', '_client'), p.update_checks ? await getVersionInfo(p.specifier, p.loadedBy) : { latest: null })));
|
|
54
57
|
},
|
|
58
|
+
async POST(req) {
|
|
59
|
+
await assertAdmin(this, req);
|
|
60
|
+
const { plugin: name, config } = await parseBody(req, PluginUpdate);
|
|
61
|
+
let plugin;
|
|
62
|
+
try {
|
|
63
|
+
plugin = _findPlugin(name);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
error(404, 'Plugin not found');
|
|
67
|
+
}
|
|
68
|
+
if (config) {
|
|
69
|
+
const { schema } = serverConfigs.get(name) || {};
|
|
70
|
+
if (!schema)
|
|
71
|
+
error(400, 'Plugin does not have a configuration schema');
|
|
72
|
+
if (!plugin._configPath)
|
|
73
|
+
error(503, 'Plugin configuration path is not set');
|
|
74
|
+
plugin.config ||= {};
|
|
75
|
+
const parsed = await schema.parseAsync(config).catch(e => error(400, errorText(e)));
|
|
76
|
+
deepAssign(plugin.config, parsed);
|
|
77
|
+
mkdirSync(dirname(plugin._configPath), { recursive: true });
|
|
78
|
+
writeJSON(plugin._configPath, plugin.config);
|
|
79
|
+
}
|
|
80
|
+
return {};
|
|
81
|
+
},
|
|
55
82
|
});
|
|
56
83
|
addRoute({
|
|
57
84
|
path: '/api/admin/users',
|
package/dist/config.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type DeepRequired } from 'utilium';
|
|
2
2
|
import * as z from 'zod';
|
|
3
|
-
export declare
|
|
3
|
+
export declare const Config: z.ZodObject<{
|
|
4
4
|
admin_api: z.ZodOptional<z.ZodBoolean>;
|
|
5
5
|
allow_new_users: z.ZodOptional<z.ZodBoolean>;
|
|
6
6
|
apps: z.ZodOptional<z.ZodObject<{
|
|
@@ -21,7 +21,7 @@ export declare let ConfigSchema: z.ZodObject<{
|
|
|
21
21
|
}, z.core.$loose>>;
|
|
22
22
|
db: z.ZodOptional<z.ZodObject<{
|
|
23
23
|
host: z.ZodOptional<z.ZodString>;
|
|
24
|
-
port: z.ZodOptional<z.
|
|
24
|
+
port: z.ZodOptional<z.ZodInt>;
|
|
25
25
|
password: z.ZodOptional<z.ZodString>;
|
|
26
26
|
user: z.ZodOptional<z.ZodString>;
|
|
27
27
|
database: z.ZodOptional<z.ZodString>;
|
|
@@ -56,10 +56,9 @@ export declare let ConfigSchema: z.ZodObject<{
|
|
|
56
56
|
build: z.ZodOptional<z.ZodString>;
|
|
57
57
|
}, z.core.$loose>>;
|
|
58
58
|
}, z.core.$loose>;
|
|
59
|
-
export
|
|
60
|
-
export interface Config extends z.infer<typeof ConfigSchema> {
|
|
59
|
+
export interface Config extends z.infer<typeof Config> {
|
|
61
60
|
}
|
|
62
|
-
export declare const configFiles: Map<string,
|
|
61
|
+
export declare const configFiles: Map<string, ConfigFile>;
|
|
63
62
|
export declare function plainConfig(): Omit<DeepRequired<Config>, keyof typeof configShortcuts>;
|
|
64
63
|
export declare const defaultConfig: DeepRequired<Config>;
|
|
65
64
|
declare const configShortcuts: {
|
|
@@ -70,12 +69,12 @@ declare const configShortcuts: {
|
|
|
70
69
|
save: typeof saveConfig;
|
|
71
70
|
saveTo: typeof saveConfigTo;
|
|
72
71
|
set: typeof setConfig;
|
|
73
|
-
files: Map<string,
|
|
72
|
+
files: Map<string, ConfigFile>;
|
|
74
73
|
defaults: DeepRequired<Config>;
|
|
75
74
|
};
|
|
76
75
|
export declare const config: DeepRequired<Config> & typeof configShortcuts;
|
|
77
76
|
export default config;
|
|
78
|
-
export declare const
|
|
77
|
+
export declare const ConfigFile: z.ZodObject<{
|
|
79
78
|
include: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
80
79
|
plugins: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
81
80
|
admin_api: z.ZodOptional<z.ZodOptional<z.ZodBoolean>>;
|
|
@@ -98,7 +97,7 @@ export declare const FileSchema: z.ZodObject<{
|
|
|
98
97
|
}, z.core.$loose>>>;
|
|
99
98
|
db: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
100
99
|
host: z.ZodOptional<z.ZodString>;
|
|
101
|
-
port: z.ZodOptional<z.
|
|
100
|
+
port: z.ZodOptional<z.ZodInt>;
|
|
102
101
|
password: z.ZodOptional<z.ZodString>;
|
|
103
102
|
user: z.ZodOptional<z.ZodString>;
|
|
104
103
|
database: z.ZodOptional<z.ZodString>;
|
|
@@ -133,9 +132,8 @@ export declare const FileSchema: z.ZodObject<{
|
|
|
133
132
|
build: z.ZodOptional<z.ZodString>;
|
|
134
133
|
}, z.core.$loose>>>;
|
|
135
134
|
}, z.core.$loose>;
|
|
136
|
-
export interface
|
|
135
|
+
export interface ConfigFile extends z.infer<typeof ConfigFile> {
|
|
137
136
|
}
|
|
138
|
-
export declare function addConfigDefaults(other: Config, _target?: Record<string, any>, _noDefault?: boolean): void;
|
|
139
137
|
/**
|
|
140
138
|
* Update the current config
|
|
141
139
|
*/
|
package/dist/config.js
CHANGED
|
@@ -7,9 +7,10 @@ import { capitalize, deepAssign, omit } from 'utilium';
|
|
|
7
7
|
import * as z from 'zod';
|
|
8
8
|
import { dirs, logger, systemDir } from './io.js';
|
|
9
9
|
import { _duplicateStateWarnings, _unique } from './state.js';
|
|
10
|
+
import { serverConfigs, toBaseName } from '@axium/core';
|
|
10
11
|
const audit_severity_levels = ['emergency', 'alert', 'critical', 'error', 'warning', 'notice', 'info', 'debug'];
|
|
11
12
|
const z_audit_severity = z.literal([...audit_severity_levels, ...audit_severity_levels.map(capitalize)]);
|
|
12
|
-
export
|
|
13
|
+
export const Config = z
|
|
13
14
|
.looseObject({
|
|
14
15
|
/** Whether /api/admin is enabled */
|
|
15
16
|
admin_api: z.boolean(),
|
|
@@ -42,7 +43,7 @@ export let ConfigSchema = z
|
|
|
42
43
|
db: z
|
|
43
44
|
.looseObject({
|
|
44
45
|
host: z.string(),
|
|
45
|
-
port: z.
|
|
46
|
+
port: z.int().min(1).max(65535),
|
|
46
47
|
password: z.string(),
|
|
47
48
|
user: z.string(),
|
|
48
49
|
database: z.string(),
|
|
@@ -82,9 +83,6 @@ export let ConfigSchema = z
|
|
|
82
83
|
.partial(),
|
|
83
84
|
})
|
|
84
85
|
.partial();
|
|
85
|
-
export function addConfig(shape) {
|
|
86
|
-
ConfigSchema = z.looseObject({ ...ConfigSchema.shape, ...shape });
|
|
87
|
-
}
|
|
88
86
|
export const configFiles = _unique('configFiles', new Map());
|
|
89
87
|
export function plainConfig() {
|
|
90
88
|
return omit(config, Object.keys(configShortcuts));
|
|
@@ -161,26 +159,13 @@ export const config = _unique('config', {
|
|
|
161
159
|
});
|
|
162
160
|
export default config;
|
|
163
161
|
// config from file
|
|
164
|
-
export const
|
|
162
|
+
export const ConfigFile = z
|
|
165
163
|
.looseObject({
|
|
166
|
-
...
|
|
164
|
+
...Config.shape,
|
|
167
165
|
include: z.string().array(),
|
|
168
166
|
plugins: z.string().array(),
|
|
169
167
|
})
|
|
170
168
|
.partial();
|
|
171
|
-
export function addConfigDefaults(other, _target = config, _noDefault = false) {
|
|
172
|
-
if (!_noDefault)
|
|
173
|
-
deepAssign(defaultConfig, other);
|
|
174
|
-
for (const [key, value] of Object.entries(other)) {
|
|
175
|
-
if (!(key in _target) || _target[key] === null || _target[key] === undefined || Number.isNaN(_target[key])) {
|
|
176
|
-
_target[key] = value;
|
|
177
|
-
continue;
|
|
178
|
-
}
|
|
179
|
-
if (typeof value == 'object' && value != null && typeof _target[key] == 'object') {
|
|
180
|
-
addConfigDefaults(value, _target[key], true);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
169
|
/**
|
|
185
170
|
* Update the current config
|
|
186
171
|
*/
|
|
@@ -222,7 +207,7 @@ export async function loadConfig(path, options = {}) {
|
|
|
222
207
|
}
|
|
223
208
|
let file;
|
|
224
209
|
try {
|
|
225
|
-
file =
|
|
210
|
+
file = ConfigFile.parse(json);
|
|
226
211
|
if (file.web?.build)
|
|
227
212
|
file.web.build = resolve(dirname(path), file.web.build);
|
|
228
213
|
}
|
|
@@ -241,6 +226,25 @@ export async function loadConfig(path, options = {}) {
|
|
|
241
226
|
const plugin = await loadPlugin('server', pluginPath, path, options.safe);
|
|
242
227
|
if (!plugin)
|
|
243
228
|
continue;
|
|
229
|
+
const serverConfig = serverConfigs.get(plugin.name);
|
|
230
|
+
if (serverConfig) {
|
|
231
|
+
plugin.config ||= {};
|
|
232
|
+
let configPath;
|
|
233
|
+
for (const dir of dirs) {
|
|
234
|
+
configPath = join(dir, 'plugins', toBaseName(plugin.name) + '.json');
|
|
235
|
+
if (!existsSync(configPath))
|
|
236
|
+
continue;
|
|
237
|
+
try {
|
|
238
|
+
const data = io.readJSON(configPath, serverConfig.schema.partial());
|
|
239
|
+
deepAssign(plugin.config, data);
|
|
240
|
+
io.debug(`Loaded config for plugin ${plugin.name} from ${configPath}`);
|
|
241
|
+
}
|
|
242
|
+
catch (e) {
|
|
243
|
+
io.warn(`Failed to load config for plugin ${plugin.name} at ${configPath}: ${e}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
plugin._configPath = configPath;
|
|
247
|
+
}
|
|
244
248
|
}
|
|
245
249
|
}
|
|
246
250
|
export async function loadDefaultConfigs(safe = false) {
|
package/dist/database.d.ts
CHANGED
|
@@ -439,7 +439,7 @@ export declare const SchemaFile: z.ZodObject<{
|
|
|
439
439
|
drop_indexes: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodTemplateLiteral<`${string}:${string}`>>>>;
|
|
440
440
|
}, z.core.$strict>], "delta">>;
|
|
441
441
|
wipe: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
442
|
-
latest: z.ZodOptional<z.
|
|
442
|
+
latest: z.ZodOptional<z.ZodInt32>;
|
|
443
443
|
acl_tables: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>>;
|
|
444
444
|
}, z.core.$strip>;
|
|
445
445
|
export interface SchemaFile extends z.infer<typeof SchemaFile> {
|
|
@@ -555,11 +555,11 @@ export declare function getFullSchema(opt?: {
|
|
|
555
555
|
versions: Record<string, number>;
|
|
556
556
|
};
|
|
557
557
|
export declare const UpgradesInfo: z.ZodObject<{
|
|
558
|
-
current: z.ZodDefault<z.ZodRecord<z.ZodString, z.
|
|
558
|
+
current: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodInt32>>;
|
|
559
559
|
upgrades: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
560
560
|
timestamp: z.ZodCoercedDate<unknown>;
|
|
561
|
-
from: z.ZodRecord<z.ZodString, z.
|
|
562
|
-
to: z.ZodRecord<z.ZodString, z.
|
|
561
|
+
from: z.ZodRecord<z.ZodString, z.ZodInt32>;
|
|
562
|
+
to: z.ZodRecord<z.ZodString, z.ZodInt32>;
|
|
563
563
|
}, z.core.$strip>>>;
|
|
564
564
|
}, z.core.$strip>;
|
|
565
565
|
export interface UpgradesInfo extends z.infer<typeof UpgradesInfo> {
|
package/dist/database.js
CHANGED
|
@@ -352,7 +352,7 @@ export const SchemaFile = z.object({
|
|
|
352
352
|
/** List of tables to wipe */
|
|
353
353
|
wipe: z.string().array().optional().default([]),
|
|
354
354
|
/** Set the latest version, defaults to the last one */
|
|
355
|
-
latest: z.
|
|
355
|
+
latest: z.int32().nonnegative().optional(),
|
|
356
356
|
/** Maps tables to their ACL tables, e.g. `"storage": "acl.storage"` */
|
|
357
357
|
acl_tables: z.record(z.string(), z.string()).optional().default({}),
|
|
358
358
|
});
|
|
@@ -416,7 +416,7 @@ const schemaToIntrospected = {
|
|
|
416
416
|
integer: 'int4',
|
|
417
417
|
'text[]': '_text',
|
|
418
418
|
};
|
|
419
|
-
const VersionMap = z.record(z.string(), z.
|
|
419
|
+
const VersionMap = z.record(z.string(), z.int32().nonnegative());
|
|
420
420
|
export const UpgradesInfo = z.object({
|
|
421
421
|
current: VersionMap.default({}),
|
|
422
422
|
upgrades: z.object({ timestamp: z.coerce.date(), from: VersionMap, to: VersionMap }).array().default([]),
|
package/dist/linking.d.ts
CHANGED
|
@@ -12,5 +12,6 @@ export declare function listRouteLinks(options?: LinkOptions): Generator<LinkInf
|
|
|
12
12
|
* Symlinks .svelte page routes for plugins and the server into a project's routes directory.
|
|
13
13
|
*/
|
|
14
14
|
export declare function linkRoutes(options?: LinkOptions): void;
|
|
15
|
+
export declare function writePluginHooks(): void;
|
|
15
16
|
export declare function unlinkRoutes(options?: LinkOptions): void;
|
|
16
17
|
export {};
|
package/dist/linking.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as io from '@axium/core/node/io';
|
|
2
2
|
import { plugins } from '@axium/core/plugins';
|
|
3
|
-
import { existsSync, symlinkSync, unlinkSync } from 'node:fs';
|
|
4
|
-
import { join, resolve } from 'node:path/posix';
|
|
3
|
+
import { existsSync, symlinkSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { join, relative, resolve } from 'node:path/posix';
|
|
5
5
|
import config from './config.js';
|
|
6
6
|
const textFor = {
|
|
7
7
|
builtin: 'built-in routes',
|
|
@@ -54,6 +54,20 @@ export function linkRoutes(options = {}) {
|
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
export function writePluginHooks() {
|
|
58
|
+
const hooksPath = join(import.meta.dirname, '../.hooks.js');
|
|
59
|
+
io.start('Writing web client hooks for plugins');
|
|
60
|
+
let hooks = `// auto-generated plugin hooks //\n`;
|
|
61
|
+
for (const plugin of plugins.values()) {
|
|
62
|
+
if (!plugin.server?.web_client_hooks)
|
|
63
|
+
continue;
|
|
64
|
+
const specifier = relative(resolve(import.meta.dirname, '..'), resolve(plugin.dirname, plugin.server.web_client_hooks));
|
|
65
|
+
hooks += `import '${specifier}';\n`;
|
|
66
|
+
}
|
|
67
|
+
writeFileSync(hooksPath, hooks, 'utf8');
|
|
68
|
+
io.done();
|
|
69
|
+
io.debug('Wrote', hooksPath);
|
|
70
|
+
}
|
|
57
71
|
export function unlinkRoutes(options = {}) {
|
|
58
72
|
for (const info of listRouteLinks(options)) {
|
|
59
73
|
const { text, from } = info;
|
package/dist/main.js
CHANGED
|
@@ -66,10 +66,10 @@ import * as z from 'zod';
|
|
|
66
66
|
import $pkg from '../package.json' with { type: 'json' };
|
|
67
67
|
import { audit, getEvents, styleSeverity } from './audit.js';
|
|
68
68
|
import { diffUpdate, lookupUser, userText } from './cli.js';
|
|
69
|
-
import config, {
|
|
69
|
+
import config, { ConfigFile, configFiles, saveConfigTo } from './config.js';
|
|
70
70
|
import * as db from './database.js';
|
|
71
71
|
import { _portActions, _portMethods, restrictedPorts } from './io.js';
|
|
72
|
-
import { linkRoutes, listRouteLinks, unlinkRoutes } from './linking.js';
|
|
72
|
+
import { linkRoutes, listRouteLinks, unlinkRoutes, writePluginHooks } from './linking.js';
|
|
73
73
|
import { serve } from './serve.js';
|
|
74
74
|
async function rlConfirm(question = 'Is this ok') {
|
|
75
75
|
const { data, error } = z
|
|
@@ -97,7 +97,7 @@ function configReplacer(opt) {
|
|
|
97
97
|
return opt.redact && ['password', 'secret'].includes(key) ? '[redacted]' : value;
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
|
-
var rl, safe, configFromCLI, noAutoDB, opts, axiumDB, axiumConfig, axiumPlugin, axiumApps, argUserLookup;
|
|
100
|
+
var rl, safe, debug, configFromCLI, noAutoDB, opts, axiumDB, axiumConfig, axiumPlugin, axiumApps, argUserLookup;
|
|
101
101
|
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
102
102
|
try {
|
|
103
103
|
rl = __addDisposableResource(env_1, createInterface({
|
|
@@ -105,14 +105,23 @@ try {
|
|
|
105
105
|
output: process.stdout,
|
|
106
106
|
}), false);
|
|
107
107
|
// Need these before Command is set up (e.g. for CLI integrations)
|
|
108
|
-
({
|
|
108
|
+
({
|
|
109
|
+
safe,
|
|
110
|
+
debug,
|
|
111
|
+
config: configFromCLI
|
|
112
|
+
} = parseArgs({
|
|
109
113
|
options: {
|
|
110
114
|
safe: { type: 'boolean', default: z.stringbool().default(false).parse(process.env.SAFE?.toLowerCase()) },
|
|
115
|
+
debug: { type: 'boolean', default: z.stringbool().default(false).parse(process.env.DEBUG?.toLowerCase()) },
|
|
111
116
|
config: { type: 'string', short: 'c' },
|
|
112
117
|
},
|
|
113
118
|
allowPositionals: true,
|
|
114
119
|
strict: false,
|
|
115
120
|
}).values);
|
|
121
|
+
if (debug) {
|
|
122
|
+
io._setDebugOutput(true);
|
|
123
|
+
config.set({ debug: true });
|
|
124
|
+
}
|
|
116
125
|
await config.loadDefaults(safe);
|
|
117
126
|
if (configFromCLI)
|
|
118
127
|
await config.load(configFromCLI, { safe });
|
|
@@ -495,7 +504,7 @@ try {
|
|
|
495
504
|
.action(() => {
|
|
496
505
|
const opt = axiumConfig.optsWithGlobals();
|
|
497
506
|
try {
|
|
498
|
-
const schema = z.toJSONSchema(
|
|
507
|
+
const schema = z.toJSONSchema(ConfigFile, { io: 'input' });
|
|
499
508
|
console.log(opt.json ? JSON.stringify(schema, configReplacer(opt), 4) : schema);
|
|
500
509
|
}
|
|
501
510
|
catch (e) {
|
|
@@ -777,6 +786,7 @@ try {
|
|
|
777
786
|
return;
|
|
778
787
|
}
|
|
779
788
|
linkRoutes(linkOpts);
|
|
789
|
+
writePluginHooks();
|
|
780
790
|
});
|
|
781
791
|
program
|
|
782
792
|
.command('audit')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axium/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.1",
|
|
4
4
|
"author": "James Prevett <axium@jamespre.dev>",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -47,8 +47,8 @@
|
|
|
47
47
|
"clean": "rm -rf build .svelte-kit node_modules/{.vite,.vite-temp}"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@axium/client": ">=0.
|
|
51
|
-
"@axium/core": ">=0.
|
|
50
|
+
"@axium/client": ">=0.12.0",
|
|
51
|
+
"@axium/core": ">=0.18.0",
|
|
52
52
|
"kysely": "^0.28.0",
|
|
53
53
|
"utilium": "^2.6.0",
|
|
54
54
|
"zod": "^4.0.5"
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { ClipboardCopy, FormDialog, Icon, Logout,
|
|
2
|
+
import { ClipboardCopy, FormDialog, Icon, Logout, SessionList, ZodForm } from '@axium/client/components';
|
|
3
|
+
import { fetchAPI } from '@axium/client/requests';
|
|
3
4
|
import '@axium/client/styles/account';
|
|
4
5
|
import { createPasskey, deletePasskey, deleteUser, sendVerificationEmail, updatePasskey, updateUser } from '@axium/client/user';
|
|
6
|
+
import { preferenceLabels, Preferences } from '@axium/core/preferences';
|
|
5
7
|
import { getUserImage } from '@axium/core/user';
|
|
6
8
|
import type { PageProps } from './$types';
|
|
7
9
|
|
|
@@ -161,7 +163,13 @@
|
|
|
161
163
|
|
|
162
164
|
<div id="preferences" class="section main">
|
|
163
165
|
<h3>Preferences</h3>
|
|
164
|
-
<
|
|
166
|
+
<ZodForm
|
|
167
|
+
bind:rootValue={user.preferences}
|
|
168
|
+
idPrefix="preferences"
|
|
169
|
+
schema={Preferences}
|
|
170
|
+
labels={preferenceLabels}
|
|
171
|
+
updateValue={(preferences: Preferences) => fetchAPI('PATCH', 'users/:id', { preferences }, user.id)}
|
|
172
|
+
/>
|
|
165
173
|
</div>
|
|
166
174
|
</div>
|
|
167
175
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { Version } from '@axium/client/components';
|
|
2
|
+
import { Version, ZodForm } from '@axium/client/components';
|
|
3
|
+
import { fetchAPI } from '@axium/client/requests';
|
|
4
|
+
import { serverConfigs } from '@axium/core';
|
|
3
5
|
|
|
4
6
|
const { data } = $props();
|
|
5
7
|
</script>
|
|
@@ -11,6 +13,7 @@
|
|
|
11
13
|
<h2>Plugins</h2>
|
|
12
14
|
|
|
13
15
|
{#each data.plugins as plugin}
|
|
16
|
+
{@const cfg = serverConfigs.get(plugin.name)}
|
|
14
17
|
<div class="plugin">
|
|
15
18
|
<h3>{plugin.name}<Version v={plugin.version} latest={plugin.latest} /></h3>
|
|
16
19
|
<p>
|
|
@@ -35,6 +38,17 @@
|
|
|
35
38
|
{:else}<i>None</i>{/if}
|
|
36
39
|
</p>
|
|
37
40
|
<p>{plugin.description}</p>
|
|
41
|
+
{#if cfg && plugin.config}
|
|
42
|
+
<h4>Configuration</h4>
|
|
43
|
+
{@const { schema, labels } = cfg}
|
|
44
|
+
<ZodForm
|
|
45
|
+
rootValue={plugin.config}
|
|
46
|
+
idPrefix={plugin.name}
|
|
47
|
+
{schema}
|
|
48
|
+
{labels}
|
|
49
|
+
updateValue={config => fetchAPI('POST', 'admin/plugins', { plugin: plugin.name, config })}
|
|
50
|
+
/>
|
|
51
|
+
{/if}
|
|
38
52
|
</div>
|
|
39
53
|
{:else}
|
|
40
54
|
<i>No plugins loaded.</i>
|
|
@@ -60,4 +74,8 @@
|
|
|
60
74
|
.apps a {
|
|
61
75
|
text-decoration: underline;
|
|
62
76
|
}
|
|
77
|
+
|
|
78
|
+
.plugin :global(label:not(.checkbox)) {
|
|
79
|
+
font-family: monospace;
|
|
80
|
+
}
|
|
63
81
|
</style>
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { ClipboardCopy,
|
|
2
|
+
import { ClipboardCopy, FormDialog, Icon, SessionList, ZodForm } from '@axium/client/components';
|
|
3
|
+
import { fetchAPI } from '@axium/client/requests';
|
|
3
4
|
import '@axium/client/styles/account';
|
|
4
5
|
import { deleteUser } from '@axium/client/user';
|
|
6
|
+
import { preferenceLabels, Preferences } from '@axium/core';
|
|
5
7
|
import { formatDateRange } from '@axium/core/format';
|
|
6
8
|
|
|
7
9
|
const { data } = $props();
|
|
@@ -108,7 +110,12 @@
|
|
|
108
110
|
|
|
109
111
|
<div id="preferences" class="section main">
|
|
110
112
|
<h3>Preferences</h3>
|
|
111
|
-
<
|
|
113
|
+
<ZodForm
|
|
114
|
+
bind:rootValue={user.preferences}
|
|
115
|
+
schema={Preferences}
|
|
116
|
+
labels={preferenceLabels}
|
|
117
|
+
updateValue={(preferences: Preferences) => fetchAPI('PATCH', 'users/:id', { preferences }, user.id)}
|
|
118
|
+
/>
|
|
112
119
|
</div>
|
|
113
120
|
|
|
114
121
|
<style>
|
package/schemas/config.json
CHANGED
package/schemas/db.json
CHANGED