115 lines
3.1 KiB
JavaScript
115 lines
3.1 KiB
JavaScript
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>
|
||
);
|
||
}
|