From 1fcc1f77d726b116a5cda539728f86763bb90761 Mon Sep 17 00:00:00 2001
From: Peng-YM <1048217874pengym@gmail.com>
Date: Wed, 26 Aug 2020 21:09:34 +0800
Subject: [PATCH] Add cache
---
backend/.idea/workspace.xml | 141 ++++++++++++++++++++++--------------
backend/sub-store.js | 93 ++++++++++++++----------
web/public/index.html | 27 ++++---
web/src/App.vue | 7 +-
web/src/config.js | 2 +-
5 files changed, 164 insertions(+), 106 deletions(-)
diff --git a/backend/.idea/workspace.xml b/backend/.idea/workspace.xml
index 0ca3d0f..a7d753c 100644
--- a/backend/.idea/workspace.xml
+++ b/backend/.idea/workspace.xml
@@ -20,15 +20,11 @@
-
-
-
-
-
-
-
+
+
+
@@ -83,7 +79,8 @@
-
+
+
@@ -103,101 +100,133 @@
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
-
-
+
+
-
+
+
+
\ No newline at end of file
diff --git a/backend/sub-store.js b/backend/sub-store.js
index 47c123c..6e23d3f 100644
--- a/backend/sub-store.js
+++ b/backend/sub-store.js
@@ -4,22 +4,32 @@
* @Description:
* 适用于QX,Loon,Surge的订阅管理工具。
* - 功能
- * 1. 订阅转换,支持SS, SSR, V2RayN, QX, Loon, Surge格式的互相转换。
+ * 1. 订阅转换,支持SS, SSR, V2RayN, QX, Loon, Surge, Clash格式的互相转换。
* 2. 节点过滤,重命名,排序等。
* 3. 订阅拆分,组合。
*/
-const $ = API("sub-store");
+const $ = API("sub-store", true);
// Constants
const SUBS_KEY = "subs";
const COLLECTIONS_KEY = "collections";
+const RESOURCE_CACHE_KEY = "resources";
// SOME INITIALIZATIONS
if (!$.read(SUBS_KEY)) $.write({}, SUBS_KEY);
if (!$.read(COLLECTIONS_KEY)) $.write({}, COLLECTIONS_KEY);
+if (!$.read(RESOURCE_CACHE_KEY)) $.write({}, RESOURCE_CACHE_KEY);
// BACKEND API
const $app = express();
+// download
+$app.get("/download/:name", downloadSub);
+$app.get("/download/collection/:name", downloadCollection);
+
+// refresh
+$app.post("/api/refresh", refreshResource);
+
+// subscriptions
$app.route("/api/sub/:name")
.get(getSub)
.patch(updateSub)
@@ -30,11 +40,7 @@ $app.route("/api/sub")
.post(newSub)
.delete(deleteAllSubs);
-// subscriptions
-$app.get("/download/:name", downloadSub);
-
// collections
-$app.get("/download/collection/:name", downloadCollection);
$app.route("/api/collection/:name")
.get(getCollection)
.patch(updateCollection)
@@ -57,6 +63,7 @@ const DEFAULT_SUPPORTED_PLATFORMS = {
Surge: true,
Raw: true
}
+
const AVAILABLE_FILTERS = {
"Keyword Filter": KeywordFilter,
"Discard Keyword Filter": DiscardKeywordFilter,
@@ -81,6 +88,22 @@ const AVAILABLE_OPERATORS = {
}
/**************************** API -- Subscriptions ***************************************/
+// refresh resource
+async function refreshResource(req, res) {
+ const {url} = req.body;
+ const cachedResources = $.read(RESOURCE_CACHE_KEY);
+ cachedResources[url] = await $.http.get(url).then(resp => resp => resp.body).catch(err => {
+ res.status(500).json({
+ status: "failed",
+ message: `Cannot refresh remote resource: ${url}\n Reason: ${err}`
+ });
+ });
+ $.write(cachedResources, cachedResources);
+ res.json({
+ status: "success"
+ });
+}
+
// download subscription, for APP only
async function downloadSub(req, res) {
const {name} = req.params;
@@ -92,7 +115,6 @@ async function downloadSub(req, res) {
const output = await parseSub(sub, platform);
res.send(output);
} catch (err) {
- $.notify('[Sub-Store]', '❌ 无法获取订阅!', `错误信息:${err}`)
res.status(500).json({
status: "failed",
message: err
@@ -107,12 +129,23 @@ async function downloadSub(req, res) {
}
async function parseSub(sub, platform) {
- // download from url
- const raw = await $.http.get(sub.url).then(resp => resp.body).catch(err => {
+ let raw;
+ const cachedResources = $.read(RESOURCE_CACHE_KEY);
+ if (platform === "Raw") {
+ // use cache if available
+ raw = cachedResources[sub.url];
+ }
+
+ // always download from url
+ raw = raw || await $.http.get(sub.url).then(resp => resp.body).catch(err => {
throw new Error(err);
});
- console.log("=======================================================================");
- console.log(`Processing subscription: ${sub.name}, target platform ==> ${platform}.`);
+ cachedResources[sub.url] = raw;
+
+ $.write(cachedResources, RESOURCE_CACHE_KEY);
+
+ $.log("=======================================================================");
+ $.log(`Processing subscription: ${sub.name}, target platform ==> ${platform}.`);
const $parser = ProxyParser(platform);
let proxies = $parser.parse(raw);
@@ -136,14 +169,14 @@ async function parseSub(sub, platform) {
if (filter) {
$filter.addFilters(filter(...(item.args || [])));
proxies = $filter.process(proxies);
- console.log(`Applying filter "${item.type}" with arguments:\n >>> ${item.args || "None"}`);
+ $.log(`Applying filter "${item.type}" with arguments:\n >>> ${item.args || "None"}`);
}
} else if (item.type.indexOf("Operator") !== -1) {
const operator = AVAILABLE_OPERATORS[item.type];
if (operator) {
$operator.addOperators(operator(...(item.args || [])));
proxies = $operator.process(proxies);
- console.log(`Applying operator "${item.type}" with arguments: \n >>> ${item.args || "None"}`);
+ $.log(`Applying operator "${item.type}" with arguments: \n >>> ${item.args || "None"}`);
}
}
}
@@ -470,17 +503,16 @@ function ProxyParser(targetPlatform) {
// https://github.com/KOP-XIAO/QuantumultX
if (raw.indexOf("{") !== -1) {
raw = raw.replace(/: {/g, ": {, ")
- .replace(/, (host|path|tls|mux|skip)/g,", $1")
- .replace(/{name: /g,"{name: \"")
- .replace(/, server:/g,"\", server:")
- .replace(/{|}/g,"")
- .replace(/,/g,"\n ")
+ .replace(/, (host|path|tls|mux|skip)/g, ", $1")
+ .replace(/{name: /g, "{name: \"")
+ .replace(/, server:/g, "\", server:")
+ .replace(/{|}/g, "")
+ .replace(/,/g, "\n ")
}
- raw = raw.replace(/ -\n.*name/g," - name");
+ raw = raw.replace(/ -\n.*name/g, " - name");
const proxies = YAML.eval(raw).proxies;
output = proxies.map(p => JSON.stringify(p));
- }
- else {
+ } else {
// check if content is based64 encoded
const Base64 = new Base64Code();
const keys = ["dm1lc3M", "c3NyOi8v", "dHJvamFu", "c3M6Ly", "c3NkOi8v"];
@@ -1283,7 +1315,6 @@ function QX_Producer() {
}
function Loon_Producer() {
- $.notify("LOON")
const targetPlatform = "Loon";
const output = (proxy) => {
let obfs_opts, tls_opts;
@@ -2083,22 +2114,6 @@ function API(name = "untitled", debug = false) {
error(msg) {
console.log("ERROR: " + msg);
}
-
- wait(millisec) {
- return new Promise((resolve) => setTimeout(resolve, millisec));
- }
-
- done(value = {}) {
- if (isQX || isLoon || isSurge) {
- $done(value);
- } else if (isNode && !isJSBox) {
- if (typeof $context !== "undefined") {
- $context.headers = value.headers;
- $context.statusCode = value.statusCode;
- $context.body = value.body;
- }
- }
- }
})(name, debug);
}
@@ -2129,7 +2144,7 @@ function express(port = 3000) {
// adapter
app.start = () => {
app.listen(port, () => {
- console.log(`Express started on port: ${port}`);
+ $.log(`Express started on port: ${port}`);
})
}
return app;
diff --git a/web/public/index.html b/web/public/index.html
index bc51465..0324028 100644
--- a/web/public/index.html
+++ b/web/public/index.html
@@ -1,19 +1,28 @@
-
+
+
+
+
+
+
+
+
<%= htmlWebpackPlugin.options.title %>
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/web/src/App.vue b/web/src/App.vue
index 4317d12..7aa38e7 100644
--- a/web/src/App.vue
+++ b/web/src/App.vue
@@ -31,6 +31,7 @@
import TopToolbar from "@/components/TopToolbar";
import BottomNav from "@/components/BottomNav";
+
function initStore(store) {
store.dispatch('FETCH_SUBSCRIPTIONS');
store.dispatch("FETCH_COLLECTIONS");
@@ -72,4 +73,8 @@ export default {
}
}
}
-
\ No newline at end of file
+
+
+
\ No newline at end of file
diff --git a/web/src/config.js b/web/src/config.js
index e19e5c9..6cf5a03 100644
--- a/web/src/config.js
+++ b/web/src/config.js
@@ -1,3 +1,3 @@
const DEBUG = true;
-export const BACKEND_BASE = DEBUG ? `http://127.0.0.1:3000` : `http://sub.store`;
\ No newline at end of file
+export const BACKEND_BASE = DEBUG ? `http://192.168.1.134:3000` : `http://sub.store`;
\ No newline at end of file