feat(settings): report which saved keys need a restart
Every settings update answered with a bare success, so the console could not tell a change that took effect at once from one written to the file that the running program would keep ignoring until it was restarted — the listener address, the storage paths, the Komari dashboard URL. Classify the patch instead. config.RestartRequiredKeys expands a patch into the dot-separated paths of its leaves and intersects them with the settings main reads before it starts serving, and the settings endpoint returns that list as data.restartRequired. It is a pure function of the patch, so UpdateSettings keeps its signature and nothing else has to change; the write succeeds either way, and the field only says which edits are not live. Matching is by overlap rather than equality, so a patch that names a section — replacing it, or deleting it with a null — reports the startup-only keys inside it, while a sibling subtree like webhook.endpoints is not mistaken for webhook.enabled. The result is never nil, so an update with nothing to report serializes as [] rather than null and a client can iterate it without a guard. The console reads the field in ConfigSection and warns instead of confirming, naming the keys that are pending; Settings.vue's hints for the Komari URL, the listen address and the storage paths now say which of their fields is affected rather than labelling the whole card.
This commit is contained in:
@@ -13,6 +13,10 @@ export const serverApi = {
|
||||
|
||||
// /api/settings/get?type=… / /api/settings/set?type=…
|
||||
// get → { success, message, data: { config } }.
|
||||
// set → { success, message, data: { type, restartRequired } }, where
|
||||
// restartRequired lists the keys the update changed that are only read at
|
||||
// startup, so the caller can say which edits are not live yet. It is
|
||||
// always an array, empty when the whole update took effect.
|
||||
// `type` is one of global | bot_user_config | bot_node_config.
|
||||
// For set, pass a partial object; a JSON null value removes that key.
|
||||
export const settingsApi = {
|
||||
|
||||
@@ -133,8 +133,15 @@ async function save() {
|
||||
const patch = wrapRoot(props.section, nest(obj));
|
||||
saving.value = true;
|
||||
try {
|
||||
await settingsApi.set('global', patch);
|
||||
toast.success(`${props.section.title} saved`);
|
||||
const res = await settingsApi.set('global', patch);
|
||||
// The backend reports the keys it wrote that are only read at startup.
|
||||
// A plain "saved" would suggest those are live too.
|
||||
const pending = (res && res.data && res.data.restartRequired) || [];
|
||||
if (pending.length) {
|
||||
toast.warn(`${props.section.title} saved — restart to apply: ${pending.join(', ')}`, 7000);
|
||||
} else {
|
||||
toast.success(`${props.section.title} saved`);
|
||||
}
|
||||
emit('saved');
|
||||
} catch (e) {
|
||||
toast.error('Failed to save: ' + e.message);
|
||||
|
||||
@@ -11,7 +11,7 @@ const sections = [
|
||||
{
|
||||
id: 'system',
|
||||
title: 'System',
|
||||
hint: 'HTTP listener and global runtime switches.',
|
||||
hint: 'HTTP listener and global runtime switches. A changed listen address or port applies on restart.',
|
||||
root: ['system'],
|
||||
fields: [
|
||||
{ key: 'debugMode', type: 'bool', label: 'Debug mode', help: 'Skipped X-Timestamp checks and verbose debug logging.' },
|
||||
@@ -37,7 +37,7 @@ const sections = [
|
||||
{
|
||||
id: 'komari',
|
||||
title: 'Komari dashboard',
|
||||
hint: 'Connection the monitor reads node data from. Takes effect on restart.',
|
||||
hint: 'Connection the monitor reads node data from. The URL applies on restart; the account is re-read on the next login.',
|
||||
root: ['komari'],
|
||||
fields: [
|
||||
{ key: 'dashboardURL', type: 'text', label: 'Dashboard URL' },
|
||||
|
||||
Reference in New Issue
Block a user