diff --git a/package.json b/package.json index 74da45c8..cfbaa191 100644 --- a/package.json +++ b/package.json @@ -27,8 +27,11 @@ "@vitejs/plugin-vue": "^4.2.3", "esbuild-plugin-copy": "^2.1.1", "esbuild-style-plugin": "^1.6.2", + "fast-glob": "^3.3.1", "minimist": "^1.2.8", "rollup-plugin-livereload": "^2.0.5", + "stylus": "^0.59.0", + "typescript": "^5.1.6", "unplugin-auto-import": "^0.16.6", "unplugin-vue-components": "^0.25.1", "vite": "^4.4.7", @@ -38,9 +41,27 @@ "vue-tsc": "^1.8.8" }, "dependencies": { + "@element-plus/icons-vue": "^2.1.0", + "@vueuse/components": "^10.3.0", + "@vueuse/core": "^10.3.0", + "element-plus": "^2.3.8", + "js-base64": "^3.7.5", + "pinia": "^2.1.6", + "shorthash2": "^1.0.3", + "simple-xmlrpc": "^1.4.2", "siyuan": "^0.7.8", + "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.1", + "zhi-common": "^1.11.5", "zhi-device": "^2.3.0", + "zhi-fetch-middleware": "^0.1.20", + "zhi-github-middleware": "^0.1.2", "zhi-lib-base": "^0.4.2", - "zhi-siyuan-api": "^1.28.2" + "zhi-siyuan-api": "^1.28.2", + "zhi-xmlrpc-middleware": "^0.3.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ece29cab..a3f30c46 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,18 +5,72 @@ settings: excludeLinksFromLockfile: false dependencies: + '@element-plus/icons-vue': + specifier: ^2.1.0 + version: 2.1.0(vue@3.3.4) + '@vueuse/components': + specifier: ^10.3.0 + version: 10.3.0(vue@3.3.4) + '@vueuse/core': + specifier: ^10.3.0 + version: 10.3.0(vue@3.3.4) + element-plus: + specifier: ^2.3.8 + version: 2.3.8(vue@3.3.4) + js-base64: + specifier: ^3.7.5 + version: 3.7.5 + pinia: + specifier: ^2.1.6 + version: 2.1.6(typescript@5.1.6)(vue@3.3.4) + shorthash2: + specifier: ^1.0.3 + version: 1.0.3 + simple-xmlrpc: + specifier: ^1.4.2 + version: 1.4.2 siyuan: specifier: ^0.7.8 version: 0.7.8 + uuid: + specifier: ^9.0.0 + version: 9.0.0 + vue: + specifier: ^3.3.4 + version: 3.3.4 + vue-i18n: + specifier: ^9.2.2 + version: 9.2.2(vue@3.3.4) + vue-router: + specifier: ^4.2.4 + version: 4.2.4(vue@3.3.4) + xmlbuilder2: + specifier: ^3.1.1 + version: 3.1.1 + zhi-blog-api: + specifier: ^1.20.1 + version: 1.20.1 + zhi-common: + specifier: ^1.11.5 + version: 1.11.5 zhi-device: specifier: ^2.3.0 version: 2.3.0 + zhi-fetch-middleware: + specifier: ^0.1.20 + version: 0.1.20 + zhi-github-middleware: + specifier: ^0.1.2 + version: 0.1.2 zhi-lib-base: specifier: ^0.4.2 version: 0.4.2 zhi-siyuan-api: specifier: ^1.28.2 version: 1.28.2 + zhi-xmlrpc-middleware: + specifier: ^0.3.0 + version: 0.3.0 devDependencies: '@terwer/esbuild-config-custom': @@ -37,15 +91,24 @@ devDependencies: esbuild-style-plugin: specifier: ^1.6.2 version: 1.6.2 + fast-glob: + specifier: ^3.3.1 + version: 3.3.1 minimist: specifier: ^1.2.8 version: 1.2.8 rollup-plugin-livereload: specifier: ^2.0.5 version: 2.0.5 + stylus: + specifier: ^0.59.0 + version: 0.59.0 + typescript: + specifier: ^5.1.6 + version: 5.1.6 unplugin-auto-import: specifier: ^0.16.6 - version: 0.16.6 + version: 0.16.6(@vueuse/core@10.3.0) unplugin-vue-components: specifier: ^0.25.1 version: 0.25.1(vue@3.3.4) @@ -95,12 +158,10 @@ packages: /@babel/helper-string-parser@7.22.5: resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-validator-identifier@7.22.5: resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} engines: {node: '>=6.9.0'} - dev: true /@babel/parser@7.22.7: resolution: {integrity: sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==} @@ -108,7 +169,6 @@ packages: hasBin: true dependencies: '@babel/types': 7.22.5 - dev: true /@babel/types@7.22.5: resolution: {integrity: sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==} @@ -117,7 +177,19 @@ packages: '@babel/helper-string-parser': 7.22.5 '@babel/helper-validator-identifier': 7.22.5 to-fast-properties: 2.0.0 - dev: true + + /@ctrl/tinycolor@3.6.0: + resolution: {integrity: sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ==} + engines: {node: '>=10'} + dev: false + + /@element-plus/icons-vue@2.1.0(vue@3.3.4): + resolution: {integrity: sha512-PSBn3elNoanENc1vnCfh+3WA9fimRC7n+fWkf3rE5jvv+aBohNHABC/KAR5KWPecxWxDTVT1ERpRbOMRcOV/vA==} + peerDependencies: + vue: ^3.2.0 + dependencies: + vue: 3.3.4 + dev: false /@esbuild/android-arm64@0.17.19: resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} @@ -561,6 +633,23 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@floating-ui/core@1.4.1: + resolution: {integrity: sha512-jk3WqquEJRlcyu7997NtR5PibI+y5bi+LS3hPmguVClypenMsCY3CBa3LAQnozRCtCrYWSEtAdiskpamuJRFOQ==} + dependencies: + '@floating-ui/utils': 0.1.1 + dev: false + + /@floating-ui/dom@1.5.1: + resolution: {integrity: sha512-KwvVcPSXg6mQygvA1TjbN/gh///36kKtllIF8SUm0qpFj8+rvYrpvlYdL1JoA71SHpDqgSSdGOSoQ0Mp3uY5aw==} + dependencies: + '@floating-ui/core': 1.4.1 + '@floating-ui/utils': 0.1.1 + dev: false + + /@floating-ui/utils@0.1.1: + resolution: {integrity: sha512-m0G6wlnhm/AX0H12IOWtK8gASEMffnX08RtKkCgTdHb9JpHKGloI7icFfLg9ZmQeavcvR0PKmzxClyuFPSjKWw==} + dev: false + /@humanwhocodes/config-array@0.11.10: resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==} engines: {node: '>=10.10.0'} @@ -581,6 +670,44 @@ packages: resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} dev: true + /@intlify/core-base@9.2.2: + resolution: {integrity: sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==} + engines: {node: '>= 14'} + dependencies: + '@intlify/devtools-if': 9.2.2 + '@intlify/message-compiler': 9.2.2 + '@intlify/shared': 9.2.2 + '@intlify/vue-devtools': 9.2.2 + dev: false + + /@intlify/devtools-if@9.2.2: + resolution: {integrity: sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==} + engines: {node: '>= 14'} + dependencies: + '@intlify/shared': 9.2.2 + dev: false + + /@intlify/message-compiler@9.2.2: + resolution: {integrity: sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==} + engines: {node: '>= 14'} + dependencies: + '@intlify/shared': 9.2.2 + source-map: 0.6.1 + dev: false + + /@intlify/shared@9.2.2: + resolution: {integrity: sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==} + engines: {node: '>= 14'} + dev: false + + /@intlify/vue-devtools@9.2.2: + resolution: {integrity: sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==} + engines: {node: '>= 14'} + dependencies: + '@intlify/core-base': 9.2.2 + '@intlify/shared': 9.2.2 + dev: false + /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -632,7 +759,6 @@ packages: /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - dev: true /@jridgewell/trace-mapping@0.3.18: resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} @@ -677,6 +803,101 @@ packages: - supports-color dev: true + /@octokit/auth-token@4.0.0: + resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==} + engines: {node: '>= 18'} + dev: false + + /@octokit/core@5.0.0: + resolution: {integrity: sha512-YbAtMWIrbZ9FCXbLwT9wWB8TyLjq9mxpKdgB3dUNxQcIVTf9hJ70gRPwAcqGZdY6WdJPZ0I7jLaaNDCiloGN2A==} + engines: {node: '>= 18'} + dependencies: + '@octokit/auth-token': 4.0.0 + '@octokit/graphql': 7.0.1 + '@octokit/request': 8.1.1 + '@octokit/request-error': 5.0.0 + '@octokit/types': 11.1.0 + before-after-hook: 2.2.3 + universal-user-agent: 6.0.0 + dev: false + + /@octokit/endpoint@9.0.0: + resolution: {integrity: sha512-szrQhiqJ88gghWY2Htt8MqUDO6++E/EIXqJ2ZEp5ma3uGS46o7LZAzSLt49myB7rT+Hfw5Y6gO3LmOxGzHijAQ==} + engines: {node: '>= 18'} + dependencies: + '@octokit/types': 11.1.0 + is-plain-object: 5.0.0 + universal-user-agent: 6.0.0 + dev: false + + /@octokit/graphql@7.0.1: + resolution: {integrity: sha512-T5S3oZ1JOE58gom6MIcrgwZXzTaxRnxBso58xhozxHpOqSTgDS6YNeEUvZ/kRvXgPrRz/KHnZhtb7jUMRi9E6w==} + engines: {node: '>= 18'} + dependencies: + '@octokit/request': 8.1.1 + '@octokit/types': 11.1.0 + universal-user-agent: 6.0.0 + dev: false + + /@octokit/openapi-types@18.0.0: + resolution: {integrity: sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw==} + dev: false + + /@octokit/request-error@5.0.0: + resolution: {integrity: sha512-1ue0DH0Lif5iEqT52+Rf/hf0RmGO9NWFjrzmrkArpG9trFfDM/efx00BJHdLGuro4BR/gECxCU2Twf5OKrRFsQ==} + engines: {node: '>= 18'} + dependencies: + '@octokit/types': 11.1.0 + deprecation: 2.3.1 + once: 1.4.0 + dev: false + + /@octokit/request@8.1.1: + resolution: {integrity: sha512-8N+tdUz4aCqQmXl8FpHYfKG9GelDFd7XGVzyN8rc6WxVlYcfpHECnuRkgquzz+WzvHTK62co5di8gSXnzASZPQ==} + engines: {node: '>= 18'} + dependencies: + '@octokit/endpoint': 9.0.0 + '@octokit/request-error': 5.0.0 + '@octokit/types': 11.1.0 + is-plain-object: 5.0.0 + universal-user-agent: 6.0.0 + dev: false + + /@octokit/types@11.1.0: + resolution: {integrity: sha512-Fz0+7GyLm/bHt8fwEqgvRBWwIV1S6wRRyq+V6exRKLVWaKGsuy6H9QFYeBVDV7rK6fO3XwHgQOPxv+cLj2zpXQ==} + dependencies: + '@octokit/openapi-types': 18.0.0 + dev: false + + /@oozcitak/dom@1.15.10: + resolution: {integrity: sha512-0JT29/LaxVgRcGKvHmSrUTEvZ8BXvZhGl2LASRUgHqDTC1M5g1pLmVv56IYNyt3bG2CUjDkc67wnyZC14pbQrQ==} + engines: {node: '>=8.0'} + dependencies: + '@oozcitak/infra': 1.0.8 + '@oozcitak/url': 1.0.4 + '@oozcitak/util': 8.3.8 + dev: false + + /@oozcitak/infra@1.0.8: + resolution: {integrity: sha512-JRAUc9VR6IGHOL7OGF+yrvs0LO8SlqGnPAMqyzOuFZPSZSXI7Xf2O9+awQPSMXgIWGtgUf/dA6Hs6X6ySEaWTg==} + engines: {node: '>=6.0'} + dependencies: + '@oozcitak/util': 8.3.8 + dev: false + + /@oozcitak/url@1.0.4: + resolution: {integrity: sha512-kDcD8y+y3FCSOvnBI6HJgl00viO/nGbQoCINmQ0h98OhnGITrWR3bOGfwYCthgcrV8AnTJz8MzslTQbC3SOAmw==} + engines: {node: '>=8.0'} + dependencies: + '@oozcitak/infra': 1.0.8 + '@oozcitak/util': 8.3.8 + dev: false + + /@oozcitak/util@8.3.8: + resolution: {integrity: sha512-T8TbSnGsxo6TDBJx/Sgv/BlVJL3tshxZP7Aq5R1mSnM5OcHY2dQaxLMu2+E8u3gN0MLOzdjurqN4ZRVuzQycOQ==} + engines: {node: '>=8.0'} + dev: false + /@pkgjs/parseargs@0.11.0: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -740,6 +961,10 @@ packages: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} dev: true + /@sxzz/popperjs-es@2.11.7: + resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==} + dev: false + /@terwer/esbuild-config-custom@1.0.12(@types/minimist@1.2.2)(dotenv@16.3.1)(esbuild-plugin-copy@2.1.1)(esbuild-plugin-d.ts@1.1.0)(esbuild-plugin-ifdef@1.0.1)(esbuild-plugin-inline-image@0.0.9)(esbuild-plugin-vue3@0.3.2)(esbuild-style-plugin@1.6.2)(esbuild@0.17.19)(minimist@1.2.8)(rimraf@4.4.1)(stylus@0.59.0): resolution: {integrity: sha512-fllPEIgmC9bJer+VQ1xuJ+uIDKEcWIW2tdQe1BkZ71us/ZNSLl5GPMxGr9OGH8lXqkxlErw3M6BXOJzVaCQYqg==} hasBin: true @@ -823,6 +1048,16 @@ packages: resolution: {integrity: sha512-1YXyYH83h6We1djyoUEqTlVyQtCfJAFXELSKW2ZRtjHD4hQ82CC4lvrv5D0l0FLcKBaiPbXyi3MpMsI9ZRgKsw==} dev: true + /@types/lodash-es@4.17.8: + resolution: {integrity: sha512-euY3XQcZmIzSy7YH5+Unb3b2X12Wtk54YWINBvvGQ5SmMvwb11JQskGsfkH/5HXK77Kr8GF0wkVDIxzAisWtog==} + dependencies: + '@types/lodash': 4.14.196 + dev: false + + /@types/lodash@4.14.196: + resolution: {integrity: sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==} + dev: false + /@types/minimist@1.2.2: resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} dev: true @@ -848,6 +1083,13 @@ packages: '@types/node': 18.17.1 dev: true + /@types/web-bluetooth@0.0.16: + resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} + dev: false + + /@types/web-bluetooth@0.0.17: + resolution: {integrity: sha512-4p9vcSmxAayx72yn70joFoL44c9MO/0+iVEBIQXe3v2h2SiAsEIo/G5v6ObFWvNKRFjbrVadNf9LqEEZeQPzdA==} + /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.46.0)(typescript@4.9.5): resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1161,14 +1403,12 @@ packages: '@vue/shared': 3.3.4 estree-walker: 2.0.2 source-map-js: 1.0.2 - dev: true /@vue/compiler-dom@3.3.4: resolution: {integrity: sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==} dependencies: '@vue/compiler-core': 3.3.4 '@vue/shared': 3.3.4 - dev: true /@vue/compiler-sfc@3.3.4: resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==} @@ -1183,14 +1423,16 @@ packages: magic-string: 0.30.2 postcss: 8.4.27 source-map-js: 1.0.2 - dev: true /@vue/compiler-ssr@3.3.4: resolution: {integrity: sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==} dependencies: '@vue/compiler-dom': 3.3.4 '@vue/shared': 3.3.4 - dev: true + + /@vue/devtools-api@6.5.0: + resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==} + dev: false /@vue/language-core@1.8.8(typescript@5.1.6): resolution: {integrity: sha512-i4KMTuPazf48yMdYoebTkgSOJdFraE4pQf0B+FTOFkbB+6hAfjrSou/UmYWRsWyZV6r4Rc6DDZdI39CJwL0rWw==} @@ -1219,20 +1461,17 @@ packages: '@vue/shared': 3.3.4 estree-walker: 2.0.2 magic-string: 0.30.2 - dev: true /@vue/reactivity@3.3.4: resolution: {integrity: sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==} dependencies: '@vue/shared': 3.3.4 - dev: true /@vue/runtime-core@3.3.4: resolution: {integrity: sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==} dependencies: '@vue/reactivity': 3.3.4 '@vue/shared': 3.3.4 - dev: true /@vue/runtime-dom@3.3.4: resolution: {integrity: sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==} @@ -1240,7 +1479,6 @@ packages: '@vue/runtime-core': 3.3.4 '@vue/shared': 3.3.4 csstype: 3.1.2 - dev: true /@vue/server-renderer@3.3.4(vue@3.3.4): resolution: {integrity: sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==} @@ -1250,11 +1488,9 @@ packages: '@vue/compiler-ssr': 3.3.4 '@vue/shared': 3.3.4 vue: 3.3.4 - dev: true /@vue/shared@3.3.4: resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==} - dev: true /@vue/typescript@1.8.8(typescript@5.1.6): resolution: {integrity: sha512-jUnmMB6egu5wl342eaUH236v8tdcEPXXkPgj+eI/F6JwW/lb+yAU6U07ZbQ3MVabZRlupIlPESB7ajgAGixhow==} @@ -1265,6 +1501,64 @@ packages: - typescript dev: true + /@vueuse/components@10.3.0(vue@3.3.4): + resolution: {integrity: sha512-EeZz3kjmJI7bH7JSxxMlLyk21LGl6GQjXfpl2n/GiI9QSJi+BVzIra5kEty5eM8McwAanx3e/HnK4drYTgFOWw==} + dependencies: + '@vueuse/core': 10.3.0(vue@3.3.4) + '@vueuse/shared': 10.3.0(vue@3.3.4) + vue-demi: 0.14.5(vue@3.3.4) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: false + + /@vueuse/core@10.3.0(vue@3.3.4): + resolution: {integrity: sha512-BEM5yxcFKb5btFjTSAFjTu5jmwoW66fyV9uJIP4wUXXU8aR5Hl44gndaaXp7dC5HSObmgbnR2RN+Un1p68Mf5Q==} + dependencies: + '@types/web-bluetooth': 0.0.17 + '@vueuse/metadata': 10.3.0 + '@vueuse/shared': 10.3.0(vue@3.3.4) + vue-demi: 0.14.5(vue@3.3.4) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + /@vueuse/core@9.13.0(vue@3.3.4): + resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} + dependencies: + '@types/web-bluetooth': 0.0.16 + '@vueuse/metadata': 9.13.0 + '@vueuse/shared': 9.13.0(vue@3.3.4) + vue-demi: 0.14.5(vue@3.3.4) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: false + + /@vueuse/metadata@10.3.0: + resolution: {integrity: sha512-Ema3YhNOa4swDsV0V7CEY5JXvK19JI/o1szFO1iWxdFg3vhdFtCtSTP26PCvbUpnUtNHBY2wx5y3WDXND5Pvnw==} + + /@vueuse/metadata@9.13.0: + resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==} + dev: false + + /@vueuse/shared@10.3.0(vue@3.3.4): + resolution: {integrity: sha512-kGqCTEuFPMK4+fNWy6dUOiYmxGcUbtznMwBZLC1PubidF4VZY05B+Oht7Jh7/6x4VOWGpvu3R37WHi81cKpiqg==} + dependencies: + vue-demi: 0.14.5(vue@3.3.4) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + /@vueuse/shared@9.13.0(vue@3.3.4): + resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} + dependencies: + vue-demi: 0.14.5(vue@3.3.4) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: false + /acorn-jsx@5.3.2(acorn@8.10.0): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -1332,6 +1626,12 @@ packages: picomatch: 2.3.1 dev: true + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: false + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true @@ -1395,6 +1695,10 @@ packages: synckit: 0.8.5 dev: true + /async-validator@4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} + dev: false + /async@3.2.4: resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} dev: true @@ -1418,6 +1722,10 @@ packages: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} dev: true + /before-after-hook@2.2.3: + resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} + dev: false + /big-integer@1.6.51: resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} engines: {node: '>=0.6'} @@ -1568,6 +1876,10 @@ packages: load-tsconfig: 0.2.5 dev: true + /byte-base64@1.1.0: + resolution: {integrity: sha512-56cXelkJrVMdCY9V/3RfDxTh4VfMFCQ5km7B7GkIGfo4bcPL9aACyJLB0Ms3Ezu5rsHmLB2suis96z4fLM03DA==} + dev: false + /cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -1794,7 +2106,10 @@ packages: /csstype@3.1.2: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} - dev: true + + /dayjs@1.11.9: + resolution: {integrity: sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==} + dev: false /de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} @@ -1854,6 +2169,10 @@ packages: object-keys: 1.1.1 dev: true + /deprecation@2.3.1: + resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} + dev: false + /dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -1959,6 +2278,31 @@ packages: jake: 10.8.7 dev: true + /element-plus@2.3.8(vue@3.3.4): + resolution: {integrity: sha512-yHQR0/tG2LvPkpGUt7Te/hPmP2XW/BytBNUbx+EFO54VnGCOE3upmQcVffNp1PLgwg9sthYDXontUWpnpmLPJw==} + peerDependencies: + vue: ^3.2.0 + dependencies: + '@ctrl/tinycolor': 3.6.0 + '@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-es': 4.17.8 + '@vueuse/core': 9.13.0(vue@3.3.4) + async-validator: 4.2.5 + dayjs: 1.11.9 + escape-html: 1.0.3 + lodash: 4.17.21 + lodash-es: 4.17.21 + lodash-unified: 1.0.3(@types/lodash-es@4.17.8)(lodash-es@4.17.21)(lodash@4.17.21) + memoize-one: 6.0.0 + normalize-wheel-es: 1.2.0 + vue: 3.3.4 + transitivePeerDependencies: + - '@vue/composition-api' + dev: false + /elliptic@6.5.4: resolution: {integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==} dependencies: @@ -2328,6 +2672,10 @@ packages: '@esbuild/win32-x64': 0.18.17 dev: true + /escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: false + /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -2504,6 +2852,12 @@ packages: eslint-visitor-keys: 3.4.2 dev: true + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: false + /esquery@1.5.0: resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} engines: {node: '>=0.10'} @@ -2530,7 +2884,6 @@ packages: /estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - dev: true /estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -3038,6 +3391,11 @@ packages: engines: {node: '>=8'} dev: true + /is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + dev: false + /is-reference@3.0.1: resolution: {integrity: sha512-baJJdQLiYaJdvFbJqXrcGv3WU3QCzBlUcI5QhbesIm6/xPsvmO+2CDoi/GMOFBQEQm+PXkwOPrp9KK5ozZsp2w==} dependencies: @@ -3106,6 +3464,18 @@ packages: engines: {node: '>=10'} dev: true + /js-base64@3.7.5: + resolution: {integrity: sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==} + dev: false + + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: false + /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -3198,6 +3568,22 @@ packages: p-locate: 5.0.0 dev: true + /lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + dev: false + + /lodash-unified@1.0.3(@types/lodash-es@4.17.8)(lodash-es@4.17.21)(lodash@4.17.21): + resolution: {integrity: sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==} + peerDependencies: + '@types/lodash-es': '*' + lodash: '*' + lodash-es: '*' + dependencies: + '@types/lodash-es': 4.17.8 + lodash: 4.17.21 + lodash-es: 4.17.21 + dev: false + /lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} dev: true @@ -3212,7 +3598,6 @@ packages: /lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - dev: true /loupe@2.3.6: resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} @@ -3250,7 +3635,6 @@ packages: engines: {node: '>=12'} dependencies: '@jridgewell/sourcemap-codec': 1.4.15 - dev: true /md5.js@1.3.5: resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} @@ -3264,6 +3648,10 @@ packages: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} dev: true + /memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + dev: false + /merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} dev: true @@ -3377,7 +3765,6 @@ packages: resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - dev: true /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} @@ -3439,6 +3826,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /normalize-wheel-es@1.2.0: + resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==} + dev: false + /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -3485,7 +3876,6 @@ packages: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} dependencies: wrappy: 1.0.2 - dev: true /onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} @@ -3661,13 +4051,30 @@ packages: /picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - dev: true /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} dev: true + /pinia@2.1.6(typescript@5.1.6)(vue@3.3.4): + resolution: {integrity: sha512-bIU6QuE5qZviMmct5XwCesXelb5VavdOWKWaB17ggk++NUwQWWbP5YnsONTk3b752QkW9sACiR81rorpeOMSvQ==} + peerDependencies: + '@vue/composition-api': ^1.4.0 + typescript: '>=4.4.4' + vue: ^2.6.14 || ^3.3.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + typescript: + optional: true + dependencies: + '@vue/devtools-api': 6.5.0 + typescript: 5.1.6 + vue: 3.3.4 + vue-demi: 0.14.5(vue@3.3.4) + dev: false + /pirates@4.0.6: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} @@ -3799,7 +4206,6 @@ packages: nanoid: 3.3.6 picocolors: 1.0.0 source-map-js: 1.0.2 - dev: true /prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -4022,7 +4428,6 @@ packages: /sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} - dev: true /scule@1.0.0: resolution: {integrity: sha512-4AsO/FrViE/iDNEPaAQlb77tf0csuq27EsVpy6ett584EcRTp6pTDLoGWVxCD77y5iU5FauOvhsI4o1APwPoSQ==} @@ -4060,6 +4465,10 @@ packages: engines: {node: '>=8'} dev: true + /shorthash2@1.0.3: + resolution: {integrity: sha512-oB8s64JsyJ2xhHJlnTwGg++Y3BTF6XnXeXMC7OygD8vtNcCRDiMxEGONvUOeZbxfwEXENmRlqPDouMR/OtGDsw==} + dev: false + /side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} dependencies: @@ -4081,6 +4490,13 @@ packages: engines: {node: '>=14'} dev: true + /simple-xmlrpc@1.4.2: + resolution: {integrity: sha512-2gFNsEjudzyAqCYQ1XmsETHbmG2vp9jrIeqb+Mxnb/xgnESWfMlXrSEHh10lYIb1hXeco+vSWAAuk2NLl26yFw==} + dependencies: + byte-base64: 1.1.0 + sax: 1.2.4 + dev: false + /siyuan@0.7.8: resolution: {integrity: sha512-49aQRgva9Cj5u1PuXPAfsug6GHeuhdrb7AnWBqJKyPRaVI2IskmPuisIDWIkG7JhVJ1e1KCg813a2G9QOpYRkQ==} dev: false @@ -4093,7 +4509,6 @@ packages: /source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} - dev: true /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -4105,7 +4520,6 @@ packages: /source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - dev: true /source-map@0.7.4: resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} @@ -4119,6 +4533,10 @@ packages: whatwg-url: 7.1.0 dev: true + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: false + /stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} dev: true @@ -4353,7 +4771,6 @@ packages: /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} - dev: true /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} @@ -4472,7 +4889,6 @@ packages: resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} engines: {node: '>=14.17'} hasBin: true - dev: true /ufo@1.2.0: resolution: {integrity: sha512-RsPyTbqORDNDxqAdQPQBpgqhWle1VcTSou/FraClYlHf6TZnQcGslpLcAphNR+sQW4q5lLWLbOsRlh9j24baQg==} @@ -4496,12 +4912,16 @@ packages: - rollup dev: true + /universal-user-agent@6.0.0: + resolution: {integrity: sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==} + dev: false + /universalify@2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} dev: true - /unplugin-auto-import@0.16.6: + /unplugin-auto-import@0.16.6(@vueuse/core@10.3.0): resolution: {integrity: sha512-M+YIITkx3C/Hg38hp8HmswP5mShUUyJOzpifv7RTlAbeFlO2Tyw0pwrogSSxnipHDPTtI8VHFBpkYkNKzYSuyA==} engines: {node: '>=14'} peerDependencies: @@ -4515,6 +4935,7 @@ packages: dependencies: '@antfu/utils': 0.7.5 '@rollup/pluginutils': 5.0.2 + '@vueuse/core': 10.3.0(vue@3.3.4) fast-glob: 3.3.1 local-pkg: 0.4.3 magic-string: 0.30.2 @@ -4595,6 +5016,11 @@ packages: which-typed-array: 1.1.11 dev: true + /uuid@9.0.0: + resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} + hasBin: true + dev: false + /vite-node@0.33.0(@types/node@18.17.1)(stylus@0.59.0): resolution: {integrity: sha512-19FpHYbwWWxDr73ruNahC+vtEdza52kA90Qb3La98yZ0xULqV8A5JLNPUff0f5zID4984tW7l3DH2przTJUZSw==} engines: {node: '>=v14.18.0'} @@ -4755,6 +5181,20 @@ packages: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} dev: true + /vue-demi@0.14.5(vue@3.3.4): + resolution: {integrity: sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + vue: 3.3.4 + /vue-eslint-parser@9.3.1(eslint@8.46.0): resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==} engines: {node: ^14.17.0 || >=16.0.0} @@ -4773,6 +5213,28 @@ packages: - supports-color dev: true + /vue-i18n@9.2.2(vue@3.3.4): + resolution: {integrity: sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==} + engines: {node: '>= 14'} + peerDependencies: + vue: ^3.0.0 + dependencies: + '@intlify/core-base': 9.2.2 + '@intlify/shared': 9.2.2 + '@intlify/vue-devtools': 9.2.2 + '@vue/devtools-api': 6.5.0 + vue: 3.3.4 + dev: false + + /vue-router@4.2.4(vue@3.3.4): + resolution: {integrity: sha512-9PISkmaCO02OzPVOMq2w82ilty6+xJmQrarYZDkjZBfl4RvYAlt4PKnEX21oW4KTtWfa9OuO/b3qk1Od3AEdCQ==} + peerDependencies: + vue: ^3.2.0 + dependencies: + '@vue/devtools-api': 6.5.0 + vue: 3.3.4 + dev: false + /vue-template-compiler@2.7.14: resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==} dependencies: @@ -4800,7 +5262,6 @@ packages: '@vue/runtime-dom': 3.3.4 '@vue/server-renderer': 3.3.4(vue@3.3.4) '@vue/shared': 3.3.4 - dev: true /webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} @@ -4871,7 +5332,6 @@ packages: /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: true /ws@7.5.9: resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} @@ -4891,6 +5351,16 @@ packages: engines: {node: '>=12'} dev: true + /xmlbuilder2@3.1.1: + resolution: {integrity: sha512-WCSfbfZnQDdLQLiMdGUQpMxxckeQ4oZNMNhLVkcekTu7xhD4tuUDyAPoY8CwXvBYE6LwBHd6QW2WZXlOWr1vCw==} + engines: {node: '>=12.0'} + dependencies: + '@oozcitak/dom': 1.15.10 + '@oozcitak/infra': 1.0.8 + '@oozcitak/util': 8.3.8 + js-yaml: 3.14.1 + dev: false + /xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -4931,6 +5401,23 @@ packages: resolution: {integrity: sha512-we04mMt4BEqeKDxoqdJQsDFYImYlcp/yaAIkpxpMGg+9tBURfNeHPHIHkFias9VbM8yuk6xjAX6B2xJjvYE/iw==} dev: false + /zhi-fetch-middleware@0.1.20: + resolution: {integrity: sha512-SbrrALam1OlurWZ1gGpxzWj6kvGr3lyJvCr5WwiFsFdwAIyQ/H4Qd0Bg5p/RyrM69q1EMIVAoJgBn34MYKab6g==} + dependencies: + zhi-common: 1.11.5 + zhi-device: 2.3.0 + zhi-lib-base: 0.4.2 + dev: false + + /zhi-github-middleware@0.1.2: + resolution: {integrity: sha512-L1ED4wl+b16xT3Zih3pcbSO4mq1sri4EhJlHtyKJFWfHd/oX8IjvE0fdUOvLrApTOuyR2zGdfAzRUrgV9xzZyA==} + dependencies: + '@octokit/core': 5.0.0 + js-base64: 3.7.5 + zhi-common: 1.11.5 + zhi-lib-base: 0.4.2 + dev: false + /zhi-lib-base@0.4.2: resolution: {integrity: sha512-kfKgN4hb5efUf+k5Y9028CVV1p/hsnK0NbRsxI3uwJoTlHfTBjfi6ZfrkhDn+XEnEYeWidO75sx4RdcnT7C3KA==} dev: false @@ -4942,3 +5429,11 @@ packages: zhi-common: 1.11.5 zhi-lib-base: 0.4.2 dev: false + + /zhi-xmlrpc-middleware@0.3.0: + resolution: {integrity: sha512-ME+3sVLiMGjXEqNvLesUew+o2f8f0MQYhWmWbAH0OsehA6Ouo/inaUdapSrI8/YC3ydC2+eLUe01SizgrUiQfQ==} + dependencies: + zhi-common: 1.11.5 + zhi-device: 2.3.0 + zhi-lib-base: 0.4.2 + dev: false diff --git a/src/App.vue b/src/App.vue index 84ee6b87..9a1d44e0 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,7 +1,40 @@ - + + + diff --git a/src/adaptors/api/base/commonblog/config/CommonblogConfig.ts b/src/adaptors/api/base/commonblog/config/CommonblogConfig.ts new file mode 100644 index 00000000..4f69ae22 --- /dev/null +++ b/src/adaptors/api/base/commonblog/config/CommonblogConfig.ts @@ -0,0 +1,114 @@ +import { BlogConfig, PageTypeEnum } from "zhi-blog-api" +import { MetaweblogPlaceholder } from "~/src/adaptors/api/base/metaweblog/config/MetaweblogPlaceholder.ts" +import { CommonblogPlaceholder } from "~/src/adaptors/api/base/commonblog/config/CommonblogPlaceholder.ts" + +/* + * 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. + */ +export class CommonblogConfig extends BlogConfig { + /** + * 首页 + */ + public override home = "" + + /** + * API地址 + */ + public override apiUrl = "" + /** + * 用户名 + */ + public override username = "" + /** + * 密码 + */ + public override password = "" + + /** + * 是否发布 + */ + public override apiStatus = false + + /** + * 博客ID + */ + public override blogid = "" + + /** + * 博客名(API获取) + */ + public override blogName = "" + + /** + * 文章别名key + */ + public override posidKey = "" + + /** + * 文章预览链接 + */ + public override previewUrl = "" + + /** + * 文章类型 + */ + public override pageType = PageTypeEnum.Markdown + + /** + * token设置地址 + */ + public override tokenSettingUrl = "" + + /** + * 操作提示 + */ + public override placeholder = {} as CommonblogPlaceholder + + /** + * 代理地址 + */ + public override middlewareUrl = "" + + /** + * 是否启用用户名 + */ + public usernameEnabled = false + + constructor(home: string, apiUrl: string, username: string, password: string, middlewareUrl?: string) { + super() + this.home = home + this.apiUrl = apiUrl + this.username = username + this.password = password + this.apiStatus = false + this.blogid = "" + this.blogName = "" + this.posidKey = "" + this.previewUrl = "" + this.pageType = PageTypeEnum.Markdown + this.placeholder = new MetaweblogPlaceholder() + this.middlewareUrl = middlewareUrl + this.usernameEnabled = false + } +} diff --git a/src/adaptors/api/base/commonblog/config/CommonblogPlaceholder.ts b/src/adaptors/api/base/commonblog/config/CommonblogPlaceholder.ts new file mode 100644 index 00000000..fa0666e5 --- /dev/null +++ b/src/adaptors/api/base/commonblog/config/CommonblogPlaceholder.ts @@ -0,0 +1,80 @@ +/* + * 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 { BlogPlaceholder } from "zhi-blog-api" + +/** + * Metaweblog 操作提示 + */ +export class CommonblogPlaceholder extends BlogPlaceholder { + /** + * 首页操作提示 + */ + public override homePlaceholder = "" + + /** + * API 地址操作提示 + */ + public override apiUrlPlaceholder = "" + + /** + * 用户名操作提示 + */ + public override usernamePlaceholder = "" + + /** + * 密码操作提示 + */ + public override passwordPlaceholder = "" + + /** + * API状态是否正常操作提示 + */ + public override apiStatusPlaceholder = false + + /** + * 博客名(API获取)操作提示 + */ + public override blogNamePlaceholder = "" + + /** + * 文章别名key操作提示 + */ + public override posidKeyPlaceholder = "" + /** + * 文章预览链接操作提示 + */ + public override previewUrlPlaceholder = "" + /** + * 文章类型操作提示 + */ + public override pageTypePlaceholder = "" + + constructor() { + super() + } +} + diff --git a/src/adaptors/api/base/github/adaptor/CommonGithubApiAdaptor.ts b/src/adaptors/api/base/github/adaptor/CommonGithubApiAdaptor.ts new file mode 100644 index 00000000..bf4db7ac --- /dev/null +++ b/src/adaptors/api/base/github/adaptor/CommonGithubApiAdaptor.ts @@ -0,0 +1,39 @@ +/* + * 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 } from "zhi-blog-api" + +/** + * Github API 适配器 + * + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class CommonGithubApiAdaptor extends BlogApi { + +} + +export { CommonGithubApiAdaptor } diff --git a/src/adaptors/api/base/github/config/CommonGithubConfig.ts b/src/adaptors/api/base/github/config/CommonGithubConfig.ts new file mode 100644 index 00000000..bc01844a --- /dev/null +++ b/src/adaptors/api/base/github/config/CommonGithubConfig.ts @@ -0,0 +1,28 @@ +/* + * 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. + */ + +class CommonGithubConfig {} + +export { CommonGithubConfig } diff --git a/src/adaptors/api/base/metaweblog/config/MetaweblogConfig.ts b/src/adaptors/api/base/metaweblog/config/MetaweblogConfig.ts new file mode 100644 index 00000000..8baa108f --- /dev/null +++ b/src/adaptors/api/base/metaweblog/config/MetaweblogConfig.ts @@ -0,0 +1,111 @@ +/* + * 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 { BlogConfig, PageTypeEnum } from "zhi-blog-api" +import { MetaweblogPlaceholder } from "~/src/adaptors/api/base/metaweblog/config/MetaweblogPlaceholder.ts" + +/** + * Metaweblog配置类 + */ +export class MetaweblogConfig extends BlogConfig { + /** + * 首页 + */ + public override home = "" + + /** + * API地址 + */ + public override apiUrl = "" + /** + * 用户名 + */ + public override username = "" + /** + * 密码 + */ + public override password = "" + + /** + * 是否发布 + */ + public override apiStatus = false + + /** + * 博客ID + */ + public override blogid = "" + + /** + * 博客名(API获取) + */ + public override blogName = "" + + /** + * 文章别名key + */ + public override posidKey = "" + + /** + * 文章预览链接 + */ + public override previewUrl = "" + + /** + * 文章类型 + */ + public override pageType = PageTypeEnum.Markdown + + /** + * 操作提示 + */ + public override placeholder = {} as MetaweblogPlaceholder + + /** + * 跨域请求代理 + */ + public override middlewareUrl = "" + + /** + * 是否展示Token设置地址 + */ + public showTokenTip = false + + constructor(home: string, apiUrl: string, username: string, password: string, middlewareUrl?: string) { + super() + this.home = home + this.apiUrl = apiUrl + this.username = username + this.password = password + this.apiStatus = false + this.blogid = "" + this.blogName = "" + this.posidKey = "" + this.previewUrl = "" + this.pageType = PageTypeEnum.Markdown + this.placeholder = new MetaweblogPlaceholder() + this.middlewareUrl = middlewareUrl + } +} diff --git a/src/adaptors/api/base/metaweblog/config/MetaweblogPlaceholder.ts b/src/adaptors/api/base/metaweblog/config/MetaweblogPlaceholder.ts new file mode 100644 index 00000000..f82f08e3 --- /dev/null +++ b/src/adaptors/api/base/metaweblog/config/MetaweblogPlaceholder.ts @@ -0,0 +1,78 @@ +/* + * 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 { BlogPlaceholder } from "zhi-blog-api" + +/** + * Metaweblog 操作提示 + */ +export class MetaweblogPlaceholder extends BlogPlaceholder { + /** + * 首页操作提示 + */ + public override homePlaceholder = "" + + /** + * API 地址操作提示 + */ + public override apiUrlPlaceholder = "" + + /** + * 用户名操作提示 + */ + public override usernamePlaceholder = "" + + /** + * 密码操作提示 + */ + public override passwordPlaceholder = "" + + /** + * API状态是否正常操作提示 + */ + public override apiStatusPlaceholder = false + + /** + * 博客名(API获取)操作提示 + */ + public override blogNamePlaceholder = "" + + /** + * 文章别名key操作提示 + */ + public override posidKeyPlaceholder = "" + /** + * 文章预览链接操作提示 + */ + public override previewUrlPlaceholder = "" + /** + * 文章类型操作提示 + */ + public override pageTypePlaceholder = "" + + constructor() { + super() + } +} diff --git a/src/adaptors/api/base/metaweblog/metaweblogBlogApi.ts b/src/adaptors/api/base/metaweblog/metaweblogBlogApi.ts new file mode 100644 index 00000000..1a36f238 --- /dev/null +++ b/src/adaptors/api/base/metaweblog/metaweblogBlogApi.ts @@ -0,0 +1,277 @@ +/* + * 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, CategoryInfo, Post, PostStatusEnum, UserBlog } from "zhi-blog-api" +import { AppInstance } from "~/src/appInstance.ts" +import { CnblogsConfig } from "~/src/adaptors/api/cnblogs/config/cnblogsConfig.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { CommonXmlrpcClient } from "zhi-xmlrpc-middleware" +import { MetaweblogConfig } from "~/src/adaptors/api/base/metaweblog/config/MetaweblogConfig.ts" +import { MetaweblogConstants } from "~/src/adaptors/api/base/metaweblog/metaweblogConstants.ts" +import { StrUtil } from "zhi-common" +import { BrowserUtil } from "zhi-device" + +/** + * MetaweblogBlogApi 类继承自 BlogApi 类,并为 Metaweblog API 提供了额外的功能 + * + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class MetaweblogBlogApi extends BlogApi { + protected readonly cfg: MetaweblogConfig + protected logger + private readonly commonXmlrpcClient + + /** + * 初始化 metaweblog API 适配器 + * + * @param appInstance 应用实例 + * @param cfg 配置项 + */ + constructor(appInstance: AppInstance, cfg: CnblogsConfig) { + super() + + this.cfg = cfg + this.cfg.blogid = "metaweblog" + this.logger = createAppLogger("metaweblog-api-adaptor") + this.commonXmlrpcClient = new CommonXmlrpcClient(appInstance, cfg.apiUrl) + } + + public override async getUsersBlogs(): Promise> { + let result: UserBlog[] = [] + result = await this.metaweblogCall(MetaweblogConstants.METHOD_GET_USERS_BLOGS, [ + this.cfg.blogid, + this.cfg.username, + this.cfg.password, + ]) + this.logger.debug("getUsersBlogs=>", result) + return result + } + + /** + * Not supported + * + * @param keyword + */ + public override async getRecentPostsCount(keyword?: string): Promise { + return 0 + } + + public override async getRecentPosts(numOfPosts: number): Promise { + const result: Post[] = [] + const blogPosts = await this.metaweblogCall(MetaweblogConstants.METHOD_GET_RECENT_POSTS, [ + this.cfg.blogid, + this.cfg.username, + this.cfg.password, + numOfPosts, + ]) + + for (let i = 0; i < blogPosts.length; i++) { + const blogPost = blogPosts[i] + + // 适配公共属性 + const commonPost = new Post() + commonPost.postid = blogPost.postid + commonPost.title = blogPost.title + commonPost.mt_keywords = blogPost.mt_keywords + commonPost.permalink = blogPost.permalink + commonPost.description = blogPost.description + commonPost.wp_slug = blogPost.wp_slug + commonPost.dateCreated = blogPost.dateCreated + commonPost.categories = blogPost.categories + result.push(commonPost) + } + + return result + } + + public async getPost(postid: string): Promise { + const data = await this.metaweblogCall(MetaweblogConstants.METHOD_GET_POST, [ + postid, + this.cfg.username, + this.cfg.password, + ]) + return data + } + + /** + * 新建文章 + * @param post + * @param publish + */ + public async newPost(post: Post, publish: boolean = true): Promise { + // 草稿 + if (!publish) { + post.post_status = PostStatusEnum.PostStatusEnum_Draft + } + + const postStruct = this.createPostStruct(post) + this.logger.debug("postStruct=>", postStruct) + let ret = await this.metaweblogCall(MetaweblogConstants.METHOD_NEW_POST, [ + this.cfg.blogid, + this.cfg.username, + this.cfg.password, + postStruct, + publish, + ]) + ret = ret + "" + ret = ret.replace(/"/g, "") + this.logger.debug("ret=>", ret) + + return ret + } + + public async editPost(postid: string, post: Post, publish: boolean = true): Promise { + // 草稿 + if (!publish) { + post.post_status = PostStatusEnum.PostStatusEnum_Draft + } + + const postStruct = this.createPostStruct(post) + this.logger.debug("postStruct=>", postStruct) + const ret = await this.metaweblogCall(MetaweblogConstants.METHOD_EDIT_POST, [ + postid, + this.cfg.username, + this.cfg.password, + postStruct, + publish, + ]) + this.logger.debug("ret=>", ret) + return ret + } + + public async deletePost(postid: string): Promise { + const ret = await this.metaweblogCall(MetaweblogConstants.METHOD_DELETE_POST, [ + this.cfg.blogid, + postid, + this.cfg.username, + this.cfg.password, + ]) + this.logger.debug("ret=>", ret) + + return ret + } + + public async getCategories(): Promise { + const result = [] as CategoryInfo[] + + try { + const ret = await this.metaweblogCall(MetaweblogConstants.METHOD_GET_CATEGORIES, [ + this.cfg.blogid, + this.cfg.username, + this.cfg.password, + ]) + const dataArr = ret + this.logger.debug("获取的分类信息,dataArr=>", dataArr) + + dataArr.forEach((item: any) => { + const cat = new CategoryInfo() + cat.description = item.description + cat.categoryId = item.categoryId + result.push(cat) + }) + } catch (e) { + this.logger.error("分类获取失败", e) + } + + return result + } + + protected async metaweblogCall(method: string, params: any[]) { + return await this.commonXmlrpcClient.methodCall(method, params, this.cfg.middlewareUrl) + } + + /** + * 适配文章字段 + * @param post 原始文章 + * @private + */ + private createPostStruct(post: Post): object { + const postObj = {} + + if (!StrUtil.isEmptyString(post.title)) { + Object.assign(postObj, { + title: post.title, + }) + } + + if (!StrUtil.isEmptyString(post.mt_keywords)) { + Object.assign(postObj, { + mt_keywords: post.mt_keywords, + }) + } + + if (!StrUtil.isEmptyString(post.description)) { + Object.assign(postObj, { + description: post.description, + }) + } + + if (!StrUtil.isEmptyString(post.wp_slug)) { + Object.assign(postObj, { + wp_slug: post.wp_slug, + }) + } + + // 浏览器端的date转换有问题 + if (!BrowserUtil.isInBrowser) { + Object.assign(postObj, { + // 这里要注意时间格式 + // http://www.ab-weblog.com/en/create-new-posts-with-publishing-date-in-wordpress-using-xml-rpc-and-php/ + // dateCreated: post.dateCreated.toISOString() || new Date().toISOString() + dateCreated: post.dateCreated || new Date(), + }) + } + + Object.assign(postObj, { + categories: post.categories || [], + }) + + Object.assign(postObj, { + post_status: post.post_status ?? PostStatusEnum.PostStatusEnum_Publish, + }) + + if (!StrUtil.isEmptyString(post.wp_password)) { + Object.assign(postObj, { + wp_password: post.wp_password, + }) + } + + return postObj + // return { + // title: post.title || '', + // mt_keywords: post.mt_keywords || '', + // description: post.description || '', + // wp_slug: post.wp_slug || '', + // dateCreated: post.dateCreated.toISOString() || new Date().toISOString(), + // categories: post.categories || [], + // post_status: post.post_status || POST_STATUS_CONSTANTS.POST_STATUS_PUBLISH, + // wp_password: post.wp_password || '' + // } + } +} + +export { MetaweblogBlogApi } diff --git a/src/adaptors/api/base/metaweblog/metaweblogConstants.ts b/src/adaptors/api/base/metaweblog/metaweblogConstants.ts new file mode 100644 index 00000000..de10ff3a --- /dev/null +++ b/src/adaptors/api/base/metaweblog/metaweblogConstants.ts @@ -0,0 +1,43 @@ +/* + * 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. + */ + +/** + * 预定义 Metaweblog 变量 + * + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class MetaweblogConstants { + public static METHOD_GET_USERS_BLOGS = "metaWeblog.getUsersBlogs" + public static METHOD_NEW_POST = "metaWeblog.newPost" + public static METHOD_EDIT_POST = "metaWeblog.editPost" + public static METHOD_DELETE_POST = "blogger.deletePost" + public static METHOD_GET_RECENT_POSTS = "metaWeblog.getRecentPosts" + public static METHOD_GET_POST = "metaWeblog.getPost" + public static METHOD_GET_CATEGORIES = "metaWeblog.getCategories" +} + +export { MetaweblogConstants } diff --git a/src/adaptors/api/cnblogs/adaptor/cnblogsApiAdaptor.ts b/src/adaptors/api/cnblogs/adaptor/cnblogsApiAdaptor.ts new file mode 100644 index 00000000..8d35ac99 --- /dev/null +++ b/src/adaptors/api/cnblogs/adaptor/cnblogsApiAdaptor.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 { UserBlog } from "zhi-blog-api" +import { CnblogsConfig } from "~/src/adaptors/api/cnblogs/config/cnblogsConfig.ts" +import { AppInstance } from "~/src/appInstance.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { CnblogsConstants } from "~/src/adaptors/api/cnblogs/cnblogsConstants.ts" +import { MetaweblogBlogApi } from "~/src/adaptors/api/base/metaweblog/metaweblogBlogApi.ts" + +/** + * 博客园 API 适配器 + * + * @see [博客园 API 文档](https://rpc.cnblogs.com/metaweblog/tangyouwei) + * + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class CnblogsApiAdaptor extends MetaweblogBlogApi { + /** + * 初始化博客园 API 适配器 + * + * @param appInstance 应用实例 + * @param cfg 配置项 + */ + constructor(appInstance: AppInstance, cfg: CnblogsConfig) { + super(appInstance, cfg) + this.logger = createAppLogger("cnblogs-api-adaptor") + this.cfg.blogid = "cnblogs" + } + + public override async getUsersBlogs(): Promise> { + let result: UserBlog[] = [] + result = await this.metaweblogCall(CnblogsConstants.METHOD_GET_USERS_BLOGS, [ + this.cfg.blogid, + this.cfg.username, + this.cfg.password, + ]) + this.logger.debug("getUsersBlogs=>", result) + return result + } + + public override async getPreviewUrl(postid: string): Promise { + return this.cfg.previewUrl.replace(/\[postid\]/g, postid) + } + + public override async deletePost(postid: string): Promise { + const ret = await this.metaweblogCall(CnblogsConstants.METHOD_DELETE_POST, [ + this.cfg.blogid, + postid, + this.cfg.username, + this.cfg.password, + false, + ]) + this.logger.debug("ret=>", ret) + + return ret + } +} +export { CnblogsApiAdaptor } diff --git a/src/adaptors/api/cnblogs/cnblogsConstants.ts b/src/adaptors/api/cnblogs/cnblogsConstants.ts new file mode 100644 index 00000000..37cdcf92 --- /dev/null +++ b/src/adaptors/api/cnblogs/cnblogsConstants.ts @@ -0,0 +1,40 @@ +/* + * 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. + */ + +/** + * 预定义博客园变量 + * + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class CnblogsConstants { + // 博客园不支持 metaWeblog.getUsersBlogs 只支持 blogger.getUsersBlogs + public static METHOD_GET_USERS_BLOGS = "blogger.getUsersBlogs" + // 博客园不支持 metaWeblog.deletePost 只支持 blogger.deletePost + public static METHOD_DELETE_POST = "blogger.deletePost" +} + +export { CnblogsConstants } diff --git a/src/adaptors/api/cnblogs/config/cnblogsConfig.ts b/src/adaptors/api/cnblogs/config/cnblogsConfig.ts new file mode 100644 index 00000000..f50f2cd2 --- /dev/null +++ b/src/adaptors/api/cnblogs/config/cnblogsConfig.ts @@ -0,0 +1,54 @@ +/* + * 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 {PageTypeEnum} from "zhi-blog-api" +import {MetaweblogConfig} from "~/src/adaptors/api/base/metaweblog/config/MetaweblogConfig.ts" + +/** + * 博客园配置 + * + * @author terwer + * @since 0.9.0 + */ +class CnblogsConfig extends MetaweblogConfig { + /** + * 博客园配置项 + * + * @param apiUrl API 地址 + * @param username 用户名 + * @param password 密码 + * @param middlewareUrl 代理地址 + */ + constructor(apiUrl: string, username: string, password: string, middlewareUrl?: string) { + super("https://www.cnblogs.com/[your-blog-name]", apiUrl, username, password, middlewareUrl) + + this.tokenSettingUrl = "https://i.cnblogs.com/settings" + this.previewUrl = "/p/[postid].html" + this.pageType = PageTypeEnum.Markdown + this.showTokenTip = true + } +} + +export { CnblogsConfig } diff --git a/src/adaptors/api/cnblogs/config/cnblogsPlaceholder.ts b/src/adaptors/api/cnblogs/config/cnblogsPlaceholder.ts new file mode 100644 index 00000000..6915b5e4 --- /dev/null +++ b/src/adaptors/api/cnblogs/config/cnblogsPlaceholder.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" + +/** + * 博客园操作提示 + */ +class CnblogsPlaceholder extends MetaweblogPlaceholder {} + +export { CnblogsPlaceholder } diff --git a/src/adaptors/api/cnblogs/docs.md b/src/adaptors/api/cnblogs/docs.md new file mode 100644 index 00000000..caf2ec43 --- /dev/null +++ b/src/adaptors/api/cnblogs/docs.md @@ -0,0 +1 @@ +# CnblogsApiAdaptor \ No newline at end of file diff --git a/src/adaptors/api/cnblogs/useCnblogsApi.ts b/src/adaptors/api/cnblogs/useCnblogsApi.ts new file mode 100644 index 00000000..8ab53b03 --- /dev/null +++ b/src/adaptors/api/cnblogs/useCnblogsApi.ts @@ -0,0 +1,99 @@ +/* + * 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 { Utils } from "~/src/utils/utils.ts" +import { CnblogsConfig } from "~/src/adaptors/api/cnblogs/config/cnblogsConfig.ts" +import { CnblogsApiAdaptor } from "~/src/adaptors/api/cnblogs/adaptor/cnblogsApiAdaptor.ts" +import { AppInstance } from "~/src/appInstance.ts" +import { useSettingStore } from "~/src/stores/useSettingStore.ts" +import {JsonUtil, ObjectUtil, StrUtil} from "zhi-common" +import { getDynPostidKey } from "~/src/components/set/publish/platform/dynamicConfig.ts" + +/** + * 使用Cnblogs API的自定义hook + * + * @param key 配置键值,可选参数 + * @param newCfg + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +export const useCnblogsApi = async (key?: string, newCfg?: CnblogsConfig) => { + // 创建应用日志记录器 + const logger = createAppLogger("use-cnblogs-api") + + // 记录开始使用Cnblogs API + logger.info("Start using Cnblogs API...") + + // 创建应用实例 + const appInstance = new AppInstance() + + let cfg: CnblogsConfig + if (newCfg) { + logger.info("Initialize with the latest newCfg passed in...") + cfg = newCfg + } else { + // 从配置中获取数据 + const { getSetting } = useSettingStore() + const setting = await getSetting() + cfg = JsonUtil.safeParse(setting[key], {} as CnblogsConfig) + // 如果配置为空,则使用默认的环境变量值,并记录日志 + if (ObjectUtil.isEmptyObject(cfg)) { + // 从环境变量获取Cnblogs API的URL、用户名、认证令牌和中间件URL + const cnblogsApiUrl = Utils.emptyOrDefault( + process.env.VITE_CNBLOGS_API_URL, + "https://rpc.cnblogs.com/metaweblog/[your-blog-name]" + ) + const cnblogsUsername = Utils.emptyOrDefault(process.env.VITE_CNBLOGS_USERNAME, "") + const cnblogsAuthToken = Utils.emptyOrDefault(process.env.VITE_CNBLOGS_AUTH_TOKEN, "") + const middlewareUrl = Utils.emptyOrDefault( + process.env.VITE_MIDDLEWARE_URL, + "https://api.terwer.space/api/middleware" + ) + + cfg = new CnblogsConfig(cnblogsApiUrl, cnblogsUsername, cnblogsAuthToken, middlewareUrl) + logger.debug("Configuration is empty, using default environment variables.") + } else { + logger.info("Using configuration from settings...") + } + // 初始化posidKey + if (StrUtil.isEmptyString(cfg.posidKey)) { + // 默认值 + cfg.posidKey = getDynPostidKey(key) + } + } + + // 创建Cnblogs API适配器 + const blogApi = new CnblogsApiAdaptor(appInstance, cfg) + + // 记录Cnblogs API创建成功 + logger.info("Cnblogs API created successfully.") + + return { + cfg, + blogApi, + } +} diff --git a/src/adaptors/api/hexo/useHexoApi.ts b/src/adaptors/api/hexo/useHexoApi.ts new file mode 100644 index 00000000..04633007 --- /dev/null +++ b/src/adaptors/api/hexo/useHexoApi.ts @@ -0,0 +1,25 @@ +/* + * 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. + */ + diff --git a/src/adaptors/api/typecho/adaptor/typechoApiAdaptor.ts b/src/adaptors/api/typecho/adaptor/typechoApiAdaptor.ts new file mode 100644 index 00000000..0c5ad4b6 --- /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 00000000..28b608fb --- /dev/null +++ b/src/adaptors/api/typecho/config/typechoConfig.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 { 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 { + /** + * 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 00000000..2bd7c707 --- /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 00000000..c8514e52 --- /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 00000000..9c8c7254 --- /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 00000000..65aee6a8 --- /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/typecho/useTypechoApi.ts b/src/adaptors/api/typecho/useTypechoApi.ts new file mode 100644 index 00000000..793854c3 --- /dev/null +++ b/src/adaptors/api/typecho/useTypechoApi.ts @@ -0,0 +1,93 @@ +/* + * 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, StrUtil} 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 配置键值,可选参数 + * @param newCfg + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +export const useTypechoApi = async (key?: string, newCfg?: TypechoConfig) => { + // 创建应用日志记录器 + const logger = createAppLogger("use-typecho-api") + + // 记录开始使用Typecho API + logger.info("Start using Typecho API...") + + // 创建应用实例 + const appInstance = new AppInstance() + + let cfg: TypechoConfig + if (newCfg) { + logger.info("Initialize with the latest newCfg passed in...") + cfg = newCfg + } else { + // 从配置中获取数据 + const { getSetting } = useSettingStore() + const setting = await getSetting() + cfg = JsonUtil.safeParse(setting[key], {} as TypechoConfig) + // 如果配置为空,则使用默认的环境变量值,并记录日志 + if (ObjectUtil.isEmptyObject(cfg)) { + // 从环境变量获取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, + "https://api.terwer.space/api/middleware" + ) + cfg = new TypechoConfig(typechoApiUrl, typechoUsername, typechoAuthToken, middlewareUrl) + logger.info("Configuration is empty, using default environment variables.") + } else { + logger.info("Using configuration from settings...") + } + // 初始化posidKey + if (StrUtil.isEmptyString(cfg.posidKey)) { + // 默认值 + cfg.posidKey = getDynPostidKey(key) + } + } + + // 创建Typecho API适配器 + const blogApi = new TypechoApiAdaptor(appInstance, cfg) + logger.info("Typecho API created successfully.", cfg) + + return { + cfg, + blogApi, + } +} diff --git a/src/adaptors/api/wordpress/adaptor/wordpressApiAdaptor.ts b/src/adaptors/api/wordpress/adaptor/wordpressApiAdaptor.ts new file mode 100644 index 00000000..d3d5d066 --- /dev/null +++ b/src/adaptors/api/wordpress/adaptor/wordpressApiAdaptor.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 { WordpressConfig } from "~/src/adaptors/api/wordpress/config/wordpressConfig.ts" +import { CommonXmlrpcClient } from "zhi-xmlrpc-middleware" +import { AppInstance } from "~/src/appInstance.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { WordpressConstants } from "~/src/adaptors/api/wordpress/wordpressConstants.ts" + +/** + * WordPress API 适配器 + * + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class WordpressApiAdaptor extends BlogApi { + private readonly logger + private readonly cfg: WordpressConfig + private readonly commonXmlrpcClient + + /** + * 初始化 WordPress API 适配器 + * + * @param appInstance 应用实例 + * @param cfg 配置项 + */ + constructor(appInstance: AppInstance, cfg: WordpressConfig) { + super() + + this.cfg = cfg + this.logger = createAppLogger("wordpress-api-adaptor") + this.commonXmlrpcClient = new CommonXmlrpcClient(appInstance, cfg.apiUrl) + } + + public override async getUsersBlogs(): Promise> { + let result: UserBlog[] = [] + result = await this.wordpressCall(WordpressConstants.METHOD_GET_USERS_BLOGS, []) + this.logger.debug("getUsersBlogs=>", result) + return result + } + + private async wordpressCall(method: string, params: string[]) { + const parameters = ["wordpress", this.cfg.username, this.cfg.password] + params.forEach((param) => parameters.push(param)) + return await this.commonXmlrpcClient.methodCall(method, parameters, this.cfg.middlewareUrl) + } +} +export { WordpressApiAdaptor } diff --git a/src/adaptors/api/wordpress/config/wordpressConfig.ts b/src/adaptors/api/wordpress/config/wordpressConfig.ts new file mode 100644 index 00000000..4a7ec852 --- /dev/null +++ b/src/adaptors/api/wordpress/config/wordpressConfig.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 { MetaweblogConfig } from "~/src/adaptors/api/base/metaweblog/config/MetaweblogConfig.ts" +import { PageTypeEnum } from "zhi-blog-api" +import WordpressUtils from "~/src/adaptors/api/wordpress/wordpressUtils.ts" + +/** + * WordPress 配置 + * + * @author terwer + * @since 1.0.0 + */ +class WordpressConfig extends MetaweblogConfig { + /** + * WordPress 配置项 + * + * @param homeAddr WordPress 主页 + * @param username 用户名 + * @param password 密码 + * @param middlewareUrl 代理地址 + */ + constructor(homeAddr: string, username: string, password: string, middlewareUrl?: string) { + super(homeAddr, "", username, password, middlewareUrl) + + const { home, apiUrl } = WordpressUtils.parseHomeAndUrl(homeAddr) + this.home = home + this.apiUrl = apiUrl + this.previewUrl = "/?p=[postid]" + this.pageType = PageTypeEnum.Markdown + } +} + +export { WordpressConfig } diff --git a/src/adaptors/api/wordpress/config/wordpressPlaceholder.ts b/src/adaptors/api/wordpress/config/wordpressPlaceholder.ts new file mode 100644 index 00000000..f1d2f401 --- /dev/null +++ b/src/adaptors/api/wordpress/config/wordpressPlaceholder.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 WordpressPlaceholder extends MetaweblogPlaceholder {} + +export { WordpressPlaceholder } diff --git a/src/adaptors/api/wordpress/docs.md b/src/adaptors/api/wordpress/docs.md new file mode 100644 index 00000000..8e0cf2bb --- /dev/null +++ b/src/adaptors/api/wordpress/docs.md @@ -0,0 +1 @@ +# WordpressApiAdaptor \ No newline at end of file diff --git a/src/adaptors/api/wordpress/useWordpressApi.ts b/src/adaptors/api/wordpress/useWordpressApi.ts new file mode 100644 index 00000000..eb7d4eab --- /dev/null +++ b/src/adaptors/api/wordpress/useWordpressApi.ts @@ -0,0 +1,93 @@ +/* + * 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 {JsonUtil, ObjectUtil, StrUtil} from "zhi-common" +import { WordpressConfig } from "~/src/adaptors/api/wordpress/config/wordpressConfig.ts" +import { WordpressApiAdaptor } from "~/src/adaptors/api/wordpress/adaptor/wordpressApiAdaptor.ts" +import { getDynPostidKey } from "~/src/components/set/publish/platform/dynamicConfig.ts" + +/** + * 使用Wordpress API的自定义hook + * + * @param key 配置键值,可选参数 + * @param newCfg + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +export const useWordpressApi = async (key?: string, newCfg?: WordpressConfig) => { + // 创建应用日志记录器 + const logger = createAppLogger("use-wordpress-api") + + // 记录开始使用Wordpress API + logger.info("Start using Wordpress API...") + + // 创建应用实例 + const appInstance = new AppInstance() + + let cfg: WordpressConfig + if (newCfg) { + logger.info("Initialize with the latest newCfg passed in...") + cfg = newCfg + } else { + // 从配置中获取数据 + const { getSetting } = useSettingStore() + const setting = await getSetting() + cfg = JsonUtil.safeParse(setting[key], {} as WordpressConfig) + // 如果配置为空,则使用默认的环境变量值,并记录日志 + if (ObjectUtil.isEmptyObject(cfg)) { + // 从环境变量获取Wordpress API的URL、用户名、认证令牌和中间件URL + 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, + "https://api.terwer.space/api/middleware" + ) + cfg = new WordpressConfig(wordpressApiUrl, wordpressUsername, wordpressAuthToken, middlewareUrl) + logger.info("Configuration is empty, using default environment variables.") + } else { + logger.info("Using configuration from settings...") + } + // 初始化posidKey + if (StrUtil.isEmptyString(cfg.posidKey)) { + // 默认值 + cfg.posidKey = getDynPostidKey(key) + } + } + + // 创建Wordpress API适配器 + const blogApi = new WordpressApiAdaptor(appInstance, cfg) + logger.info("Wordpress API created successfully.", cfg) + + return { + cfg, + blogApi, + } +} diff --git a/src/adaptors/api/wordpress/wordpressConstants.ts b/src/adaptors/api/wordpress/wordpressConstants.ts new file mode 100644 index 00000000..b581df2a --- /dev/null +++ b/src/adaptors/api/wordpress/wordpressConstants.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. + */ + +/** + * 预定义 WordPress 变量 + * + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class WordpressConstants { + public static METHOD_GET_USERS_BLOGS = "metaWeblog.getUsersBlogs" +} + +export { WordpressConstants } diff --git a/src/adaptors/api/wordpress/wordpressUtils.ts b/src/adaptors/api/wordpress/wordpressUtils.ts new file mode 100644 index 00000000..84ac62f8 --- /dev/null +++ b/src/adaptors/api/wordpress/wordpressUtils.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" + +/** + * 用于处理WordPress相关操作的实用工具类 + */ +class WordpressUtils { + private static logger = createAppLogger("wordpress-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("/xmlrpc.php")) { + apiUrl = home + home = home.replace("/xmlrpc.php", "") + } else { + home = home.replace(/\/$/, "") + apiUrl = `${home}/xmlrpc.php` + } + + this.logger.debug(`Parse result: home=${home}, apiUrl=${apiUrl}`) + return { home, apiUrl } + } +} + +export default WordpressUtils diff --git a/src/adaptors/api/yuque/adaptor/yuqueApi.ts b/src/adaptors/api/yuque/adaptor/yuqueApi.ts new file mode 100644 index 00000000..c7d6d988 --- /dev/null +++ b/src/adaptors/api/yuque/adaptor/yuqueApi.ts @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2022-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 { CommonFetchClient } from "zhi-fetch-middleware" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { ObjectUtil } from "zhi-common" + +/** + * 语雀API + * + * https://www.yuque.com/yuque/developer + */ +export class YuqueApi { + private readonly logger + private readonly commonFetchClient + private readonly baseUrl: string + private readonly blogid: string + private readonly username: string + private readonly token: string + private readonly middlewareUrl: string + + constructor( + appInstance: any, + baseUrl: string, + blogid: string, + username: string, + token: string, + middlewareUrl?: string + ) { + this.logger = createAppLogger("yuque-api") + this.commonFetchClient = new CommonFetchClient(appInstance, baseUrl) + this.baseUrl = baseUrl + this.blogid = blogid + this.username = username + this.token = token + this.middlewareUrl = middlewareUrl + } + + /** + * 语雀知识库列表 + */ + public async repos(): Promise { + const url = "/users/" + this.username + "/repos" + const data = {} + return await this.yuqueRequest(url, data, "GET") + } + + /** + * 向默认知识库添加文档 + * + * @param title 标题 + * @param slug 别名 + * @param content 内容 + * @param repo 知识库(可选) + */ + public async addDoc(title: string, slug: string, content: string, repo?: string): Promise { + let url = "/repos/" + this.blogid + "/docs" + if (repo) { + url = "/repos/" + repo + "/docs" + this.logger.warn("yuque addDoc with repo", repo) + } + const data = { + title, + slug, + format: "markdown", + body: content, + } + const result = await this.yuqueRequest(url, data, "POST") + this.logger.debug("yuqueRequest addDoc=>", result) + if (!result) { + throw new Error("请求语雀API异常") + } + + return String(result.id) + } + + /** + * 更新语雀文档 + * + * @param docId 文档ID + * @param title 标题 + * @param slug 别名 + * @param content 内容 + * @param repo 知识库(可选) + */ + public async updateDoc(docId: string, title: string, slug: string, content: string, repo?: string): Promise { + let url = "/repos/" + this.blogid + "/docs/" + docId + if (repo) { + url = "/repos/" + repo + "/docs/" + docId + this.logger.warn("yuque updateDoc with repo", repo) + } + const data = { + title, + slug, + body: content, + _force_asl: 1, + } + const result = await this.yuqueRequest(url, data, "PUT") + if (!result) { + throw new Error("请求语雀API异常") + } + + return true + } + + /** + * 删除 yuque 文档 + * + * @param docId 文档ID + * @param repo 知识库(可选) + */ + public async delDoc(docId: string, repo?: string): Promise { + let url = "/repos/" + this.blogid + "/docs/" + docId + if (repo) { + url = "/repos/" + repo + "/docs/" + docId + this.logger.warn("yuque delDoc with repo", repo) + } + const data = {} + const result = await this.yuqueRequest(url, data, "DELETE") + if (!result) { + throw new Error("请求语雀API异常") + } + + return true + } + + /** + * 获取 yuque 文档 + * + * @param docId 文档ID + * @param repo 知识库(可选) + */ + public async getDoc(docId: string, repo?: string): Promise { + let url = "/repos/" + this.blogid + "/docs/" + docId + if (repo) { + url = "/repos/" + repo + "/docs/" + docId + this.logger.warn("yuque getDoc with repo", repo) + } + const data = {} + const result = await this.yuqueRequest(url, data, "GET") + if (!result) { + throw new Error("请求语雀API异常") + } + + return result + } + + // ========================================================================== + // ========================================================================== + /** + * 向语雀请求数据 + * + * @param url 请求地址 + * @param data 数据 + * @param method 请求方法 GET | POST + * @private + */ + private async yuqueRequest(url: string, data?: any, method?: string): Promise { + let m = "POST" + if (method) { + m = method + } + + const fetchOptions = { + method: m, + } + + // 数据不为空才传递 + if (!ObjectUtil.isEmptyObject(data)) { + Object.assign(fetchOptions, { + body: JSON.stringify(data), + }) + } + + Object.assign(fetchOptions, { + headers: { + "Content-Type": "application/json", + "X-Auth-Token": this.token, + "User-Agent": "Terwer/0.1.0", + }, + }) + + // 发送请求 + // 设置请求参数 + const apiUrl = this.baseUrl + url + this.logger.debug("向语雀请求数据,apiUrl=>", apiUrl) + this.logger.debug("向语雀请求数据,fetchOps=>", fetchOptions) + + // 使用兼容的fetch调用并返回统一的JSON数据 + const resJson = await this.commonFetchClient.fetchCall(url, fetchOptions, this.middlewareUrl) + + this.logger.debug("向语雀请求数据,resJson=>", resJson) + return resJson.data ? resJson.data : null + } +} diff --git a/src/adaptors/api/yuque/adaptor/yuqueApiAdaptor.ts b/src/adaptors/api/yuque/adaptor/yuqueApiAdaptor.ts new file mode 100644 index 00000000..c90bb86e --- /dev/null +++ b/src/adaptors/api/yuque/adaptor/yuqueApiAdaptor.ts @@ -0,0 +1,177 @@ +/* + * 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, CategoryInfo, Post, UserBlog } from "zhi-blog-api" +import { YuqueApi } from "~/src/adaptors/api/yuque/adaptor/yuqueApi.ts" +import { YuqueConfig } from "~/src/adaptors/api/yuque/config/yuqueConfig.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { StrUtil } from "zhi-common" + +/** + * Yuque API 适配器 + */ +class YuqueApiAdaptor extends BlogApi { + private readonly logger + private readonly yuqueApi: YuqueApi + private readonly cfg + + constructor(appInstance: any, cfg: YuqueConfig) { + super() + this.logger = createAppLogger("yuque-api-adaptor") + this.cfg = cfg + this.yuqueApi = new YuqueApi( + appInstance, + cfg.apiUrl, + cfg.blogid ?? "", + cfg.username ?? "", + cfg.password ?? "", + cfg.middlewareUrl + ) + } + + public async getUsersBlogs(): Promise { + const result: UserBlog[] = [] + + const repos = await this.yuqueApi.repos() + this.logger.debug("repos=>", repos) + + // 数据适配 + repos.forEach((item: any) => { + const userblog: UserBlog = new UserBlog() + userblog.blogid = item.namespace + userblog.blogName = item.name + userblog.url = item.namespace + result.push(userblog) + }) + + return result + } + + public async deletePost(postid: string): Promise { + const yuquePostidKey = this.getYuquePostKey(postid) + return await this.yuqueApi.delDoc(yuquePostidKey.docId, yuquePostidKey.docRepo) + } + + public async editPost(postid: string, post: Post, publish?: boolean): Promise { + const yuquePostidKey = this.getYuquePostKey(postid) + return await this.yuqueApi.updateDoc( + yuquePostidKey.docId, + post.title, + post.wp_slug, + post.description, + yuquePostidKey.docRepo + ) + } + + public async newPost(post: Post, publish?: boolean): Promise { + if (post.cate_slugs != null && post.cate_slugs.length > 0) { + const repo = post.cate_slugs[0] + return await this.yuqueApi.addDoc(post.title, post.wp_slug, post.description, repo) + } else { + return await this.yuqueApi.addDoc(post.title, post.wp_slug, post.description) + } + } + + public async getPost(postid: string, useSlug?: boolean): Promise { + const yuquePostidKey = this.getYuquePostKey(postid) + + const yuqueDoc = await this.yuqueApi.getDoc(yuquePostidKey.docId, yuquePostidKey.docRepo) + this.logger.debug("yuqueDoc=>", yuqueDoc) + + const commonPost = new Post() + commonPost.title = yuqueDoc.title + commonPost.description = yuqueDoc.body + + const book = yuqueDoc.book + const cats = [] + const catSlugs = [] + + cats.push(book.name) + commonPost.categories = cats + + catSlugs.push(book.namespace) + commonPost.cate_slugs = catSlugs + + return commonPost + } + + public async getCategories(): Promise { + const cats = [] as CategoryInfo[] + + const repos: any[] = await this.yuqueApi.repos() + this.logger.debug("yuque repos=>", repos) + if (repos && repos.length > 0) { + repos.forEach((repo) => { + // 只获取文档库 + if (repo.type === "Book") { + const cat = new CategoryInfo() + cat.categoryId = repo.slug + cat.categoryName = repo.name + cat.description = repo.name + cat.categoryDescription = repo.name + cats.push(cat) + } + }) + } + + return cats + } + + async getPreviewUrl(postid: string): Promise { + // 替换文章链接 + const purl = this.cfg.previewUrl ?? "" + const yuquePostidKey = this.getYuquePostKey(postid) + const docId = yuquePostidKey.docId + const repo = yuquePostidKey.docRepo ?? this.cfg.blogid ?? "" + const postUrl = purl.replace("[postid]", docId).replace("[notebook]", repo) + // 路径组合 + return StrUtil.pathJoin(this.cfg.home ?? "", postUrl) + } + + /** + * 获取封装的postid + * @param postid + * @private postid + */ + private getYuquePostKey(postid: string): any { + let docId + let docRepo + if (postid.indexOf("_") > 0) { + const idArr = postid.split("_") + docId = idArr[0] + docRepo = idArr[1] + // docRepo就是book.namespace + } else { + docId = postid + } + + return { + docId, + docRepo, + } + } +} + +export { YuqueApiAdaptor } diff --git a/src/adaptors/api/yuque/config/yuqueConfig.ts b/src/adaptors/api/yuque/config/yuqueConfig.ts new file mode 100644 index 00000000..41b34f6f --- /dev/null +++ b/src/adaptors/api/yuque/config/yuqueConfig.ts @@ -0,0 +1,44 @@ +/* + * 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 { PageTypeEnum, PasswordType } from "zhi-blog-api" +import { CommonblogConfig } from "~/src/adaptors/api/base/commonblog/config/CommonblogConfig.ts" + +/** + * Yuque 配置 + */ +class YuqueConfig extends CommonblogConfig { + constructor(username: string, password: string, middlewareUrl?: string) { + super("https://www.yuque.com/", "https://www.yuque.com/api/v2", username, password, middlewareUrl) + + this.tokenSettingUrl = "https://www.yuque.com/settings/tokens" + this.previewUrl = "/[notebook]/[postid]" + this.pageType = PageTypeEnum.Markdown + this.usernameEnabled = true + this.passwordType = PasswordType.PasswordType_Token + } +} + +export { YuqueConfig } diff --git a/src/adaptors/api/yuque/config/yuquePlaceHolder.ts b/src/adaptors/api/yuque/config/yuquePlaceHolder.ts new file mode 100644 index 00000000..0f067edb --- /dev/null +++ b/src/adaptors/api/yuque/config/yuquePlaceHolder.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 { CommonblogPlaceholder } from "~/src/adaptors/api/base/commonblog/config/CommonblogPlaceholder.ts" + +/** + * Yuque 配置提示 + */ +class YuquePlaceHolder extends CommonblogPlaceholder {} + +export { YuquePlaceHolder } diff --git a/src/adaptors/api/yuque/useYuqueApi.ts b/src/adaptors/api/yuque/useYuqueApi.ts new file mode 100644 index 00000000..6311cd2d --- /dev/null +++ b/src/adaptors/api/yuque/useYuqueApi.ts @@ -0,0 +1,86 @@ +/* + * 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 { YuqueConfig } from "~/src/adaptors/api/yuque/config/yuqueConfig.ts" +import { useSettingStore } from "~/src/stores/useSettingStore.ts" +import {JsonUtil, ObjectUtil, StrUtil} from "zhi-common" +import { getDynPostidKey } from "~/src/components/set/publish/platform/dynamicConfig.ts" +import { YuqueApiAdaptor } from "~/src/adaptors/api/yuque/adaptor/yuqueApiAdaptor.ts" + +const useYuqueApi = async (key: string, newCfg?: YuqueConfig) => { + // 创建应用日志记录器 + const logger = createAppLogger("use-yuque-api") + + // 记录开始使用 Yuque API + logger.info("Start using Yuque API...") + + // 创建应用实例 + const appInstance = new AppInstance() + + let cfg: YuqueConfig + if (newCfg) { + logger.info("Initialize with the latest newCfg passed in...") + cfg = newCfg + } else { + // 从配置中获取数据 + const { getSetting } = useSettingStore() + const setting = await getSetting() + cfg = JsonUtil.safeParse(setting[key], {} as YuqueConfig) + + // 如果配置为空,则使用默认的环境变量值,并记录日志 + if (ObjectUtil.isEmptyObject(cfg)) { + // 从环境变量获取 Yuque API 的 URL、认证令牌和其他配置信息 + const yuqueUsername = Utils.emptyOrDefault(process.env.VITE_YUQUE_USERNAME, "") + const yuqueAuthToken = Utils.emptyOrDefault(process.env.VITE_YUQUE_AUTH_TOKEN, "") + const middlewareUrl = Utils.emptyOrDefault( + process.env.VITE_MIDDLEWARE_URL, + "https://api.terwer.space/api/middleware" + ) + cfg = new YuqueConfig(yuqueUsername, yuqueAuthToken, middlewareUrl) + logger.info("Configuration is empty, using default environment variables.") + } else { + logger.info("Using configuration from settings...") + } + // 初始化posidKey + if (StrUtil.isEmptyString(cfg.posidKey)) { + // 默认值 + cfg.posidKey = getDynPostidKey(key) + } + } + + // 创建 Yuque API 适配器 + const blogApi = new YuqueApiAdaptor(appInstance, cfg) + logger.info("Yuque API created successfully.", cfg) + + return { + cfg, + blogApi, + } +} + +export { useYuqueApi } diff --git a/src/adaptors/index.ts b/src/adaptors/index.ts new file mode 100644 index 00000000..721a4844 --- /dev/null +++ b/src/adaptors/index.ts @@ -0,0 +1,113 @@ +/* + * 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 { BlogAdaptor, WebAdaptor } from "zhi-blog-api" +import { getSubPlatformTypeByKey, SubPlatformType } from "~/src/components/set/publish/platform/dynamicConfig.ts" +import { useCnblogsApi } from "~/src/adaptors/api/cnblogs/useCnblogsApi.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { useWordpressApi } from "~/src/adaptors/api/wordpress/useWordpressApi.ts" +import { useTypechoApi } from "~/src/adaptors/api/typecho/useTypechoApi.ts" +import { useYuqueApi } from "~/src/adaptors/api/yuque/useYuqueApi.ts" +import { useZhihuWeb } from "~/src/adaptors/web/zhihu/useZhihuWeb.ts" +import { useCsdnWeb } from "~/src/adaptors/web/csdn/useCsdnWeb.ts" +import { useJianshuWeb } from "~/src/adaptors/web/jianshu/useJianshuWeb.ts" +import { useJuejinWeb } from "~/src/adaptors/web/juejin/useJuejinWeb.ts" +import { useWechatWeb } from "~/src/adaptors/web/wechat/useWechatWeb.ts" + +/** + * 适配器统一入口 + * + * @author terwer + * @since 0.9.0 + */ +class Adaptors { + private static logger = createAppLogger("adaptors") + + /** + * 根据平台key查找适配器 + * + * @param key + * @param newCfg + */ + public static async getAdaptor(key: string, newCfg?: any): Promise { + let blogAdaptor = null + const type: SubPlatformType = getSubPlatformTypeByKey(key) + + switch (type) { + case SubPlatformType.Common_Yuque: { + const { blogApi } = await useYuqueApi(key, newCfg) + blogAdaptor = blogApi + break + } + case SubPlatformType.Metaweblog_Cnblogs: { + const { blogApi } = await useCnblogsApi(key, newCfg) + blogAdaptor = blogApi + break + } + case SubPlatformType.Metaweblog_Typecho: { + const { blogApi } = await useTypechoApi(key, newCfg) + blogAdaptor = blogApi + break + } + case SubPlatformType.Wordpress_Wordpress: { + const { blogApi } = await useWordpressApi(key, newCfg) + blogAdaptor = blogApi + break + } + case SubPlatformType.Custom_Zhihu: { + const { webApi } = await useZhihuWeb(key, newCfg) + blogAdaptor = webApi + break + } + case SubPlatformType.Custom_CSDN: { + const { webApi } = await useCsdnWeb(key, newCfg) + blogAdaptor = webApi + break + } + case SubPlatformType.Custom_Jianshu: { + const { webApi } = await useJianshuWeb(key, newCfg) + blogAdaptor = webApi + break + } + case SubPlatformType.Custom_Juejin: { + const { webApi } = await useJuejinWeb(key, newCfg) + blogAdaptor = webApi + break + } + case SubPlatformType.Custom_Wechat: { + const { webApi } = await useWechatWeb(key, newCfg) + blogAdaptor = webApi + break + } + default: { + break + } + } + this.logger.debug(`get blogAdaptor from key ${key}=>`, blogAdaptor) + return blogAdaptor + } +} + +export default Adaptors diff --git a/src/adaptors/web/base/web/WebAuthApi.ts b/src/adaptors/web/base/web/WebAuthApi.ts new file mode 100644 index 00000000..9599292a --- /dev/null +++ b/src/adaptors/web/base/web/WebAuthApi.ts @@ -0,0 +1,135 @@ +/* + * 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 { ElectronCookie, WebApi, WebConfig } from "zhi-blog-api" +import { SiyuanKernelApi } from "zhi-siyuan-api" +import { CommonFetchClient } from "zhi-fetch-middleware" +import { AppInstance } from "~/src/appInstance.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { useSiyuanApi } from "~/src/composables/useSiyuanApi.ts" +import { useSiyuanDevice } from "~/src/composables/useSiyuanDevice.ts" +import { ZhihuConfig } from "~/src/adaptors/web/zhihu/config/zhihuConfig.ts" +import { JsonUtil } from "zhi-common" + +/** + * 网页授权统一封装基类 + * + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +export class WebAuthApi extends WebApi { + protected logger + protected cfg: WebConfig + private readonly kernelApi: SiyuanKernelApi + private readonly commonFetchClient: CommonFetchClient + private isInSiyuanWidget + private isInChromeExtension + + /** + * 初始化网页授权 API 适配器 + * + * @param appInstance 应用实例 + * @param cfg 配置项 + */ + constructor(appInstance: AppInstance, cfg: WebConfig) { + super() + + this.cfg = cfg + this.logger = createAppLogger("web-auth-api") + const { kernelApi } = useSiyuanApi() + const { isInSiyuanWidget, isInChromeExtension } = useSiyuanDevice() + this.kernelApi = kernelApi + this.commonFetchClient = new CommonFetchClient(appInstance) + this.isInSiyuanWidget = isInSiyuanWidget() + this.isInChromeExtension = isInChromeExtension() + } + + public updateCfg(cfg: ZhihuConfig) { + this.cfg = cfg + } + + public async buildCookie(cookies: ElectronCookie[]): Promise { + return cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join(";") + } + + // ================ + // private methods + // ================ + /** + * 网页授权通用的请求代理 + * + * @param url - url + * @param params - 参数 + */ + protected async proxyFetch(url: string, params: any = {}): Promise { + if (this.isInSiyuanWidget) { + this.logger.info("using siyuan forwardProxy") + const fetchResult = await this.kernelApi.forwardProxy( + url, + [ + { + Cookie: this.cfg.password, + }, + ], + params, + "GET", + "application/json", + 7000 + ) + this.logger.debug("proxyFetch result=>", fetchResult) + const resText = fetchResult?.body + const res = JsonUtil.safeParse(resText, {} as any) + return res + } else if (this.isInChromeExtension) { + this.logger.info("using chrome background") + const fetchOptions = { + method: "GET", + headers: { + "Content-Type": "application/json", + Cookie: this.cfg.password, + }, + } + this.logger.info("commonFetchClient from proxyFetch url =>", url) + this.logger.info("commonFetchClient from proxyFetch fetchOptions =>", fetchOptions) + const res = await this.commonFetchClient.fetchCall(url, fetchOptions) + this.logger.debug("commonFetchClient res from proxyFetch =>", res) + return res + } else { + this.logger.info("using middleware proxy") + const fetchOptions = { + method: "GET", + headers: { + "Content-Type": "application/json", + Cookie: this.cfg.password, + }, + } + this.logger.info("commonFetchClient from proxyFetch url =>", url) + this.logger.info("commonFetchClient from proxyFetch fetchOptions =>", fetchOptions) + const res = await this.commonFetchClient.fetchCall(url, fetchOptions, this.cfg.middlewareUrl) + this.logger.debug("commonFetchClient res from proxyFetch =>", res) + return res + } + } +} diff --git a/src/adaptors/web/base/web/config/CommonWebConfig.ts b/src/adaptors/web/base/web/config/CommonWebConfig.ts new file mode 100644 index 00000000..9b71429e --- /dev/null +++ b/src/adaptors/web/base/web/config/CommonWebConfig.ts @@ -0,0 +1,31 @@ +/* + * 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 { WebConfig } from "zhi-blog-api" + +/** + * 网页授权配置 + */ +export class CommonWebConfig extends WebConfig {} diff --git a/src/adaptors/web/base/web/config/CommonWebPlaceholder.ts b/src/adaptors/web/base/web/config/CommonWebPlaceholder.ts new file mode 100644 index 00000000..34599f53 --- /dev/null +++ b/src/adaptors/web/base/web/config/CommonWebPlaceholder.ts @@ -0,0 +1,31 @@ +/* + * 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 { WebPlaceholder } from "zhi-blog-api" + +/** + * 网页授权操作提示 + */ +export class CommonWebPlaceholder extends WebPlaceholder {} diff --git a/src/adaptors/web/csdn/adaptor/csdnWebAdaptor.ts b/src/adaptors/web/csdn/adaptor/csdnWebAdaptor.ts new file mode 100644 index 00000000..88b1e5c2 --- /dev/null +++ b/src/adaptors/web/csdn/adaptor/csdnWebAdaptor.ts @@ -0,0 +1,55 @@ +/* + * 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 { WebAuthApi } from "~/src/adaptors/web/base/web/WebAuthApi.ts" + +/** + * CSDN网页授权适配器 + * + * @see [wechatsync csdn adaptor](https://github.com/wechatsync/Wechatsync/blob/master/packages/@wechatsync/drivers/src/CSDN.js) + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class CsdnWebAdaptor extends WebAuthApi { + public async getMetaData(): Promise { + const res = await this.proxyFetch("https://bizapi.csdn.net/blog-console-api/v1/user/info") + const flag = !!res.data.csdnid + this.logger.info(`get csdn metadata finished, flag => ${flag}`) + return { + flag: flag, + uid: res.data.csdnid, + title: res.data.username, + avatar: res.data.avatarurl, + type: "csdn", + displayName: "CSDN", + supportTypes: ["markdown", "html"], + home: "https://mp.csdn.net/", + icon: "https://g.csdnimg.cn/static/logo/favicon32.ico", + } + } +} + +export { CsdnWebAdaptor } diff --git a/src/adaptors/web/csdn/config/csdnConfig.ts b/src/adaptors/web/csdn/config/csdnConfig.ts new file mode 100644 index 00000000..a63a5bcb --- /dev/null +++ b/src/adaptors/web/csdn/config/csdnConfig.ts @@ -0,0 +1,31 @@ +/* + * 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 { CommonWebConfig } from "~/src/adaptors/web/base/web/config/CommonWebConfig.ts" + +/** + * CSDN配置 + */ +export class CsdnConfig extends CommonWebConfig {} diff --git a/src/adaptors/web/csdn/docs.md b/src/adaptors/web/csdn/docs.md new file mode 100644 index 00000000..c91c3fb3 --- /dev/null +++ b/src/adaptors/web/csdn/docs.md @@ -0,0 +1 @@ +# CsdnWebAdaptor diff --git a/src/adaptors/web/csdn/useCsdnWeb.ts b/src/adaptors/web/csdn/useCsdnWeb.ts new file mode 100644 index 00000000..2cc50733 --- /dev/null +++ b/src/adaptors/web/csdn/useCsdnWeb.ts @@ -0,0 +1,85 @@ +/* + * 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 { CsdnConfig } from "~/src/adaptors/web/csdn/config/csdnConfig.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { AppInstance } from "~/src/appInstance.ts" +import { useSettingStore } from "~/src/stores/useSettingStore.ts" +import { JsonUtil, ObjectUtil, StrUtil } from "zhi-common" +import { Utils } from "~/src/utils/utils.ts" +import { getDynPostidKey } from "~/src/components/set/publish/platform/dynamicConfig.ts" +import { CsdnWebAdaptor } from "~/src/adaptors/web/csdn/adaptor/csdnWebAdaptor.ts" + +/** + * 用于获取CsdnWeb的API的自定义Hook + */ +const useCsdnWeb = async (key?: string, newCfg?: CsdnConfig) => { + // 创建应用日志记录器 + const logger = createAppLogger("use-csdn-web") + + // 记录开始使用Csdn WebAuth + logger.info("Start using Csdn WebAuth...") + + // 创建应用实例 + const appInstance = new AppInstance() + let cfg: CsdnConfig + if (newCfg) { + logger.info("Initialize with the latest newCfg passed in...") + cfg = newCfg + } else { + // 从配置中获取数据 + const { getSetting } = useSettingStore() + const setting = await getSetting() + cfg = JsonUtil.safeParse(setting[key], {} as CsdnConfig) + // 如果配置为空,则使用默认的环境变量值,并记录日志 + if (ObjectUtil.isEmptyObject(cfg)) { + // 从环境变量获取Csdn的cookie + const csdnCookie = Utils.emptyOrDefault(process.env.VITE_CSDN_AUTH_TOKEN, "") + cfg = new CsdnConfig(csdnCookie) + logger.debug("Configuration is empty, using default environment variables.") + } else { + logger.info("Using configuration from settings...") + } + const middlewareUrl = Utils.emptyOrDefault( + process.env.VITE_MIDDLEWARE_URL, + "https://api.terwer.space/api/middleware" + ) + if (StrUtil.isEmptyString(cfg.middlewareUrl)) { + cfg.middlewareUrl = middlewareUrl + } + // 初始化posidKey + if (StrUtil.isEmptyString(cfg.posidKey)) { + // 默认值 + cfg.posidKey = getDynPostidKey(key) + } + } + + const webApi = new CsdnWebAdaptor(appInstance, cfg) + return { + webApi, + } +} + +export { useCsdnWeb } diff --git a/src/adaptors/web/jianshu/adaptor/jianshuWebAdaptor.ts b/src/adaptors/web/jianshu/adaptor/jianshuWebAdaptor.ts new file mode 100644 index 00000000..8d290b52 --- /dev/null +++ b/src/adaptors/web/jianshu/adaptor/jianshuWebAdaptor.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 { WebAuthApi } from "~/src/adaptors/web/base/web/WebAuthApi.ts" + +/** + * 简书网页授权适配器 + * + * @see [wechatsync jianshu adaptor](https://github.com/wechatsync/Wechatsync/blob/master/packages/@wechatsync/drivers/src/jianshu.js) + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class JianshuWebAdaptor extends WebAuthApi { + public async getMetaData(): Promise { + const res = await this.proxyFetch("https://www.jianshu.com/settings/basic.json") + const notebooks = await this.proxyFetch("https://www.jianshu.com/author/notebooks") + const avatar = res.data.avatar + const uid = avatar.substring(avatar.lastIndexOf("/") + 1, avatar.lastIndexOf(".")) + const flag = !!uid + this.logger.info(`get jianshu metadata finished, flag => ${flag}`) + return { + flag: flag, + uid: uid, + title: res.data.nickname, + avatar: avatar, + type: "jianshu", + displayName: "简书", + supportTypes: ["html"], + home: "https://www.jianshu.com/settings/basic", + icon: "https://www.jianshu.com/favicon.ico", + notebooks: notebooks, + } + } +} + +export { JianshuWebAdaptor } diff --git a/src/adaptors/web/jianshu/config/JianshuConfig.ts b/src/adaptors/web/jianshu/config/JianshuConfig.ts new file mode 100644 index 00000000..e3fe1ea5 --- /dev/null +++ b/src/adaptors/web/jianshu/config/JianshuConfig.ts @@ -0,0 +1,31 @@ +/* + * 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 { CommonWebConfig } from "~/src/adaptors/web/base/web/config/CommonWebConfig.ts" + +/** + * 简书配置 + */ +export class JianshuConfig extends CommonWebConfig {} diff --git a/src/adaptors/web/jianshu/docs.md b/src/adaptors/web/jianshu/docs.md new file mode 100644 index 00000000..04069249 --- /dev/null +++ b/src/adaptors/web/jianshu/docs.md @@ -0,0 +1 @@ +# JianshuWebAdaptor diff --git a/src/adaptors/web/jianshu/useJianshuWeb.ts b/src/adaptors/web/jianshu/useJianshuWeb.ts new file mode 100644 index 00000000..1ad1f5e3 --- /dev/null +++ b/src/adaptors/web/jianshu/useJianshuWeb.ts @@ -0,0 +1,85 @@ +/* + * 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 { JianshuConfig } from "~/src/adaptors/web/jianshu/config/JianshuConfig.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { AppInstance } from "~/src/appInstance.ts" +import { useSettingStore } from "~/src/stores/useSettingStore.ts" +import { JsonUtil, ObjectUtil, StrUtil } from "zhi-common" +import { Utils } from "~/src/utils/utils.ts" +import { getDynPostidKey } from "~/src/components/set/publish/platform/dynamicConfig.ts" +import { JianshuWebAdaptor } from "~/src/adaptors/web/jianshu/adaptor/jianshuWebAdaptor.ts" + +/** + * 用于获取JianshuWeb的API的自定义Hook + */ +const useJianshuWeb = async (key?: string, newCfg?: JianshuConfig) => { + // 创建应用日志记录器 + const logger = createAppLogger("use-jianshu-web") + + // 记录开始使用Jianshu WebAuth + logger.info("Start using Jianshu WebAuth...") + + // 创建应用实例 + const appInstance = new AppInstance() + let cfg: JianshuConfig + if (newCfg) { + logger.info("Initialize with the latest newCfg passed in...") + cfg = newCfg + } else { + // 从配置中获取数据 + const { getSetting } = useSettingStore() + const setting = await getSetting() + cfg = JsonUtil.safeParse(setting[key], {} as JianshuConfig) + // 如果配置为空,则使用默认的环境变量值,并记录日志 + if (ObjectUtil.isEmptyObject(cfg)) { + // 从环境变量获取Jianshu的cookie + const jianshuCookie = Utils.emptyOrDefault(process.env.VITE_JIANSHU_AUTH_TOKEN, "") + cfg = new JianshuConfig(jianshuCookie) + logger.debug("Configuration is empty, using default environment variables.") + } else { + logger.info("Using configuration from settings...") + } + const middlewareUrl = Utils.emptyOrDefault( + process.env.VITE_MIDDLEWARE_URL, + "https://api.terwer.space/api/middleware" + ) + if (StrUtil.isEmptyString(cfg.middlewareUrl)) { + cfg.middlewareUrl = middlewareUrl + } + // 初始化posidKey + if (StrUtil.isEmptyString(cfg.posidKey)) { + // 默认值 + cfg.posidKey = getDynPostidKey(key) + } + } + + const webApi = new JianshuWebAdaptor(appInstance, cfg) + return { + webApi, + } +} + +export { useJianshuWeb } diff --git a/src/adaptors/web/juejin/adaptor/juejinWebAdaptor.ts b/src/adaptors/web/juejin/adaptor/juejinWebAdaptor.ts new file mode 100644 index 00000000..ef2293cc --- /dev/null +++ b/src/adaptors/web/juejin/adaptor/juejinWebAdaptor.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 { WebAuthApi } from "~/src/adaptors/web/base/web/WebAuthApi.ts" + +/** + * 掘金网页授权适配器 + * + * @see [wechatsync juejin adaptor](https://github.com/wechatsync/Wechatsync/blob/master/packages/@wechatsync/drivers/src/Juejin.js) + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class JuejinWebAdaptor extends WebAuthApi { + public async getMetaData(): Promise { + const res = await this.proxyFetch("https://api.juejin.cn/user_api/v1/user/get") + const flag = !!res.data.user_id + this.logger.info(`get juejin metadata finished, flag => ${flag}`) + return { + flag: flag, + uid: res.data.user_id, + title: res.data.user_name, + avatar: res.data.avatar_large, + type: 'juejin', + displayName: '掘金', + raw: res.data, + supportTypes: ['markdown', 'html'], + home: 'https://juejin.cn/editor/drafts', + icon: 'https://juejin.cn/favicon.ico', + } + } +} + +export { JuejinWebAdaptor } diff --git a/src/adaptors/web/juejin/config/JuejinConfig.ts b/src/adaptors/web/juejin/config/JuejinConfig.ts new file mode 100644 index 00000000..9ff7dfdd --- /dev/null +++ b/src/adaptors/web/juejin/config/JuejinConfig.ts @@ -0,0 +1,31 @@ +/* + * 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 { CommonWebConfig } from "~/src/adaptors/web/base/web/config/CommonWebConfig.ts" + +/** + * 掘金配置 + */ +export class JuejinConfig extends CommonWebConfig {} diff --git a/src/adaptors/web/juejin/docs.md b/src/adaptors/web/juejin/docs.md new file mode 100644 index 00000000..527f748a --- /dev/null +++ b/src/adaptors/web/juejin/docs.md @@ -0,0 +1 @@ +# JuejinWebAdaptor diff --git a/src/adaptors/web/juejin/useJuejinWeb.ts b/src/adaptors/web/juejin/useJuejinWeb.ts new file mode 100644 index 00000000..8961a675 --- /dev/null +++ b/src/adaptors/web/juejin/useJuejinWeb.ts @@ -0,0 +1,85 @@ +/* + * 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 { JuejinConfig } from "~/src/adaptors/web/juejin/config/JuejinConfig.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { AppInstance } from "~/src/appInstance.ts" +import { useSettingStore } from "~/src/stores/useSettingStore.ts" +import { JsonUtil, ObjectUtil, StrUtil } from "zhi-common" +import { Utils } from "~/src/utils/utils.ts" +import { getDynPostidKey } from "~/src/components/set/publish/platform/dynamicConfig.ts" +import { JuejinWebAdaptor } from "~/src/adaptors/web/juejin/adaptor/juejinWebAdaptor.ts" + +/** + * 用于获取JuejinWeb的API的自定义Hook + */ +const useJuejinWeb = async (key?: string, newCfg?: JuejinConfig) => { + // 创建应用日志记录器 + const logger = createAppLogger("use-juejin-web") + + // 记录开始使用Juejin WebAuth + logger.info("Start using Juejin WebAuth...") + + // 创建应用实例 + const appInstance = new AppInstance() + let cfg: JuejinConfig + if (newCfg) { + logger.info("Initialize with the latest newCfg passed in...") + cfg = newCfg + } else { + // 从配置中获取数据 + const { getSetting } = useSettingStore() + const setting = await getSetting() + cfg = JsonUtil.safeParse(setting[key], {} as JuejinConfig) + // 如果配置为空,则使用默认的环境变量值,并记录日志 + if (ObjectUtil.isEmptyObject(cfg)) { + // 从环境变量获取Juejin的cookie + const juejinCookie = Utils.emptyOrDefault(process.env.VITE_JUEJIN_AUTH_TOKEN, "") + cfg = new JuejinConfig(juejinCookie) + logger.debug("Configuration is empty, using default environment variables.") + } else { + logger.info("Using configuration from settings...") + } + const middlewareUrl = Utils.emptyOrDefault( + process.env.VITE_MIDDLEWARE_URL, + "https://api.terwer.space/api/middleware" + ) + if (StrUtil.isEmptyString(cfg.middlewareUrl)) { + cfg.middlewareUrl = middlewareUrl + } + // 初始化posidKey + if (StrUtil.isEmptyString(cfg.posidKey)) { + // 默认值 + cfg.posidKey = getDynPostidKey(key) + } + } + + const webApi = new JuejinWebAdaptor(appInstance, cfg) + return { + webApi, + } +} + +export { useJuejinWeb } diff --git a/src/adaptors/web/wechat/adaptor/wechatWebAdaptor.ts b/src/adaptors/web/wechat/adaptor/wechatWebAdaptor.ts new file mode 100644 index 00000000..093da919 --- /dev/null +++ b/src/adaptors/web/wechat/adaptor/wechatWebAdaptor.ts @@ -0,0 +1,48 @@ +/* + * 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 { WebAuthApi } from "~/src/adaptors/web/base/web/WebAuthApi.ts" + +/** + * 掘金网页授权适配器 + * + * @see [wechatsync wechat adaptor](https://github.com/wechatsync/Wechatsync/blob/master/packages/@wechatsync/drivers/src/weixin.js) + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class WechatWebAdaptor extends WebAuthApi { + public async getMetaData(): Promise { + const res = await this.proxyFetch("https://mp.weixin.qq.com/") + console.log("WechatWebAdaptor res=>", res) + const flag = false + this.logger.info(`get wechat metadata finished, flag => ${flag}`) + return { + flag: flag, + } + } +} + +export { WechatWebAdaptor } diff --git a/src/adaptors/web/wechat/config/WechatConfig.ts b/src/adaptors/web/wechat/config/WechatConfig.ts new file mode 100644 index 00000000..2d907178 --- /dev/null +++ b/src/adaptors/web/wechat/config/WechatConfig.ts @@ -0,0 +1,31 @@ +/* + * 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 { CommonWebConfig } from "~/src/adaptors/web/base/web/config/CommonWebConfig.ts" + +/** + * 微信公众号配置 + */ +export class WechatConfig extends CommonWebConfig {} diff --git a/src/adaptors/web/wechat/docs.md b/src/adaptors/web/wechat/docs.md new file mode 100644 index 00000000..0d506063 --- /dev/null +++ b/src/adaptors/web/wechat/docs.md @@ -0,0 +1 @@ +# WechatWebAdaptor \ No newline at end of file diff --git a/src/adaptors/web/wechat/useWechatWeb.ts b/src/adaptors/web/wechat/useWechatWeb.ts new file mode 100644 index 00000000..b5440c60 --- /dev/null +++ b/src/adaptors/web/wechat/useWechatWeb.ts @@ -0,0 +1,85 @@ +/* + * 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 { WechatConfig } from "~/src/adaptors/web/wechat/config/WechatConfig.ts" +import { AppInstance } from "~/src/appInstance.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { useSettingStore } from "~/src/stores/useSettingStore.ts" +import { JsonUtil, ObjectUtil, StrUtil } from "zhi-common" +import { Utils } from "~/src/utils/utils.ts" +import { getDynPostidKey } from "~/src/components/set/publish/platform/dynamicConfig.ts" +import { WechatWebAdaptor } from "~/src/adaptors/web/wechat/adaptor/wechatWebAdaptor.ts" + +/** + * 用于获取WechatWeb的API的自定义Hook + */ +const useWechatWeb = async (key?: string, newCfg?: WechatConfig) => { + // 创建应用日志记录器 + const logger = createAppLogger("use-wechat-web") + + // 记录开始使用Wechat WebAuth + logger.info("Start using Wechat WebAuth...") + + // 创建应用实例 + const appInstance = new AppInstance() + let cfg: WechatConfig + if (newCfg) { + logger.info("Initialize with the latest newCfg passed in...") + cfg = newCfg + } else { + // 从配置中获取数据 + const { getSetting } = useSettingStore() + const setting = await getSetting() + cfg = JsonUtil.safeParse(setting[key], {} as WechatConfig) + // 如果配置为空,则使用默认的环境变量值,并记录日志 + if (ObjectUtil.isEmptyObject(cfg)) { + // 从环境变量获取Wechat的cookie + const wechatCookie = Utils.emptyOrDefault(process.env.VITE_WECHAT_AUTH_TOKEN, "") + cfg = new WechatConfig(wechatCookie) + logger.debug("Configuration is empty, using default environment variables.") + } else { + logger.info("Using configuration from settings...") + } + const middlewareUrl = Utils.emptyOrDefault( + process.env.VITE_MIDDLEWARE_URL, + "https://api.terwer.space/api/middleware" + ) + if (StrUtil.isEmptyString(cfg.middlewareUrl)) { + cfg.middlewareUrl = middlewareUrl + } + // 初始化posidKey + if (StrUtil.isEmptyString(cfg.posidKey)) { + // 默认值 + cfg.posidKey = getDynPostidKey(key) + } + } + + const webApi = new WechatWebAdaptor(appInstance, cfg) + return { + webApi, + } +} + +export { useWechatWeb } diff --git a/src/adaptors/web/zhihu/adaptor/zhihuWebAdaptor.ts b/src/adaptors/web/zhihu/adaptor/zhihuWebAdaptor.ts new file mode 100644 index 00000000..533ef3c6 --- /dev/null +++ b/src/adaptors/web/zhihu/adaptor/zhihuWebAdaptor.ts @@ -0,0 +1,74 @@ +/* + * 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 { WebAuthApi } from "~/src/adaptors/web/base/web/WebAuthApi.ts" + +/** + * 知乎网页授权适配器 + * + * @see [wechatsync zhihu adaptor](https://github.com/wechatsync/Wechatsync/blob/master/packages/%40wechatsync/drivers/src/zhihu.js) + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class ZhihuWebAdaptor extends WebAuthApi { + // /** + // * 初始化知乎 API 适配器 + // * + // * @param appInstance 应用实例 + // * @param cfg 配置项 + // */ + // constructor(appInstance: AppInstance, cfg: ZhihuConfig) { + // super(appInstance, cfg) + // + // this.cfg = cfg + // this.logger = createAppLogger("zhihu-web-adaptor") + // } + + public async getMetaData(): Promise { + const res = await this.proxyFetch( + "https://www.zhihu.com/api/v4/me?include=account_status%2Cis_bind_phone%2Cis_force_renamed%2Cemail%2Crenamed_fullname" + ) + const flag = !!res.uid + this.logger.info(`get zhihu metadata finished, flag => ${flag}`) + return { + flag: flag, + uid: res.uid, + title: res.name, + avatar: res.avatar_url, + supportTypes: ["html"], + type: "zhihu", + displayName: "知乎", + home: "https://www.zhihu.com/settings/account", + icon: "https://static.zhihu.com/static/favicon.ico", + } + } + + // public async getPreviewUrl(postid: string): Promise { + // return Promise.resolve(`https://zhuanlan.zhihu.com/p/${postid}`) + // } +} + +export { ZhihuWebAdaptor } diff --git a/src/adaptors/web/zhihu/config/zhihuConfig.ts b/src/adaptors/web/zhihu/config/zhihuConfig.ts new file mode 100644 index 00000000..794f9e86 --- /dev/null +++ b/src/adaptors/web/zhihu/config/zhihuConfig.ts @@ -0,0 +1,31 @@ +/* + * 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 { CommonWebConfig } from "~/src/adaptors/web/base/web/config/CommonWebConfig.ts" + +/** + * 知乎配置 + */ +export class ZhihuConfig extends CommonWebConfig {} diff --git a/src/adaptors/web/zhihu/docs.md b/src/adaptors/web/zhihu/docs.md new file mode 100644 index 00000000..05c171ff --- /dev/null +++ b/src/adaptors/web/zhihu/docs.md @@ -0,0 +1 @@ +# ZhihuWebAdaptor diff --git a/src/adaptors/web/zhihu/useZhihuWeb.ts b/src/adaptors/web/zhihu/useZhihuWeb.ts new file mode 100644 index 00000000..018f4917 --- /dev/null +++ b/src/adaptors/web/zhihu/useZhihuWeb.ts @@ -0,0 +1,85 @@ +/* + * 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 { ZhihuWebAdaptor } from "~/src/adaptors/web/zhihu/adaptor/zhihuWebAdaptor.ts" +import { ZhihuConfig } from "~/src/adaptors/web/zhihu/config/zhihuConfig.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { AppInstance } from "~/src/appInstance.ts" +import { useSettingStore } from "~/src/stores/useSettingStore.ts" +import { JsonUtil, ObjectUtil, StrUtil } from "zhi-common" +import { Utils } from "~/src/utils/utils.ts" +import { getDynPostidKey } from "~/src/components/set/publish/platform/dynamicConfig.ts" + +/** + * 用于获取ZhihuWeb的API的自定义Hook + */ +const useZhihuWeb = async (key?: string, newCfg?: ZhihuConfig) => { + // 创建应用日志记录器 + const logger = createAppLogger("use-zhihu-web") + + // 记录开始使用Zhihu WebAuth + logger.info("Start using Zhihu WebAuth...") + + // 创建应用实例 + const appInstance = new AppInstance() + let cfg: ZhihuConfig + if (newCfg) { + logger.info("Initialize with the latest newCfg passed in...") + cfg = newCfg + } else { + // 从配置中获取数据 + const { getSetting } = useSettingStore() + const setting = await getSetting() + cfg = JsonUtil.safeParse(setting[key], {} as ZhihuConfig) + // 如果配置为空,则使用默认的环境变量值,并记录日志 + if (ObjectUtil.isEmptyObject(cfg)) { + // 从环境变量获取Zhihu的cookie + const zhihuCookie = Utils.emptyOrDefault(process.env.VITE_ZHIHU_AUTH_TOKEN, "") + cfg = new ZhihuConfig(zhihuCookie) + logger.debug("Configuration is empty, using default environment variables.") + } else { + logger.info("Using configuration from settings...") + } + const middlewareUrl = Utils.emptyOrDefault( + process.env.VITE_MIDDLEWARE_URL, + "https://api.terwer.space/api/middleware" + ) + if(StrUtil.isEmptyString(cfg.middlewareUrl)){ + cfg.middlewareUrl = middlewareUrl + } + // 初始化posidKey + if (StrUtil.isEmptyString(cfg.posidKey)) { + // 默认值 + cfg.posidKey = getDynPostidKey(key) + } + } + + const webApi = new ZhihuWebAdaptor(appInstance, cfg) + return { + webApi, + } +} + +export { useZhihuWeb } diff --git a/src/appInstance.ts b/src/appInstance.ts new file mode 100644 index 00000000..fa5cf8fa --- /dev/null +++ b/src/appInstance.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" +import { DeviceDetection } from "zhi-device" +import xmlbuilder2 from "xmlbuilder2" +import { Deserializer, Serializer, SimpleXmlRpcClient, XmlrpcUtil } from "simple-xmlrpc" + +/** + * 应用实例 + */ +export class AppInstance { + public logger + public deviceType + + public fetch + public xmlbuilder2 + public simpleXmlrpc + + constructor() { + this.logger = createAppLogger("app-instance") + this.deviceType = DeviceDetection.getDevice() + + // Node 18 已经原生支持fetch,不再 polyfill + this.fetch = fetch + this.xmlbuilder2 = xmlbuilder2 + this.simpleXmlrpc = { + SimpleXmlRpcClient: SimpleXmlRpcClient, + Serializer: Serializer, + Deserializer: Deserializer, + XmlrpcUtil: XmlrpcUtil, + } + } +} diff --git a/src/assets/style.css b/src/assets/style.css new file mode 100644 index 00000000..5d8da850 --- /dev/null +++ b/src/assets/style.css @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2022-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 "./webfont.css"; + +html { + /*filter: grayscale(1);*/ + --el-font-family: var(--g-font-family) !important; + --custom-app-color: #161616; + --custom-app-bg-color: #ffffff; + height: 100%; +} + +body { + margin: 0; + font-size: 100%; + height: 97.5%; +} + +#app { + color: var(--custom-app-color); + background: var(--custom-app-bg-color); + padding: 10px 0; + min-width: 600px; + min-height: 99%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: none; +} + +a:hover { + color: #535bf2; +} + +:root { + --g-font-family: "Open Sans", "LXGW WenKai", "JetBrains Mono", "-apple-system", "Microsoft YaHei", "Times New Roman", + "方正北魏楷书_GBK"; + font-family: var(--g-font-family) !important; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +/* Mono font (optional) */ +pre, +code { + font-family: var(--g-font-family) !important; + tab-size: 4; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } +} + +/* common */ +.pull-right { + float: right; +} + +/* util */ +.disabled-click { + pointer-events: none; +} + +/* table */ +.post-detail-content-box table { + font-family: var(--g-font-family); + font-size: 14px; + color: #333333; + border-width: 1px; + border-color: #666666; + border-collapse: collapse; + margin-top: 10px; +} + +.post-detail-content-box table th { + border-width: 1px; + padding: 8px; + border-style: solid; + border-color: #666666; + background-color: #dedede; +} + +.post-detail-content-box table td { + border-width: 1px; + padding: 8px; + border-style: solid; + border-color: #666666; + background-color: #ffffff; +} + +.el-header { + --el-header-padding: 0 20px; + --el-header-height: 20px !important; +} + +.el-alert { + margin-top: 10px !important; +} + +.el-card__body { + text-align: left !important; + --el-card-padding: 10px; +} + +.el-dialog { + --el-dialog-width: 88% !important; + --el-dialog-margin-top: 10vh; + margin: var(--el-dialog-margin-top, 15vh) auto 0 5.5%; + text-align: left !important; +} + +.el-dialog__header { + text-align: left !important; +} + +.el-dialog__body { + padding-bottom: 10px !important; +} + +.dialog-action { + margin: calc(var(--el-dialog-padding-primary) + 16px) var(--el-dialog-padding-primary); + margin-bottom: 0; + padding-bottom: 0; + padding-right: 0; + margin-right: 0; +} + +.dialog-action .el-form-item { + margin-bottom: 0 !important; +} + +.dialog-action .el-form-item__content { + justify-content: flex-end; +} + +.dialog-action .el-form-item__content .el-button { + margin-left: 10px; +} + +.pic-manage-btn { + margin-left: 10px; +} +.pic-manage-btn svg { + margin-right: 5px; +} diff --git a/src/assets/style.dark.css b/src/assets/style.dark.css new file mode 100644 index 00000000..eddd020a --- /dev/null +++ b/src/assets/style.dark.css @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2022, 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. + */ + +html.dark { + /* 自定义深色背景颜色 */ + --custom-app-color: #ffffff; + --custom-app-bg-color: #000000; +} + +button, +input, +select, +textarea { + font-family: inherit; + font-size: inherit; + line-height: inherit; + color: inherit; +} + +html.dark .post-detail-content-box .hljs { + color: #ffffff; + background-color: #161616 !important; + border: 1px solid #ccc !important; +} + +/* dark table */ +html.dark .post-detail-content-box table { + color: #ffffff; + border-color: #ccc; +} + +html.dark .post-detail-content-box table th { + border-color: #ccc; + background-color: #161616; +} + +html.dark .post-detail-content-box table td { + border-color: #ccc; + background-color: #161616; +} + +html.dark .el-header h1 { + --custom-title-text-color: #ffffff; +} + +html.dark .el-page-header__left { + color: #ffffff; +} + +html.dark .post-detail-content-box { + color: #ffffff; +} + +/** dark code */ +html.dark .post-detail-content-box .hljs { + color: #ffffff; + background-color: #161616 !important; + border: 1px solid #ccc !important; +} + +html.dark code { + color: #2fd945 !important; +} + +html.dark .hljs-keyword, +html.dark .hljs-selector-tag, +html.dark .hljs-built_in, +html.dark .hljs-name, +html.dark .hljs-tag { + color: #539dc5 !important; +} + +html.dark .post-detail-content-box .hljs-string, +html.dark .post-detail-content-box .hljs-title, +html.dark .post-detail-content-box .hljs-section, +html.dark .post-detail-content-box .hljs-attribute, +html.dark .post-detail-content-box .hljs-literal, +html.dark .post-detail-content-box .hljs-template-tag, +html.dark .post-detail-content-box .hljs-template-variable, +html.dark .post-detail-content-box .hljs-type, +html.dark .post-detail-content-box .hljs-addition { + color: #2fd945 !important; +} + +html.dark .language-math { + color: #ffffff; + background: var(--custom-app-bg-color); +} diff --git a/src/assets/tech.md b/src/assets/tech.md new file mode 100644 index 00000000..8aa21cef --- /dev/null +++ b/src/assets/tech.md @@ -0,0 +1,246 @@ +## 愿景 + +

用思源笔记记录你的创作,剩下的交给我

+ +为什么要做这个项目? + +发布到不同平台,并且保持同步,一直以来都是一个痛苦的过程。试想一下,带着灵感满心欢喜的创作完成,然后打开每个平台,登录账号,复制粘贴,修改格式,填写属性,点击发布,还没到最后一步,已经感觉不耐烦了。如果有多个平台,那会更加抓狂。 + +此时,您可能会想,要是有一个一次配置,然后以后一键发布更新的该多好。恭喜你,用我就对了。 + +如果您有幸接触我这个不起眼的项目,我希望它能让这个过程变成自动的(或者某种操作简单的半自动)、高效的、愉快的,这也是创作本来该有的体验。 + +## 尝鲜体验 + +从 0.1.0+ 版本开始,增加临时尝鲜版,直接体验最新特性,无需等待版本发布,支持自定义配置思源笔记的 API 地址。只要修改思源 API +地址和 token 即可,网页版需要填写外网地址。所有配置均存储在浏览器本地。切换浏览器或者换电脑配置不共享。 + +猛击入口体验:https://publish.terwer.space/blog/index.html + +共享说明:https://publish.terwer.space/detail/index.html?id=20221120201546-daxmt2z + +## 快速上手 + +| 商店 | 版本 | 上架状态 | +| :----------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :------: | +| ![](https://static-rs-terwer.oss-cn-beijing.aliyuncs.com/icons/browser/edge20.svg) | [latest](https://microsoftedge.microsoft.com/addons/detail/%E6%80%9D%E6%BA%90%E7%AC%94%E8%AE%B0%E5%8F%91%E5%B8%83%E8%BE%85%E5%8A%A9%E5%B7%A5%E5%85%B7/aejmkigifflimhjlhjkdckclhabbilee) | 已上架 | +| ![](https://static-rs-terwer.oss-cn-beijing.aliyuncs.com/icons/browser/chrome20.svg) | [latest](https://chrome.google.com/webstore/detail/%E6%80%9D%E6%BA%90%E7%AC%94%E8%AE%B0%E5%8F%91%E5%B8%83%E8%BE%85%E5%8A%A9%E5%B7%A5%E5%85%B7/gemlnnppcphbiimfjnobfgdkohjmgifm) | 已上架 | +| 网页版 | [latest](https://publish.terwer.space/blog/index.html) | 可使用 | +| 离线 zip | [latest](https://github.com/terwer/sy-post-publisher/releases) | 可下载 | +| 挂件版 | [latest](https://github.com/terwer/sy-post-publisher/releases) | 可使用 | + +小贴士: + +1. 推荐直接从 `思源笔记集市`、 `Chrome商店` 或者 `Edge商店` 等官方商店下载使用。 + +2. 如果您想 `临时尝鲜新版无需等待版本发布` + 可以从网页版直接使用,网页版访问地址:https://publish.terwer.space/blog/index.html + + 只要修改思源 API 地址和 token 即可,网页版需要填写外网地址。 + + **本插件承诺:所有配置均存储在浏览器本地,本程序不会收集任何敏感资料,请放心使用。本程序不会开发,将来也永远不会开发云端功能。 + ** + +3. 离线 zip 使用方法=>将 zip 文件解压到某个目录,打开浏览器插件开发者模式,点击加载已解压的扩展程序,选择刚刚解压的目录即可。 + +4. 因为所有配置是配置直接存储在浏览器本地,切换浏览器数据不会共享。卸载插件也会清空所有的配置数据,后续会考虑配置备份功能( + 需要调研实现这个需求的必要性)。 + +## 核心特性 + +目前支持基于 `Github` 0.0.1+ 、 `metaweblog API` 0.0.2+ 、`Wordpress API` 0.0.2+ +以及 `自定义HTTP协议` 1.0.0+(预研) 的平台 + +同时提供了一个 [统一通用的 API 适配器](https://github.com/terwer/sy-post-publisher/blob/main/utils/api.ts) +,让适配任何平台成为可能。 + +- [x] 兼容 Metaweblog API 以及自定义平台 0.0.3+ +- [x] 支持平台开关 0.0.3+ +- [x] 支持选择文章分类 0.0.3+ +- [x] 自动生成 yaml(目前兼容 Vuepress,0.1.0 会兼容更多平台) 0.0.1+ +- [x] 自动生成文档别名(使用 Google 翻译)、摘要与标签(使用 jieba 分词) 0.0.2+ +- [x] 支持文章与平台绑定 0.0.2+ +- [x] 支持文章文章更新与删除 0.0.2+ +- [x] 自动适配暗黑模式与浅色模式 0.0.3+ +- [x] 多语言支持,支持中文版和英文版 0.0.1+ +- [x] 支持子目录模式 0.1.0+ + + - 现在无需在所有页面引用挂件了,只需要在父级页面引用一个挂件即可。 + + 1. 如果检测到没有子文档,会兼容 0.0.3 版本以前的方式,展示当前文档的发布页面。 + + 2. 如果检测到有子文档,会以列表加分页的方式展示所有子文档列表。可单独选择某个子文档进行发布操作。 + +- [x] 发布页面支持预览 0.1.0+ +- [ ] 支持自定义接口协议 1.0.0+ +- [x] 支持文章标题使用数字编号 0.0.3+ +- [x] [多平台支持,并且持续适配中](https://github.com/terwer/sy-post-publisher#%E6%94%AF%E6%8C%81%E5%B9%B3%E5%8F%B0) + 0.0.1+ +- [x] 支持同步到 Github(Github pages、Hugo、Hexo、Jekyll、Vuepress、Vitepress、Nuxt content、Next.js),0.1.0+ + 之后更加通用,支持动态添加管理多个 0.0.1+ 0.1.0+ +- [x] 支持多种发布视图,简单模式、详细模式和源码模式(Github 系列平台) 0.1.0+ 0.0.1+ +- [x] 支持自定义设置 API 地址,支持本地、局域网、远程 0.1.0+ +- [x] 支持配置导入导出 0.2.0+ +- [x] 通用设置支持标题序号开关、正文 H1 开关、新窗口开关、自动标签开关 0.2.0+ +- [x] 支持设置 Anki 牌组标记 0.6.0+ + + - anki 使用入门请参考:https://ld246.com/article/1670312056742 + +- [x] 支持通过 PicGO 管理图床 + 0.6.0+ [挂件版已经完美集成 PicGO,可直接使用;浏览器插件只能通过 HTTP 调用本地的 PicGO,需手动下载和配置] + + - PicGO 使用注意事项: + + 1. 如果你使用的是浏览器插件,需要从这里下载配置 PicGO 客户端:https://github.com/Molunerfinn/PicGo/releases + + 2. 如果你使用的是思源笔记挂件,需要自行配置已经集成好的 PicGO,挂件版 PicGO + 配置文件在:`[思源工作空间]/data/widgets/sy-post-publisher/lib/picgo/picgo.cfg.json` + ,请参考 PicGO + 官方文档进行配置:https://picgo.github.io/PicGo-Core-Doc/zh/guide/config.html#%E6%89%8B%E5%8A%A8%E7%94%9F%E6%88%90 + +## Vue 3 + TypeScript + Vite + +项目使用 Vue 3 框架,TypeScript 开发语言,Vite 作为构建工具。 + +项目使用了 Vue 3 的 ` + + + + diff --git a/src/components/publish/PublishIndex.vue b/src/components/publish/PublishIndex.vue new file mode 100644 index 00000000..304e586e --- /dev/null +++ b/src/components/publish/PublishIndex.vue @@ -0,0 +1,186 @@ + + + + + + + diff --git a/src/components/publish/form/PublishPlatform.vue b/src/components/publish/form/PublishPlatform.vue new file mode 100644 index 00000000..b4f231e3 --- /dev/null +++ b/src/components/publish/form/PublishPlatform.vue @@ -0,0 +1,116 @@ + + + + + + + diff --git a/src/components/publish/form/PublishTags.vue b/src/components/publish/form/PublishTags.vue new file mode 100644 index 00000000..9711aab0 --- /dev/null +++ b/src/components/publish/form/PublishTags.vue @@ -0,0 +1,54 @@ + + + + + + + diff --git a/src/components/publish/form/PublishTips.vue b/src/components/publish/form/PublishTips.vue new file mode 100644 index 00000000..5d8a8c2d --- /dev/null +++ b/src/components/publish/form/PublishTips.vue @@ -0,0 +1,38 @@ + + + + + + + diff --git a/src/components/set/GeneralSetting.vue b/src/components/set/GeneralSetting.vue new file mode 100644 index 00000000..f3f5ec50 --- /dev/null +++ b/src/components/set/GeneralSetting.vue @@ -0,0 +1,36 @@ + + + + + + + diff --git a/src/components/set/PublishSetting.vue b/src/components/set/PublishSetting.vue new file mode 100644 index 00000000..08d35cec --- /dev/null +++ b/src/components/set/PublishSetting.vue @@ -0,0 +1,792 @@ + + + + + + + diff --git a/src/components/set/SetIndex.vue b/src/components/set/SetIndex.vue new file mode 100644 index 00000000..daa7856c --- /dev/null +++ b/src/components/set/SetIndex.vue @@ -0,0 +1,45 @@ + + + + + + + diff --git a/src/components/set/preference/ChangeLocal.vue b/src/components/set/preference/ChangeLocal.vue new file mode 100644 index 00000000..de422b5b --- /dev/null +++ b/src/components/set/preference/ChangeLocal.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/src/components/set/publish/PlatformAddForm.vue b/src/components/set/publish/PlatformAddForm.vue new file mode 100644 index 00000000..078582fd --- /dev/null +++ b/src/components/set/publish/PlatformAddForm.vue @@ -0,0 +1,302 @@ + + + + + + + diff --git a/src/components/set/publish/PlatformQuickAdd.vue b/src/components/set/publish/PlatformQuickAdd.vue new file mode 100644 index 00000000..5c0ae424 --- /dev/null +++ b/src/components/set/publish/PlatformQuickAdd.vue @@ -0,0 +1,140 @@ + + + + + + + \ No newline at end of file diff --git a/src/components/set/publish/PlatformUpdateForm.vue b/src/components/set/publish/PlatformUpdateForm.vue new file mode 100644 index 00000000..c71685af --- /dev/null +++ b/src/components/set/publish/PlatformUpdateForm.vue @@ -0,0 +1,224 @@ + + + + + + + diff --git a/src/components/set/publish/platform/dynamicConfig.spec.ts b/src/components/set/publish/platform/dynamicConfig.spec.ts new file mode 100644 index 00000000..c11ec721 --- /dev/null +++ b/src/components/set/publish/platform/dynamicConfig.spec.ts @@ -0,0 +1,50 @@ +/* + * 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 { + getNewPlatformKey, + getSubPlatformTypeByKey, + PlatformType, + SubPlatformType, +} from "~/src/components/set/publish/platform/dynamicConfig.ts" +import { describe, it, expect } from "vitest" + +describe("test dynamicConfig", () => { + it("test getSubPlatformTypeByKey", () => { + // const key = "custom_Zhihu-z2jom6d" + const key = "custom_Zhihu" + const result = getSubPlatformTypeByKey(key) + console.log("result=>", result) + expect(result).toBe(SubPlatformType.Custom_Zhihu) + }) + + it("test getNewPlatformKey", () => { + const ptype = PlatformType.Common + const subtype = SubPlatformType.Common_Yuque + const result = getNewPlatformKey(ptype, subtype) + console.log("result=>", result) + expect(result).toMatch(/common_Zhihu-\w+/) + }) +}) diff --git a/src/components/set/publish/platform/dynamicConfig.ts b/src/components/set/publish/platform/dynamicConfig.ts new file mode 100644 index 00000000..00c52853 --- /dev/null +++ b/src/components/set/publish/platform/dynamicConfig.ts @@ -0,0 +1,457 @@ +/* + * 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 { YamlConvertAdaptor } from "~/src/components/set/publish/platform/yamlConvertAdaptor.ts" +import idUtil from "~/src/utils/idUtil.ts" +import { StrUtil } from "zhi-common" + +export class DynamicConfig { + /** + * 动态平台类型(通用类型) + */ + platformType: PlatformType + + /** + * 子平台类型(细分子类型) + * + * @since 0.1.0+ + */ + subPlatformType?: SubPlatformType + + /** + * 平台Key + */ + platformKey: string + + /** + * 平台名称 + */ + platformName: string + + /** + * 平台图标(svg代码) + * + * @since 0.9.0+ + */ + platformIcon?: string + + /** + * 是否授权 + */ + isEnabled: boolean + + /** + * 是否授权 + */ + isAuth: boolean + + /** + * 授权模式 + */ + authMode: AuthMode + + /** + * 登录地址,网页授权需要 + */ + authUrl?: string + + /** + * 域名 + */ + domain?: string + + /** + * 是否内置 + */ + isSys: boolean + + /** + * YAML转换器 + */ + yamlConverter?: YamlConvertAdaptor + + constructor( + platformType: PlatformType, + platformKey: string, + platformName: string, + subPlatformType?: SubPlatformType, + platformIcon?: string, + yamlConverter?: YamlConvertAdaptor + ) { + this.platformType = platformType + this.platformKey = platformKey + this.platformName = platformName + this.isAuth = false + this.isEnabled = false + this.authMode = AuthMode.API + this.isSys = false + + this.subPlatformType = subPlatformType + this.platformIcon = platformIcon + this.yamlConverter = yamlConverter + } +} + +/** + * 授权模式 + */ +export enum AuthMode { + API = "api", + WEBSITE = "web", +} + +/** + * 动态平台类型枚举 + */ +export enum PlatformType { + /** + * 通用平台(Yuque) + */ + Common = "Common", + + /** + * Metaweblog + */ + Metaweblog = "Metaweblog", + + /** + * WordPress + */ + Wordpress = "Wordpress", + + /** + * GitHub(Hugo、Hexo、Jekyll、Vuepress、Vitepress、Nuxt content、Next.js) + */ + Github = "Github", + + /** + * 自定义(zhihu) + */ + Custom = "Custom", + + /** + * 内置平台,仅内部使用,用户不能使用也不能更改(Siyuan) + */ + System = "System", +} + +/** + * 平台子类型 + * + * @since 0.1.0+ + * @author terwer + */ +export enum SubPlatformType { + // Common + Common_Yuque = "Yuque", + + // Github 子平台 + Github_Hexo = "Hexo", + // Github_Hugo = "Hugo", + // Github_Jekyll = "Jekyll", + // Github_Vuepress = "Vuepress", + // Github_Vitepress = "Vitepress", + // Github_Nuxt = "Nuxt", + // Github_Next = "Next", + + // Metaweblog + Metaweblog_Cnblogs = "Cnblogs", + Metaweblog_Typecho = "Typecho", + + // WordPress + Wordpress_Wordpress = "Wordpress", + + // Custom + Custom_Zhihu = "Zhihu", + Custom_CSDN = "Csdn", + Custom_Jianshu = "Jianshu", + Custom_Juejin = "Juejin", + Custom_Wechat = "Wechat", + + // System + System_Siyuan = "Siyuan", + + NONE = "none", +} + +/** + * 动态配置类型封装 + */ +export interface DynamicJsonCfg { + totalCfg: DynamicConfig[] + commonCfg: DynamicConfig[] + githubCfg: DynamicConfig[] + metaweblogCfg: DynamicConfig[] + wordpressCfg: DynamicConfig[] + customCfg: DynamicConfig[] + systemCfg: DynamicConfig[] +} + +/** + * 获取子平台列表 + */ +export function getSubtypeList(ptype: PlatformType): SubPlatformType[] { + const subtypeList: SubPlatformType[] = [] + + switch (ptype) { + case PlatformType.Common: + subtypeList.push(SubPlatformType.Common_Yuque) + break + case PlatformType.Github: + subtypeList.push(SubPlatformType.Github_Hexo) + // subtypeList.push(SubPlatformType.Github_Hugo) + // subtypeList.push(SubPlatformType.Github_Jekyll) + // subtypeList.push(SubPlatformType.Github_Vuepress) + // subtypeList.push(SubPlatformType.Github_Vitepress) + // subtypeList.push(SubPlatformType.Github_Nuxt) + // subtypeList.push(SubPlatformType.Github_Next) + break + case PlatformType.Metaweblog: + subtypeList.push(SubPlatformType.Metaweblog_Cnblogs) + subtypeList.push(SubPlatformType.Metaweblog_Typecho) + break + case PlatformType.Wordpress: + subtypeList.push(SubPlatformType.Wordpress_Wordpress) + break + case PlatformType.Custom: + subtypeList.push(SubPlatformType.Custom_Zhihu) + subtypeList.push(SubPlatformType.Custom_CSDN) + subtypeList.push(SubPlatformType.Custom_Jianshu) + subtypeList.push(SubPlatformType.Custom_Juejin) + subtypeList.push(SubPlatformType.Custom_Wechat) + break + case PlatformType.System: + subtypeList.push(SubPlatformType.System_Siyuan) + break + default: + break + } + + return subtypeList +} + +/** + * 设置动态平台JSON配置 + * + * @param dynamicConfigArray + */ +export function setDynamicJsonCfg(dynamicConfigArray: DynamicConfig[]): DynamicJsonCfg { + const totalCfg: DynamicConfig[] = dynamicConfigArray + const commonCfg: DynamicConfig[] = [] + const githubCfg: DynamicConfig[] = [] + const metaweblogCfg: DynamicConfig[] = [] + const wordpressCfg: DynamicConfig[] = [] + const customCfg: DynamicConfig[] = [] + const systemCfg: DynamicConfig[] = [] + + // 按照类型组装便于后面数据使用 + totalCfg.forEach((item) => { + switch (item.platformType) { + case PlatformType.Common: + commonCfg.push(item) + break + case PlatformType.Github: + githubCfg.push(item) + break + case PlatformType.Metaweblog: + metaweblogCfg.push(item) + break + case PlatformType.Wordpress: + wordpressCfg.push(item) + break + case PlatformType.Custom: + customCfg.push(item) + break + case PlatformType.System: + systemCfg.push(item) + break + default: + break + } + }) + + const dynamicJsonCfg: DynamicJsonCfg = { + totalCfg, + commonCfg, + githubCfg, + metaweblogCfg, + wordpressCfg, + customCfg, + systemCfg, + } + + return dynamicJsonCfg +} + +// ===================== +// 动态平台key规则 +// ===================== +export function getSubPlatformTypeByKey(key: string): SubPlatformType { + const keyParts = key.split("-") + let subtype = "" + + if (keyParts.length > 0) { + const subPlatformParts = keyParts[0].split("_") + subtype = subPlatformParts.length > 1 ? subPlatformParts[1] : subPlatformParts[0] + } else { + throw new Error("Invalid platform key") + } + + const enumValues = Object.values(SubPlatformType) + const foundType = enumValues.find( + (value) => typeof value === "string" && value.toLowerCase() === subtype.toLowerCase() + ) + + if (foundType) { + return foundType as SubPlatformType + } + + throw new Error("Invalid platform key") +} + +/** + * 生成新的平台key + * + * 平台与ID之间用-分割 + * 平台与子平台直接用_分割 + * @param ptype 平台类型 + * @param subtype 子平台类型 + */ +export function getNewPlatformKey(ptype: PlatformType, subtype: SubPlatformType): string { + let ret: any + const newId = idUtil.newID() + ret = ptype.toLowerCase() + + if (!StrUtil.isEmptyString(subtype) && SubPlatformType.NONE !== subtype) { + ret = [ret, "_", StrUtil.upperFirst(subtype)].join("") + } + return [ret, "-", newId].join("") +} + +/** + * 检测动态平台key是否重复 + */ +export function isDynamicKeyExists(dynamicConfigArray: DynamicConfig[], key: string): boolean { + let flag = false + for (let i = 0; i < dynamicConfigArray.length; i++) { + if (dynamicConfigArray[i].platformKey === key) { + flag = true + break + } + } + return flag +} + +/** + * 通过平台key查询平台 + */ +export function getDynCfgByKey(dynamicConfigArray: DynamicConfig[], key: string): DynamicConfig { + for (let i = 0; i < dynamicConfigArray.length; i++) { + if (dynamicConfigArray[i].platformKey === key) { + return dynamicConfigArray[i] + } + } + return null +} + +/** + * 根据平台key替换平台配置 + * + * @param dynamicConfigArray 动态配置数组 + * @param key 平台key + * @param newConfig 新的平台配置 + * @returns 替换后的动态配置数组 + */ +export function replacePlatformByKey( + dynamicConfigArray: DynamicConfig[], + key: string, + newConfig: DynamicConfig +): DynamicConfig[] { + const newArray = [...dynamicConfigArray] + for (let i = 0; i < newArray.length; i++) { + if (newArray[i].platformKey === key) { + newArray[i] = newConfig + break + } + } + return newArray +} + +/** + * 从dynamicConfigArray数组中删除匹配给定key的元素 + * + * @param dynamicConfigArray - 要删除元素的数组 + * @param key - 要匹配的键 + * @returns 删除元素后的新数组 + */ +export function deletePlatformByKey(dynamicConfigArray: any[], key: string): any[] { + return dynamicConfigArray.filter((item) => item.platformKey !== key) +} + +// ===================== +// 动态平台文章ID规则 +// ===================== +/** + * 获取动态文章ID的key + * @param platformKey + */ +export function getDynPostidKey(platformKey: string): string { + return "custom-" + platformKey + "-post-id" +} + +// ====================== +// 动态平台Object对象初始化 +// ====================== +/** + * 根据平台key获取YAML转换器 + * @param platformKey + */ +export const getDynYamlConverterAdaptor = (platformKey: string): YamlConvertAdaptor => { + const yamlConverter = new YamlConvertAdaptor() + if (platformKey.includes("-")) { + const typeArr = platformKey.split("-") + if (typeArr.length > 0) { + const ptype = typeArr[0].toLowerCase() + + // if (ptype.includes(SubPlatformType.Github_Vuepress.toLowerCase())) { + // yamlConverter = new VuepressYamlConvertAdaptor() + // } else if (ptype.includes(SubPlatformType.Github_Hugo.toLowerCase())) { + // yamlConverter = new HugoYamlConverterAdaptor() + // } else if (ptype.includes(SubPlatformType.Github_Hexo.toLowerCase())) { + // yamlConverter = new HexoYamlConverterAdaptor() + // } else if (ptype.includes(SubPlatformType.Github_Jekyll.toLowerCase())) { + // yamlConverter = new JekyllYamlConverterAdaptor() + // } else if (ptype.includes(SubPlatformType.Github_Vitepress.toLowerCase())) { + // yamlConverter = new VitepressYamlConverterAdaptor() + // } else if (ptype.includes(SubPlatformType.Github_Nuxt.toLowerCase())) { + // yamlConverter = new NuxtYamlConverterAdaptor() + // } else if (ptype.includes(SubPlatformType.Github_Next.toLowerCase())) { + // yamlConverter = new NextYamlConvertAdaptor() + // } + } + } + + return yamlConverter +} diff --git a/src/components/set/publish/platform/yamlConvertAdaptor.ts b/src/components/set/publish/platform/yamlConvertAdaptor.ts new file mode 100644 index 00000000..8ab4833d --- /dev/null +++ b/src/components/set/publish/platform/yamlConvertAdaptor.ts @@ -0,0 +1,47 @@ +/* + * 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 { PostForm } from "~/src/models/postForm.ts" +import { YamlFormatObj } from "~/src/models/yamlFormatObj.ts" +import { CommonGithubConfig } from "~/src/adaptors/api/base/github/config/CommonGithubConfig.ts" + +export interface IYamlConvertAdaptor { + convertToYaml(postForm: PostForm, githubCfg?: CommonGithubConfig): YamlFormatObj + + convertToAttr(yamlObj: YamlFormatObj, githubCfg?: CommonGithubConfig): PostForm +} + +/** + * YAML转换适配器 + */ +export class YamlConvertAdaptor implements IYamlConvertAdaptor { + convertToYaml(postForm: PostForm, githubCfg?: CommonGithubConfig): YamlFormatObj { + throw new Error("YamlConvertAdaptor.convertToYaml: 该功能未实现,请在子类重写该方法") + } + + convertToAttr(yamlFormatObj: YamlFormatObj, githubCfg?: CommonGithubConfig): PostForm { + throw new Error("YamlConvertAdaptor.convertToAttr: 该功能未实现,请在子类重写该方法") + } +} diff --git a/src/components/set/publish/singleplatform/CommonBlogSetting.vue b/src/components/set/publish/singleplatform/CommonBlogSetting.vue new file mode 100644 index 00000000..b5004c75 --- /dev/null +++ b/src/components/set/publish/singleplatform/CommonBlogSetting.vue @@ -0,0 +1,251 @@ + + + + + + + diff --git a/src/components/set/publish/singleplatform/CookieSetting.vue b/src/components/set/publish/singleplatform/CookieSetting.vue new file mode 100644 index 00000000..459cf827 --- /dev/null +++ b/src/components/set/publish/singleplatform/CookieSetting.vue @@ -0,0 +1,126 @@ + + + + + + + diff --git a/src/components/set/publish/singleplatform/MetaweblogSetting.vue b/src/components/set/publish/singleplatform/MetaweblogSetting.vue new file mode 100644 index 00000000..d6b9675d --- /dev/null +++ b/src/components/set/publish/singleplatform/MetaweblogSetting.vue @@ -0,0 +1,244 @@ + + + + + + + diff --git a/src/components/set/publish/singleplatform/SettingEntry.vue b/src/components/set/publish/singleplatform/SettingEntry.vue new file mode 100644 index 00000000..b902d412 --- /dev/null +++ b/src/components/set/publish/singleplatform/SettingEntry.vue @@ -0,0 +1,63 @@ + + + + + + + diff --git a/src/components/set/publish/singleplatform/commonblog/YuqueSetting.vue b/src/components/set/publish/singleplatform/commonblog/YuqueSetting.vue new file mode 100644 index 00000000..3497f136 --- /dev/null +++ b/src/components/set/publish/singleplatform/commonblog/YuqueSetting.vue @@ -0,0 +1,55 @@ + + + + + + + diff --git a/src/components/set/publish/singleplatform/metaweblog/CnblogsSetting.vue b/src/components/set/publish/singleplatform/metaweblog/CnblogsSetting.vue new file mode 100644 index 00000000..6fee1e11 --- /dev/null +++ b/src/components/set/publish/singleplatform/metaweblog/CnblogsSetting.vue @@ -0,0 +1,54 @@ + + + + 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 00000000..625df62b --- /dev/null +++ b/src/components/set/publish/singleplatform/metaweblog/TypechoSetting.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/src/components/set/publish/singleplatform/metaweblog/WordpressSetting.vue b/src/components/set/publish/singleplatform/metaweblog/WordpressSetting.vue new file mode 100644 index 00000000..878256af --- /dev/null +++ b/src/components/set/publish/singleplatform/metaweblog/WordpressSetting.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/src/components/test/CnblogsTest.vue b/src/components/test/CnblogsTest.vue new file mode 100644 index 00000000..5bb363b7 --- /dev/null +++ b/src/components/test/CnblogsTest.vue @@ -0,0 +1,363 @@ + + + + + + + diff --git a/src/components/test/HexoTest.vue b/src/components/test/HexoTest.vue new file mode 100644 index 00000000..bdf203e8 --- /dev/null +++ b/src/components/test/HexoTest.vue @@ -0,0 +1,288 @@ + + + + + + + diff --git a/src/components/test/HugoTest.vue b/src/components/test/HugoTest.vue new file mode 100644 index 00000000..0f8bee24 --- /dev/null +++ b/src/components/test/HugoTest.vue @@ -0,0 +1,288 @@ + + + + + + + \ No newline at end of file diff --git a/src/components/test/SiyuanTest.vue b/src/components/test/SiyuanTest.vue new file mode 100644 index 00000000..bdeb9707 --- /dev/null +++ b/src/components/test/SiyuanTest.vue @@ -0,0 +1,364 @@ + + + + + + + diff --git a/src/components/test/TypechoTest.vue b/src/components/test/TypechoTest.vue new file mode 100644 index 00000000..69275c44 --- /dev/null +++ b/src/components/test/TypechoTest.vue @@ -0,0 +1,289 @@ + + + + + + + diff --git a/src/components/test/VitepressTest.vue b/src/components/test/VitepressTest.vue new file mode 100644 index 00000000..4c2ec904 --- /dev/null +++ b/src/components/test/VitepressTest.vue @@ -0,0 +1,288 @@ + + + + + + + diff --git a/src/components/test/WordpressTest.vue b/src/components/test/WordpressTest.vue new file mode 100644 index 00000000..b6746127 --- /dev/null +++ b/src/components/test/WordpressTest.vue @@ -0,0 +1,295 @@ + + + + + + + \ No newline at end of file diff --git a/src/components/test/YuqueTest.vue b/src/components/test/YuqueTest.vue new file mode 100644 index 00000000..beb982eb --- /dev/null +++ b/src/components/test/YuqueTest.vue @@ -0,0 +1,293 @@ + + + + + + + diff --git a/src/components/test/ZhihuTest.vue b/src/components/test/ZhihuTest.vue new file mode 100644 index 00000000..b98ea395 --- /dev/null +++ b/src/components/test/ZhihuTest.vue @@ -0,0 +1,137 @@ + + + + + + + diff --git a/src/components/test/kmsTest.vue b/src/components/test/kmsTest.vue new file mode 100644 index 00000000..755e9636 --- /dev/null +++ b/src/components/test/kmsTest.vue @@ -0,0 +1,312 @@ + + + + + + + diff --git a/src/composables/usePlatformDefine.ts b/src/composables/usePlatformDefine.ts new file mode 100644 index 00000000..2688b3fd --- /dev/null +++ b/src/composables/usePlatformDefine.ts @@ -0,0 +1,109 @@ +/* + * 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 { DynamicConfig, PlatformType } from "~/src/components/set/publish/platform/dynamicConfig.ts" +import { useVueI18n } from "~/src/composables/useVueI18n.ts" +import { pre } from "~/src/utils/import/pre.ts" + +/** + * 通用平台定义 + * + * @returns {Object} - 包含平台类型列表、getPlatformType函数和getPrePlatform函数的对象。 + * @since 0.9.0 + */ +export const usePlatformDefine = () => { + const { t } = useVueI18n() + + const platformTypeList = [ + { + type: PlatformType.Common, + title: t("setting.platform.universal"), + img: "./images/universal.webp", + description: t("setting.platform.universal.desc"), + }, + { + type: PlatformType.Github, + title: t("setting.platform.github"), + img: "./images/github.png", + description: t("setting.platform.github.desc"), + }, + { + type: PlatformType.Metaweblog, + title: t("setting.platform.metaweblog"), + img: "./images/xmlrpc.png", + description: t("setting.platform.metaweblog.desc"), + }, + { + type: PlatformType.Wordpress, + title: t("setting.platform.wordpress"), + img: "./images/wordpress-logo.svg", + description: t("setting.platform.wordpress.desc"), + }, + { + type: PlatformType.Custom, + title: t("setting.platform.custom"), + img: "./images/http.png", + description: t("setting.platform.custom.desc"), + }, + ] + + const prePlatformList: DynamicConfig[] = [ + ...pre.commonCfg, + ...pre.githubCfg, + ...pre.metaweblogCfg, + ...pre.wordpressCfg, + ...pre.customCfg + ] + + /** + * 根据键获取平台类型 + * + * @param key - 平台类型的键 + */ + const getPlatformType = (key) => { + return platformTypeList.find((platformType) => platformType.type === key) + } + + /** + * 根据类型获取预定义平台 + */ + const getPrePlatformList = (type: PlatformType): DynamicConfig[] => { + return prePlatformList.filter((platform) => platform.platformType === type) + } + + /** + * 根据类型获取预定义平台 + */ + const getPrePlatform = (key: string): DynamicConfig => { + return prePlatformList.find((platform) => platform.platformKey === key) + } + + return { + platformTypeList, + getPlatformType, + getPrePlatformList, + getPrePlatform, + } +} diff --git a/src/composables/usePublish.ts b/src/composables/usePublish.ts new file mode 100644 index 00000000..036db3f5 --- /dev/null +++ b/src/composables/usePublish.ts @@ -0,0 +1,141 @@ +/* + * 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 { reactive } from "vue" +import { SypConfig } from "~/syp.config.ts" +import { JsonUtil, StrUtil } from "zhi-common" +import { AppInstance } from "~/src/appInstance.ts" +import Adaptors from "~/src/adaptors" +import { Utils } from "~/src/utils/utils.ts" +import { Post } 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" +import { pre } from "~/src/utils/import/pre.ts" + +/** + * 通用发布组件 + */ +const usePublish = () => { + const logger = createAppLogger("use-publish") + + // uses + const { t } = useVueI18n() + const { getSetting, updateSetting } = useSettingStore() + const { kernelApi } = useSiyuanApi() + + // datas + const singleFormData = reactive({ + isPublishLoading: false, + publishProcessStatus: false, + errMsg: "", + + setting: {} as typeof SypConfig, + cfg: {} as any, + isAdd: true, + postid: "", + previewUrl: "", + }) + + const doSinglePublish = async (key: string, id: string, doc: Post) => { + try { + // 加载配置 + singleFormData.setting = await getSetting() + singleFormData.cfg = JsonUtil.safeParse(singleFormData.setting[key], {} as any) + + // 系统内置 + const isSys = pre.systemCfg.some((item) => item.platformKey === key) + logger.info("isSys=>", isSys) + + // 初始化API + const appInstance = new AppInstance() + const apiAdaptor = await Adaptors.getAdaptor(key) + const api = Utils.blogApi(appInstance, apiAdaptor) + logger.info("api=>", api) + + // 检测是否发布 + const posidKey = singleFormData.cfg.posidKey + if (StrUtil.isEmptyString(posidKey)) { + throw new Error("配置错误,posidKey不能为空,请检查配置") + } + const postMeta = singleFormData.setting[id] ?? {} + singleFormData.postid = postMeta[posidKey] ?? "" + singleFormData.isAdd = StrUtil.isEmptyString(singleFormData.postid) + + if (!singleFormData.isAdd || isSys) { + logger.info("文章已发布,准备更新") + const post = new Post() + post.title = doc.title + post.description = doc.description + // result 正常情况下就是 postid + const result = await api.editPost(singleFormData.postid, post) + logger.info("edit post=>", result) + } else { + logger.info("文章未发布,准备发布") + const post = new Post() + post.title = doc.title + post.description = doc.description + // result 正常情况下就是 postid + const result = await api.newPost(post) + + // 写入postid到配置 + singleFormData.postid = result + postMeta[posidKey] = singleFormData.postid + singleFormData.setting[id] = postMeta + await updateSetting(singleFormData.setting) + logger.info("new post=>", result) + } + const previewUrl = await api.getPreviewUrl(singleFormData.postid) + singleFormData.previewUrl = `${singleFormData.cfg.home}${previewUrl}` + + singleFormData.publishProcessStatus = true + } catch (e) { + singleFormData.errMsg = t("main.opt.failure") + "=>" + e + logger.error(e) + // ElMessage.error(singleFormData.errMsg) + await kernelApi.pushErrMsg({ + msg: singleFormData.errMsg, + timeout: 7000, + }) + singleFormData.publishProcessStatus = false + } + + return { + key: key, + status: singleFormData.publishProcessStatus, + name: singleFormData.cfg?.blogName, + previewUrl: singleFormData.previewUrl, + errMsg: singleFormData.errMsg, + } + } + + return { + singleFormData, + doSinglePublish, + } +} + +export { usePublish } diff --git a/src/composables/useSiyuanApi.ts b/src/composables/useSiyuanApi.ts new file mode 100644 index 00000000..ffbd60a5 --- /dev/null +++ b/src/composables/useSiyuanApi.ts @@ -0,0 +1,62 @@ +/* + * 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 { SiYuanApiAdaptor, SiyuanConfig, SiyuanKernelApi } from "zhi-siyuan-api" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { Utils } from "~/src/utils/utils.ts" + +/** + * 通用 Siyuan API 封装 + */ +export const useSiyuanApi = () => { + const logger = createAppLogger("use-siyuan-api") + + const siyuanApiUrl = Utils.emptyOrDefault(process.env.VITE_SIYUAN_API_URL, "") + 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, "") + const blogApi = new SiYuanApiAdaptor(siyuanConfig) + const kernelApi = new SiyuanKernelApi(siyuanConfig) + + const isStorageViaSiyuanApi = () => { + // docker - 在 .env.docker 配置 VITE_DEFAULT_TYPE=siyuan + // vercel - 在环境变量配置 VITE_DEFAULT_TYPE=siyuan + // node - 启动参数加 VITE_DEFAULT_TYPE=siyuan node VITE_SIYUAN_API_URL=http://127.0.0.1:6806 + // 插件SPA(PC客户端) - VITE_DEFAULT_TYPE: siyuan + // 插件SPA(Docker浏览器客户端) - VITE_DEFAULT_TYPE: siyuan + // 插件SPA(本地客户端浏览器) - VITE_DEFAULT_TYPE: siyuan + // const storeViaSiyuanApi = process.env.VITE_DEFAULT_TYPE === "siyuan" + const storeViaSiyuanApi = process.env.VITE_DEFAULT_TYPE === "siyuan" + logger.info("defaultType=>", process.env.VITE_DEFAULT_TYPE) + logger.info("storeViaSiyuanApi=>", String(storeViaSiyuanApi)) + return storeViaSiyuanApi + } + + return { + blogApi, + kernelApi, + isStorageViaSiyuanApi, + } +} diff --git a/src/composables/useSiyuanDevice.ts b/src/composables/useSiyuanDevice.ts new file mode 100644 index 00000000..1fa5601d --- /dev/null +++ b/src/composables/useSiyuanDevice.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 { DeviceDetection, DeviceTypeEnum } from "zhi-device" +import { createAppLogger } from "~/src/utils/appLogger.ts" + +/** + * 设备检测 + */ +export const useSiyuanDevice = () => { + const logger = createAppLogger("use-siyuan-device") + + const isInSiyuanMainWin = () => { + const deviceType = DeviceDetection.getDevice() + const isSiyuanOrSiyuanNewWin = deviceType === DeviceTypeEnum.DeviceType_Siyuan_MainWin + logger.debug("deviceType=>", deviceType) + logger.debug("isSiyuanOrSiyuanNewWin=>", String(isSiyuanOrSiyuanNewWin)) + return isSiyuanOrSiyuanNewWin + } + + const isInSiyuanWidget = () => { + const deviceType = DeviceDetection.getDevice() + const isSiyuanOrSiyuanNewWin = deviceType === DeviceTypeEnum.DeviceType_Siyuan_Widget + logger.debug("deviceType=>", deviceType) + logger.debug("isSiyuanOrSiyuanNewWin=>", String(isSiyuanOrSiyuanNewWin)) + return isSiyuanOrSiyuanNewWin + } + + const isInChromeExtension = () => { + const deviceType = DeviceDetection.getDevice() + const isSiyuanOrSiyuanNewWin = deviceType === DeviceTypeEnum.DeviceType_Chrome_Extension + logger.debug("deviceType=>", deviceType) + logger.debug("isSiyuanOrSiyuanNewWin=>", String(isSiyuanOrSiyuanNewWin)) + return isSiyuanOrSiyuanNewWin + } + + const isInSiyuanOrSiyuanNewWin = () => { + const deviceType = DeviceDetection.getDevice() + // 三种情况,主窗口、挂件、新窗口 + const isSiyuanOrSiyuanNewWin = + deviceType === DeviceTypeEnum.DeviceType_Siyuan_MainWin || + deviceType === DeviceTypeEnum.DeviceType_Siyuan_NewWin || + deviceType === DeviceTypeEnum.DeviceType_Siyuan_Widget + logger.debug("deviceType=>", deviceType) + logger.debug("isSiyuanOrSiyuanNewWin=>", String(isSiyuanOrSiyuanNewWin)) + return isSiyuanOrSiyuanNewWin + } + + return { isInSiyuanMainWin, isInSiyuanWidget, isInChromeExtension, isInSiyuanOrSiyuanNewWin } +} diff --git a/src/composables/useVueI18n.ts b/src/composables/useVueI18n.ts new file mode 100644 index 00000000..075a3879 --- /dev/null +++ b/src/composables/useVueI18n.ts @@ -0,0 +1,41 @@ +/* + * 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 { useI18n } from "vue-i18n" + +/** + * 多语言封装,解决 CSP + * + * https://github.com/intlify/vue-i18n-next/issues/543 + */ +export const useVueI18n = () => { + const { messages, locale } = useI18n() + + const translate = (key) => { + const localeMessages = messages.value?.[locale.value] + return localeMessages[key] || key + } + + return { t: translate, locale } +} diff --git a/src/composables/useVueRouter.ts b/src/composables/useVueRouter.ts new file mode 100644 index 00000000..7b5e67f7 --- /dev/null +++ b/src/composables/useVueRouter.ts @@ -0,0 +1,97 @@ +/* + * 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 { createRouter, createWebHashHistory, Router, RouteRecordRaw } from "vue-router" +import Home from "~/src/pages/Home.vue" +import Setting from "~/src/pages/Setting.vue" +import GeneralSetting from "~/src/components/set/GeneralSetting.vue" +import PublishSetting from "~/src/components/set/PublishSetting.vue" +import SettingEntry from "~/src/components/set/publish/singleplatform/SettingEntry.vue" +import PlatformAddForm from "~/src/components/set/publish/PlatformAddForm.vue" +import PlatformUpdateForm from "~/src/components/set/publish/PlatformUpdateForm.vue" +import PlatformQuickAdd from "~/src/components/set/publish/PlatformQuickAdd.vue" +import QuickPublish from "~/src/workers/quickPublish.vue" + +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") + +const routes: RouteRecordRaw[] = [ + { path: "/", component: Home }, + { path: "/workers/quickPublish/:key/:id", component: QuickPublish }, + { + path: "/test", + component: ApiTest, + children: [ + { path: "", component: SiyuanTest }, + { path: "siyuan", component: SiyuanTest }, + { path: "cnblogs", component: CnblogsTest }, + { path: "wordpress", component: WordpressTest }, + { path: "typecho", component: TypechoTest }, + { path: "yuque", component: YuqueTest }, + { path: "zhihu", component: ZhihuTest }, + ], + }, + { + path: "/setting", + component: Setting, + }, + { + path: "/setting/publish", + component: PublishSetting, + }, + { + path: "/setting/platform/quickadd/:type", + component: PlatformQuickAdd, + }, + { + path: "/setting/platform/add/:type", + component: PlatformAddForm, + }, + { + path: "/setting/platform/update/:key", + component: PlatformUpdateForm, + }, + { + name: "setting-platform-single", + path: "/setting/platform/single/:key", + component: SettingEntry, + }, + { + path: "/setting/general", + component: GeneralSetting, + }, +] + +export const useVueRouter = (): Router => { + return createRouter({ + history: createWebHashHistory(), + routes, + }) +} diff --git a/src/extensions/manifest.json b/src/extensions/manifest.json index d99121d5..a1148225 100644 --- a/src/extensions/manifest.json +++ b/src/extensions/manifest.json @@ -1,6 +1,6 @@ { "name": "思源笔记发布辅助工具", - "version": "1.0.0", + "version": "0.9.0", "manifest_version": 3, "author": "terwer", "icons": { diff --git a/src/extensions/mv2/manifest-v2-for-firefox.json b/src/extensions/mv2/manifest-v2-for-firefox.json index c22f7280..1b6f8817 100644 --- a/src/extensions/mv2/manifest-v2-for-firefox.json +++ b/src/extensions/mv2/manifest-v2-for-firefox.json @@ -1,6 +1,6 @@ { "name": "开发版 - 思源笔记发布辅助工具", - "version": "1.0.0", + "version": "0.9.0", "manifest_version": 2, "author": "terwer", "icons": { diff --git a/src/layouts/AppLayout.vue b/src/layouts/AppLayout.vue new file mode 100644 index 00000000..8cf0c6c4 --- /dev/null +++ b/src/layouts/AppLayout.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/src/layouts/default/AppLayoutDefault.vue b/src/layouts/default/AppLayoutDefault.vue new file mode 100644 index 00000000..0437a338 --- /dev/null +++ b/src/layouts/default/AppLayoutDefault.vue @@ -0,0 +1,48 @@ + + + + + + + diff --git a/src/layouts/default/DefaultFooter.vue b/src/layouts/default/DefaultFooter.vue new file mode 100644 index 00000000..cdfa339a --- /dev/null +++ b/src/layouts/default/DefaultFooter.vue @@ -0,0 +1,126 @@ + + + + + + + diff --git a/src/layouts/default/DefaultHeader.vue b/src/layouts/default/DefaultHeader.vue new file mode 100644 index 00000000..2faeb1bc --- /dev/null +++ b/src/layouts/default/DefaultHeader.vue @@ -0,0 +1,38 @@ + + + + + + + diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts new file mode 100644 index 00000000..04537b91 --- /dev/null +++ b/src/locales/en_US.ts @@ -0,0 +1,518 @@ +/* + * Copyright (c) 2022-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. + */ + +export default { + "lang.choose": "Choose lang", + "lang.choose.placeholder": "Please select language", + "theme.mode.choose": "Mode", + "theme.mode.dark": "Dark mode", + "theme.mode.light": "Light mode", + "service.tab.publish.service": "Publish service", + "service.tab.publish.setting": "Publish setting", + "service.tab.platform.setting": "Platform setting", + "service.tab.post.bind": "Post bind", + "service.tab.service.switch": "Service switch", + "service.tab.change.local": "Change language", + "main.publish.editmode": "Edit mode", + "main.publish.editmode.simple": "Simple", + "main.publish.editmode.complex": "Complex", + "main.publish.editmode.source": "Source", + "main.publish.vuepress.tip": + "Recommends use it with vuepress-theme-vdoing , and the subsequent bug will only update this theme", + "main.publish.github.error.tip": + "Configure error or API is not available, please check. If you modify the configuration, please refresh the page", + "main.publish.github": "Send Github", + "main.publish.github.tip": + "Checking the Markdown text of the article to the GitHub warehouse corresponding to Vuepress (need to perform related configuration first, unsatisfactory or configuration error will be ignored)", + "main.publish.github.choose.path": "Choose path", + "main.publish.github.published.path": "Published path", + "main.publish.github.choose.path.use.default": "Default path", + "main.publish.github.choose.path.use.default.tip": + "Using the default directory, all articles are published in the configuration default directory, no need to select the directory every time it is published", + "main.publish.github.no.tip": "You have not opened github release, please copy the right text on your own", + "main.publish.github.choose.title": "File name", + "main.publish.github.choose.title.tip": + "Vuepress-theme-VDoing support file name plus serial number to identify. If you don’t know this usage, please do not modify the file name", + "main.publish.see.preview": "Click here see new post", + "main.publish.see.md.preview": "Click here see new md file", + "main.publish.see.real.preview": "Click here see new post", + "main.publish.to.wordpress": "Send to WP", + "main.title": "Post title", + "main.slug": "Post slug", + "main.auto.fetch.slug": "Auto fetch slug", + "main.use.google.translate": "Use Google translate API", + "main.use.hash": "Use hash", + "main.use.hash.tip": + "Do not use HASH to show more meaningful URLs, but it may cause the only path of the article to repeat (the consequence is that Vuepress will build a failure unless manual solution to the other problem), please know", + "main.force.refresh": "Force refresh", + "main.force.refresh.tip": + "In order to prevent the wrongdoing aliases by mistake, the attribute will not be updated by default. If you need to update, please check for compulsory refresh.", + "main.desc": "Post desc", + "main.auto.fetch.desc": "Auto fetch desc", + "main.create.time": "Pub time", + "main.create.time.placeholder": "Please select time", + "main.tag": "Post tag", + "main.tag.new": "New tag", + "main.auto.fetch.tag": "Auto fetch tag", + "main.tag.auto.switch": "Tag switch", + "main.tag.auto.switch.no.tip": "Auto tag is closed", + "main.cat": "Category", + "main.cat.empty": "No Category", + "main.cat.select": "Please select category", + "main.yaml.formatter": "YAML Formatter", + "main.siyuan.to.yaml": "Siyuan attr to YAML", + "main.yaml.to.siyuan": "YAML to Siyuan attr", + "main.save.attr.to.siyuan": "Save attr to Siyuan", + "main.copy": "Copy", + "main.publish": "Publish", + "main.publish.loading": "Publishing...", + "main.loading": "Loading...", + "main.update": "Update", + "main.cancel": "Cancel", + "main.publish.oneclick.attr": "Click to attr", + "main.publish.oneclick.attr.finish": "Make attr finished", + "main.publish.status.unpublish": "Unpublish", + "main.publish.status.published": "Published", + "main.publish.to.vuepress": "Send to Vuepress", + "main.publish.to.hugo": "Send to Hugo", + "main.publish.to.hexo": "Send to Hexo", + "main.publish.to.jekyll": "Send to Jekyll", + "main.publish.to.jvue": "Send to JVue", + "main.publish.to.conf": "Send to Confluence", + "main.publish.to.cnblogs": "Send to Cnblogs", + "main.publish.to.liandi": "Send to Liandi", + "main.publish.to.yuque": "Send to Yuque", + "main.publish.to.kms": "Send to KMS", + "main.opt.success": "Success", + "main.opt.failure": "Error", + "main.copy.success": "Copy Success", + "main.copy.failure": "Copy Error", + "main.opt.quick": "Quick", + "main.opt.status.publish": "Post published success", + "main.opt.status.cancel": "Post publishing has been canceled", + "main.opt.loading": "Loading...", + "main.opt.warning": "Warning", + "main.opt.ok": "Confirm", + "main.opt.cancel": "Cancel", + "main.opt.warning.tip": "The operation will delete the data, whether to continue?", + "main.opt.tip": "Tips", + "setting.noneed": "No need setting", + "setting.blog.setting": " Setting", + "setting.blog.index": "System setting", + "setting.blog.type": "Blog type", + "setting.blog.type.placeholder": "Please select type", + "setting.blog.type.wordpress.value": "Wordpress", + "setting.blog.type.wordpress.label": "Wordpress", + "setting.blog.type.metaweblog.value": "MetaweblogApi", + "setting.blog.type.metaweblog.label": "Metaweblog API", + "setting.blog.type.github.value": "Github", + "setting.blog.type.github.label": "Github", + "setting.blog.type.github.user": "Github user", + "setting.blog.type.github.user.tip": "Github user, eg:terwer", + "setting.blog.type.github.repo": "Github repo", + "setting.blog.type.github.repo.tip": "Github repo, eg:terwer.github.io", + "setting.blog.type.github.token": "Github token", + "setting.blog.type.github.token.tip": "Github token, eg:ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "setting.blog.type.github.token.gen": "Generate Github Personal Token", + "setting.blog.type.github.default.path": "Doc path", + "setting.blog.type.github.default.path.tip": "Doc path, eg:docs/_posts", + "setting.blog.type.github.msg": "Commit msg", + "setting.blog.type.github.msg.tip": "Commit msg, eg:auto published by sy-post-publisher", + "setting.blog.type.github.author": "Author", + "setting.blog.type.github.author.tip": "Author, eg:terwer", + "setting.blog.type.github.email": "Email", + "setting.blog.type.github.email.tip": "Email", + "setting.blog.url": "Blog addr", + "setting.blog.previewUrl": "Preview rule", + "setting.blog.previewUrl.tip": + "Preview Rules (place occupied: [yyyy] [mm] [dd] [postid]), for example:/post/[postid].html or /[cats]/[yyyy]/[mm]/[dd]/[[dd]/[postid].html", + "setting.blog.previewMdUrl": "MD preview rule", + "setting.blog.previewMdUrl.tip": + "MD file preview rules (place occupied: [user] [repo] [Branch] [docpath]), for example:/[user]/[repo]/blob/[branch]/[docpath]", + "setting.blog.mdFilenameRule": "File rule", + "setting.blog.mdFilenameRule.tip": + "Markdown File Name Rules (Plasma: [yyyy] [MM] [dd] [slug] [filename]), for example: [filename].md or [yyyy]-[MM]-[dd]-[slug].md", + "setting.blog.username": "Username", + "setting.blog.password": "Password", + "setting.blog.apiurl": "API Url", + "setting.blog.pageType": "Page Type", + "setting.blog.validate": "Validate", + "setting.blog.save": "Save", + "setting.blog.vali": "Validate", + "setting.blog.vali.tip": + "Please verify the configuration to ensure the availability of the API, and it will not be used without verification.", + "setting.blog.vali.tip.metaweblog": + "Please verify the configuration to ensure the availability of the API, and it will not be used without verification.", + "setting.blog.vali.ing": "Validating...", + "setting.blog.vali.ok": "Available", + "setting.blog.vali.ok.metaweblog": "The configuration has been saved and verified", + "setting.blog.vali.error": "Validate error", + "setting.blog.platform.support.github": "Current github supported platform:", + "setting.blog.platform.support.metaweblog": "Current metaweblog supported platform:", + "setting.blog.cancel": "Cancel", + "setting.vuepress": "Vuepress", + "setting.hugo": "Hugo", + "setting.hexo": "Hexo", + "setting.jekyll": "Jekyll", + "setting.jvue": "JVue", + "setting.conf": "Confluence", + "setting.conf.tip": + "Published to Confluence needs to cooperate with my other adapter project (strong recommendation), please refer to:https://github.com/terwer/node-metaweblog-api-adaptor", + "setting.cnblogs": "Cnblogs", + "setting.wordpress": "Wordpress", + "setting.liandi": "Liandi", + "setting.yuque": "Yuque", + "setting.kms": "KMS", + "setting.other1": "Other1", + "service.switch.vuepress": "Vuepress", + "service.switch.hugo": "Hugo", + "service.switch.hexo": "Hexo", + "service.switch.jekyll": "Jekyll", + "service.switch.jvue": "JVue", + "service.switch.conf": "Confluence", + "service.switch.cnblogs": "Cnblogs", + "service.switch.wordpress": "Wordpress", + "service.switch.yuque": "Yuque", + "service.switch.liandi": "Liandi", + "service.switch.wechat": "WeChat", + "service.switch.wemp": "WeMedia", + "service.switch.zhihu": "Zhihu", + "service.switch.kms": "LandrayKms", + "service.switch.must.select.one": "You must add at least one platform", + "post.bind.vuepress.slug": "Vuepress slug", + "post.bind.hugo.slug": "Hugo slug", + "post.bind.hexo.slug": "Hexo slug", + "post.bind.jekyll.slug": "Jekyll slug", + "post.bind.cnblogs.postid": "Cnblogs ID", + "post.bind.jvue.postid": "JVue ID", + "post.bind.conf.postid": "Conf ID", + "post.bind.wordpress.postid": "WP ID", + "post.bind.liandi.postid": "Liandi ID", + "post.bind.yuque.postid": "Yuque ID", + "post.bind.kms.postid": "KMS ID", + "post.bind.conf.save": "Save", + "post.bind.conf.cancel": "Cancel", + "form.validate.name.required": "Please input name", + "form.validate.name.length": "Length should be 0 to 1", + "form.validate.github.auto.delete": "Auto delete", + "dynamic.platform.new": "New platform", + "dynamic.platform.tip": + "If the built -in release service cannot be satisfied or multiple instances need to be added, you can add supporting platforms here. Currently supporting Metaweblog API and WordPress API", + "dynamic.platform.type": "Platform type", + "dynamic.platform.subtype": "Platform subtype", + "dynamic.platform.type.metaweblog": "metaweblog API", + "dynamic.platform.type.wordpress": "Wordpress", + "dynamic.platform.type.common": "Common", + "dynamic.platform.type.custom": "Custom", + "dynamic.platform.key": "Platform key", + "dynamic.platform.key.tip": "The unique logo of the platform, cannot be repeated, for example: myblog", + "dynamic.platform.type.github": "Github", + "dynamic.platform.name": "Platform name", + "dynamic.platform.name.tip": "Platform name, for example: my blog", + "dynamic.platform.opt.add": "Submit", + "dynamic.platform.opt.noselect": "Please select platform type", + "dynamic.platform.opt.key.exist": "Platform key exists", + "dynamic.platform.opt.del.select": "Delete", + "dynamic.platform.opt.del.confirm": + "Deleting this platform will completely remove the relevant configuration information of this platform. Do you continue?", + "dynamic.platform.opt.item.select": "Item no select", + "dynamic.platform.opt.item.select.tip": "The current selection is:", + "dynamic.platform.opt.item.no.select.tip": "Please select the line to be deleted", + "setting.common.home": "Home", + "setting.common.tip": "The platform homepage is used to preview articles, for example: https://terwork.space", + "setting.common.apiurl": "API", + "setting.common.token": "Token", + "setting.common.token.gen": "Token addr", + "setting.common.username": "Username", + "setting.common.username.gen": "Username setting", + "setting.common.password": "Password", + "setting.blog.platform.support.common": "Current platform:", + "platform.must.select.one": "You must enable at least one platform", + "blog.list.toptip": + "Reminder: Click the table line to enter the article details page. The details page has the release button! (〃'▽'〃)", + "config.error.msg": "Network or config error, no data fetched", + "config.platform.none": "No available platform, please go to switch", + "post.delete.by.platform": + "The article has been deleted by the platform by itself, please click to cancel the compulsory lifting connection", + "blog.top-data-tip": + "Reminder: Please ensure that the Siyuan notes are started and opened the servo. The default servo address: http://127.0.0.0.1: 6806.0.0. Note: The modification will cover the value of the startup setting.", + "blog.top-data-tip.siyuan": + "It is currently in the pendant mode. Starting from version 0.1.0, there is no need to reference the pendant on all pages. You only need to reference a pendant on the parent page. The specific rules are as follows: 1. If there are no subsidiaries, it will be compatible with the previous method of version 0.0.3 to display the release page of the current document. 2. If a subsidiary document is detected, all the subsidiaries will be displayed in a list and paging. You can choose a sub -document separately for release operations.", + "blog.newwin.open": "Open in a new window", + "blog.change.siyuan.api": "Modify Siyuan API address", + "setting.blog.siyuan.password": "Siyuan Note token, please copying from【set up-> about 】", + "setting.blog.siyuan.apiurl": + "The API address of Siyuan Note, including ports, for example: http://127.0.0.0.1: 6806", + "setting.blog.siyuan.current.apiurl": "Current apiUrl=>", + "setting.conf.export": "Export config", + "setting.conf.import": "Import config", + "setting.conf.transport": "Transport", + "setting.conf.clear": "Clear config", + "page.no.id": "pageId fetch error", + "main.read.mode": "Readonly", + "main.edit.mode": "Editable", + "main.read.mode.tip": "Readonly tips", + "main.opt.onclick.confirm.tip": + "This operation will re -generate all attributes other than alias. If you just want to modify individual attributes, please click the individual function button to operate in the detailed mode. Do you continue?", + "main.cat.list.error": "Category list get error", + "main.yaml.no.save": + "Have you edited YAML, do you synchronize to attributes, and cancel the loss of YAML modification?", + "setting.github.baseUrl": "Git repo index", + "setting.github.baseUrl.tip": "Git platform address is used for md file preview, the default is: https://github.com", + "preference.setting.fixTitle": "Fix title", + "preference.setting.removeH1": "Remove h1", + "preference.setting.removeH1.tip": + "Delete H1 may accidentally delete the beginning of the beginning of Markdown. If you need to open it, please use more than 2 ones.", + "preference.setting.newWin": "New win", + "siyuan.browser.show.close.btn": "Show close btn", + "siyuan.browser.menu.quick.btn": "Quick publish", + "siyuan.browser.menu.publish.btn": "Publish", + "siyuan.browser.menu.preview.btn": "Preview", + "siyuan.browser.menu.manage.btn": "Manage", + "siyuan.browser.menu.anki.btn": "Anki", + "siyuan.browser.menu.picture.btn": "PicGO", + "siyuan.browser.menu.setting.btn": "Setting", + "anki.siyuan.deck": "Deck", + "anki.siyuan.deck.new": "New deck", + "anki.siyuan.tag": "Tag", + "anki.siyuan.tag.new": "New tag", + "picgo.chrome.tip": + "If you are using a browser plug -in, you need to download the configuration Picgo client from here: https://github.com/Molunerfinn/PicGo/releases", + "picgo.siyuan.tip": + "If you are using Siyuan notes pendants, you need to configure your own integration of PICGO. The pendant version of the PICGO configuration file is:`[Siyuan Workspace]/data/widgets/sy-post-publisher/lib/picgo/picgo.cfg.json`,Please refer to the official picgo document for configuration:https://picgo.github.io/PicGo-Core-Doc/zh/guide/config.html#%E6%89%8B%E5%8A%A8%E7%94%9F%E6%88%90", + "post.detail.button.copy.id": "Copy post ID", + "post.detail.button.share.link": "Copy share link", + "post.detail.button.browser.open": "Open with default browser", + "post.detail.button.pic.manage": "Picture manage", + "post.detail.button.anki.mark": "Anki mark", + "post.detail.index.send.to.publish": "Send to other platform", + "post.detail.index.export.to.pdf": "Export to PDF", + "picgo.upload.status": "Upload status", + "picgo.upload.select.pic": "Select picture", + "picgo.upload.clipboard": "Clipboard picture", + "picgo.upload.onclick": "One click upload", + "picgo.download.onclick": "One click download", + "picgo.download.local.to.bed": "Upload picture to bed", + "picgo.download.bed.to.local": "Upload picture from bed", + "picgo.pic.preview": "Picture preview", + "picgo.pic.setting": "Picture setting", + "picgo.pic.setting.no.tip": + "You are currently using the browser plug -in and cannot inherit PicGo. You can only call the local Picgo client on the local Picgo client through HTTP", + "github.use.permalink": "Use permalink", + "github.use.permalink.no.warn": + "You have closed the permanent link, and Formatter will not generate a mark, which may be useful in some scenarios", + "github.menu.title": "Menu title", + "github.menu.title.placeholder": + "The menu column title (HUGO platform is dedicated, but the empty is not displayed in the menu)", + "github.weight": "Weight", + "github.weight.placeholder": "Weight (decide to display the order, the smaller the display, the more upward)", + "github.use.date": "Show date", + "github.use.date.no.warn": "You have closed the date show that it will not generate the date field in Formatter", + "github.post.picgo.use": "Use picbed", + "github.post.picgo.use.tip": + "To enable this option, the local picture will be uploaded to the custom diagram bed (the remote picture is not uploaded), please make sure that the PICGO diagram bed configuration is correct", + "github.post.picgo.start.upload": + "The picture bed has been opened, and the picture bed is about to upload the picture bed to the bed", + "github.post.picgo.picbed.error": + "The picture uploaded to the picture bed failed, it may be an error in the configuration of the graph, please check the picture bed configuration", + "setting.yuque.home.tip": "The homepage of the language bird platform is usually fixed: https://www.yuque.com/", + "setting.yuque.username.tip": + "The user name of the spark platform, note that the user name is fixed in [account settings], not the user nickname (*^▽^*)", + "setting.yuque.password.tip": + "For the token of the spark platform, please follow the link to get it. You only need to grant the document to read and modify the two permissions.", + "setting.yuque.apiurl.tip": + "The API address of the language bird platform is usually fixed: https://www.yuque.com/api/v2", + "setting.liandi.home.tip": "The homepage of the chain drop platform is usually fixed: https://ld246.com/", + "setting.liandi.username.tip": + "The user name of the chain drop platform, note that after clicking [Personal Homepage], the user name displayed behind the MEMBER on the URL is not a user nickname (*^▽^*)", + "setting.liandi.password.tip": "Token on the chain drop platform, please click on the link to get", + "setting.liandi.apiurl.tip": "The API address of the chain drop platform is usually fixed: https://ld246.com/api/v2", + "setting.kms.home.tip": "KMS platform homepage, for example: http:// localhost: 9564/kms16_release/kms/multidoc", + "setting.kms.username.tip": "KMS platform REST account user name, please create according to the link instruction", + "setting.kms.password.tip": "KMS platform REST account password, please create according to the link instruction", + "setting.kms.apiurl.tip": + "The API address of the KMS platform, for example: http:// localhost: 9564/kms16_release/API/KMS-Multidoc/KMSMultidOWRESTSERVICE", + "siyuan.local.share.tip": + "Reminder: If you find that the sharing link or the browser can be displayed or the network connection is rejected, please check according to the following operation. 1: Siyuan Note-> Settings-> About-> Open the network servo; 2: After the browser is opened, click the bottom Siyuan address at the bottom to modify the corresponding address to the local area network IP address.", + "setting.cnblogs.home.tip": "The homepage of your blog park is usually: https://www.cnblogs.com/ ", + "setting.cnblogs.username.tip": + "Your blog garden MetaWeblog login name, you can go to your blog garden background-> Settings, note: login name, not a user nickname (*^▽^*)", + "setting.cnblogs.password.tip": + "Your blog garden Metaweblog access token, you can go to your blog garden backstage-> Settings", + "setting.cnblogs.apiUrl.tip": + "Your blog garden Metaweblog access address, you can go to your blog park background-> settings, usually: https://rpc.cnblogs.com/metaweblog/ ", + "setting.cnblogs.previewUrl.tip": "Preview rules for your blog garden article", + + "setting.wordpress.home.tip": "Wordpress homepage", + "setting.wordpress.username.tip": "WordPress login name, note: it is login name, not a user nickname (*^▽^*)", + "setting.wordpress.password.tip": "Wordpress login password", + "setting.wordpress.apiUrl.tip": + "WordPress's XMLRPC remote release address, usually: https:// /xmlrpc.php", + "setting.wordpress.previewUrl.tip": "The preview rules of the WordPress platform are usually :? P = [Postid]", + + "setting.oschina.home.tip": "Homepage of open source China, usually: https://my.oschina.net/ ", + "setting.oschina.username.tip": "Open source China login name, you can enter the login mailbox", + "setting.oschina.password.tip": "Open source China login password", + "setting.oschina.apiUrl.tip": + "Open source China XMLRPC remote publishing address, usually fixed: https://www.oschina.net/ACTION/xmlrpc", + "setting.oschina.previewUrl.tip": "The preview rules of the open source China platform are usually:/blog/[postid]", + + "setting.typecho.home.tip": "Homepage of the Typecho platform", + "setting.typecho.username.tip": "Typecho platform login name", + "setting.typecho.password.tip": "Typecho platform password", + "setting.typecho.apiUrl.tip": + "The XMLRPC remote publishing address of the Typecho platform is usually fixed: https: // /index.php/xmlrpc", + "setting.typecho.previewUrl.tip": + "The preview rules of the Typecho platform are usually: /index.php/archives/ [Postid]", + + "setting.jvue.home.tip": "Homepage of the Jvue platform", + "setting.jvue.username.tip": "Jvue platform login name", + "setting.jvue.password.tip": "Jvue platform password", + "setting.jvue.apiUrl.tip": + "The XMLRPC remote release address of the JVUE platform is usually fixed: https: // /xmlrpc", + "setting.jvue.previewUrl.tip": "The preview rules of the Jvue platform are usually:/post/[postid] .html", + + "setting.conf.home.tip": + "Confluence Cloud, homepage, usually: https: // .atlassian.net/wiki/spaces/", + "setting.conf.username.tip": "CONFLUENCE login name, you can go to your Atlassian account to get", + "setting.conf.password.tip": "Confluence access token, you can go to your Atlassian account to get", + "setting.conf.apiUrl.tip": "You can go to https://developper.atlassian.com/Cloud/confluence/rest/v1/1/1/", + "setting.conf.previewUrl.tip": "CONFLUENCE platform articles preview rules, usually:/pages/[postid]", + "setting.picgo.refer.to": "For details, please refer to:", + "setting.picgo.refer.to.online.doc": "Picgo configuration online documentation", + "setting.picgo.picbed": "Picbed setting", + "setting.picgo.picgo": "PicGO setting", + "main.opt.edit": "Edit", + "main.opt.delete": "Delete", + "setting.picgo.picgo.open.config.file": "Open config file", + "setting.picgo.picgo.click.to.open": "Click open", + "setting.picgo.picgo.choose.showed.picbed": "Please select showed picbed", + "setting.picgo.picbed.selected.tip": "Selected", + "setting.picgo.picbed.unselected.tip": "Unselected", + "setting.picgo.picbed.set.default": "Set as default picbed", + "setting.picgo.picbed.current.selected.tip": "Current selected picbed is:", + "setting.picgo.picbed.current.tip": "Current picbed is:", + "setting.picgo.picbed.uploader.config.name": "Picbed config name", + "setting.picgo.picbed.uploader.config.name.placeholder": "Please input config name", + "setting.picgo.config.name": "Config name", + "setting.picgo.config.name.placeholder": "Please input config name", + "setting.picgo.index.tip": + "Note: The image uploaded here will not be automatically inserted into the document. Please manually click the copy button to copy the link, and then Ctrl+V to paste it into the document. This picture list only shows the pictures contained in this document.", + "setting.picgo.index.copy.link": "Copy link", + "setting.picgo.setting.timestamp.rename": "Timestamp rename", + "setting.picgo.setting.close": "Close", + "setting.picgo.setting.open": "Open", + "setting.picgo.setting.log.file": "Log file", + "setting.picgo.plugin": "Plugin setting", + "setting.picgo.plugin.list": "Plugin list", + "setting.picgo.plugin.import.local": "Import local plugin", + "setting.picgo.plugin.install": "Install", + "setting.picgo.plugin.installing": "Installing", + "setting.picgo.plugin.installed": "Installed", + "setting.picgo.plugin.doing.something": "Loading", + "setting.picgo.plugin.search.placeholder": + "Search the picgo plug -in on NPM, or click the button above to view the excellent plug -in list", + "setting.picgo.plugin.gui.not.implemented": + "This plug -in does not optimize the visual interface, is it continued to be installed?", + "setting.picgo.setting.node.path": "Node install path", + "setting.picgo.setting.node.path.tip": + "Please enter Node to install the full path, for example: /opt/node-v16.14.0-darwin-x64/bin, node version> = 16", + "setting.picgo.setting.node.registry": "NPM registry", + "setting.picgo.setting.node.registry.tip": "Mainland recommend:https://registry.npmmirror.com", + "setting.picgo.setting.node.proxy": "NPM proxy", + "setting.picgo.setting.node.proxy.tip": + "Generally, you can leave empty. If you have special needs, you can set a custom agent", + "setting.picgo.setting.config.tip": + "Note: The attributes above the horizontal line will change the behavior of uploading pictures, and the configuration can be stored automatically. The attribute below the horizontal line is only used when installing the plug -in, and you need to click the [Confirm] button to save.", + "setting.picgo.plugin.install.success": + "The plug -in has been successfully installed, please clear the search keyword refresh list. Click the plug -in corresponding [Settings] icon to view the plug -in details and customize the plug -in", + "syp.about": "About me", + "setting.picgo.plugin.uninstall": "Uninstall plugin", + "setting.picgo.plugin.uninstall.success": "Plugin is uninstalled successfully", + "setting.picgo.plugin.enable": "Enable plugin", + "setting.picgo.plugin.disable": "Disable plugin", + "setting.picgo.plugin.update": "Update plugin", + "setting.picgo.plugin.config.setting": "Plugin setting", + "setting.picgo.plugin.work": "Work", + "setting.picgo.plugin.nowork": "Nowork", + "setting.picgo.plugin.nouse": "Nouse", + "setting.picgo.plugin.update.success": "Update success", + "setting.conf.import.syp": "Import pendant configuration", + "setting.conf.import.picgo": "Import picgo configuration", + "setting.conf.export.syp": "Export pendant configuration", + "setting.conf.export.picgo": "Export picgo configuration", + "setting.conf.import.syp.tip": + "Please select effective historical backup JSON files for import. Note: The pendant configuration will be replaced.", + "setting.conf.import.picgo.tip": + "Please select effective historical backup JSON files for import. Note: PICGO configuration will be replaced directly. For the compatibility of configuration, please uninstall all plug -in and perform the picgo configuration export operation. Uninstall the PICGO plugin will not delete the plug -in configuration, only the plug -in file and the plug -in registration record will be deleted. Please rest assured.", + "setting.conf.export.syp.tip": + "The corresponding JSON configuration file will be exported, and configuration backups are strongly recommended. In principle, support is compatible. If there is any compatibility problem, it will be specifically explained.", + "setting.conf.export.picgo.tip": + "The corresponding JSON configuration file will be exported, and configuration backups are strongly recommended. In principle, support is compatible. If there is any compatibility problem, it will be specifically explained.", + "setting.conf.clear.syp": "Clear pendant configuration", + "setting.conf.clear.picgo": "Clear picgo configuration", + "setting.conf.clear.tip": + "Clear configuration cannot be restored, please do it carefully. It is strongly recommended to backup configuration first.", + "setting.conf.clear.picgo.tip": + "Clear configuration cannot be restored, please do it carefully. It is strongly recommended to backup configuration first. This operation will delete PicGo -related configuration folders, including configuration, plug -in, cache, etc. It is used only when picgo is completely unavailable, otherwise the consequences of the loss of configuration will be at your own risk!", + "setting.main.background": "Widget background", + "setting.main.background.tip": + "Support hexadecimal and RGB, for example: #000000, no settings or staying empty unchanged", + "setting.picgo.manage": "Picture manage", + "setting.platform.add": "Add platform", + "setting.platform.add.this": "Add this platform", + "setting.platform.universal": "Universal platform", + "setting.platform.universal.desc": + "Currently supported general platforms include Zhihu, Yuque, CSDN, etc. Click the icon to quickly add, or click the button below to customize and add", + "setting.platform.wordpress": "WordPress", + "setting.platform.wordpress.desc": + "WordPress is a free and open source blog software and Content management system based on PHP and MySQL. WordPress has a plugin architecture and template system. As of April 2018, over 30.6% of the top 10 million websites used WordPress. WordPress is the most popular website Content management system. Approximately 40% of global websites (7500 million) are built using WordPress. WordPress is currently the most popular blog system on the Internet. WordPress stands out in the most famous stage of online publishing. Today, it is used on over 70 million sites.", + "setting.platform.github": "Github", + "setting.platform.github.desc": + "At present, the platforms that support Github release are: Hexo, Hugo, Jekyll, Vitepress, etc., using API authorization. GitHub is a platform and cloud-based service that uses Git for software development and version control, allowing developers to store and manage their code.", + "setting.platform.metaweblog": "Metaweblog", + "setting.platform.metaweblog.desc": + "At present, the platforms that support Metaweblog are: Blog Park, Typecho, etc., using API authorization. The MetaWeblog API is an application programming interface created by software developer Dave Winer that allows writing, editing, and deletion using Web services. The API is implemented as an XML-RPC Web service with three methods whose names describe its functionality: metaweblog.newPost(), metaweblog.getPost(), and metaweblog.editPost(). These methods take parameters that specify the username and password of the blogger and information related to a single blog entry.", + "setting.platform.custom": "Customization", + "setting.platform.custom.desc": "Customize the HTTP protocol, stay tuned", + "setting.platform.right.tips0": "Special Notes:", + "setting.platform.right.tips1": + "1. You can publish the configuration here, and you can directly click the menu or the following icons to configure it.", + "setting.platform.right.tips2": "2. If you need to add a new platform, click the left button directly.", + "setting.platform.right.tips3": + "3. At present, we support webpage authorization and API authorization. The complex points of API authorization are relatively stable. The webpage authorization is simple but may fail. Surprise: Webpage authorization mode 100 compatible", + "setting.platform.right.tips4": "4. If you need to be compatible with other platforms, please contact me", + "setting.entry.title": "Platform Unified Settings - ", + "setting.entry.not.supported": "Unsupported platforms", + "setting.upgrade.syp.tip1": "Ready to start migration ...", + "setting.upgrade.syp.tip2": "Migration successfully.", + "setting.upgrade.syp.tip3": "It is already the latest, no need to migrate.", + "setting.upgrade.syp.tip4": "Migration failed, the error is as follows", + "setting.upgrade.syp.tip5": "The migration operation is completed.", + "setting.upgrade.syp.doTip1": "Detect whether the configuration file is the latest version", + "setting.upgrade.syp.doTip2": "Detected old configuration, preparation to upgrade the configuration file", + "setting.upgrade.syp.doTip3": "Old configuration upgraded", + "setting.upgrade.syp.doTip4": "No update for version, skip upgrade", +} diff --git a/src/locales/index.ts b/src/locales/index.ts new file mode 100644 index 00000000..8fcfc7be --- /dev/null +++ b/src/locales/index.ts @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022, 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 { createI18n } from "vue-i18n" +import zh_CN from "./zh_CN" +import en_US from "./en_US" + +const i18n = createI18n({ + legacy: false, + locale: "zh_CN", // 默认显示语言 + fallbackLocale: "en_US", // 次要语言 + messages: { + zh_CN, + en_US, + }, +}) + +export default i18n diff --git a/src/locales/zh_CN.ts b/src/locales/zh_CN.ts new file mode 100644 index 00000000..66d88fe4 --- /dev/null +++ b/src/locales/zh_CN.ts @@ -0,0 +1,505 @@ +/* + * Copyright (c) 2022-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. + */ + +export default { + "lang.choose": "语言选择", + "lang.choose.placeholder": "请选择语言", + "theme.mode.choose": "切换模式", + "theme.mode.dark": "暗黑模式", + "theme.mode.light": "浅色模式", + "service.tab.publish.service": "发布服务", + "service.tab.publish.setting": "发布配置", + "service.tab.platform.setting": "平台配置", + "service.tab.post.bind": "文章绑定", + "service.tab.service.switch": "服务开关", + "service.tab.change.local": "偏好设置", + "main.publish.editmode": "编辑模式", + "main.publish.editmode.simple": "简洁模式", + "main.publish.editmode.complex": "详细模式", + "main.publish.editmode.source": "源码模式", + "main.publish.vuepress.tip": + "推荐Vuepress的V1版本,配合 vuepress-theme-vdoing 使用,后续bug也只会对V1和此主题进行更新", + "main.publish.github.error.tip": "配置错误或者api不可用,请检查。若修改过配置,请刷新页面", + "main.publish.github": "发布页面", + "main.publish.github.tip": "自动将文章的markdown文本发布到Vuepress对应的Github仓库(需要先进行相关配置)", + "main.publish.github.no.tip": "您未开启Github pages发布,请切换源码模式复制YAML以及Markdown文本", + "main.publish.github.choose.path": "选择目录", + "main.publish.github.published.path": "发布路径", + "main.publish.github.choose.path.use.default": "默认目录", + "main.publish.github.choose.path.use.default.tip": + "使用默认目录,则默认所有文章都发布在配置好的默认目录下(可在发布配置设置),无需每次发布的时候来选择目录,当前平台的默认目录为:", + "main.publish.github.choose.title": "文件名称", + "main.publish.github.choose.title.tip": + "Vuepress-theme-vdoing支持文件名加上序号来标识,如果你不清楚此用法,请不要修改文件名", + "main.publish.github.failure": "发布失败,请自行复制右侧文本", + "main.publish.see.preview": "点击这里查看最新文章", + "main.publish.see.md.preview": "点击这里查看MD文件", + "main.publish.see.real.preview": "点击这里查看最新文章", + "main.publish.to.wordpress": "发布到Wordpress", + "main.title": "文章标题", + "main.slug": "文章别名", + "main.auto.fetch.slug": "自动生成别名", + "main.use.google.translate": "使用Google翻译API", + "main.use.hash": "别名后缀", + "main.use.hash.tip": + "不使用hash会展示更有意义的url,但是可能导致文章别名重复(后果是Vuepress会Build失败除非手动解决别名唯一问题),请知悉。", + "main.force.refresh": "刷新别名", + "main.force.refresh.tip": "为了防止误操作更新别名,别名不为空的时候默认不会更新别名,如需更新,请勾选刷新别名。", + "main.desc": "文章摘要", + "main.auto.fetch.desc": "自动提取摘要", + "main.create.time": "发布时间", + "main.create.time.placeholder": "请选择发布时间", + "main.tag": "文章标签", + "main.tag.new": "新标签", + "main.auto.fetch.tag": "自动提取标签", + "main.tag.auto.switch": "自动标签", + "main.tag.auto.switch.no.tip": "自动标签已关闭,将不会自动生成标签", + "main.cat": "文章分类", + "main.cat.empty": "暂无分类", + "main.cat.select": "请选择分类", + "main.yaml.formatter": "YAML Formatter", + "main.siyuan.to.yaml": "文章属性转YAML", + "main.yaml.to.siyuan": "YAML转文章属性", + "main.save.attr.to.siyuan": "保存属性到思源", + "main.copy": "复制", + "main.publish": "发布", + "main.publish.loading": "发布中...", + "main.loading": "加载中...", + "main.update": "更新", + "main.cancel": "取消", + "main.publish.oneclick.attr": "一键生成属性", + "main.publish.oneclick.attr.finish": "属性已生成", + "main.publish.status.unpublish": "未发布", + "main.publish.status.published": "已发布", + "main.publish.to.vuepress": "发布到Vuepress", + "main.publish.to.hugo": "发布到Hugo", + "main.publish.to.hexo": "发布到Hexo", + "main.publish.to.jekyll": "发布到Jekyll", + "main.publish.to.jvue": "发布到JVue", + "main.publish.to.conf": "发布到Confluence", + "main.publish.to.cnblogs": "发布到博客园", + "main.publish.to.liandi": "发布到链滴", + "main.publish.to.yuque": "发布到语雀", + "main.publish.to.kms": "发布到KMS", + "main.opt.success": "操作成功", + "main.opt.failure": "操作失败", + "main.copy.success": "复制成功", + "main.copy.failure": "复制失败", + "main.opt.quick": "快捷操作", + "main.opt.status.publish": "文章已发布", + "main.opt.status.updated": "文章已更新", + "main.opt.status.cancel": "文章发布已取消", + "main.opt.loading": "操作中...", + "main.opt.warning": "警告信息", + "main.opt.tip": "温馨提示", + "main.opt.ok": "确认", + "main.opt.cancel": "取消", + "main.opt.warning.tip": "此操作不可恢复,是否继续?", + "setting.noneed": "无需配置", + "setting.blog.setting": "设置", + "setting.blog.index": "偏好设置", + "setting.blog.type": "博客类型", + "setting.blog.type.placeholder": "请选择博客类型", + "setting.blog.type.wordpress.value": "Wordpress", + "setting.blog.type.wordpress.label": "Wordpress", + "setting.blog.type.metaweblog.value": "MetaweblogApi", + "setting.blog.type.metaweblog.label": "Metaweblog API", + "setting.blog.type.github.value": "Github", + "setting.blog.type.github.label": "Github", + "setting.blog.type.github.user": "Github用户名", + "setting.blog.type.github.user.tip": "Github用户名,例如:terwer", + "setting.blog.type.github.repo": "Github仓库名", + "setting.blog.type.github.repo.tip": "Github仓库名,例如:terwer.github.io", + "setting.blog.type.github.token": "Github令牌", + "setting.blog.type.github.token.tip": "Github令牌,例如:ghp_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "setting.blog.type.github.token.gen": "Github 个人令牌生成地址", + "setting.blog.type.github.default.branch": "默认分支", + "setting.blog.type.github.default.branch.tip": "默认分支(旧版仓库为master,新版仓库默认为main),例如:main", + "setting.blog.type.github.default.path": "存储目录", + "setting.blog.type.github.default.path.tip": + "存储目录(相对于仓库根目录的相对路径,默认所有文章都发布在这里),例如:docs/_posts", + "setting.blog.type.github.msg": "提交信息", + "setting.blog.type.github.msg.tip": "提交信息,例如:auto published by sy-post-publisher", + "setting.blog.type.github.author": "作者", + "setting.blog.type.github.author.tip": "作者,例如:terwer", + "setting.blog.type.github.email": "邮箱", + "setting.blog.type.github.email.tip": "邮箱", + "setting.blog.url": "博客网址", + "setting.blog.username": "账号", + "setting.blog.password": "密码/Token", + "setting.blog.apiurl": "API地址", + "setting.blog.previewUrl": "预览规则", + "setting.blog.previewUrl.tip": + "预览规则(占位符:[yyyy] [MM] [dd] [postid]),例如:/post/[postid].html 或者 /[cats]/[yyyy]/[mm]/[dd]/[postid].html", + "setting.blog.previewMdUrl": "MD预览规则", + "setting.blog.previewMdUrl.tip": + "MD文件预览规则(占位符:[user] [repo] [branch] [docpath]),例如:/[user]/[repo]/blob/[branch]/[docpath]", + "setting.blog.mdFilenameRule": "文件规则", + "setting.blog.mdFilenameRule.tip": + "Markdown文件名规则(占位符:[yyyy] [MM] [dd] [slug] [filename] ),例如:[filename].md 或者 [yyyy]-[mm]-[dd]-[slug].md", + "setting.blog.pageType": "发布格式", + "setting.blog.validate": "验证", + "setting.blog.save": "保存", + "setting.blog.vali": "验证", + "setting.blog.vali.tip": + "强烈建议保存之前先验证配置以保证api的可用性,验证过程中将发送一篇测试文章,您可以稍后在平台手动删除它。您也可以先保存,稍后修改,但是在验证通过之前该api将不可使用。", + "setting.blog.vali.tip.metaweblog": "配置错误或者api不可用,请检查。若修改过配置,请刷新页面", + "setting.blog.vali.ing": "验证中...", + "setting.blog.vali.ok": "验证通过,不要忘了点击下方保存哟(*^▽^*)", + "setting.blog.vali.ok.metaweblog": "配置已保存并验证通过", + "setting.blog.vali.error": "API验证失败,请检查配置", + "setting.blog.platform.support.github": "您当前操作的是支持Github的平台:", + "setting.blog.platform.support.metaweblog": "您当前操作的是支持metaweblog的平台:", + "setting.blog.cancel": "取消", + "setting.vuepress": "Vuepress", + "setting.hugo": "Hugo", + "setting.hexo": "Hexo", + "setting.jekyll": "Jekyll", + "setting.jvue": "JVue", + "setting.conf": "Confluence", + "setting.conf.tip": + "发布到Confluence需要配合我的另一个适配器项目使用(强烈推荐),请参考:https://github.com/terwer/node-metaweblog-api-adaptor", + "setting.cnblogs": "博客园", + "setting.wordpress": "Wordpress", + "setting.liandi": "链滴", + "setting.yuque": "语雀", + "setting.kms": "KMS", + "setting.other1": "其他1", + "service.switch.vuepress": "Vuepress", + "service.switch.hugo": "Hugo", + "service.switch.hexo": "Hexo", + "service.switch.jekyll": "Jekyll", + "service.switch.jvue": "JVue", + "service.switch.conf": "Confluence", + "service.switch.cnblogs": "博客园", + "service.switch.wordpress": "Wordpress", + "service.switch.yuque": "语雀", + "service.switch.liandi": "链滴", + "service.switch.wechat": "微信聊天", + "service.switch.wemp": "微信公众号", + "service.switch.zhihu": "知乎", + "service.switch.kms": "知识仓库", + "service.switch.must.select.one": "您必须选择一个至少平台", + "post.bind.vuepress.slug": "Vuepress别名", + "post.bind.hugo.slug": "Hugo别名", + "post.bind.hexo.slug": "Hexo别名", + "post.bind.jekyll.slug": "Jekyll别名", + "post.bind.cnblogs.postid": "博客园别名", + "post.bind.jvue.postid": "JVue的ID", + "post.bind.conf.postid": "Conf的ID", + "post.bind.wordpress.postid": "WP的ID", + "post.bind.liandi.postid": "链滴的ID", + "post.bind.yuque.postid": "语雀的ID", + "post.bind.kms.postid": "KMS的ID", + "post.bind.conf.save": "保存", + "post.bind.conf.cancel": "取消", + "form.validate.name.required": "请输入名称", + "form.validate.name.length": "长度必须在0到1之间", + "form.validate.github.auto.delete": "自动删除", + "dynamic.platform.new": "平台管理", + "dynamic.platform.tip": + "如果内置的发布服务无法满足或者需要添加多个实例,您可以在这里添加受支持的平台,目前支持通用平台(Zhihu、语雀、CSDN等)、Github(HUGO、HEXO、Jekyll等)、metaweblog API(开源中国等)和Wordpress API", + "dynamic.platform.type": "平台类型[必选]", + "dynamic.platform.subtype": "子平台名称", + "dynamic.platform.type.metaweblog": "metaweblog API", + "dynamic.platform.type.wordpress": "Wordpress", + "dynamic.platform.type.github": "Github", + "dynamic.platform.type.common": "通用平台", + "dynamic.platform.type.custom": "自定义", + "dynamic.platform.key": "平台key", + "dynamic.platform.key.tip": "平台唯一标识,不能重复,例如:myblog", + "dynamic.platform.name": "平台名称", + "dynamic.platform.name.tip": "平台名称,例如:我的博客", + "dynamic.platform.opt.add": "提交", + "dynamic.platform.opt.noselect": "请选择平台类型", + "dynamic.platform.opt.key.exist": "平台key已经存在", + "dynamic.platform.opt.del.confirm": "删除此平台将会彻底移除此平台相关配置信息,是否继续?", + "dynamic.platform.opt.del.select": "删除", + "dynamic.platform.opt.item.select": "未选择", + "dynamic.platform.opt.item.select.tip": "当前选择的是:", + "dynamic.platform.opt.item.no.select.tip": "请选择要删除的行", + "setting.common.home": "平台首页", + "setting.common.tip": "平台首页,用于预览文章,例如:https://terwer.space", + "setting.common.apiurl": "API地址", + "setting.common.token": "鉴权token", + "setting.common.token.gen": "Token生成地址", + "setting.common.username": "用户名", + "setting.common.username.gen": "访问策略设置", + "setting.common.password": "密码", + "setting.blog.platform.support.common": "您当前操作的平台是:", + "platform.must.select.one": + "哇哦,离使用就差一步啦 o(╥﹏╥)o ,您必须添加并启用至少一个平台。如果列出的平台没有你想要的,可以点击新增按钮添加。有任何疑问欢迎邮件 youweics#163.com ", + "blog.list.toptip": "温馨提示:单击表格行可以进入文章详情页,详情页有发布按钮哦! (〃'▽'〃)", + "config.error.msg": "配置错误,请前往对应发布配置修改", + "config.platform.none": "没有启用的发布平台,请前往服务开关开启", + "post.delete.by.platform": "文章已被平台自行删除,请点击取消强制解除关联", + "blog.top-data-tip": + "温馨提示:1. 0.1.0+ 以后,可通过【偏好设置->思源地址】来修改默认伺服配置,支持本地和远程;如果是远程还需要设置token,如果使用浏览器插件或者挂件之外的访问方式还需要设置跨域请求代理。 2、升级新版之前强烈建议导出配置,防止配置丢失。 3、如果出现新版本不兼容的异常情况,建议清空配置。此操作不可恢复,请谨慎操作!", + "blog.top-data-tip.siyuan": + "温馨提示:1、当前处于挂件模式,从 0.1.0 版本开始,无需在所有页面引用挂件了,只需要在父级页面引用一个挂件即可。具体规则如下:(1). 如果检测到没有子文档,会兼容 0.0.3 版本以前的方式,展示当前文档的发布页面。 (2). 如果检测到有子文档,会以列表加分页的方式展示所有子文档列表。可单独选择某个子文档进行发布操作。 2、升级新版之前强烈建议导出配置,防止配置丢失。 3、如果出现新版本不兼容的异常情况,建议清空配置。此操作不可恢复,请谨慎操作!", + "blog.newwin.open": "页签模式", + "blog.change.siyuan.api": "思源地址", + "setting.blog.siyuan.password": "思源笔记鉴权Token,请从【设置->关于】复制,如果是本地笔记留空即可", + "setting.blog.siyuan.apiurl": "思源笔记的API地址,包括端口,例如:http://127.0.0.1:6806", + "setting.blog.siyuan.current.apiurl": "当前API地址=>", + "setting.blog.middlewareUrl": "跨域请求代理", + "setting.blog.middlewareUrl.tip": "某些场景下需要(例如自部署模式,Nginx伺服等、vite开发环境伺服等),其他情况可忽略", + "setting.blog.middlewareUrl.my.tip": + "如果没有部署代理,可以使用我的共享地址:https://api.terwer.space/api/middleware 。出于性能考虑,建议自己部署一份,请使用 https://github.com/terwer/node-metaweblog-api-adaptor 进行部署。", + "setting.conf.export": "导出配置", + "setting.conf.import": "导入配置", + "setting.conf.transport": "导入导出", + "setting.conf.clear": "清空配置", + "page.no.id": "请求非法,原因:pageId获取失败", + "yaml.show.type.yaml": "YAML", + "yaml.show.type.md": "MD正文", + "yaml.show.type.yamlmd": "YAML+MD正文", + "yaml.show.type.html": "HTML正文", + "main.read.mode": "只读模式", + "main.edit.mode": "编辑模式", + "main.read.mode.tip": + "温馨提示:只读模式下点击可直接复制,编辑模式下需要点击复制按钮复制。YAML转文章属性仅支持title、permalink、date、description、tags", + "main.opt.onclick.confirm.tip": + "此操作默认将重新生成除别名以外的所有属性,如果只想修改个别属性,请在详细模式点击单独的功能按钮进行对应操作,是否继续?", + "main.cat.list.error": "分类获取失败", + "main.yaml.no.save": "您编辑过YAML,点击确认可同步YAML到属性,取消将丢失YAML修改,是否继续?", + "setting.github.baseUrl": "Git平台地址", + "setting.github.baseUrl.tip": "Git平台地址,用于MD文件预览,默认是:https://github.com", + "preference.setting.fixTitle": "去除标题编号", + "preference.setting.removeH1": "去除正文H1", + "preference.setting.removeH1.tip": + "删除H1可能会误删除Markdown里面的#开头的注释,如需开启,请使用2个以上的#注释,是否继续?", + "preference.setting.newWin": "新窗口操作", + "siyuan.browser.show.close.btn": "显示关闭按钮", + "siyuan.browser.menu.quick.btn": "一键快速发布", + "siyuan.browser.menu.publish.btn": "文章发布(自动检测,有子文档->列表页;无子文档->发布页)", + "siyuan.browser.menu.preview.btn": "文章预览(当前文章只读预览)", + "siyuan.browser.menu.manage.btn": "文章管理(全部文章统一管理)", + "siyuan.browser.menu.anki.btn": "Anki标记(配合ankisiyuan生成Anki标记)", + "siyuan.browser.menu.picture.btn": "PicGO图床(支持图床配置与图片上传)", + "siyuan.browser.menu.setting.btn": "偏好设置(系统设置统一入口)", + "anki.siyuan.deck": "牌组", + "anki.siyuan.deck.new": "新牌组", + "anki.siyuan.tag": "标签", + "anki.siyuan.tag.new": "新标签", + "picgo.chrome.tip": + "您当前使用的是浏览器插件,需要从这里下载配置 PicGO 客户端:https://github.com/Molunerfinn/PicGo/releases", + "picgo.siyuan.tip": "您当前使用的是思源笔记挂件,PicGO已内置,只需配置即可", + "post.detail.button.copy.id": "复制本文ID", + "post.detail.button.share.link": "复制分享链接", + "post.detail.button.browser.open": "默认浏览器打开", + "post.detail.button.pic.manage": "图片管理", + "post.detail.button.anki.mark": "Anki标记", + "post.detail.index.send.to.publish": "发布到其他平台", + "post.detail.index.export.to.pdf": "导出为PDF", + "picgo.upload.status": "上传状态", + "picgo.upload.select.pic": "选择图片", + "picgo.upload.clipboard": "剪贴板图片", + "picgo.upload.onclick": "一键上传本地图片到图床", + "picgo.download.onclick": "一键下载远程图片到本地", + "picgo.download.local.to.bed": "上传本地图片到图床", + "picgo.download.bed.to.local": "下载远程图片到本地", + "picgo.pic.preview": "图片预览", + "picgo.pic.setting": "图床设置", + "picgo.pic.setting.no.tip": + "由于您当前使用的是浏览器插件,无法直接集成 PicGO ,只能通过 HTTP 请求间接调用本地 PicGO 客户端上图片,请直接在本地 PicGO 客户端进行设置。如需体验更加完整的功能,请通过思源笔记挂件版挂载菜单打开新窗口的方式使用。详情请参考:https://docs.publish.terwer.space/post/the-pendant-mode-is-used-in-the-method-of-mounting-menu-169wrw.html", + "github.use.permalink": "永久链接", + "github.use.permalink.no.warn": + "您已关闭永久链接,formatter将不会生成标记,这在某些特殊场景下可能有用,大部分情况下需要开启此选项,注意:目前仅仅在HUGO平台生效,主要是适配Docsy", + "github.menu.title": "菜单标题", + "github.menu.title.placeholder": "菜单栏标题(HUGO平台专用,为空则不显示在菜单)", + "github.weight": "显示权重", + "github.weight.placeholder": "权重(决定显示顺序,越小显示越靠前)", + "github.use.date": "显示日期", + "github.use.date.no.warn": "您已关闭日期显示,将不会在formatter生成date字段", + "github.post.picgo.use": "使用图床", + "github.post.picgo.use.tip": + "启用此选项,将会自动将此文档包含的所有本地图片上传至自定义图床(远程图片不上传),请确保PicGO图床配置正确", + "github.post.picgo.start.upload": "图床已开启,即将上传本地图片到图床", + "github.post.picgo.picbed.error": "文档可能已经成功发布,但是图片上传失败或者当前场景不支持图片上传,详细信息", + "setting.yuque.home.tip": "语雀平台首页,通常固定是:https://www.yuque.com/", + "setting.yuque.username.tip": "语雀平台用户名,注意是【账户设置】里面固定连接里面的用户名,不是用户昵称哦(*^▽^*)", + "setting.yuque.password.tip": + "语雀平台的Token,请按照链接点击获取,只需要勾选【读取你的知识库】和【修改和读取你的文档】两个权限即可", + "setting.yuque.apiurl.tip": "语雀平台的API地址,通常固定是:https://www.yuque.com/api/v2", + "setting.liandi.home.tip": "链滴平台首页,通常固定是:https://ld246.com/", + "setting.liandi.username.tip": + "链滴平台用户名,注意是点击【个人主页】之后URL上member后面显示的用户名,不是用户昵称哦(*^▽^*)", + "setting.liandi.password.tip": "链滴平台的Token,请按照链接点击获取", + "setting.liandi.apiurl.tip": "链滴平台的API地址,通常固定是:https://ld246.com/api/v2", + "setting.kms.home.tip": "KMS平台首页,例如:http://localhost:9564/kms16_release/kms/multidoc", + "setting.kms.username.tip": "KMS平台Rest账户用户名,请按照链接指示创建", + "setting.kms.password.tip": "KMS平台Rest账户密码,请按照链接指示创建", + "setting.kms.apiurl.tip": + "KMS平台的API地址,例如:http://localhost:9564/kms16_release/api/kms-multidoc/kmsMultidocKnowledgeRestService", + "siyuan.local.share.tip": + "温馨提示:如果发现分享链接或者浏览器打开无法显示或者网络连接被拒绝,请按照下面操作排查。1:思源笔记->设置->关于->打开网络伺服;2:在浏览器打开之后,点击底部思源地址,修改对应地址为局域网ip地址。", + "setting.cnblogs.home.tip": "您的博客园首页,通常是:https://www.cnblogs.com/<您的用户名>", + "setting.cnblogs.username.tip": + "您的博客园MetaWeblog登录名,可前往您的博客园后台->设置查看,注意:是登录名,不是用户昵称哦(*^▽^*)", + "setting.cnblogs.password.tip": "您的博客园MetaWeblog访问令牌,可前往您的博客园后台->设置查看", + "setting.cnblogs.apiUrl.tip": + "您的博客园MetaWeblog访问地址,可前往您的博客园后台->设置查看,通常是:https://rpc.cnblogs.com/metaweblog/<您的用户名>", + "setting.cnblogs.previewUrl.tip": "博客园平台的文章预览规则,通常是:/p/[postid].html", + + "setting.wordpress.home.tip": "WordPress首页", + "setting.wordpress.username.tip": "WordPress登录名,注意:是登录名,不是用户昵称哦(*^▽^*)", + "setting.wordpress.password.tip": "WordPress登录密码", + "setting.wordpress.apiUrl.tip": "WordPress的xmlrpc远程发布地址,通常是:https:///xmlrpc.php", + "setting.wordpress.previewUrl.tip": "WordPress平台的文章预览规则,通常是:?p=[postid]", + + "setting.oschina.home.tip": "开源中国首页,通常是:https://my.oschina.net/<您的用户名>", + "setting.oschina.username.tip": "开源中国登录名,可以输入登录邮箱", + "setting.oschina.password.tip": "开源中国登录密码", + "setting.oschina.apiUrl.tip": "开源中国的xmlrpc远程发布地址,通常固定是:https://www.oschina.net/action/xmlrpc", + "setting.oschina.previewUrl.tip": "开源中国平台的文章预览规则,通常是:/blog/[postid]", + + "setting.typecho.home.tip": "Typecho平台的首页", + "setting.typecho.username.tip": "Typecho平台登录名", + "setting.typecho.password.tip": "Typecho平台密码", + "setting.typecho.apiUrl.tip": + "Typecho平台的xmlrpc远程发布地址,通常固定是:https://<平台地址>/index.php/action/xmlrpc", + "setting.typecho.previewUrl.tip": "Typecho平台的文章预览规则,通常是:/index.php/archives/[postid]", + + "setting.jvue.home.tip": "JVue平台的首页", + "setting.jvue.username.tip": "JVue平台登录名", + "setting.jvue.password.tip": "JVue平台密码", + "setting.jvue.apiUrl.tip": "JVue平台的xmlrpc远程发布地址,通常固定是:https://<平台地址>/xmlrpc", + "setting.jvue.previewUrl.tip": "JVue平台的文章预览规则,通常是:/post/[postid].html", + "setting.conf.home.tip": "Confluence Cloud,首页,通常是:https://<您的用户名>.atlassian.net/wiki/spaces/<空间名>", + "setting.conf.username.tip": "Confluence登录名,可前往您的atlassian账户获取", + "setting.conf.password.tip": "Confluence访问令牌,可前往您的atlassian账户获取", + "setting.conf.apiUrl.tip": + "可前往 https://developer.atlassian.com/cloud/confluence/rest/v1/#api-wiki-rest-api-content-get 查看文档", + "setting.conf.previewUrl.tip": "Confluence平台文章预览规则,通常是:/pages/[postid]", + "setting.picgo.refer.to": "详情请参考:", + "setting.picgo.refer.to.online.doc": "PicGO配置在线文档", + "setting.picgo.picbed": "图床设置", + "setting.picgo.picgo": "PicGO设置", + "main.opt.edit": "编辑", + "main.opt.delete": "删除", + "setting.picgo.picgo.open.config.file": "打开配置文件", + "setting.picgo.picgo.click.to.open": "点击打开", + "setting.picgo.picgo.choose.showed.picbed": "请选择显示的图床", + "setting.picgo.picbed.selected.tip": "已选中", + "setting.picgo.picbed.unselected.tip": "未选中", + "setting.picgo.picbed.set.default": "设为默认图床", + "setting.picgo.picbed.current.selected.tip": "已选中图床:", + "setting.picgo.picbed.current.tip": "当前默认图床是:", + "setting.picgo.picbed.uploader.config.name": "图床配置名", + "setting.picgo.picbed.uploader.config.name.placeholder": "请输入配置名称", + "setting.picgo.config.name": "配置名称", + "setting.picgo.config.name.placeholder": "请输入配置名称", + "setting.picgo.index.tip": + "注意:1、此处上传的图片不会自动插入文档中,请手动点击按钮复制链接,然后 Ctrl+V 粘贴到文档中。2、对于文档当中原本已经存在的本地图片,点击上传之后不会直接替换原始图片,只会存储本地图片与图床图片的映射信息,需要在发布文章时手动勾选【使用图床】才会进行临时链接替换,请知悉。这样做是为了不破坏其他地方对文档图片的处理。当然,您也可以手动复制图床链接,然后删除原图片,替换为图床图片。3、该图片列表仅展示此文档包含的图片。", + "setting.picgo.index.copy.link": "复制链接", + "setting.picgo.setting.timestamp.rename": "时间戳重命名", + "setting.picgo.setting.close": "关", + "setting.picgo.setting.open": "开", + "setting.picgo.setting.log.file": "日志文件", + "setting.picgo.plugin": "插件设置", + "setting.picgo.plugin.list": "插件列表", + "setting.picgo.plugin.import.local": "导入本地插件", + "setting.picgo.plugin.install": "安装", + "setting.picgo.plugin.installing": "安装中", + "setting.picgo.plugin.installed": "已安装", + "setting.picgo.plugin.doing.something": "进行中", + "setting.picgo.plugin.search.placeholder": + "搜索npm上的PicGo插件,或者点击上方按钮查看优秀插件列表。推荐:watermark-elec、s3", + "setting.picgo.plugin.gui.not.implemented": "该插件未对可视化界面进行优化, 是否继续安装?", + "setting.picgo.setting.node.path": "Node安装路径", + "setting.picgo.setting.node.path.tip": "请输入Node安装完整路径,例如:/opt/node16/bin,Node版本>=16", + "setting.picgo.setting.node.registry": "NPM的registry", + "setting.picgo.setting.node.registry.tip": "国内推荐:https://registry.npmmirror.com", + "setting.picgo.setting.node.proxy": "NPM代理", + "setting.picgo.setting.node.proxy.tip": "一般可留空,如有特殊需求可设置自定义代理", + "setting.picgo.setting.config.tip": + "注意:横线以上的属性会改变上传图片的行为,配置可自动保存。横线以下的属性仅仅在安装插件时用到,并且需要点击【确认】按钮才能保存。", + "setting.picgo.plugin.install.success": + "插件已经成功安装,请清除搜索关键字刷新列表。点击插件对应【设置】图标可查看插件详情,并对插件进行自定义设置。如果是图床扩展,请关闭重新打开,然后启用图床并进行设置。", + "syp.about": "关于作者", + "setting.picgo.plugin.uninstall": "卸载插件", + "setting.picgo.plugin.uninstall.success": "插件已经成功卸载。", + "setting.picgo.plugin.enable": "启用插件", + "setting.picgo.plugin.disable": "禁用插件", + "setting.picgo.plugin.update": "更新插件", + "setting.picgo.plugin.config.setting": "插件设置", + "setting.picgo.plugin.work": "已兼容", + "setting.picgo.plugin.nowork": "不兼容", + "setting.picgo.plugin.nouse": "不可用", + "setting.picgo.plugin.update.success": "更新成功", + "setting.conf.import.syp": "导入挂件配置", + "setting.conf.import.picgo": "导入PicGO配置", + "setting.conf.export.syp": "导出挂件配置", + "setting.conf.export.picgo": "导出PicGO配置", + "setting.conf.import.syp.tip": "请选择有效的历史备份json文件进行导入。注意:挂件配置会进行配置替换。", + "setting.conf.import.picgo.tip": + "请选择有效的历史备份json文件进行导入。注意:PicGO插件的注册信息不导入。为了配置的兼容性,请卸载所有插件之后,进行PicGO配置导出操作。卸载PicGO插件不会删除插件配置,只会删除插件文件以及插件注册记录。请放心卸载。", + "setting.conf.export.syp.tip": + "将导出对应的json配置文件,强烈建议定期进行配置备份。原则上支持向后兼容。如有不兼容问题,会特别说明。", + "setting.conf.export.picgo.tip": + "将导出对应的json配置文件,强烈建议定期进行配置备份。原则上支持向后兼容。如有不兼容问题,会特别说明。", + "setting.conf.clear.syp": "清空挂件配置", + "setting.conf.clear.picgo": "清空PicGO配置", + "setting.conf.clear.tip": "清空配置不可恢复,请谨慎操作。强烈建议先备份配置。", + "setting.conf.clear.picgo.tip": + "清空配置不可恢复,请谨慎操作。强烈建议先备份配置。此操作会删除PicGO相关配置文件夹,包括配置、插件、缓存等,仅在PicGO加载发生异常,并且完全无法使用的时候使用,否则丢失配置后果自负!", + "setting.main.background": "挂件背景", + "setting.main.background.tip": "支持16进制和rgb,例如:#000000,未设置或者留空不变。如果新窗口设置,请重新刷新文档。", + "setting.picgo.manage": "图床管理", + "setting.platform.add": "新增平台", + "setting.platform.add.this": "新增此平台", + "setting.platform.universal": "通用平台", + "setting.platform.universal.desc": + "目前支持的通用平台有:语雀,采用API授权。这些平台通常提供了现代化的REST API,可以方便的进行集成。", + "setting.platform.wordpress": "WordPress", + "setting.platform.wordpress.desc": + "Wordpress 通过Xmlrpc API支持,采用 API 授权。WordPress是一个自由开源的博客软件和内容管理系统,具有数量众多的优质插件和主题。", + "setting.platform.github": "Github", + "setting.platform.github.desc": + "目前支持Github发布的的平台有:Hexo、Hugo、Jekyll、Vitepress等,采用 API 授权。GitHub 是一个使用 Git 进行软件开发和版本控制的代码平台。", + "setting.platform.metaweblog": "Metaweblog", + "setting.platform.metaweblog.desc": + "目前支持Metaweblog的平台有:博客园、Typecho等,采用 API 授权。MetaWeblog API 作为XML-RPC Web 服务实现,是目前公认的开放博客标准。", + "setting.platform.custom": "自定义 HTTP 协议", + "setting.platform.custom.desc": "目前支持自定义HTTP协议的平台有:知乎、简书、掘金等,采用网页授权。自定义HTTP协议通过类似 WeChatSync 的方式实现。", + "setting.platform.right.tips0": "特别提示:", + "setting.platform.right.tips1": "1、在这里可以进行发布配置,直接点击 [设置图标] 即可进行配置。", + "setting.platform.right.tips2": "2、如需新增平台,直接点击左侧 + 按钮即可。", + "setting.platform.right.tips3": + "3、目前支持 网页授权 和 API 授权两种方式,API授权 复杂点但是相对稳定,网页授权简单但是可能会失效。网页授权模式实现思路类似 wechatsync 。", + "setting.platform.right.tips4": "4、如需兼容其他平台,请联系我:youweics@163.com 或者填写下面的表单。", + "setting.entry.title": "平台统一设置 - ", + "setting.entry.not.supported": "不支持的平台", + "setting.upgrade.syp.tip1": "准备开始迁移...", + "setting.upgrade.syp.tip2": "迁移成功.", + "setting.upgrade.syp.tip3": "已经是最新,无需迁移.", + "setting.upgrade.syp.tip4": "迁移失败,错误如下", + "setting.upgrade.syp.tip5": "迁移操作完成.", + "setting.upgrade.syp.doTip1": "检测配置文件是否为最新版本", + "setting.upgrade.syp.doTip2": "检测到旧配置,准备升级配置文件", + "setting.upgrade.syp.doTip3": "旧配置升级", + "setting.upgrade.syp.doTip4": "没有版本更新,跳过升级", +} diff --git a/src/main.ts b/src/main.ts index e4eb3647..1495f5b6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -25,14 +25,14 @@ import { createApp } from "vue" import App from "./App.vue" -// import { createAppLogger } from "~/src/utils/appLogger.ts" -// import { useVueRouter } from "./composables/useVueRouter.ts" -// import i18n from "~/src/locales" -// import "element-plus/dist/index.css" -// import "element-plus/theme-chalk/dark/css-vars.css" -// import { InjectKeys } from "~/src/utils/injectKeys.ts" -// import { AppInstance } from "~/src/appInstance.ts" -// import { createPinia } from "pinia" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { useVueRouter } from "./composables/useVueRouter.ts" +import i18n from "~/src/locales" +import "element-plus/dist/index.css" +import "element-plus/theme-chalk/dark/css-vars.css" +import { InjectKeys } from "~/src/utils/injectKeys.ts" +import { AppInstance } from "~/src/appInstance.ts" +import { createPinia } from "pinia" /** * Vue 入口 @@ -42,37 +42,37 @@ import App from "./App.vue" * @since 0.0.1 */ const createVueApp = async () => { - // const logger = createAppLogger("vue-main-entry") + const logger = createAppLogger("vue-main-entry") // https://stackoverflow.com/a/62383325/4037224 const app = createApp(App) - // // 国际化 - // app.use(i18n) - // - // // pinia - // const pinia = createPinia() - // app.use(pinia) - // - // // router - // const router = useVueRouter() - // app.use(router) - // - // // appInstance - // const appInstance = new AppInstance() - // app.provide(InjectKeys.APP_INSTANCE, appInstance) - // logger.info("appInstance provided=>", appInstance) - // - // // ElementPlus 包太大,需要改成按需引入 - // // https://element-plus.org/zh-CN/guide/quickstart.html#%E6%8C%89%E9%9C%80%E5%AF%BC%E5%85%A5 - // // app.use(ElementPlus) + // 国际化 + app.use(i18n) + + // pinia + const pinia = createPinia() + app.use(pinia) + + // router + const router = useVueRouter() + app.use(router) + + // appInstance + const appInstance = new AppInstance() + app.provide(InjectKeys.APP_INSTANCE, appInstance) + logger.info("appInstance provided=>", appInstance) + + // ElementPlus 包太大,需要改成按需引入 + // https://element-plus.org/zh-CN/guide/quickstart.html#%E6%8C%89%E9%9C%80%E5%AF%BC%E5%85%A5 + // app.use(ElementPlus) // 挂载 vue app app.mount("#app") - // // 暴露 Vue 实例 - // app.provide(InjectKeys.VUE_INSTANCE, app) - // logger.info("vue app created") + // 暴露 Vue 实例 + app.provide(InjectKeys.VUE_INSTANCE, app) + logger.info("vue app created") } ;(async () => { diff --git a/src/models/postForm.ts b/src/models/postForm.ts new file mode 100644 index 00000000..36ab8968 --- /dev/null +++ b/src/models/postForm.ts @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022-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. + */ + +/** + * 通用发布表单数据定义 + * @author terwer + * @since 0.1.0 + */ +export class PostForm { + formData = { + title: "", + customSlug: "", + desc: "", + created: "", + tag: { + inputValue: "", + dynamicTags: [], + inputVisible: false, + }, + customPath: "", + categories: ["默认分类"], + mdContent: "", + htmlContent: "", + usePermalink: true, + useDate: true, + linkTitle: "", + weight: 0, + } +} diff --git a/src/models/yamlFormatObj.ts b/src/models/yamlFormatObj.ts new file mode 100644 index 00000000..7842cca6 --- /dev/null +++ b/src/models/yamlFormatObj.ts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022-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. + */ + +/** + * 通用的YAML模型定义 + * @author terwer + * @since 0.1.0 + */ +export class YamlFormatObj { + /** + * YAML + */ + yamlObj = {} as any + /** + * YAML字符串 + */ + formatter = "" + /** + * Markdown文本 + */ + mdContent = "" + /** + * YAML+MD + */ + mdFullContent: string + /** + * HTML文本 + */ + htmlContent: string +} diff --git a/src/pages/ApiTest.vue b/src/pages/ApiTest.vue new file mode 100644 index 00000000..2da0f8b2 --- /dev/null +++ b/src/pages/ApiTest.vue @@ -0,0 +1,98 @@ + + + + + + + diff --git a/src/pages/Home.vue b/src/pages/Home.vue new file mode 100644 index 00000000..56be816d --- /dev/null +++ b/src/pages/Home.vue @@ -0,0 +1,34 @@ + + + + + + + diff --git a/src/pages/Setting.vue b/src/pages/Setting.vue new file mode 100644 index 00000000..70c91eee --- /dev/null +++ b/src/pages/Setting.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/src/stores/common/commonStorage.ts b/src/stores/common/commonStorage.ts new file mode 100644 index 00000000..a382a665 --- /dev/null +++ b/src/stores/common/commonStorage.ts @@ -0,0 +1,128 @@ +/* + * 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 { StorageLikeAsync } from "@vueuse/core" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { StrUtil } from "zhi-common" +import { useSiyuanApi } from "~/src/composables/useSiyuanApi.ts" +import { SiyuanDevice } from "zhi-device" + +/** + * 通用存储实现,实现了 `StorageLikeAsync` 接口。 + * https://github.com/vueuse/vueuse/blob/main/packages/core/ssr-handlers.ts#L11 + * + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +class CommonStorage implements StorageLikeAsync { + private readonly logger + private readonly storageViaSiyuanApi + private readonly kernelApi + public readonly key + + constructor(storageKey: string) { + this.logger = createAppLogger("common-storage") + + const { isStorageViaSiyuanApi, kernelApi } = useSiyuanApi() + this.storageViaSiyuanApi = isStorageViaSiyuanApi() + this.kernelApi = kernelApi + + if (this.storageViaSiyuanApi) { + this.key = storageKey + } else { + const fileName = storageKey.split("/").pop() ?? "" + this.key = fileName.slice(0, fileName.lastIndexOf(".")) + } + } + + /** + * 异步获取与给定键关联的值。 + * + * @param key - 要获取值的键。 + * @returns 一个 Promise,它解析为与给定键关联的值,如果键不存在则解析为 `null`。 + */ + public async getItem(key: string): Promise { + this.logger.info(`Retrieving value for '${key}' from CommonStorage.`) + let ret + if (this.storageViaSiyuanApi) { + // 如果当前运行在思源笔记中,则直接返回 null + try { + ret = (await this.kernelApi.getFile(key, "text")) ?? "" + this.logger.debug(`Use SiYuan Api LocalStorageAdaptor to getItem - Retrieving '${key}', Value: ${ret}`) + } catch (error) { + this.logger.error(`Failed to get value for key '${key}' from SiYuan Api LocalStorageAdaptor. Error:`, error) + } + } else { + try { + const win = SiyuanDevice.siyuanWindow() + const value = win.localStorage.getItem(key) + ret = value ?? "" + this.logger.debug(`Use Browser LocalStorageAdaptor to getItem - Retrieving '${key}', Value: ${ret}`) + } catch (error) { + this.logger.error(`Failed to get value for key '${key}' from Browser LocalStorageAdaptor. Error:`, error) + } + } + + // 根据 ret 的值返回不同类型的结果 + if (StrUtil.isEmptyString(ret)) { + ret = "{}" + } + this.logger.debug(`Final getItem - '${key}', Value: '${ret}'`) + return ret + } + + /** + * 异步删除与给定键关联的值。 + * + * @param key - 要删除值的键。 + * @returns 一个 Promise,在删除值后解析。 + */ + public async removeItem(key: string): Promise { + this.logger.info(`Removing value for ${key} from CommonStorage.`) + } + + /** + * 异步设置与给定键关联的值。 + * + * @param key - 要设置值的键。 + * @param value - 给定键的新值。 + * @returns 一个 Promise,在设置值后解析。 + */ + public async setItem(key: string, value: string): Promise { + this.logger.debug(`Setting value for '${key}' in CommonStorage to '${value}'.`) + if (this.storageViaSiyuanApi) { + // 如果当前运行在思源笔记中,则直接返回空字符串 + await this.kernelApi.saveTextData(key, value) + this.logger.debug(`Use SiYuan Api LocalStorageAdaptor to setItem - Key '${key}', Value: '${value}'`) + } else { + const win = SiyuanDevice.siyuanWindow() + win.localStorage.setItem(key, value) + this.logger.debug(`Use Browser LocalStorageAdaptor to setItem - Key '${key}', Value: '${value}'`) + } + } +} + +export default CommonStorage diff --git a/src/stores/common/useCommonStorageAsync.ts b/src/stores/common/useCommonStorageAsync.ts new file mode 100644 index 00000000..519976f7 --- /dev/null +++ b/src/stores/common/useCommonStorageAsync.ts @@ -0,0 +1,99 @@ +/* + * 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 CommonStorage from "~/src/stores/common/commonStorage.ts" +import { StorageSerializers, toValue } from "@vueuse/core" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { ObjectUtil } from "zhi-common" + +/** + * https://vueuse.org/core/useStorageAsync/ + * + * @param storageKey - 存储key + * @param initialValue - 默认值 + */ +export const useCommonStorageAsync = ( + storageKey: string, + initialValue: T +) => { + const logger = createAppLogger("common-storage-async") + const commonStorage = new CommonStorage(storageKey) + + // 获取 initialValue 类型对应的序列化器,如果不存在则使用默认序列化器 + const rawInit: T = toValue(initialValue) + const type = guessSerializerType(rawInit) as + | "boolean" + | "object" + | "number" + | "any" + | "string" + | "map" + | "set" + | "date" + logger.debug(`It is detected that the serialization type is ${type}`) + const serializer = StorageSerializers[type] + + // 定义 commonStore 对象 + const commonStore = { + async get(): Promise { + logger.debug("Fetching data from common storage...") + const rawValue = (await commonStorage.getItem(commonStorage.key)) ?? "{}" + let ret = (await serializer.read(rawValue)) ?? {} + + if (ObjectUtil.isEmptyObject(ret)) { + logger.info("Initial data not found in common storage. Setting initial value...") + await commonStorage.setItem(commonStorage.key, serializer.write(initialValue)) + logger.debug("Initial value set:", initialValue) + ret = initialValue + } + return ret + }, + async set(value: T): Promise { + await commonStorage.setItem(commonStorage.key, serializer.write(value)) + }, + } + + return { commonStore } +} + +function guessSerializerType(rawInit: T) { + return rawInit == null + ? "any" + : rawInit instanceof Set + ? "set" + : rawInit instanceof Map + ? "map" + : rawInit instanceof Date + ? "date" + : typeof rawInit === "boolean" + ? "boolean" + : typeof rawInit === "string" + ? "string" + : typeof rawInit === "object" + ? "object" + : !Number.isNaN(rawInit) + ? "number" + : "any" +} diff --git a/src/stores/useSettingStore.ts b/src/stores/useSettingStore.ts new file mode 100755 index 00000000..dc4373a6 --- /dev/null +++ b/src/stores/useSettingStore.ts @@ -0,0 +1,85 @@ +import { defineStore } from "pinia" +import { SypConfig } from "~/syp.config.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { useCommonStorageAsync } from "~/src/stores/common/useCommonStorageAsync.ts" +import { computed, ref } from "vue" + +/** + * 设置配置存储 + * https://pinia.vuejs.org/ssr/nuxt.html + * 从0.9.0+ 开始,配置文件变更为 /data/storage/syp/sy-p-plus-cfg.json ,旧数据会自动迁移 + */ +export const useSettingStore = defineStore("setting", () => { + const logger = createAppLogger("use-setting-store") + const storageKey = "/data/storage/syp/sy-p-plus-cfg.json" + const initialValue = SypConfig + const { commonStore } = useCommonStorageAsync(storageKey, initialValue) + const settingRef = ref(null) + + const getSettingRef = computed(async () => { + const setting = await commonStore.get() + logger.debug("get data from setting=>", setting) + settingRef.value = setting + return setting + }) + + /** + * 获取配置 + */ + const getSetting = async (): Promise => { + if (settingRef.value === null) { + logger.info("Setting not initialized. Initializing now...") + // 如果设置还没有被初始化,则调用 getSettingRef 函数 + const setting = getSettingRef.value + logger.info(`Loaded setting from remote api`) + return setting ?? {} + } + logger.info(`Loaded setting from cache.`) + return settingRef.value ?? {} + } + + /** + * 修改配置 + * + * @param setting - 需要修改的配置 + */ + const updateSetting = async (setting: Partial) => { + logger.debug("update setting=>", setting) + await commonStore.set(setting) + settingRef.value = { ...settingRef.value, ...setting } + } + + const checkKeyExists = (targetKey: string): boolean => { + const obj = settingRef.value + if (!obj) { + return false + } + + // 遍历所有属性名,检查是否存在目标属性 + for (const key in obj) { + if (key === targetKey) { + return true + } + } + + // 如果以上情况都不符合,则说明不存在目标属性 + return false + } + + const deleteKey = (targetKey: string): void => { + const obj = settingRef.value + if (!obj) { + return + } + + // 遍历所有属性名,检查是否存在目标属性 + for (const key in obj) { + if (key === targetKey) { + delete obj[key] + return + } + } + } + + return { getSetting, updateSetting, checkKeyExists, deleteKey } +}) diff --git a/src/utils/appLogger.ts b/src/utils/appLogger.ts new file mode 100644 index 00000000..daf14cfa --- /dev/null +++ b/src/utils/appLogger.ts @@ -0,0 +1,61 @@ +/* + * 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 { isDev } from "~/src/utils/constants.ts" +import { simpleLogger } from "zhi-lib-base" + +/** + * 使用 eruda 更好的控制日志 + */ +window.console = isDev ? (window as any).eruda.get("console") : window.console + +/** + * 简单的日志接口 + */ +interface ILogger { + debug: (msg: string, obj?: any) => void + info: (msg: string, obj?: any) => void + warn: (msg: string, obj?: any) => void + error: (msg: string | Error, obj?: any) => void +} + +/** + * 一个简单轻量级的日志记录器 + * + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +export const createAppLogger = (name: string): ILogger => { + return simpleLogger(name, "publisher-widget", isDev) +} + +/** + * 销毁日志 + */ +export const destroyLogger = (): void => { + const win = window as any + win.eruda.destroy() +} diff --git a/src/utils/constants.ts b/src/utils/constants.ts new file mode 100644 index 00000000..d1eba5ad --- /dev/null +++ b/src/utils/constants.ts @@ -0,0 +1,31 @@ +/* + * 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. + */ + +export const isDev = process.env.DEV_MODE === "true" + +/** + * 动态配置key,全系统唯一,请勿更改 + */ +export const DYNAMIC_CONFIG_KEY = "dynamic-config" diff --git a/src/utils/idUtil.ts b/src/utils/idUtil.ts new file mode 100644 index 00000000..f9be5f91 --- /dev/null +++ b/src/utils/idUtil.ts @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022-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 shortHash from "shorthash2" +import { v4 as uuidv4 } from "uuid" + +/** + * 唯一ID + */ +const newID = (): string => { + const newstr = new Date().toISOString() + return shortHash(newstr).toLowerCase() +} + +/** + * ID生成统一入口 + */ +const newUuid = () => { + return uuidv4() +} + +const idUtil = { + newUuid, + newID, +} + +export default idUtil diff --git a/src/utils/import/pre.ts b/src/utils/import/pre.ts new file mode 100644 index 00000000..cd36b279 --- /dev/null +++ b/src/utils/import/pre.ts @@ -0,0 +1,160 @@ +import { + AuthMode, + DynamicConfig, + PlatformType, + SubPlatformType, +} from "~/src/components/set/publish/platform/dynamicConfig.ts" +import { svgIcons } from "~/src/utils/svgIcons.ts" + +export const pre = { + commonCfg: [ + { + platformType: PlatformType.Common, + subPlatformType: SubPlatformType.Common_Yuque, + platformKey: "common_Yuque", + platformName: "语雀", + platformIcon: svgIcons.iconIFYuque, + authMode: AuthMode.API, + isEnabled: false, + }, + ], + githubCfg: [ + { + platformType: PlatformType.Github, + subPlatformType: SubPlatformType.Github_Hexo, + platformKey: "github_Hexo", + platformName: "Hexo", + platformIcon: svgIcons.iconIFHexo, + authMode: AuthMode.API, + isEnabled: false, + }, + // { + // platformType: PlatformType.Github, + // subPlatformType: SubPlatformType.Github_Hugo, + // platformKey: "github_Hugo", + // platformName: "Hugo", + // platformIcon: svgIcons.iconIFHugo, + // authMode: AuthMode.API, + // isEnabled: false + // }, + // { + // platformType: PlatformType.Github, + // subPlatformType: SubPlatformType.Github_Vitepress, + // platformKey: "github_Vitepress", + // platformName: "Vitepress", + // platformIcon: svgIcons.iconIFVue, + // authMode: AuthMode.API, + // isEnabled: false + // }, + // { + // platformType: PlatformType.Github, + // subPlatformType: SubPlatformType.Github_Nuxt, + // platformKey: "github_Nuxt", + // platformName: "Nuxt", + // platformIcon: svgIcons.iconIFNuxt, + // authMode: AuthMode.API, + // isEnabled: false + // }, + ], + metaweblogCfg: [ + { + platformType: PlatformType.Metaweblog, + subPlatformType: SubPlatformType.Metaweblog_Cnblogs, + platformKey: "metaweblog_Cnblogs", + platformName: "博客园", + platformIcon: svgIcons.iconIFCnblogs, + authMode: AuthMode.API, + isEnabled: false, + }, + { + platformType: PlatformType.Metaweblog, + subPlatformType: SubPlatformType.Metaweblog_Typecho, + platformKey: "metaweblog_Typecho", + platformName: "Typecho", + platformIcon: svgIcons.iconIFTypecho, + authMode: AuthMode.API, + isEnabled: false, + }, + ], + wordpressCfg: [ + { + platformType: PlatformType.Wordpress, + subPlatformType: SubPlatformType.Wordpress_Wordpress, + platformKey: "wordpress_Wordpress", + platformName: "Wordpress", + platformIcon: svgIcons.iconIFWordpress, + authMode: AuthMode.API, + isEnabled: false, + }, + ], + customCfg: [ + { + platformType: PlatformType.Custom, + subPlatformType: SubPlatformType.Custom_Zhihu, + platformKey: "custom_Zhihu", + platformName: "知乎", + platformIcon: svgIcons.iconIFZhihu, + authMode: AuthMode.WEBSITE, + authUrl: "https://www.zhihu.com/signin", + domain: "zhihu.com", + isEnabled: false, + }, + // CSDN 目前有CA验证 + { + platformType: PlatformType.Custom, + subPlatformType: SubPlatformType.Custom_CSDN, + platformKey: "custom_Csdn", + platformName: "CSDN", + platformIcon: svgIcons.iconIFCSDN, + authMode: AuthMode.WEBSITE, + authUrl: "https://passport.csdn.net/login", + domain: "csdn.net", + isEnabled: false, + }, + { + platformType: PlatformType.Custom, + subPlatformType: SubPlatformType.Custom_Jianshu, + platformKey: "custom_Jianshu", + platformName: "简书", + platformIcon: svgIcons.iconIFJianshu, + authMode: AuthMode.WEBSITE, + authUrl: "https://www.jianshu.com/sign_in", + domain: "jianshu.com", + isEnabled: false, + }, + { + platformType: PlatformType.Custom, + subPlatformType: SubPlatformType.Custom_Juejin, + platformKey: "custom_Juejin", + platformName: "掘金", + platformIcon: svgIcons.iconIFJuejin, + authMode: AuthMode.WEBSITE, + authUrl: "https://juejin.cn/login", + domain: "juejin.cn", + isEnabled: false, + }, + { + platformType: PlatformType.Custom, + subPlatformType: SubPlatformType.Custom_Wechat, + platformKey: "custom_Wechat", + platformName: "微信公众号", + platformIcon: svgIcons.iconIFWechat, + authMode: AuthMode.WEBSITE, + authUrl: "https://mp.weixin.qq.com/", + domain: "qq.com", + isEnabled: false, + }, + ], + systemCfg: [ + { + platformType: PlatformType.System, + subPlatformType: SubPlatformType.System_Siyuan, + platformKey: "system_Siyuan", + platformName: "思源笔记", + platformIcon: svgIcons.iconIFSiyuan, + authMode: AuthMode.API, + isEnabled: true, + isSys: true, + }, + ], +} diff --git a/src/utils/injectKeys.ts b/src/utils/injectKeys.ts new file mode 100644 index 00000000..126e7337 --- /dev/null +++ b/src/utils/injectKeys.ts @@ -0,0 +1,32 @@ +/* + * 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. + */ + +/** + * 依赖注入 key 通用定义 + */ +export enum InjectKeys { + VUE_INSTANCE = "vueInstance", + APP_INSTANCE = "appInstance", +} diff --git a/src/utils/polyfillUtils.ts b/src/utils/polyfillUtils.ts new file mode 100644 index 00000000..969e4b56 --- /dev/null +++ b/src/utils/polyfillUtils.ts @@ -0,0 +1,47 @@ +/* + * 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 { Buffer } from "node:buffer" + +/** + * 将 file 对象转换为 Buffer + * + * @param file - file + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +export const fileToBuffer = async (file: any): Promise => { + return new Promise((resolve, reject) => { + const reader = new FileReader() + reader.onload = (e: any) => { + // 将 ArrayBuffer 转换成 Buffer 对象 + const buffer = Buffer.from(e.target.result) + resolve(buffer) + } + reader.onerror = reject + reader.readAsArrayBuffer(file) + }) +} diff --git a/src/utils/svgIcons.ts b/src/utils/svgIcons.ts new file mode 100644 index 00000000..802672d7 --- /dev/null +++ b/src/utils/svgIcons.ts @@ -0,0 +1,52 @@ +/* + * 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. + */ + +/** + * 图标集合 + */ +export const svgIcons = { + // Element-Plus svgIcons + iconEPPlus: ``, + // iconfont svgIcons + iconIFEdit: ``, + iconIFHexo: ``, + iconIFHugo: ``, + iconIFVue: ``, + iconIFNuxt: ``, + iconIFYuque: ``, + iconIFCnblogs: ``, + iconIFTypecho: ``, + iconIFWordpress: ``, + iconIFZhihu: ``, + iconIFCSDN: ``, + iconIFJianshu: ``, + iconIFJuejin: ``, + iconIFWechat: ``, + iconIFSiyuan: ``, + // FontAwesome svgIcons + // Other icons + iconOTYes: ``, + iconOTNo: ``, +} diff --git a/src/utils/utils.ts b/src/utils/utils.ts new file mode 100644 index 00000000..3434338a --- /dev/null +++ b/src/utils/utils.ts @@ -0,0 +1,98 @@ +/* + * 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 { AppInstance } from "~/src/appInstance.ts" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { BlogAdaptor, WebAdaptor } from "zhi-blog-api" +import { StrUtil } from "zhi-common" + +/** + * 通用工具类 + * + * @author terwer + * @version 0.9.0 + * @since 0.9.0 + */ +export class Utils { + private static logger = createAppLogger("publisher-widget-utils") + + public static blogApi(appInstance: AppInstance, apiAdaptor: any) { + if (!apiAdaptor) { + throw new Error("apiAdaptor cannot be null") + } + + if (!apiAdaptor.getUsersBlogs) { + this.logger.error("apiAdaptor must implements BlogApi", apiAdaptor) + throw new Error(`apiAdaptor must implements BlogApi => ${this.getObjectName(apiAdaptor)}`) + } + + return new BlogAdaptor(apiAdaptor) + } + + public static webApi(appInstance: AppInstance, webAdaptor: any) { + if (!webAdaptor) { + throw new Error("webAdaptor cannot be null") + } + + if (!webAdaptor.getMetaData) { + this.logger.error("webAdaptor must implements WebApi", webAdaptor) + throw new Error(`webAdaptor must implements WebApi => ${this.getObjectName(webAdaptor)}`) + } + + return new WebAdaptor(webAdaptor) + } + + private static getObjectName(obj) { + try { + // 判断是否为类 + if (typeof obj === "function" && /^class\s/.test(obj.toString())) { + return obj.name + } + // 判断是否为函数 + else if (typeof obj === "function") { + return obj.name || "anonymous function" + } + // 判断是否为枚举 + else if (typeof obj === "object" && Object.values(obj.constructor).includes(obj)) { + return Object.keys(obj.constructor)[Object.values(obj.constructor).indexOf(obj)] + } + // 判断是否为属性 + else if (typeof obj !== "object") { + return obj + } + // 默认返回空字符串 + else { + return "{}" + } + } catch (e) { + console.error(e) + return "{}" + } + } + + public static emptyOrDefault(value: any, defaultValue: any) { + return StrUtil.isEmptyString(value) ? defaultValue : value + } +} diff --git a/src/utils/widgetUtils.ts b/src/utils/widgetUtils.ts new file mode 100644 index 00000000..3952a125 --- /dev/null +++ b/src/utils/widgetUtils.ts @@ -0,0 +1,201 @@ +/* + * 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 { useSiyuanDevice } from "~/src/composables/useSiyuanDevice.ts" +import { StrUtil } from "zhi-common" +import { BrowserUtil, SiyuanDevice } from "zhi-device" +import { createAppLogger } from "~/src/utils/appLogger.ts" +import { DynamicConfig } from "~/src/components/set/publish/platform/dynamicConfig.ts" + +const logger = createAppLogger("widget-utils") + +/** + * 打开网页弹窗 + */ +export const openBrowserWindow = (url: string, dynCfg?: DynamicConfig, cookieCb?: any) => { + const { isInSiyuanWidget } = useSiyuanDevice() + + if (isInSiyuanWidget()) { + const isDev = false + const isModel = false + const isShow = !cookieCb + doOpenBrowserWindow(url, undefined, undefined, isDev, isModel, isShow, dynCfg, cookieCb) + } else { + window.open(url) + } +} + +/** + * 打开新窗口 + * + * 示例: + * + * ``` + * ## development + * openBrowserWindow("https://www.baidu.com", undefined, undefined, true, false) + * openBrowserWindow("https://www.baidu.com", { "key1": "value1", "key2": "value2" }, undefined, true, false) + * + * ## production + * openBrowserWindow("https://www.baidu.com") + * ``` + * + * @param url - url + * @param params - 参数 + * @param win - 父窗口 + * @param isDev - 是否打开开发者工具 + * @param modal - 是否模态 + * @param isShow - 是否显示 + * @param dynCfg - 动态配置 + * @param cookieCallback - 窗口关闭回调 + */ +const doOpenBrowserWindow = ( + url: string, + params?: Record, + win?: any, + isDev = false, + modal = false, + isShow = true, + dynCfg?: DynamicConfig, + cookieCallback = undefined +) => { + try { + if (StrUtil.isEmptyString(url)) { + logger.error("Url cannot be empty") + return + } + + const { isInSiyuanWidget } = useSiyuanDevice() + if (!BrowserUtil.isElectron() && !isInSiyuanWidget) { + logger.info("BrowserWindow can ony be available in siyuan Electron environment") + return + } + + if (params) { + Object.keys(params).forEach((key: string) => { + const value = params[key] + url = BrowserUtil.setUrlParameter(url, key, value) + }) + } + + logger.info(StrUtil.f("Opening a new BrowserWindow from url => {0}", url)) + + const mainWin = win ?? SiyuanDevice.siyuanWindow() + const { app, BrowserWindow, getCurrentWindow } = mainWin.require("@electron/remote") + const remote = mainWin.require("@electron/remote").require("@electron/remote/main") + const mainWindow = getCurrentWindow() + const newWindow = new BrowserWindow({ + parent: mainWindow, + width: 900, + height: 750, + show: isShow, + resizable: true, + modal: modal, + icon: SiyuanDevice.browserJoinPath( + SiyuanDevice.siyuanWindow().siyuan.config.system.appDir, + "stage", + "icon-large.png" + ), + titleBarOverlay: { + color: "#cccccca5", + symbolColor: "black", + }, + webPreferences: { + nativeWindowOpen: true, + nodeIntegration: true, + webviewTag: true, + webSecurity: false, + contextIsolation: false, + }, + }) + + newWindow.webContents.userAgent = `SiYuan/${app.getVersion()} https://b3log.org/siyuan Electron` + // 允许 + remote.enable(newWindow.webContents) + if (isDev) { + newWindow.webContents.openDevTools() + } + + // 监听 close 事件 + newWindow.on("close", (evt: any) => { + logger.info("窗口关闭事件触发") + }) + newWindow.loadURL(url) + + // 读取指定域的所有 Cookie + if (cookieCallback) { + const readCookies = () => { + // https://www.electronjs.org/zh/docs/latest/api/session + const ses = newWindow.webContents.session + const domain = dynCfg.domain + ses.cookies + .get({ domain }) + .then(async (cookies: any) => { + logger.info(`读取cookie事件触发,准备读取 ${domain} 下的所有 Cookie`) + await cookieCallback(dynCfg, cookies) + }) + .catch((error: any) => { + console.error(`读取 Cookie 失败:${error}`) + }) + } + readCookies() + newWindow.hide() + newWindow.close() + } + } catch (e) { + logger.error("Open browser window failed", e) + } +} + +/** + * 获取挂件所在的块ID + * 如果挂件未找到或块ID无效,则返回空字符串。 + */ +export const getWidgetId = (): string => { + // 检查是否在 iframe 中 + if ( + window.frameElement == null || + window.frameElement.parentElement == null || + window.frameElement.parentElement.parentElement == null + ) { + // 如果不在 iframe 中,返回空字符串 + return "" + } + + // 获取 iframe 的父级父级元素(即挂件所在的块) + const widgetContainer = window.frameElement.parentElement.parentElement + if (!widgetContainer) { + // 如果父级父级元素不存在,返回空字符串 + return "" + } + + // 获取块的唯一标识符(数据节点ID) + const widgetId = widgetContainer.getAttribute("data-node-id") + if (!widgetId) { + // 如果块的唯一标识符不存在,返回空字符串 + return "" + } + + // 返回挂件所在的块ID + return widgetId +} diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 00000000..36b6ecc3 --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1,26 @@ +/* + * 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. + */ + +/// diff --git a/src/vue-shim.d.ts b/src/vue-shim.d.ts new file mode 100644 index 00000000..c5a212d8 --- /dev/null +++ b/src/vue-shim.d.ts @@ -0,0 +1,30 @@ +/* + * 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. + */ + +declare module "*.vue" { + import { defineComponent } from "vue"; + const component: ReturnType; + export default component; +} \ No newline at end of file diff --git a/src/workers/quickPublish.vue b/src/workers/quickPublish.vue new file mode 100644 index 00000000..7e832382 --- /dev/null +++ b/src/workers/quickPublish.vue @@ -0,0 +1,99 @@ + + + + + + + diff --git a/syp.config.ts b/syp.config.ts new file mode 100644 index 00000000..b1ba1ad6 --- /dev/null +++ b/syp.config.ts @@ -0,0 +1,49 @@ +/* + * 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 { DYNAMIC_CONFIG_KEY } from "~/src/utils/constants.ts" + +interface ISypConfig { + // version?: "" + lang?: "zh_CN" | "en_US" + // 平台总的集合 + [DYNAMIC_CONFIG_KEY]?: any + + // [平台key1]: {平台配置1} + // [平台key2]: {平台配置2} + + // [siyuan文档ID]: { + // [custom-slug]: 初始化生成,初始化可读取siyuan属性,但是之后不能再修改 + // [动态平台1postid的key]: 对应平台的文章ID + // [动态平台2postid的key]: 对应平台的文章ID + // } + + [key: string]: any +} + +export const SypConfig: ISypConfig = { + lang: "zh_CN", + [DYNAMIC_CONFIG_KEY]: "{}", +}