Experimental: LAN Cache Support (WIP)
Some checks failed
Build / Build (push) Has been cancelled
Build / Diff output (push) Has been cancelled
Build / Deploy to Cloudflare Pages (3.114.12) (push) Has been cancelled
Build / Deploy to GitHub and GitLab (push) Has been cancelled
Build / Remove Artifacts after Deployment (push) Has been cancelled

This commit is contained in:
SukkaW
2026-03-06 17:32:16 +08:00
parent 8dda300d48
commit baeae2dd73
2 changed files with 93 additions and 4 deletions

View File

@@ -3,9 +3,10 @@ import path from 'node:path';
import { DOMESTICS, DOH_BOOTSTRAP, AdGuardHomeDNSMapping } from '../Source/non_ip/domestic';
import { DIRECTS, HOSTS, LAN } from '../Source/non_ip/direct';
import type { DNSMapping } from '../Source/non_ip/direct';
import { readFileIntoProcessedArray } from './lib/fetch-text-by-line';
import { fetchRemoteTextByLine, readFileIntoProcessedArray } from './lib/fetch-text-by-line';
import { compareAndWriteFile } from './lib/create-file';
import { task } from './trace';
import type { Span } from './trace';
import { SHARED_DESCRIPTION } from './constants/description';
import { once } from 'foxts/once';
import * as yaml from 'yaml';
@@ -13,6 +14,7 @@ import { appendArrayInPlace } from 'foxts/append-array-in-place';
import { OUTPUT_INTERNAL_DIR, OUTPUT_MODULES_DIR, OUTPUT_MODULES_RULES_DIR, SOURCE_DIR } from './constants/dir';
import { MihomoNameserverPolicyOutput, RulesetOutput } from './lib/rules/ruleset';
import { SurgeOnlyRulesetOutput } from './lib/rules/ruleset';
import { $$fetch } from './lib/fetch-retry';
export function createGetDnsMappingRule(allowWildcard: boolean) {
const hasWildcard = (domain: string) => {
@@ -112,6 +114,7 @@ export const buildDomesticRuleset = task(require.main === module, __filename)(as
.addFromRuleset(lans)
.write(),
buildLANCacheRuleset(span),
...dataset.map(([name, { ruleset, domains }]) => {
if (!ruleset) {
return;
@@ -340,3 +343,84 @@ export const buildDomesticRuleset = task(require.main === module, __filename)(as
)
]);
});
async function buildLANCacheRuleset(span: Span) {
const childSpan = span.traceChild('build LAN cache ruleset');
const cacheDomainsData = await childSpan.traceChildAsync('fetch cache_domains.json', async () => (await $$fetch('https://cdn.jsdelivr.net/gh/uklans/cache-domains@master/cache_domains.json')).json());
if (!cacheDomainsData || typeof cacheDomainsData !== 'object' || !('cache_domains' in cacheDomainsData) || !Array.isArray(cacheDomainsData.cache_domains)) {
throw new TypeError('Invalid cache domains data');
}
const allDomainFiles = cacheDomainsData.cache_domains.reduce<string[]>((acc, { domain_files }) => {
if (Array.isArray(domain_files)) {
appendArrayInPlace(acc, domain_files);
}
return acc;
}, []);
const allDomains = (
await Promise.all(
allDomainFiles.map(
async (file) => childSpan.traceChildAsync(
'download ' + file,
async () => Array.fromAsync(await fetchRemoteTextByLine('https://cdn.jsdelivr.net/gh/uklans/cache-domains@master/' + file, true))
)
)
)
).flat();
const surgeOutput = new SurgeOnlyRulesetOutput(
span,
'lancache',
'sukka_local_dns_mapping',
OUTPUT_MODULES_RULES_DIR
)
.withTitle('Sukka\'s Ruleset - Local DNS Mapping (lancache)')
.appendDescription(
SHARED_DESCRIPTION,
'',
'This is an internal rule that is only referenced by sukka_local_dns_mapping.sgmodule',
'Do not use this file in your Rule section.'
);
const mihomoOutput = new MihomoNameserverPolicyOutput(
span,
'lancache',
'mihomo_nameserver_policy',
OUTPUT_INTERNAL_DIR
)
.withTitle('Sukka\'s Ruleset - Local DNS Mapping for Mihomo NameServer Policy (lancache)')
.appendDescription(
SHARED_DESCRIPTION,
'',
'This ruleset is only used for mihomo\'s nameserver-policy feature, which',
'is similar to the RULE-SET referenced by sukka_local_dns_mapping.sgmodule.',
'Do not use this file in your Rule section.'
);
for (let i = 0, len = allDomains.length; i < len; i++) {
const domain = allDomains[i];
if (domain.includes('*')) {
// If only *. prefix is used, we can convert it to DOMAIN-SUFFIX
if (domain.startsWith('*.') && !domain.slice(2).includes('*')) {
const domainSuffix = domain.slice(2);
surgeOutput.addDomainSuffix(domainSuffix);
mihomoOutput.addDomainSuffix(domainSuffix);
continue;
}
surgeOutput.addDomainWildcard(domain);
mihomoOutput.addDomainWildcard(domain);
continue;
}
surgeOutput.addDomain(domain);
mihomoOutput.addDomain(domain);
}
return Promise.all([
surgeOutput.write(),
mihomoOutput.write()
]);
}

View File

@@ -147,9 +147,14 @@ export class FileOutput {
return this;
}
bulkAddDomainWildcard(domains: string[]) {
for (let i = 0, len = domains.length; i < len; i++) {
this.wildcardSet.add(domains[i]);
addDomainWildcard(wildcard: string) {
this.wildcardSet.add(wildcard);
return this;
}
bulkAddDomainWildcard(wildcards: string[]) {
for (let i = 0, len = wildcards.length; i < len; i++) {
this.wildcardSet.add(wildcards[i]);
}
return this;
}