From c60ef6fde9c0de6abc98f2cb2de2a7e981719f3e Mon Sep 17 00:00:00 2001 From: Viren070 Date: Fri, 13 Jun 2025 16:24:51 +0100 Subject: [PATCH] fix: update handling of default/forced values --- packages/core/src/db/schemas.ts | 6 +- packages/core/src/proxy/base.ts | 14 +- packages/core/src/utils/config.ts | 64 ++++-- .../frontend/src/components/menu/proxy.tsx | 191 +++++------------- .../frontend/src/components/menu/services.tsx | 44 +--- packages/frontend/src/context/userData.tsx | 100 ++++++++- 6 files changed, 207 insertions(+), 212 deletions(-) diff --git a/packages/core/src/db/schemas.ts b/packages/core/src/db/schemas.ts index 7a25c619..31440af5 100644 --- a/packages/core/src/db/schemas.ts +++ b/packages/core/src/db/schemas.ts @@ -41,9 +41,9 @@ const Formatter = z.object({ const StreamProxyConfig = z.object({ enabled: z.boolean().optional(), - id: z.enum(constants.PROXY_SERVICES), - url: z.string().url(), - credentials: z.string().min(1), + id: z.enum(constants.PROXY_SERVICES).optional(), + url: z.string().optional(), + credentials: z.string().min(1).optional(), publicIp: z.string().ip().optional(), proxiedAddons: z.array(z.string().min(1)).optional(), proxiedServices: z.array(z.string().min(1)).optional(), diff --git a/packages/core/src/proxy/base.ts b/packages/core/src/proxy/base.ts index 26c47c3a..4d805255 100644 --- a/packages/core/src/proxy/base.ts +++ b/packages/core/src/proxy/base.ts @@ -13,14 +13,24 @@ export interface ProxyStream { }; } +type ValidatedStreamProxyConfig = StreamProxyConfig & { + id: 'mediaflow' | 'stremthru'; + url: string; + credentials: string; +}; + export abstract class BaseProxy { - protected readonly config: StreamProxyConfig; + protected readonly config: ValidatedStreamProxyConfig; private readonly PRIVATE_CIDR = /^(10\.|127\.|172\.(1[6-9]|2[0-9]|3[01])\.|192\.168\.)/; constructor(config: StreamProxyConfig) { + if (!config.id || !config.credentials || !config.url) { + throw new Error('Proxy configuration is missing'); + } + this.config = { - enabled: config.enabled, + enabled: config.enabled ?? false, id: config.id, url: config.url, credentials: config.credentials, diff --git a/packages/core/src/utils/config.ts b/packages/core/src/utils/config.ts index be1c3990..5303ee29 100644 --- a/packages/core/src/utils/config.ts +++ b/packages/core/src/utils/config.ts @@ -13,7 +13,7 @@ import { createProxy } from '../proxy'; import { constants } from '.'; import { isEncrypted, decryptString, encryptString } from './crypto'; import { Env } from './env'; -import { createLogger } from './logger'; +import { createLogger, maskSensitiveInfo } from './logger'; import { ZodError } from 'zod'; import { ConditionParser } from '../parser/conditions'; import { RPDB } from './rpdb'; @@ -273,11 +273,15 @@ export async function validateConfig( } if (config.proxy) { - config.proxy = await validateProxy( - config.proxy, - skipErrorsFromAddonsOrProxies, - decryptValues - ); + const decryptedProxy = ensureDecrypted(config).proxy; + if (decryptedProxy) { + config.proxy = await validateProxy( + config.proxy, + decryptedProxy, + skipErrorsFromAddonsOrProxies, + decryptValues + ); + } } if (config.rpdbApiKey) { @@ -351,7 +355,7 @@ async function validateRegexes(config: UserData) { ); } -function ensureDecrypted(config: UserData) { +function ensureDecrypted(config: UserData): UserData { const decryptedConfig = { ...config }; // Helper function to decrypt a value if needed @@ -369,7 +373,7 @@ function ensureDecrypted(config: UserData) { if (!service.credentials) continue; for (const [credential, value] of Object.entries(service.credentials)) { service.credentials[credential] = tryDecrypt( - value, + decodeURIComponent(value), `credential ${credential}` ); } @@ -378,8 +382,14 @@ function ensureDecrypted(config: UserData) { // Decrypt proxy config if (decryptedConfig.proxy) { const proxy = decryptedConfig.proxy; - proxy.credentials = tryDecrypt(proxy.credentials, 'proxy credentials'); - proxy.url = tryDecrypt(proxy.url, 'proxy URL'); + proxy.credentials = tryDecrypt( + proxy.credentials ? decodeURIComponent(proxy.credentials) : undefined, + 'proxy credentials' + ); + proxy.url = tryDecrypt( + proxy.url ? decodeURIComponent(proxy.url) : undefined, + 'proxy URL' + ); } return decryptedConfig; @@ -519,6 +529,7 @@ function validateOption( if (option.forced) { value = option.forced; } + value = decodeURIComponent(value); if (isEncrypted(value) && decryptValues) { const { success, data, error } = decryptString(value); if (!success) { @@ -547,6 +558,7 @@ function validateOption( async function validateProxy( proxy: StreamProxyConfig, + decryptedProxy: StreamProxyConfig, skipProxyErrors: boolean = false, decryptCredentials: boolean = false ): Promise { @@ -572,18 +584,10 @@ async function validateProxy( throw new Error('Proxy credentials are required'); } - const ProxyService = createProxy(proxy); - - try { - proxy.publicIp || (await ProxyService.getPublicIp()); - } catch (error) { - if (!skipProxyErrors) { - throw new Error( - `Failed to get the public IP of the proxy service ${proxy.id}: ${error}` - ); - } - } - + proxy.credentials = decodeURIComponent(proxy.credentials); + proxy.url = proxy.url.startsWith('aioEncrypt') + ? decodeURIComponent(proxy.url) + : proxy.url; if (isEncrypted(proxy.credentials) && decryptCredentials) { const { success, data, error } = decryptString(proxy.credentials); if (!success) { @@ -602,6 +606,22 @@ async function validateProxy( } proxy.url = data; } + + // use decrypted proxy config for validation. + const ProxyService = createProxy(decryptedProxy); + + try { + proxy.publicIp || (await ProxyService.getPublicIp()); + } catch (error) { + if (!skipProxyErrors) { + logger.error( + `Failed to get the public IP of the proxy service ${proxy.id} (${maskSensitiveInfo(proxy.url)}): ${error}` + ); + throw new Error( + `Failed to get the public IP of the proxy service ${proxy.id}: ${error}` + ); + } + } } return proxy; } diff --git a/packages/frontend/src/components/menu/proxy.tsx b/packages/frontend/src/components/menu/proxy.tsx index 424038f5..9d7e78eb 100644 --- a/packages/frontend/src/components/menu/proxy.tsx +++ b/packages/frontend/src/components/menu/proxy.tsx @@ -35,68 +35,12 @@ export function ProxyMenu() { ); } -// provides a page to configure a proxy -// use constants.PROXY_DETAILS to get the list of proxies -// and use status.settings.defaults.proxy to load default values if current userData doesn't have a value -// use status.settings.forced.proxy to always load the forced values - -// should look like this. -// a switch to enable/disable the proxy. should be enabled on left and then switch on right, with descriptin of setting below. -// when disabled, hide the rest of the settings -// a select menu to choose a proxy, mapped to the proxy.id option (Get name/label from details) -// shows the description below the select. -// then a password input to provide the credential -// then a multi select menu to choose services that proxy is used for -// then a multi select menu to choose addons that proxy is used for -// addon labels should use the addon name, and value should be the ID, calculated using same method as getPresetUniqueKey in addons.tsx - function Content() { const { status } = useStatus(); const { userData, setUserData } = useUserData(); const details = constants.PROXY_SERVICE_DETAILS; - // Initialize proxy configuration from userData, defaults, or forced values - const [proxyEnabled, setProxyEnabled] = useState(false); - const [selectedProxyId, setSelectedProxyId] = - useState('mediaflow'); - const [proxyUrl, setProxyUrl] = useState(''); - const [proxyCredentials, setProxyCredentials] = useState(''); - const [proxiedServices, setProxiedServices] = useState( - undefined - ); - const [proxiedAddons, setProxiedAddons] = useState( - undefined - ); - const [hasChanges, setHasChanges] = useState(false); - // Effect to initialize values from userData/defaults/forced - useEffect(() => { - if (!status) return; - - const forced = status.settings.forced.proxy; - const defaults = status.settings.defaults.proxy; - const current = userData.proxy; - - // Apply forced values first, then current values, then defaults - setProxyEnabled( - forced.enabled ?? current?.enabled ?? defaults.enabled ?? false - ); - setSelectedProxyId( - (forced.id ?? current?.id ?? defaults.id ?? '') as ProxyServiceId - ); - setProxyCredentials( - forced.credentials ?? current?.credentials ?? defaults.credentials ?? '' - ); - setProxyUrl(forced.url ?? current?.url ?? defaults.url ?? ''); - setProxiedServices( - forced.proxiedServices ?? - current?.proxiedServices ?? - defaults.proxiedServices ?? - [] - ); - setProxiedAddons(current?.proxiedAddons); - setHasChanges(false); - }, [status]); // Generate options for proxy service select const proxyOptions = Object.entries(details).map(([id, detail]) => ({ @@ -126,61 +70,7 @@ function Content() { }; }); - // Handle changes - const handleProxyChange = (enabled: boolean) => { - setProxyEnabled(enabled); - if (!enabled && !selectedProxyId && userData.proxy === undefined) { - setHasChanges(false); - } else { - setHasChanges(true); - } - }; - - const handleUrlChange = (value: string) => { - setProxyUrl(value); - setHasChanges(true); - }; - - const handleProxyServiceChange = (value: string) => { - setSelectedProxyId(value as ProxyServiceId); - setHasChanges(true); - }; - - const handleCredentialsChange = (value: string) => { - setProxyCredentials(value); - setHasChanges(true); - }; - - const handleProxiedServicesChange = (values: string[]) => { - setProxiedServices(values); - setHasChanges(true); - }; - - const handleProxiedAddonsChange = (values: string[]) => { - setProxiedAddons(values); - setHasChanges(true); - }; - - const handleSubmit = () => { - const proxyConfig: ProxyConfig = { - enabled: proxyEnabled, - id: selectedProxyId, - url: proxyUrl, - credentials: proxyCredentials, - proxiedServices: proxiedServices, - proxiedAddons: proxiedAddons, - }; - - setUserData((prev) => ({ - ...prev, - proxy: proxyConfig, - })); - - setHasChanges(false); - toast.success('Proxy settings saved'); - }; - - // Check if values are forced + // lues are forced const isForced = status?.settings.forced.proxy; const isProxyForced = isForced?.enabled !== null; const isUrlForced = isForced?.url !== null; @@ -189,8 +79,8 @@ function Content() { const isServicesForced = isForced?.proxiedServices !== null; const isProxiedAddonsDisabled = isForced?.disableProxiedAddons; - const selectedProxyDetails = selectedProxyId - ? details[selectedProxyId] + const selectedProxyDetails = userData.proxy?.id + ? details[userData.proxy.id] : undefined; return ( @@ -213,8 +103,13 @@ function Content() { { + setUserData((prev) => ({ + ...prev, + proxy: { ...prev.proxy, enabled: v }, + })); + }} disabled={isProxyForced} /> @@ -223,10 +118,15 @@ function Content() {