mirror of
https://github.com/sub-store-org/Sub-Store.git
synced 2025-08-10 00:52:40 +00:00
Alpha test
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
const DEBUG = true;
|
||||
const DEBUG = false;
|
||||
|
||||
export const BACKEND_BASE = DEBUG ? `http://192.168.1.134:3000` : `http://sub.store`;
|
||||
export const BACKEND_BASE = DEBUG ? `http://192.168.1.134:3000` : `https://sub.store`;
|
||||
@@ -48,7 +48,7 @@ const router = new Router({
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const {meta} = to;
|
||||
document.title = to.meta.title
|
||||
// document.title = to.meta.title
|
||||
store.commit("SET_NAV_TITLE", meta.title);
|
||||
next();
|
||||
})
|
||||
|
||||
@@ -39,53 +39,55 @@ const store = new Vuex.Store({
|
||||
actions: {
|
||||
// fetch subscriptions
|
||||
async FETCH_SUBSCRIPTIONS({state}) {
|
||||
axios.get("/sub").then(resp => {
|
||||
return axios.get("/sub").then(resp => {
|
||||
const {data} = resp.data;
|
||||
state.subscriptions = data;
|
||||
});
|
||||
},
|
||||
// fetch collections
|
||||
async FETCH_COLLECTIONS({state}) {
|
||||
axios.get("/collection").then(resp => {
|
||||
return axios.get("/collection").then(resp => {
|
||||
const {data} = resp.data;
|
||||
state.collections = data;
|
||||
});
|
||||
},
|
||||
// update subscriptions
|
||||
async UPDATE_SUBSCRIPTION({commit, dispatch}, {name, sub}) {
|
||||
axios.patch(`/sub/${name}`, sub).then(() => {
|
||||
async UPDATE_SUBSCRIPTION({dispatch}, {name, sub}) {
|
||||
return axios.patch(`/sub/${name}`, sub).then(() => {
|
||||
dispatch("FETCH_SUBSCRIPTIONS");
|
||||
dispatch("FETCH_COLLECTIONS");
|
||||
commit("SET_SUCCESS_MESSAGE", `成功更新订阅${sub.name || name}`);
|
||||
}).catch(err => {
|
||||
commit("SET_ERROR_MESSAGE", err);
|
||||
});
|
||||
},
|
||||
// new subscription
|
||||
async NEW_SUBSCRIPTION() {
|
||||
|
||||
async NEW_SUBSCRIPTION({dispatch}, sub) {
|
||||
return axios.post(`/sub`, sub).then(() => {
|
||||
dispatch("FETCH_SUBSCRIPTIONS");
|
||||
});
|
||||
},
|
||||
// delete subscription
|
||||
async DELETE_SUBSCRIPTION({commit, dispatch}, name) {
|
||||
axios.delete(`/sub/${name}`).then(() => {
|
||||
async DELETE_SUBSCRIPTION({dispatch}, name) {
|
||||
return axios.delete(`/sub/${name}`).then(() => {
|
||||
dispatch("FETCH_SUBSCRIPTIONS");
|
||||
dispatch("FETCH_COLLECTIONS");
|
||||
commit("SET_SUCCESS_MESSAGE", `成功删除订阅${name}`);
|
||||
}).catch(err => {
|
||||
commit("SET_ERROR_MESSAGE", err);
|
||||
})
|
||||
});
|
||||
},
|
||||
// update collection
|
||||
async UPDATE_COLLECTION() {
|
||||
|
||||
async UPDATE_COLLECTION({dispatch}, {name, collection}) {
|
||||
return axios.patch(`/collection/${name}`, collection).then(() => {
|
||||
dispatch("FETCH_COLLECTIONS");
|
||||
});
|
||||
},
|
||||
// new collection
|
||||
async NEW_COLLECTION() {
|
||||
|
||||
async NEW_COLLECTION({dispatch}, collection) {
|
||||
return axios.post(`/collection`, collection).then(() => {
|
||||
dispatch("FETCH_COLLECTIONS");
|
||||
})
|
||||
},
|
||||
// delete collection
|
||||
async DELETE_COLLECTION() {
|
||||
|
||||
async DELETE_COLLECTION({dispatch}, name) {
|
||||
return axios.delete(`/collection/${name}`).then(() => {
|
||||
dispatch("FETCH_COLLECTIONS");
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Axios from 'axios';
|
||||
import store from "@/store";
|
||||
import {BACKEND_BASE} from "@/config";
|
||||
|
||||
export const axios = Axios.create({
|
||||
@@ -8,4 +9,12 @@ export const axios = Axios.create({
|
||||
|
||||
export function isEmptyObj(obj) {
|
||||
return Object.keys(obj).length === 0;
|
||||
}
|
||||
|
||||
export function showInfo(msg) {
|
||||
store.commit("SET_SUCCESS_MESSAGE", msg);
|
||||
}
|
||||
|
||||
export function showError(err) {
|
||||
store.commit("SET_ERROR_MESSAGE", err);
|
||||
}
|
||||
@@ -1,10 +1,107 @@
|
||||
<template>
|
||||
<v-container></v-container>
|
||||
<v-container>
|
||||
<v-card class="mb-4">
|
||||
<v-card-title>订阅配置</v-card-title>
|
||||
<v-form class="pl-4 pr-4 pb-4" v-model="valid">
|
||||
<v-subheader class="pl-0">订阅名称</v-subheader>
|
||||
<v-text-field
|
||||
v-model="name"
|
||||
class="mt-2"
|
||||
:rules="validations.nameRules"
|
||||
required
|
||||
placeholder="填入订阅名称,名称需唯一"
|
||||
/>
|
||||
<v-divider></v-divider>
|
||||
<v-subheader class="pl-0">包含的订阅</v-subheader>
|
||||
<v-list dense>
|
||||
<v-list-item v-for="sub in availableSubs" :key="sub.name">
|
||||
<v-list-item-avatar dark>
|
||||
<v-icon>mdi-cloud</v-icon>
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
{{ sub.name }}
|
||||
</v-list-item-content>
|
||||
<v-spacer></v-spacer>
|
||||
<v-checkbox
|
||||
:value="sub.name"
|
||||
v-model="selected"
|
||||
class="pr-1"
|
||||
/>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-form>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="save"><v-icon>save_alt</v-icon></v-btn>
|
||||
<v-btn icon @click="discard"><v-icon>settings_backup_restore</v-icon></v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {showInfo, showError} from "@/utils";
|
||||
|
||||
export default {
|
||||
name: "CollectionEditor"
|
||||
data: function () {
|
||||
return {
|
||||
valid: false,
|
||||
validations: {
|
||||
nameRules: [
|
||||
v => !!v || "名字不能为空",
|
||||
v => /^[\w-_]*$/.test(v) || "订阅名称只能包含英文字符、横杠和下划线!"
|
||||
]
|
||||
},
|
||||
selected: [],
|
||||
name: ""
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
availableSubs() {
|
||||
return this.$store.state.subscriptions;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
save() {
|
||||
if (!this.valid || this.selected.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (this.$route.params.name === 'UNTITLED') {
|
||||
this.$store.dispatch("NEW_COLLECTION", {
|
||||
name: this.name,
|
||||
subscriptions: this.selected
|
||||
}).then(() => {
|
||||
showInfo(`成功创建订阅:${this.name}!`)
|
||||
}).catch(() => {
|
||||
showError(`发生错误,无法创建订阅!`)
|
||||
});
|
||||
} else {
|
||||
this.$store.dispatch("UPDATE_COLLECTION", {
|
||||
name: this.$route.params.name,
|
||||
collection: {
|
||||
name: this.name,
|
||||
subscriptions: this.selected
|
||||
}
|
||||
}).then(() => {
|
||||
showInfo(`成功保存订阅:${this.name}!`)
|
||||
}).catch(() => {
|
||||
showError(`发生错误,无法保存订阅!`)
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
discard() {
|
||||
this.$router.back();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const name = this.$route.params.name;
|
||||
const collection = this.$store.state.collections[name] || {};
|
||||
this.$store.commit("SET_NAV_TITLE", collection.name ? `组合订阅编辑 -- ${collection.name}` : "新建组合订阅");
|
||||
this.name = collection.name;
|
||||
this.selected = collection.subscriptions || [];
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<v-container>
|
||||
<v-card class="mb-4">
|
||||
<v-subheader>基本信息</v-subheader>
|
||||
<v-form class="pl-4 pr-4 pb-4" v-model="formState.basicValid">
|
||||
<v-form class="pl-4 pr-4 pb-0" v-model="formState.basicValid">
|
||||
<v-text-field
|
||||
v-model="options.name"
|
||||
class="mt-2"
|
||||
@@ -11,20 +11,41 @@
|
||||
label="订阅名称"
|
||||
placeholder="填入订阅名称,名称需唯一"
|
||||
/>
|
||||
<v-text-field
|
||||
<v-textarea
|
||||
v-model="options.url"
|
||||
class="mt-2"
|
||||
rows="2"
|
||||
:rules="validations.urlRules"
|
||||
required
|
||||
label="订阅链接"
|
||||
placeholder="填入机场原始订阅链接"
|
||||
/>
|
||||
</v-form>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn icon @click="save"><v-icon>save_alt</v-icon></v-btn>
|
||||
<v-btn icon @click="discard"><v-icon>settings_backup_restore</v-icon></v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
<v-card class="mb-4">
|
||||
<v-subheader>常用选项</v-subheader>
|
||||
<v-form class="pl-4 pr-4">
|
||||
<v-item-group>
|
||||
<v-radio-group
|
||||
v-model="options.useless"
|
||||
dense
|
||||
class="mt-0 mb-0"
|
||||
>
|
||||
过滤非法节点
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-radio label="保留" value="KEEP"/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-radio label="删除" value="REMOVE"/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-radio-group>
|
||||
<v-radio-group
|
||||
v-model="options.flag"
|
||||
dense
|
||||
@@ -32,13 +53,15 @@
|
||||
>
|
||||
国旗
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-radio label="默认" value="DEFAULT"/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-radio label="添加国旗" value="ADD"/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-radio label="删除国旗" value="REMOVE"/>
|
||||
</v-col>
|
||||
<v-col></v-col>
|
||||
</v-row>
|
||||
</v-radio-group>
|
||||
|
||||
@@ -66,16 +89,16 @@
|
||||
dense
|
||||
class="mt-0 mb-0"
|
||||
>
|
||||
证书验证
|
||||
跳过证书验证
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-radio label="默认" value="DEFAULT"/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-radio label="强制开启" value="FORCE_OPEN"/>
|
||||
<v-radio label="强制跳过" value="FORCE_OPEN"/>
|
||||
</v-col>
|
||||
<v-col>
|
||||
<v-radio label="强制关闭" value="FORCE_CLOSE"/>
|
||||
<v-radio label="强制验证" value="FORCE_CLOSE"/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-radio-group>
|
||||
@@ -109,31 +132,12 @@
|
||||
</v-btn>
|
||||
</v-subheader>
|
||||
<v-divider></v-divider>
|
||||
<region-filter/>
|
||||
<keyword-filter/>
|
||||
<regex-filter/>
|
||||
<sort></sort>
|
||||
<keyword-sort></keyword-sort>
|
||||
</v-card>
|
||||
</v-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// const operations = [
|
||||
// {
|
||||
// type: "Region Filter",
|
||||
// name: "区域过滤",
|
||||
// desc: "按照区域过滤节点",
|
||||
// }
|
||||
// ];
|
||||
|
||||
import RegionFilter from "@/components/RegionFilter";
|
||||
import KeywordFilter from "@/components/KeywordFilter";
|
||||
import RegexFilter from "@/components/RegexFilter";
|
||||
import Sort from "@/components/Sort";
|
||||
import KeywordSort from "@/components/KeywordSort";
|
||||
export default {
|
||||
components: {KeywordSort, Sort, RegexFilter, KeywordFilter, RegionFilter},
|
||||
data: function () {
|
||||
return {
|
||||
validations: {
|
||||
@@ -152,39 +156,98 @@ export default {
|
||||
options: {
|
||||
name: "",
|
||||
url: "",
|
||||
useless: "KEEP",
|
||||
udp: "DEFAULT",
|
||||
flag: "ADD",
|
||||
flag: "DEFAULT",
|
||||
scert: "DEFAULT",
|
||||
tfo: "DEFAULT",
|
||||
process: [],
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
options: {
|
||||
handler(opt) {
|
||||
if (this.formState.basicValid) {
|
||||
console.log(`FORM UPDATED: ${JSON.stringify(opt)}`)
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const name = this.$route.params.name;
|
||||
const sub = (!!name || name === 'UNTITLED') ? {} : this.$store.state.subscriptions[name];
|
||||
const sub = (typeof name === 'undefined' || name === 'UNTITLED') ? {} : this.$store.state.subscriptions[name];
|
||||
this.$store.commit("SET_NAV_TITLE", sub.name ? `订阅编辑 -- ${sub.name}` : "新建订阅");
|
||||
this.options = {
|
||||
...this.options,
|
||||
name: sub.name,
|
||||
url: sub.url,
|
||||
udp: "DEFAULT",
|
||||
flag: "ADD",
|
||||
scert: "DEFAULT",
|
||||
tfo: "DEFAULT"
|
||||
this.options = loadSubscription(this.options, sub);
|
||||
},
|
||||
methods: {
|
||||
save() {
|
||||
if (this.options.name && this.options.url) {
|
||||
const sub = buildSubscription(this.options);
|
||||
if (this.$route.params.name !== "UNTITLED") {
|
||||
this.$store.dispatch("UPDATE_SUBSCRIPTION", {
|
||||
name: this.$route.params.name,
|
||||
sub
|
||||
});
|
||||
} else {
|
||||
this.$store.dispatch("NEW_SUBSCRIPTION", sub);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
discard() {
|
||||
this.$router.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadSubscription(options, sub) {
|
||||
options = {
|
||||
...options,
|
||||
name: sub.name,
|
||||
url: sub.url
|
||||
}
|
||||
// flag
|
||||
for (const p of (sub.process || [])) {
|
||||
switch (p.type) {
|
||||
case 'Useless Filter':
|
||||
options.useless = "REMOVE";
|
||||
break
|
||||
case 'Flag Operator':
|
||||
options.flag = p.args[0] ? "ADD" : "REMOVE";
|
||||
break
|
||||
case 'Set Property Operator':
|
||||
options[p.args[0]] = p.args[1] ? "FORCE_OPEN" : "FORCE_CLOSE";
|
||||
break
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
function buildSubscription(options) {
|
||||
const sub = {
|
||||
name: options.name,
|
||||
url: options.url,
|
||||
process: []
|
||||
};
|
||||
// useless filter
|
||||
if (options.useless === 'REMOVE') {
|
||||
sub.process.push({
|
||||
type: "Useless Filter"
|
||||
});
|
||||
}
|
||||
// flag
|
||||
if (options.flag !== 'DEFAULT') {
|
||||
sub.process.push({
|
||||
type: "Flag Operator",
|
||||
args: [options.flag === 'ADD']
|
||||
});
|
||||
}
|
||||
// udp, tfo, scert
|
||||
for (const opt of ['udp', 'tfo', 'scert']) {
|
||||
if (options[opt] !== 'DEFAULT') {
|
||||
sub.process.push({
|
||||
type: "Set Property Operator",
|
||||
args: [opt, options[opt] === 'FORCE_OPEN']
|
||||
});
|
||||
}
|
||||
}
|
||||
// for (const p of options.process) {
|
||||
//
|
||||
// }
|
||||
return sub;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -120,6 +120,13 @@
|
||||
>
|
||||
<v-icon>mdi-plus</v-icon>
|
||||
</v-btn>
|
||||
<v-btn
|
||||
fab
|
||||
color="primary"
|
||||
@click="createCol"
|
||||
>
|
||||
<v-icon>create_new_folder</v-icon>
|
||||
</v-btn>
|
||||
</v-speed-dial>
|
||||
</v-fab-transition>
|
||||
<v-dialog fullscreen hide-overlay transition="dialog-bottom-transition" v-model="showProxyList" scrollable>
|
||||
@@ -209,7 +216,7 @@ export default {
|
||||
this.$store.commit("SET_SUCCESS_MESSAGE", "成功复制订阅链接");
|
||||
break
|
||||
case 'EDIT':
|
||||
this.$router.push(`/sub-edit/${collection.name}`);
|
||||
this.$router.push(`/collection-edit/${collection.name}`);
|
||||
break
|
||||
case 'DELETE':
|
||||
this.$store.dispatch("DELETE_COLLECTION", collection.name);
|
||||
|
||||
Reference in New Issue
Block a user