Skip to content

Commit 3b85063

Browse files
committed
feat: 简单实现了 SUB_STORE_MMDB_CRON 定时更新 MMDB. ASN: SUB_STORE_MMDB_ASN_PATH, SUB_STORE_MMDB_ASN_URL. COUNTRY: SUB_STORE_MMDB_COUNTRY_PATH, SUB_STORE_MMDB_COUNTRY_URL; 脚本中新增 ProxyUtils.downloadFile 方便下载二进制文件.
1 parent 7f691c8 commit 3b85063

File tree

5 files changed

+96
-13
lines changed

5 files changed

+96
-13
lines changed

backend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sub-store",
3-
"version": "2.19.29",
3+
"version": "2.19.30",
44
"description": "Advanced Subscription Manager for QX, Loon, Surge, Stash and Shadowrocket.",
55
"main": "src/main.js",
66
"scripts": {

backend/src/core/proxy-utils/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Buffer } from 'buffer';
22
import rs from '@/utils/rs';
33
import YAML from '@/utils/yaml';
4-
import download from '@/utils/download';
4+
import download, { downloadFile } from '@/utils/download';
55
import {
66
isIPv4,
77
isIPv6,
@@ -330,6 +330,7 @@ export const ProxyUtils = {
330330
MMDB,
331331
Gist,
332332
download,
333+
downloadFile,
333334
isValidUUID,
334335
doh,
335336
};

backend/src/restful/index.js

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import express from '@/vendor/express';
22
import $ from '@/core/app';
33
import migrate from '@/utils/migration';
4-
import download from '@/utils/download';
4+
import download, { downloadFile } from '@/utils/download';
55
import { syncArtifacts, produceArtifact } from '@/restful/sync';
66
import { gistBackupAction } from '@/restful/miscs';
77
import { TOKENS_KEY } from '@/constants';
@@ -35,7 +35,7 @@ export default function serve() {
3535
const fe_be_path = eval('process.env.SUB_STORE_FRONTEND_BACKEND_PATH');
3636
const fe_path = eval('process.env.SUB_STORE_FRONTEND_PATH');
3737
if (be_prefix || be_merge) {
38-
if(!fe_be_path.startsWith('/')){
38+
if (!fe_be_path.startsWith('/')) {
3939
throw new Error(
4040
'SUB_STORE_FRONTEND_BACKEND_PATH should start with /',
4141
);
@@ -48,15 +48,20 @@ export default function serve() {
4848
$app.use((req, res, next) => {
4949
if (req.path.startsWith(fe_be_path)) {
5050
req.url = req.url.replace(fe_be_path, '') || '/';
51-
if(be_merge && req.url.startsWith('/api/')){
51+
if (be_merge && req.url.startsWith('/api/')) {
5252
req.query['share'] = 'true';
5353
}
5454
next();
5555
return;
5656
}
57-
const pathname = decodeURIComponent(req._parsedUrl.pathname) || '/';
58-
if(be_merge && req.path.startsWith('/share/') && req.query.token){
59-
if (req.method.toLowerCase() !== 'get'){
57+
const pathname =
58+
decodeURIComponent(req._parsedUrl.pathname) || '/';
59+
if (
60+
be_merge &&
61+
req.path.startsWith('/share/') &&
62+
req.query.token
63+
) {
64+
if (req.method.toLowerCase() !== 'get') {
6065
res.status(405).send('Method not allowed');
6166
return;
6267
}
@@ -67,14 +72,14 @@ export default function serve() {
6772
`/share/${t.type}/${t.name}` === pathname &&
6873
(t.exp == null || t.exp > Date.now()),
6974
);
70-
if (token){
75+
if (token) {
7176
next();
7277
return;
7378
}
7479
}
75-
if (be_merge && fe_path && req.path.indexOf('/',1) == -1) {
76-
if (req.path.indexOf('.') == -1){
77-
req.url = "/index.html"
80+
if (be_merge && fe_path && req.path.indexOf('/', 1) == -1) {
81+
if (req.path.indexOf('.') == -1) {
82+
req.url = '/index.html';
7883
}
7984
const express_ = eval(`require("express")`);
8085
const mime_ = eval(`require("mime-types")`);
@@ -85,7 +90,7 @@ export default function serve() {
8590
if (type) {
8691
res.set('Content-Type', type);
8792
}
88-
}
93+
},
8994
});
9095
staticFileMiddleware(req, res, next);
9196
return;
@@ -230,6 +235,60 @@ export default function serve() {
230235
// 'Asia/Shanghai' // timeZone
231236
);
232237
}
238+
const mmdb_cron = eval('process.env.SUB_STORE_MMDB_CRON');
239+
const countryFile = eval('process.env.SUB_STORE_MMDB_COUNTRY_PATH');
240+
const countryUrl = eval('process.env.SUB_STORE_MMDB_COUNTRY_URL');
241+
const asnFile = eval('process.env.SUB_STORE_MMDB_ASN_PATH');
242+
const asnUrl = eval('process.env.SUB_STORE_MMDB_ASN_URL');
243+
if (mmdb_cron && ((countryFile && countryUrl) || (asnFile && asnUrl))) {
244+
$.info(`[MMDB CRON] ${mmdb_cron} enabled`);
245+
const { CronJob } = eval(`require("cron")`);
246+
new CronJob(
247+
mmdb_cron,
248+
async function () {
249+
try {
250+
$.info(`[MMDB CRON] ${mmdb_cron} started`);
251+
if (countryFile && countryUrl) {
252+
try {
253+
$.info(
254+
`[MMDB CRON] downloading ${countryUrl} to ${countryFile}`,
255+
);
256+
await downloadFile(countryUrl, countryFile);
257+
} catch (e) {
258+
$.error(
259+
`[MMDB CRON] ${countryUrl} download failed: ${
260+
e.message ?? e
261+
}`,
262+
);
263+
}
264+
}
265+
if (asnFile && asnUrl) {
266+
try {
267+
$.info(
268+
`[MMDB CRON] downloading ${asnUrl} to ${asnFile}`,
269+
);
270+
await downloadFile(asnUrl, asnFile);
271+
} catch (e) {
272+
$.error(
273+
`[MMDB CRON] ${asnUrl} download failed: ${
274+
e.message ?? e
275+
}`,
276+
);
277+
}
278+
}
279+
280+
$.info(`[MMDB CRON] ${mmdb_cron} finished`);
281+
} catch (e) {
282+
$.error(
283+
`[MMDB CRON] ${mmdb_cron} error: ${e.message ?? e}`,
284+
);
285+
}
286+
}, // onTick
287+
null, // onComplete
288+
true, // start
289+
// 'Asia/Shanghai' // timeZone
290+
);
291+
}
233292
const path = eval(`require("path")`);
234293
const fs = eval(`require("fs")`);
235294
const data_url = eval('process.env.SUB_STORE_DATA_URL');

backend/src/utils/download.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,25 @@ export default async function download(
273273
}
274274
return result;
275275
}
276+
277+
export async function downloadFile(url, file) {
278+
const undici = eval("require('undici')");
279+
const fs = eval("require('fs')");
280+
const { pipeline } = eval("require('stream/promises')");
281+
const { Agent, interceptors, request } = undici;
282+
$.info(`Downloading file...\nURL: ${url}\nFile: ${file}`);
283+
const { body, statusCode } = await request(url, {
284+
dispatcher: new Agent().compose(
285+
interceptors.redirect({
286+
maxRedirections: 3,
287+
throwOnRedirect: true,
288+
}),
289+
),
290+
});
291+
if (statusCode !== 200)
292+
throw new Error(`Failed to download file from ${url}`);
293+
const fileStream = fs.createWriteStream(file);
294+
await pipeline(body, fileStream);
295+
$.info(`File downloaded from ${url} to ${file}`);
296+
return file;
297+
}

scripts/demo.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ function operator(proxies = [], targetPlatform, context) {
116116
// getISO, // 获取 ISO 3166-1 alpha-2 代码
117117
// Gist, // Gist 类
118118
// download, // 内部的下载方法, 见 backend/src/utils/download.js
119+
// downloadFile, // 下载二进制文件, 见 backend/src/utils/download.js
119120
// MMDB, // Node.js 环境 可用于模拟 Surge/Loon 的 $utils.ipasn, $utils.ipaso, $utils.geoip. 具体见 https://t.me/zhetengsha/1269
120121
// isValidUUID, // 辅助判断是否为有效的 UUID
121122
// }

0 commit comments

Comments
 (0)