This commit is contained in:
2026-05-11 17:19:13 +08:00
parent 03a3973014
commit 1b16641e26
22 changed files with 3012 additions and 847 deletions

View File

@@ -0,0 +1,114 @@
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>
);
}