Refactor VPN proxy client implementation
Build and Deploy Gateway / build-and-push (push) Failing after 2s
Build and Deploy Gateway / deploy (push) Has been skipped

This commit is contained in:
2026-08-09 00:41:52 +03:00
parent 32be4380a3
commit 34d8b681ad
190 changed files with 20064 additions and 9761 deletions
+79
View File
@@ -0,0 +1,79 @@
export const INITIAL_ROUTE_RULES = Object.freeze([
Object.freeze({ type: 'domain_suffix', value: 'ru', enabled: true }),
]);
const RULE_TYPES = new Set(['domain', 'domain_suffix', 'domain_keyword']);
export const MAX_ROUTE_RULES = 200;
export type RouteRuleType = 'domain' | 'domain_suffix' | 'domain_keyword';
export interface NormalizedRouteRule {
type: RouteRuleType;
value: string;
enabled: boolean;
}
function record(value: unknown): Record<string, unknown> {
return value && typeof value === 'object' && !Array.isArray(value)
? value as Record<string, unknown>
: {};
}
function hostname(value: unknown) {
const input = String(value || '').trim().replace(/^\*\./, '').replace(/^\./, '');
if (!input) throw new TypeError('Domain rule value is required');
const url = new URL(/^[a-z][a-z\d+.-]*:\/\//i.test(input) ? input : `https://${input}`);
const normalized = url.hostname.replace(/\.$/, '').toLowerCase();
if (!normalized || normalized.length > 253) throw new TypeError('Invalid domain rule value');
return normalized;
}
function normalizeRule(input: unknown): NormalizedRouteRule {
const rule = record(input);
const type = String(rule.type || '').trim();
if (!RULE_TYPES.has(type)) throw new TypeError('Invalid domain rule type');
if (Object.hasOwn(rule, 'enabled') && typeof rule.enabled !== 'boolean') {
throw new TypeError('Invalid domain rule enabled state');
}
const value = type === 'domain_keyword'
? String(rule.value || '').trim().toLowerCase()
: hostname(rule.value);
if (!value || value.length > 253 || /[\s/:?#]/.test(value)) {
throw new TypeError('Invalid domain rule value');
}
return { type: type as RouteRuleType, value, enabled: rule.enabled !== false };
}
export function normalizeRouteRules(
value: unknown,
{ strict = false }: { strict?: boolean } = {},
): NormalizedRouteRule[] {
if (!Array.isArray(value)) {
if (strict) throw new TypeError('Route rules must be an array');
return [];
}
if (strict && value.length > MAX_ROUTE_RULES) {
throw new TypeError(`Route rules limit is ${MAX_ROUTE_RULES}`);
}
const seen = new Set<string>();
const normalized: NormalizedRouteRule[] = [];
for (const candidate of value.slice(0, MAX_ROUTE_RULES)) {
try {
const rule = normalizeRule(candidate);
const key = `${rule.type}:${rule.value}`;
if (seen.has(key)) continue;
seen.add(key);
normalized.push(rule);
} catch (error) {
if (strict) throw error;
}
}
return normalized;
}
export function canAppendRouteRule(rules: unknown) {
return Array.isArray(rules) &&
rules.length < MAX_ROUTE_RULES &&
rules.every((rule) => String(record(rule).value || '').trim());
}