diff --git a/src/utils/ranges.ts b/src/utils/ranges.ts index a0a38d3..9419a64 100644 --- a/src/utils/ranges.ts +++ b/src/utils/ranges.ts @@ -1,4 +1,4 @@ -import { readdir } from 'node:fs/promises' +import { readdir, stat } from 'node:fs/promises' import * as path from 'node:path' export interface BlockedIpResult { @@ -62,6 +62,35 @@ let networkRefreshPromise: Promise | null = null let networksCheckedAt = 0 let networksEtag: string | null = null +interface BlockedListCache { + mtimeMs: number; + parsedCidrs: { cidr: string, parsed: ParsedCidr }[]; +} +const blockedCache = new Map(); + +async function getBlockedCidrs(fileName: string, filePath: string) { + const fileStat = await stat(filePath).catch(() => null); + if (!fileStat) return []; + + const cached = blockedCache.get(fileName); + if (cached && cached.mtimeMs === fileStat.mtimeMs) { + return cached.parsedCidrs; + } + + const content = await Bun.file(filePath).text(); + const cidrs = extractCidrs(content); + const parsedCidrs = []; + for (const cidr of cidrs) { + const parsed = parseCidr(cidr); + if (parsed) { + parsedCidrs.push({ cidr, parsed }); + } + } + + blockedCache.set(fileName, { mtimeMs: fileStat.mtimeMs, parsedCidrs }); + return parsedCidrs; +} + function ipv4ToInt(ip: string): number | null { const parts = ip.trim().split('.') if (parts.length !== 4) return null @@ -326,11 +355,19 @@ export async function checkIpRanges(ip: string): Promise { for (const fileName of files) { const filePath = path.join(BLOCKED_DIR, fileName) - const content = await Bun.file(filePath).text() - const cidrs = extractCidrs(content) + const cachedCidrs = await getBlockedCidrs(fileName, filePath) - for (const cidr of cidrs) { - if (isIpInCidr(parsedIp, cidr)) { + for (const item of cachedCidrs) { + const { cidr, parsed } = item; + let matched = false; + + if (parsedIp.version === 4 && parsed.version === 4) { + matched = (parsedIp.value & parsed.mask) === parsed.network + } else if (parsedIp.version === 6 && parsed.version === 6) { + matched = (parsedIp.value & parsed.mask) === parsed.network + } + + if (matched) { return { blocked: path.basename(fileName, '.txt') != 'cloudflare', list: path.basename(fileName, '.txt'), diff --git a/src/utils/redis.ts b/src/utils/redis.ts index 7b10eec..378eade 100644 --- a/src/utils/redis.ts +++ b/src/utils/redis.ts @@ -8,19 +8,20 @@ const redis = new Redis({ redis.on('ready', async function () { console.log('connected to redis') - - setInterval(async () => { - const files = fs.readdirSync('videos') - const targetFiles = files.filter((file) => file.endsWith('.webm') || file.endsWith('.mp4') || file.endsWith('.m4a')) - targetFiles.forEach(async (f) => { - const videoId = f.includes('_') ? f.split('_')[0] : f.replace('.mp4', '') - const isActive = await redis.get(videoId) - if (!isActive) { - fs.unlinkSync(`./videos/${f}`) - console.log(`deleted file ${f} because there is no active download of it`) - } - }) - }, 5 * 60000) }) +setInterval(async () => { + if (redis.status !== 'ready') return; + const files = fs.readdirSync('videos') + const targetFiles = files.filter((file) => file.endsWith('.webm') || file.endsWith('.mp4') || file.endsWith('.m4a')) + targetFiles.forEach(async (f) => { + const videoId = f.includes('_') ? f.split('_')[0] : f.replace('.mp4', '') + const isActive = await redis.get(videoId) + if (!isActive) { + fs.unlinkSync(`./videos/${f}`) + console.log(`deleted file ${f} because there is no active download of it`) + } + }) +}, 5 * 60000) + export default redis \ No newline at end of file