mirror of
https://github.com/Viren070/AIOStreams.git
synced 2025-12-01 23:14:04 +01:00
133 lines
3.7 KiB
TypeScript
133 lines
3.7 KiB
TypeScript
import { StreamProxyConfig } from '../db';
|
|
import { Cache, createLogger, maskSensitiveInfo, Env } from '../utils';
|
|
|
|
const logger = createLogger('proxy');
|
|
const cache = Cache.getInstance<string, string>('publicIp');
|
|
|
|
export interface ProxyStream {
|
|
url: string;
|
|
filename?: string;
|
|
headers?: {
|
|
request?: Record<string, string>;
|
|
response?: Record<string, string>;
|
|
};
|
|
}
|
|
|
|
type ValidatedStreamProxyConfig = StreamProxyConfig & {
|
|
id: 'mediaflow' | 'stremthru';
|
|
url: string;
|
|
credentials: string;
|
|
};
|
|
|
|
export abstract class BaseProxy {
|
|
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 ?? false,
|
|
id: config.id,
|
|
url: config.url,
|
|
credentials: config.credentials,
|
|
publicIp: config.publicIp,
|
|
proxiedAddons: config.proxiedAddons,
|
|
proxiedServices: config.proxiedServices,
|
|
};
|
|
}
|
|
|
|
public getConfig(): StreamProxyConfig {
|
|
return this.config;
|
|
}
|
|
|
|
protected abstract generateProxyUrl(endpoint: string): URL;
|
|
protected abstract getPublicIpEndpoint(): string;
|
|
protected abstract getPublicIpFromResponse(data: any): string | null;
|
|
protected abstract generateStreamUrls(
|
|
streams: ProxyStream[]
|
|
): Promise<string[] | null>;
|
|
|
|
public async getPublicIp(): Promise<string | null> {
|
|
if (!this.config.url) {
|
|
logger.error('Proxy URL is missing');
|
|
throw new Error('Proxy URL is missing');
|
|
}
|
|
|
|
if (this.config.publicIp) {
|
|
return this.config.publicIp;
|
|
}
|
|
|
|
const proxyUrl = new URL(this.config.url.replace(/\/$/, ''));
|
|
if (this.PRIVATE_CIDR.test(proxyUrl.hostname)) {
|
|
logger.error('Proxy URL is a private IP address, returning null');
|
|
return null;
|
|
}
|
|
|
|
const cacheKey = `${this.config.id}:${this.config.url}:${this.config.credentials}`;
|
|
const cachedPublicIp = cache ? cache.get(cacheKey) : null;
|
|
if (cachedPublicIp) {
|
|
logger.debug('Returning cached public IP');
|
|
return cachedPublicIp;
|
|
}
|
|
|
|
const ipUrl = this.generateProxyUrl(this.getPublicIpEndpoint());
|
|
|
|
if (Env.LOG_SENSITIVE_INFO) {
|
|
logger.debug(`GET ${ipUrl.toString()}`);
|
|
} else {
|
|
logger.debug(
|
|
`GET ${ipUrl.protocol}://${maskSensitiveInfo(ipUrl.hostname)}${ipUrl.port ? `:${ipUrl.port}` : ''}${ipUrl.pathname}`
|
|
);
|
|
}
|
|
|
|
const response = await fetch(ipUrl.toString(), {
|
|
method: 'GET',
|
|
headers: this.getHeaders(),
|
|
signal: AbortSignal.timeout(30000), // 30 second timeout
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`${response.status}: ${response.statusText}`);
|
|
}
|
|
|
|
const data = await response.json();
|
|
const publicIp = this.getPublicIpFromResponse(data);
|
|
|
|
if (publicIp && cache) {
|
|
cache.set(cacheKey, publicIp, 900); // 15 minute cache
|
|
} else {
|
|
logger.error(
|
|
`Proxy did not respond with a public IP. Response: ${JSON.stringify(data)}`
|
|
);
|
|
throw new Error('Proxy did not respond with a public IP');
|
|
}
|
|
|
|
return publicIp;
|
|
}
|
|
|
|
protected abstract getHeaders(): Record<string, string>;
|
|
|
|
public async generateUrls(streams: ProxyStream[]): Promise<string[] | null> {
|
|
if (!streams.length) {
|
|
return [];
|
|
}
|
|
|
|
if (!this.config) {
|
|
throw new Error('Proxy configuration is missing');
|
|
}
|
|
|
|
try {
|
|
return await this.generateStreamUrls(streams);
|
|
} catch (error) {
|
|
logger.error(
|
|
`Failed to generate proxy URLs: ${error instanceof Error ? error.message : String(error)}`
|
|
);
|
|
return null;
|
|
}
|
|
}
|
|
}
|