mirror of
https://git.mirrors.martin98.com/https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-14 10:45:52 +08:00
支持配置Gist同步
This commit is contained in:
parent
d30ece21ae
commit
3b57b895e2
@ -17,6 +17,7 @@ $.http = HTTP({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
// Constants
|
// Constants
|
||||||
|
const SETTINGS_KEY = "settings";
|
||||||
const SUBS_KEY = "subs";
|
const SUBS_KEY = "subs";
|
||||||
const COLLECTIONS_KEY = "collections";
|
const COLLECTIONS_KEY = "collections";
|
||||||
const AVAILABLE_FILTERS = {
|
const AVAILABLE_FILTERS = {
|
||||||
@ -43,6 +44,7 @@ const AVAILABLE_OPERATORS = {
|
|||||||
// SOME INITIALIZATIONS
|
// SOME INITIALIZATIONS
|
||||||
if (!$.read(SUBS_KEY)) $.write({}, SUBS_KEY);
|
if (!$.read(SUBS_KEY)) $.write({}, SUBS_KEY);
|
||||||
if (!$.read(COLLECTIONS_KEY)) $.write({}, COLLECTIONS_KEY);
|
if (!$.read(COLLECTIONS_KEY)) $.write({}, COLLECTIONS_KEY);
|
||||||
|
if (!$.read(SETTINGS_KEY)) $.write({}, SETTINGS_KEY);
|
||||||
|
|
||||||
// BACKEND API
|
// BACKEND API
|
||||||
$.info("Initializing Express...");
|
$.info("Initializing Express...");
|
||||||
@ -56,7 +58,6 @@ $app.get("/api/IP_API/:server", IP_API);
|
|||||||
|
|
||||||
// subscriptions
|
// subscriptions
|
||||||
$app.route("/api/sub/:name").get(getSub).patch(updateSub).delete(deleteSub);
|
$app.route("/api/sub/:name").get(getSub).patch(updateSub).delete(deleteSub);
|
||||||
|
|
||||||
$app.route("/api/sub").get(getAllSubs).post(newSub).delete(deleteAllSubs);
|
$app.route("/api/sub").get(getAllSubs).post(newSub).delete(deleteAllSubs);
|
||||||
|
|
||||||
// refresh
|
// refresh
|
||||||
@ -75,6 +76,14 @@ $app
|
|||||||
.post(newCollection)
|
.post(newCollection)
|
||||||
.delete(deleteAllCollections);
|
.delete(deleteAllCollections);
|
||||||
|
|
||||||
|
// settings
|
||||||
|
$app.route("/api/settings")
|
||||||
|
.get(getSettings)
|
||||||
|
.patch(updateSettings);
|
||||||
|
|
||||||
|
// backup
|
||||||
|
$app.get("/api/backup", gistBackup);
|
||||||
|
|
||||||
$app.all("/", async (req, res) => {
|
$app.all("/", async (req, res) => {
|
||||||
res.send("Hello from Sub-Store! Made with ❤️ by Peng-YM.");
|
res.send("Hello from Sub-Store! Made with ❤️ by Peng-YM.");
|
||||||
});
|
});
|
||||||
@ -93,13 +102,7 @@ async function IP_API(req, res) {
|
|||||||
async function downloadResource(url) {
|
async function downloadResource(url) {
|
||||||
let raw = await $.http
|
let raw = await $.http
|
||||||
.get(url)
|
.get(url)
|
||||||
.then((resp) => resp.body)
|
.then((resp) => resp.body);
|
||||||
.catch((err) => {
|
|
||||||
res.status(500).json({
|
|
||||||
status: "failed",
|
|
||||||
message: `Cannot refresh remote resource: ${url}\n Reason: ${err}`,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
// trim Clash config to save memory
|
// trim Clash config to save memory
|
||||||
const start = raw.indexOf("proxies:");
|
const start = raw.indexOf("proxies:");
|
||||||
if (start !== -1) {
|
if (start !== -1) {
|
||||||
@ -109,6 +112,61 @@ async function downloadResource(url) {
|
|||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function gistBackup(req, res) {
|
||||||
|
const {action} = req.query;
|
||||||
|
// read token
|
||||||
|
const { gistToken } = $.read(SETTINGS_KEY);
|
||||||
|
if (!gistToken) {
|
||||||
|
res.status(500).json({
|
||||||
|
status: "failed",
|
||||||
|
message: "未找到Gist备份Token!"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const gist = new Gist("Auto Generated Sub-Store Backup", gistToken);
|
||||||
|
try{
|
||||||
|
let content;
|
||||||
|
switch (action) {
|
||||||
|
case "upload":
|
||||||
|
content = $.read("#sub-store");
|
||||||
|
await gist.upload(JSON.stringify(content));
|
||||||
|
break;
|
||||||
|
case "download":
|
||||||
|
content = await gist.download();
|
||||||
|
// restore settings
|
||||||
|
$.write(content,"#sub-store");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
res.json({
|
||||||
|
status: "success",
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
res.status(500).json({
|
||||||
|
status: "failed",
|
||||||
|
message: `${action === "upload" ? "上传" : "下载"}备份失败!${err}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// settings
|
||||||
|
async function getSettings(req, res) {
|
||||||
|
const settings = $.read(SETTINGS_KEY);
|
||||||
|
res.json(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateSettings(req, res) {
|
||||||
|
const data = req.body;
|
||||||
|
const settings = $.read(SETTINGS_KEY);
|
||||||
|
$.write({
|
||||||
|
...settings,
|
||||||
|
...data
|
||||||
|
}, SETTINGS_KEY);
|
||||||
|
res.json({
|
||||||
|
status: "success"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**************************** API -- Subscriptions ***************************************/
|
/**************************** API -- Subscriptions ***************************************/
|
||||||
// refresh resource
|
// refresh resource
|
||||||
async function refreshResource(req, res) {
|
async function refreshResource(req, res) {
|
||||||
@ -2580,7 +2638,10 @@ function API(name = "untitled", debug = false) {
|
|||||||
this.log(`SET ${key}`);
|
this.log(`SET ${key}`);
|
||||||
if (key.indexOf("#") !== -1) {
|
if (key.indexOf("#") !== -1) {
|
||||||
key = key.substr(1);
|
key = key.substr(1);
|
||||||
if (isSurge & isLoon) {
|
if (key === name) {
|
||||||
|
this.cache = JSON.parse(data);
|
||||||
|
}
|
||||||
|
if (isSurge || isLoon) {
|
||||||
$persistentStore.write(data, key);
|
$persistentStore.write(data, key);
|
||||||
}
|
}
|
||||||
if (isQX) {
|
if (isQX) {
|
||||||
@ -2599,7 +2660,8 @@ function API(name = "untitled", debug = false) {
|
|||||||
this.log(`READ ${key}`);
|
this.log(`READ ${key}`);
|
||||||
if (key.indexOf("#") !== -1) {
|
if (key.indexOf("#") !== -1) {
|
||||||
key = key.substr(1);
|
key = key.substr(1);
|
||||||
if (isSurge & isLoon) {
|
if (key === name) return this.cache;
|
||||||
|
if (isSurge || isLoon) {
|
||||||
return $persistentStore.read(key);
|
return $persistentStore.read(key);
|
||||||
}
|
}
|
||||||
if (isQX) {
|
if (isQX) {
|
||||||
@ -2617,7 +2679,7 @@ function API(name = "untitled", debug = false) {
|
|||||||
this.log(`DELETE ${key}`);
|
this.log(`DELETE ${key}`);
|
||||||
if (key.indexOf("#") !== -1) {
|
if (key.indexOf("#") !== -1) {
|
||||||
key = key.substr(1);
|
key = key.substr(1);
|
||||||
if (isSurge & isLoon) {
|
if (isSurge || isLoon) {
|
||||||
$persistentStore.write(null, key);
|
$persistentStore.write(null, key);
|
||||||
}
|
}
|
||||||
if (isQX) {
|
if (isQX) {
|
||||||
@ -2962,6 +3024,80 @@ function express(port = 3000) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Gist(backupKey, token) {
|
||||||
|
const FILE_NAME = "Sub-Store";
|
||||||
|
const http = HTTP({
|
||||||
|
baseURL: "https://api.github.com",
|
||||||
|
headers: {
|
||||||
|
Authorization: `token ${token}`,
|
||||||
|
"User-Agent":
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.141 Safari/537.36",
|
||||||
|
},
|
||||||
|
events: {
|
||||||
|
onResponse: (resp) => {
|
||||||
|
if (String(resp.statusCode).startsWith("4")) {
|
||||||
|
return Promise.reject(`ERROR: ${JSON.parse(resp.body).message}`);
|
||||||
|
} else {
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
async function locate() {
|
||||||
|
return http.get("/gists").then((response) => {
|
||||||
|
const gists = JSON.parse(response.body);
|
||||||
|
for (let g of gists) {
|
||||||
|
if (g.description === backupKey) {
|
||||||
|
return g.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.upload = async function(content) {
|
||||||
|
const id = await locate();
|
||||||
|
const files = {
|
||||||
|
[FILE_NAME]: { content }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (id === -1) {
|
||||||
|
// create a new gist for backup
|
||||||
|
return http.post({
|
||||||
|
url: "/gists",
|
||||||
|
body: JSON.stringify({
|
||||||
|
description: backupKey,
|
||||||
|
public: false,
|
||||||
|
files
|
||||||
|
})
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// update an existing gist
|
||||||
|
return http.patch({
|
||||||
|
url: `/gists/${id}`,
|
||||||
|
body: JSON.stringify({ files })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.download = async function() {
|
||||||
|
const id = await locate();
|
||||||
|
if (id === -1) {
|
||||||
|
return Promise.reject("未找到Gist备份!");
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const { files } = await http
|
||||||
|
.get(`/gists/${id}`)
|
||||||
|
.then(resp => JSON.parse(resp.body));
|
||||||
|
const url = files[FILE_NAME].raw_url;
|
||||||
|
return await HTTP().get(url).then(resp => resp.body);
|
||||||
|
} catch (err) {
|
||||||
|
return Promise.reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
/******************************** Base 64 *********************************************/
|
/******************************** Base 64 *********************************************/
|
||||||
// Base64 Coding Library
|
// Base64 Coding Library
|
||||||
// https://github.com/dankogai/js-base64#readme
|
// https://github.com/dankogai/js-base64#readme
|
||||||
@ -3121,7 +3257,6 @@ Author: Diogo Costa
|
|||||||
|
|
||||||
This program is released under the MIT License
|
This program is released under the MIT License
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var YAML = (function () {
|
var YAML = (function () {
|
||||||
var errors = [],
|
var errors = [],
|
||||||
reference_blocks = [],
|
reference_blocks = [],
|
||||||
|
@ -125,10 +125,12 @@ export default {
|
|||||||
async fetch() {
|
async fetch() {
|
||||||
await axios.get(this.url).then(resp => {
|
await axios.get(this.url).then(resp => {
|
||||||
let {data} = resp;
|
let {data} = resp;
|
||||||
if (data instanceof String && data.indexOf("\n") !== -1)
|
if ((typeof data === 'string' || data instanceof String) && data.indexOf("\n") !== -1){
|
||||||
this.proxies = data.split("\n").map(p => JSON.parse(p));
|
this.proxies = data.split("\n").map(p => JSON.parse(p));
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
this.proxies = [data];
|
this.proxies = [data];
|
||||||
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
this.$store.commit("SET_ERROR_MESSAGE", err);
|
this.$store.commit("SET_ERROR_MESSAGE", err);
|
||||||
});
|
});
|
||||||
|
@ -1,11 +1,64 @@
|
|||||||
<template>
|
<template>
|
||||||
<v-container>
|
<v-card
|
||||||
</v-container>
|
class="mb-4 ml-4 mr-4 mt-4"
|
||||||
|
>
|
||||||
|
<v-card-title>
|
||||||
|
设置
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-icon small>settings</v-icon>
|
||||||
|
</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<v-text-field
|
||||||
|
label="GitHub Token"
|
||||||
|
hint="填入GitHub Token"
|
||||||
|
:value="settings.gistToken"
|
||||||
|
clearable clear-icon="clear"
|
||||||
|
/>
|
||||||
|
</v-card-text>
|
||||||
|
<v-divider></v-divider>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer></v-spacer>
|
||||||
|
<v-btn label @click="sync('upload')">上传</v-btn>
|
||||||
|
<v-btn label @click="sync('download')">下载</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
<v-divider/>
|
||||||
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import {axios} from "@/utils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "User"
|
data() {
|
||||||
|
return {
|
||||||
|
settings: {
|
||||||
|
gistToken: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
axios.get(`/settings`).then(resp => {
|
||||||
|
this.settings = resp.data;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
save() {
|
||||||
|
axios.patch(`/settings`, this.settings);
|
||||||
|
},
|
||||||
|
|
||||||
|
sync(action) {
|
||||||
|
if (!this.settings.gistToken) {
|
||||||
|
this.$store.commit("SET_ERROR_MESSAGE", "未设置GitHub Token!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.save();
|
||||||
|
axios.get(`/backup?action=${action}`).then(() => {
|
||||||
|
this.$store.commit("SET_SUCCESS_MESSAGE", `${action === 'upload' ? "备份" : "还原"}成功!`);
|
||||||
|
}).catch(err => {
|
||||||
|
this.$store.commit("SET_ERROR_MESSAGE", `备份失败!${err}`);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user