From 99c9b28ca6cea751406d7bed32a872adac45570f Mon Sep 17 00:00:00 2001 From: terwer Date: Tue, 2 Apr 2024 18:40:39 +0800 Subject: [PATCH] fix: #1109 support telegra.ph login --- .../api/telegraph/telegraphApiAdaptor.ts | 149 ++++++++++-------- src/utils/cookieUtils.ts | 30 ++-- 2 files changed, 101 insertions(+), 78 deletions(-) diff --git a/src/adaptors/api/telegraph/telegraphApiAdaptor.ts b/src/adaptors/api/telegraph/telegraphApiAdaptor.ts index b8c19e76..23834d41 100644 --- a/src/adaptors/api/telegraph/telegraphApiAdaptor.ts +++ b/src/adaptors/api/telegraph/telegraphApiAdaptor.ts @@ -42,8 +42,25 @@ class TelegraphApiAdaptor extends BaseBlogApi { public async getUsersBlogs(): Promise { const result: UserBlog[] = [] + const contentType = "text/plain" + let xCorsHeaders: Record = {} + + // x-cors-headers + xCorsHeaders["origin"] = "https://telegra.ph" + xCorsHeaders["referer"] = "https://telegra.ph/" + xCorsHeaders["Content-Type"] = contentType + + 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 + } + let cookies: any - let checkJson = await this.telegraphFetch("/check", "page_id=0", "POST") + let checkJson = await this.telegraphFetch("/check", "page_id=0", "POST", headers, contentType) this.logger.debug("checkJson =>", checkJson) if (checkJson["cors-received-headers"]) { @@ -75,11 +92,17 @@ 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: newCookies, + const { isUpdated, cookieArray } = CookieUtils.addCookieArray(this.cfg?.corsCookieArray ?? [], cookies) + // save_hash 必须和 Cookie 的有效期同时有效 + if (isUpdated) { + userblog.metadataMap = { + password: checkJson.save_hash, + corsCookieArray: cookieArray, + } + } else { + this.logger.warn("Cookie 还在有效期,可等待 Cookie 过期之后再进行操作", cookieArray) } + result.push(userblog) this.logger.debug("get telegraph cfg =>", result) @@ -87,6 +110,28 @@ class TelegraphApiAdaptor extends BaseBlogApi { } public async newPost(post: Post, _publish?: boolean): Promise { + let xCorsHeaders: Record = {} + + // x-cors-headers + const tphUuidObj = CookieUtils.getCookieObject(this.cfg.corsCookieArray, this.TPH_UUID_KEY) + if (StrUtil.isEmptyString(tphUuidObj[this.TPH_UUID_KEY])) { + throw new Error("Cookie 获取失败,无法新建文章") + } + + const requestCookie = `${this.TPH_UUID_KEY}=${tphUuidObj[this.TPH_UUID_KEY]}` + xCorsHeaders["Cookie"] = requestCookie + xCorsHeaders["origin"] = "https://telegra.ph" + 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 + } + // 这里不用这个,因为 telegraph 必须强制代理 // const { FormData, Blob } = FormDataUtils.getFormData(this.appInstance) @@ -99,7 +144,7 @@ class TelegraphApiAdaptor extends BaseBlogApi { formData.append("save_hash", this.cfg.password) formData.append("page_id", "0") - const res = await this.telegraphFormFetch("/save", formData) + const res = await this.telegraphFormFetch("/save", formData, headers) if (res.error) { throw new Error( "telegra.ph 发布错误,注意:切换设备(包括从PC到浏览器环境)需要重新验证,并且获取新token。详细错误 =>" + @@ -109,6 +154,7 @@ class TelegraphApiAdaptor extends BaseBlogApi { this.logger.debug("telegraph newPost resJson =>", res) const postMeta = { + update_cookie: requestCookie, page_id: res.page_id, path: res.path, } @@ -117,10 +163,31 @@ 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, {}) + let xCorsHeaders: Record = {} + + // x-cors-headers + if (StrUtil.isEmptyString(postMeta.update_cookie)) { + throw new Error("Cookie 获取失败,无法更新文章") + } + + xCorsHeaders["Cookie"] = postMeta.update_cookie + xCorsHeaders["origin"] = "https://telegra.ph" + 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 + } + + // 这里不用这个,因为 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" }) @@ -130,7 +197,7 @@ class TelegraphApiAdaptor extends BaseBlogApi { formData.append("save_hash", this.cfg.password) formData.append("page_id", postMeta.page_id) - const res = await this.telegraphFormFetch("/save", formData) + const res = await this.telegraphFormFetch("/save", formData, headers) if (res.error) { throw new Error( "telegra.ph 更新失败,注意:切换设备(包括从PC到浏览器环境)需要重新验证,并且获取新token。详细错误 =>" + @@ -174,42 +241,9 @@ class TelegraphApiAdaptor extends BaseBlogApi { url: string, params?: any, method: "GET" | "POST" | "PUT" | "DELETE" = "GET", - header: Record = {} + headers: Record = {}, + contentType: string = "application/json" ) { - 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])) { - 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 = { - // 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 // 输出日志 @@ -219,7 +253,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, false) + const resJson = await this.apiProxyFetch(apiUrl, [headers], body, method, contentType, true) this.logger.debug("向 Telegraph 请求数据,resJson =>", resJson) return resJson ?? null @@ -230,29 +264,10 @@ class TelegraphApiAdaptor extends BaseBlogApi { * * @param url 请求地址 * @param formData 表单数据,默认为undefined,支持 ReadableStream、Blob | BufferSource | FormData | URLSearchParams | string。这里只需要 FormData + * @param headers */ - private async telegraphFormFetch(url: string, formData: FormData) { + private async telegraphFormFetch(url: string, formData: FormData, headers: Record = {}) { const apiUrl = `${this.cfg.apiUrl}${url}` - 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])) { - xCorsHeaders["Cookie"] = `${this.TPH_UUID_KEY}=${tphUuidObj[this.TPH_UUID_KEY]}` - } - xCorsHeaders["origin"] = "https://telegra.ph" - 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", @@ -263,7 +278,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, false) + const resJson = await this.apiProxyFormFetch(apiUrl, [headers], formData, true) if (resJson.error) { throw new Error( "telegra.ph 发布错误,注意:切换设备(包括从PC到浏览器环境)需要重新验证,并且获取新token。详细错误 =>" + diff --git a/src/utils/cookieUtils.ts b/src/utils/cookieUtils.ts index 6a6a0d9b..ee578126 100644 --- a/src/utils/cookieUtils.ts +++ b/src/utils/cookieUtils.ts @@ -39,8 +39,10 @@ class CookieUtils { * * @param originCookieArray * @param newCookieArray + * @param isForce */ - public static addCookieArray(originCookieArray: string[], newCookieArray: string[]) { + public static addCookieArray(originCookieArray: string[], newCookieArray: string[], isForce: boolean = false) { + let isUpdated = false newCookieArray.forEach((newItem) => { const [key] = newItem.split("=") const newCookieObj = this.parseCookie(newItem) @@ -48,21 +50,27 @@ class CookieUtils { // 检查是否存在相同 key 的 cookie,并比较有效期 const existingIndex = originCookieArray.findIndex((item) => item.startsWith(`${key}=`)) if (existingIndex !== -1) { - const existingCookie = this.parseCookie(originCookieArray[existingIndex]) - // 若新 cookie 的有效期大于旧的,则更新 - if ( - !StrUtil.isEmptyString(newCookieObj.expires) && - !StrUtil.isEmptyString(existingCookie.expires) && - new Date(newCookieObj.expires).getTime() > new Date(existingCookie.expires).getTime() - ) { - originCookieArray[existingIndex] = newItem - } + // const existingCookie = this.parseCookie(originCookieArray[existingIndex]) + // // 若新 cookie 的有效期大于旧的,则更新 + // if ( + // (!StrUtil.isEmptyString(newCookieObj.expires) && + // !StrUtil.isEmptyString(existingCookie.expires) && + // new Date(newCookieObj.expires).getTime() > new Date(existingCookie.expires).getTime()) || + // isForce + // ) { + // originCookieArray[existingIndex] = newItem + isUpdated = false + // } } else { originCookieArray.push(newItem) + isUpdated = true } }) - return _.uniq(originCookieArray) + return { + isUpdated, + cookieArray: _.uniq(originCookieArray), + } } /**