diff --git a/src/app/createApp.jsx b/src/app/createApp.jsx index 6252a18ef..8ca1dccc7 100644 --- a/src/app/createApp.jsx +++ b/src/app/createApp.jsx @@ -158,9 +158,12 @@ export function createApp(bindings = {}) { includeAutoSelect ); await builder.build(); - return c.text(builder.formatConfig(), 200, { - 'Content-Type': 'text/yaml; charset=utf-8' - }); + const userinfo = builder.getSubscriptionUserinfo(); + const headers = { 'Content-Type': 'text/yaml; charset=utf-8' }; + if (userinfo) { + headers['subscription-userinfo'] = userinfo; + } + return c.text(builder.formatConfig(), 200, headers); } catch (error) { return handleError(c, error, runtime.logger); } @@ -200,7 +203,10 @@ export function createApp(bindings = {}) { builder.setSubscriptionUrl(c.req.url); await builder.build(); - c.header('subscription-userinfo', 'upload=0; download=0; total=10737418240; expire=2546249531'); + const userinfo = builder.getSubscriptionUserinfo(); + if (userinfo) { + c.header('subscription-userinfo', userinfo); + } return c.text(builder.formatConfig()); } catch (error) { return handleError(c, error, runtime.logger); diff --git a/src/builders/BaseConfigBuilder.js b/src/builders/BaseConfigBuilder.js index f060f5758..20f264897 100644 --- a/src/builders/BaseConfigBuilder.js +++ b/src/builders/BaseConfigBuilder.js @@ -15,6 +15,7 @@ export class BaseConfigBuilder { this.groupByCountry = groupByCountry; this.includeAutoSelect = includeAutoSelect; this.providerUrls = []; // URLs to use as providers (auto-sync) + this.subscriptionUserinfo = undefined; } async build() { @@ -91,7 +92,11 @@ export class BaseConfigBuilder { try { const fetchResult = await fetchSubscriptionWithFormat(trimmedUrl, this.userAgent); if (fetchResult) { - const { content, format, url: originalUrl } = fetchResult; + const { content, format, url: originalUrl, subscriptionUserinfo } = fetchResult; + + if (subscriptionUserinfo && !this.subscriptionUserinfo) { + this.subscriptionUserinfo = subscriptionUserinfo; + } // If format is compatible with target client, use as provider if (this.isCompatibleProviderFormat(format)) { @@ -254,6 +259,10 @@ export class BaseConfigBuilder { return this.appliedOverrideKeys?.has(key); } + getSubscriptionUserinfo() { + return this.subscriptionUserinfo; + } + getOutboundsList() { let outbounds; if (typeof this.selectedRules === 'string' && PREDEFINED_RULE_SETS[this.selectedRules]) { diff --git a/src/parsers/subscription/httpSubscriptionFetcher.js b/src/parsers/subscription/httpSubscriptionFetcher.js index 2a4682e8e..cf6fd4bad 100644 --- a/src/parsers/subscription/httpSubscriptionFetcher.js +++ b/src/parsers/subscription/httpSubscriptionFetcher.js @@ -87,7 +87,7 @@ export async function fetchSubscription(url, userAgent) { * Fetch subscription content and detect its format without parsing * @param {string} url - The subscription URL to fetch * @param {string} userAgent - Optional User-Agent header - * @returns {Promise<{content: string, format: 'clash'|'singbox'|'unknown', url: string}|null>} + * @returns {Promise<{content: string, format: 'clash'|'singbox'|'unknown', url: string, subscriptionUserinfo?: string}|null>} */ export async function fetchSubscriptionWithFormat(url, userAgent) { try { @@ -106,7 +106,9 @@ export async function fetchSubscriptionWithFormat(url, userAgent) { const content = decodeContent(text); const format = detectFormat(content); - return { content, format, url }; + const subscriptionUserinfo = response.headers.get('subscription-userinfo') || undefined; + + return { content, format, url, subscriptionUserinfo }; } catch (error) { console.error('Error fetching subscription:', error); return null;