Files
server-config-cli/src/screens/FrpConfigForm.jsx
2026-05-11 17:19:13 +08:00

115 lines
3.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from "react";
import { Box, Text, useInput } from "ink";
import TextInput from "ink-text-input";
import {
bootstrapPlan,
frpInstallPlan,
frpInitConfigPlan,
DEFAULTS,
} from "../lib/tasks.js";
import { defaultInstallDir } from "../lib/frp-config.js";
const FIELDS = [
{ key: "serverAddr", label: "Server address" },
{ key: "serverPort", label: "Server port" },
{ key: "installDir", label: "Install dir" },
];
export default function FrpConfigForm({ nav, token, purpose }) {
const [values, setValues] = useState({
serverAddr: DEFAULTS.serverAddr,
serverPort: String(DEFAULTS.serverPort),
installDir: defaultInstallDir(),
});
const [step, setStep] = useState(0);
const [error, setError] = useState(null);
useInput((input, key) => {
if (key.tab || key.downArrow) {
setStep((s) => Math.min(s + 1, FIELDS.length - 1));
return;
}
if (key.upArrow) {
setStep((s) => Math.max(s - 1, 0));
return;
}
if (key.return) {
if (step < FIELDS.length - 1) {
setStep((s) => s + 1);
return;
}
submit();
}
});
const update = (key) => (value) => {
setValues((v) => ({ ...v, [key]: value }));
};
const submit = () => {
const port = Number(values.serverPort);
if (!Number.isInteger(port) || port < 1 || port > 65535) {
setError("Server port must be an integer between 1 and 65535.");
return;
}
const options = {
token,
serverAddr: values.serverAddr.trim() || DEFAULTS.serverAddr,
serverPort: port,
installDir: values.installDir.trim() || defaultInstallDir(),
};
try {
let plan;
if (purpose === "bootstrap") {
plan = bootstrapPlan(options);
} else if (purpose === "frp-install") {
plan = frpInstallPlan(options);
} else if (purpose === "frp-init") {
plan = frpInitConfigPlan({ ...options, force: true });
} else {
setError(`Unknown purpose: ${purpose}`);
return;
}
nav.replace({ name: "plan", props: { plan, origin: purpose === "bootstrap" ? "main" : "frp" } });
} catch (err) {
setError(err.message);
}
};
return (
<Box flexDirection="column">
<Text>FRP configuration ({purpose}):</Text>
<Box flexDirection="column" marginTop={1}>
{FIELDS.map((field, index) => (
<Box key={field.key}>
<Box width={18}>
<Text color={index === step ? "cyan" : undefined}>
{index === step ? " " : " "}
{field.label}
</Text>
</Box>
<TextInput
value={values[field.key]}
onChange={update(field.key)}
focus={index === step}
/>
</Box>
))}
</Box>
{error ? (
<Box marginTop={1}>
<Text color="red">{error}</Text>
</Box>
) : null}
<Box marginTop={1}>
<Text dimColor>
Tab/ next · prev · Enter on last field submits · Esc back
</Text>
</Box>
</Box>
);
}