tambahan
This commit is contained in:
35
bin/g3n.ts
35
bin/g3n.ts
@@ -1,15 +1,18 @@
|
|||||||
#!/usr/bin/env bun
|
#!/usr/bin/env bun
|
||||||
import minimist from "minimist";
|
import minimist from "minimist";
|
||||||
|
import os from "os";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
import { generateEnvTypes } from "../generate/env.generate";
|
|
||||||
import checkPort from "./src/port";
|
|
||||||
import route from "./src/route";
|
|
||||||
import compose from "./src/compose";
|
import compose from "./src/compose";
|
||||||
import generateDockerfile from "./src/docker-file";
|
import generateDockerfile from "./src/docker-file";
|
||||||
import frp from "./src/frp";
|
import frp from "./src/frp";
|
||||||
|
import { generateEnvTypes } from "./src/generate/env.generate";
|
||||||
|
import checkPort from "./src/port";
|
||||||
|
import route from "./src/route";
|
||||||
|
|
||||||
import { version } from '../package.json' assert { type: 'json' };
|
import { version } from '../package.json' assert { type: 'json' };
|
||||||
|
import appCreate from "./src/app-create";
|
||||||
|
|
||||||
|
|
||||||
interface CheckPortResult {
|
interface CheckPortResult {
|
||||||
port: number;
|
port: number;
|
||||||
@@ -36,6 +39,7 @@ Commands:
|
|||||||
compose Generate compose.yml from name
|
compose Generate compose.yml from name
|
||||||
docker-file Generate Dockerfile
|
docker-file Generate Dockerfile
|
||||||
frp Show frp proxy list
|
frp Show frp proxy list
|
||||||
|
app-init Generate app-init.ts
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--env Path ke file .env (default: .env)
|
--env Path ke file .env (default: .env)
|
||||||
@@ -51,10 +55,28 @@ Examples:
|
|||||||
g3n compose <name>
|
g3n compose <name>
|
||||||
g3n docker-file
|
g3n docker-file
|
||||||
g3n frp
|
g3n frp
|
||||||
|
g3n app-init
|
||||||
|
|
||||||
Version: ${version}
|
Version: ${version}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const g3nConf = path.join(os.homedir(), ".g3n.conf");
|
||||||
|
|
||||||
|
if (!(await Bun.file(g3nConf).exists())) {
|
||||||
|
const conf = `
|
||||||
|
# CODE
|
||||||
|
CODE_TOKEN=
|
||||||
|
|
||||||
|
# FRP
|
||||||
|
FRP_HOST=
|
||||||
|
FRP_USER=
|
||||||
|
FRP_SECRET=
|
||||||
|
`
|
||||||
|
Bun.write(g3nConf, conf);
|
||||||
|
console.log(`✅ G3N config created at ${g3nConf}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Parse CLI arguments
|
// Parse CLI arguments
|
||||||
const args = minimist(process.argv.slice(2));
|
const args = minimist(process.argv.slice(2));
|
||||||
|
|
||||||
@@ -90,6 +112,13 @@ async function main(): Promise<void> {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case "app-create":
|
||||||
|
if (!name) {
|
||||||
|
console.error("❌ App name is required");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
appCreate({ appName: name });
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.error(HELP_TEXT);
|
console.error(HELP_TEXT);
|
||||||
|
|||||||
160
bin/src/app-create.ts
Normal file
160
bin/src/app-create.ts
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
import ora from "ora"
|
||||||
|
|
||||||
|
const appRoutesTemplate = `
|
||||||
|
import { BrowserRouter, Routes, Route } from "react-router-dom";
|
||||||
|
import Home from "./pages/Home";
|
||||||
|
import NotFound from "./pages/NotFound";
|
||||||
|
|
||||||
|
export default function AppRoutes() {
|
||||||
|
return (
|
||||||
|
<BrowserRouter>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<Home />} />
|
||||||
|
<Route path="*" element={<NotFound />} />
|
||||||
|
</Routes>
|
||||||
|
</BrowserRouter>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
const postCssTemplate = `
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
'postcss-preset-mantine': {},
|
||||||
|
'postcss-simple-vars': {
|
||||||
|
variables: {
|
||||||
|
'mantine-breakpoint-xs': '36em',
|
||||||
|
'mantine-breakpoint-sm': '48em',
|
||||||
|
'mantine-breakpoint-md': '62em',
|
||||||
|
'mantine-breakpoint-lg': '75em',
|
||||||
|
'mantine-breakpoint-xl': '88em',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
`
|
||||||
|
|
||||||
|
const appTemplate = `
|
||||||
|
import '@mantine/core/styles.css';
|
||||||
|
|
||||||
|
import { MantineProvider } from '@mantine/core';
|
||||||
|
import AppRoutes from './AppRoutes';
|
||||||
|
|
||||||
|
export function App() {
|
||||||
|
return <MantineProvider>
|
||||||
|
<AppRoutes />
|
||||||
|
</MantineProvider>;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const homeTemplate = `
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Home</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const serverTemplate = `
|
||||||
|
import Elysia from "elysia";
|
||||||
|
import Swagger from "@elysiajs/swagger";
|
||||||
|
import html from "./index.html"
|
||||||
|
|
||||||
|
const Docs = new Elysia({})
|
||||||
|
.use(Swagger({
|
||||||
|
path: "/docs",
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
const Api = new Elysia({
|
||||||
|
prefix: "/api",
|
||||||
|
})
|
||||||
|
.use(Docs)
|
||||||
|
.post("/hello", () => "Hello, world!")
|
||||||
|
|
||||||
|
|
||||||
|
const app = new Elysia()
|
||||||
|
.use(Api)
|
||||||
|
.get("/*", html)
|
||||||
|
.listen(3000, () => {
|
||||||
|
console.log("Server running at http://localhost:3000");
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export type Server = typeof app;
|
||||||
|
`
|
||||||
|
|
||||||
|
const notFoundTemplate = `
|
||||||
|
export default function NotFound() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>404 Not Found</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const cmd = (appName: string) => `
|
||||||
|
bun init --react ${appName}
|
||||||
|
echo "init react"
|
||||||
|
cd ${appName}
|
||||||
|
echo "cd ${appName}"
|
||||||
|
bun add react-router-dom
|
||||||
|
echo "add react-router-dom"
|
||||||
|
bun add @mantine/core @mantine/hooks
|
||||||
|
echo "add @mantine/core @mantine/hooks"
|
||||||
|
bun add --dev postcss postcss-preset-mantine postcss-simple-vars
|
||||||
|
echo "add --dev postcss postcss-preset-mantine postcss-simple-vars"
|
||||||
|
bun add elysia @elysiajs/cors @elysiajs/swagger @elysiajs/eden
|
||||||
|
echo "add elysia @elysiajs/cors @elysiajs/swagger @elysiajs/eden"
|
||||||
|
cat <<EOF > postcss.config.js
|
||||||
|
${postCssTemplate}
|
||||||
|
EOF
|
||||||
|
echo "postcss.config.js"
|
||||||
|
cat <<EOF > src/App.tsx
|
||||||
|
${appTemplate}
|
||||||
|
EOF
|
||||||
|
echo "src/App.tsx"
|
||||||
|
|
||||||
|
cat <<EOF > src/AppRoutes.tsx
|
||||||
|
${appRoutesTemplate}
|
||||||
|
EOF
|
||||||
|
echo "src/AppRoutes.tsx"
|
||||||
|
|
||||||
|
mkdir src/pages
|
||||||
|
echo "mkdir src/pages"
|
||||||
|
cat <<EOF > src/pages/Home.tsx
|
||||||
|
${homeTemplate}
|
||||||
|
EOF
|
||||||
|
echo "src/pages/Home.tsx"
|
||||||
|
|
||||||
|
cat <<EOF > src/index.tsx
|
||||||
|
${serverTemplate}
|
||||||
|
EOF
|
||||||
|
echo "src/index.tsx"
|
||||||
|
|
||||||
|
cat <<EOF > src/pages/NotFound.tsx
|
||||||
|
${notFoundTemplate}
|
||||||
|
EOF
|
||||||
|
echo "src/pages/NotFound.tsx"
|
||||||
|
|
||||||
|
rm src/APITester.tsx
|
||||||
|
|
||||||
|
ls
|
||||||
|
|
||||||
|
echo "✅ done"
|
||||||
|
`
|
||||||
|
|
||||||
|
|
||||||
|
export default async function appCreate({ appName }: { appName: string }) {
|
||||||
|
const spinner = ora(`Creating app ${appName}...`).start();
|
||||||
|
const { stdout } = Bun.spawnSync(["bash", "-c", cmd(appName)]);
|
||||||
|
spinner.stop();
|
||||||
|
console.log(stdout.toString());
|
||||||
|
}
|
||||||
41
bin/src/code.ts
Normal file
41
bin/src/code.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import path from "path";
|
||||||
|
import ora from "ora";
|
||||||
|
|
||||||
|
async function query({ question }: { question: string }) {
|
||||||
|
const spinner = ora("Processing...").start();
|
||||||
|
const response = await fetch(
|
||||||
|
"https://cloud-aiflow.wibudev.com/api/v1/prediction/4da85628-c638-43d3-9491-4cd0a7e6b1b8",
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer v3WdPjn61bNDsEYCO5_LYPRs16ICKjpQE6lF60DjpNo",
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({ question })
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const result: { text: string } = await response.json() as { text: string };
|
||||||
|
spinner.stop();
|
||||||
|
return result.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function code({ sourcePath }: { sourcePath: string }) {
|
||||||
|
const file = Bun.file(sourcePath);
|
||||||
|
const content = await file.text();
|
||||||
|
const result = await query({
|
||||||
|
question: content
|
||||||
|
});
|
||||||
|
|
||||||
|
const output = `${path.dirname(sourcePath)}/${path.basename(sourcePath, path.extname(sourcePath))}.md`;
|
||||||
|
|
||||||
|
Bun.write(output, result);
|
||||||
|
console.log(`✅ Code generated at ${output}`);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import.meta.main) {
|
||||||
|
code({ sourcePath: "bin/g3n.ts" });
|
||||||
|
}
|
||||||
|
|
||||||
|
export default code;
|
||||||
@@ -3,7 +3,7 @@ import { promises as fs } from "fs";
|
|||||||
import * as os from "os";
|
import * as os from "os";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
|
|
||||||
const CONFIG_FILE = path.join(os.homedir(), ".frpdev.conf");
|
const CONFIG_FILE = path.join(os.homedir(), ".g3n.conf");
|
||||||
|
|
||||||
interface FrpConfig {
|
interface FrpConfig {
|
||||||
FRP_HOST: string;
|
FRP_HOST: string;
|
||||||
|
|||||||
35
bun.lock
35
bun.lock
@@ -12,6 +12,7 @@
|
|||||||
"dedent": "^1.7.0",
|
"dedent": "^1.7.0",
|
||||||
"dotenv": "^17.2.1",
|
"dotenv": "^17.2.1",
|
||||||
"minimist": "^1.2.8",
|
"minimist": "^1.2.8",
|
||||||
|
"ora": "^9.0.0",
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"typescript": "^5",
|
"typescript": "^5",
|
||||||
@@ -49,22 +50,56 @@
|
|||||||
|
|
||||||
"@types/minimist": ["@types/minimist@1.2.5", "", {}, "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag=="],
|
"@types/minimist": ["@types/minimist@1.2.5", "", {}, "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag=="],
|
||||||
|
|
||||||
|
"ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||||
|
|
||||||
|
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
|
||||||
|
|
||||||
|
"cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="],
|
||||||
|
|
||||||
|
"cli-spinners": ["cli-spinners@3.3.0", "", {}, "sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ=="],
|
||||||
|
|
||||||
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
"debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||||
|
|
||||||
"dedent": ["dedent@1.7.0", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ=="],
|
"dedent": ["dedent@1.7.0", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ=="],
|
||||||
|
|
||||||
"dotenv": ["dotenv@17.2.1", "", {}, "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ=="],
|
"dotenv": ["dotenv@17.2.1", "", {}, "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ=="],
|
||||||
|
|
||||||
|
"get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="],
|
||||||
|
|
||||||
|
"is-interactive": ["is-interactive@2.0.0", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="],
|
||||||
|
|
||||||
|
"is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="],
|
||||||
|
|
||||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||||
|
|
||||||
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
"jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
|
||||||
|
|
||||||
|
"log-symbols": ["log-symbols@7.0.1", "", { "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" } }, "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg=="],
|
||||||
|
|
||||||
|
"mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="],
|
||||||
|
|
||||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||||
|
|
||||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
|
"onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="],
|
||||||
|
|
||||||
|
"ora": ["ora@9.0.0", "", { "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.2.2", "string-width": "^8.1.0", "strip-ansi": "^7.1.2" } }, "sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A=="],
|
||||||
|
|
||||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||||
|
|
||||||
|
"restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="],
|
||||||
|
|
||||||
|
"signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
|
||||||
|
|
||||||
|
"stdin-discarder": ["stdin-discarder@0.2.2", "", {}, "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="],
|
||||||
|
|
||||||
|
"string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="],
|
||||||
|
|
||||||
|
"strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||||
|
|
||||||
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
||||||
|
|
||||||
|
"yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
compose_dir.txt
Normal file
1
compose_dir.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
"https://cld-dkr-makuro-seafile.wibudev.com/seafhttp/files/03c405b3-1ff0-4dff-9283-e18832a7f8d4/compose_dir.txt"
|
||||||
2
index.ts
2
index.ts
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env bun
|
#!/usr/bin/env bun
|
||||||
import minimist from "minimist";
|
import minimist from "minimist";
|
||||||
import { generateEnvTypes } from "./generate/env.generate.js";
|
import { generateEnvTypes } from "./bin/src/generate/env.generate.ts";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import net from "net";
|
import net from "net";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "g3n",
|
"name": "g3n",
|
||||||
"version": "1.0.9",
|
"version": "1.0.13",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"bin": {
|
"bin": {
|
||||||
"g3n": "./bin/g3n.ts"
|
"g3n": "./bin/g3n.ts"
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
"@types/minimist": "^1.2.5",
|
"@types/minimist": "^1.2.5",
|
||||||
"dedent": "^1.7.0",
|
"dedent": "^1.7.0",
|
||||||
"dotenv": "^17.2.1",
|
"dotenv": "^17.2.1",
|
||||||
"minimist": "^1.2.8"
|
"minimist": "^1.2.8",
|
||||||
|
"ora": "^9.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
x.sh
Normal file
2
x.sh
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
curl -o compose_dir.txt -H "Authorization: Bearer fa49bf1774cad2ec89d2882ae2c6ac1f5d7df445" \
|
||||||
|
"https://cld-dkr-makuro-seafile.wibudev.com/api2/repos/e23626dc-cc18-4bb8-8fbc-d103b7d33bc8/file/?p=/compose_dir.txt&op=download"
|
||||||
167
x.ts
167
x.ts
@@ -1,167 +0,0 @@
|
|||||||
#!/usr/bin/env bun
|
|
||||||
import { promises as fs } from "fs";
|
|
||||||
import * as os from "os";
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ProxyConf {
|
|
||||||
type?: string;
|
|
||||||
remotePort?: number;
|
|
||||||
subdomain?: string;
|
|
||||||
customDomains?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Proxy {
|
|
||||||
name?: string;
|
|
||||||
status?: string;
|
|
||||||
conf?: ProxyConf;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ProxyResponse {
|
|
||||||
proxies?: Proxy[];
|
|
||||||
}
|
|
||||||
|
|
||||||
async function ensureConfigFile(): Promise<void> {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadConfig(): Promise<FrpConfig> {
|
|
||||||
await ensureConfigFile();
|
|
||||||
|
|
||||||
const raw = await fs.readFile(CONFIG_FILE, "utf8");
|
|
||||||
const lines = raw
|
|
||||||
.split("\n")
|
|
||||||
.map((line) => line.trim())
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
const conf: Record<string, string> = {};
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
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",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchFrp(config: FrpConfig, url: string): Promise<ProxyResponse> {
|
|
||||||
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"),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!resp.ok) 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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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[][],
|
|
||||||
): Promise<void> {
|
|
||||||
console.log(`========== ${title} ==========`);
|
|
||||||
|
|
||||||
if (rows.length === 0) {
|
|
||||||
console.log("No proxies found.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(formatTable(headers, rows));
|
|
||||||
console.log();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main(): Promise<void> {
|
|
||||||
const config = await loadConfig();
|
|
||||||
|
|
||||||
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 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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
main().catch((err) => {
|
|
||||||
console.error("❌ Error:", err);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user