feat: migrate validatePath to modular

This commit is contained in:
divocat
2025-10-02 22:37:36 +03:00
parent 5e95148492
commit f58472a53d
7 changed files with 183 additions and 74 deletions

View File

@@ -1,70 +1,76 @@
export const STATUS_COLORS = {
SUCCESS: '#4caf50',
ERROR: '#f44336',
WARNING: '#ff9800'
SUCCESS: '#4caf50',
ERROR: '#f44336',
WARNING: '#ff9800',
};
export const FAKEIP_CHECK_DOMAIN = 'fakeip.podkop.fyi';
export const IP_CHECK_DOMAIN = 'ip.podkop.fyi';
export const REGIONAL_OPTIONS = ['russia_inside', 'russia_outside', 'ukraine_inside'];
export const REGIONAL_OPTIONS = [
'russia_inside',
'russia_outside',
'ukraine_inside',
];
export const ALLOWED_WITH_RUSSIA_INSIDE = [
'russia_inside',
'meta',
'twitter',
'discord',
'telegram',
'cloudflare',
'google_ai',
'google_play',
'hetzner',
'ovh',
'hodca',
'digitalocean',
'cloudfront'
'russia_inside',
'meta',
'twitter',
'discord',
'telegram',
'cloudflare',
'google_ai',
'google_play',
'hetzner',
'ovh',
'hodca',
'digitalocean',
'cloudfront',
];
export const DOMAIN_LIST_OPTIONS = {
russia_inside: 'Russia inside',
russia_outside: 'Russia outside',
ukraine_inside: 'Ukraine',
geoblock: 'Geo Block',
block: 'Block',
porn: 'Porn',
news: 'News',
anime: 'Anime',
youtube: 'Youtube',
discord: 'Discord',
meta: 'Meta',
twitter: 'Twitter (X)',
hdrezka: 'HDRezka',
tiktok: 'Tik-Tok',
telegram: 'Telegram',
cloudflare: 'Cloudflare',
google_ai: 'Google AI',
google_play: 'Google Play',
hodca: 'H.O.D.C.A',
hetzner: 'Hetzner ASN',
ovh: 'OVH ASN',
digitalocean: 'Digital Ocean ASN',
cloudfront: 'CloudFront ASN'
russia_inside: 'Russia inside',
russia_outside: 'Russia outside',
ukraine_inside: 'Ukraine',
geoblock: 'Geo Block',
block: 'Block',
porn: 'Porn',
news: 'News',
anime: 'Anime',
youtube: 'Youtube',
discord: 'Discord',
meta: 'Meta',
twitter: 'Twitter (X)',
hdrezka: 'HDRezka',
tiktok: 'Tik-Tok',
telegram: 'Telegram',
cloudflare: 'Cloudflare',
google_ai: 'Google AI',
google_play: 'Google Play',
hodca: 'H.O.D.C.A',
hetzner: 'Hetzner ASN',
ovh: 'OVH ASN',
digitalocean: 'Digital Ocean ASN',
cloudfront: 'CloudFront ASN',
};
export const UPDATE_INTERVAL_OPTIONS = {
'1h': 'Every hour',
'3h': 'Every 3 hours',
'12h': 'Every 12 hours',
'1d': 'Every day',
'3d': 'Every 3 days'
'1h': 'Every hour',
'3h': 'Every 3 hours',
'12h': 'Every 12 hours',
'1d': 'Every day',
'3d': 'Every 3 days',
};
export const DNS_SERVER_OPTIONS = {
'1.1.1.1': '1.1.1.1 (Cloudflare)',
'8.8.8.8': '8.8.8.8 (Google)',
'9.9.9.9': '9.9.9.9 (Quad9)',
'dns.adguard-dns.com': 'dns.adguard-dns.com (AdGuard Default)',
'unfiltered.adguard-dns.com': 'unfiltered.adguard-dns.com (AdGuard Unfiltered)',
'family.adguard-dns.com': 'family.adguard-dns.com (AdGuard Family)'
'1.1.1.1': '1.1.1.1 (Cloudflare)',
'8.8.8.8': '8.8.8.8 (Google)',
'9.9.9.9': '9.9.9.9 (Quad9)',
'dns.adguard-dns.com': 'dns.adguard-dns.com (AdGuard Default)',
'unfiltered.adguard-dns.com':
'unfiltered.adguard-dns.com (AdGuard Unfiltered)',
'family.adguard-dns.com': 'family.adguard-dns.com (AdGuard Family)',
};
export const DIAGNOSTICS_UPDATE_INTERVAL = 10000; // 10 seconds
@@ -77,15 +83,15 @@ export const DIAGNOSTICS_INITIAL_DELAY = 100; // 100 milliseconds
// Command scheduling intervals in diagnostics (in milliseconds)
export const COMMAND_SCHEDULING = {
P0_PRIORITY: 0, // Highest priority (no delay)
P1_PRIORITY: 100, // Very high priority
P2_PRIORITY: 300, // High priority
P3_PRIORITY: 500, // Above average
P4_PRIORITY: 700, // Standard priority
P5_PRIORITY: 900, // Below average
P6_PRIORITY: 1100, // Low priority
P7_PRIORITY: 1300, // Very low priority
P8_PRIORITY: 1500, // Background execution
P9_PRIORITY: 1700, // Idle mode execution
P10_PRIORITY: 1900 // Lowest priority
P0_PRIORITY: 0, // Highest priority (no delay)
P1_PRIORITY: 100, // Very high priority
P2_PRIORITY: 300, // High priority
P3_PRIORITY: 500, // Above average
P4_PRIORITY: 700, // Standard priority
P5_PRIORITY: 900, // Below average
P6_PRIORITY: 1100, // Low priority
P7_PRIORITY: 1300, // Very low priority
P8_PRIORITY: 1500, // Background execution
P9_PRIORITY: 1700, // Idle mode execution
P10_PRIORITY: 1900, // Lowest priority
};

View File

@@ -2,4 +2,4 @@
'require baseclass';
export * from './validators';
export * from './constants'
export * from './constants';

View File

@@ -2,3 +2,4 @@ export * from './validateIp';
export * from './validateDomain';
export * from './validateDns';
export * from './validateUrl';
export * from './validatePath';

View File

@@ -0,0 +1,39 @@
import { describe, expect, it } from 'vitest';
import { validatePath } from '../validatePath';
export const validPaths = [
['Single level', '/etc'],
['Nested path', '/usr/local/bin'],
['With dash', '/var/log/nginx-access'],
['With underscore', '/opt/my_app/config'],
['With numbers', '/data123/files'],
['With dots', '/home/user/.config'],
['Deep nested', '/a/b/c/d/e/f/g'],
];
export const invalidPaths = [
['Empty string', ''],
['Missing starting slash', 'usr/local'],
['Only dot', '.'],
['Space inside', '/path with space'],
['Illegal char', '/path$'],
['Backslash not allowed', '\\windows\\path'],
['Relative path ./', './relative'],
['Relative path ../', '../parent'],
];
describe('validatePath', () => {
describe.each(validPaths)('Valid path: %s', (_desc, path) => {
it(`returns valid=true for "${path}"`, () => {
const res = validatePath(path);
expect(res.valid).toBe(true);
});
});
describe.each(invalidPaths)('Invalid path: %s', (_desc, path) => {
it(`returns valid=false for "${path}"`, () => {
const res = validatePath(path);
expect(res.valid).toBe(false);
});
});
});

View File

@@ -0,0 +1,25 @@
import { ValidationResult } from './types';
export function validatePath(value: string): ValidationResult {
if (!value) {
return {
valid: false,
message: 'Path cannot be empty',
};
}
const pathRegex = /^\/[a-zA-Z0-9_\-/.]+$/;
if (pathRegex.test(value)) {
return {
valid: true,
message: 'Valid',
};
}
return {
valid: false,
message:
'Invalid path format. Path must start with "/" and contain valid characters',
};
}

View File

@@ -409,12 +409,18 @@ function createConfigSection(section, map, network) {
o.rmempty = false;
o.ucisection = s.section;
o.validate = function (section_id, value) {
if (!value || value.length === 0) return true;
const pathRegex = /^\/[a-zA-Z0-9_\-\/\.]+$/;
if (!pathRegex.test(value)) {
return _('Invalid path format. Path must start with "/" and contain valid characters');
// Optional
if (!value || value.length === 0) {
return true
}
return true;
const validation = main.validatePath(value);
if (validation.valid) {
return true;
}
return _(validation.message)
};
o = s.taboption('basic', form.Flag, 'remote_domain_lists_enabled', _('Remote Domain Lists'), _('Download and use domain lists from remote URLs'));
@@ -453,12 +459,18 @@ function createConfigSection(section, map, network) {
o.rmempty = false;
o.ucisection = s.section;
o.validate = function (section_id, value) {
if (!value || value.length === 0) return true;
const pathRegex = /^\/[a-zA-Z0-9_\-\/\.]+$/;
if (!pathRegex.test(value)) {
return _('Invalid path format. Path must start with "/" and contain valid characters');
// Optional
if (!value || value.length === 0) {
return true
}
return true;
const validation = main.validatePath(value);
if (validation.valid) {
return true;
}
return _(validation.message)
};
o = s.taboption('basic', form.ListValue, 'user_subnet_list_type', _('User Subnet List Type'), _('Select how to add your custom subnets'));

View File

@@ -59,6 +59,27 @@ function validateUrl(url, protocols = ["http:", "https:"]) {
}
}
// src/validators/validatePath.ts
function validatePath(value) {
if (!value) {
return {
valid: false,
message: "Path cannot be empty"
};
}
const pathRegex = /^\/[a-zA-Z0-9_\-/.]+$/;
if (pathRegex.test(value)) {
return {
valid: true,
message: "Valid"
};
}
return {
valid: false,
message: 'Invalid path format. Path must start with "/" and contain valid characters'
};
}
// src/constants.ts
var STATUS_COLORS = {
SUCCESS: "#4caf50",
@@ -67,7 +88,11 @@ var STATUS_COLORS = {
};
var FAKEIP_CHECK_DOMAIN = "fakeip.podkop.fyi";
var IP_CHECK_DOMAIN = "ip.podkop.fyi";
var REGIONAL_OPTIONS = ["russia_inside", "russia_outside", "ukraine_inside"];
var REGIONAL_OPTIONS = [
"russia_inside",
"russia_outside",
"ukraine_inside"
];
var ALLOWED_WITH_RUSSIA_INSIDE = [
"russia_inside",
"meta",
@@ -174,5 +199,6 @@ return baseclass.extend({
validateDNS,
validateDomain,
validateIPV4,
validatePath,
validateUrl
});