diff --git a/README_zh_CN.md b/README_zh_CN.md index d04e8005..f8d78d37 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -59,7 +59,7 @@ ## 核心特色 - [X] **极速发布**:一次配置,一键发布 -- [ ] **图床管理**:集成 PicGO 图床,支持 s3、minio、水印插件 +- [ ] **图床管理**:集成 PicGO 图床,支持 s3、minio、水印插件 [仅支持PC客户端] - [X] **支持扩展**:基于统一的博客 API 规范,内置 metaweblogAPI 、 WordPress 、Wechatsync 和 Github 支持,并提供了统一的适配器,理论上可支持扩展到任何平台 - [X] **平台开关**:所有平台均支持启用禁用,默认开启博客园,可随时禁用 - [X] **动态新增**:支持自定义添加平台 diff --git a/esbuild.config.cjs b/esbuild.config.cjs index 1cde9f2f..6294374e 100644 --- a/esbuild.config.cjs +++ b/esbuild.config.cjs @@ -33,8 +33,8 @@ const isServe = args.serve || args.s || false let baseDir if (isWatch || isServe) { - // baseDir = "/Users/terwer/Documents/mydocs/SiYuanWorkspace/test/data/plugins/siyuan-plugin-publisher" - baseDir = "/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/data/plugins/siyuan-plugin-publisher" + baseDir = "/Users/terwer/Documents/mydocs/SiYuanWorkspace/test/data/plugins/siyuan-plugin-publisher" + // baseDir = "/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/data/plugins/siyuan-plugin-publisher" } else { baseDir = "./" } diff --git a/package.json b/package.json index 24d26508..896c9f21 100644 --- a/package.json +++ b/package.json @@ -62,19 +62,20 @@ "shorthash2": "^1.0.3", "simple-xmlrpc": "^1.4.2", "siyuan": "^0.7.9", + "siyuan-plugin-picgo": "^1.2.0", "uuid": "^9.0.0", "vue": "^3.3.4", "vue-i18n": "^9.2.2", "vue-router": "^4.2.4", "xmlbuilder2": "^3.1.1", "zhi-blog-api": "^1.20.17", - "zhi-common": "^1.14.1", + "zhi-common": "^1.14.2", "zhi-device": "^2.3.0", - "zhi-fetch-middleware": "^0.2.19", - "zhi-github-middleware": "^0.2.1", + "zhi-fetch-middleware": "^0.2.20", + "zhi-github-middleware": "^0.2.2", "zhi-lib-base": "^0.4.4", "zhi-notion-markdown": "^0.1.2", - "zhi-siyuan-api": "^2.0.19", - "zhi-xmlrpc-middleware": "^0.4.14" + "zhi-siyuan-api": "^2.0.21", + "zhi-xmlrpc-middleware": "^0.4.15" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 24b18bbc..8ea6d029 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ dependencies: siyuan: specifier: ^0.7.9 version: 0.7.9 + siyuan-plugin-picgo: + specifier: ^1.2.0 + version: 1.2.0(typescript@5.1.6) uuid: specifier: ^9.0.0 version: 9.0.0 @@ -57,17 +60,17 @@ dependencies: specifier: ^1.20.17 version: 1.20.17 zhi-common: - specifier: ^1.14.1 - version: 1.14.1 + specifier: ^1.14.2 + version: 1.14.2 zhi-device: specifier: ^2.3.0 version: 2.3.0 zhi-fetch-middleware: - specifier: ^0.2.19 - version: 0.2.19 + specifier: ^0.2.20 + version: 0.2.20 zhi-github-middleware: - specifier: ^0.2.1 - version: 0.2.1 + specifier: ^0.2.2 + version: 0.2.2 zhi-lib-base: specifier: ^0.4.4 version: 0.4.4 @@ -75,11 +78,11 @@ dependencies: specifier: ^0.1.2 version: 0.1.2 zhi-siyuan-api: - specifier: ^2.0.19 - version: 2.0.19 + specifier: ^2.0.21 + version: 2.0.21 zhi-xmlrpc-middleware: - specifier: ^0.4.14 - version: 0.4.14 + specifier: ^0.4.15 + version: 0.4.15 devDependencies: '@terwer/esbuild-config-custom': @@ -727,6 +730,46 @@ packages: resolution: {integrity: sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==} dev: false + /@fortawesome/fontawesome-common-types@6.4.2: + resolution: {integrity: sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==} + engines: {node: '>=6'} + requiresBuild: true + dev: false + + /@fortawesome/fontawesome-svg-core@6.4.2: + resolution: {integrity: sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==} + engines: {node: '>=6'} + requiresBuild: true + dependencies: + '@fortawesome/fontawesome-common-types': 6.4.2 + dev: false + + /@fortawesome/free-brands-svg-icons@6.4.2: + resolution: {integrity: sha512-LKOwJX0I7+mR/cvvf6qIiqcERbdnY+24zgpUSouySml+5w8B4BJOx8EhDR/FTKAu06W12fmUIcv6lzPSwYKGGg==} + engines: {node: '>=6'} + requiresBuild: true + dependencies: + '@fortawesome/fontawesome-common-types': 6.4.2 + dev: false + + /@fortawesome/free-solid-svg-icons@6.4.2: + resolution: {integrity: sha512-sYwXurXUEQS32fZz9hVCUUv/xu49PEJEyUOsA51l6PU/qVgfbTb2glsTEaJngVVT8VqBATRIdh7XVgV1JF1LkA==} + engines: {node: '>=6'} + requiresBuild: true + dependencies: + '@fortawesome/fontawesome-common-types': 6.4.2 + dev: false + + /@fortawesome/vue-fontawesome@3.0.3(@fortawesome/fontawesome-svg-core@6.4.2)(vue@3.3.4): + resolution: {integrity: sha512-KCPHi9QemVXGMrfuwf3nNnNo129resAIQWut9QTAMXmXqL2ErABC6ohd2yY5Ipq0CLWNbKHk8TMdTXL/Zf3ZhA==} + peerDependencies: + '@fortawesome/fontawesome-svg-core': ~1 || ~6 + vue: '>= 3.0.0 < 4' + dependencies: + '@fortawesome/fontawesome-svg-core': 6.4.2 + vue: 3.3.4 + dev: false + /@humanwhocodes/config-array@0.11.10: resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} engines: {node: '>=10.10.0'} @@ -1220,11 +1263,11 @@ packages: /@types/lodash-es@4.17.8: resolution: {integrity: sha512-euY3XQcZmIzSy7YH5+Unb3b2X12Wtk54YWINBvvGQ5SmMvwb11JQskGsfkH/5HXK77Kr8GF0wkVDIxzAisWtog==} dependencies: - '@types/lodash': 4.14.196 + '@types/lodash': 4.14.197 dev: false - /@types/lodash@4.14.196: - resolution: {integrity: sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==} + /@types/lodash@4.14.197: + resolution: {integrity: sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==} dev: false /@types/mdast@3.0.12: @@ -1262,7 +1305,7 @@ packages: resolution: {integrity: sha512-jn7qwGFmJHwUSphV8zZneO3GmtlgLsmhs/LQyVvQbIIa+fzGMUiHI4HXJZL3FT8MJmgXWbLGiVVY7ElvHq6vDA==} deprecated: This is a stub types definition. sass provides its own type definitions, so you do not need this installed. dependencies: - sass: 1.64.2 + sass: 1.65.1 dev: true /@types/semver@7.5.0: @@ -2179,6 +2222,10 @@ packages: file-uri-to-path: 1.0.0 dev: true + /blueimp-md5-es6@2.7.4: + resolution: {integrity: sha512-q7+/MX+2BXdOY0DanEdHSbY5TeTSuM7PotaHmvng9fqEPxNhLVLuj849Vag5x7vAmvZBzxBhh780gq6rJxT/rA==} + dev: false + /bn.js@4.12.0: resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} dev: true @@ -2221,6 +2268,10 @@ packages: resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} dev: true + /browser-fs-access@0.34.1: + resolution: {integrity: sha512-HPaRf2yimp8kWSuWJXc8Mi78dPbDzfduA+Gyq14H4jlMvd6XNfIRm36Y2yRLaa4x0gwcGuepj4zf14oiTlxrxQ==} + dev: false + /browser-resolve@2.0.0: resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} dependencies: @@ -2929,7 +2980,7 @@ packages: '@element-plus/icons-vue': 2.1.0(vue@3.3.4) '@floating-ui/dom': 1.5.1 '@popperjs/core': /@sxzz/popperjs-es@2.11.7 - '@types/lodash': 4.14.196 + '@types/lodash': 4.14.197 '@types/lodash-es': 4.17.8 '@vueuse/core': 9.13.0(vue@3.3.4) async-validator: 4.2.5 @@ -4024,7 +4075,7 @@ packages: hasBin: true dependencies: foreground-child: 3.1.1 - jackspeak: 2.2.2 + jackspeak: 2.2.3 minimatch: 9.0.3 minipass: 7.0.2 path-scurry: 1.10.1 @@ -4496,8 +4547,8 @@ packages: istanbul-lib-report: 3.0.1 dev: true - /jackspeak@2.2.2: - resolution: {integrity: sha512-mgNtVv4vUuaKA97yxUHoA3+FkuhtxkjdXEWOyB/N76fjy0FjezEt34oy3epBtvCvS+7DyKwqCFWx/oJLV5+kCg==} + /jackspeak@2.2.3: + resolution: {integrity: sha512-pF0kfjmg8DJLxDrizHoCZGUFz4P4czQ3HyfW4BU0ffebYkzAVlBywp5zaxW/TM+r0sGbmrQdi8EQQVTJFxnGsQ==} engines: {node: '>=14'} dependencies: '@isaacs/cliui': 8.0.2 @@ -4743,8 +4794,8 @@ packages: tslib: 2.6.1 dev: true - /lru-cache@10.0.0: - resolution: {integrity: sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==} + /lru-cache@10.0.1: + resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==} engines: {node: 14 || >=16.14} dev: true @@ -5456,7 +5507,7 @@ packages: resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} engines: {node: '>=16 || 14 >=14.17'} dependencies: - lru-cache: 10.0.0 + lru-cache: 10.0.1 minipass: 7.0.2 dev: true @@ -5948,8 +5999,8 @@ packages: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: true - /sass@1.64.2: - resolution: {integrity: sha512-TnDlfc+CRnUAgLO9D8cQLFu/GIjJIzJCGkE7o4ekIGQOH7T3GetiRR/PsTWJUHhkzcSPrARkPI+gNWn5alCzDg==} + /sass@1.65.1: + resolution: {integrity: sha512-9DINwtHmA41SEd36eVPQ9BJKpn7eKDQmUHmpI0y5Zv2Rcorrh0zS+cFrt050hdNbmmCNKTW3hV5mWfuegNRsEA==} engines: {node: '>=14.0.0'} hasBin: true dependencies: @@ -6058,6 +6109,36 @@ packages: sax: 1.2.4 dev: false + /siyuan-plugin-picgo@1.2.0(typescript@5.1.6): + resolution: {integrity: sha512-FrEo8XbJeMS5XsD1BGdIqaNFGi2RcVcmMOif629ZBFNqyCPfsziQVgpfIlbFE5jWkPh5dWrO8ZnUR6BIVb9bBw==} + dependencies: + '@element-plus/icons-vue': 2.1.0(vue@3.3.4) + '@fortawesome/fontawesome-svg-core': 6.4.2 + '@fortawesome/free-brands-svg-icons': 6.4.2 + '@fortawesome/free-solid-svg-icons': 6.4.2 + '@fortawesome/vue-fontawesome': 3.0.3(@fortawesome/fontawesome-svg-core@6.4.2)(vue@3.3.4) + '@vueuse/core': 10.3.0(vue@3.3.4) + blueimp-md5-es6: 2.7.4 + browser-fs-access: 0.34.1 + element-plus: 2.3.9(vue@3.3.4) + lodash: 4.17.21 + lodash-es: 4.17.21 + pinia: 2.1.6(typescript@5.1.6)(vue@3.3.4) + siyuan: 0.7.9 + uuid: 9.0.0 + vue: 3.3.4 + vue-i18n: 9.2.2(vue@3.3.4) + vue-router: 4.2.4(vue@3.3.4) + zhi-common: 1.14.2 + zhi-device: 2.3.0 + zhi-fetch-middleware: 0.2.20 + zhi-lib-base: 0.4.4 + zhi-siyuan-api: 2.0.21 + transitivePeerDependencies: + - '@vue/composition-api' + - typescript + dev: false + /siyuan@0.7.9: resolution: {integrity: sha512-X5olBQhfkgyPRF+d8agpcbAaFnCSvSkN3LWX7XU9jApmCfpJ/4PAmK7mKPfR1YGBpjFLRa/xTrv11BQ3hxN15Q==} dev: false @@ -7248,8 +7329,8 @@ packages: zhi-lib-base: 0.4.4 dev: false - /zhi-common@1.14.1: - resolution: {integrity: sha512-euAUWXtwrbOx+6IP3w9JLwoHtI9Ntv7bUvLePK1yKpJk/83DzRmRj/ymzl31ppnVYUL/Vkb00OT/VhFiqV2P1w==} + /zhi-common@1.14.2: + resolution: {integrity: sha512-UeGLPvhNF5djrcR5srHrA67hGIL1L5wU8IVuwHWL281aJC5JfB/UE00Np1lGT8R0YwqJsE0jy4u9vf2jfJEjSw==} dependencies: js-yaml: 4.1.0 shorthash2: 1.0.3 @@ -7262,20 +7343,20 @@ packages: resolution: {integrity: sha512-we04mMt4BEqeKDxoqdJQsDFYImYlcp/yaAIkpxpMGg+9tBURfNeHPHIHkFias9VbM8yuk6xjAX6B2xJjvYE/iw==} dev: false - /zhi-fetch-middleware@0.2.19: - resolution: {integrity: sha512-3mtlPPLP3aR+tuFTmQzHs1c0qxUvyUCVs+IQpJOU66Qxckd64m1Qp+F0ZpV4UV/6Xy9wwz/AG4JdT32vsKzxTg==} + /zhi-fetch-middleware@0.2.20: + resolution: {integrity: sha512-WPgvjEwyBxN9wQGncmR3TyEd/mTGzkHj3sJsKHdgkyrJlcxhJK1LPX0v20XKVR4KcHyehDyz3QjDxLSdTwcfLQ==} dependencies: - zhi-common: 1.14.1 + zhi-common: 1.14.2 zhi-device: 2.3.0 zhi-lib-base: 0.4.4 dev: false - /zhi-github-middleware@0.2.1: - resolution: {integrity: sha512-prvIirH/fHEMDRw3nypzrpXDkBux3IECj2KMfy/QanH+XG6dwoQGNvrkVu0wMFtu22VN1AuXNiLAG7+PYpEtlw==} + /zhi-github-middleware@0.2.2: + resolution: {integrity: sha512-Nja/IIPxSGOg1hpxjAeHf0C4scNZReCURLUzU31Dvv4fRU5qdLxhvOkAJVtHUAp6yfg65/1rminSYsFeRMCBWA==} dependencies: '@octokit/core': 5.0.0 js-base64: 3.7.5 - zhi-common: 1.14.1 + zhi-common: 1.14.2 zhi-lib-base: 0.4.4 dev: false @@ -7293,18 +7374,18 @@ packages: - supports-color dev: false - /zhi-siyuan-api@2.0.19: - resolution: {integrity: sha512-Y1HzNoqI95BmqI9fmon7TWzbtcqZr7n2L9eMx3M9TLFrne8IulRRIMKGQuqGKcM08Qiyc40lz1pG8ZIFd9cYtQ==} + /zhi-siyuan-api@2.0.21: + resolution: {integrity: sha512-fGlvQ4Cb62DBY/8r2yMhZohgQOj0LHSp+/3OEIii4LZzN7+a7FBZawzaHSQo/e+Dpr8taZV3pEr0SBG7LyIDOA==} dependencies: zhi-blog-api: 1.20.17 - zhi-common: 1.14.1 + zhi-common: 1.14.2 zhi-lib-base: 0.4.4 dev: false - /zhi-xmlrpc-middleware@0.4.14: - resolution: {integrity: sha512-3S+S/nAhoyQHRqv10jA+QmlR9bip2FpuvKI3tg2dkgxJL/Ek95PpgK1NAi4HjyjqyyoeWJ7frJWo/zAT/YiEaQ==} + /zhi-xmlrpc-middleware@0.4.15: + resolution: {integrity: sha512-XmqKZPyeyO+IU3wwDc1GPghevCFW6Ix9DKWW1MnXC4QsPTsQK8b3UN6OlMKTGqfj2mPle1Kg8H/01AihE7utrA==} dependencies: - zhi-common: 1.14.1 + zhi-common: 1.14.2 zhi-device: 2.3.0 zhi-lib-base: 0.4.4 dev: false diff --git a/src/components/set/PublishSetting.vue b/src/components/set/PublishSetting.vue index 918d86fa..3aebf286 100644 --- a/src/components/set/PublishSetting.vue +++ b/src/components/set/PublishSetting.vue @@ -36,7 +36,6 @@ import { deletePlatformByKey, DynamicConfig, DynamicJsonCfg, - getDynPostidKey, PlatformType, replacePlatformByKey, setDynamicJsonCfg, diff --git a/src/composables/usePicgoBridge.ts b/src/composables/usePicgoBridge.ts new file mode 100644 index 00000000..b4804bf8 --- /dev/null +++ b/src/composables/usePicgoBridge.ts @@ -0,0 +1,92 @@ +/* + * 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 { PicgoPostApi } from "siyuan-plugin-picgo" +import { useSiyuanApi } from "~/src/composables/useSiyuanApi.ts" +import { ElMessage } from "element-plus" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { useVueI18n } from "~/src/composables/useVueI18n.ts" +import { StrUtil } from "zhi-common" + +/** + * Picgo 桥接 API,用于上传并替换图片链接 + * + * @author terwer + * @since 1.6.1 + */ +const usePicgoBridge = () => { + const logger = createAppLogger("use-picgo-bridge") + const { t } = useVueI18n() + const { kernelApi, blogApi } = useSiyuanApi() + const picgoPostApi = new PicgoPostApi(kernelApi) + + /** + * 处理图片上传与替换 + * + * @param pageId - 页面ID + * @param mdContent - 正文,如果为空,会用 pageId 去获取最新 + */ + const handlePicgo = async (pageId: string, mdContent?: string) => { + let md: string = mdContent + + try { + const attrs = await kernelApi.getBlockAttrs(pageId) + const siyuanData = { + pageId: pageId, + meta: attrs, + } + + if (StrUtil.isEmptyString(md)) { + // 思源笔记原始文章数据 + const siyuanPost = await blogApi.getPost(pageId) + md = siyuanPost.markdown + } + + const picgoPostResult = await picgoPostApi.uploadPostImagesToBed(siyuanData.pageId, siyuanData.meta, md) + // 有图片才上传 + if (picgoPostResult.hasImages) { + if (picgoPostResult.flag) { + md = picgoPostResult.mdContent + } else { + logger.warn(t("github.post.picgo.picbed.error") + "=>" + picgoPostResult.errmsg) + ElMessage.warning(t("github.post.picgo.picbed.error") + "=>" + picgoPostResult.errmsg) + } + } else { + logger.info(t("github.post.picgo.picbed.error") + "=>" + picgoPostResult.errmsg) + } + } catch (e) { + logger.error(t("github.post.picgo.picbed.error") + "=>", e) + ElMessage.error(t("github.post.picgo.picbed.error") + "=>" + e) + } + + return md + } + + return { + handlePicgo, + } +} + +export { usePicgoBridge } diff --git a/src/composables/usePublish.ts b/src/composables/usePublish.ts index 462c7bb1..726294cd 100644 --- a/src/composables/usePublish.ts +++ b/src/composables/usePublish.ts @@ -26,8 +26,8 @@ import { createAppLogger } from "~/src/utils/appLogger.ts" import { reactive, toRaw } from "vue" import { SypConfig } from "~/syp.config.ts" -import { AliasTranslator, DateUtil, ObjectUtil, StrUtil } from "zhi-common" -import { BlogAdaptor, BlogConfig, PageTypeEnum, Post, PostStatusEnum } from "zhi-blog-api" +import { AliasTranslator, ObjectUtil, StrUtil } from "zhi-common" +import { BlogAdaptor, PageTypeEnum, Post, PostStatusEnum } from "zhi-blog-api" import { useVueI18n } from "~/src/composables/useVueI18n.ts" import { useSettingStore } from "~/src/stores/useSettingStore.ts" import { useSiyuanApi } from "~/src/composables/useSiyuanApi.ts" @@ -40,6 +40,8 @@ import { usePublishConfig } from "~/src/composables/usePublishConfig.ts" import { YamlConvertAdaptor } from "~/src/platforms/yamlConvertAdaptor.ts" import { YamlFormatObj } from "~/src/models/yamlFormatObj.ts" import { ElMessage } from "element-plus" +import { usePicgoBridge } from "~/src/composables/usePicgoBridge.ts" +import { LuteUtil } from "~/src/utils/luteUtil.ts" /** * 通用发布组件 @@ -56,6 +58,7 @@ const usePublish = () => { const { getSetting, updateSetting } = useSettingStore() const { kernelApi, blogApi } = useSiyuanApi() const { getPublishApi, getYamlApi } = usePublishConfig() + const { handlePicgo } = usePicgoBridge() // datas const singleFormData = reactive({ @@ -65,6 +68,14 @@ const usePublish = () => { errMsg: "", }) + /** + * 统一的发布操作 + * + * @param key - 平台 key + * @param id - 思源笔记的ID + * @param publishCfg - 发布配置 + * @param doc - 思源笔记原始文档 + */ const doSinglePublish = async (key: string, id: string, publishCfg: IPublishCfg, doc: Post) => { const setting: typeof SypConfig = publishCfg.setting const cfg: CommonblogConfig = publishCfg.cfg @@ -93,15 +104,20 @@ const usePublish = () => { postid = ObjectUtil.getProperty(postMeta, posidKey) } singleFormData.isAdd = StrUtil.isEmptyString(postid) + + let post = doc // 保证postid一致 - doc.postid = postid + post.postid = postid + // =================================== + // 文章处理开始 + // =================================== // 分配属性 - doc = await assignAttrs(doc, id, publishCfg) + post = await assignAttrs(post, id, publishCfg) // 全局的预处理 - logger.debug(`before preHandlePost, isAdd ${singleFormData.isAdd}, doc=>`, toRaw(doc)) - const post = preHandlePost(doc, cfg) + logger.debug(`before preHandlePost, isAdd ${singleFormData.isAdd}, doc=>`, toRaw(post)) + post = await preHandlePost(post, id, publishCfg) logger.debug(`after preHandlePost, doc=>`, toRaw(post)) // 平台相关的预处理 @@ -113,6 +129,9 @@ const usePublish = () => { } else { logger.info("yaml adaptor not found, ignore convert") } + // =================================== + // 文章处理结束 + // =================================== // 初始化API const api = await getPublishApi(key, cfg) @@ -129,7 +148,7 @@ const usePublish = () => { const posidKey = cfg.posidKey const postMeta = ObjectUtil.getProperty(setting, id, {}) postMeta[posidKey] = postid - postMeta["custom-slug"] = doc.wp_slug + postMeta["custom-slug"] = post.wp_slug setting[id] = postMeta await updateSetting(setting) @@ -142,7 +161,7 @@ const usePublish = () => { // 写入属性到配置 const postMeta = ObjectUtil.getProperty(setting, id, {}) - postMeta["custom-slug"] = doc.wp_slug + postMeta["custom-slug"] = post.wp_slug setting[id] = postMeta await updateSetting(setting) @@ -172,6 +191,13 @@ const usePublish = () => { } } + /** + * 统一的删除操作 + * + * @param key - 平台 key + * @param id - 思源笔记的ID + * @param publishCfg - 发布配置 + */ const doSingleDelete = async (key: string, id: string, publishCfg: IPublishCfg) => { const setting: typeof SypConfig = publishCfg.setting const cfg: CommonblogConfig = publishCfg.cfg @@ -228,6 +254,13 @@ const usePublish = () => { } } + /** + * 统一的强制删除操作 + * + * @param key - 平台 key + * @param id - 思源笔记的ID + * @param publishCfg - 发布配置 + */ const doForceSingleDelete = async (key: string, id: string, publishCfg: IPublishCfg) => { try { const setting: typeof SypConfig = publishCfg.setting @@ -276,8 +309,26 @@ const usePublish = () => { return isAbsoluteUrl ? previewUrl : `${cfg?.home ?? ""}${previewUrl}` } - const preHandlePost = (doc: Post, cfg: BlogConfig): Post => { - const post = doc + /** + * 文章预处理 - 仅在发布的时候调用 + * + * @param post - 文章对象 + * @param id - 思源笔记文档ID + * @param publishCfg - 发布配置 + */ + const preHandlePost = async (post: Post, id: string, publishCfg: IPublishCfg): Promise => { + const cfg: CommonblogConfig = publishCfg.cfg + + // 图片替换 + logger.debug("开始图片处理, post =>", { post }) + post.markdown = await handlePicgo(id, post.markdown) + // 利用 lute 把 md 转换成 html + post.html = LuteUtil.mdToHtml(post.markdown) + logger.debug("图片处理完毕, post.markdown =>", post.markdown) + + // 平台特定的图片上传 + // 例如知乎不支持链接,需要自行上传 + // 发布格式 if (cfg?.pageType == PageTypeEnum.Markdown) { post.description = post.markdown @@ -290,7 +341,7 @@ const usePublish = () => { // const assignCompareValue = (title1: string, title2: string) => (title1.length > title2.length ? title1 : title2) /** - * 分配属性 + * 分配属性 - 初始化和发布都可能调用 * * @param post - 文章对象 * @param id - 思源笔记文档ID diff --git a/src/composables/useSiyuanApi.ts b/src/composables/useSiyuanApi.ts index 76e1a883..a65be04a 100644 --- a/src/composables/useSiyuanApi.ts +++ b/src/composables/useSiyuanApi.ts @@ -38,6 +38,7 @@ export const useSiyuanApi = () => { const siyuanAuthToken = Utils.emptyOrDefault(process.env.VITE_SIYUAN_AUTH_TOKEN, "") const siyuanConfig = new SiyuanConfig(siyuanApiUrl, siyuanAuthToken) siyuanConfig.cookie = Utils.emptyOrDefault(process.env.VITE_SIYUAN_COOKIE, "") + siyuanConfig.fixTitle = true const blogApi = new SiYuanApiAdaptor(siyuanConfig) const kernelApi = new SiyuanKernelApi(siyuanConfig) const { isInChromeExtension } = useSiyuanDevice() diff --git a/src/pages/PicgoTest.vue b/src/pages/PicgoTest.vue new file mode 100644 index 00000000..594dd9cd --- /dev/null +++ b/src/pages/PicgoTest.vue @@ -0,0 +1,50 @@ + + + + + diff --git a/src/utils/luteUtil.ts b/src/utils/luteUtil.ts new file mode 100644 index 00000000..1bde3504 --- /dev/null +++ b/src/utils/luteUtil.ts @@ -0,0 +1,59 @@ +/* + * 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" + +/** + * Lute 工具类 + */ +class LuteUtil { + private static logger = createAppLogger("lute-util") + + /** + * 使用 Lute 渲染 HTML + * + * @param md + */ + public static mdToHtml(md: string) { + if (typeof window === "undefined") { + this.logger.warn("不是浏览器环境,不渲染") + return md + } + + const Lute = (window as any).Lute + if (!Lute) { + this.logger.warn("未找到Lute,不渲染") + return md + } + + this.logger.info("found Lute =>", Lute) + this.logger.info("使用Lute渲染Markdown") + const lute = Lute.New() + const html = lute.MarkdownStr("", md) + this.logger.debug("md to html =>", html) + return html + } +} + +export { LuteUtil } diff --git a/vite.config.ts b/vite.config.ts index 75c29427..e4685576 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -62,8 +62,8 @@ const isServe = process.env.IS_SERVE const isWatch = args.watch || args.w || false const isDev = isServe || isWatch || debugMode const isWindows = process.platform === "win32" -// let devDistDir = "/Users/terwer/Documents/mydocs/SiYuanWorkspace/test/data/plugins/siyuan-plugin-publisher" -let devDistDir = "/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/data/plugins/siyuan-plugin-publisher" +let devDistDir = "/Users/terwer/Documents/mydocs/SiYuanWorkspace/test/data/plugins/siyuan-plugin-publisher" +// let devDistDir = "/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/data/plugins/siyuan-plugin-publisher" if (isWindows) { devDistDir = "C:\\Users\\terwer\\Documents\\mydocs\\SiyuanWorkspace\\test\\data\\plugins\\siyuan-plugin-publisher" // devDistDir = "C:\\Users\\terwer\\Documents\\mydocs\\SiyuanWorkspace\\public\plugins\siyuan-plugin-publisher"