diff --git a/modules/web/.eslintrc-auto-import.json b/modules/web/.eslintrc-auto-import.json index 051d6ac72..cf5c92ea2 100644 --- a/modules/web/.eslintrc-auto-import.json +++ b/modules/web/.eslintrc-auto-import.json @@ -5,6 +5,7 @@ "ComputedRef": true, "EffectScope": true, "ElMessage": true, + "ElMessageBox": true, "InjectionKey": true, "PropType": true, "Ref": true, @@ -78,8 +79,6 @@ "watch": true, "watchEffect": true, "watchPostEffect": true, - "watchSyncEffect": true, - "toValue": true, - "WritableComputedRef": true + "watchSyncEffect": true } -} +} \ No newline at end of file diff --git a/modules/web/src/api/axios.js b/modules/web/src/api/axios.js index 16018213b..f3931c9ad 100644 --- a/modules/web/src/api/axios.js +++ b/modules/web/src/api/axios.js @@ -1,25 +1,10 @@ import axios from "axios"; const SECOND = 1000; -const remoteIp = ref(localStorage.getItem("remoteIp")); const ajax = axios.create({ - // baseURL: import.meta.env.VITE_API || location.origin, + baseURL: import.meta.env.VITE_API || localStorage.getItem("remoteIp") || location.origin, timeout: 120 * SECOND, }); -ajax.interceptors.request.use((config) => { - config.baseURL = baseUrl(); - return config; -}); - export default ajax; - -export const setRemoteIp = (ip) => { - remoteIp.value = ip; - localStorage.setItem("remoteIp", ip); -}; - -export const baseUrl = () => { - return remoteIp.value || location.origin; -}; diff --git a/modules/web/src/api/index.js b/modules/web/src/api/index.js index 86ff820ee..7e045998d 100644 --- a/modules/web/src/api/index.js +++ b/modules/web/src/api/index.js @@ -1,14 +1,48 @@ -import ajax, { baseUrl } from "./axios"; +import ajax from "./axios"; import { ElMessage } from "element-plus/es"; /** https://github.com/gedoor/legado/tree/master/app/src/main/java/io/legado/app/api */ /** https://github.com/gedoor/legado/tree/master/app/src/main/java/io/legado/app/web */ -const getUrl = () => { - const { hostname, port } = new URL(baseUrl()); - return `${hostname}:${Number(port) + 1}`; +let legado_http_origin +let legado_webSocket_origin + +const setLeagdoHttpUrl = (http_url) => { + let legado_webSocket_port; + const { protocol, hostname, port } = new URL(http_url); + if (!protocol.startsWith("http")) + throw new Error("unexpect protocol:" + http_url); + ajax.defaults.baseURL = http_url; + legado_http_origin = http_url; + if (port !== "") { + legado_webSocket_port = Number(port) + 1; + } else { + legado_webSocket_port = protocol.startsWith("https:") ? "444" : "81"; + } + legado_webSocket_origin = + `${protocol.startsWith("https:") ? "wss://" : "ws://"}${hostname}:${legado_webSocket_port}`; + + console.info("legado_server_config:"); + console.table({legado_http_origin, legado_webSocket_origin}); }; +// 手动初始化 阅读web服务地址 +setLeagdoHttpUrl(ajax.defaults.baseURL); + +const testLeagdoHttpUrlConnection = async (http_url) => { + const {data = {}} = await ajax.get("/getReadConfig", { + baseURL: http_url, + timeout: 3000 + }) + // 返回结果应该是JSON 并有键值isSuccess + try { + if ("isSuccess" in data) return + throw new Error("ReadConfig后端返回格式错误" ) + } catch { + throw new Error("ReadConfig后端返回格式错误" ) + } +} + const isSourecEditor = /source/i.test(location.href); const APIExceptionHandler = (error) => { if (isSourecEditor) { @@ -19,8 +53,10 @@ const APIExceptionHandler = (error) => { } throw error; }; + ajax.interceptors.response.use((response) => response, APIExceptionHandler); +// 书架API // Http const getReadConfig = () => ajax.get("/getReadConfig"); const saveReadConfig = (config) => ajax.post("/saveReadConfig", config); @@ -32,8 +68,8 @@ const saveBookProgressWithBeacon = (bookProgress) => { if (!bookProgress) return; // 常规请求可能会被取消 使用Fetch keep-alive 或者 navigator.sendBeacon navigator.sendBeacon( - `${baseUrl()}/saveBookProgress`, - JSON.stringify(bookProgress), + `${legado_http_origin}/saveBookProgress`, + JSON.stringify(bookProgress) ); }; @@ -44,22 +80,23 @@ const getChapterList = (/** @type {string} */ bookUrl) => const getBookContent = ( /** @type {string} */ bookUrl, - /** @type {number} */ chapterIndex, + /** @type {number} */ chapterIndex ) => ajax.get( "/getBookContent?url=" + encodeURIComponent(bookUrl) + "&index=" + - chapterIndex, + chapterIndex ); +// webSocket const search = ( /** @type {string} */ searchKey, /** @type {(data: string) => void} */ onReceive, - /** @type {() => void} */ onFinish, + /** @type {() => void} */ onFinish ) => { - // webSocket - const url = `ws://${getUrl()}/searchBook`; + + const url = `${legado_webSocket_origin}/searchBook`; const socket = new WebSocket(url); socket.onopen = () => { @@ -77,6 +114,7 @@ const deleteBook = (book) => ajax.post("/deleteBook", book); const isBookSource = /bookSource/i.test(location.href); +// 源编辑API // Http const getSources = () => isBookSource ? ajax.get("/getBookSources") : ajax.get("/getRssSources"); @@ -96,14 +134,15 @@ const deleteSource = (data) => ? ajax.post("/deleteBookSources", data) : ajax.post("/deleteRssSources", data); +// webSocket const debug = ( /** @type {string} */ sourceUrl, /** @type {string} */ searchKey, /** @type {(data: string) => void} */ onReceive, - /** @type {() => void} */ onFinish, + /** @type {() => void} */ onFinish ) => { - // webSocket - const url = `ws://${getUrl()}/${ + + const url = `${legado_webSocket_origin}/${ isBookSource ? "bookSource" : "rssSource" }Debug`; @@ -123,6 +162,32 @@ const debug = ( }; }; +/** + * 从阅读获取需要特定处理的书籍封面 + * @param {string} coverUrl + */ +const getProxyCoverUrl = (coverUrl) => { + if(coverUrl.startsWith(legado_http_origin)) return coverUrl + return legado_http_origin + "/cover?path=" + encodeURIComponent(coverUrl); +} +/** + * 从阅读获取需要特定处理的图片 + * @param {string} src + * @param {`{number}`} width + */ +const getProxyImageUrl = (src, width) => { + if (src.startsWith(legado_http_origin)) return src + return ( + legado_http_origin + + "/image?path=" + + encodeURIComponent(src) + + "&url=" + + encodeURIComponent(sessionStorage.getItem("bookUrl")) + + "&width=" + + width + ); +} + export default { getReadConfig, saveReadConfig, @@ -140,4 +205,11 @@ export default { saveSource, deleteSource, debug, + + getProxyCoverUrl, + getProxyImageUrl, + + testLeagdoHttpUrlConnection, + setLeagdoHttpUrl, + legado_http_origin, }; diff --git a/modules/web/src/auto-imports.d.ts b/modules/web/src/auto-imports.d.ts index 567fdd4b1..a0f15d7d0 100644 --- a/modules/web/src/auto-imports.d.ts +++ b/modules/web/src/auto-imports.d.ts @@ -1,12 +1,12 @@ /* eslint-disable */ /* prettier-ignore */ // @ts-nocheck -// noinspection JSUnusedGlobalSymbols // Generated by unplugin-auto-import export {} declare global { const EffectScope: typeof import('vue')['EffectScope'] const ElMessage: typeof import('element-plus/es')['ElMessage'] + const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate'] const computed: typeof import('vue')['computed'] const createApp: typeof import('vue')['createApp'] @@ -62,7 +62,6 @@ declare global { const toRaw: typeof import('vue')['toRaw'] const toRef: typeof import('vue')['toRef'] const toRefs: typeof import('vue')['toRefs'] - const toValue: typeof import('vue')['toValue'] const triggerRef: typeof import('vue')['triggerRef'] const unref: typeof import('vue')['unref'] const useAttrs: typeof import('vue')['useAttrs'] @@ -82,6 +81,5 @@ declare global { // for type re-export declare global { // @ts-ignore - export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue' - import('vue') + export type { Component, ComponentPublicInstance, ComputedRef, InjectionKey, PropType, Ref, VNode } from 'vue' } diff --git a/modules/web/src/components/BookItems.vue b/modules/web/src/components/BookItems.vue index c8543882c..cb503dfa8 100644 --- a/modules/web/src/components/BookItems.vue +++ b/modules/web/src/components/BookItems.vue @@ -12,6 +12,7 @@ class="cover" :src="getCover(book.coverUrl)" :key="book.coverUrl" + @error.once="proxyImage" alt="" loading="lazy" /> @@ -49,16 +50,20 @@