From 50e96e0d7716b517730564b08f0b1d50eb7e0123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Mon, 24 Aug 2026 04:07:16 +0000 Subject: [PATCH] plugin settings: the client already adds /api, and a Test button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Save returned 404 and the form silently showed what was typed rather than what was stored. Both from one mistake: useClient prefixes /api itself, so `/api${prefix}/config` reached /api/api/transcribe/config. Every other caller in the tree passes a bare path; I copied the shape of a curl instead. The GET failing was the worse half, because it looked like it worked — an empty field populated by typing is indistinguishable from one populated by the server. Adds Test, which asks the plugin's /_health whether its connection works NOW. The plugin validates on save, so a stored value has been reached once — but once, whenever that was, is not the same as now, and the server it points at is a separate machine that can stop answering. Offered only once something is stored, since there is nothing to test before that. Co-Authored-By: Claude Opus 5 Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043 --- .../src/apps/Plugins/PluginSettings.tsx | 29 +++++++++++++++++-- .../officerdev/src/locales/de-DE.json | 6 +++- .../officerdev/src/locales/en-GB.json | 6 +++- .../officerdev/src/locales/es-ES.json | 6 +++- .../officerdev/src/locales/fr-FR.json | 6 +++- .../officerdev/src/locales/pt-PT.json | 6 +++- .../officerdev/src/locales/sv-SE.json | 6 +++- 7 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/workspaces/officerdev/src/apps/Plugins/PluginSettings.tsx b/src/workspaces/officerdev/src/apps/Plugins/PluginSettings.tsx index 208b72bd..703e9f5a 100644 --- a/src/workspaces/officerdev/src/apps/Plugins/PluginSettings.tsx +++ b/src/workspaces/officerdev/src/apps/Plugins/PluginSettings.tsx @@ -55,7 +55,7 @@ export const PluginSettings = ({ appName, prefix, settings, enabled }: PluginSet const { data: current, isLoading } = useQuery({ queryKey: ['plugin-config', appName], - queryFn: () => client.get>(`/api${prefix}/config`), + queryFn: () => client.get>(`${prefix}/config`), enabled, // A plugin that has no /config yet, or is mid-restart, is not an error worth a red toast on a screen // the owner opened to read about the plugin. @@ -80,6 +80,23 @@ export const PluginSettings = ({ appName, prefix, settings, enabled }: PluginSet setValues((prev) => ({ ...next, ...prev })); }, [current, settings]); + /* + Test what is STORED, not what is typed. + + The plugin validates on save, so a stored value has already been reached once — but "once, whenever + that was" is not the same as "now", and the server it points at is a separate machine that can stop + answering. This asks the plugin whether its connection works at this moment, which is the question + somebody staring at a form actually has. + */ + const test = useMutation({ + mutationFn: () => client.get<{ ok?: boolean; error?: string }>(`${prefix}/_health`), + onSuccess: (r) => + r?.ok === false + ? toast.error(r.error || t('plugins.settingsTestFailed')) + : toast.success(t('plugins.settingsTestOk')), + onError: (err) => toast.error(err instanceof Error ? err.message : t('plugins.settingsTestFailed')), + }); + const save = useMutation({ mutationFn: () => { // Only what was typed. An untouched secret must not be sent as an empty string, which the plugin @@ -89,7 +106,7 @@ export const PluginSettings = ({ appName, prefix, settings, enabled }: PluginSet const v = values[f.key]; if (v !== undefined && v !== '') body[f.key] = v; } - return client.put(`/api${prefix}/config`, body); + return client.put(`${prefix}/config`, body); }, onSuccess: () => { toast.success(t('plugins.settingsSaved')); @@ -146,7 +163,13 @@ export const PluginSettings = ({ appName, prefix, settings, enabled }: PluginSet onClick={() => save.mutate()} > {save.isPending ? t('plugins.settingsSaving') : t('plugins.settingsSave')} - + {' '} + {/* Offered only once something is stored — there is nothing to test before that. */} + {current ? ( + + ) : null} )} diff --git a/src/workspaces/officerdev/src/locales/de-DE.json b/src/workspaces/officerdev/src/locales/de-DE.json index ae35f870..696ce99c 100644 --- a/src/workspaces/officerdev/src/locales/de-DE.json +++ b/src/workspaces/officerdev/src/locales/de-DE.json @@ -206,7 +206,11 @@ "settingsSaving": "Wird gespeichert…", "settingsSaved": "Einstellungen gespeichert", "settingsFailed": "Einstellungen konnten nicht gespeichert werden", - "settingsSecretSet": "Gesetzt — leer lassen, um es zu behalten" + "settingsSecretSet": "Gesetzt — leer lassen, um es zu behalten", + "settingsTest": "Testen", + "settingsTesting": "Wird getestet…", + "settingsTestOk": "Verbindung funktioniert", + "settingsTestFailed": "Nicht erreichbar" }, "commandHq": { "selectOne": "Wählen Sie einen Commander aus oder starten Sie einen neuen.", diff --git a/src/workspaces/officerdev/src/locales/en-GB.json b/src/workspaces/officerdev/src/locales/en-GB.json index 707f0995..085016b4 100644 --- a/src/workspaces/officerdev/src/locales/en-GB.json +++ b/src/workspaces/officerdev/src/locales/en-GB.json @@ -206,7 +206,11 @@ "settingsSaving": "Saving…", "settingsSaved": "Settings saved", "settingsFailed": "Could not save the settings", - "settingsSecretSet": "Set — leave blank to keep" + "settingsSecretSet": "Set — leave blank to keep", + "settingsTest": "Test", + "settingsTesting": "Testing…", + "settingsTestOk": "Connection works", + "settingsTestFailed": "Could not reach it" }, "commandHq": { "selectOne": "Select a commander, or start a new one.", diff --git a/src/workspaces/officerdev/src/locales/es-ES.json b/src/workspaces/officerdev/src/locales/es-ES.json index 189427b6..af8401f1 100644 --- a/src/workspaces/officerdev/src/locales/es-ES.json +++ b/src/workspaces/officerdev/src/locales/es-ES.json @@ -206,7 +206,11 @@ "settingsSaving": "Guardando…", "settingsSaved": "Ajustes guardados", "settingsFailed": "No se han podido guardar los ajustes", - "settingsSecretSet": "Definido — déjalo vacío para mantenerlo" + "settingsSecretSet": "Definido — déjalo vacío para mantenerlo", + "settingsTest": "Probar", + "settingsTesting": "Probando…", + "settingsTestOk": "La conexión funciona", + "settingsTestFailed": "No se ha podido alcanzar" }, "commandHq": { "selectOne": "Selecciona un commander, o inicia uno nuevo.", diff --git a/src/workspaces/officerdev/src/locales/fr-FR.json b/src/workspaces/officerdev/src/locales/fr-FR.json index fd26d296..36e1d684 100644 --- a/src/workspaces/officerdev/src/locales/fr-FR.json +++ b/src/workspaces/officerdev/src/locales/fr-FR.json @@ -206,7 +206,11 @@ "settingsSaving": "Enregistrement…", "settingsSaved": "Paramètres enregistrés", "settingsFailed": "Impossible d'enregistrer les paramètres", - "settingsSecretSet": "Défini — laissez vide pour le conserver" + "settingsSecretSet": "Défini — laissez vide pour le conserver", + "settingsTest": "Tester", + "settingsTesting": "Test en cours…", + "settingsTestOk": "La connexion fonctionne", + "settingsTestFailed": "Impossible de le joindre" }, "commandHq": { "selectOne": "Sélectionnez un commander, ou démarrez-en un nouveau.", diff --git a/src/workspaces/officerdev/src/locales/pt-PT.json b/src/workspaces/officerdev/src/locales/pt-PT.json index a25d03f1..f4fa131b 100644 --- a/src/workspaces/officerdev/src/locales/pt-PT.json +++ b/src/workspaces/officerdev/src/locales/pt-PT.json @@ -206,7 +206,11 @@ "settingsSaving": "A guardar…", "settingsSaved": "Definições guardadas", "settingsFailed": "Não foi possível guardar as definições", - "settingsSecretSet": "Definido — deixe em branco para manter" + "settingsSecretSet": "Definido — deixe em branco para manter", + "settingsTest": "Testar", + "settingsTesting": "A testar…", + "settingsTestOk": "A ligação funciona", + "settingsTestFailed": "Não foi possível alcançar" }, "commandHq": { "selectOne": "Selecione um comandante ou inicie um novo.", diff --git a/src/workspaces/officerdev/src/locales/sv-SE.json b/src/workspaces/officerdev/src/locales/sv-SE.json index 4f624d09..b121de16 100644 --- a/src/workspaces/officerdev/src/locales/sv-SE.json +++ b/src/workspaces/officerdev/src/locales/sv-SE.json @@ -206,7 +206,11 @@ "settingsSaving": "Sparar…", "settingsSaved": "Inställningarna sparades", "settingsFailed": "Det gick inte att spara inställningarna", - "settingsSecretSet": "Angivet — lämna tomt för att behålla" + "settingsSecretSet": "Angivet — lämna tomt för att behålla", + "settingsTest": "Testa", + "settingsTesting": "Testar…", + "settingsTestOk": "Anslutningen fungerar", + "settingsTestFailed": "Det gick inte att nå den" }, "commandHq": { "selectOne": "Välj en commander, eller starta en ny.",