Spaces:
Runtime error
Runtime error
| /** | |
| * Pure parser for the proxy bulk-import textarea. | |
| * | |
| * Supported line formats (one proxy per line): | |
| * 1. Pipe-delimited: NAME|HOST|PORT[|USERNAME|PASSWORD|TYPE|REGION|STATUS|NOTES] | |
| * 2. Auth-less short: HOST:PORT → name is auto-generated as "Imported HOST:PORT" | |
| * | |
| * Lines starting with # and blank lines are skipped. | |
| */ | |
| export type ParsedProxyEntry = { | |
| name: string; | |
| host: string; | |
| port: number; | |
| username: string; | |
| password: string; | |
| type: string; | |
| region: string; | |
| status: string; | |
| notes: string; | |
| }; | |
| export type ParseError = { | |
| line: number; | |
| reason: string; | |
| }; | |
| export const VALID_PROXY_TYPES = new Set(["http", "https", "socks5"]); | |
| export const VALID_PROXY_STATUSES = new Set(["active", "inactive"]); | |
| export function parseBulkImportText(text: string): { | |
| entries: ParsedProxyEntry[]; | |
| errors: ParseError[]; | |
| skipped: number; | |
| } { | |
| const lines = text.split("\n"); | |
| const entries: ParsedProxyEntry[] = []; | |
| const errors: ParseError[] = []; | |
| let skipped = 0; | |
| for (let i = 0; i < lines.length; i++) { | |
| const raw = lines[i].trim(); | |
| if (!raw || raw.startsWith("#")) { | |
| skipped++; | |
| continue; | |
| } | |
| const lineNum = i + 1; | |
| // Auth-less shorthand: HOST:PORT (no pipe characters, exactly one colon) | |
| if (!raw.includes("|")) { | |
| const colonIdx = raw.lastIndexOf(":"); | |
| if (colonIdx > 0) { | |
| const host = raw.slice(0, colonIdx).trim(); | |
| const portStr = raw.slice(colonIdx + 1).trim(); | |
| const port = Number(portStr); | |
| if (!host) { | |
| errors.push({ line: lineNum, reason: "bulkImportErrorMissingHost" }); | |
| continue; | |
| } | |
| if (!portStr || isNaN(port) || port < 1 || port > 65535) { | |
| errors.push({ line: lineNum, reason: "bulkImportErrorInvalidPort" }); | |
| continue; | |
| } | |
| entries.push({ | |
| name: `Imported ${host}:${portStr}`, | |
| host, | |
| port, | |
| username: "", | |
| password: "", | |
| type: "http", | |
| region: "", | |
| status: "active", | |
| notes: "", | |
| }); | |
| continue; | |
| } | |
| errors.push({ line: lineNum, reason: "bulkImportErrorMissingHost" }); | |
| continue; | |
| } | |
| // Full pipe-delimited format: NAME|HOST|PORT[|USERNAME|PASSWORD|TYPE|REGION|STATUS|NOTES] | |
| const parts = raw.split("|").map((p) => p.trim()); | |
| const [name, host, portStr, username, password, type, region, status, notes] = parts; | |
| if (!name) { | |
| errors.push({ line: lineNum, reason: "bulkImportErrorMissingName" }); | |
| continue; | |
| } | |
| if (!host) { | |
| errors.push({ line: lineNum, reason: "bulkImportErrorMissingHost" }); | |
| continue; | |
| } | |
| const port = Number(portStr); | |
| if (!portStr || isNaN(port) || port < 1 || port > 65535) { | |
| errors.push({ line: lineNum, reason: "bulkImportErrorInvalidPort" }); | |
| continue; | |
| } | |
| const normalizedType = (type || "socks5").toLowerCase(); | |
| if (!VALID_PROXY_TYPES.has(normalizedType)) { | |
| errors.push({ line: lineNum, reason: "bulkImportErrorInvalidType" }); | |
| continue; | |
| } | |
| const normalizedStatus = (status || "active").toLowerCase(); | |
| if (!VALID_PROXY_STATUSES.has(normalizedStatus)) { | |
| errors.push({ line: lineNum, reason: "bulkImportErrorInvalidStatus" }); | |
| continue; | |
| } | |
| entries.push({ | |
| name, | |
| host, | |
| port, | |
| username: username || "", | |
| password: password || "", | |
| type: normalizedType, | |
| region: region || "", | |
| status: normalizedStatus, | |
| notes: notes || "", | |
| }); | |
| } | |
| return { entries, errors, skipped }; | |
| } | |