From 4b5649b695c7b514fa3406a9dcb91b44ee463791 Mon Sep 17 00:00:00 2001 From: terwer Date: Mon, 1 Apr 2024 18:45:32 +0800 Subject: [PATCH] fix: #1109 support telegra.ph login --- README.md | 86 +++++++++---------- README_zh_CN.md | 12 +-- src/adaptors/api/base/baseBlogApi.ts | 52 ++++++----- .../api/telegraph/telegraphApiAdaptor.ts | 81 ++++++++++++++--- src/adaptors/api/telegraph/useTelegraphApi.ts | 9 +- src/adaptors/web/base/baseWebApi.ts | 31 ++++--- src/adaptors/web/csdn/csdnWebAdaptor.ts | 3 + src/adaptors/web/jianshu/jianshuWebAdaptor.ts | 3 +- src/adaptors/web/wechat/wechatWebAdaptor.ts | 6 +- .../singleplatform/base/CommonBlogSetting.vue | 4 +- src/composables/useProxy.ts | 20 +---- src/utils/constants.ts | 9 +- testdata/telegra-ph/telegra.ph.http | 4 + 13 files changed, 189 insertions(+), 131 deletions(-) diff --git a/README.md b/README.md index c02a0991..d31654e8 100644 --- a/README.md +++ b/README.md @@ -30,49 +30,49 @@ Note: Image upload availability refers to the installation of [PicGo plugin1.9.0+](https://github.com/terwer/siyuan-plugin-picgo) and accurate configuration. -| Type | Platform | Status | PC Client | PC Image Upload | Docker | Docker Image Upload | Platform Image Upload | Docker Proxy | PC Proxy | Note | -| :-------------------: | :---------------------: | :----: | :------------------: | :-------------: | :----------------------------------------------------: | :-----------------: | --------------------- | ------------- | --------- | :--------------------------------------------------------- | -| Generic | Yuque | ✔ | Compatible | ✔ | Compatible | ✔ | Not Supported | Bundled Proxy | No | [Official Website](https://yuque.com) | -| Generic | Notion | ✔ | Compatible | ✔ | Compatible | ✔ | Not Supported | Bundled Proxy | No | [Official Website](https://www.notion.so) | -| Generic | Halo | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | Bundled Proxy | No | [Official Website](https://halo.run) | -| Generic | Evernote | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://www.evernote.com) | -| Generic | Github | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://github.com) | -| Static Site Generator | Hexo | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://hexo.io/zh-cn/) | -| Static Site Generator | Hugo | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gohugo.io/) | -| Static Site Generator | Jekyll | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://jekyllrb.com/) | -| Static Site Generator | Vuepress | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://vuepress.vuejs.org/) | -| Static Site Generator | Vuepress2 | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://v2.vuepress.vuejs.org/) | -| Static Site Generator | Vitepress | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://vitepress.vuejs.org/) | -| Static Site Generator | Antora | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://antora.org/) | -| Static Site Generator | Docsify | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://docsify.js.org/) | -| Git Hosting | Gitlab | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gitlab.com/) | -| Git Hosting | Gitlabhexo | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gitlab.com/) | -| Git Hosting | Gitlabhugo | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gitlab.com/) | -| Git Hosting | Gitlabjekyll | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gitlab.com/) | -| Git Hosting | Gitlabvuepress | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gitlab.com/) | -| Git Hosting | Gitlabvuepress2 | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gitlab.com/) | -| Git Hosting | Gitlabvitepress | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gitlab.com/) | -| Git Hosting | Gitlabantora | TODO | TODO | TODO | TODO | TODO | Supported | TODO | TODO | [Official Website](https://gitlab.com/) | -| Git Hosting | Gitlabdocsify | TODO | TODO | TODO | TODO | TODO | Supported | TODO | TODO | [Official Website](https://gitlab.com/) | -| Blog | Metaweblog | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | Bundled Proxy | No | [Official Website](http://xmlrpc.scripting.com/metaWeblog) | -| Blog | CnBlogs | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | Bundled Proxy | No | [Official Website](https://cnblogs.com) | -| Blog | Typecho | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | Bundled Proxy | No | [Official Website](https://typecho.org/) | -| Blog | Jvue | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | Bundled Proxy | No | [Official Website](https://github.com/terwer/jvue) | -| Blog | WordPress | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | Bundled Proxy | No | [Official Website](https://wordpress.org/) | -| Blog | WordPress.com | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | Bundled Proxy | No | [Official Website](https://wordpress.com) | -| Social Media | Zhihu | ✔ | Fully Compatible | ✔ | Partially Compatible, Requires PC Account Verification | ❌ | | | | [Official Website](https://www.zhihu.com/) | -| Social Media | CSDN | ✔ | Fully Compatible | ✔ | Partially Compatible, Requires PC Account Verification | ❌ | | | | [Official Website](https://www.csdn.net/) | -| Social Media | WeChat Official Account | ✔ | Fully Compatible | ✔ | Partially Compatible, Requires PC Account Verification | ❌ | | | | [Official Website](https://mp.weixin.qq.com/) | -| Social Media | Jianshu | ✔ | Fully Compatible | ✔ | Partially Compatible, Requires PC Account Verification | ❌ | | | | [Official Website](https://www.jianshu.com/) | -| Social Media | Juejin | ✔ | Fully Compatible | ✔ | Partially Compatible, Requires PC Account Verification | ❌ | | | | [Official Website](https://juejin.cn/) | -| Community | 52pojie | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://www.52pojie.cn/) | -| Community | Bilibili | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://www.bilibili.com/) | -| Community | Xiaohongshu | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://www.xiaohongshu.com/) | -| Community | Douban | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://www.douban.com/) | -| Others | Xlog | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://xlog.cn/) | -| Others | Mdnice | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://mdnice.com/) | -| Others | Flowus | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://www.flowus.com/) | -| Others | telegra.ph | ✔ | Partially Compatible | ❌ | Partially Compatible | ❌ | | | | [Official Website](https://telegra.ph) | +| Type | Platform | Status | PC Client | PC Image Upload | Docker | Docker Image Upload | Platform Image Upload | Docker Proxy | PC Proxy | Note | +| :-------------------: | :---------------------: | :----: | :------------------: | :-------------: | :------------------: | :-----------------: | --------------------- | ------------- | ------------ | :--------------------------------------------------------- | +| Generic | Yuque | ✔ | Compatible | ✔ | Compatible | ✔ | Not Supported | Bundled Proxy | No | [Official Website](https://yuque.com) | +| Generic | Notion | ✔ | Compatible | ✔ | Compatible | ✔ | Not Supported | Bundled Proxy | No | [Official Website](https://www.notion.so) | +| Generic | Halo | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | Bundled Proxy | No | [Official Website](https://halo.run) | +| Generic | Evernote | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://www.evernote.com) | +| Generic | Github | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://github.com) | +| Static Site Generator | Hexo | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://hexo.io/zh-cn/) | +| Static Site Generator | Hugo | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gohugo.io/) | +| Static Site Generator | Jekyll | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://jekyllrb.com/) | +| Static Site Generator | Vuepress | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://vuepress.vuejs.org/) | +| Static Site Generator | Vuepress2 | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://v2.vuepress.vuejs.org/) | +| Static Site Generator | Vitepress | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://vitepress.vuejs.org/) | +| Static Site Generator | Antora | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://antora.org/) | +| Static Site Generator | Docsify | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://docsify.js.org/) | +| Git Hosting | Gitlab | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gitlab.com/) | +| Git Hosting | Gitlabhexo | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gitlab.com/) | +| Git Hosting | Gitlabhugo | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gitlab.com/) | +| Git Hosting | Gitlabjekyll | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gitlab.com/) | +| Git Hosting | Gitlabvuepress | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gitlab.com/) | +| Git Hosting | Gitlabvuepress2 | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gitlab.com/) | +| Git Hosting | Gitlabvitepress | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | No | No | [Official Website](https://gitlab.com/) | +| Git Hosting | Gitlabantora | TODO | TODO | TODO | TODO | TODO | Supported | TODO | TODO | [Official Website](https://gitlab.com/) | +| Git Hosting | Gitlabdocsify | TODO | TODO | TODO | TODO | TODO | Supported | TODO | TODO | [Official Website](https://gitlab.com/) | +| Blog | Metaweblog | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | Bundled Proxy | No | [Official Website](http://xmlrpc.scripting.com/metaWeblog) | +| Blog | CnBlogs | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | Bundled Proxy | No | [Official Website](https://cnblogs.com) | +| Blog | Typecho | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | Bundled Proxy | No | [Official Website](https://typecho.org/) | +| Blog | Jvue | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | Bundled Proxy | No | [Official Website](https://github.com/terwer/jvue) | +| Blog | WordPress | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | Bundled Proxy | No | [Official Website](https://wordpress.org/) | +| Blog | WordPress.com | ✔ | Compatible | ✔ | Compatible | ✔ | Supported | Bundled Proxy | No | [Official Website](https://wordpress.com) | +| Social Media | Zhihu | ✔ | Fully Compatible | ✔ | Requires PC Auth | ✖ | Supported | Bundled Proxy | No | [Official Website](https://www.zhihu.com/) | +| Social Media | CSDN | ✔ | Fully Compatible | ✔ | Requires PC Auth | ✖ | Supported | Bundled Proxy | No | [Official Website](https://www.csdn.net/) | +| Social Media | WeChat Official Account | ✔ | Fully Compatible | ✔ | Requires PC Auth | ✖ | Supported | Bundled Proxy | No | [Official Website](https://mp.weixin.qq.com/) | +| Social Media | Jianshu | ✔ | Fully Compatible | ✔ | Requires PC Auth | ✖ | Supported | Bundled Proxy | No | [Official Website](https://www.jianshu.com/) | +| Social Media | Juejin | ✔ | Fully Compatible | ✔ | Requires PC Auth | ✔ | Not Supported | Bundled Proxy | No | [Official Website](https://juejin.cn/) | +| Community | 52pojie | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://www.52pojie.cn/) | +| Community | Bilibili | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://www.bilibili.com/) | +| Community | Xiaohongshu | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://www.xiaohongshu.com/) | +| Community | Douban | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://www.douban.com/) | +| Others | Xlog | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://xlog.cn/) | +| Others | Mdnice | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://mdnice.com/) | +| Others | Flowus | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [Official Website](https://www.flowus.com/) | +| Others | telegra.ph | ✔ | Partially Compatible | ✖ | Partially Compatible | ✖ | Not Supported | Custom Proxy | Custom Proxy | [Official Website](https://telegra.ph) | ## Core Features diff --git a/README_zh_CN.md b/README_zh_CN.md index 2d0d8978..847b1cd1 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -57,11 +57,11 @@ | 博客 | Jvue | ✔ | 完全兼容 | ✔ | 完全兼容 | ✔ | 支持 | 内置代理 | 不需要 | [官网](https://github.com/terwer/jvue) | | 博客 | WordPress | ✔ | 完全兼容 | ✔ | 完全兼容 | ✔ | 支持 | 内置代理 | 不需要 | [官网](https://wordpress.org/) | | 博客 | WordPress.com | ✔ | 完全兼容 | ✔ | 完全兼容 | ✔ | 支持 | 内置代理 | 不需要 | [官网](https://wordpress.com) | -| 社交媒体 | 知乎 | ✔ | 完全兼容 | ✔ | PC 验证 | ✖ | | | | [官网](https://www.zhihu.com/) | -| 社交媒体 | CSDN | ✔ | 完全兼容 | ✔ | PC 验证 | ✖ | | | | [官网](https://www.csdn.net/) | -| 社交媒体 | 微信公众号 | ✔ | 完全兼容 | ✔ | PC 验证 | ✖ | | | | [官网](https://mp.weixin.qq.com/) | -| 社交媒体 | 简书 | ✔ | 完全兼容 | ✔ | PC 验证 | ✖ | | | | [官网](https://www.jianshu.com/) | -| 社交媒体 | 掘金 | ✔ | 完全兼容 | ✔ | PC 验证 | ✔ | | | | [官网](https://juejin.cn/) | +| 社交媒体 | 知乎 | ✔ | 完全兼容 | ✔ | PC 验证 | ✖ | 支持 | 内置代理 | 不需要 | [官网](https://www.zhihu.com/) | +| 社交媒体 | CSDN | ✔ | 完全兼容 | ✔ | PC 验证 | ✖ | 支持 | 内置代理 | 不需要 | [官网](https://www.csdn.net/) | +| 社交媒体 | 微信公众号 | ✔ | 完全兼容 | ✔ | PC 验证 | ✖ | 支持 | 内置代理 | 不需要 | [官网](https://mp.weixin.qq.com/) | +| 社交媒体 | 简书 | ✔ | 完全兼容 | ✔ | PC 验证 | ✖ | 支持 | 内置代理 | 不需要 | [官网](https://www.jianshu.com/) | +| 社交媒体 | 掘金 | ✔ | 完全兼容 | ✔ | PC 验证 | ✔ | 不支持 | 内置代理 | 不需要 | [官网](https://juejin.cn/) | | 社区 | 52破解 | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [官网](https://www.52pojie.cn/) | | 社区 | Bilibili | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [官网](https://www.bilibili.com/) | | 社区 | 小红书 | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [官网](https://www.xiaohongshu.com/) | @@ -69,7 +69,7 @@ | 其他 | Xlog | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [官网](https://xlog.cn/) | | 其他 | Mdnice | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [官网](https://mdnice.com/) | | 其他 | Flowus | TODO | TODO | TODO | TODO | TODO | TODO | TODO | TODO | [官网](https://www.flowus.com/) | -| 其他 | telegra.ph | ✔ | 部分兼容 | ✖ | 部分兼容 | ✖ | | | | [官网](https://telegra.ph) | +| 其他 | telegra.ph | ✔ | 部分兼容 | ✖ | 部分兼容 | ✖ | 不支持 | 自建代理 | 自建代理 | [官网](https://telegra.ph) | ## 核心特色 diff --git a/src/adaptors/api/base/baseBlogApi.ts b/src/adaptors/api/base/baseBlogApi.ts index b5be6a01..6131d62f 100644 --- a/src/adaptors/api/base/baseBlogApi.ts +++ b/src/adaptors/api/base/baseBlogApi.ts @@ -28,7 +28,7 @@ import { PublisherAppInstance } from "~/src/publisherAppInstance.ts" 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" +import { JsonUtil } from "zhi-common" import { useSiyuanDevice } from "~/src/composables/useSiyuanDevice.ts" import { Base64 } from "js-base64" import FormDataUtils from "~/src/utils/FormDataUtils.ts" @@ -132,29 +132,36 @@ export class BaseBlogApi extends BlogApi { | "base32-hex" | "hex" = "text" ) { - const isCorsProxyAvailable = !StrUtil.isEmptyString(this.cfg.corsAnywhereUrl) + const header = headers.length > 0 ? headers[0] : {} + // 如果没有可用的 CORS 代理或者没有强制使用代理,使用默认的自动检测机制 - if (this.isUseSiyuanProxy || !isCorsProxyAvailable) { - 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 - // ) => { + if (this.isUseSiyuanProxy || (!this.isUseSiyuanProxy && forceProxy)) { this.logger.info("Using legency api fetch") - return this.proxyFetch(url, headers, params, method, contentType, forceProxy, payloadEncoding, responseEncoding) + // remove cors fetch header + delete header["x-cors-headers"] + const blogHeaders = [ + { + ...header, + }, + ] + return this.proxyFetch( + url, + blogHeaders, + params, + method, + contentType, + forceProxy, + payloadEncoding, + responseEncoding + ) } else { - // 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) + const blogHeaders = [ + { + ...header, + }, + ] + return this.corsFetch(url, blogHeaders, params, method) } } @@ -167,13 +174,12 @@ export class BaseBlogApi extends BlogApi { * @param forceProxy - 是否强制使用代理,默认为 false */ public async apiProxyFormFetch(url: string, headers: any[], formData: FormData, forceProxy: boolean = false) { - const isCorsProxyAvailable = !StrUtil.isEmptyString(this.cfg.corsAnywhereUrl) // 如果没有可用的 CORS 代理或者没有强制使用代理,使用默认的自动检测机制 - if (this.isUseSiyuanProxy || !isCorsProxyAvailable) { + if (this.isUseSiyuanProxy || (!this.isUseSiyuanProxy && forceProxy)) { this.logger.info("Using legency api formFetch") const { isInSiyuanOrSiyuanNewWin } = useSiyuanDevice() - if (!isInSiyuanOrSiyuanNewWin()) { + if (!isInSiyuanOrSiyuanNewWin() || forceProxy) { const fetchResult = await this.apiProxyFetch( url, headers, diff --git a/src/adaptors/api/telegraph/telegraphApiAdaptor.ts b/src/adaptors/api/telegraph/telegraphApiAdaptor.ts index b46cd987..b8c19e76 100644 --- a/src/adaptors/api/telegraph/telegraphApiAdaptor.ts +++ b/src/adaptors/api/telegraph/telegraphApiAdaptor.ts @@ -29,6 +29,7 @@ import { TelegraphConfig } from "~/src/adaptors/api/telegraph/telegraphConfig.ts import { JsonUtil, StrUtil } from "zhi-common" import CookieUtils from "~/src/utils/cookieUtils.ts" import md from "telegraph.md" +import FormDataUtils from "~/src/utils/FormDataUtils.ts" /** * Telegraph API 适配器 @@ -41,14 +42,31 @@ class TelegraphApiAdaptor extends BaseBlogApi { public async getUsersBlogs(): Promise { const result: UserBlog[] = [] - const checkJson = await this.telegraphFetch("/check", "page_id=0", "POST") - if (checkJson.error) { - throw new Error("telegra.ph request error =>" + checkJson.error) - } - const corsHeaders = JsonUtil.safeParse(checkJson["cors-received-headers"], {}) - const cookies = corsHeaders["Set-Cookie-Array"] + let cookies: any + let checkJson = await this.telegraphFetch("/check", "page_id=0", "POST") this.logger.debug("checkJson =>", checkJson) + if (checkJson["cors-received-headers"]) { + // use cors proxy + if (checkJson.error) { + throw new Error("telegra.ph request error =>" + checkJson.error) + } + const corsHeaders = JsonUtil.safeParse(checkJson["cors-received-headers"], {}) + cookies = corsHeaders["Set-Cookie-Array"] + } else { + // use siyuan proxy + if (checkJson.status == 200) { + cookies = checkJson.headers["Set-Cookie"] + const body = JsonUtil.safeParse(checkJson.body, {}) + checkJson = { + ...checkJson, + ...body, + } + } else { + throw new Error(`telegra.ph request error ${checkJson.status}=>` + checkJson.body) + } + } + // 数据适配 const userblog: UserBlog = new UserBlog() const cfg = this.cfg as TelegraphConfig @@ -57,9 +75,10 @@ class TelegraphApiAdaptor extends BaseBlogApi { userblog.url = cfg.apiUrl // 元数据映射 // @since 1.20.0 + const newCookies = CookieUtils.addCookieArray(this.cfg?.corsCookieArray ?? [], cookies) userblog.metadataMap = { password: checkJson.save_hash, - corsCookieArray: CookieUtils.addCookieArray(this.cfg?.corsCookieArray ?? [], cookies), + corsCookieArray: newCookies, } result.push(userblog) this.logger.debug("get telegraph cfg =>", result) @@ -68,6 +87,9 @@ class TelegraphApiAdaptor extends BaseBlogApi { } public async newPost(post: Post, _publish?: boolean): Promise { + // 这里不用这个,因为 telegraph 必须强制代理 + // const { FormData, Blob } = FormDataUtils.getFormData(this.appInstance) + const formData = new FormData() const content = md(post.description) const blobData = new Blob([JSON.stringify(content)], { type: "text/plain" }) @@ -95,6 +117,8 @@ class TelegraphApiAdaptor extends BaseBlogApi { } public async editPost(postid: string, post: Post, publish?: boolean): Promise { + const { FormData, Blob } = FormDataUtils.getFormData(this.appInstance) + const postMeta = JsonUtil.safeParse(postid, {}) const formData = new FormData() @@ -153,16 +177,39 @@ class TelegraphApiAdaptor extends BaseBlogApi { header: Record = {} ) { const contentType = "text/plain" + // const tphUuidObj = CookieUtils.getCookieObject(this.cfg.corsCookieArray, this.TPH_UUID_KEY) + // if (!StrUtil.isEmptyString(tphUuidObj[this.TPH_UUID_KEY])) { + // header["Cookie"] = `${this.TPH_UUID_KEY}=${tphUuidObj[this.TPH_UUID_KEY]}` + // } + + // const headers = { + // "Content-Type": contentType, + // origin: "https://telegra.ph", + // referer: "https://telegra.ph/", + // ...header, + // } + let xCorsHeaders: Record = {} + + // header + + // x-cors-headers const tphUuidObj = CookieUtils.getCookieObject(this.cfg.corsCookieArray, this.TPH_UUID_KEY) if (!StrUtil.isEmptyString(tphUuidObj[this.TPH_UUID_KEY])) { - header["Cookie"] = `${this.TPH_UUID_KEY}=${tphUuidObj[this.TPH_UUID_KEY]}` + xCorsHeaders["Cookie"] = `${this.TPH_UUID_KEY}=${tphUuidObj[this.TPH_UUID_KEY]}` } + xCorsHeaders["origin"] = "https://telegra.ph" + xCorsHeaders["referer"] = "https://telegra.ph/" + xCorsHeaders["Content-Type"] = contentType + const headers = { - "Content-Type": contentType, - origin: "https://telegra.ph", - referer: "https://telegra.ph/", - ...header, + // for cors proxy + // siyuan proxy should ignore this header + "x-cors-headers": JSON.stringify(xCorsHeaders), + } + for (const [xkey, xvalue] of Object.entries(xCorsHeaders)) { + headers[xkey] = xvalue } + const body = params // 输出日志 @@ -172,7 +219,7 @@ class TelegraphApiAdaptor extends BaseBlogApi { this.logger.debug("向 Telegraph 请求数据,headers =>", headers) this.logger.debug("向 Telegraph 请求数据,body =>", body) - const resJson = await this.apiProxyFetch(apiUrl, [headers], body, method, contentType, true) + const resJson = await this.apiProxyFetch(apiUrl, [headers], body, method, contentType, false) this.logger.debug("向 Telegraph 请求数据,resJson =>", resJson) return resJson ?? null @@ -199,8 +246,14 @@ class TelegraphApiAdaptor extends BaseBlogApi { xCorsHeaders["referer"] = "https://telegra.ph/" const headers = { + // for cors proxy + // siyuan proxy should ignore this header "x-cors-headers": JSON.stringify(xCorsHeaders), } + for (const [xkey, xvalue] of Object.entries(xCorsHeaders)) { + headers[xkey] = xvalue + } + const options: RequestInit = { method: "POST", headers: headers, @@ -210,7 +263,7 @@ class TelegraphApiAdaptor extends BaseBlogApi { this.logger.debug("向 Telegraph 发送表单数据,apiUrl =>", apiUrl) this.logger.debug("向 Telegraph 发送表单数据,options =>", options) - const resJson = await this.apiProxyFormFetch(apiUrl, [headers], formData, true) + const resJson = await this.apiProxyFormFetch(apiUrl, [headers], formData, false) if (resJson.error) { throw new Error( "telegra.ph 发布错误,注意:切换设备(包括从PC到浏览器环境)需要重新验证,并且获取新token。详细错误 =>" + diff --git a/src/adaptors/api/telegraph/useTelegraphApi.ts b/src/adaptors/api/telegraph/useTelegraphApi.ts index c7c01238..0833e19c 100644 --- a/src/adaptors/api/telegraph/useTelegraphApi.ts +++ b/src/adaptors/api/telegraph/useTelegraphApi.ts @@ -31,7 +31,7 @@ import { JsonUtil, ObjectUtil, StrUtil } from "zhi-common" import { Utils } from "~/src/utils/utils.ts" import { getDynPostidKey } from "~/src/platforms/dynamicConfig.ts" import { TelegraphApiAdaptor } from "~/src/adaptors/api/telegraph/telegraphApiAdaptor.ts" -import { CORS_PROXT_URL, LEGENCY_SHARED_PROXT_MIDDLEWARE } from "~/src/utils/constants.ts" +import { LEGENCY_SHARED_PROXT_MIDDLEWARE } from "~/src/utils/constants.ts" const useTelegraphApi = async (key: string, newCfg?: TelegraphConfig) => { const logger = createAppLogger("use-telegraph-api") @@ -51,7 +51,7 @@ const useTelegraphApi = async (key: string, newCfg?: TelegraphConfig) => { if (ObjectUtil.isEmptyObject(cfg)) { const telegraphUrl = Utils.emptyOrDefault(process.env.VITE_TELEGRAPH_URL, "https://telegra.ph") const middlewareUrl = Utils.emptyOrDefault(process.env.VITE_MIDDLEWARE_URL, LEGENCY_SHARED_PROXT_MIDDLEWARE) - const telegraphToken = Utils.emptyOrDefault(process.env.VITE_TELEGRAPH_TOKEN, CORS_PROXT_URL) + const telegraphToken = Utils.emptyOrDefault(process.env.VITE_TELEGRAPH_TOKEN, "") cfg = new TelegraphConfig(telegraphUrl, telegraphToken, middlewareUrl) logger.info("Configuration is empty, using default environment variables.") } else { @@ -63,11 +63,6 @@ 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 diff --git a/src/adaptors/web/base/baseWebApi.ts b/src/adaptors/web/base/baseWebApi.ts index 53af7d95..6ff5c5a6 100644 --- a/src/adaptors/web/base/baseWebApi.ts +++ b/src/adaptors/web/base/baseWebApi.ts @@ -192,17 +192,17 @@ class BaseWebApi extends WebApi { | "hex" = "text" ) { const header = headers.length > 0 ? headers[0] : {} - const webHeaders = [ - { - ...header, - Cookie: this.cfg.password, - }, - ] - - const isCorsProxyAvailable = !StrUtil.isEmptyString(this.cfg.corsAnywhereUrl) // 如果没有可用的 CORS 代理或者没有强制使用代理,使用默认的自动检测机制 - if (this.isUseSiyuanProxy || !isCorsProxyAvailable) { + if (this.isUseSiyuanProxy || (!this.isUseSiyuanProxy && forceProxy)) { this.logger.info("Using legency web fetch") + // remove cors fetch header + delete header["x-cors-headers"] + const webHeaders = [ + { + ...header, + Cookie: this.cfg.password, + }, + ] return await this.proxyFetch( url, webHeaders, @@ -215,7 +215,13 @@ class BaseWebApi extends WebApi { ) } else { this.logger.info("Using cors web fetch") - return this.corsFetch(url, headers, params, method) + const webHeaders = [ + { + ...header, + Cookie: this.cfg.password, + }, + ] + return this.corsFetch(url, webHeaders, params, method) } } @@ -253,13 +259,12 @@ class BaseWebApi extends WebApi { | "base32-hex" | "hex" = "text" ) { - const isCorsProxyAvailable = !StrUtil.isEmptyString(this.cfg.corsAnywhereUrl) // 如果没有可用的 CORS 代理或者没有强制使用代理,使用默认的自动检测机制 - if (this.isUseSiyuanProxy || !isCorsProxyAvailable) { + if (this.isUseSiyuanProxy || (!this.isUseSiyuanProxy && forceProxy)) { this.logger.info("Using legency web formFetch") const { isInSiyuanOrSiyuanNewWin } = useSiyuanDevice() - if (!isInSiyuanOrSiyuanNewWin()) { + if (!isInSiyuanOrSiyuanNewWin() || forceProxy) { const fetchResult = await this.webProxyFetch( url, headers, diff --git a/src/adaptors/web/csdn/csdnWebAdaptor.ts b/src/adaptors/web/csdn/csdnWebAdaptor.ts index 956534b5..91891b14 100644 --- a/src/adaptors/web/csdn/csdnWebAdaptor.ts +++ b/src/adaptors/web/csdn/csdnWebAdaptor.ts @@ -29,6 +29,7 @@ import { BlogConfig, CategoryInfo, PageTypeEnum, Post, UserBlog } from "zhi-blog import { JsonUtil } from "zhi-common" import WebUtils from "~/src/adaptors/web/base/webUtils.ts" import _ from "lodash-es" +import FormDataUtils from "~/src/utils/FormDataUtils.ts"; /** * CSDN网页授权适配器 @@ -303,6 +304,8 @@ class CsdnWebAdaptor extends BaseWebApi { } public async uploadFile(file: File | Blob, filename?: string): Promise { + const { FormData, Blob } = FormDataUtils.getFormData(this.appInstance) + this.logger.debug(`csdn start uploadFile ${filename}=>`, file) if (file instanceof Blob) { const uploadData = await this.requestUpload(filename) diff --git a/src/adaptors/web/jianshu/jianshuWebAdaptor.ts b/src/adaptors/web/jianshu/jianshuWebAdaptor.ts index 012f4c18..3814a8e0 100644 --- a/src/adaptors/web/jianshu/jianshuWebAdaptor.ts +++ b/src/adaptors/web/jianshu/jianshuWebAdaptor.ts @@ -182,6 +182,8 @@ class JianshuWebAdaptor extends BaseWebApi { } public async uploadFile(file: File | Blob, filename?: string): Promise { + const { FormData, Blob } = FormDataUtils.getFormData(this.appInstance) + this.logger.debug(`jianshu start uploadFile ${filename}=>`, file) if (file instanceof Blob) { // import @@ -189,7 +191,6 @@ class JianshuWebAdaptor extends BaseWebApi { if (!win.require) { throw new Error("非常抱歉,目前仅思源笔记PC客户端支持上传图片") } - const { FormData, Blob } = FormDataUtils.getFormData(this.appInstance) // uploadUrl const uploadUrl = "https://upload.qiniup.com/" diff --git a/src/adaptors/web/wechat/wechatWebAdaptor.ts b/src/adaptors/web/wechat/wechatWebAdaptor.ts index 9f40b5f1..84b01709 100644 --- a/src/adaptors/web/wechat/wechatWebAdaptor.ts +++ b/src/adaptors/web/wechat/wechatWebAdaptor.ts @@ -443,6 +443,9 @@ class WechatWebAdaptor extends BaseWebApi { } public async uploadFile(file: File | Blob, filename?: string): Promise { + // get formData and Blob + const { FormData, Blob } = FormDataUtils.getFormData(this.appInstance) + this.logger.debug(`wechat start uploadFile ${filename}=>`, file) if (file instanceof Blob) { // import @@ -450,7 +453,6 @@ class WechatWebAdaptor extends BaseWebApi { if (!win.require) { throw new Error("非常抱歉,目前仅思源笔记PC客户端支持上传图片") } - const { FormData, Blob } = FormDataUtils.getFormData(this.appInstance) // uploadUrl const ticket_id = this.cfg.metadata.commonData.data.user_name @@ -535,6 +537,8 @@ class WechatWebAdaptor extends BaseWebApi { */ private async wechatFetch(url: string, params: Record) { this.logger.debug("before getFormdataFetch, params =>", params) + // get formData and Blob + const { FormData } = FormDataUtils.getFormData(this.appInstance) // formData const formData: any = new FormData() diff --git a/src/components/set/publish/singleplatform/base/CommonBlogSetting.vue b/src/components/set/publish/singleplatform/base/CommonBlogSetting.vue index ed19be94..41fe6ac0 100644 --- a/src/components/set/publish/singleplatform/base/CommonBlogSetting.vue +++ b/src/components/set/publish/singleplatform/base/CommonBlogSetting.vue @@ -470,7 +470,7 @@ onMounted(async () => { @@ -483,7 +483,7 @@ onMounted(async () => { diff --git a/src/composables/useProxy.ts b/src/composables/useProxy.ts index 9b4665b0..52c5cf4d 100644 --- a/src/composables/useProxy.ts +++ b/src/composables/useProxy.ts @@ -26,11 +26,10 @@ import { useSiyuanApi } from "~/src/composables/useSiyuanApi.ts" import { JsonUtil, ObjectUtil, StrUtil } from "zhi-common" import { CommonFetchClient } from "zhi-fetch-middleware" -import { CORS_PROXT_URL, isDev, LEGENCY_SHARED_PROXT_MIDDLEWARE } from "~/src/utils/constants.ts" +import { 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" -import { Base64 } from "js-base64" /** * 用于处理代理请求的自定义 hook @@ -51,7 +50,7 @@ const useProxy = (middlewareUrl?: string, corsProxyUrl?: string) => { const appInstance = new PublisherAppInstance() const apiUrl = "" middlewareUrl = middlewareUrl ?? LEGENCY_SHARED_PROXT_MIDDLEWARE - corsProxyUrl = corsProxyUrl ?? CORS_PROXT_URL + corsProxyUrl = corsProxyUrl ?? "" const commonFetchClient = new CommonFetchClient(appInstance, apiUrl, middlewareUrl, isDev) const serializer = new Serializer(appInstance) @@ -93,18 +92,9 @@ const useProxy = (middlewareUrl?: string, corsProxyUrl?: string) => { | "base32-hex" | "hex" = "text" ) => { - if (isUseSiyuanProxy) { + if (isUseSiyuanProxy || (!isUseSiyuanProxy && forceProxy)) { logger.info("Using Siyuan forwardProxy") - return await siyuanProxyFetch( - url, - headers, - params, - method, - contentType, - forceProxy, - payloadEncoding, - responseEncoding - ) + return await siyuanProxyFetch(url, headers, params, method, contentType, payloadEncoding, responseEncoding) } else { logger.info("Using middleware proxy fetch") const header = headers.length > 0 ? headers[0] : {} @@ -223,7 +213,6 @@ const useProxy = (middlewareUrl?: string, corsProxyUrl?: string) => { * @param params - 请求的参数 * @param method - 请求的 HTTP 方法 * @param contentType - 请求的内容类型 - * @param forceProxy - 是否强制使用代理 * @param payloadEncoding - 请求体的编码方式,默认为 text * @param responseEncoding - 响应体的编码方式,默认为 text */ @@ -233,7 +222,6 @@ const useProxy = (middlewareUrl?: string, corsProxyUrl?: string) => { params: any = {}, method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH" = "GET", contentType: string = "application/json", - forceProxy: boolean = false, payloadEncoding: | "text" | "base64" diff --git a/src/utils/constants.ts b/src/utils/constants.ts index c339a4fd..49e6bd0e 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -34,7 +34,6 @@ export const aboutUrl = "https://terwer.space/about" */ export const DYNAMIC_CONFIG_KEY = "dynamic-config" - /** * 自动映射分类占位符 */ @@ -54,9 +53,9 @@ export const MAX_TITLE_LENGTH = 10 export const LEGENCY_SHARED_PROXT_MIDDLEWARE = "https://api.terwer.space/api/middleware" /** - * 新版通用 HTTP 代理 + * 新版通用 HTTP 代理,不再免费提供 * - * @since 1.20.0 - * @version 1.20.0 + * @since 1.20.2 + * @version 1.20.2 */ -export const CORS_PROXT_URL = "https://cors.terwer.space" +// export const CORS_PROXT_URL = "" diff --git a/testdata/telegra-ph/telegra.ph.http b/testdata/telegra-ph/telegra.ph.http index ac2a675c..c9bb759f 100644 --- a/testdata/telegra-ph/telegra.ph.http +++ b/testdata/telegra-ph/telegra.ph.http @@ -1,6 +1,7 @@ # telegra.ph ### getCfg +# @no-cookie-jar POST https://edit.telegra.ph/check origin: https://telegra.ph referer: https://telegra.ph/ @@ -9,9 +10,12 @@ Content-Type: text/plain page_id=0 ### newArticle +# @no-cookie-jar POST https://edit.telegra.ph/save origin: https://telegra.ph referer: https://telegra.ph/ +Cookie: tph_uuid=E8eRGiWLjdx4Qbs0csRuQz2tBniMZQ1DYguCwroNET +#Cookie: tph_uuid=OA4kiCWNRETS7Laf9JLG5m5yDG7IQh2CP4W8GQTvvM Content-Type: multipart/form-data; boundary=TelegraPhBoundary21 --TelegraPhBoundary21