index.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. import ajax from "./axios";
  2. import { ElMessage } from "element-plus/es";
  3. /** https://github.com/gedoor/legado/tree/master/app/src/main/java/io/legado/app/api */
  4. /** https://github.com/gedoor/legado/tree/master/app/src/main/java/io/legado/app/web */
  5. /**@type string */
  6. export let legado_http_entry_point;
  7. /**@type string */
  8. export let legado_webSocket_entry_point;
  9. /**
  10. * @param {string|URL} http_url
  11. * @returns {URL}
  12. * @throws {Error}
  13. */
  14. export const validatorHttpUrl = (http_url) => {
  15. try {
  16. const url = new URL(http_url);
  17. if (url.toString() === legado_http_entry_point)
  18. throw new Error("Please input different url: " + legado_http_entry_point);
  19. const { protocol } = url;
  20. if (!protocol.startsWith("http"))
  21. throw new Error("Expect http:/https: protocol but " + protocol);
  22. return url;
  23. } catch (e) {
  24. if (localStorage.getItem("remoteUrl") == http_url) {
  25. localStorage.removeItem("remoteUrl");
  26. console.warn("Remove remoteUrl from localStorage");
  27. }
  28. throw new Error("Fail to parse Leagdo remoteUrl " + http_url, { cause: e });
  29. }
  30. };
  31. /**
  32. * @param {string|URL} http_url
  33. * @returns
  34. */
  35. export const setLeagdoHttpUrl = (http_url) => {
  36. let url = new URL(location.origin); //默认当前网址的origin部分
  37. try {
  38. url = validatorHttpUrl(http_url);
  39. } catch (e) {
  40. console.warn(e);
  41. console.info(
  42. "setLeagdoHttpUrl: FallBack to location.origin: " + location.origin,
  43. );
  44. }
  45. const { protocol, port, href } = url;
  46. // websocket服务端口 为http服务端口 + 1
  47. let legado_webSocket_port, legado_webSocket_protocol;
  48. if (port !== "") {
  49. legado_webSocket_port = String(Number(port) + 1);
  50. } else {
  51. legado_webSocket_port = protocol.startsWith("https:") ? "444" : "81";
  52. }
  53. // websocket协议是否为加密版本
  54. legado_webSocket_protocol = protocol.startsWith("https:")
  55. ? "wss://"
  56. : "ws://";
  57. ajax.defaults.baseURL = href;
  58. //持久化
  59. localStorage.setItem("remoteUrl", href);
  60. legado_http_entry_point = href;
  61. url.protocol = legado_webSocket_protocol;
  62. url.port = legado_webSocket_port;
  63. legado_webSocket_entry_point = url.href;
  64. console.info("legado_api_config:");
  65. console.table({
  66. "http API入口": legado_http_entry_point,
  67. "webSocket API入口": legado_webSocket_entry_point,
  68. });
  69. };
  70. // 手动初始化 阅读web服务地址
  71. setLeagdoHttpUrl(ajax.defaults.baseURL);
  72. /**
  73. * @param {string|URL} http_url
  74. * @returns
  75. */
  76. const testLeagdoHttpUrlConnection = async (http_url) => {
  77. const { data = {} } = await ajax.get("/getReadConfig", {
  78. baseURL: new URL(http_url).toString(),
  79. timeout: 3000,
  80. });
  81. // 返回结果应该是JSON 并有键值isSuccess
  82. try {
  83. if ("isSuccess" in data) return data.data;
  84. throw new Error("ReadConfig后端返回格式错误");
  85. } catch {
  86. throw new Error("ReadConfig后端返回格式错误");
  87. }
  88. };
  89. const isSourecEditor = /source/i.test(location.href);
  90. const APIExceptionHandler = (error) => {
  91. if (isSourecEditor) {
  92. ElMessage({
  93. message: "后端错误,检查网络或者阅读app",
  94. type: "error",
  95. });
  96. }
  97. throw error;
  98. };
  99. ajax.interceptors.response.use((response) => response, APIExceptionHandler);
  100. // 书架API
  101. // Http
  102. /** @returns {Promise<import("axios").AxiosResponse<{isSuccess: boolean, data: string, errorMsg:string}>>} */
  103. const getReadConfig = () => ajax.get("/getReadConfig", { timeout: 3000 });
  104. const saveReadConfig = (config) => ajax.post("/saveReadConfig", config);
  105. const saveBookProgress = (bookProgress) =>
  106. ajax.post("/saveBookProgress", bookProgress);
  107. const saveBookProgressWithBeacon = (bookProgress) => {
  108. if (!bookProgress) return;
  109. // 常规请求可能会被取消 使用Fetch keep-alive 或者 navigator.sendBeacon
  110. navigator.sendBeacon(
  111. new URL("/saveBookProgress", legado_http_entry_point),
  112. JSON.stringify(bookProgress),
  113. );
  114. };
  115. const getBookShelf = () => ajax.get("/getBookshelf");
  116. const getChapterList = (/** @type {string} */ bookUrl) =>
  117. ajax.get("/getChapterList?url=" + encodeURIComponent(bookUrl));
  118. const getBookContent = (
  119. /** @type {string} */ bookUrl,
  120. /** @type {number} */ chapterIndex,
  121. ) =>
  122. ajax.get(
  123. "/getBookContent?url=" +
  124. encodeURIComponent(bookUrl) +
  125. "&index=" +
  126. chapterIndex,
  127. );
  128. // webSocket
  129. const search = (
  130. /** @type {string} */ searchKey,
  131. /** @type {(data: string) => void} */ onReceive,
  132. /** @type {() => void} */ onFinish,
  133. ) => {
  134. const socket = new WebSocket(
  135. new URL("/searchBook", legado_webSocket_entry_point),
  136. );
  137. socket.onopen = () => {
  138. socket.send(`{"key":"${searchKey}"}`);
  139. };
  140. socket.onmessage = ({ data }) => onReceive(data);
  141. socket.onclose = () => {
  142. onFinish();
  143. };
  144. };
  145. const saveBook = (book) => ajax.post("/saveBook", book);
  146. const deleteBook = (book) => ajax.post("/deleteBook", book);
  147. const isBookSource = /bookSource/i.test(location.href);
  148. // 源编辑API
  149. // Http
  150. const getSources = () =>
  151. isBookSource ? ajax.get("/getBookSources") : ajax.get("/getRssSources");
  152. const saveSource = (data) =>
  153. isBookSource
  154. ? ajax.post("/saveBookSource", data)
  155. : ajax.post("/saveRssSource", data);
  156. const saveSources = (data) =>
  157. isBookSource
  158. ? ajax.post("/saveBookSources", data)
  159. : ajax.post("/saveRssSources", data);
  160. const deleteSource = (data) =>
  161. isBookSource
  162. ? ajax.post("/deleteBookSources", data)
  163. : ajax.post("/deleteRssSources", data);
  164. // webSocket
  165. const debug = (
  166. /** @type {string} */ sourceUrl,
  167. /** @type {string} */ searchKey,
  168. /** @type {(data: string) => void} */ onReceive,
  169. /** @type {() => void} */ onFinish,
  170. ) => {
  171. const url = new URL(
  172. `/${isBookSource ? "bookSource" : "rssSource"}Debug`,
  173. legado_webSocket_entry_point,
  174. );
  175. const socket = new WebSocket(url);
  176. socket.onopen = () => {
  177. socket.send(JSON.stringify({ tag: sourceUrl, key: searchKey }));
  178. };
  179. socket.onmessage = ({ data }) => onReceive(data);
  180. socket.onclose = () => {
  181. ElMessage({
  182. message: "调试已关闭!",
  183. type: "info",
  184. });
  185. onFinish();
  186. };
  187. };
  188. /**
  189. * 从阅读获取需要特定处理的书籍封面
  190. * @param {string} coverUrl
  191. */
  192. const getProxyCoverUrl = (coverUrl) => {
  193. if (coverUrl.startsWith(legado_http_entry_point)) return coverUrl;
  194. return new URL(
  195. "/cover?path=" + encodeURIComponent(coverUrl),
  196. legado_http_entry_point,
  197. ).href;
  198. };
  199. /**
  200. * 从阅读获取需要特定处理的图片
  201. * @param {string} src
  202. * @param {number|`${number}`} width
  203. */
  204. const getProxyImageUrl = (src, width) => {
  205. if (src.startsWith(legado_http_entry_point)) return src;
  206. return new URL(
  207. "/image?path=" +
  208. encodeURIComponent(src) +
  209. "&url=" +
  210. encodeURIComponent(sessionStorage.getItem("bookUrl")) +
  211. "&width=" +
  212. width,
  213. legado_http_entry_point,
  214. );
  215. };
  216. export default {
  217. getReadConfig,
  218. saveReadConfig,
  219. saveBookProgress,
  220. saveBookProgressWithBeacon,
  221. getBookShelf,
  222. getChapterList,
  223. getBookContent,
  224. search,
  225. saveBook,
  226. deleteBook,
  227. getSources,
  228. saveSources,
  229. saveSource,
  230. deleteSource,
  231. debug,
  232. getProxyCoverUrl,
  233. getProxyImageUrl,
  234. testLeagdoHttpUrlConnection,
  235. };