@studiometa/trafic-agent 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +140 -0
- package/dist/cli.js.map +1 -0
- package/dist/index-C7OaqqbZ.js +865 -0
- package/dist/index-C7OaqqbZ.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/server-CHT47EXr.js +618 -0
- package/dist/server-CHT47EXr.js.map +1 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +9 -0
- package/dist/server.js.map +1 -0
- package/dist/setup/agent.d.ts +17 -0
- package/dist/setup/agent.d.ts.map +1 -0
- package/dist/setup/audit.d.ts +10 -0
- package/dist/setup/audit.d.ts.map +1 -0
- package/dist/setup/ddev.d.ts +27 -0
- package/dist/setup/ddev.d.ts.map +1 -0
- package/dist/setup/docker.d.ts +13 -0
- package/dist/setup/docker.d.ts.map +1 -0
- package/dist/setup/hardening.d.ts +29 -0
- package/dist/setup/hardening.d.ts.map +1 -0
- package/dist/setup/index.d.ts +12 -0
- package/dist/setup/index.d.ts.map +1 -0
- package/dist/setup/steps.d.ts +51 -0
- package/dist/setup/steps.d.ts.map +1 -0
- package/dist/setup/types.d.ts +37 -0
- package/dist/setup/types.d.ts.map +1 -0
- package/dist/setup/user.d.ts +9 -0
- package/dist/setup/user.d.ts.map +1 -0
- package/dist/tasks/stop-idle.d.ts +11 -0
- package/dist/tasks/stop-idle.d.ts.map +1 -0
- package/dist/types.d.ts +99 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/auth.d.ts +25 -0
- package/dist/utils/auth.d.ts.map +1 -0
- package/dist/utils/config.d.ts +14 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/db.d.ts +43 -0
- package/dist/utils/db.d.ts.map +1 -0
- package/dist/utils/ddev.d.ts +35 -0
- package/dist/utils/ddev.d.ts.map +1 -0
- package/dist/utils/project-config.d.ts +23 -0
- package/dist/utils/project-config.d.ts.map +1 -0
- package/package.json +56 -0
- package/templates/error.html +59 -0
- package/templates/wait.html +86 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server-CHT47EXr.js","sources":["../src/utils/auth.ts","../src/utils/ddev.ts","../src/utils/db.ts","../src/utils/project-config.ts","../src/server.ts"],"sourcesContent":["import type { AuthConfig, AuthRequest, AuthResult, AuthRule } from \"../types.js\";\n\n/**\n * Check if an IP matches a pattern (supports CIDR notation)\n */\nexport function matchIp(ip: string, pattern: string): boolean {\n // Exact match\n if (ip === pattern) return true;\n\n // CIDR notation\n if (pattern.includes(\"/\")) {\n return matchCidr(ip, pattern);\n }\n\n // Wildcard (e.g., 192.168.*)\n if (pattern.includes(\"*\")) {\n const regex = new RegExp(\n \"^\" + pattern.replace(/\\./g, \"\\\\.\").replace(/\\*/g, \".*\") + \"$\",\n );\n return regex.test(ip);\n }\n\n return false;\n}\n\n/**\n * Match IP against CIDR range\n */\nfunction matchCidr(ip: string, cidr: string): boolean {\n const [range, bits] = cidr.split(\"/\");\n const mask = parseInt(bits, 10);\n\n const ipNum = ipToNumber(ip);\n const rangeNum = ipToNumber(range);\n\n if (ipNum === null || rangeNum === null) return false;\n\n const maskNum = ~((1 << (32 - mask)) - 1);\n return (ipNum & maskNum) === (rangeNum & maskNum);\n}\n\n/**\n * Convert IP string to number\n */\nfunction ipToNumber(ip: string): number | null {\n const parts = ip.split(\".\");\n if (parts.length !== 4) return null;\n\n let num = 0;\n for (const part of parts) {\n const n = parseInt(part, 10);\n if (isNaN(n) || n < 0 || n > 255) return null;\n num = (num << 8) | n;\n }\n return num >>> 0; // Convert to unsigned\n}\n\n/**\n * Match hostname against a glob pattern\n */\nexport function matchHostname(hostname: string, pattern: string): boolean {\n // Exact match\n if (hostname === pattern) return true;\n\n // Glob pattern\n const regex = new RegExp(\n \"^\" +\n pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, \"\\\\$&\")\n .replace(/\\*/g, \".*\")\n .replace(/\\?/g, \".\") +\n \"$\",\n );\n return regex.test(hostname);\n}\n\n/**\n * Parse Basic auth header\n */\nexport function parseBasicAuth(\n header: string,\n): { username: string; password: string } | null {\n if (!header.startsWith(\"Basic \")) return null;\n\n try {\n const encoded = header.slice(6);\n const decoded = Buffer.from(encoded, \"base64\").toString(\"utf-8\");\n const colonIndex = decoded.indexOf(\":\");\n if (colonIndex === -1) return null;\n\n return {\n username: decoded.slice(0, colonIndex),\n password: decoded.slice(colonIndex + 1),\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Parse Bearer token from header\n */\nexport function parseBearerToken(header: string): string | null {\n if (!header.startsWith(\"Bearer \")) return null;\n return header.slice(7);\n}\n\n/**\n * Check authentication for a request\n */\nexport function checkAuth(request: AuthRequest, config: AuthConfig): AuthResult {\n const { hostname, ip, authorization } = request;\n\n // Get real IP (handle X-Forwarded-For)\n const realIp = request.forwardedFor?.split(\",\")[0].trim() ?? ip;\n\n // Check IP whitelist first (always allows)\n for (const pattern of config.allowedIps) {\n if (matchIp(realIp, pattern)) {\n return { allowed: true, reason: \"ip\" };\n }\n }\n\n // Check per-hostname rules\n for (const rule of config.rules) {\n if (matchHostname(hostname, rule.match)) {\n return checkRule(rule, realIp, authorization, config);\n }\n }\n\n // Apply default policy\n return applyDefaultPolicy(config, realIp, authorization);\n}\n\n/**\n * Check a specific rule\n */\nfunction checkRule(\n rule: AuthRule,\n ip: string,\n authorization: string | undefined,\n config: AuthConfig,\n): AuthResult {\n // Rule-specific IP whitelist\n if (rule.allowedIps) {\n for (const pattern of rule.allowedIps) {\n if (matchIp(ip, pattern)) {\n return { allowed: true, reason: \"rule\" };\n }\n }\n }\n\n switch (rule.policy) {\n case \"allow\":\n return { allowed: true, reason: \"rule\" };\n\n case \"deny\":\n return { allowed: false, reason: \"rule\" };\n\n case \"token\": {\n if (!authorization) return { allowed: false, reason: \"rule\" };\n const token = parseBearerToken(authorization);\n const tokens = rule.tokens ?? config.tokens;\n if (token && tokens.includes(token)) {\n return { allowed: true, reason: \"token\" };\n }\n return { allowed: false, reason: \"rule\" };\n }\n\n case \"basic\": {\n if (!authorization) return { allowed: false, reason: \"rule\" };\n const creds = parseBasicAuth(authorization);\n if (creds) {\n const credString = `${creds.username}:${creds.password}`;\n if (config.basicAuth.includes(credString)) {\n return { allowed: true, reason: \"basic\" };\n }\n }\n return { allowed: false, reason: \"rule\" };\n }\n\n default:\n return { allowed: false, reason: \"rule\" };\n }\n}\n\n/**\n * Apply default policy\n */\nfunction applyDefaultPolicy(\n config: AuthConfig,\n ip: string,\n authorization: string | undefined,\n): AuthResult {\n switch (config.defaultPolicy) {\n case \"allow\":\n return { allowed: true, reason: \"default\" };\n\n case \"deny\":\n return { allowed: false, reason: \"default\" };\n\n case \"basic\": {\n if (!authorization) return { allowed: false, reason: \"default\" };\n const creds = parseBasicAuth(authorization);\n if (creds) {\n const credString = `${creds.username}:${creds.password}`;\n if (config.basicAuth.includes(credString)) {\n return { allowed: true, reason: \"basic\" };\n }\n }\n // Also check tokens as fallback\n const token = parseBearerToken(authorization);\n if (token && config.tokens.includes(token)) {\n return { allowed: true, reason: \"token\" };\n }\n return { allowed: false, reason: \"default\" };\n }\n\n case \"token\": {\n if (!authorization) return { allowed: false, reason: \"default\" };\n const token = parseBearerToken(authorization);\n if (token && config.tokens.includes(token)) {\n return { allowed: true, reason: \"token\" };\n }\n return { allowed: false, reason: \"default\" };\n }\n\n default:\n return { allowed: false, reason: \"default\" };\n }\n}\n","import { readFileSync, existsSync, watch } from \"node:fs\";\nimport { execSync } from \"node:child_process\";\nimport type { DdevProject } from \"../types.js\";\n\n// Simple YAML parser for project_list.yaml (avoid dependencies)\n// The file format is simple: key: value pairs with quoted strings\nfunction parseSimpleYaml(content: string): Record<string, string> {\n const result: Record<string, string> = {};\n const lines = content.split(\"\\n\");\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n\n const colonIndex = trimmed.indexOf(\":\");\n if (colonIndex === -1) continue;\n\n const key = trimmed.slice(0, colonIndex).trim();\n let value = trimmed.slice(colonIndex + 1).trim();\n\n // Remove quotes\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n result[key] = value;\n }\n\n return result;\n}\n\n/**\n * Load projects from DDEV's project_list.yaml\n * Returns a map of project name -> app root path\n */\nexport function loadProjectList(\n projectListPath: string,\n): Map<string, string> {\n if (!existsSync(projectListPath)) {\n return new Map();\n }\n\n const content = readFileSync(projectListPath, \"utf-8\");\n const parsed = parseSimpleYaml(content);\n\n return new Map(Object.entries(parsed));\n}\n\n/**\n * Build a hostname -> project name index\n */\nexport function buildHostnameIndex(\n projects: Map<string, string>,\n tld: string,\n): Map<string, string> {\n const index = new Map<string, string>();\n\n for (const [name] of projects) {\n // Primary hostname: project-name.tld\n index.set(`${name}.${tld}`, name);\n }\n\n return index;\n}\n\n/**\n * Get project name from hostname\n */\nexport function getProjectFromHostname(\n hostname: string,\n index: Map<string, string>,\n): string | undefined {\n return index.get(hostname);\n}\n\n/**\n * Watch project_list.yaml for changes\n */\nexport function watchProjectList(\n projectListPath: string,\n onChange: () => void,\n): void {\n if (!existsSync(projectListPath)) {\n console.warn(`Project list not found: ${projectListPath}`);\n return;\n }\n\n watch(projectListPath, (eventType) => {\n if (eventType === \"change\") {\n onChange();\n }\n });\n}\n\n/**\n * Get detailed project info using ddev describe\n */\nexport function getProjectInfo(name: string): DdevProject | undefined {\n try {\n const output = execSync(`ddev describe ${name} -j`, {\n encoding: \"utf-8\",\n timeout: 10000,\n });\n const data = JSON.parse(output);\n\n if (!data.raw) return undefined;\n\n const raw = data.raw;\n return {\n name: raw.name,\n status: raw.status,\n appRoot: raw.approot,\n httpURLs: raw.httpURLs ?? [],\n httpsURLs: raw.httpsURLs ?? [],\n type: raw.type,\n phpVersion: raw.php_version,\n dbType: raw.dbinfo?.dbType,\n };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Start a DDEV project\n */\nexport function startProject(name: string): boolean {\n try {\n execSync(`ddev start ${name}`, {\n encoding: \"utf-8\",\n timeout: 120000, // 2 minutes\n stdio: \"pipe\",\n });\n return true;\n } catch (error) {\n console.error(`Failed to start project ${name}:`, error);\n return false;\n }\n}\n\n/**\n * Stop a DDEV project\n */\nexport function stopProject(name: string): boolean {\n try {\n execSync(`ddev stop ${name}`, {\n encoding: \"utf-8\",\n timeout: 60000, // 1 minute\n stdio: \"pipe\",\n });\n return true;\n } catch (error) {\n console.error(`Failed to stop project ${name}:`, error);\n return false;\n }\n}\n\n/**\n * List all DDEV projects with their status\n */\nexport function listProjects(): DdevProject[] {\n try {\n const output = execSync(\"ddev list -j\", {\n encoding: \"utf-8\",\n timeout: 30000,\n });\n const data = JSON.parse(output);\n\n if (!Array.isArray(data)) return [];\n\n return data.map((p: Record<string, unknown>) => ({\n name: p.name as string,\n status: p.status as DdevProject[\"status\"],\n appRoot: p.approot as string,\n httpURLs: (p.httpURLs as string[]) ?? [],\n httpsURLs: (p.httpsURLs as string[]) ?? [],\n type: p.type as string,\n }));\n } catch {\n return [];\n }\n}\n","import Database from \"better-sqlite3\";\nimport type { ProjectRecord, AccessLog } from \"../types.js\";\n\nlet db: Database.Database | null = null;\n\n/**\n * Initialize the database connection and schema\n */\nexport function initDb(dbPath: string): Database.Database {\n db = new Database(dbPath);\n db.pragma(\"journal_mode = WAL\");\n\n // Create tables\n db.exec(`\n CREATE TABLE IF NOT EXISTS projects (\n name TEXT PRIMARY KEY,\n last_access INTEGER NOT NULL,\n status TEXT NOT NULL DEFAULT 'stopped'\n );\n\n CREATE TABLE IF NOT EXISTS access_logs (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n project TEXT NOT NULL,\n timestamp INTEGER NOT NULL,\n ip TEXT NOT NULL,\n user_agent TEXT NOT NULL,\n path TEXT NOT NULL\n );\n\n CREATE INDEX IF NOT EXISTS idx_access_logs_project ON access_logs(project);\n CREATE INDEX IF NOT EXISTS idx_access_logs_timestamp ON access_logs(timestamp);\n `);\n\n return db;\n}\n\n/**\n * Get the database instance\n */\nexport function getDb(): Database.Database {\n if (!db) {\n throw new Error(\"Database not initialized. Call initDb first.\");\n }\n return db;\n}\n\n/**\n * Close the database connection\n */\nexport function closeDb(): void {\n if (db) {\n db.close();\n db = null;\n }\n}\n\n/**\n * Update project last access time\n */\nexport function updateProjectAccess(name: string): void {\n const database = getDb();\n const stmt = database.prepare(`\n INSERT INTO projects (name, last_access, status)\n VALUES (?, ?, 'running')\n ON CONFLICT(name) DO UPDATE SET last_access = excluded.last_access\n `);\n stmt.run(name, Date.now());\n}\n\n/**\n * Get project record\n */\nexport function getProject(name: string): ProjectRecord | undefined {\n const database = getDb();\n const stmt = database.prepare(\n \"SELECT name, last_access as lastAccess, status FROM projects WHERE name = ?\",\n );\n return stmt.get(name) as ProjectRecord | undefined;\n}\n\n/**\n * Set project status\n */\nexport function setProjectStatus(\n name: string,\n status: ProjectRecord[\"status\"],\n): void {\n const database = getDb();\n const stmt = database.prepare(`\n INSERT INTO projects (name, last_access, status)\n VALUES (?, ?, ?)\n ON CONFLICT(name) DO UPDATE SET status = excluded.status\n `);\n stmt.run(name, Date.now(), status);\n}\n\n/**\n * Get idle projects (last access older than threshold)\n */\nexport function getIdleProjects(thresholdMs: number): ProjectRecord[] {\n const database = getDb();\n const cutoff = Date.now() - thresholdMs;\n const stmt = database.prepare(`\n SELECT name, last_access as lastAccess, status\n FROM projects\n WHERE last_access < ? AND status = 'running'\n `);\n return stmt.all(cutoff) as ProjectRecord[];\n}\n\n/**\n * Log an access\n */\nexport function logAccess(log: Omit<AccessLog, \"id\">): void {\n const database = getDb();\n const stmt = database.prepare(`\n INSERT INTO access_logs (project, timestamp, ip, user_agent, path)\n VALUES (?, ?, ?, ?, ?)\n `);\n stmt.run(log.project, log.timestamp, log.ip, log.userAgent, log.path);\n}\n\n/**\n * Get recent access logs for a project\n */\nexport function getAccessLogs(project: string, limit = 100): AccessLog[] {\n const database = getDb();\n const stmt = database.prepare(`\n SELECT id, project, timestamp, ip, user_agent as userAgent, path\n FROM access_logs\n WHERE project = ?\n ORDER BY timestamp DESC\n LIMIT ?\n `);\n return stmt.all(project, limit) as AccessLog[];\n}\n\n/**\n * Clean old access logs (older than given days)\n */\nexport function cleanOldLogs(days: number): number {\n const database = getDb();\n const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;\n const stmt = database.prepare(\"DELETE FROM access_logs WHERE timestamp < ?\");\n const result = stmt.run(cutoff);\n return result.changes;\n}\n","import { readFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\n\n/**\n * Per-project Trafic configuration\n * Read from .ddev/config.trafic.yaml\n */\nexport interface ProjectConfig {\n /** Auth policy override: allow | deny | basic | token */\n auth_policy?: \"allow\" | \"deny\" | \"basic\" | \"token\";\n /** Idle timeout override: \"never\" or duration like \"30m\", \"4h\" */\n idle_timeout?: string;\n}\n\n/**\n * Simple YAML parser for config.trafic.yaml\n * Only supports simple key: value pairs (no nesting, no arrays)\n */\nfunction parseSimpleYaml(content: string): Record<string, string> {\n const result: Record<string, string> = {};\n const lines = content.split(\"\\n\");\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // Skip empty lines and comments\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n\n const colonIndex = trimmed.indexOf(\":\");\n if (colonIndex === -1) continue;\n\n const key = trimmed.slice(0, colonIndex).trim();\n let value = trimmed.slice(colonIndex + 1).trim();\n\n // Remove quotes if present\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n result[key] = value;\n }\n\n return result;\n}\n\n/**\n * Load project-specific Trafic config from .ddev/config.trafic.yaml\n */\nexport function loadProjectConfig(projectDir: string): ProjectConfig {\n const configPath = join(projectDir, \".ddev\", \"config.trafic.yaml\");\n\n if (!existsSync(configPath)) {\n return {};\n }\n\n try {\n const content = readFileSync(configPath, \"utf-8\");\n const parsed = parseSimpleYaml(content);\n\n const config: ProjectConfig = {};\n\n // Validate auth_policy\n if (parsed.auth_policy) {\n const policy = parsed.auth_policy.toLowerCase();\n if ([\"allow\", \"deny\", \"basic\", \"token\"].includes(policy)) {\n config.auth_policy = policy as ProjectConfig[\"auth_policy\"];\n }\n }\n\n // Validate idle_timeout\n if (parsed.idle_timeout) {\n config.idle_timeout = parsed.idle_timeout;\n }\n\n return config;\n } catch (error) {\n console.warn(`Failed to load project config from ${configPath}:`, error);\n return {};\n }\n}\n\n/**\n * Check if a project should never be stopped (idle_timeout = \"never\")\n */\nexport function shouldNeverStop(config: ProjectConfig): boolean {\n return config.idle_timeout?.toLowerCase() === \"never\";\n}\n\n/**\n * Get idle timeout in milliseconds, or undefined to use global default\n */\nexport function getIdleTimeoutMs(config: ProjectConfig): number | undefined {\n if (!config.idle_timeout) return undefined;\n if (shouldNeverStop(config)) return undefined;\n\n // Parse duration string (e.g., \"30m\", \"4h\", \"2h30m\")\n const regex = /(?:(\\d+)h)?(?:(\\d+)m)?(?:(\\d+)s)?/;\n const match = config.idle_timeout.match(regex);\n if (!match) return undefined;\n\n const hours = parseInt(match[1] ?? \"0\", 10);\n const minutes = parseInt(match[2] ?? \"0\", 10);\n const seconds = parseInt(match[3] ?? \"0\", 10);\n\n const ms = (hours * 3600 + minutes * 60 + seconds) * 1000;\n return ms > 0 ? ms : undefined;\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\nimport { readFileSync } from \"node:fs\";\nimport { resolve, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { AgentConfig, AuthConfig } from \"./types.js\";\nimport { checkAuth } from \"./utils/auth.js\";\nimport {\n loadProjectList,\n buildHostnameIndex,\n watchProjectList,\n startProject,\n getProjectInfo,\n} from \"./utils/ddev.js\";\nimport {\n initDb,\n updateProjectAccess,\n logAccess,\n setProjectStatus,\n getProject,\n} from \"./utils/db.js\";\nimport { loadProjectConfig } from \"./utils/project-config.js\";\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\n\n// State\nlet config: AgentConfig;\nlet projectList: Map<string, string>;\nlet hostnameIndex: Map<string, string>;\n// Cache of per-project configs (project name -> config)\nconst projectConfigs = new Map<string, ReturnType<typeof loadProjectConfig>>();\n\n/**\n * Load HTML template\n */\nfunction loadTemplate(name: string): string {\n const templatePath = resolve(__dirname, \"..\", \"templates\", `${name}.html`);\n try {\n return readFileSync(templatePath, \"utf-8\");\n } catch {\n return `<html><body><h1>${name}</h1></body></html>`;\n }\n}\n\n/**\n * Get effective auth config for a project (merges global + per-project)\n */\nfunction getEffectiveAuthConfig(projectName: string | undefined): AuthConfig {\n if (!projectName) return config.auth;\n\n const projectConfig = projectConfigs.get(projectName);\n if (!projectConfig?.auth_policy) return config.auth;\n\n // Override default policy with project-specific policy\n return {\n ...config.auth,\n defaultPolicy: projectConfig.auth_policy,\n };\n}\n\n/**\n * Handle forward auth requests from Traefik\n * Traefik sends the original request headers, we return 200 (allow) or 401 (deny)\n */\nfunction handleAuth(req: IncomingMessage, res: ServerResponse): void {\n const hostname = req.headers[\"x-forwarded-host\"] as string ?? \"\";\n const ip = req.headers[\"x-forwarded-for\"] as string ?? req.socket.remoteAddress ?? \"\";\n const authorization = req.headers[\"authorization\"];\n const path = req.headers[\"x-forwarded-uri\"] as string ?? \"/\";\n\n // Find project from hostname\n const projectName = hostnameIndex.get(hostname);\n\n // Get effective auth config (global + per-project overrides)\n const authConfig = getEffectiveAuthConfig(projectName);\n\n const result = checkAuth(\n {\n hostname,\n ip: ip.split(\",\")[0].trim(),\n authorization,\n forwardedFor: ip,\n },\n authConfig,\n );\n\n if (result.allowed) {\n // Log access and update last access time\n if (projectName) {\n updateProjectAccess(projectName);\n logAccess({\n project: projectName,\n timestamp: Date.now(),\n ip: ip.split(\",\")[0].trim(),\n userAgent: req.headers[\"user-agent\"] ?? \"\",\n path,\n });\n }\n\n res.writeHead(200);\n res.end();\n } else {\n // Return 401 with WWW-Authenticate header for basic auth\n res.writeHead(401, {\n \"WWW-Authenticate\": 'Basic realm=\"Trafic\"',\n });\n res.end(\"Unauthorized\");\n }\n}\n\n/**\n * Handle errors middleware requests (502 from Traefik)\n * When a project is stopped, Traefik returns 502. We show a waiting page and start the project.\n */\nasync function handleErrors(\n req: IncomingMessage,\n res: ServerResponse,\n): Promise<void> {\n const hostname = req.headers[\"x-forwarded-host\"] as string ?? req.headers.host ?? \"\";\n const projectName = hostnameIndex.get(hostname);\n\n if (!projectName) {\n // Unknown project\n const template = loadTemplate(\"error\");\n res.writeHead(404, { \"Content-Type\": \"text/html\" });\n res.end(template.replace(\"{{message}}\", \"Project not found\"));\n return;\n }\n\n // Check if project is already starting\n const record = getProject(projectName);\n if (record?.status === \"starting\") {\n // Show waiting page\n const template = loadTemplate(\"wait\");\n res.writeHead(503, {\n \"Content-Type\": \"text/html\",\n \"Retry-After\": \"5\",\n });\n res.end(\n template\n .replace(/\\{\\{project\\}\\}/g, projectName)\n .replace(/\\{\\{hostname\\}\\}/g, hostname),\n );\n return;\n }\n\n // Mark as starting\n setProjectStatus(projectName, \"starting\");\n\n // Show waiting page immediately\n const template = loadTemplate(\"wait\");\n res.writeHead(503, {\n \"Content-Type\": \"text/html\",\n \"Retry-After\": \"5\",\n });\n res.end(\n template\n .replace(/\\{\\{project\\}\\}/g, projectName)\n .replace(/\\{\\{hostname\\}\\}/g, hostname),\n );\n\n // Start project in background\n setTimeout(() => {\n const success = startProject(projectName);\n setProjectStatus(projectName, success ? \"running\" : \"stopped\");\n }, 100);\n}\n\n/**\n * Handle status polling requests\n */\nfunction handleStatus(req: IncomingMessage, res: ServerResponse): void {\n const url = new URL(req.url ?? \"/\", `http://${req.headers.host}`);\n const projectName = url.searchParams.get(\"project\");\n\n if (!projectName) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Missing project parameter\" }));\n return;\n }\n\n const info = getProjectInfo(projectName);\n const record = getProject(projectName);\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n name: projectName,\n status: info?.status ?? record?.status ?? \"unknown\",\n ready: info?.status === \"running\",\n }),\n );\n}\n\n/**\n * Request handler\n */\nasync function handleRequest(\n req: IncomingMessage,\n res: ServerResponse,\n): Promise<void> {\n const url = req.url ?? \"/\";\n\n try {\n // Route requests\n if (url === \"/__auth__\" || url.startsWith(\"/__auth__/\")) {\n handleAuth(req, res);\n } else if (url === \"/__status__\" || url.startsWith(\"/__status__/\")) {\n handleStatus(req, res);\n } else if (url === \"/__health__\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ status: \"ok\", version: \"__VERSION__\" }));\n } else {\n // Default: errors middleware\n await handleErrors(req, res);\n }\n } catch (error) {\n console.error(\"Request error:\", error);\n res.writeHead(500, { \"Content-Type\": \"text/plain\" });\n res.end(\"Internal Server Error\");\n }\n}\n\n/**\n * Reload project list and their configs\n */\nfunction reloadProjects(): void {\n projectList = loadProjectList(config.projectListPath);\n hostnameIndex = buildHostnameIndex(projectList, config.tld);\n\n // Load per-project configs\n projectConfigs.clear();\n for (const [name, projectDir] of projectList) {\n const projectConfig = loadProjectConfig(projectDir);\n if (projectConfig.auth_policy || projectConfig.idle_timeout) {\n projectConfigs.set(name, projectConfig);\n console.log(` ${name}: auth=${projectConfig.auth_policy ?? \"default\"}, idle=${projectConfig.idle_timeout ?? \"default\"}`);\n }\n }\n\n console.log(`Loaded ${projectList.size} projects (${projectConfigs.size} with custom config)`);\n}\n\n/**\n * Start the agent server\n */\nexport function startServer(agentConfig: AgentConfig): void {\n config = agentConfig;\n\n // Initialize database\n initDb(config.dbPath);\n\n // Load projects\n reloadProjects();\n\n // Watch for changes\n watchProjectList(config.projectListPath, () => {\n console.log(\"Project list changed, reloading...\");\n reloadProjects();\n });\n\n // Create HTTP server\n const server = createServer((req, res) => {\n handleRequest(req, res).catch((error) => {\n console.error(\"Unhandled error:\", error);\n if (!res.headersSent) {\n res.writeHead(500);\n res.end(\"Internal Server Error\");\n }\n });\n });\n\n server.listen(config.port, () => {\n console.log(`Trafic agent listening on port ${config.port}`);\n console.log(`TLD: ${config.tld}`);\n console.log(`Projects: ${projectList.size}`);\n });\n}\n"],"names":["config","parseSimpleYaml","__dirname","template"],"mappings":";;;;;;AAKO,SAAS,QAAQ,IAAY,SAA0B;AAE5D,MAAI,OAAO,QAAS,QAAO;AAG3B,MAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,WAAO,UAAU,IAAI,OAAO;AAAA,EAC9B;AAGA,MAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,UAAM,QAAQ,IAAI;AAAA,MAChB,MAAM,QAAQ,QAAQ,OAAO,KAAK,EAAE,QAAQ,OAAO,IAAI,IAAI;AAAA,IAAA;AAE7D,WAAO,MAAM,KAAK,EAAE;AAAA,EACtB;AAEA,SAAO;AACT;AAKA,SAAS,UAAU,IAAY,MAAuB;AACpD,QAAM,CAAC,OAAO,IAAI,IAAI,KAAK,MAAM,GAAG;AACpC,QAAM,OAAO,SAAS,MAAM,EAAE;AAE9B,QAAM,QAAQ,WAAW,EAAE;AAC3B,QAAM,WAAW,WAAW,KAAK;AAEjC,MAAI,UAAU,QAAQ,aAAa,KAAM,QAAO;AAEhD,QAAM,UAAU,GAAG,KAAM,KAAK,QAAS;AACvC,UAAQ,QAAQ,cAAc,WAAW;AAC3C;AAKA,SAAS,WAAW,IAA2B;AAC7C,QAAM,QAAQ,GAAG,MAAM,GAAG;AAC1B,MAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,MAAI,MAAM;AACV,aAAW,QAAQ,OAAO;AACxB,UAAM,IAAI,SAAS,MAAM,EAAE;AAC3B,QAAI,MAAM,CAAC,KAAK,IAAI,KAAK,IAAI,IAAK,QAAO;AACzC,UAAO,OAAO,IAAK;AAAA,EACrB;AACA,SAAO,QAAQ;AACjB;AAKO,SAAS,cAAc,UAAkB,SAA0B;AAExE,MAAI,aAAa,QAAS,QAAO;AAGjC,QAAM,QAAQ,IAAI;AAAA,IAChB,MACE,QACG,QAAQ,qBAAqB,MAAM,EACnC,QAAQ,OAAO,IAAI,EACnB,QAAQ,OAAO,GAAG,IACrB;AAAA,EAAA;AAEJ,SAAO,MAAM,KAAK,QAAQ;AAC5B;AAKO,SAAS,eACd,QAC+C;AAC/C,MAAI,CAAC,OAAO,WAAW,QAAQ,EAAG,QAAO;AAEzC,MAAI;AACF,UAAM,UAAU,OAAO,MAAM,CAAC;AAC9B,UAAM,UAAU,OAAO,KAAK,SAAS,QAAQ,EAAE,SAAS,OAAO;AAC/D,UAAM,aAAa,QAAQ,QAAQ,GAAG;AACtC,QAAI,eAAe,GAAI,QAAO;AAE9B,WAAO;AAAA,MACL,UAAU,QAAQ,MAAM,GAAG,UAAU;AAAA,MACrC,UAAU,QAAQ,MAAM,aAAa,CAAC;AAAA,IAAA;AAAA,EAE1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,iBAAiB,QAA+B;AAC9D,MAAI,CAAC,OAAO,WAAW,SAAS,EAAG,QAAO;AAC1C,SAAO,OAAO,MAAM,CAAC;AACvB;AAKO,SAAS,UAAU,SAAsBA,SAAgC;AAC9E,QAAM,EAAE,UAAU,IAAI,cAAA,IAAkB;AAGxC,QAAM,SAAS,QAAQ,cAAc,MAAM,GAAG,EAAE,CAAC,EAAE,KAAA,KAAU;AAG7D,aAAW,WAAWA,QAAO,YAAY;AACvC,QAAI,QAAQ,QAAQ,OAAO,GAAG;AAC5B,aAAO,EAAE,SAAS,MAAM,QAAQ,KAAA;AAAA,IAClC;AAAA,EACF;AAGA,aAAW,QAAQA,QAAO,OAAO;AAC/B,QAAI,cAAc,UAAU,KAAK,KAAK,GAAG;AACvC,aAAO,UAAU,MAAM,QAAQ,eAAeA,OAAM;AAAA,IACtD;AAAA,EACF;AAGA,SAAO,mBAAmBA,SAAQ,QAAQ,aAAa;AACzD;AAKA,SAAS,UACP,MACA,IACA,eACAA,SACY;AAEZ,MAAI,KAAK,YAAY;AACnB,eAAW,WAAW,KAAK,YAAY;AACrC,UAAI,QAAQ,IAAI,OAAO,GAAG;AACxB,eAAO,EAAE,SAAS,MAAM,QAAQ,OAAA;AAAA,MAClC;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,KAAK,QAAA;AAAA,IACX,KAAK;AACH,aAAO,EAAE,SAAS,MAAM,QAAQ,OAAA;AAAA,IAElC,KAAK;AACH,aAAO,EAAE,SAAS,OAAO,QAAQ,OAAA;AAAA,IAEnC,KAAK,SAAS;AACZ,UAAI,CAAC,cAAe,QAAO,EAAE,SAAS,OAAO,QAAQ,OAAA;AACrD,YAAM,QAAQ,iBAAiB,aAAa;AAC5C,YAAM,SAAS,KAAK,UAAUA,QAAO;AACrC,UAAI,SAAS,OAAO,SAAS,KAAK,GAAG;AACnC,eAAO,EAAE,SAAS,MAAM,QAAQ,QAAA;AAAA,MAClC;AACA,aAAO,EAAE,SAAS,OAAO,QAAQ,OAAA;AAAA,IACnC;AAAA,IAEA,KAAK,SAAS;AACZ,UAAI,CAAC,cAAe,QAAO,EAAE,SAAS,OAAO,QAAQ,OAAA;AACrD,YAAM,QAAQ,eAAe,aAAa;AAC1C,UAAI,OAAO;AACT,cAAM,aAAa,GAAG,MAAM,QAAQ,IAAI,MAAM,QAAQ;AACtD,YAAIA,QAAO,UAAU,SAAS,UAAU,GAAG;AACzC,iBAAO,EAAE,SAAS,MAAM,QAAQ,QAAA;AAAA,QAClC;AAAA,MACF;AACA,aAAO,EAAE,SAAS,OAAO,QAAQ,OAAA;AAAA,IACnC;AAAA,IAEA;AACE,aAAO,EAAE,SAAS,OAAO,QAAQ,OAAA;AAAA,EAAO;AAE9C;AAKA,SAAS,mBACPA,SACA,IACA,eACY;AACZ,UAAQA,QAAO,eAAA;AAAA,IACb,KAAK;AACH,aAAO,EAAE,SAAS,MAAM,QAAQ,UAAA;AAAA,IAElC,KAAK;AACH,aAAO,EAAE,SAAS,OAAO,QAAQ,UAAA;AAAA,IAEnC,KAAK,SAAS;AACZ,UAAI,CAAC,cAAe,QAAO,EAAE,SAAS,OAAO,QAAQ,UAAA;AACrD,YAAM,QAAQ,eAAe,aAAa;AAC1C,UAAI,OAAO;AACT,cAAM,aAAa,GAAG,MAAM,QAAQ,IAAI,MAAM,QAAQ;AACtD,YAAIA,QAAO,UAAU,SAAS,UAAU,GAAG;AACzC,iBAAO,EAAE,SAAS,MAAM,QAAQ,QAAA;AAAA,QAClC;AAAA,MACF;AAEA,YAAM,QAAQ,iBAAiB,aAAa;AAC5C,UAAI,SAASA,QAAO,OAAO,SAAS,KAAK,GAAG;AAC1C,eAAO,EAAE,SAAS,MAAM,QAAQ,QAAA;AAAA,MAClC;AACA,aAAO,EAAE,SAAS,OAAO,QAAQ,UAAA;AAAA,IACnC;AAAA,IAEA,KAAK,SAAS;AACZ,UAAI,CAAC,cAAe,QAAO,EAAE,SAAS,OAAO,QAAQ,UAAA;AACrD,YAAM,QAAQ,iBAAiB,aAAa;AAC5C,UAAI,SAASA,QAAO,OAAO,SAAS,KAAK,GAAG;AAC1C,eAAO,EAAE,SAAS,MAAM,QAAQ,QAAA;AAAA,MAClC;AACA,aAAO,EAAE,SAAS,OAAO,QAAQ,UAAA;AAAA,IACnC;AAAA,IAEA;AACE,aAAO,EAAE,SAAS,OAAO,QAAQ,UAAA;AAAA,EAAU;AAEjD;AChOA,SAASC,kBAAgB,SAAyC;AAChE,QAAM,SAAiC,CAAA;AACvC,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAEhC,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,KAAK,KAAA;AACrB,QAAI,CAAC,WAAW,QAAQ,WAAW,GAAG,EAAG;AAEzC,UAAM,aAAa,QAAQ,QAAQ,GAAG;AACtC,QAAI,eAAe,GAAI;AAEvB,UAAM,MAAM,QAAQ,MAAM,GAAG,UAAU,EAAE,KAAA;AACzC,QAAI,QAAQ,QAAQ,MAAM,aAAa,CAAC,EAAE,KAAA;AAG1C,QACG,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,KAC3C,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,GAC5C;AACA,cAAQ,MAAM,MAAM,GAAG,EAAE;AAAA,IAC3B;AAEA,WAAO,GAAG,IAAI;AAAA,EAChB;AAEA,SAAO;AACT;AAMO,SAAS,gBACd,iBACqB;AACrB,MAAI,CAAC,WAAW,eAAe,GAAG;AAChC,+BAAW,IAAA;AAAA,EACb;AAEA,QAAM,UAAU,aAAa,iBAAiB,OAAO;AACrD,QAAM,SAASA,kBAAgB,OAAO;AAEtC,SAAO,IAAI,IAAI,OAAO,QAAQ,MAAM,CAAC;AACvC;AAKO,SAAS,mBACd,UACA,KACqB;AACrB,QAAM,4BAAY,IAAA;AAElB,aAAW,CAAC,IAAI,KAAK,UAAU;AAE7B,UAAM,IAAI,GAAG,IAAI,IAAI,GAAG,IAAI,IAAI;AAAA,EAClC;AAEA,SAAO;AACT;AAKO,SAAS,uBACd,UACA,OACoB;AACpB,SAAO,MAAM,IAAI,QAAQ;AAC3B;AAKO,SAAS,iBACd,iBACA,UACM;AACN,MAAI,CAAC,WAAW,eAAe,GAAG;AAChC,YAAQ,KAAK,2BAA2B,eAAe,EAAE;AACzD;AAAA,EACF;AAEA,QAAM,iBAAiB,CAAC,cAAc;AACpC,QAAI,cAAc,UAAU;AAC1B,eAAA;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAKO,SAAS,eAAe,MAAuC;AACpE,MAAI;AACF,UAAM,SAAS,SAAS,iBAAiB,IAAI,OAAO;AAAA,MAClD,UAAU;AAAA,MACV,SAAS;AAAA,IAAA,CACV;AACD,UAAM,OAAO,KAAK,MAAM,MAAM;AAE9B,QAAI,CAAC,KAAK,IAAK,QAAO;AAEtB,UAAM,MAAM,KAAK;AACjB,WAAO;AAAA,MACL,MAAM,IAAI;AAAA,MACV,QAAQ,IAAI;AAAA,MACZ,SAAS,IAAI;AAAA,MACb,UAAU,IAAI,YAAY,CAAA;AAAA,MAC1B,WAAW,IAAI,aAAa,CAAA;AAAA,MAC5B,MAAM,IAAI;AAAA,MACV,YAAY,IAAI;AAAA,MAChB,QAAQ,IAAI,QAAQ;AAAA,IAAA;AAAA,EAExB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAKO,SAAS,aAAa,MAAuB;AAClD,MAAI;AACF,aAAS,cAAc,IAAI,IAAI;AAAA,MAC7B,UAAU;AAAA,MACV,SAAS;AAAA;AAAA,MACT,OAAO;AAAA,IAAA,CACR;AACD,WAAO;AAAA,EACT,SAAS,OAAO;AACd,YAAQ,MAAM,2BAA2B,IAAI,KAAK,KAAK;AACvD,WAAO;AAAA,EACT;AACF;AAKO,SAAS,YAAY,MAAuB;AACjD,MAAI;AACF,aAAS,aAAa,IAAI,IAAI;AAAA,MAC5B,UAAU;AAAA,MACV,SAAS;AAAA;AAAA,MACT,OAAO;AAAA,IAAA,CACR;AACD,WAAO;AAAA,EACT,SAAS,OAAO;AACd,YAAQ,MAAM,0BAA0B,IAAI,KAAK,KAAK;AACtD,WAAO;AAAA,EACT;AACF;AAKO,SAAS,eAA8B;AAC5C,MAAI;AACF,UAAM,SAAS,SAAS,gBAAgB;AAAA,MACtC,UAAU;AAAA,MACV,SAAS;AAAA,IAAA,CACV;AACD,UAAM,OAAO,KAAK,MAAM,MAAM;AAE9B,QAAI,CAAC,MAAM,QAAQ,IAAI,UAAU,CAAA;AAEjC,WAAO,KAAK,IAAI,CAAC,OAAgC;AAAA,MAC/C,MAAM,EAAE;AAAA,MACR,QAAQ,EAAE;AAAA,MACV,SAAS,EAAE;AAAA,MACX,UAAW,EAAE,YAAyB,CAAA;AAAA,MACtC,WAAY,EAAE,aAA0B,CAAA;AAAA,MACxC,MAAM,EAAE;AAAA,IAAA,EACR;AAAA,EACJ,QAAQ;AACN,WAAO,CAAA;AAAA,EACT;AACF;ACrLA,IAAI,KAA+B;AAK5B,SAAS,OAAO,QAAmC;AACxD,OAAK,IAAI,SAAS,MAAM;AACxB,KAAG,OAAO,oBAAoB;AAG9B,KAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAkBP;AAED,SAAO;AACT;AAKO,SAAS,QAA2B;AACzC,MAAI,CAAC,IAAI;AACP,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AACA,SAAO;AACT;AAKO,SAAS,UAAgB;AAC9B,MAAI,IAAI;AACN,OAAG,MAAA;AACH,SAAK;AAAA,EACP;AACF;AAKO,SAAS,oBAAoB,MAAoB;AACtD,QAAM,WAAW,MAAA;AACjB,QAAM,OAAO,SAAS,QAAQ;AAAA;AAAA;AAAA;AAAA,GAI7B;AACD,OAAK,IAAI,MAAM,KAAK,IAAA,CAAK;AAC3B;AAKO,SAAS,WAAW,MAAyC;AAClE,QAAM,WAAW,MAAA;AACjB,QAAM,OAAO,SAAS;AAAA,IACpB;AAAA,EAAA;AAEF,SAAO,KAAK,IAAI,IAAI;AACtB;AAKO,SAAS,iBACd,MACA,QACM;AACN,QAAM,WAAW,MAAA;AACjB,QAAM,OAAO,SAAS,QAAQ;AAAA;AAAA;AAAA;AAAA,GAI7B;AACD,OAAK,IAAI,MAAM,KAAK,IAAA,GAAO,MAAM;AACnC;AAKO,SAAS,gBAAgB,aAAsC;AACpE,QAAM,WAAW,MAAA;AACjB,QAAM,SAAS,KAAK,IAAA,IAAQ;AAC5B,QAAM,OAAO,SAAS,QAAQ;AAAA;AAAA;AAAA;AAAA,GAI7B;AACD,SAAO,KAAK,IAAI,MAAM;AACxB;AAKO,SAAS,UAAU,KAAkC;AAC1D,QAAM,WAAW,MAAA;AACjB,QAAM,OAAO,SAAS,QAAQ;AAAA;AAAA;AAAA,GAG7B;AACD,OAAK,IAAI,IAAI,SAAS,IAAI,WAAW,IAAI,IAAI,IAAI,WAAW,IAAI,IAAI;AACtE;AAKO,SAAS,cAAc,SAAiB,QAAQ,KAAkB;AACvE,QAAM,WAAW,MAAA;AACjB,QAAM,OAAO,SAAS,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,GAM7B;AACD,SAAO,KAAK,IAAI,SAAS,KAAK;AAChC;AAKO,SAAS,aAAa,MAAsB;AACjD,QAAM,WAAW,MAAA;AACjB,QAAM,SAAS,KAAK,IAAA,IAAQ,OAAO,KAAK,KAAK,KAAK;AAClD,QAAM,OAAO,SAAS,QAAQ,6CAA6C;AAC3E,QAAM,SAAS,KAAK,IAAI,MAAM;AAC9B,SAAO,OAAO;AAChB;AChIA,SAAS,gBAAgB,SAAyC;AAChE,QAAM,SAAiC,CAAA;AACvC,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAEhC,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,KAAK,KAAA;AAGrB,QAAI,CAAC,WAAW,QAAQ,WAAW,GAAG,EAAG;AAEzC,UAAM,aAAa,QAAQ,QAAQ,GAAG;AACtC,QAAI,eAAe,GAAI;AAEvB,UAAM,MAAM,QAAQ,MAAM,GAAG,UAAU,EAAE,KAAA;AACzC,QAAI,QAAQ,QAAQ,MAAM,aAAa,CAAC,EAAE,KAAA;AAG1C,QACG,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,KAC3C,MAAM,WAAW,GAAG,KAAK,MAAM,SAAS,GAAG,GAC5C;AACA,cAAQ,MAAM,MAAM,GAAG,EAAE;AAAA,IAC3B;AAEA,WAAO,GAAG,IAAI;AAAA,EAChB;AAEA,SAAO;AACT;AAKO,SAAS,kBAAkB,YAAmC;AACnE,QAAM,aAAa,KAAK,YAAY,SAAS,oBAAoB;AAEjE,MAAI,CAAC,WAAW,UAAU,GAAG;AAC3B,WAAO,CAAA;AAAA,EACT;AAEA,MAAI;AACF,UAAM,UAAU,aAAa,YAAY,OAAO;AAChD,UAAM,SAAS,gBAAgB,OAAO;AAEtC,UAAMD,UAAwB,CAAA;AAG9B,QAAI,OAAO,aAAa;AACtB,YAAM,SAAS,OAAO,YAAY,YAAA;AAClC,UAAI,CAAC,SAAS,QAAQ,SAAS,OAAO,EAAE,SAAS,MAAM,GAAG;AACxD,QAAAA,QAAO,cAAc;AAAA,MACvB;AAAA,IACF;AAGA,QAAI,OAAO,cAAc;AACvB,MAAAA,QAAO,eAAe,OAAO;AAAA,IAC/B;AAEA,WAAOA;AAAA,EACT,SAAS,OAAO;AACd,YAAQ,KAAK,sCAAsC,UAAU,KAAK,KAAK;AACvE,WAAO,CAAA;AAAA,EACT;AACF;AAKO,SAAS,gBAAgBA,SAAgC;AAC9D,SAAOA,QAAO,cAAc,YAAA,MAAkB;AAChD;AAKO,SAAS,iBAAiBA,SAA2C;AAC1E,MAAI,CAACA,QAAO,aAAc,QAAO;AACjC,MAAI,gBAAgBA,OAAM,EAAG,QAAO;AAGpC,QAAM,QAAQ;AACd,QAAM,QAAQA,QAAO,aAAa,MAAM,KAAK;AAC7C,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,QAAQ,SAAS,MAAM,CAAC,KAAK,KAAK,EAAE;AAC1C,QAAM,UAAU,SAAS,MAAM,CAAC,KAAK,KAAK,EAAE;AAC5C,QAAM,UAAU,SAAS,MAAM,CAAC,KAAK,KAAK,EAAE;AAE5C,QAAM,MAAM,QAAQ,OAAO,UAAU,KAAK,WAAW;AACrD,SAAO,KAAK,IAAI,KAAK;AACvB;ACvFA,MAAME,cAAY,QAAQ,cAAc,YAAY,GAAG,CAAC;AAGxD,IAAI;AACJ,IAAI;AACJ,IAAI;AAEJ,MAAM,qCAAqB,IAAA;AAK3B,SAAS,aAAa,MAAsB;AAC1C,QAAM,eAAe,QAAQA,aAAW,MAAM,aAAa,GAAG,IAAI,OAAO;AACzE,MAAI;AACF,WAAO,aAAa,cAAc,OAAO;AAAA,EAC3C,QAAQ;AACN,WAAO,mBAAmB,IAAI;AAAA,EAChC;AACF;AAKA,SAAS,uBAAuB,aAA6C;AAC3E,MAAI,CAAC,YAAa,QAAO,OAAO;AAEhC,QAAM,gBAAgB,eAAe,IAAI,WAAW;AACpD,MAAI,CAAC,eAAe,YAAa,QAAO,OAAO;AAG/C,SAAO;AAAA,IACL,GAAG,OAAO;AAAA,IACV,eAAe,cAAc;AAAA,EAAA;AAEjC;AAMA,SAAS,WAAW,KAAsB,KAA2B;AACnE,QAAM,WAAW,IAAI,QAAQ,kBAAkB,KAAe;AAC9D,QAAM,KAAK,IAAI,QAAQ,iBAAiB,KAAe,IAAI,OAAO,iBAAiB;AACnF,QAAM,gBAAgB,IAAI,QAAQ,eAAe;AACjD,QAAM,OAAO,IAAI,QAAQ,iBAAiB,KAAe;AAGzD,QAAM,cAAc,cAAc,IAAI,QAAQ;AAG9C,QAAM,aAAa,uBAAuB,WAAW;AAErD,QAAM,SAAS;AAAA,IACb;AAAA,MACE;AAAA,MACA,IAAI,GAAG,MAAM,GAAG,EAAE,CAAC,EAAE,KAAA;AAAA,MACrB;AAAA,MACA,cAAc;AAAA,IAAA;AAAA,IAEhB;AAAA,EAAA;AAGF,MAAI,OAAO,SAAS;AAElB,QAAI,aAAa;AACf,0BAAoB,WAAW;AAC/B,gBAAU;AAAA,QACR,SAAS;AAAA,QACT,WAAW,KAAK,IAAA;AAAA,QAChB,IAAI,GAAG,MAAM,GAAG,EAAE,CAAC,EAAE,KAAA;AAAA,QACrB,WAAW,IAAI,QAAQ,YAAY,KAAK;AAAA,QACxC;AAAA,MAAA,CACD;AAAA,IACH;AAEA,QAAI,UAAU,GAAG;AACjB,QAAI,IAAA;AAAA,EACN,OAAO;AAEL,QAAI,UAAU,KAAK;AAAA,MACjB,oBAAoB;AAAA,IAAA,CACrB;AACD,QAAI,IAAI,cAAc;AAAA,EACxB;AACF;AAMA,eAAe,aACb,KACA,KACe;AACf,QAAM,WAAW,IAAI,QAAQ,kBAAkB,KAAe,IAAI,QAAQ,QAAQ;AAClF,QAAM,cAAc,cAAc,IAAI,QAAQ;AAE9C,MAAI,CAAC,aAAa;AAEhB,UAAMC,YAAW,aAAa,OAAO;AACrC,QAAI,UAAU,KAAK,EAAE,gBAAgB,aAAa;AAClD,QAAI,IAAIA,UAAS,QAAQ,eAAe,mBAAmB,CAAC;AAC5D;AAAA,EACF;AAGA,QAAM,SAAS,WAAW,WAAW;AACrC,MAAI,QAAQ,WAAW,YAAY;AAEjC,UAAMA,YAAW,aAAa,MAAM;AACpC,QAAI,UAAU,KAAK;AAAA,MACjB,gBAAgB;AAAA,MAChB,eAAe;AAAA,IAAA,CAChB;AACD,QAAI;AAAA,MACFA,UACG,QAAQ,oBAAoB,WAAW,EACvC,QAAQ,qBAAqB,QAAQ;AAAA,IAAA;AAE1C;AAAA,EACF;AAGA,mBAAiB,aAAa,UAAU;AAGxC,QAAM,WAAW,aAAa,MAAM;AACpC,MAAI,UAAU,KAAK;AAAA,IACjB,gBAAgB;AAAA,IAChB,eAAe;AAAA,EAAA,CAChB;AACD,MAAI;AAAA,IACF,SACG,QAAQ,oBAAoB,WAAW,EACvC,QAAQ,qBAAqB,QAAQ;AAAA,EAAA;AAI1C,aAAW,MAAM;AACf,UAAM,UAAU,aAAa,WAAW;AACxC,qBAAiB,aAAa,UAAU,YAAY,SAAS;AAAA,EAC/D,GAAG,GAAG;AACR;AAKA,SAAS,aAAa,KAAsB,KAA2B;AACrE,QAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ,IAAI,EAAE;AAChE,QAAM,cAAc,IAAI,aAAa,IAAI,SAAS;AAElD,MAAI,CAAC,aAAa;AAChB,QAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB;AACzD,QAAI,IAAI,KAAK,UAAU,EAAE,OAAO,4BAAA,CAA6B,CAAC;AAC9D;AAAA,EACF;AAEA,QAAM,OAAO,eAAe,WAAW;AACvC,QAAM,SAAS,WAAW,WAAW;AAErC,MAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB;AACzD,MAAI;AAAA,IACF,KAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN,QAAQ,MAAM,UAAU,QAAQ,UAAU;AAAA,MAC1C,OAAO,MAAM,WAAW;AAAA,IAAA,CACzB;AAAA,EAAA;AAEL;AAKA,eAAe,cACb,KACA,KACe;AACf,QAAM,MAAM,IAAI,OAAO;AAEvB,MAAI;AAEF,QAAI,QAAQ,eAAe,IAAI,WAAW,YAAY,GAAG;AACvD,iBAAW,KAAK,GAAG;AAAA,IACrB,WAAW,QAAQ,iBAAiB,IAAI,WAAW,cAAc,GAAG;AAClE,mBAAa,KAAK,GAAG;AAAA,IACvB,WAAW,QAAQ,eAAe;AAChC,UAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB;AACzD,UAAI,IAAI,KAAK,UAAU,EAAE,QAAQ,MAAM,SAAS,cAAA,CAAe,CAAC;AAAA,IAClE,OAAO;AAEL,YAAM,aAAa,KAAK,GAAG;AAAA,IAC7B;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,MAAM,kBAAkB,KAAK;AACrC,QAAI,UAAU,KAAK,EAAE,gBAAgB,cAAc;AACnD,QAAI,IAAI,uBAAuB;AAAA,EACjC;AACF;AAKA,SAAS,iBAAuB;AAC9B,gBAAc,gBAAgB,OAAO,eAAe;AACpD,kBAAgB,mBAAmB,aAAa,OAAO,GAAG;AAG1D,iBAAe,MAAA;AACf,aAAW,CAAC,MAAM,UAAU,KAAK,aAAa;AAC5C,UAAM,gBAAgB,kBAAkB,UAAU;AAClD,QAAI,cAAc,eAAe,cAAc,cAAc;AAC3D,qBAAe,IAAI,MAAM,aAAa;AACtC,cAAQ,IAAI,KAAK,IAAI,UAAU,cAAc,eAAe,SAAS,UAAU,cAAc,gBAAgB,SAAS,EAAE;AAAA,IAC1H;AAAA,EACF;AAEA,UAAQ,IAAI,UAAU,YAAY,IAAI,cAAc,eAAe,IAAI,sBAAsB;AAC/F;AAKO,SAAS,YAAY,aAAgC;AAC1D,WAAS;AAGT,SAAO,OAAO,MAAM;AAGpB,iBAAA;AAGA,mBAAiB,OAAO,iBAAiB,MAAM;AAC7C,YAAQ,IAAI,oCAAoC;AAChD,mBAAA;AAAA,EACF,CAAC;AAGD,QAAM,SAAS,aAAa,CAAC,KAAK,QAAQ;AACxC,kBAAc,KAAK,GAAG,EAAE,MAAM,CAAC,UAAU;AACvC,cAAQ,MAAM,oBAAoB,KAAK;AACvC,UAAI,CAAC,IAAI,aAAa;AACpB,YAAI,UAAU,GAAG;AACjB,YAAI,IAAI,uBAAuB;AAAA,MACjC;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AAED,SAAO,OAAO,OAAO,MAAM,MAAM;AAC/B,YAAQ,IAAI,kCAAkC,OAAO,IAAI,EAAE;AAC3D,YAAQ,IAAI,QAAQ,OAAO,GAAG,EAAE;AAChC,YAAQ,IAAI,aAAa,YAAY,IAAI,EAAE;AAAA,EAC7C,CAAC;AACH;"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,YAAY,CAAC;AA8O1D;;GAEG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CA+B1D"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install Node.js using fnm
|
|
3
|
+
*/
|
|
4
|
+
export declare function installNode(): void;
|
|
5
|
+
/**
|
|
6
|
+
* Install the Trafic agent
|
|
7
|
+
*/
|
|
8
|
+
export declare function installAgent(): void;
|
|
9
|
+
/**
|
|
10
|
+
* Create the agent configuration
|
|
11
|
+
*/
|
|
12
|
+
export declare function createAgentConfig(tld: string, _email?: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* Create systemd service for the agent
|
|
15
|
+
*/
|
|
16
|
+
export declare function createSystemdService(): void;
|
|
17
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/setup/agent.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAqClC;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAOnC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAoCpE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CA2C3C"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AuditCheck } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Run all audit checks
|
|
4
|
+
*/
|
|
5
|
+
export declare function runAudit(): AuditCheck[];
|
|
6
|
+
/**
|
|
7
|
+
* Print audit results
|
|
8
|
+
*/
|
|
9
|
+
export declare function printAuditResults(checks: AuditCheck[]): void;
|
|
10
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/setup/audit.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AA6N7C;;GAEG;AACH,wBAAgB,QAAQ,IAAI,UAAU,EAAE,CAQvC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAyB5D"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install DDEV
|
|
3
|
+
*/
|
|
4
|
+
export declare function installDdev(): void;
|
|
5
|
+
/**
|
|
6
|
+
* Configure DDEV global settings for production
|
|
7
|
+
*
|
|
8
|
+
* Note on DNS and /etc/hosts:
|
|
9
|
+
* When using a custom TLD (not ddev.site), DDEV may try to edit /etc/hosts
|
|
10
|
+
* if it can't resolve project hostnames via DNS. To avoid this:
|
|
11
|
+
* 1. Configure DNS so *.{tld} resolves to the server's IP (recommended)
|
|
12
|
+
* 2. Or install dnsmasq for local wildcard DNS resolution
|
|
13
|
+
*
|
|
14
|
+
* With proper DNS, DDEV won't need sudo for hostname management.
|
|
15
|
+
*/
|
|
16
|
+
export declare function configureDdev(tld: string, email?: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* Install dnsmasq for local DNS resolution (optional)
|
|
19
|
+
* This allows the server itself to resolve *.{tld} hostnames
|
|
20
|
+
* without needing to edit /etc/hosts for each project
|
|
21
|
+
*/
|
|
22
|
+
export declare function installDnsmasq(tld: string): void;
|
|
23
|
+
/**
|
|
24
|
+
* Configure Traefik for forward auth
|
|
25
|
+
*/
|
|
26
|
+
export declare function configureTraefik(): void;
|
|
27
|
+
//# sourceMappingURL=ddev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ddev.d.ts","sourceRoot":"","sources":["../../src/setup/ddev.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,WAAW,IAAI,IAAI,CAgBlC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CA8B/D;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAuChD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAsDvC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install Docker using the official script
|
|
3
|
+
*/
|
|
4
|
+
export declare function installDocker(): void;
|
|
5
|
+
/**
|
|
6
|
+
* Configure Docker for production use
|
|
7
|
+
*/
|
|
8
|
+
export declare function configureDocker(): void;
|
|
9
|
+
/**
|
|
10
|
+
* Setup Docker system prune cron job
|
|
11
|
+
*/
|
|
12
|
+
export declare function setupDockerPrune(): void;
|
|
13
|
+
//# sourceMappingURL=docker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../src/setup/docker.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAwBpC;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAsCtC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAYvC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configure SSH hardening
|
|
3
|
+
*/
|
|
4
|
+
export declare function hardenSsh(allowedUsers?: string[]): void;
|
|
5
|
+
/**
|
|
6
|
+
* Configure UFW firewall
|
|
7
|
+
*/
|
|
8
|
+
export declare function configureFirewall(): void;
|
|
9
|
+
/**
|
|
10
|
+
* Configure automatic security updates
|
|
11
|
+
*/
|
|
12
|
+
export declare function configureUnattendedUpgrades(): void;
|
|
13
|
+
/**
|
|
14
|
+
* Configure Fail2ban
|
|
15
|
+
*/
|
|
16
|
+
export declare function configureFail2ban(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Configure system limits
|
|
19
|
+
*/
|
|
20
|
+
export declare function configureSystemLimits(): void;
|
|
21
|
+
/**
|
|
22
|
+
* Set proper file permissions
|
|
23
|
+
*/
|
|
24
|
+
export declare function configureFilePermissions(): void;
|
|
25
|
+
/**
|
|
26
|
+
* Run all hardening steps
|
|
27
|
+
*/
|
|
28
|
+
export declare function hardenServer(sshUsers?: string[]): void;
|
|
29
|
+
//# sourceMappingURL=hardening.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hardening.d.ts","sourceRoot":"","sources":["../../src/setup/hardening.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,SAAS,CAAC,YAAY,GAAE,MAAM,EAAa,GAAG,IAAI,CAiDjE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAoBxC;AAED;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CA+ClD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAiCxC;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAc5C;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAuB/C;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,GAAE,MAAM,EAAa,GAAG,IAAI,CAOhE"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SetupOptions } from "./types.js";
|
|
2
|
+
export * from "./types.js";
|
|
3
|
+
export * from "./audit.js";
|
|
4
|
+
/**
|
|
5
|
+
* Run the full server setup
|
|
6
|
+
*/
|
|
7
|
+
export declare function setup(options: SetupOptions): Promise<void>;
|
|
8
|
+
/**
|
|
9
|
+
* Run security audit
|
|
10
|
+
*/
|
|
11
|
+
export declare function audit(): Promise<void>;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/setup/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAS/C,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAE3B;;GAEG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA8EhE;AAED;;GAEG;AACH,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAQ3C"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Set dry-run mode
|
|
3
|
+
*/
|
|
4
|
+
export declare function setDryRun(value: boolean): void;
|
|
5
|
+
/**
|
|
6
|
+
* Reset step counter
|
|
7
|
+
*/
|
|
8
|
+
export declare function resetSteps(): void;
|
|
9
|
+
/**
|
|
10
|
+
* Log a step
|
|
11
|
+
*/
|
|
12
|
+
export declare function step(message: string): void;
|
|
13
|
+
/**
|
|
14
|
+
* Log success
|
|
15
|
+
*/
|
|
16
|
+
export declare function success(message: string): void;
|
|
17
|
+
/**
|
|
18
|
+
* Log warning
|
|
19
|
+
*/
|
|
20
|
+
export declare function warn(message: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Log error
|
|
23
|
+
*/
|
|
24
|
+
export declare function error(message: string): void;
|
|
25
|
+
/**
|
|
26
|
+
* Log info
|
|
27
|
+
*/
|
|
28
|
+
export declare function info(message: string): void;
|
|
29
|
+
/**
|
|
30
|
+
* Execute a command (or show in dry-run mode)
|
|
31
|
+
*/
|
|
32
|
+
export declare function exec(command: string, options?: {
|
|
33
|
+
silent?: boolean;
|
|
34
|
+
}): string;
|
|
35
|
+
/**
|
|
36
|
+
* Execute a command and return output (silent)
|
|
37
|
+
*/
|
|
38
|
+
export declare function execSilent(command: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Check if a command exists
|
|
41
|
+
*/
|
|
42
|
+
export declare function commandExists(command: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Check if running as root
|
|
45
|
+
*/
|
|
46
|
+
export declare function isRoot(): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Write a file (or show in dry-run mode)
|
|
49
|
+
*/
|
|
50
|
+
export declare function writeFile(path: string, content: string): void;
|
|
51
|
+
//# sourceMappingURL=steps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"steps.d.ts","sourceRoot":"","sources":["../../src/setup/steps.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE9C;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,IAAI,CAEjC;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAG1C;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED;;GAEG;AACH,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAiB5E;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAalD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAOtD;AAED;;GAEG;AACH,wBAAgB,MAAM,IAAI,OAAO,CAEhC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAe7D"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup command options
|
|
3
|
+
*/
|
|
4
|
+
export interface SetupOptions {
|
|
5
|
+
/** TLD for DDEV projects (required) */
|
|
6
|
+
tld: string;
|
|
7
|
+
/** Email for Let's Encrypt certificates */
|
|
8
|
+
email?: string;
|
|
9
|
+
/** Skip hardening steps */
|
|
10
|
+
noHardening?: boolean;
|
|
11
|
+
/** Skip Docker installation (already installed) */
|
|
12
|
+
noDocker?: boolean;
|
|
13
|
+
/** Skip DDEV installation (already installed) */
|
|
14
|
+
noDdev?: boolean;
|
|
15
|
+
/** Additional SSH users to allow */
|
|
16
|
+
sshUsers?: string[];
|
|
17
|
+
/** Run in dry-run mode (show what would be done) */
|
|
18
|
+
dryRun?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Step result
|
|
22
|
+
*/
|
|
23
|
+
export interface StepResult {
|
|
24
|
+
success: boolean;
|
|
25
|
+
message?: string;
|
|
26
|
+
skipped?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Audit check result
|
|
30
|
+
*/
|
|
31
|
+
export interface AuditCheck {
|
|
32
|
+
name: string;
|
|
33
|
+
status: "pass" | "warn" | "fail";
|
|
34
|
+
message: string;
|
|
35
|
+
fix?: string;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/setup/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mDAAmD;IACnD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iDAAiD;IACjD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,oDAAoD;IACpD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create the ddev user with proper permissions
|
|
3
|
+
*/
|
|
4
|
+
export declare function createDdevUser(): void;
|
|
5
|
+
/**
|
|
6
|
+
* Setup authorized_keys for ddev user
|
|
7
|
+
*/
|
|
8
|
+
export declare function setupAuthorizedKeys(publicKey?: string): void;
|
|
9
|
+
//# sourceMappingURL=user.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/setup/user.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAqCrC;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAqB5D"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AgentConfig } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Stop idle projects that haven't been accessed recently
|
|
4
|
+
* Respects per-project idle_timeout settings from .ddev/config.trafic.yaml
|
|
5
|
+
*/
|
|
6
|
+
export declare function stopIdleProjects(config: AgentConfig): void;
|
|
7
|
+
/**
|
|
8
|
+
* Start the idle check scheduler
|
|
9
|
+
*/
|
|
10
|
+
export declare function startIdleScheduler(config: AgentConfig): NodeJS.Timeout;
|
|
11
|
+
//# sourceMappingURL=stop-idle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop-idle.d.ts","sourceRoot":"","sources":["../../src/tasks/stop-idle.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAkD1D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,OAAO,CActE"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent configuration loaded from TOML file
|
|
3
|
+
*/
|
|
4
|
+
export interface AgentConfig {
|
|
5
|
+
/** TLD for DDEV projects (required) */
|
|
6
|
+
tld: string;
|
|
7
|
+
/** Port for the agent HTTP server */
|
|
8
|
+
port: number;
|
|
9
|
+
/** Path to the SQLite database */
|
|
10
|
+
dbPath: string;
|
|
11
|
+
/** Path to DDEV's project_list.yaml */
|
|
12
|
+
projectListPath: string;
|
|
13
|
+
/** Directory where projects are stored */
|
|
14
|
+
projectsDir: string;
|
|
15
|
+
/** Idle timeout before stopping projects (e.g., "30m", "1h") */
|
|
16
|
+
idleTimeout: string;
|
|
17
|
+
/** Interval for checking idle projects (e.g., "5m") */
|
|
18
|
+
idleCheckInterval: string;
|
|
19
|
+
/** Auth configuration */
|
|
20
|
+
auth: AuthConfig;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Authentication configuration
|
|
24
|
+
*/
|
|
25
|
+
export interface AuthConfig {
|
|
26
|
+
/** Default policy: "allow" | "deny" | "basic" | "token" */
|
|
27
|
+
defaultPolicy: "allow" | "deny" | "basic" | "token";
|
|
28
|
+
/** IP addresses or CIDR ranges to always allow */
|
|
29
|
+
allowedIps: string[];
|
|
30
|
+
/** Bearer tokens to allow */
|
|
31
|
+
tokens: string[];
|
|
32
|
+
/** Basic auth credentials (username:password) */
|
|
33
|
+
basicAuth: string[];
|
|
34
|
+
/** Per-hostname rules */
|
|
35
|
+
rules: AuthRule[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Per-hostname authentication rule
|
|
39
|
+
*/
|
|
40
|
+
export interface AuthRule {
|
|
41
|
+
/** Hostname pattern (glob supported) */
|
|
42
|
+
match: string;
|
|
43
|
+
/** Policy for this hostname */
|
|
44
|
+
policy: "allow" | "deny" | "basic" | "token";
|
|
45
|
+
/** Optional: specific tokens for this hostname */
|
|
46
|
+
tokens?: string[];
|
|
47
|
+
/** Optional: specific IPs for this hostname */
|
|
48
|
+
allowedIps?: string[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* DDEV project from project_list.yaml
|
|
52
|
+
*/
|
|
53
|
+
export interface DdevProject {
|
|
54
|
+
name: string;
|
|
55
|
+
status: "running" | "stopped" | "paused" | "unhealthy";
|
|
56
|
+
appRoot: string;
|
|
57
|
+
httpURLs: string[];
|
|
58
|
+
httpsURLs: string[];
|
|
59
|
+
type: string;
|
|
60
|
+
phpVersion?: string;
|
|
61
|
+
dbType?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Project record in the database
|
|
65
|
+
*/
|
|
66
|
+
export interface ProjectRecord {
|
|
67
|
+
name: string;
|
|
68
|
+
lastAccess: number;
|
|
69
|
+
status: "running" | "stopped" | "starting";
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Access log entry
|
|
73
|
+
*/
|
|
74
|
+
export interface AccessLog {
|
|
75
|
+
id?: number;
|
|
76
|
+
project: string;
|
|
77
|
+
timestamp: number;
|
|
78
|
+
ip: string;
|
|
79
|
+
userAgent: string;
|
|
80
|
+
path: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Auth check result
|
|
84
|
+
*/
|
|
85
|
+
export interface AuthResult {
|
|
86
|
+
allowed: boolean;
|
|
87
|
+
reason: "ip" | "token" | "basic" | "rule" | "default";
|
|
88
|
+
project?: string;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* HTTP request context for auth
|
|
92
|
+
*/
|
|
93
|
+
export interface AuthRequest {
|
|
94
|
+
hostname: string;
|
|
95
|
+
ip: string;
|
|
96
|
+
authorization?: string;
|
|
97
|
+
forwardedFor?: string;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,eAAe,EAAE,MAAM,CAAC;IACxB,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,yBAAyB;IACzB,IAAI,EAAE,UAAU,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,2DAA2D;IAC3D,aAAa,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IACpD,kDAAkD;IAClD,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,6BAA6B;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,iDAAiD;IACjD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,yBAAyB;IACzB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IAC7C,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IACtD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { AuthConfig, AuthRequest, AuthResult } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Check if an IP matches a pattern (supports CIDR notation)
|
|
4
|
+
*/
|
|
5
|
+
export declare function matchIp(ip: string, pattern: string): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Match hostname against a glob pattern
|
|
8
|
+
*/
|
|
9
|
+
export declare function matchHostname(hostname: string, pattern: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Parse Basic auth header
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseBasicAuth(header: string): {
|
|
14
|
+
username: string;
|
|
15
|
+
password: string;
|
|
16
|
+
} | null;
|
|
17
|
+
/**
|
|
18
|
+
* Parse Bearer token from header
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseBearerToken(header: string): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Check authentication for a request
|
|
23
|
+
*/
|
|
24
|
+
export declare function checkAuth(request: AuthRequest, config: AuthConfig): AuthResult;
|
|
25
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/utils/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAY,MAAM,aAAa,CAAC;AAEjF;;GAEG;AACH,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAkB5D;AAkCD;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAcxE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,GACb;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAgB/C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG9D;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,GAAG,UAAU,CAsB9E"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AgentConfig } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Parse a duration string (e.g., "30m", "1h", "2h30m") to milliseconds
|
|
4
|
+
*/
|
|
5
|
+
export declare function parseDuration(duration: string): number;
|
|
6
|
+
/**
|
|
7
|
+
* Load configuration from TOML file
|
|
8
|
+
*/
|
|
9
|
+
export declare function loadConfig(configPath?: string): AgentConfig;
|
|
10
|
+
/**
|
|
11
|
+
* Validate configuration
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateConfig(config: AgentConfig): string[];
|
|
14
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAwB,MAAM,aAAa,CAAC;AAyBrE;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUtD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,CAgC3D;AA0BD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,EAAE,CAqB5D"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import type { ProjectRecord, AccessLog } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Initialize the database connection and schema
|
|
5
|
+
*/
|
|
6
|
+
export declare function initDb(dbPath: string): Database.Database;
|
|
7
|
+
/**
|
|
8
|
+
* Get the database instance
|
|
9
|
+
*/
|
|
10
|
+
export declare function getDb(): Database.Database;
|
|
11
|
+
/**
|
|
12
|
+
* Close the database connection
|
|
13
|
+
*/
|
|
14
|
+
export declare function closeDb(): void;
|
|
15
|
+
/**
|
|
16
|
+
* Update project last access time
|
|
17
|
+
*/
|
|
18
|
+
export declare function updateProjectAccess(name: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* Get project record
|
|
21
|
+
*/
|
|
22
|
+
export declare function getProject(name: string): ProjectRecord | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Set project status
|
|
25
|
+
*/
|
|
26
|
+
export declare function setProjectStatus(name: string, status: ProjectRecord["status"]): void;
|
|
27
|
+
/**
|
|
28
|
+
* Get idle projects (last access older than threshold)
|
|
29
|
+
*/
|
|
30
|
+
export declare function getIdleProjects(thresholdMs: number): ProjectRecord[];
|
|
31
|
+
/**
|
|
32
|
+
* Log an access
|
|
33
|
+
*/
|
|
34
|
+
export declare function logAccess(log: Omit<AccessLog, "id">): void;
|
|
35
|
+
/**
|
|
36
|
+
* Get recent access logs for a project
|
|
37
|
+
*/
|
|
38
|
+
export declare function getAccessLogs(project: string, limit?: number): AccessLog[];
|
|
39
|
+
/**
|
|
40
|
+
* Clean old access logs (older than given days)
|
|
41
|
+
*/
|
|
42
|
+
export declare function cleanOldLogs(days: number): number;
|
|
43
|
+
//# sourceMappingURL=db.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/utils/db.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI5D;;GAEG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC,QAAQ,CA0BxD;AAED;;GAEG;AACH,wBAAgB,KAAK,IAAI,QAAQ,CAAC,QAAQ,CAKzC;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAK9B;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAQtD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAMlE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,GAC9B,IAAI,CAQN;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,EAAE,CASpE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,IAAI,CAO1D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAM,GAAG,SAAS,EAAE,CAUvE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMjD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { DdevProject } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Load projects from DDEV's project_list.yaml
|
|
4
|
+
* Returns a map of project name -> app root path
|
|
5
|
+
*/
|
|
6
|
+
export declare function loadProjectList(projectListPath: string): Map<string, string>;
|
|
7
|
+
/**
|
|
8
|
+
* Build a hostname -> project name index
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildHostnameIndex(projects: Map<string, string>, tld: string): Map<string, string>;
|
|
11
|
+
/**
|
|
12
|
+
* Get project name from hostname
|
|
13
|
+
*/
|
|
14
|
+
export declare function getProjectFromHostname(hostname: string, index: Map<string, string>): string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Watch project_list.yaml for changes
|
|
17
|
+
*/
|
|
18
|
+
export declare function watchProjectList(projectListPath: string, onChange: () => void): void;
|
|
19
|
+
/**
|
|
20
|
+
* Get detailed project info using ddev describe
|
|
21
|
+
*/
|
|
22
|
+
export declare function getProjectInfo(name: string): DdevProject | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Start a DDEV project
|
|
25
|
+
*/
|
|
26
|
+
export declare function startProject(name: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Stop a DDEV project
|
|
29
|
+
*/
|
|
30
|
+
export declare function stopProject(name: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* List all DDEV projects with their status
|
|
33
|
+
*/
|
|
34
|
+
export declare function listProjects(): DdevProject[];
|
|
35
|
+
//# sourceMappingURL=ddev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ddev.d.ts","sourceRoot":"","sources":["../../src/utils/ddev.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAgC/C;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,eAAe,EAAE,MAAM,GACtB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CASrB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,GAAG,EAAE,MAAM,GACV,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CASrB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACzB,MAAM,GAAG,SAAS,CAEpB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,IAAI,GACnB,IAAI,CAWN;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAwBpE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAYlD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAYjD;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,WAAW,EAAE,CAqB5C"}
|