diff --git a/README.md b/README.md
index 7b0e8880..3faa25b1 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,7 @@ In later releases, the release configuration will only be backward compatible to
## Recent critical updates and bug fixes
+- All platforms migrated to official forward proxy
- Support replacing picture bed image links with Picgo plugin
- Fixed the issue that the release preview of the authorization code mode was invalidated
- Support publishing to Zhihu
@@ -30,21 +31,14 @@ In later releases, the release configuration will only be backward compatible to
This plugin supports almost all devices and platforms of Siyuan Note, and the specific compatibility is as follows:
- [X] Siyuan Note Client (zero configuration) is highly recommended
-- [X] Servo environment (cross-domain request proxy needs to be set)
+- [X] Servo environment (Zero configuration, cross-domain request proxy built-in)
- [X] CentSource Note Browser Servo
- [X] Siyuan Note Client Servo
- [X] Siyuan Notemaker mobile servo
-- [X] Siyuan Note docker version (need to set up cross-domain request proxy)
+- [X] Siyuan Note docker version (Zero configuration, cross-domain request proxy built-in)
-**Note: If it is a LAN servo, you need to deploy the cross-domain proxy on the LAN.**
+🎉 **All platforms have been migrated to the official forward proxy, achieving zero user configuration and supporting cross-domain request proxies by default 🎉**
-**Set up the method, clone https://github.com/terwer/node-metaweblog-api-adaptor then `pnpm install & pnpm dev`, after startup the proxy address is https://:3000/api/middleware .**
-
-**If the Internet needs to be deployed on the Internet, the cross-domain request proxy of the Internet can also be used directly: https://api.terwer.space/api/middleware**
-
-**It may be migrated to the official forward proxy to achieve zero configuration in the future, but for now you must set it yourself, you can follow the progress here.**
-
-- Progress 1: The Yuque and Notion platforms have used the built-in forward proxy and do not need to be configured.
## Platform List
diff --git a/README_zh_CN.md b/README_zh_CN.md
index 22a843b7..7e96562f 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -14,6 +14,7 @@
## 最近的关键更新与 Bug 修复
+- 所有平台迁移到官方的正向代理
- 支持使用Picgo插件的情况下替换图床图片链接
- 修复授权码模式发布预览失效问题
- 支持发布到知乎
@@ -28,21 +29,13 @@
本插件支持思源笔记几乎所有设备和平台,具体兼容情况如下:
- [X] 思源笔记客户端(零配置)强烈推荐
-- [X] 伺服环境(需要设置跨域请求代理)
+- [X] 伺服环境(零配置,跨域请求代理已内置)
- [X] 思源笔记浏览器伺服
- [X] 思源笔记客户端伺服
- [X] 思源笔记客移动端伺服
-- [X] 思源笔记docker版(需要设置跨域请求代理)
+- [X] 思源笔记docker版(零配置,跨域请求代理已内置)
-**注意:如果是局域网伺服,需要在局域网自行部署跨域代理。**
-
-**设置方法,clone https://github.com/terwer/node-metaweblog-api-adaptor 然后 `pnpm install && pnpm dev`,启动之后代理地址为:https://<局域网IP>:3000/api/middleware 。**
-
-**如果是外网需要部署在外网,外网的跨域请求代理也可以直接使用:https://api.terwer.space/api/middleware**
-
-**后续可能会迁移到官方的正向代理实现零配置,但是目前还是必须要自己设置,可在这里关注进展。**
-
-- 进度1:语雀、Notion平台已使用内置正向代理,无需配置。
+**🎉 所有平台均已迁移到官方的正向代理,实现了用户零配置,默认支持跨域请求代理 🎉**
## 平台列表
diff --git a/src/adaptors/api/base/baseBlogApi.ts b/src/adaptors/api/base/baseBlogApi.ts
index 77916782..8ca21711 100644
--- a/src/adaptors/api/base/baseBlogApi.ts
+++ b/src/adaptors/api/base/baseBlogApi.ts
@@ -24,13 +24,9 @@
*/
import { BlogApi, BlogConfig, Post } from "zhi-blog-api"
-import { SiyuanKernelApi } from "zhi-siyuan-api"
-import { CommonFetchClient } from "zhi-fetch-middleware"
import { AppInstance } from "~/src/appInstance.ts"
import { createAppLogger, ILogger } from "~/src/utils/appLogger.ts"
-import { useSiyuanApi } from "~/src/composables/useSiyuanApi.ts"
-import { JsonUtil, ObjectUtil, StrUtil } from "zhi-common"
-import { isDev } from "~/src/utils/constants.ts"
+import { useProxy } from "~/src/composables/useProxy.ts"
/**
* API授权统一封装基类
@@ -42,9 +38,7 @@ import { isDev } from "~/src/utils/constants.ts"
export class BaseBlogApi extends BlogApi {
protected logger: ILogger
protected cfg: BlogConfig
- private readonly kernelApi: SiyuanKernelApi
- private readonly commonFetchClient: CommonFetchClient
- private readonly useSiyuanProxy: boolean
+ protected readonly proxyFetch: any
/**
* 初始化API授权适配器
@@ -57,11 +51,9 @@ export class BaseBlogApi extends BlogApi {
this.cfg = cfg
this.logger = createAppLogger("base-blog-api")
- this.commonFetchClient = new CommonFetchClient(appInstance, cfg.apiUrl, cfg.middlewareUrl, isDev)
- const { kernelApi, isUseSiyuanProxy } = useSiyuanApi()
- this.kernelApi = kernelApi
- this.useSiyuanProxy = isUseSiyuanProxy()
+ const { proxyFetch } = useProxy(cfg.middlewareUrl)
+ this.proxyFetch = proxyFetch
}
public async preEditPost(post: Post, id?: string, publishCfg?: any): Promise {
@@ -72,78 +64,11 @@ export class BaseBlogApi extends BlogApi {
// ================
// private methods
// ================
- protected async readFileToBlob(url: string) {
+ public async readFileToBlob(url: string) {
const response = await this.proxyFetch(url, [], {}, "GET", "image/jpeg")
const body = response.body
const blobData = new Blob([body], { type: response.contentType })
this.logger.debug("blobData =>", blobData)
return blobData
}
-
- /**
- * 网页授权通用的请求代理
- *
- * @param url - url
- * @param headers - headers,默认是[]
- * @param params - 参数,默认是 {}
- * @param method - 方法,默认是GET
- * @param contentType - 类型,默认是 application/json
- */
- protected async proxyFetch(
- url: string,
- headers: any[] = [],
- params: any = {},
- method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" = "GET",
- contentType: string = "application/json"
- ): Promise {
- let body: any
- if (typeof params === "string" && !StrUtil.isEmptyString(params)) {
- body = params
- } else if (typeof params === "object" && !ObjectUtil.isEmptyObject(params)) {
- body = params
- }
-
- if (this.useSiyuanProxy) {
- this.logger.info("using siyuan forwardProxy")
- const apiUrl = `${this.cfg.apiUrl}${url}`
- this.logger.info("siyuan forwardProxy url =>", apiUrl)
- this.logger.info("siyuan forwardProxy fetchOptions =>", {
- headers,
- body,
- method,
- contentType,
- })
- const fetchResult = await this.kernelApi.forwardProxy(apiUrl, headers, body, method, contentType, 7000)
- this.logger.debug("siyuan forwardProxy result=>", fetchResult)
- // 后续调试可打开这个日志
- // this.logger.debug("proxyFetch resText=>", resText)
- if (contentType === "application/json") {
- const resText = fetchResult?.body
- const res = JsonUtil.safeParse(resText, {} as any)
- return res
- } else if (contentType === "text/html") {
- const resText = fetchResult?.body
- return resText
- } else {
- return fetchResult
- }
- } else {
- this.logger.info("using commonFetchClient")
- const header = headers.length > 0 ? headers[0] : {}
- let fetchOptions: any = {
- method,
- headers: {
- ...header,
- },
- }
- if (body) {
- fetchOptions.body = body
- }
- this.logger.info("commonFetchClient url =>", url)
- this.logger.info("commonFetchClient fetchOptions =>", fetchOptions)
- const res = await this.commonFetchClient.fetchCall(url, fetchOptions)
- this.logger.debug("commonFetchClient res =>", res)
- return res
- }
- }
}
diff --git a/src/adaptors/api/base/metaweblog/metaweblogBlogApiAdaptor.ts b/src/adaptors/api/base/metaweblog/metaweblogBlogApiAdaptor.ts
index eeecb4ac..fbe642c2 100644
--- a/src/adaptors/api/base/metaweblog/metaweblogBlogApiAdaptor.ts
+++ b/src/adaptors/api/base/metaweblog/metaweblogBlogApiAdaptor.ts
@@ -25,14 +25,12 @@
import { Attachment, CategoryInfo, MediaObject, Post, PostStatusEnum, UserBlog } from "zhi-blog-api"
import { createAppLogger } from "~/src/utils/appLogger.ts"
-import { CommonXmlrpcClient } from "zhi-xmlrpc-middleware"
import { MetaweblogConstants } from "~/src/adaptors/api/base/metaweblog/metaweblogConstants.ts"
import { StrUtil } from "zhi-common"
import { BrowserUtil } from "zhi-device"
import { BaseBlogApi } from "~/src/adaptors/api/base/baseBlogApi.ts"
import { MetaweblogConfig } from "~/src/adaptors/api/base/metaweblog/metaweblogConfig.ts"
-import { result } from "lodash-es"
-import { data } from "cheerio/lib/api/attributes"
+import { useProxy } from "~/src/composables/useProxy.ts"
/**
* MetaweblogBlogApi 类继承自 BaseBlogApi 类,并为 Metaweblog API 提供了额外的功能
@@ -42,7 +40,7 @@ import { data } from "cheerio/lib/api/attributes"
* @since 0.9.0
*/
class MetaweblogBlogApiAdaptor extends BaseBlogApi {
- private readonly commonXmlrpcClient: CommonXmlrpcClient
+ private readonly proxyXmlrpc: any
/**
* 初始化 metaweblog API 适配器
@@ -55,7 +53,8 @@ class MetaweblogBlogApiAdaptor extends BaseBlogApi {
this.cfg.blogid = "metaweblog"
this.logger = createAppLogger("metaweblog-api-adaptor")
- this.commonXmlrpcClient = new CommonXmlrpcClient(appInstance, cfg.apiUrl)
+ const { proxyXmlrpc } = useProxy(cfg.middlewareUrl)
+ this.proxyXmlrpc = proxyXmlrpc
}
public override async getUsersBlogs(): Promise> {
@@ -230,7 +229,7 @@ class MetaweblogBlogApiAdaptor extends BaseBlogApi {
}
protected async metaweblogCall(method: string, params: any[]) {
- return await this.commonXmlrpcClient.methodCall(method, params, this.cfg.middlewareUrl)
+ return await this.proxyXmlrpc(this.cfg.apiUrl, method, params)
}
/**
diff --git a/src/adaptors/api/yuque/yuqueApiAdaptor.ts b/src/adaptors/api/yuque/yuqueApiAdaptor.ts
index 59fd4735..19281889 100644
--- a/src/adaptors/api/yuque/yuqueApiAdaptor.ts
+++ b/src/adaptors/api/yuque/yuqueApiAdaptor.ts
@@ -297,12 +297,13 @@ class YuqueApiAdaptor extends BaseBlogApi {
}
// 打印日志
- this.logger.debug("向语雀请求数据,url =>", url)
+ const apiUrl = `${this.cfg.apiUrl}${url}`
+ this.logger.debug("向语雀请求数据,apiUrl =>", apiUrl)
this.logger.debug("向语雀请求数据,params =>", params)
// 使用兼容的fetch调用并返回统一的JSON数据
const body = ObjectUtil.isEmptyObject(params) ? "" : JSON.stringify(params)
- const resJson = await this.proxyFetch(url, [headers], body, method, contentType)
+ const resJson = await this.proxyFetch(apiUrl, [headers], body, method, contentType)
this.logger.debug("向语雀请求数据,resJson =>", resJson)
if (resJson?.status === 401) {
diff --git a/src/adaptors/web/base/baseWebApi.ts b/src/adaptors/web/base/baseWebApi.ts
index 35464965..94b5ae97 100644
--- a/src/adaptors/web/base/baseWebApi.ts
+++ b/src/adaptors/web/base/baseWebApi.ts
@@ -23,13 +23,9 @@
* questions.
*/
import { Attachment, ElectronCookie, MediaObject, Post, WebApi, WebConfig } from "zhi-blog-api"
-import { SiyuanKernelApi } from "zhi-siyuan-api"
-import { CommonFetchClient } from "zhi-fetch-middleware"
import { AppInstance } from "~/src/appInstance.ts"
import { createAppLogger, ILogger } from "~/src/utils/appLogger.ts"
-import { useSiyuanApi } from "~/src/composables/useSiyuanApi.ts"
-import { JsonUtil } from "zhi-common"
-import { isDev } from "~/src/utils/constants.ts"
+import { useProxy } from "~/src/composables/useProxy.ts"
/**
* 网页授权统一封装基类
@@ -41,9 +37,7 @@ import { isDev } from "~/src/utils/constants.ts"
class BaseWebApi extends WebApi {
protected logger: ILogger
protected cfg: WebConfig
- private readonly kernelApi: SiyuanKernelApi
- private readonly commonFetchClient: CommonFetchClient
- private readonly useSiyuanProxy: boolean
+ protected readonly proxyFetch: any
/**
* 初始化网页授权 API 适配器
@@ -56,11 +50,9 @@ class BaseWebApi extends WebApi {
this.cfg = cfg
this.logger = createAppLogger("base-web-api")
- this.commonFetchClient = new CommonFetchClient(appInstance, cfg.apiUrl, cfg.middlewareUrl, isDev)
- const { kernelApi, isUseSiyuanProxy } = useSiyuanApi()
- this.kernelApi = kernelApi
- this.useSiyuanProxy = isUseSiyuanProxy()
+ const { proxyFetch } = useProxy(cfg.middlewareUrl)
+ this.proxyFetch = proxyFetch
}
// web 适配器专有
@@ -116,77 +108,13 @@ class BaseWebApi extends WebApi {
// ================
// private methods
// ================
- protected async readFileToBlob(url: string) {
+ public async readFileToBlob(url: string) {
const response = await this.proxyFetch(url, [], {}, "GET", "image/jpeg")
const body = response.body
const blobData = new Blob([body], { type: response.contentType })
this.logger.debug("blobData =>", blobData)
return blobData
}
-
- /**
- * 网页授权通用的请求代理
- *
- * @param url - url
- * @param headers - 请求头,默认取 password 作为 cookie
- * @param params - 参数,默认是 {}
- * @param method - 方法,默认是GET
- * @param contentType - 类型,默认是 application/json
- */
- protected async proxyFetch(
- url: string,
- headers: any[] = [],
- params: any = {},
- method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" = "GET",
- contentType: string = "application/json"
- ): Promise {
- if (this.useSiyuanProxy) {
- this.logger.info("using siyuan forwardProxy")
- const fetchResult = await this.kernelApi.forwardProxy(
- url,
- headers.length > 0
- ? headers
- : [
- {
- Cookie: this.cfg.password,
- },
- ],
- params,
- method,
- contentType,
- 7000
- )
- this.logger.debug("proxyFetch result=>", fetchResult)
- // 后续调试可打开这个日志
- // this.logger.debug("proxyFetch resText=>", resText)
- if (contentType === "application/json") {
- const resText = fetchResult?.body
- const res = JsonUtil.safeParse(resText, {} as any)
- return res
- } else if (contentType === "text/html") {
- const resText = fetchResult?.body
- return resText
- } else {
- return fetchResult
- }
- } else {
- this.logger.info("using middleware proxy")
- const header = headers.length > 0 ? headers[0] : {}
- const fetchOptions = {
- method: method,
- headers: {
- "Content-Type": "application/json",
- Cookie: this.cfg.password,
- ...header,
- },
- }
- this.logger.info("commonFetchClient from proxyFetch url =>", url)
- this.logger.info("commonFetchClient from proxyFetch fetchOptions =>", fetchOptions)
- const res = await this.commonFetchClient.fetchCall(url, fetchOptions)
- this.logger.debug("commonFetchClient res from proxyFetch =>", res)
- return res
- }
- }
}
export { BaseWebApi }
diff --git a/src/components/test/CnblogsTest.vue b/src/components/test/CnblogsTest.vue
index b37e501c..184be340 100644
--- a/src/components/test/CnblogsTest.vue
+++ b/src/components/test/CnblogsTest.vue
@@ -28,7 +28,6 @@ import { AppInstance } from "~/src/appInstance.ts"
import { Utils } from "~/src/utils/utils.ts"
import { reactive, ref } from "vue"
import { fileToBuffer } from "~/src/utils/polyfillUtils.ts"
-import { SimpleXmlRpcClient } from "simple-xmlrpc"
import { MediaObject, Post } from "zhi-blog-api"
import { createAppLogger } from "~/src/utils/appLogger.ts"
import Adaptors from "~/src/adaptors"
@@ -200,6 +199,14 @@ const cnblogsHandleApi = async () => {
const result = await cnblogsApi.getUsersBlogs()
logMessage.value = JSON.stringify(result)
logger.info("cnblogs users blogs=>", result)
+
+ // const key = "metaweblog_Cnblogs"
+ // const cfg = await Adaptors.getCfg(key)
+ // const { proxyXmlrpc } = useProxy(cfg.middlewareUrl)
+ //
+ // const result = await proxyXmlrpc(cfg.apiUrl, "blogger.getUsersBlogs", ["", cfg.username, cfg.password])
+ // logMessage.value = JSON.stringify(result)
+ // logger.info("cnblogs users blogs=>", result)
break
}
case METHOD_GET_RECENT_POSTS_COUNT: {
@@ -307,17 +314,58 @@ const cnblogsHandleApi = async () => {
logger.info("mediaObject=>", mediaObject)
// 设置文件的元数据
- const metadata = {
- name: mediaObject.name,
- type: mediaObject.type,
- bits: mediaObject.bits,
- overwrite: true,
- }
- const cfg = await Adaptors.getCfg(key)
- const client = new SimpleXmlRpcClient(appInstance, cfg.apiUrl, {})
- const result = await client.methodCall("metaWeblog.newMediaObject", ["", cfg.username, cfg.password, metadata])
+ const cnblogsApiAdaptor = await Adaptors.getAdaptor(key)
+ const cnblogsApi = Utils.blogApi(appInstance, cnblogsApiAdaptor)
+ logger.info("cnblogsApi=>", cnblogsApi)
+ const result = await cnblogsApi.newMediaObject(mediaObject)
logMessage.value = JSON.stringify(result)
logger.info("cnblogs new mediaObject result=>", result)
+
+ // const key = "metaweblog_Cnblogs"
+ //
+ // const file = paramFile.value
+ // const bits = await fileToBuffer(file)
+ // const mediaObject = new MediaObject(file.name, file.type, bits)
+ // logger.info("mediaObject=>", mediaObject)
+ //
+ // // 设置文件的元数据
+ // const metadata = {
+ // name: mediaObject.name,
+ // type: mediaObject.type,
+ // bits: mediaObject.bits,
+ // overwrite: true,
+ // }
+ // const cfg = await Adaptors.getCfg(key)
+ // const client = new SimpleXmlRpcClient(appInstance, cfg.apiUrl, {})
+ // const result = await client.methodCall("metaWeblog.newMediaObject", ["", cfg.username, cfg.password, metadata])
+ // logMessage.value = JSON.stringify(result)
+ // logger.info("cnblogs new mediaObject result=>", result)
+
+ // proxy
+ // const key = "metaweblog_Cnblogs"
+ // const cfg = await Adaptors.getCfg(key)
+ // const { proxyXmlrpc } = useProxy(cfg.middlewareUrl)
+ //
+ // const file = paramFile.value
+ // const bits = await fileToBuffer(file)
+ // const mediaObject = new MediaObject(file.name, file.type, bits)
+ // logger.info("mediaObject=>", mediaObject)
+ //
+ // // 设置文件的元数据
+ // const metadata = {
+ // name: mediaObject.name,
+ // type: mediaObject.type,
+ // bits: mediaObject.bits,
+ // overwrite: true,
+ // }
+ // const result = await proxyXmlrpc(cfg.apiUrl, "metaWeblog.newMediaObject", [
+ // "",
+ // cfg.username,
+ // cfg.password,
+ // metadata,
+ // ])
+ // logMessage.value = JSON.stringify(result)
+ // logger.info("cnblogs new mediaObject result=>", result)
break
}
default:
diff --git a/src/composables/useProxy.ts b/src/composables/useProxy.ts
new file mode 100644
index 00000000..22ee1bd1
--- /dev/null
+++ b/src/composables/useProxy.ts
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2023, Terwer . All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Terwer designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Terwer in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com
+ * or visit www.terwer.space if you need additional information or have any
+ * questions.
+ */
+
+import { useSiyuanApi } from "~/src/composables/useSiyuanApi.ts"
+import { JsonUtil, ObjectUtil, StrUtil } from "zhi-common"
+import { CommonFetchClient } from "zhi-fetch-middleware"
+import { isDev } from "~/src/utils/constants.ts"
+import { AppInstance } from "~/src/appInstance.ts"
+import { createAppLogger } from "~/src/utils/appLogger.ts"
+import { Deserializer, Serializer, XmlrpcUtil } from "simple-xmlrpc"
+
+/**
+ * 用于处理代理请求的自定义 hook
+ *
+ * @param middlewareUrl - 可选,如果使用 CommonFetchClient 需要传递,否则可留空
+ * @author terwer
+ * @version 1.7.0
+ * @since 1.7.0
+ */
+const useProxy = (middlewareUrl?: string) => {
+ const logger = createAppLogger("use-proxy")
+ const { kernelApi, isUseSiyuanProxy } = useSiyuanApi()
+
+ /**
+ * 创建应用程序实例和通用的 fetch 客户端实例
+ */
+ const appInstance = new AppInstance()
+ const apiUrl = ""
+ middlewareUrl = middlewareUrl ?? "https://api.terwer.space/api/middleware"
+ const commonFetchClient = new CommonFetchClient(appInstance, apiUrl, middlewareUrl, isDev)
+ const serializer = new Serializer(appInstance)
+
+ /**
+ * 执行代理 fetch 请求
+ *
+ * @param url - 请求的 URL
+ * @param headers - 请求的头部信息
+ * @param params - 请求的参数
+ * @param method - 请求的 HTTP 方法
+ * @param contentType - 请求的内容类型
+ * @returns 返回一个 Promise,解析为响应结果
+ */
+ const proxyFetch = async (
+ url: string,
+ headers: any[] = [],
+ params: any = {},
+ method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" = "GET",
+ contentType: string = "application/json"
+ ) => {
+ if (isUseSiyuanProxy) {
+ logger.info("Using Siyuan forwardProxy")
+ let body: any
+ if (typeof params === "string" && !StrUtil.isEmptyString(params)) {
+ body = params
+ } else if (typeof params === "object" && !ObjectUtil.isEmptyObject(params)) {
+ body = params
+ }
+ const reqUrl = `${apiUrl}${url}`
+ logger.info("siyuan forwardProxy url =>", apiUrl)
+ logger.info("siyuan forwardProxy fetchOptions =>", {
+ headers,
+ body,
+ method,
+ contentType,
+ })
+ const fetchResult = await kernelApi.forwardProxy(reqUrl, headers, body, method, contentType, 7000)
+ logger.debug("proxyFetch result =>", fetchResult)
+
+ if (contentType === "application/json") {
+ const resText = fetchResult?.body
+ const res = JsonUtil.safeParse(resText, {} as any)
+ return res
+ } else if (contentType === "text/html") {
+ const resText = fetchResult?.body
+ return resText
+ } else {
+ return fetchResult
+ }
+ } else {
+ logger.info("Using middleware proxy")
+ const header = headers.length > 0 ? headers[0] : {}
+ const fetchOptions = {
+ method: method,
+ headers: {
+ "Content-Type": "application/json",
+ ...header,
+ },
+ }
+ logger.info("commonFetchClient url in proxyFetch =>", url)
+ logger.info("commonFetchClient fetchOptions in proxyFetch =>", fetchOptions)
+ const res = await commonFetchClient.fetchCall(url, fetchOptions)
+ logger.debug("Result of proxyFetch in commonFetchClient =>", res)
+ return res
+ }
+ }
+
+ /**
+ * 通过代理调用 XML-RPC 方法
+ *
+ * @param url - xmlrpc 端点地址
+ * @param reqMethod - 请求的方法名
+ * @param reqParams - 请求的参数
+ */
+ const proxyXmlrpc = async (url: string, reqMethod: string, reqParams: any[]) => {
+ const body = serializer.serializeMethodCall(reqMethod, reqParams)
+ const res = await proxyFetch(url, [], body, "POST", "text/xml")
+ let resText = res.body
+ resText = XmlrpcUtil.removeXmlHeader(resText)
+ const deserializer = new Deserializer()
+ const resJson = await deserializer.deserializeMethodResponse(resText)
+ logger.debug("xmlrpc fetch result, resJson =>", resJson)
+ return resJson
+ }
+
+ return { proxyFetch, proxyXmlrpc }
+}
+
+export { useProxy }