Rene Fichtmueller edc9311d7b feat: add proxy network, image backfill, and scraper improvements
- Add TIP Proxy Network (packages/proxy-agent): SOCKS5 proxy agent
  for residential IP bypass of CloudFront WAF blocks
- Add /api/proxy/* routes: node registration, heartbeat, load balancing
- Add image extraction to Flexoptix catalog scraper (GraphQL small_image)
- Add image extraction to Optcore scraper (Playwright gallery img)
- Fix Fluxlight price scraping (BigCommerce HTML structure: data-product-price-without-tax)
- Add SmartOptics scraper (8 DWDM/coherent products, og:image extraction)
- Fix findOrCreateScrapedTransceiver to update image_url for existing records
- Add image backfill script (backfill-images.ts): 178 Flexoptix images added
- Fix DB connection pool: max 5, idleTimeoutMillis 10s (was unlimited, caused >100 connections)
- Add proxy.ts utility for scraper proxy rotation
2026-04-03 21:13:03 +02:00

168 lines
4.0 KiB
JavaScript

#!/usr/bin/env node
/**
* TIP Proxy Agent CLI
*
* Usage:
* tip-agent start --token TOKEN [--port 1080] [--max-gb 10] [--name "My Node"]
* tip-agent status
* tip-agent stop
*/
import { startAgent } from "./agent";
import { loadConfig, saveConfig, loadPid, clearPid } from "./config";
const AGENT_VERSION = "1.0.0";
function parseArgs(argv: string[]): Record<string, string> {
const args: Record<string, string> = {};
for (let i = 0; i < argv.length; i++) {
const arg = argv[i];
if (arg && arg.startsWith("--")) {
const key = arg.slice(2);
const next = argv[i + 1];
if (next && !next.startsWith("--")) {
args[key] = next;
i++;
} else {
args[key] = "true";
}
}
}
return args;
}
function isProcessRunning(pid: number): boolean {
try {
process.kill(pid, 0);
return true;
} catch {
return false;
}
}
function cmdStart(rawArgs: string[]): void {
const args = parseArgs(rawArgs);
const token = args["token"];
if (!token) {
console.error("Error: --token is required");
console.error(" tip-agent start --token YOUR_TOKEN [--port 1080] [--max-gb 10] [--name 'My Node']");
process.exit(1);
}
const cfg = saveConfig({
token,
port: args["port"] ? parseInt(args["port"], 10) : 1080,
maxGb: args["max-gb"] ? parseFloat(args["max-gb"]) : 10,
name: args["name"] ?? undefined,
serverUrl: args["server"] ?? "https://transceiver-db.context-x.org",
});
console.log(`\nTIP Proxy Agent v${AGENT_VERSION}`);
console.log("==========================================");
console.log(` Token: ${cfg.token.slice(0, 8)}...`);
console.log(` Port: ${cfg.port}`);
console.log(` Max GB: ${cfg.maxGb}`);
console.log(` Name: ${cfg.name}`);
console.log(` Server: ${cfg.serverUrl}`);
console.log("==========================================\n");
startAgent(cfg).catch((err) => {
console.error("[agent] Fatal error:", err.message);
process.exit(1);
});
}
function cmdStatus(): void {
const cfg = loadConfig();
const pid = loadPid();
if (!cfg || !pid) {
console.log("TIP Agent: not running");
process.exit(0);
}
const running = isProcessRunning(pid);
console.log(`TIP Agent: ${running ? "RUNNING" : "STOPPED (stale PID)"}`);
console.log(` PID: ${pid}`);
console.log(` Port: ${cfg.port}`);
console.log(` Token: ${cfg.token.slice(0, 8)}...`);
console.log(` Max GB: ${cfg.maxGb}`);
if (cfg.startedAt) {
console.log(` Started: ${cfg.startedAt}`);
}
if (!running) {
clearPid();
}
}
function cmdStop(): void {
const pid = loadPid();
if (!pid) {
console.log("TIP Agent: not running");
process.exit(0);
}
try {
process.kill(pid, "SIGTERM");
console.log(`TIP Agent (PID ${pid}) stopped.`);
clearPid();
} catch {
console.log(`TIP Agent: process ${pid} not found (already stopped)`);
clearPid();
}
}
function printHelp(): void {
console.log(`
TIP Proxy Agent v${AGENT_VERSION}
Contribute bandwidth to the TIP network, get free API access.
Usage:
tip-agent start --token TOKEN [options]
tip-agent status
tip-agent stop
Commands:
start Start the proxy agent
status Show agent status
stop Stop the agent
Options:
--token TOKEN Your TIP node token (required for start)
--port N SOCKS5 listen port (default: 1080)
--max-gb N Max bandwidth to donate in GB (default: 10)
--name NAME Node name (default: hostname)
--server URL TIP server URL (default: https://transceiver-db.context-x.org)
Example:
tip-agent start --token abc123 --port 1080 --max-gb 10 --name "Home Node"
`);
}
const [, , command, ...rest] = process.argv;
switch (command) {
case "start":
cmdStart(rest);
break;
case "status":
cmdStatus();
break;
case "stop":
cmdStop();
break;
case "--help":
case "-h":
case "help":
printHelp();
break;
default:
if (command) {
console.error(`Unknown command: ${command}`);
}
printHelp();
process.exit(command ? 1 : 0);
}