The hub keeps a baseline per app in /data/apps/<app>/baseline.json. Local fields (overrides.json) are left out of the sync and out of the matrix comparison. Each server gets the baseline plus its own values. Applying goes target picker, preview with per-server diffs and warnings, then apply. Apply refuses a server whose config changed since the preview, saves the old config to history, then reads the new one back to check it. Large config bodies are read only for signed-in callers. The App config dialog has Compare, Baseline and Local fields tabs and an empty state that offers an import. Co-Authored-By: Claude Opus 5.5 <[email protected]>
98 lines
2.6 KiB
TypeScript
98 lines
2.6 KiB
TypeScript
import { useEffect, useState } from 'react'
|
|
import { setAppConfig } from '../../api/apps'
|
|
import { Button } from '../../ui/Button'
|
|
import { Dialog } from '../../ui/Dialog'
|
|
import { Field } from '../../ui/Field'
|
|
import { error, type AlertState } from './Apps'
|
|
import styles from './Apps.module.css'
|
|
import { Notifier } from '../../ui/Notifier'
|
|
|
|
/*
|
|
A replica of `modalAppConfig` (index.html:6255-6280) and `saveAppConfig`
|
|
(apps.js:493-522).
|
|
|
|
It is a TEXT EDITOR, not a form: the content of `dnsApp.config` is defined by each
|
|
app and the console does not know what fields it has. Upstream puts a 15-row
|
|
`<textarea>` with the spellchecker off, and that is what is here.
|
|
|
|
The config arrives already read from the screen, because upstream also asks for it
|
|
BEFORE opening the modal (the "Config" button sits at "Loading...").
|
|
*/
|
|
export function AppConfig({
|
|
open,
|
|
onOpenChange,
|
|
token,
|
|
name,
|
|
initialConfig,
|
|
onSaved,
|
|
node = '',
|
|
}: {
|
|
open: boolean
|
|
onOpenChange: (o: boolean) => void
|
|
token: string | null
|
|
name: string
|
|
initialConfig: string
|
|
onSaved: (name: string) => void
|
|
/** Behind a hub: the one server it goes to. */
|
|
node?: string
|
|
}) {
|
|
const [config, setConfig] = useState(initialConfig)
|
|
const [alert, setAlert] = useState<AlertState | null>(null)
|
|
const [busy, setBusy] = useState(false)
|
|
|
|
useEffect(() => {
|
|
if (open) {
|
|
setConfig(initialConfig)
|
|
setAlert(null)
|
|
setBusy(false)
|
|
}
|
|
}, [open, initialConfig])
|
|
|
|
async function save() {
|
|
setBusy(true)
|
|
const outcome = await setAppConfig(token, name, config, node)
|
|
setBusy(false)
|
|
|
|
if (outcome.kind !== 'ok') {
|
|
setAlert(error(outcome))
|
|
return
|
|
}
|
|
onSaved(name)
|
|
}
|
|
|
|
return (
|
|
<Dialog
|
|
open={open}
|
|
onOpenChange={onOpenChange}
|
|
size="medium"
|
|
title={`App Config - ${name}`}
|
|
actions={
|
|
<>
|
|
<Button variant="primary" disabled={busy} onClick={() => void save()}>
|
|
Save
|
|
</Button>
|
|
</>
|
|
}
|
|
>
|
|
<Notifier notice={alert} onClose={() => setAlert(null)} />
|
|
<p className={styles.note}>
|
|
Edit the <code>dnsApp.config</code> config file below as required by the DNS application.
|
|
</p>
|
|
<Field label="Config File">
|
|
{(id) => (
|
|
<textarea
|
|
id={id}
|
|
className={styles.config}
|
|
spellCheck={false}
|
|
value={config}
|
|
onChange={(e) => setConfig(e.target.value)}
|
|
/>
|
|
)}
|
|
</Field>
|
|
<p className={styles.note}>
|
|
Note: The app will reload the config automatically after you save it.
|
|
</p>
|
|
</Dialog>
|
|
)
|
|
}
|