update:
This commit is contained in:
18
README.md
18
README.md
@@ -32,9 +32,10 @@ server-config zsh install
|
||||
会执行:
|
||||
|
||||
- `apt update && apt upgrade`
|
||||
- 安装 `zsh git curl`
|
||||
- 安装 `zsh git curl wget`
|
||||
- 切换当前用户 shell 到 `/bin/zsh`
|
||||
- 安装 oh-my-zsh
|
||||
- 安装 nvm,并执行 `nvm install --lts`
|
||||
- 安装 `zsh-autosuggestions` 和 `zsh-syntax-highlighting`
|
||||
- 更新 `~/.zshrc` 的插件列表为 `git zsh-autosuggestions zsh-syntax-highlighting`
|
||||
|
||||
@@ -63,13 +64,14 @@ server-config frp install --token "$FRP_TOKEN"
|
||||
|
||||
默认配置:
|
||||
|
||||
- `server_addr = "81.70.134.9"`
|
||||
- `server_port = 15443`
|
||||
- `tls_enable = false`
|
||||
- `tcp_mux = true`
|
||||
- `log_file = "/var/log/frpc.log"`
|
||||
- `log_level = "info"`
|
||||
- `log_max_days = 7`
|
||||
- `serverAddr = "81.70.134.9"`
|
||||
- `serverPort = 15443`
|
||||
- `auth.token = "$FRP_TOKEN"`
|
||||
- `transport.tls.enable = false`
|
||||
- `transport.tcpMux = true`
|
||||
- `log.to = "/var/log/frpc.log"`
|
||||
- `log.level = "info"`
|
||||
- `log.maxDays = 7`
|
||||
- frp 版本:`0.58.1`
|
||||
- 安装目录:`/opt/frp/frp_0.58.1_linux_amd64`
|
||||
- systemd 服务:`frpc`
|
||||
|
||||
114
src/cli.js
114
src/cli.js
@@ -10,6 +10,23 @@ const DEFAULT_FRP_ARCH = "amd64";
|
||||
const DEFAULT_FRP_SERVER_ADDR = "81.70.134.9";
|
||||
const DEFAULT_FRP_SERVER_PORT = 15443;
|
||||
const DEFAULT_SERVICE_NAME = "frpc";
|
||||
const NVM_INSTALL_VERSION = "v0.40.4";
|
||||
const NVM_INSTALL_COMMAND = `set -euo pipefail
|
||||
export NVM_DIR="$HOME/.nvm"
|
||||
if [ ! -s "$NVM_DIR/nvm.sh" ]; then
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_INSTALL_VERSION}/install.sh | bash
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/${NVM_INSTALL_VERSION}/install.sh | bash
|
||||
else
|
||||
echo "curl or wget is required to install nvm" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
nvm install --lts
|
||||
nvm alias default 'lts/*'
|
||||
nvm use --lts`;
|
||||
|
||||
function main(argv) {
|
||||
const parsed = parseArgs(argv);
|
||||
@@ -95,13 +112,14 @@ function handleFrp(action, argv, rootFlags, runner) {
|
||||
function installZsh(runner, flags) {
|
||||
runner.run("sudo", ["apt", "update"]);
|
||||
runner.run("sudo", ["apt", "upgrade", "-y"]);
|
||||
runner.run("sudo", ["apt", "install", "zsh", "git", "curl", "-y"]);
|
||||
runner.run("sudo", ["apt", "install", "zsh", "git", "curl", "wget", "-y"]);
|
||||
runner.run("chsh", ["-s", "/bin/zsh"]);
|
||||
|
||||
runner.run("sh", [
|
||||
"-c",
|
||||
'RUNZSH=no CHSH=no KEEP_ZSHRC=yes sh -c "$(curl -fsSL https://gitee.com/pocmon/ohmyzsh/raw/master/tools/install.sh)"',
|
||||
]);
|
||||
runner.run("bash", ["-c", NVM_INSTALL_COMMAND]);
|
||||
|
||||
const customDir = process.env.ZSH_CUSTOM || path.join(os.homedir(), ".oh-my-zsh", "custom");
|
||||
installGitPlugin(
|
||||
@@ -175,8 +193,9 @@ function installFrp(runner, flags) {
|
||||
function initFrpConfig(flags, runner) {
|
||||
const configPath = getConfigPath(flags);
|
||||
const force = Boolean(flags.force);
|
||||
const configExists = fs.existsSync(configPath);
|
||||
|
||||
if (fs.existsSync(configPath) && !force) {
|
||||
if (configExists && !force) {
|
||||
console.log(`${configPath} already exists. Use --force to rewrite it.`);
|
||||
return;
|
||||
}
|
||||
@@ -186,16 +205,24 @@ function initFrpConfig(flags, runner) {
|
||||
throw new Error("frp token is required. Pass --token <value> or set FRP_TOKEN.");
|
||||
}
|
||||
|
||||
const proxies = configExists && force
|
||||
? [...parseFrpConfig(fs.readFileSync(configPath, "utf8")).sections.values()].map((section) => ({
|
||||
name: section.name,
|
||||
...section.values,
|
||||
}))
|
||||
: [];
|
||||
|
||||
const config = renderFrpConfig({
|
||||
server_addr: stringFlag(flags, "server-addr", DEFAULT_FRP_SERVER_ADDR),
|
||||
server_port: numberFlag(flags, "server-port", DEFAULT_FRP_SERVER_PORT),
|
||||
token,
|
||||
tls_enable: booleanFlag(flags, "tls-enable", false),
|
||||
tcp_mux: booleanFlag(flags, "tcp-mux", true),
|
||||
log_file: stringFlag(flags, "log-file", "/var/log/frpc.log"),
|
||||
log_level: stringFlag(flags, "log-level", "info"),
|
||||
log_max_days: numberFlag(flags, "log-max-days", 7),
|
||||
}, []);
|
||||
serverAddr: stringFlag(flags, "server-addr", DEFAULT_FRP_SERVER_ADDR),
|
||||
serverPort: numberFlag(flags, "server-port", DEFAULT_FRP_SERVER_PORT),
|
||||
"auth.method": "token",
|
||||
"auth.token": token,
|
||||
"transport.tls.enable": booleanFlag(flags, "tls-enable", false),
|
||||
"transport.tcpMux": booleanFlag(flags, "tcp-mux", true),
|
||||
"log.to": stringFlag(flags, "log-file", "/var/log/frpc.log"),
|
||||
"log.level": stringFlag(flags, "log-level", "info"),
|
||||
"log.maxDays": numberFlag(flags, "log-max-days", 7),
|
||||
}, proxies);
|
||||
|
||||
writeFile(configPath, config, runner);
|
||||
}
|
||||
@@ -300,6 +327,12 @@ function parseFrpConfig(text) {
|
||||
let current = null;
|
||||
|
||||
for (const line of text.split(/\r?\n/)) {
|
||||
const proxyMatch = line.match(/^\s*\[\[proxies\]\]\s*$/);
|
||||
if (proxyMatch) {
|
||||
current = { name: "", values: {} };
|
||||
continue;
|
||||
}
|
||||
|
||||
const sectionMatch = line.match(/^\s*\[([A-Za-z0-9_.-]+)]\s*$/);
|
||||
if (sectionMatch) {
|
||||
current = { name: sectionMatch[1], values: {} };
|
||||
@@ -308,19 +341,65 @@ function parseFrpConfig(text) {
|
||||
}
|
||||
|
||||
if (!current) {
|
||||
globals.push(line);
|
||||
globals.push(migrateFrpGlobalLine(line));
|
||||
continue;
|
||||
}
|
||||
|
||||
const keyValue = parseTomlKeyValue(line);
|
||||
if (keyValue) {
|
||||
current.values[keyValue.key] = keyValue.value;
|
||||
applyProxyConfigValue(current, keyValue, sections);
|
||||
}
|
||||
}
|
||||
|
||||
return { globals: trimTrailingBlankLines(globals), sections };
|
||||
}
|
||||
|
||||
function migrateFrpGlobalLine(line) {
|
||||
const keyMap = {
|
||||
server_addr: "serverAddr",
|
||||
server_port: "serverPort",
|
||||
token: "auth.token",
|
||||
tls_enable: "transport.tls.enable",
|
||||
tcp_mux: "transport.tcpMux",
|
||||
log_file: "log.to",
|
||||
log_level: "log.level",
|
||||
log_max_days: "log.maxDays",
|
||||
};
|
||||
const match = line.match(/^(\s*)([A-Za-z0-9_.-]+)(\s*=.*)$/);
|
||||
if (!match) {
|
||||
return line;
|
||||
}
|
||||
|
||||
const [, indent, key, rest] = match;
|
||||
return `${indent}${keyMap[key] || key}${rest}`;
|
||||
}
|
||||
|
||||
function applyProxyConfigValue(current, keyValue, sections) {
|
||||
const key = normalizeProxyKey(keyValue.key);
|
||||
|
||||
if (key === "name") {
|
||||
if (current.name) {
|
||||
sections.delete(current.name);
|
||||
}
|
||||
|
||||
current.name = String(keyValue.value);
|
||||
sections.set(current.name, current);
|
||||
return;
|
||||
}
|
||||
|
||||
current.values[key] = keyValue.value;
|
||||
}
|
||||
|
||||
function normalizeProxyKey(key) {
|
||||
const keyMap = {
|
||||
localIP: "local_ip",
|
||||
localPort: "local_port",
|
||||
remotePort: "remote_port",
|
||||
};
|
||||
|
||||
return keyMap[key] || key;
|
||||
}
|
||||
|
||||
function parseTomlKeyValue(line) {
|
||||
const withoutComment = line.replace(/\s+#.*$/, "").trim();
|
||||
const match = withoutComment.match(/^([A-Za-z0-9_.-]+)\s*=\s*(.+)$/);
|
||||
@@ -374,11 +453,12 @@ function renderFrpConfig(globals, proxies) {
|
||||
}
|
||||
|
||||
function renderProxy(name, values) {
|
||||
return `[${name}]
|
||||
return `[[proxies]]
|
||||
name = ${formatTomlValue(name)}
|
||||
type = ${formatTomlValue(values.type || "tcp")}
|
||||
local_ip = ${formatTomlValue(values.local_ip || "127.0.0.1")}
|
||||
local_port = ${formatTomlValue(values.local_port)}
|
||||
remote_port = ${formatTomlValue(values.remote_port)}
|
||||
localIP = ${formatTomlValue(values.local_ip || "127.0.0.1")}
|
||||
localPort = ${formatTomlValue(values.local_port)}
|
||||
remotePort = ${formatTomlValue(values.remote_port)}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ test("parses and renders frp proxy sections", () => {
|
||||
const parsed = parseFrpConfig(`server_addr = "81.70.134.9"
|
||||
server_port = 15443
|
||||
token = "secret"
|
||||
log_file = "/var/log/frpc.log"
|
||||
|
||||
[ssh]
|
||||
type = "tcp"
|
||||
@@ -32,8 +33,32 @@ remote_port = 17227
|
||||
|
||||
const rendered = renderParsedFrpConfig(parsed);
|
||||
|
||||
assert.match(rendered, /\[ssh]/);
|
||||
assert.match(rendered, /local_port = 22/);
|
||||
assert.match(rendered, /\[mysql]/);
|
||||
assert.match(rendered, /remote_port = 33061/);
|
||||
assert.match(rendered, /serverAddr = "81.70.134.9"/);
|
||||
assert.match(rendered, /serverPort = 15443/);
|
||||
assert.match(rendered, /auth.token = "secret"/);
|
||||
assert.match(rendered, /log.to = "\/var\/log\/frpc.log"/);
|
||||
assert.match(rendered, /\[\[proxies\]\]/);
|
||||
assert.match(rendered, /name = "ssh"/);
|
||||
assert.match(rendered, /localPort = 22/);
|
||||
assert.match(rendered, /name = "mysql"/);
|
||||
assert.match(rendered, /remotePort = 33061/);
|
||||
assert.doesNotMatch(rendered, /log_file/);
|
||||
});
|
||||
|
||||
test("parses modern frp proxy arrays", () => {
|
||||
const parsed = parseFrpConfig(`serverAddr = "81.70.134.9"
|
||||
serverPort = 15443
|
||||
auth.token = "secret"
|
||||
|
||||
[[proxies]]
|
||||
name = "ssh"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 22
|
||||
remotePort = 17227
|
||||
`);
|
||||
|
||||
assert.equal(parsed.sections.get("ssh").values.local_ip, "127.0.0.1");
|
||||
assert.equal(parsed.sections.get("ssh").values.local_port, 22);
|
||||
assert.equal(parsed.sections.get("ssh").values.remote_port, 17227);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user