diff --git a/components.d.ts b/components.d.ts index f8fba32..51424db 100644 --- a/components.d.ts +++ b/components.d.ts @@ -58,6 +58,7 @@ declare module '@vue/runtime-core' { SetIndex: typeof import('./src/components/set/SetIndex.vue')['default'] SettingEntry: typeof import('./src/components/set/publish/singleplatform/SettingEntry.vue')['default'] SiyuanTest: typeof import('./src/components/test/SiyuanTest.vue')['default'] + TypechoSetting: typeof import('./src/components/set/publish/singleplatform/metaweblog/TypechoSetting.vue')['default'] TypechoTest: typeof import('./src/components/test/TypechoTest.vue')['default'] VitepressTest: typeof import('./src/components/test/VitepressTest.vue')['default'] WordpressSetting: typeof import('./src/components/set/publish/singleplatform/metaweblog/WordpressSetting.vue')['default'] diff --git a/src/adaptors/api/typecho/adaptor/typechoApiAdaptor.ts b/src/adaptors/api/typecho/adaptor/typechoApiAdaptor.ts new file mode 100644 index 0000000..0c5ad4b --- /dev/null +++ b/src/adaptors/api/typecho/adaptor/typechoApiAdaptor.ts @@ -0,0 +1,72 @@ +/* + * 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 { BlogApi, UserBlog } from "zhi-blog-api" +import { CommonXmlrpcClient } from "zhi-xmlrpc-middleware" +import { AppInstance } from "~/src/appInstance.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { TypechoConfig } from "~/src/adaptors/api/typecho/config/typechoConfig.ts" +import { TypechoConstants } from "~/src/adaptors/api/typecho/typechoConstants.ts" + +/** + * WordPress API 适配器 + * + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class TypechoApiAdaptor extends BlogApi { + private readonly logger + private readonly cfg: TypechoConfig + private readonly commonXmlrpcClient + + /** + * 初始化 WordPress API 适配器 + * + * @param appInstance 应用实例 + * @param cfg 配置项 + */ + constructor(appInstance: AppInstance, cfg: TypechoConfig) { + super() + + this.cfg = cfg + this.logger = createAppLogger("typecho-api-adaptor") + this.commonXmlrpcClient = new CommonXmlrpcClient(appInstance, cfg.apiUrl) + } + + public override async getUsersBlogs(): Promise> { + let result: UserBlog[] = [] + result = await this.typechoCall(TypechoConstants.METHOD_GET_USERS_BLOGS, []) + this.logger.debug("getUsersBlogs=>", result) + return result + } + + private async typechoCall(method: string, params: string[]) { + const parameters = ["typecho", this.cfg.username, this.cfg.password] + params.forEach((param) => parameters.push(param)) + return await this.commonXmlrpcClient.methodCall(method, parameters, this.cfg.middlewareUrl) + } +} +export { TypechoApiAdaptor } diff --git a/src/adaptors/api/typecho/config/typechoConfig.ts b/src/adaptors/api/typecho/config/typechoConfig.ts new file mode 100644 index 0000000..5123dea --- /dev/null +++ b/src/adaptors/api/typecho/config/typechoConfig.ts @@ -0,0 +1,76 @@ +/* + * 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 { MetaweblogConfig } from "~/src/adaptors/api/base/metaweblog/config/MetaweblogConfig.ts" +import { PageTypeEnum } from "zhi-blog-api" +import TypechoUtils from "~/src/adaptors/api/Typecho/TypechoUtils.ts" + +/** + * Typecho 配置 + * + * @author terwer + * @since 1.0.0 + */ +class TypechoConfig extends MetaweblogConfig { + /** + * API 地址 + */ + public override apiUrl = "" + + /** + * 用户名 + */ + public override username = "" + + /** + * 密码 + */ + public override password = "" + + /** + * 代理地址 + */ + public override middlewareUrl = "" + + /** + * Typecho 配置项 + * + * @param homeAddr Typecho 主页 + * @param username 用户名 + * @param password 密码 + * @param middlewareUrl 代理地址 + */ + constructor(homeAddr: string, username: string, password: string, middlewareUrl?: string) { + super(homeAddr, "", username, password, middlewareUrl) + + const { home, apiUrl } = TypechoUtils.parseHomeAndUrl(homeAddr) + this.home = home + this.apiUrl = apiUrl + this.previewUrl = "/?p=[postid]" + this.pageType = PageTypeEnum.Markdown + } +} + +export { TypechoConfig } diff --git a/src/adaptors/api/typecho/config/typechoPlaceholder.ts b/src/adaptors/api/typecho/config/typechoPlaceholder.ts new file mode 100644 index 0000000..2bd7c70 --- /dev/null +++ b/src/adaptors/api/typecho/config/typechoPlaceholder.ts @@ -0,0 +1,33 @@ +/* + * 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 { MetaweblogPlaceholder } from "~/src/adaptors/api/base/metaweblog/config/MetaweblogPlaceholder.ts" + +/** + * WordPress 操作提示 + */ +class TypechoPlaceholder extends MetaweblogPlaceholder {} + +export { TypechoPlaceholder } diff --git a/src/adaptors/api/typecho/docs.md b/src/adaptors/api/typecho/docs.md new file mode 100644 index 0000000..c8514e5 --- /dev/null +++ b/src/adaptors/api/typecho/docs.md @@ -0,0 +1 @@ +# TypechoApiAdaptor \ No newline at end of file diff --git a/src/adaptors/api/typecho/typechoConstants.ts b/src/adaptors/api/typecho/typechoConstants.ts new file mode 100644 index 0000000..9c8c725 --- /dev/null +++ b/src/adaptors/api/typecho/typechoConstants.ts @@ -0,0 +1,37 @@ +/* + * 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. + */ + +/** + * 预定义 Typecho 变量 + * + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class TypechoConstants { + public static METHOD_GET_USERS_BLOGS = "metaWeblog.getUsersBlogs" +} + +export { TypechoConstants } diff --git a/src/adaptors/api/typecho/typechoUtils.ts b/src/adaptors/api/typecho/typechoUtils.ts new file mode 100644 index 0000000..65aee6a --- /dev/null +++ b/src/adaptors/api/typecho/typechoUtils.ts @@ -0,0 +1,56 @@ +/* + * 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 { createAppLogger } from "~/src/utils/appLogger.ts" + +/** + * 用于处理Typecho相关操作的实用工具类 + */ +class TypechoUtils { + private static logger = createAppLogger("typecho-utils") + + /** + * 解析给定的主页地址并生成相应的apiUrl地址 + * + * @param home - 主页地址 + */ + public static parseHomeAndUrl(home: string): { home: string; apiUrl: string } { + this.logger.debug(`Parsing Home address: ${home}`) + // 解析主页地址 + let apiUrl = "" + if (home.endsWith("/index.php/action/xmlrpc")) { + apiUrl = home + home = home.replace("/index.php/action/xmlrpc", "") + } else { + home = home.replace(/\/$/, "") + apiUrl = `${home}/index.php/action/xmlrpc` + } + + this.logger.debug(`Parse result: home=${home}, apiUrl=${apiUrl}`) + return { home, apiUrl } + } +} + +export default TypechoUtils diff --git a/src/adaptors/api/wordpress/docs.md b/src/adaptors/api/wordpress/docs.md new file mode 100644 index 0000000..8e0cf2b --- /dev/null +++ b/src/adaptors/api/wordpress/docs.md @@ -0,0 +1 @@ +# WordpressApiAdaptor \ No newline at end of file diff --git a/src/adaptors/index.ts b/src/adaptors/index.ts index ef8e750..35a0a5a 100644 --- a/src/adaptors/index.ts +++ b/src/adaptors/index.ts @@ -23,12 +23,12 @@ * questions. */ -import { BlogAdaptor } from "zhi-blog-api" -import { useSiyuanApi } from "~/src/composables/api/useSiyuanApi.ts" -import { getSubPlatformTypeByKey, SubPlatformType } from "~/src/components/set/publish/platform/dynamicConfig.ts" -import { useCnblogsApi } from "~/src/composables/api/useCnblogsApi.ts" -import { createAppLogger } from "~/src/utils/appLogger.ts" +import {BlogAdaptor} from "zhi-blog-api" +import {getSubPlatformTypeByKey, SubPlatformType} from "~/src/components/set/publish/platform/dynamicConfig.ts" +import {useCnblogsApi} from "~/src/composables/api/useCnblogsApi.ts" +import {createAppLogger} from "~/src/utils/appLogger.ts" import {useWordpressApi} from "~/src/composables/api/useWordpressApi.ts"; +import {useTypechoApi} from "~/src/composables/api/useTypechoApi.ts"; /** * 适配器统一入口 @@ -59,6 +59,11 @@ class Adaptors { blogAdaptor = blogApi break } + case SubPlatformType.Metaweblog_Typecho:{ + const { blogApi } = await useTypechoApi(key) + blogAdaptor = blogApi + break + } default: { break } diff --git a/src/components/set/publish/singleplatform/SettingEntry.vue b/src/components/set/publish/singleplatform/SettingEntry.vue index 51bb555..6e4ef28 100644 --- a/src/components/set/publish/singleplatform/SettingEntry.vue +++ b/src/components/set/publish/singleplatform/SettingEntry.vue @@ -31,6 +31,7 @@ import { useVueI18n } from "~/src/composables/useVueI18n.ts" import { getSubPlatformTypeByKey, SubPlatformType } from "~/src/components/set/publish/platform/dynamicConfig.ts" import CnblogsSetting from "~/src/components/set/publish/singleplatform/metaweblog/CnblogsSetting.vue" import WordpressSetting from "~/src/components/set/publish/singleplatform/metaweblog/WordpressSetting.vue" +import TypechoSetting from "~/src/components/set/publish/singleplatform/metaweblog/TypechoSetting.vue" // uses const { t } = useVueI18n() @@ -46,6 +47,7 @@ const subtype = getSubPlatformTypeByKey(apiType) + diff --git a/src/components/set/publish/singleplatform/metaweblog/TypechoSetting.vue b/src/components/set/publish/singleplatform/metaweblog/TypechoSetting.vue new file mode 100644 index 0000000..bd751db --- /dev/null +++ b/src/components/set/publish/singleplatform/metaweblog/TypechoSetting.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/src/components/test/TypechoTest.vue b/src/components/test/TypechoTest.vue index ca50d4c..69275c4 100644 --- a/src/components/test/TypechoTest.vue +++ b/src/components/test/TypechoTest.vue @@ -31,8 +31,9 @@ import { fileToBuffer } from "~/src/utils/polyfillUtils.ts" import { SimpleXmlRpcClient } from "simple-xmlrpc" import { MediaObject } from "zhi-blog-api" import { createAppLogger } from "~/src/utils/appLogger.ts" +import Adaptors from "~/src/adaptors" -const logger = createAppLogger("wordpress-test") +const logger = createAppLogger("typecho-test") const params = ref("{}") const showParamFile = ref(false) @@ -178,10 +179,10 @@ const onImageSelect = async (event: Event) => { } } -const wordpressHandleApi = async () => { +const typechoHandleApi = async () => { isLoading.value = true logMessage.value = "" - logMessage.value = "wordpress requesting..." + logMessage.value = "typecho requesting..." try { // appInstance const appInstance = new AppInstance() @@ -189,8 +190,8 @@ const wordpressHandleApi = async () => { switch (methodOption.value) { case METHOD_GET_USERS_BLOGS: { - const typechoCfg = {} - const typechoApiAdaptor = {} + const key = "metaweblog_Typecho" + const typechoApiAdaptor = await Adaptors.getAdaptor(key) const typechoApi = Utils.blogApi(appInstance, typechoApiAdaptor) const result = await typechoApi.getUsersBlogs() logMessage.value = JSON.stringify(result) @@ -238,7 +239,7 @@ const wordpressHandleApi = async () => { const client = new SimpleXmlRpcClient(xmlrpcApiUrl, "", {}) const result = await client.methodCall("metaWeblog.newMediaObject", ["", "terwer", "123456", metadata]) logMessage.value = JSON.stringify(result) - logger.info("wordpress new mediaObject result=>", result) + logger.info("typecho new mediaObject result=>", result) break } default: @@ -255,7 +256,7 @@ const wordpressHandleApi = async () => { \ No newline at end of file + diff --git a/src/composables/api/useTypechoApi.ts b/src/composables/api/useTypechoApi.ts new file mode 100644 index 0000000..f751abe --- /dev/null +++ b/src/composables/api/useTypechoApi.ts @@ -0,0 +1,83 @@ +/* + * 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 { createAppLogger } from "~/src/utils/appLogger.ts" +import { AppInstance } from "~/src/appInstance.ts" +import { Utils } from "~/src/utils/utils.ts" +import { useSettingStore } from "~/src/stores/useSettingStore.ts" +import { TypechoConfig } from "~/src/adaptors/api/typecho/config/typechoConfig.ts" +import { JsonUtil, ObjectUtil } from "zhi-common" +import { getDynPostidKey } from "~/src/components/set/publish/platform/dynamicConfig.ts" +import { TypechoApiAdaptor } from "~/src/adaptors/api/typecho/adaptor/typechoApiAdaptor.ts" + +/** + * 使用Typecho API的自定义hook + * + * @param key 配置键值,可选参数 + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +export const useTypechoApi = async (key?: string) => { + // 创建应用日志记录器 + const logger = createAppLogger("use-typecho-api") + + // 记录开始使用Typecho API + logger.info("Start using Typecho API...") + + // 创建应用实例 + const appInstance = new AppInstance() + + // 从环境变量获取Typecho API的URL、用户名、认证令牌和中间件URL + const typechoApiUrl = Utils.emptyOrDefault(process.env.VITE_TYPECHO_API_URL, "http://your-typecho-home.com/") + const typechoUsername = Utils.emptyOrDefault(process.env.VITE_TYPECHO_USERNAME, "") + const typechoAuthToken = Utils.emptyOrDefault(process.env.VITE_TYPECHO_AUTH_TOKEN, "") + const middlewareUrl = Utils.emptyOrDefault(process.env.VITE_MIDDLEWARE_URL, "http://localhost:3000/api/middleware") + + // 从配置中获取数据 + const { getSetting } = useSettingStore() + const setting = await getSetting() + let cfg: TypechoConfig = JsonUtil.safeParse(setting[key], {} as TypechoConfig) + // 如果配置为空,则使用默认的环境变量值,并记录日志 + if (ObjectUtil.isEmptyObject(cfg)) { + cfg = new TypechoConfig(typechoApiUrl, typechoUsername, typechoAuthToken, middlewareUrl) + logger.debug("Configuration is empty, using default environment variables.") + } else { + logger.info("Using configuration from settings...") + } + // 默认值 + cfg.posidKey = getDynPostidKey(key) + + // 创建Typecho API适配器 + const blogApi = new TypechoApiAdaptor(appInstance, cfg) + + // 记录Typecho API创建成功 + logger.info("Typecho API created successfully.", cfg) + + return { + cfg, + blogApi, + } +} diff --git a/src/composables/api/useWordpressApi.ts b/src/composables/api/useWordpressApi.ts index 78723e2..4b9b980 100644 --- a/src/composables/api/useWordpressApi.ts +++ b/src/composables/api/useWordpressApi.ts @@ -51,7 +51,7 @@ export const useWordpressApi = async (key?: string) => { const appInstance = new AppInstance() // 从环境变量获取Wordpress API的URL、用户名、认证令牌和中间件URL - const wordpressApiUrl = Utils.emptyOrDefault(process.env.VITE_WORDPRESS_API_URL, "http://your-wordpress-api-url.com/xmlrpc.php") + const wordpressApiUrl = Utils.emptyOrDefault(process.env.VITE_WORDPRESS_API_URL, "http://your-wordpress-home.com") const wordpressUsername = Utils.emptyOrDefault(process.env.VITE_WORDPRESS_USERNAME, "") const wordpressAuthToken = Utils.emptyOrDefault(process.env.VITE_WORDPRESS_AUTH_TOKEN, "") const middlewareUrl = Utils.emptyOrDefault(process.env.VITE_MIDDLEWARE_URL, "http://localhost:3000/api/middleware") diff --git a/src/composables/useVueRouter.ts b/src/composables/useVueRouter.ts index 6f80479..ffb6815 100644 --- a/src/composables/useVueRouter.ts +++ b/src/composables/useVueRouter.ts @@ -37,6 +37,7 @@ const ApiTest = () => import("~/src/pages/ApiTest.vue") const SiyuanTest = () => import("~/src/components/test/SiyuanTest.vue") const CnblogsTest = () => import("~/src/components/test/CnblogsTest.vue") const WordpressTest = () => import("~/src/components/test/WordpressTest.vue") +const TypechoTest = () => import("~/src/components/test/TypechoTest.vue") const YuqueTest = () => import("~/src/components/test/YuqueTest.vue") const ZhihuTest = () => import("~/src/components/test/ZhihuTest.vue") @@ -50,6 +51,7 @@ const routes: RouteRecordRaw[] = [ { path: "siyuan", component: SiyuanTest }, { path: "cnblogs", component: CnblogsTest }, { path: "wordpress", component: WordpressTest }, + { path: "typecho", component: TypechoTest }, { path: "yuque", component: YuqueTest }, { path: "zhihu", component: ZhihuTest }, ], diff --git a/src/pages/ApiTest.vue b/src/pages/ApiTest.vue index d7db4be..2da0f8b 100644 --- a/src/pages/ApiTest.vue +++ b/src/pages/ApiTest.vue @@ -53,6 +53,9 @@ const changeTab = (tab: TabsPaneContext, event: Event) => { + + +