Files
g3n/bin/g3n.ts
2025-10-07 16:26:23 +08:00

208 lines
4.5 KiB
TypeScript
Executable File

#!/usr/bin/env bun
import minimist from "minimist";
import os from "os";
import path from "path";
import compose from "./src/compose";
import generateDockerfile from "./src/docker-file";
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 appCreate from "./src/app-create";
interface CheckPortResult {
port: number;
open: boolean;
}
// Default constants (12-Factor App)
const DEFAULTS = {
ENV_FILE: ".env",
ENV_OUT: "types/env.d.ts",
PORT_START: 3000,
PORT_END: 4000,
HOST: "127.0.0.1",
};
// CLI Help
const HELP_TEXT = `
g3n [command] [options]
Commands:
env Generate env.d.ts from .env file
scan-port Scan port range (default 3000-4000)
route Generate routes.ts from AppRoutes.tsx
compose Generate compose.yml from name
docker-file Generate Dockerfile
frp Show frp proxy list
app-create Generate app with Elysia and React
Options:
--env Path ke file .env (default: .env)
--out Path file output (default: types/env.d.ts)
--start Port awal scan (default: 3000)
--end Port akhir scan (default: 4000)
--host Host/IP target (default: 127.0.0.1)
Examples:
g3n env --env .env.local --out src/types/env.d.ts
g3n scan-port --start 7700 --end 7800 --host 127.0.0.1
g3n route
g3n compose <name>
g3n docker-file
g3n frp
g3n app-create <name>
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=
FRP_AUTH_TOKEN=
`
Bun.write(g3nConf, conf);
console.log(`✅ G3N config created at ${g3nConf}`);
}
// Parse CLI arguments
const args = minimist(process.argv.slice(2));
/**
* Main CLI handler
*/
async function main(): Promise<void> {
const [command, name] = args._;
switch (command) {
case "env":
handleEnv();
break;
case "scan-port":
await handleScanPort();
break;
case "route":
route();
break;
case "compose":
handleCompose(name);
break;
case "docker-file":
generateDockerfile();
break;
case "frp":
frp().catch((err) => {
console.error("❌ Error:", err);
process.exit(1);
});
break;
case "app-create":
if (!name) {
console.error("❌ App name is required");
return;
}
appCreate({ appName: name });
break;
default:
console.error(HELP_TEXT);
break;
}
}
/**
* Handle "env" command
*/
function handleEnv(): void {
const envFile = args.env || DEFAULTS.ENV_FILE;
const output = args.out || DEFAULTS.ENV_OUT;
generateEnvTypes({
envFilePath: envFile,
outputDir: path.dirname(output),
outputFileName: path.basename(output),
});
console.log(`✅ Env types generated at ${output}`);
}
/**
* Handle "scan-port" command
*/
async function handleScanPort(): Promise<void> {
const start = Number(args.start) || DEFAULTS.PORT_START;
const end = Number(args.end) || DEFAULTS.PORT_END;
const host = args.host || DEFAULTS.HOST;
console.log(`🔍 Scanning ports ${start}-${end} on host ${host}...`);
const ports = Array.from({ length: end - start + 1 }, (_, i) => start + i);
const results: CheckPortResult[] = await Promise.all(
ports.map((port) => checkPort(port, host))
);
const openPorts = results.filter((r) => r.open);
openPorts.forEach((r) => console.log(`✅ Port ${r.port} is open`));
console.log("✅ Scan completed");
}
/**
* Handle "compose" command
*/
function handleCompose(name?: string): void {
if (!name) {
console.error("❌ Compose name is required");
return;
}
if (!args.env) {
console.error("❌ Compose env (staging/prod) is required");
return;
}
if (args.env !== "staging" && args.env !== "prod") {
console.error("❌ Compose env (staging/prod) is required");
return;
}
if (!args.port) {
console.error("❌ Compose port is required");
return;
}
const _port = args.port.toString().padStart(2, "0");
if(_port.length !== 2) {
console.error(`❌ Compose port must be 2 digits [00-99] , ${args.port}`);
return;
}
compose(name, args.env, _port);
console.log(`✅ Compose file generated for ${name}`);
}
// Execute CLI
main().catch((err) => {
console.error("❌ Unexpected error:", err);
process.exit(1);
});