import { Argument, InvalidArgumentError, program } from "commander"; import * as fs from "fs"; import * as os from "os"; import * as path from "path"; import { initClient, startClient } from "./api"; import { PROFILE_DEFAULT, PROFILE_PATH, SERVER_DEFAULT_URL, TOKEN_FREE } from "./constant"; import { getTokenFree } from './sdk'; import { generateUUID } from "./util"; const packageInfo = require("../package.json"); program .name("hlt") .description( "CUBETIQ HTTP tunnel client with free access for local tunneling" ) .version(`v${packageInfo.version}`); // init program .command("init") .description("generate a new client and token with free access") .option("-s --server ", "setting server url", SERVER_DEFAULT_URL) .option( "-t --token ", "setting token (default generate FREE access token)", "" ) .option("-a --access ", "setting token access type", TOKEN_FREE) .option("-c --client ", "setting client (auto generate uuid)") .option( "-k --key ", "setting client api key for authentication access" ) .option("-p --profile ", "setting profile name", PROFILE_DEFAULT) .option("-f --force", "force to generate new client and token", false) .action(async (options) => { initClient(options); }); // start program .command("start") .description("start a connection with specific port") .argument("", "local server port number", (value) => { const port = parseInt(value, 10); if (isNaN(port)) { throw new InvalidArgumentError("Not a number."); } return port; }) .option("-s, --suffix ", "suffix for client name") .option( "-K, --keep_connection ", "keep connection for client and old connection will be closed (override connection)", true ) .option( "-k --key ", "setting client api key for authentication access" ) .option("-a, --access ", "access type (FREE)", TOKEN_FREE) .option("-p, --profile ", "profile name", PROFILE_DEFAULT) .option("-h, --host ", "local host value", "localhost") .option("-o, --origin ", "change request origin") .action((port, options) => { startClient({ port, options, }) }); // config program .command("config") .description("create and update config file for connection") .addArgument( new Argument("", "config type").choices([ "access", "token", "server", "client", "key", ]) ) .argument("", "config value") .option("-p --profile ", "setting profile name", PROFILE_DEFAULT) .action(async (type, value, options) => { if (!type) { console.error("type config is required!"); return; } const configDir = path.resolve(os.homedir(), PROFILE_PATH); if (!fs.existsSync(configDir)) { fs.mkdirSync(configDir); console.log(`config file ${configDir} was created`); } let config: any = {}; const configFilename = `${options.profile}.json`; const configFilePath = path.resolve(configDir, configFilename); if (fs.existsSync(configFilePath)) { config = JSON.parse(fs.readFileSync(configFilePath, "utf8")); } if (!config.server) { config.server = SERVER_DEFAULT_URL; } // Error Code status let errorCode = 0; if (type === "token" || type === "jwt") { config.token = value; } else if (type === "server") { config.server = value; } else if (type === "clientId" || type === "client") { if (!value || value === "" || value === "new") { config.clientId = generateUUID(); } else { config.clientId = value; } console.log(`client: ${config.clientId} was set to config`); } else if (type === "apiKey" || type === "key") { config.apiKey = value; } else if (type === "access") { config.access = (value && value.toUpperCase().trim()) || TOKEN_FREE; // FREE if (config.access === TOKEN_FREE) { await getTokenFree(config.server) .then((resp: any) => { if (resp.data?.token) { config.token = resp.data?.token; } else { errorCode = 1; console.error("Generate free token failed, return with null or empty from server!", resp); return; } }) .catch((err: any) => { errorCode = 1; console.error("cannot get free token from server", err); return; }); } } if (!config.clientId && config.apiKey) { config.clientId = config.apiKey; } if (errorCode === 0) { fs.writeFileSync(configFilePath, JSON.stringify(config, null, 2)); console.log(`${type} config saved successfully to: ${configFilePath}`); } }); // config program .command("config-get") .description("get type from config file") .addArgument( new Argument("", "config type").choices([ "access", "token", "server", "client", "key", ]) ) .option("-p --profile ", "setting profile name", PROFILE_DEFAULT) .action(async (type, options) => { if (!type) { console.error("type config is required!"); return; } const configDir = path.resolve(os.homedir(), PROFILE_PATH); if (!fs.existsSync(configDir)) { console.log(`config file ${configDir} not found`); return; } let config: any = {}; const configFilename = `${options.profile}.json`; const configFilePath = path.resolve(configDir, configFilename); if (fs.existsSync(configFilePath)) { config = JSON.parse(fs.readFileSync(configFilePath, "utf8")); } else { console.log(`config file ${configFilePath} not found`); return; } if (type === "token" || type === "jwt") { console.log(config.token); } else if (type === "server") { console.log(config.server); } else if (type === "clientId" || type === "client") { console.log(config.clientId); } else if (type === "apiKey" || type === "key") { console.log(config.apiKey); } else if (type === "access") { console.log(config.access); } else { console.log('no config found for type: "' + type + '"'); } }); program.parse();