From 7e4791dc4bf5bb2ae33a551de4e24243a210736a Mon Sep 17 00:00:00 2001 From: bipproduction Date: Thu, 25 Sep 2025 14:22:55 +0800 Subject: [PATCH] tambahannya --- bin/src/frp.ts | 220 ++++++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 116 insertions(+), 106 deletions(-) diff --git a/bin/src/frp.ts b/bin/src/frp.ts index ebaa45a..9bb69dc 100644 --- a/bin/src/frp.ts +++ b/bin/src/frp.ts @@ -6,159 +6,169 @@ import * as path from "path"; const CONFIG_FILE = path.join(os.homedir(), ".frpdev.conf"); interface FrpConfig { - FRP_HOST: string; - FRP_PORT: string; - FRP_USER: string; - FRP_SECRET: string; - FRP_PROTO: string; + FRP_HOST: string; + FRP_PORT: string; + FRP_USER: string; + FRP_SECRET: string; + FRP_PROTO: string; } interface ProxyConf { - type?: string; - remotePort?: number; - subdomain?: string; - customDomains?: string[]; + type?: string; + remotePort?: number; + subdomain?: string; + customDomains?: string[]; } interface Proxy { - name?: string; - status?: string; - conf?: ProxyConf; + name?: string; + status?: string; + conf?: ProxyConf; } interface ProxyResponse { - proxies?: Proxy[]; + proxies?: Proxy[]; } async function ensureConfigFile(): Promise { - try { - await fs.access(CONFIG_FILE); - } catch { - const template = ` + try { + await fs.access(CONFIG_FILE); + } catch { + const template = ` FRP_HOST="" FRP_USER="" FRP_SECRET="" `; - console.error(`❌ Config not found. Template created at: ${CONFIG_FILE}`); - console.log(template); - process.exit(1); - } + console.error(`❌ Config not found. Template created at: ${CONFIG_FILE}`); + console.log(template); + process.exit(1); + } } async function loadConfig(): Promise { - await ensureConfigFile(); + await ensureConfigFile(); - const raw = await fs.readFile(CONFIG_FILE, "utf8"); - const lines = raw - .split("\n") - .map((line) => line.trim()) - .filter(Boolean); + const raw = await fs.readFile(CONFIG_FILE, "utf8"); + const lines = raw + .split("\n") + .map((line) => line.trim()) + .filter(Boolean); - const conf: Record = {}; - for (const line of lines) { - const [key, ...rest] = line.split("="); - if (!key) continue; - let value = rest.join("=").trim(); + const conf: Record = {}; + for (const line of lines) { + const [key, ...rest] = line.split("="); + if (!key) continue; + let value = rest.join("=").trim(); - if (value.startsWith('"') && value.endsWith('"')) { - value = value.slice(1, -1); + if (value.startsWith('"') && value.endsWith('"')) { + value = value.slice(1, -1); + } + conf[key] = value; } - conf[key] = value; - } - return { - FRP_HOST: conf.FRP_HOST || "", - FRP_PORT: "443", - FRP_USER: conf.FRP_USER || "", - FRP_SECRET: conf.FRP_SECRET || "", - FRP_PROTO: "https", - }; + return { + FRP_HOST: conf.FRP_HOST || "", + FRP_PORT: "443", + FRP_USER: conf.FRP_USER || "", + FRP_SECRET: conf.FRP_SECRET || "", + FRP_PROTO: "https", + }; } async function fetchFrp(config: FrpConfig, url: string): Promise { - const fullUrl = `${config.FRP_PROTO}://${config.FRP_HOST}:${config.FRP_PORT}${url}`; + const fullUrl = `${config.FRP_PROTO}://${config.FRP_HOST}:${config.FRP_PORT}${url}`; - try { - const resp = await fetch(fullUrl, { - headers: { - Authorization: - "Basic " + - Buffer.from(`${config.FRP_USER}:${config.FRP_SECRET}`).toString("base64"), - }, - }); + try { + const resp = await fetch(fullUrl, { + headers: { + Authorization: + "Basic " + + Buffer.from(`${config.FRP_USER}:${config.FRP_SECRET}`).toString("base64"), + }, + }); - if (!resp.ok) return { proxies: [] }; + if (!resp.ok) return { proxies: [] }; - return (await resp.json()) as ProxyResponse; - } catch { - return { proxies: [] }; - } + return (await resp.json()) as ProxyResponse; + } catch { + return { proxies: [] }; + } } function sortProxies(proxies: Proxy[]): Proxy[] { - return [...proxies].sort((a, b) => { - const order = (status?: string) => - status?.toLowerCase() === "online" || status?.toLowerCase() === "running" - ? 0 - : 1; - return order(a.status) - order(b.status); - }); + return [...proxies].sort((a, b) => { + // Urutan utama: status (online/running duluan) + const order = (status?: string) => + status?.toLowerCase() === "online" || status?.toLowerCase() === "running" + ? 0 + : 1; + + const statusDiff = order(a.status) - order(b.status); + if (statusDiff !== 0) return statusDiff; + + // Jika status sama, urutkan berdasarkan remotePort numerik (ascending) + const portA = a.conf?.remotePort ?? Number.MAX_SAFE_INTEGER; + const portB = b.conf?.remotePort ?? Number.MAX_SAFE_INTEGER; + + return portA - portB; + }); } -function formatTable(headers: string[], rows: string[][]): string { - const allRows = [headers, ...rows]; - const colWidths = headers.map((_, i) => - Math.max(...allRows.map((row) => (row[i] || "").length)), - ); - return allRows - .map((row) => - row.map((cell, i) => (cell || "").padEnd(colWidths[i] ?? 0)).join(" ").trimEnd(), - ) - .join("\n"); +function formatTable(headers: string[], rows: string[][]): string { + const allRows = [headers, ...rows]; + const colWidths = headers.map((_, i) => + Math.max(...allRows.map((row) => (row[i] || "").length)), + ); + + return allRows + .map((row) => + row.map((cell, i) => (cell || "").padEnd(colWidths[i] ?? 0)).join(" ").trimEnd(), + ) + .join("\n"); } async function printTable( - title: string, - headers: string[], - rows: string[][], + title: string, + headers: string[], + rows: string[][], ): Promise { - console.log(`========== ${title} ==========`); + console.log(`========== ${title} ==========`); - if (rows.length === 0) { - console.log("No proxies found.\n"); - return; - } + if (rows.length === 0) { + console.log("No proxies found.\n"); + return; + } - console.log(formatTable(headers, rows)); - console.log(); + console.log(formatTable(headers, rows)); + console.log(); } async function frp(): Promise { - const config = await loadConfig(); + const config = await loadConfig(); - const [tcpResp, httpResp] = await Promise.all([ - fetchFrp(config, "/api/proxy/tcp"), - fetchFrp(config, "/api/proxy/http"), - ]); + const [tcpResp, httpResp] = await Promise.all([ + fetchFrp(config, "/api/proxy/tcp"), + fetchFrp(config, "/api/proxy/http"), + ]); - const tcpRows: string[][] = sortProxies(tcpResp.proxies || []).map((p) => [ - p.name ?? "-", - p.status ?? "-", - p.conf?.remotePort?.toString() ?? "-", - ]); - await printTable("TCP PROXIES", ["NAME", "STATUS", "PORT"], tcpRows); + const tcpRows: string[][] = sortProxies(tcpResp.proxies || []).map((p) => [ + p.name ?? "-", + p.status ?? "-", + p.conf?.remotePort?.toString() ?? "-", + ]); + await printTable("TCP PROXIES", ["NAME", "STATUS", "PORT"], tcpRows); - const httpRows: string[][] = sortProxies(httpResp.proxies || []).map((p) => [ - p.name ?? "-", - p.status ?? "-", - Array.isArray(p.conf?.customDomains) ? p.conf.customDomains.join(",") : "", - ]); - await printTable( - "HTTP PROXIES", - ["NAME", "STATUS", "CUSTOM_DOMAIN"], - httpRows, - ); + const httpRows: string[][] = sortProxies(httpResp.proxies || []).map((p) => [ + p.name ?? "-", + p.status ?? "-", + Array.isArray(p.conf?.customDomains) ? p.conf.customDomains.join(",") : "", + ]); + await printTable( + "HTTP PROXIES", + ["NAME", "STATUS", "CUSTOM_DOMAIN"], + httpRows, + ); } export default frp; diff --git a/package.json b/package.json index beb3df5..fc4c4dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "g3n", - "version": "1.0.8", + "version": "1.0.9", "type": "module", "bin": { "g3n": "./bin/g3n.ts"