diff --git a/packages/shared/package.json b/packages/shared/package.json
index 02c876e2..f9fe1776 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -30,6 +30,7 @@
"dependencies": {
"@any-reader/core": "workspace:^",
"@any-reader/epub": "workspace:^",
+ "axios": "^1.7.2",
"chardet": "^2.0.0",
"fs-extra": "^11.1.1",
"iconv-lite": "^0.6.3",
diff --git a/packages/shared/rollup.config.js b/packages/shared/rollup.config.js
index dfe09732..dbb54318 100644
--- a/packages/shared/rollup.config.js
+++ b/packages/shared/rollup.config.js
@@ -25,6 +25,7 @@ const external = [
// 'lodash-es',
'lowdb',
'uuid',
+ 'axios',
]
const plugins = [
diff --git a/packages/shared/src/api.ts b/packages/shared/src/api.ts
index a9c32b68..ef704bd6 100644
--- a/packages/shared/src/api.ts
+++ b/packages/shared/src/api.ts
@@ -252,4 +252,5 @@ export function useApi(register: any, { CONFIG_PATH, bookDir }: any) {
register('post@batchUpdateRules', async (data: any) => success(await batchUpdateRules(data)))
register('post@delRules', async (data: any) => success(await delRules(data)))
register('post@updateRuleSort', async (data: any) => success(await ruleFileManager.updateRuleSort(data && data.id)))
+ register('post@importRules', async (data: any) => success(await ruleFileManager.importRules(data && data.url)))
}
diff --git a/packages/shared/src/ruleFileManager.ts b/packages/shared/src/ruleFileManager.ts
index a1433edc..bfd2eb5d 100644
--- a/packages/shared/src/ruleFileManager.ts
+++ b/packages/shared/src/ruleFileManager.ts
@@ -4,6 +4,7 @@ import { v4 as uuidV4 } from 'uuid'
import _ from 'lodash-es'
import type { Low } from 'lowdb/lib'
import { JSONFilePreset } from 'lowdb/node'
+import axios from 'axios'
import type { Rule } from '@any-reader/core'
import { BOOK_SOURCE_PATH } from './constants'
@@ -83,3 +84,33 @@ export async function updateRuleSort(ids: string[]) {
}
writeDB()
}
+
+/**
+ *
+ * @param rule
+ * @returns {boolean}
+ */
+export function isRule(rule: any): boolean {
+ if (typeof rule === 'string')
+ return rule.startsWith('eso://:')
+
+ if (typeof rule !== 'object')
+ return false
+
+ return rule.id && rule.host && rule.contentType
+}
+
+export async function importRules(url: string) {
+ const res = await axios.create().get(url).catch((e) => {
+ console.warn(e)
+ })
+ if (!res || Array.isArray(res?.data))
+ return
+
+ for (const rule of res.data) {
+ if (isRule(rule))
+ await update(rule).catch(() => {})
+ }
+
+ return res.data.length
+}
diff --git a/packages/web/electron/main.ts b/packages/web/electron/main.ts
index 2eac1321..94639729 100644
--- a/packages/web/electron/main.ts
+++ b/packages/web/electron/main.ts
@@ -6,7 +6,11 @@ app.whenReady().then(async () => {
const win = new BrowserWindow({
title: 'AnyReader',
titleBarStyle: 'hidden',
- webPreferences: { nodeIntegration: true, contextIsolation: false }
+ webPreferences: {
+ nodeIntegration: true,
+ contextIsolation: false,
+ webSecurity: false
+ }
});
api.init();
diff --git a/packages/web/src/api/index.ts b/packages/web/src/api/index.ts
index e3aff395..4a378262 100644
--- a/packages/web/src/api/index.ts
+++ b/packages/web/src/api/index.ts
@@ -187,3 +187,12 @@ export function updateRuleSort(data: any) {
data
});
}
+
+// 导入规则
+export function importRules(data: any) {
+ return request({
+ method: 'post',
+ url: 'importRules',
+ data
+ });
+}
diff --git a/packages/web/src/pages/pc/rules/ImportRules.vue b/packages/web/src/pages/pc/rules/ImportRules.vue
new file mode 100644
index 00000000..2c103080
--- /dev/null
+++ b/packages/web/src/pages/pc/rules/ImportRules.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+ 网络不佳可能导致导入不成功
+
+
+
+
+
diff --git a/packages/web/src/pages/pc/rules/index.vue b/packages/web/src/pages/pc/rules/index.vue
index 4995ec1d..87ad1cd5 100644
--- a/packages/web/src/pages/pc/rules/index.vue
+++ b/packages/web/src/pages/pc/rules/index.vue
@@ -10,13 +10,17 @@
+
-
-
-
+
+ 添加规则
+
+ 单个添加
+ 从文件导入
+ 从URL导入
- 添加规则
-
+
+
测速
一键删除超时规则
@@ -39,7 +43,12 @@
:loading="loading"
:draggable="{ type: 'handle', width: 40 }"
row-key="id"
- :pagination="true"
+ :pagination="{
+ showTotal: true,
+ showJumper: true,
+ showPageSize: true,
+ pageSizeOptions: [10, 20, 50, 100, 500]
+ }"
:row-selection="{
type: 'checkbox',
showCheckedAll: true,
@@ -68,7 +77,9 @@ import { CONTENT_TYPES } from '@/constants';
import { useRulesStore } from '@/stores/rules';
import { ping, batchUpdateRules, delRules, updateRuleSort, createRule } from '@/api';
import { timeoutWith } from '@/utils/promise';
+import { isRule } from '@/utils/rule';
import { useRuleExtra } from './hooks/useRuleExtra';
+import ImportRules from './ImportRules.vue';
const router = useRouter();
const rulesStore = useRulesStore();
@@ -76,6 +87,7 @@ const ruleExtra = useRuleExtra();
const pingIds = ref([]);
const selectedKeys = ref([]);
const loading = ref(false);
+const fileInputRef = ref();
rulesStore.sync();
ruleExtra.sync();
@@ -115,12 +127,22 @@ const tableColumns = ref([
tooltip: true,
sortable: {
sortDirections: ['ascend', 'descend']
+ },
+ render: ({ record }) => {
+ return (
+
{
+ window.open(record.host);
+ }}>
+ {record.host}
+
+ );
}
},
{
title: '延迟',
dataIndex: 'extra.ping',
- width: 100,
+ width: 90,
align: 'center',
render: ({ record }) => {
const extra = record.extra;
@@ -159,7 +181,7 @@ const tableColumns = ref([
},
{
title: '启用搜索',
- width: 120,
+ width: 100,
align: 'center',
filterable: {
filters: [
@@ -183,14 +205,11 @@ const tableColumns = ref([
})
}
/>
- ),
- sortable: {
- sortDirections: ['ascend', 'descend']
- }
+ )
},
{
title: '启用发现',
- width: 120,
+ width: 100,
align: 'center',
filterable: {
filters: [
@@ -215,10 +234,7 @@ const tableColumns = ref([
})
}
/>
- ),
- sortable: {
- sortDirections: ['ascend', 'descend']
- }
+ )
},
{
title: '操作',
@@ -257,6 +273,33 @@ function addRule() {
});
}
+function addRuleFile() {
+ fileInputRef.value.click();
+}
+
+function addRuleUrl() {
+ const modal = Modal.open({
+ draggable: true,
+ mask: true,
+ width: 400,
+ footer: false,
+ title: '导入规则',
+ content: (
+
{
+ rulesStore.sync();
+ Message.success({
+ content: `导入${count}条数据`,
+ closable: true,
+ resetOnHover: true
+ });
+ modal.close();
+ }}
+ />
+ )
+ });
+}
+
async function pingAll() {
const rows = _.chunk(tableDataFilter.value, 5);
for (const row of rows) {
@@ -322,6 +365,7 @@ async function handleChange(data, extra, currentData) {
}
async function drop(event) {
+ event.preventdefault();
const files = event.dataTransfer.files;
for (const file of files) {
await dropFile(file);
@@ -345,8 +389,6 @@ function readFile(file) {
});
}
-const isRule = (data) => data.id && data.host && data.contentType;
-
async function dropFile(file) {
let count = 0;
const rules = await readFile(file);
@@ -363,4 +405,11 @@ async function dropFile(file) {
resetOnHover: true
});
}
+
+async function changeFile(e) {
+ const files = e.target.files;
+ for (const file of files) {
+ await dropFile(file);
+ }
+}
diff --git a/packages/web/src/utils/rule.ts b/packages/web/src/utils/rule.ts
new file mode 100644
index 00000000..8bcb29bf
--- /dev/null
+++ b/packages/web/src/utils/rule.ts
@@ -0,0 +1,9 @@
+export const isRule = (rule: any) => {
+ if (typeof rule === 'string') {
+ return rule.startsWith('eso://:');
+ }
+
+ if (typeof rule !== 'object') return false;
+
+ return rule.id && rule.host && rule.contentType;
+};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f7b3bc5f..a1973702 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -207,6 +207,9 @@ importers:
'@any-reader/epub':
specifier: workspace:^
version: link:../epub
+ axios:
+ specifier: ^1.7.2
+ version: 1.7.2
chardet:
specifier: ^2.0.0
version: 2.0.0
@@ -5715,6 +5718,16 @@ packages:
- debug
dev: false
+ /axios@1.7.2:
+ resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==}
+ dependencies:
+ follow-redirects: 1.15.6
+ form-data: 4.0.0
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+ dev: false
+
/b-tween@0.3.3:
resolution: {integrity: sha512-oEHegcRpA7fAuc9KC4nktucuZn2aS8htymCPcP3qkEGPqiBH+GfqtqoG2l7LxHngg6O0HFM7hOeOYExl1Oz4ZA==}
dev: false
@@ -8653,6 +8666,16 @@ packages:
optional: true
dev: false
+ /follow-redirects@1.15.6:
+ resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+ dev: false
+
/for-each@0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
dependencies:
@@ -8889,6 +8912,7 @@ packages:
/glob@7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ deprecated: Glob versions prior to v9 are no longer supported
dependencies:
fs.realpath: 1.0.0
inflight: 1.0.6
@@ -11784,6 +11808,7 @@ packages:
/rimraf@3.0.2:
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+ deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
dependencies:
glob: 7.2.3