Spaces:
Runtime error
Runtime error
File size: 3,583 Bytes
cd8bd0a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 | /**
* 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 };
}
|