Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions src/app/createApp.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
11 changes: 10 additions & 1 deletion src/builders/BaseConfigBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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]) {
Expand Down
6 changes: 4 additions & 2 deletions src/parsers/subscription/httpSubscriptionFetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand Down