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

77 lines
1.9 KiB
TypeScript

/**
* Config management — persists agent config to ~/.tip-agent/config.json
*/
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
const CONFIG_DIR = path.join(os.homedir(), ".tip-agent");
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
export interface AgentConfig {
token: string;
port: number;
maxGb: number;
name: string;
serverUrl: string;
pid?: number;
startedAt?: string;
}
const DEFAULTS: Partial<AgentConfig> = {
port: 1080,
maxGb: 10,
name: os.hostname(),
serverUrl: "https://transceiver-db.context-x.org",
};
export function loadConfig(): AgentConfig | null {
if (!fs.existsSync(CONFIG_FILE)) return null;
try {
const raw = fs.readFileSync(CONFIG_FILE, "utf8");
return JSON.parse(raw) as AgentConfig;
} catch {
return null;
}
}
export function saveConfig(cfg: Partial<AgentConfig>): AgentConfig {
if (!fs.existsSync(CONFIG_DIR)) {
fs.mkdirSync(CONFIG_DIR, { recursive: true });
}
const existing = loadConfig() ?? {};
const merged = { ...DEFAULTS, ...existing, ...cfg } as AgentConfig;
fs.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2), "utf8");
return merged;
}
export function clearConfig(): void {
if (fs.existsSync(CONFIG_FILE)) {
fs.unlinkSync(CONFIG_FILE);
}
}
export function getPidFile(): string {
return path.join(CONFIG_DIR, "agent.pid");
}
export function savePid(pid: number): void {
if (!fs.existsSync(CONFIG_DIR)) {
fs.mkdirSync(CONFIG_DIR, { recursive: true });
}
fs.writeFileSync(getPidFile(), String(pid), "utf8");
}
export function loadPid(): number | null {
const pidFile = getPidFile();
if (!fs.existsSync(pidFile)) return null;
const raw = fs.readFileSync(pidFile, "utf8").trim();
const pid = parseInt(raw, 10);
return isNaN(pid) ? null : pid;
}
export function clearPid(): void {
const pidFile = getPidFile();
if (fs.existsSync(pidFile)) fs.unlinkSync(pidFile);
}