Skip to content

Commit

Permalink
feat: #958 #1009 全面适配 docker(实验性)
Browse files Browse the repository at this point in the history
  • Loading branch information
terwer committed Mar 4, 2024
1 parent 2706b3f commit c5efd75
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 54 deletions.
55 changes: 30 additions & 25 deletions src/adaptors/api/base/baseBlogApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,27 +29,7 @@ import { createAppLogger, ILogger } from "~/src/utils/appLogger.ts"
import { useProxy } from "~/src/composables/useProxy.ts"
import { BaseExtendApi } from "~/src/adaptors/base/baseExtendApi.ts"
import { JsonUtil, StrUtil } from "zhi-common"

/**
* 执行代理 fetch 请求
*
* @param url - 请求的 URL
* @param headers - 请求的头部信息
* @param params - 请求的参数
* @param method - 请求的 HTTP 方法
* @param contentType - 请求的内容类型
* @param forceProxy - 是否强制使用代理
*
* @returns 返回一个 Promise,解析为响应结果
*/
export type ProxyFetchType = (
url: string,
headers?: any[],
params?: any,
method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
contentType?: string,
forceProxy?: boolean
) => Promise<any>
import { useSiyuanDevice } from "~/src/composables/useSiyuanDevice.ts"

/**
* API授权统一封装基类
Expand All @@ -63,7 +43,8 @@ export class BaseBlogApi extends BlogApi {
protected logger: ILogger
protected cfg: BlogConfig
protected readonly baseExtendApi: BaseExtendApi
private readonly proxyFetch: ProxyFetchType
private readonly proxyFetch: any
private readonly corsFetch: any

/**
* 初始化API授权适配器
Expand All @@ -79,8 +60,9 @@ export class BaseBlogApi extends BlogApi {
this.logger = createAppLogger("base-blog-api")
this.baseExtendApi = new BaseExtendApi(this, cfg)

const { proxyFetch } = useProxy(cfg.middlewareUrl)
const { proxyFetch, corsFetch } = useProxy(cfg.middlewareUrl, cfg.corsAnywhereUrl)
this.proxyFetch = proxyFetch
this.corsFetch = corsFetch
}

public async checkAuth(): Promise<boolean> {
Expand Down Expand Up @@ -129,10 +111,26 @@ export class BaseBlogApi extends BlogApi {
const isCorsProxyAvailable = !StrUtil.isEmptyString(this.cfg.corsAnywhereUrl)
// 如果没有可用的 CORS 代理或者没有强制使用代理,使用默认的自动检测机制
if (!isCorsProxyAvailable || !forceProxy) {
this.logger.info("Using legency api fetch")
// const proxyFetch = async (
// url: string,
// headers: any[] = [],
// params: any = {},
// method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" = "GET",
// contentType: string = "application/json",
// forceProxy: boolean = false
// ) => {
this.logger.info("Using legency api fetch")
return this.proxyFetch(url, headers, params, method, contentType, forceProxy)
} else {
throw new Error("using cors proxy api")
// const corsFetch = async (
// url: string,
// headers: any[] = [],
// params: BodyInit = undefined,
// method: "GET" | "POST" | "PUT" | "DELETE" = "GET"
// )
this.logger.info("Using cors api fetch")
return this.corsFetch(url, headers, params, method)
}
}

Expand All @@ -149,6 +147,12 @@ export class BaseBlogApi extends BlogApi {
// 如果没有可用的 CORS 代理或者没有强制使用代理,使用默认的自动检测机制
if (!isCorsProxyAvailable || !forceProxy) {
this.logger.info("Using legency api formFetch")
const { isInSiyuanOrSiyuanNewWin } = useSiyuanDevice()
if (!isInSiyuanOrSiyuanNewWin()) {
throw new Error(
"检测到当前为非 electron 环境并且未设置 cors 代理,此功能将不可用!请设置 cors 代理或者使用PC 客户端"
)
}
const win = this.appInstance.win
const doFetch = win.require(`${this.appInstance.moduleBase}libs/zhi-formdata-fetch/index.cjs`)

Expand All @@ -164,7 +168,8 @@ export class BaseBlogApi extends BlogApi {

return resJson
} else {
throw new Error("using cors proxy api formFetch")
this.logger.info("Using cors-anywhere api formFetch")
return this.corsFetch(url, headers, formData, "POST")
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/adaptors/api/telegraph/telegraphApiAdaptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ class TelegraphApiAdaptor extends BaseBlogApi {
this.logger.debug("向 Telegraph 发送表单数据,apiUrl =>", apiUrl)
this.logger.debug("向 Telegraph 发送表单数据,options =>", options)

const resJson = await this.apiFormFetch(apiUrl, [headers], formData)
const resJson = await this.apiFormFetch(apiUrl, [headers], formData, true)
if (resJson.error) {
throw new Error(
"telegra.ph 发布错误,注意:切换设备(包括从PC到浏览器环境)需要重新验证,并且获取新token。详细错误 =>" +
Expand Down
5 changes: 5 additions & 0 deletions src/adaptors/api/telegraph/useTelegraphApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ const useTelegraphApi = async (key: string, newCfg?: TelegraphConfig) => {
// 默认值
cfg.posidKey = getDynPostidKey(key)
}
// 初始化corsAnywhereUrl
if (StrUtil.isEmptyString(cfg.corsAnywhereUrl)) {
// 默认值
cfg.corsAnywhereUrl = Utils.emptyOrDefault(process.env.VITE_CORS_ANYWHERE_URL, CORS_PROXT_URL)
}
}

cfg.usernameEnabled = true
Expand Down
41 changes: 16 additions & 25 deletions src/adaptors/web/base/baseWebApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,27 +38,7 @@ import { createAppLogger, ILogger } from "~/src/utils/appLogger.ts"
import { useProxy } from "~/src/composables/useProxy.ts"
import { BaseExtendApi } from "~/src/adaptors/base/baseExtendApi.ts"
import { JsonUtil, StrUtil } from "zhi-common"

/**
* 执行代理 fetch 请求
*
* @param url - 请求的 URL
* @param headers - 请求的头部信息
* @param params - 请求的参数
* @param method - 请求的 HTTP 方法
* @param contentType - 请求的内容类型
* @param forceProxy - 是否强制使用代理
*
* @returns 返回一个 Promise,解析为响应结果
*/
export type ProxyFetchType = (
url: string,
headers?: any[],
params?: any,
method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
contentType?: string,
forceProxy?: boolean
) => Promise<any>
import { useSiyuanDevice } from "~/src/composables/useSiyuanDevice.ts"

/**
* 网页授权统一封装基类
Expand All @@ -72,7 +52,8 @@ class BaseWebApi extends WebApi {
protected logger: ILogger
protected cfg: WebConfig
protected readonly baseExtendApi: BaseExtendApi
private readonly proxyFetch: ProxyFetchType
private readonly proxyFetch: any
private readonly corsFetch: any

/**
* 初始化网页授权 API 适配器
Expand All @@ -88,8 +69,9 @@ class BaseWebApi extends WebApi {
this.logger = createAppLogger("base-web-api")
this.baseExtendApi = new BaseExtendApi(this, cfg)

const { proxyFetch } = useProxy(cfg.middlewareUrl)
const { proxyFetch, corsFetch } = useProxy(cfg.middlewareUrl, cfg.corsAnywhereUrl)
this.proxyFetch = proxyFetch
this.corsFetch = corsFetch
}

public async checkAuth(): Promise<boolean> {
Expand Down Expand Up @@ -200,7 +182,8 @@ class BaseWebApi extends WebApi {
this.logger.info("Using legency web fetch")
return await this.proxyFetch(url, webHeaders, params, method, contentType, forceProxy)
} else {
throw new Error("using cors proxy web")
this.logger.info("Using cors web fetch")
return this.corsFetch(url, headers, params, method)
}
}

Expand All @@ -218,6 +201,13 @@ class BaseWebApi extends WebApi {
// 如果没有可用的 CORS 代理或者没有强制使用代理,使用默认的自动检测机制
if (!isCorsProxyAvailable || !forceProxy) {
this.logger.info("Using legency web formFetch")
const { isInSiyuanOrSiyuanNewWin } = useSiyuanDevice()
if (!isInSiyuanOrSiyuanNewWin()) {
throw new Error(
"检测到当前为非 electron 环境并且未设置 cors 代理,此功能将不可用!请设置 cors 代理或者使用PC 客户端"
)
}

const win = this.appInstance.win
const doFetch = win.require(`${this.appInstance.moduleBase}libs/zhi-formdata-fetch/index.cjs`)

Expand All @@ -233,7 +223,8 @@ class BaseWebApi extends WebApi {

return resJson
} else {
throw new Error("using cors proxy web formFetch")
this.logger.info("Using cors-anywhere web formFetch")
return this.corsFetch(url, headers, formData, "POST")
}
}

Expand Down
77 changes: 74 additions & 3 deletions src/composables/useProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import { useSiyuanApi } from "~/src/composables/useSiyuanApi.ts"
import { JsonUtil, ObjectUtil, StrUtil } from "zhi-common"
import { CommonFetchClient } from "zhi-fetch-middleware"
import { isDev, LEGENCY_SHARED_PROXT_MIDDLEWARE } from "~/src/utils/constants.ts"
import { CORS_PROXT_URL, isDev, LEGENCY_SHARED_PROXT_MIDDLEWARE } from "~/src/utils/constants.ts"
import { PublisherAppInstance } from "~/src/publisherAppInstance.ts"
import { createAppLogger } from "~/src/utils/appLogger.ts"
import { Deserializer, Serializer, XmlrpcUtil } from "simple-xmlrpc"
Expand All @@ -35,11 +35,12 @@ import { Deserializer, Serializer, XmlrpcUtil } from "simple-xmlrpc"
* 用于处理代理请求的自定义 hook
*
* @param middlewareUrl - 可选,如果使用 CommonFetchClient 需要传递,否则可留空
* @param corsProxyUrl - 可选,可留空
* @author terwer
* @version 1.7.0
* @since 1.7.0
*/
const useProxy = (middlewareUrl?: string) => {
const useProxy = (middlewareUrl?: string, corsProxyUrl?: string) => {
const logger = createAppLogger("use-proxy")
const { kernelApi, isUseSiyuanProxy } = useSiyuanApi()

Expand All @@ -49,6 +50,7 @@ const useProxy = (middlewareUrl?: string) => {
const appInstance = new PublisherAppInstance()
const apiUrl = ""
middlewareUrl = middlewareUrl ?? LEGENCY_SHARED_PROXT_MIDDLEWARE
corsProxyUrl = corsProxyUrl ?? CORS_PROXT_URL
const commonFetchClient = new CommonFetchClient(appInstance, apiUrl, middlewareUrl, isDev)
const serializer = new Serializer(appInstance)

Expand Down Expand Up @@ -164,7 +166,76 @@ const useProxy = (middlewareUrl?: string) => {
return resJson
}

return { proxyFetch, proxyXmlrpc }
/**
* 向 Telegraph 发送表单数据
*
* @param url 请求地址
* @param headers 请求头,默认为{}
* @param params 表单数据,默认为undefined,支持 ReadableStream、Blob | BufferSource | FormData | URLSearchParams | string
* @param method 请求方法,默认为GET
*/
const corsFetch = async (
url: string,
headers: any[] = [],
params: BodyInit = undefined,
method: "GET" | "POST" | "PUT" | "DELETE" = "GET"
) => {
// 如果corsProxyUrl结尾有/,则直接使用,否则在url前加上/
const apiUrl = `${corsProxyUrl.endsWith("/") ? corsProxyUrl : corsProxyUrl + "/"}${url}`
const header = headers.length > 0 ? headers[0] : {}

// 处理不安全的 header
const UNSAFE_HEADERS = ["Origin", "Referer", "Cookie"]
const xCorsHeaderString = ObjectUtil.getProperty(header, "x-cors-headers")
let xCorsHeaders = JsonUtil.safeParse<any>(xCorsHeaderString, {})
for (const [key, value] of Object.entries(header)) {
const lowercaseKey = key.toLowerCase()
if (UNSAFE_HEADERS.map((unsafeHeaderItem) => unsafeHeaderItem.toLowerCase()).includes(lowercaseKey)) {
logger.warn(`corsFetch header ${key} is not allowed`)
xCorsHeaders[key] = value
delete header[key]
}
}
header["x-cors-headers"] = JSON.stringify(xCorsHeaders)

const options: RequestInit = {
method: method,
headers: header,
body: params,
}

logger.debug("corsFetch url =>", apiUrl)
logger.debug("corsFetch options =>", options)

const res = await fetch(apiUrl, options)

// 处理返回 header
const corsRespHeaders = {} as any
const respHeaderObj = JsonUtil.safeParse<any>(res.headers.get("cors-received-headers"), {})
for (const [resp_key, resp_value] of Object.entries(respHeaderObj)) {
if (resp_key === "cors-received-headers") {
const corsRecv = respHeaderObj["cors-received-headers"]
for (const [cors_key, cors_value] of Object.entries(corsRecv)) {
corsRespHeaders[cors_key] = cors_value
}
delete respHeaderObj[resp_key]
} else {
corsRespHeaders[resp_key] = resp_value
}
}
logger.debug("corsFetch corsRespHeaders =>", corsRespHeaders)

const resText = await res.text()
logger.debug("corsFetch resText =>", resText)

const resJson = JsonUtil.safeParse<any>(resText, {})
resJson["cors-received-headers"] = JSON.stringify(corsRespHeaders)
logger.debug("corsFetch resJson =>", resJson)

return resJson
}

return { proxyFetch, proxyXmlrpc, corsFetch }
}

export { useProxy }

0 comments on commit c5efd75

Please sign in to comment.