added bot api question on wev-setup

This commit is contained in:
onysd 2026-09-11 05:15:00 +03:00
parent 2f1818d656
commit c04a8ddc6a
5 changed files with 125 additions and 18 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -23,7 +23,7 @@
})(); })();
</script> </script>
<script type="module" crossorigin src="/assets/index-B7vjbJOs.js"></script> <script type="module" crossorigin src="/assets/index-3atviP2p.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-P_k7ini0.css"> <link rel="stylesheet" crossorigin href="/assets/index-P_k7ini0.css">
</head> </head>
<body> <body>

View file

@ -27,13 +27,14 @@ import { Alert } from "./ui";
// you naming your own server" is friction with no audit value. // you naming your own server" is friction with no audit value.
const WIZARD_REASON = "Set from the first-run setup wizard"; const WIZARD_REASON = "Set from the first-run setup wizard";
type StepId = "welcome" | "identity" | "network" | "account" | "done"; type StepId = "welcome" | "identity" | "network" | "botapi" | "account" | "done";
const STEP_ORDER: StepId[] = ["welcome", "identity", "network", "account", "done"]; const STEP_ORDER: StepId[] = ["welcome", "identity", "network", "botapi", "account", "done"];
const STEP_LABEL: Record<StepId, string> = { const STEP_LABEL: Record<StepId, string> = {
welcome: "Welcome", welcome: "Welcome",
identity: "Identity", identity: "Identity",
network: "Network", network: "Network",
botapi: "Bot API",
account: "Account", account: "Account",
done: "Done" done: "Done"
}; };
@ -82,7 +83,8 @@ export function SetupWizard() {
{step === "welcome" && <WelcomeStep onNext={() => goTo("identity")} />} {step === "welcome" && <WelcomeStep onNext={() => goTo("identity")} />}
{step === "identity" && <IdentityStep onNext={() => goTo("network")} />} {step === "identity" && <IdentityStep onNext={() => goTo("network")} />}
{step === "network" && <NetworkStep onNext={() => goTo("account")} />} {step === "network" && <NetworkStep onNext={() => goTo("botapi")} />}
{step === "botapi" && <BotApiStep onNext={() => goTo("account")} />}
{step === "account" && <AccountStep onNext={() => goTo("done")} />} {step === "account" && <AccountStep onNext={() => goTo("done")} />}
{step === "done" && <DoneStep />} {step === "done" && <DoneStep />}
</section> </section>
@ -284,6 +286,121 @@ function NetworkStep({ onNext }: { onNext: () => void }) {
); );
} }
const BOT_API_KEY = "TELESRV_BOT_API_ADDR";
const BOT_API_DEFAULT_ADDR = "127.0.0.1:2500";
// An empty TELESRV_BOT_API_ADDR is what disables the gateway: botapi.Start
// returns early on a blank address. A malformed or already-taken one is worth
// catching here rather than server-side, because cmd/telesrv/main.go turns a
// failed botapi.Start into a fatal "start bot api" error -- and the step that
// applies this is the wizard's own restart, so a typo would leave the operator
// staring at a server that never comes back.
function botApiAddrError(addr: string): string {
const value = addr.trim();
if (value === "") return "Enter an address like " + BOT_API_DEFAULT_ADDR + ".";
const colon = value.lastIndexOf(":");
if (colon < 0) return "Include a port, for example " + BOT_API_DEFAULT_ADDR + ".";
const port = Number(value.slice(colon + 1));
if (!Number.isInteger(port) || port < 1 || port > 65535) return "Port must be a whole number between 1 and 65535.";
return "";
}
function BotApiStep({ onNext }: { onNext: () => void }) {
const [enabled, setEnabled] = useState(false);
const [addr, setAddr] = useState("");
const [loaded, setLoaded] = useState(false);
const [busy, setBusy] = useState(false);
const [error, setError] = useState("");
useEffect(() => {
let cancelled = false;
api.serverEnv()
.then((groups) => {
if (cancelled) return;
for (const group of groups) {
for (const field of group.fields) {
if (field.key !== BOT_API_KEY) continue;
setEnabled(field.value.trim() !== "");
setAddr(field.value.trim());
}
}
setLoaded(true);
})
.catch((err) => { if (!cancelled) { setError(errorMessage(err)); setLoaded(true); } });
return () => { cancelled = true; };
}, []);
const addrError = enabled ? botApiAddrError(addr) : "";
async function submit() {
if (addrError) return;
setBusy(true);
setError("");
try {
const result = await api.action("/api/actions/update-server-env", {
command_id: "", reason: WIZARD_REASON, confirm: true,
values: { [BOT_API_KEY]: enabled ? addr.trim() : "" }
});
if (result.error) {
setError(result.error);
return;
}
onNext();
} catch (err) {
setError(errorMessage(err));
} finally {
setBusy(false);
}
}
return (
<div className="wizard-step-body">
<p className="wizard-step-hint">
{"An HTTP gateway that lets bot libraries -- python-telegram-bot, aiogram and friends -- "}
{"talk to this server. Leave it off if you are not running bots; you can turn it on later in Server Settings."}
</p>
{error && <Alert>{error}</Alert>}
<label className="checkline">
<input
type="checkbox"
checked={enabled}
disabled={!loaded}
onChange={(event) => {
const next = event.target.checked;
setEnabled(next);
if (next && addr.trim() === "") setAddr(BOT_API_DEFAULT_ADDR);
}}
/>
{" Enable the Bot API gateway"}
</label>
{enabled && (
<label className="form-field env-field">
<span>{"Listen address"}</span>
<span className="env-field-desc">
{"Keep 127.0.0.1 to accept only local bots; use 0.0.0.0 to expose it. "}
{"The server will refuse to start if this port is already taken."}
</span>
<input
value={addr}
placeholder={BOT_API_DEFAULT_ADDR}
disabled={!loaded}
spellCheck={false}
autoCapitalize="none"
onChange={(event) => setAddr(event.target.value)}
/>
{addrError && <span className="env-field-desc">{addrError}</span>}
</label>
)}
<WizardActions>
<button className="btn primary icon-text" type="button" disabled={busy || !loaded || addrError !== ""} onClick={() => void submit()}>
{busy ? <Loader2 className="spin" size={15} /> : <ArrowRight size={15} />}
{"Continue"}
</button>
</WizardActions>
</div>
);
}
function AccountStep({ onNext }: { onNext: () => void }) { function AccountStep({ onNext }: { onNext: () => void }) {
const [username, setUsername] = useState(""); const [username, setUsername] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
@ -375,8 +492,8 @@ function DoneStep() {
return ( return (
<div className="wizard-step-body"> <div className="wizard-step-body">
<p> <p>
{"That's the essentials. Finishing restarts the server so the network settings from the previous "} {"That's the essentials. Finishing restarts the server so the network and Bot API settings from "}
{"step take effect. Everything here stays editable from Server Settings and Operators any time."} {"the earlier steps take effect. Everything here stays editable from Server Settings and Operators any time."}
</p> </p>
{error && <Alert>{error}</Alert>} {error && <Alert>{error}</Alert>}
<WizardActions> <WizardActions>