From 51cf3d577acbc83ed22649f450aa48c7cc10b218 Mon Sep 17 00:00:00 2001 From: terwer Date: Sun, 11 Jun 2023 20:59:21 +0800 Subject: [PATCH 1/4] docs: update deps --- .changeset/config.json | 2 +- libs/zhi-device/src/lib/browserUtil.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/config.json b/.changeset/config.json index 6d2119a4..351ac15b 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -5,7 +5,7 @@ "fixed": [], "linked": [], "access": "restricted", - "baseBranch": "main", + "baseBranch": "dev", "updateInternalDependencies": "patch", "ignore": [] } diff --git a/libs/zhi-device/src/lib/browserUtil.ts b/libs/zhi-device/src/lib/browserUtil.ts index 1279a3fd..781f888d 100644 --- a/libs/zhi-device/src/lib/browserUtil.ts +++ b/libs/zhi-device/src/lib/browserUtil.ts @@ -79,7 +79,7 @@ class BrowserUtil { * @since 0.0.1 */ public static getQueryParam = (key: string) => { - if (BrowserUtil.isInBrowser) { + if (!BrowserUtil.isInBrowser) { return "" } From a7bb2995d73f6d0d26d6254467c66fe004d59352 Mon Sep 17 00:00:00 2001 From: terwer Date: Sun, 11 Jun 2023 21:00:41 +0800 Subject: [PATCH 2/4] docs: update deps --- apps/zhi-cli/CHANGELOG.md | 6 ++++++ apps/zhi-cli/package.json | 2 +- libs/zhi-device/CHANGELOG.md | 6 ++++++ libs/zhi-device/package.json | 2 +- libs/zhi-device/src/lib/browserUtil.ts | 1 + libs/zhi-fetch-middleware/CHANGELOG.md | 7 +++++++ libs/zhi-fetch-middleware/package.json | 2 +- libs/zhi-log/CHANGELOG.md | 7 +++++++ libs/zhi-log/package.json | 2 +- libs/zhi-xmlrpc-middleware/CHANGELOG.md | 7 +++++++ libs/zhi-xmlrpc-middleware/package.json | 2 +- 11 files changed, 39 insertions(+), 5 deletions(-) diff --git a/apps/zhi-cli/CHANGELOG.md b/apps/zhi-cli/CHANGELOG.md index 1d773f3e..01ddcb93 100644 --- a/apps/zhi-cli/CHANGELOG.md +++ b/apps/zhi-cli/CHANGELOG.md @@ -1,5 +1,11 @@ # zhi-cli +## 1.5.8 + +### Patch Changes + +- zhi-log@1.14.8 + ## 1.5.7 ### Patch Changes diff --git a/apps/zhi-cli/package.json b/apps/zhi-cli/package.json index 3e251aa4..e5f5e558 100644 --- a/apps/zhi-cli/package.json +++ b/apps/zhi-cli/package.json @@ -1,6 +1,6 @@ { "name": "zhi-cli", - "version": "1.5.7", + "version": "1.5.8", "description": "a tool for generating zhi framework related projects", "type": "module", "bin": { diff --git a/libs/zhi-device/CHANGELOG.md b/libs/zhi-device/CHANGELOG.md index 7512c78e..35d09a37 100644 --- a/libs/zhi-device/CHANGELOG.md +++ b/libs/zhi-device/CHANGELOG.md @@ -1,5 +1,11 @@ # zhi-device +## 2.1.0 + +### Minor Changes + +- fix hashcquery + ## 2.0.0 ### Major Changes diff --git a/libs/zhi-device/package.json b/libs/zhi-device/package.json index 91bd5561..98d96c36 100644 --- a/libs/zhi-device/package.json +++ b/libs/zhi-device/package.json @@ -1,6 +1,6 @@ { "name": "zhi-device", - "version": "2.0.0", + "version": "2.1.0", "type": "module", "main": "./dist/index.js", "typings": "./dist/index.d.ts", diff --git a/libs/zhi-device/src/lib/browserUtil.ts b/libs/zhi-device/src/lib/browserUtil.ts index 781f888d..c2346232 100644 --- a/libs/zhi-device/src/lib/browserUtil.ts +++ b/libs/zhi-device/src/lib/browserUtil.ts @@ -79,6 +79,7 @@ class BrowserUtil { * @since 0.0.1 */ public static getQueryParam = (key: string) => { + // check env if (!BrowserUtil.isInBrowser) { return "" } diff --git a/libs/zhi-fetch-middleware/CHANGELOG.md b/libs/zhi-fetch-middleware/CHANGELOG.md index 3625771e..400fb0c4 100644 --- a/libs/zhi-fetch-middleware/CHANGELOG.md +++ b/libs/zhi-fetch-middleware/CHANGELOG.md @@ -1,5 +1,12 @@ # zhi-fetch-middleware +## 0.1.8 + +### Patch Changes + +- Updated dependencies + - zhi-device@2.1.0 + ## 0.1.7 ### Patch Changes diff --git a/libs/zhi-fetch-middleware/package.json b/libs/zhi-fetch-middleware/package.json index f5e52355..eb3121ae 100644 --- a/libs/zhi-fetch-middleware/package.json +++ b/libs/zhi-fetch-middleware/package.json @@ -1,6 +1,6 @@ { "name": "zhi-fetch-middleware", - "version": "0.1.7", + "version": "0.1.8", "type": "module", "description": "an intermediate tier prepared for fetch requests", "main": "./dist/index.js", diff --git a/libs/zhi-log/CHANGELOG.md b/libs/zhi-log/CHANGELOG.md index d2adb28d..f079dd7f 100644 --- a/libs/zhi-log/CHANGELOG.md +++ b/libs/zhi-log/CHANGELOG.md @@ -1,5 +1,12 @@ # zhi-log +## 1.14.8 + +### Patch Changes + +- Updated dependencies + - zhi-device@2.1.0 + ## 1.14.7 ### Patch Changes diff --git a/libs/zhi-log/package.json b/libs/zhi-log/package.json index 0b9a2969..a3a6f0fe 100644 --- a/libs/zhi-log/package.json +++ b/libs/zhi-log/package.json @@ -1,6 +1,6 @@ { "name": "zhi-log", - "version": "1.14.7", + "version": "1.14.8", "type": "module", "main": "./dist/index.js", "typings": "./dist/index.d.ts", diff --git a/libs/zhi-xmlrpc-middleware/CHANGELOG.md b/libs/zhi-xmlrpc-middleware/CHANGELOG.md index d714c753..ceda113e 100644 --- a/libs/zhi-xmlrpc-middleware/CHANGELOG.md +++ b/libs/zhi-xmlrpc-middleware/CHANGELOG.md @@ -1,5 +1,12 @@ # zhi-xmlrpc-middleware +## 0.2.8 + +### Patch Changes + +- Updated dependencies + - zhi-device@2.1.0 + ## 0.2.7 ### Patch Changes diff --git a/libs/zhi-xmlrpc-middleware/package.json b/libs/zhi-xmlrpc-middleware/package.json index 5e0054f5..40c0f9c3 100644 --- a/libs/zhi-xmlrpc-middleware/package.json +++ b/libs/zhi-xmlrpc-middleware/package.json @@ -1,6 +1,6 @@ { "name": "zhi-xmlrpc-middleware", - "version": "0.2.7", + "version": "0.2.8", "type": "module", "description": "abstract xmlrpc middle layer", "main": "./dist/index.js", From 415c2e3dd6d7529d8a57713332c026ee6f453b68 Mon Sep 17 00:00:00 2001 From: terwer Date: Sun, 11 Jun 2023 23:02:28 +0800 Subject: [PATCH 3/4] docs: update deps --- .../.env.development.local.example | 1 + libs/zhi-siyuan-api/.eslintrc.cjs | 4 + libs/zhi-siyuan-api/.gitignore | 3 + libs/zhi-siyuan-api/CHANGELOG.md | 7 + libs/zhi-siyuan-api/README.md | 89 +++ libs/zhi-siyuan-api/index.html | 12 + libs/zhi-siyuan-api/package.json | 45 ++ libs/zhi-siyuan-api/src/index.spec.ts | 32 ++ libs/zhi-siyuan-api/src/index.ts | 10 + .../src/lib/adaptor/siYuanApiAdaptor.spec.ts | 173 ++++++ .../src/lib/adaptor/siYuanApiAdaptor.ts | 280 ++++++++++ .../src/lib/client/siyuanClientApi.ts | 28 + .../src/lib/config/siyuanConfig.ts | 87 +++ .../src/lib/config/siyuanPlaceholder.ts | 33 ++ .../src/lib/kernel/ISiyuanKernelApi.ts | 84 +++ .../src/lib/kernel/siyuanKernelApi.spec.ts | 145 +++++ .../src/lib/kernel/siyuanKernelApi.ts | 520 ++++++++++++++++++ libs/zhi-siyuan-api/src/lib/siyuanApi.ts | 59 ++ .../zhi-siyuan-api/src/lib/siyuanConstants.ts | 45 ++ .../src/lib/zhi-siyuan-api.spec.ts | 44 ++ libs/zhi-siyuan-api/src/lib/zhi-siyuan-api.ts | 27 + libs/zhi-siyuan-api/tsconfig.json | 28 + libs/zhi-siyuan-api/tsconfig.node.json | 10 + libs/zhi-siyuan-api/vite.config.ts | 78 +++ pnpm-lock.yaml | 39 +- 25 files changed, 1872 insertions(+), 11 deletions(-) create mode 100644 libs/zhi-siyuan-api/.env.development.local.example create mode 100644 libs/zhi-siyuan-api/.eslintrc.cjs create mode 100644 libs/zhi-siyuan-api/.gitignore create mode 100644 libs/zhi-siyuan-api/CHANGELOG.md create mode 100644 libs/zhi-siyuan-api/README.md create mode 100644 libs/zhi-siyuan-api/index.html create mode 100644 libs/zhi-siyuan-api/package.json create mode 100644 libs/zhi-siyuan-api/src/index.spec.ts create mode 100644 libs/zhi-siyuan-api/src/index.ts create mode 100644 libs/zhi-siyuan-api/src/lib/adaptor/siYuanApiAdaptor.spec.ts create mode 100644 libs/zhi-siyuan-api/src/lib/adaptor/siYuanApiAdaptor.ts create mode 100644 libs/zhi-siyuan-api/src/lib/client/siyuanClientApi.ts create mode 100644 libs/zhi-siyuan-api/src/lib/config/siyuanConfig.ts create mode 100644 libs/zhi-siyuan-api/src/lib/config/siyuanPlaceholder.ts create mode 100644 libs/zhi-siyuan-api/src/lib/kernel/ISiyuanKernelApi.ts create mode 100644 libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.spec.ts create mode 100644 libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.ts create mode 100644 libs/zhi-siyuan-api/src/lib/siyuanApi.ts create mode 100644 libs/zhi-siyuan-api/src/lib/siyuanConstants.ts create mode 100644 libs/zhi-siyuan-api/src/lib/zhi-siyuan-api.spec.ts create mode 100644 libs/zhi-siyuan-api/src/lib/zhi-siyuan-api.ts create mode 100644 libs/zhi-siyuan-api/tsconfig.json create mode 100644 libs/zhi-siyuan-api/tsconfig.node.json create mode 100644 libs/zhi-siyuan-api/vite.config.ts diff --git a/libs/zhi-siyuan-api/.env.development.local.example b/libs/zhi-siyuan-api/.env.development.local.example new file mode 100644 index 00000000..04f0ca42 --- /dev/null +++ b/libs/zhi-siyuan-api/.env.development.local.example @@ -0,0 +1 @@ +VITE_DEBUG_MODE=true diff --git a/libs/zhi-siyuan-api/.eslintrc.cjs b/libs/zhi-siyuan-api/.eslintrc.cjs new file mode 100644 index 00000000..a1993b7b --- /dev/null +++ b/libs/zhi-siyuan-api/.eslintrc.cjs @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: ["./node_modules/@terwer/eslint-config-custom/typescript/index.cjs"], +} diff --git a/libs/zhi-siyuan-api/.gitignore b/libs/zhi-siyuan-api/.gitignore new file mode 100644 index 00000000..7341ab31 --- /dev/null +++ b/libs/zhi-siyuan-api/.gitignore @@ -0,0 +1,3 @@ +.idea +.DS_Store +testdata \ No newline at end of file diff --git a/libs/zhi-siyuan-api/CHANGELOG.md b/libs/zhi-siyuan-api/CHANGELOG.md new file mode 100644 index 00000000..20ddc9f0 --- /dev/null +++ b/libs/zhi-siyuan-api/CHANGELOG.md @@ -0,0 +1,7 @@ +# zhi-siyuan-api + +## 1.8.0 + +### Minor Changes + +- new kernel api diff --git a/libs/zhi-siyuan-api/README.md b/libs/zhi-siyuan-api/README.md new file mode 100644 index 00000000..90fffc01 --- /dev/null +++ b/libs/zhi-siyuan-api/README.md @@ -0,0 +1,89 @@ +# zhi-siyuan-api + +a siyuan-note api including both kernel and client + +## Usage + +```ts +import { SiyuanKernelApi } from "zhi-siyuan-api" +import { ZhiUtil } from "zhi-common" + +// appInstance +const appInstance: any = {} +appInstance.zhiCommon = { + ZhiUtil: ZhiUtil +} +console.log(appInstance) + +// kernelApi +const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") +const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) +const result = await kernelApi.lsNotebooks() +console.log("result=>", result) +``` + +dynamic invoke + +```ts +import { SiyuanKernelApi } from "zhi-siyuan-api" + +// appInstance也可以动态加载,减小打包体积 +const appInstance: any = {} + +// polyfills +const moduleBase = "" +console.log("moduleBase=>", moduleBase) +// https://github.com/terwer/siyuan-plugin-publisher/blob/main/public/polyfills/fs.js +appInstance.fs = (await import(`${moduleBase}/polyfills/fs.js`))["default"] +// https://github.com/terwer/siyuan-plugin-publisher/blob/main/public/polyfills/path.js +appInstance.path = (await import(`${moduleBase}/polyfills/path.js`))["default"] +appInstance.importDep = async (moduleName) => { + return await import(appInstance.path.join(moduleBase, moduleName)) +} + +const zhiCommon = (await appInstance.importDep("./libs/zhi-common/index.js")) as any +appInstance.zhiCommon = { + ZhiUtil: zhiCommon["ZhiUtil"], +} +console.log(appInstance) + +// kernelApi +const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") +const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) +const result = await kernelApi.lsNotebooks() +console.log("result=>", result) +``` + +## Deps + +``` +├── zhi-common - [dynaminc dependency] +├── zhi-blog-api +├── zhi-lib-base +``` + +## Dev + +```bash +pnpm dev -F zhi-siyuan-api +``` + +## Build + +```bash +pnpm build -F zhi-siyuan-api +``` + +## Test + +Execute the unit tests via [vitest](https://vitest.dev) + +```bash +pnpm test -F zhi-siyuan-api +``` + +## Publish + +```bash +pnpm publish -F zhi-siyuan-api --tag latest +``` \ No newline at end of file diff --git a/libs/zhi-siyuan-api/index.html b/libs/zhi-siyuan-api/index.html new file mode 100644 index 00000000..9ecb5ed8 --- /dev/null +++ b/libs/zhi-siyuan-api/index.html @@ -0,0 +1,12 @@ + + + + + + Vite + Vue + TS + + + This file is for lib hot-load test only, see /src/index.ts + + + diff --git a/libs/zhi-siyuan-api/package.json b/libs/zhi-siyuan-api/package.json new file mode 100644 index 00000000..b52256a7 --- /dev/null +++ b/libs/zhi-siyuan-api/package.json @@ -0,0 +1,45 @@ +{ + "name": "zhi-siyuan-api", + "version": "1.8.0", + "type": "module", + "description": "a siyuan-note api including both kernel and client", + "main": "./dist/index.js", + "typings": "./dist/index.d.ts", + "repository": "terwer/zhi", + "homepage": "https://github.com/terwer/zhi/tree/main/libs/zhi-siyuan-api", + "author": "terwer", + "license": "GPL", + "files": [ + "dist", + "README.md" + ], + "keywords": [ + "zhi", + "lib", + "siyuan", + "note", + "api" + ], + "scripts": { + "serve": "vite", + "dev": "vite build --watch", + "build": "vite build", + "start": "vite preview", + "test": "vitest --watch" + }, + "devDependencies": { + "@terwer/eslint-config-custom": "workspace:*", + "@terwer/tsconfig": "workspace:*", + "@terwer/vite-config-custom": "workspace:*", + "form-data": "^4.0.0" + }, + "dependencies": { + "cross-fetch": "^3.1.6", + "zhi-blog-api": "workspace:*", + "zhi-common": "workspace:*", + "zhi-lib-base": "workspace:*" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/libs/zhi-siyuan-api/src/index.spec.ts b/libs/zhi-siyuan-api/src/index.spec.ts new file mode 100644 index 00000000..d51b318c --- /dev/null +++ b/libs/zhi-siyuan-api/src/index.spec.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. + */ + +import { describe, it } from "vitest" + +describe("zhi-siyuan-api", () => { + it("index", () => { + console.log("hello world") + }) +}) diff --git a/libs/zhi-siyuan-api/src/index.ts b/libs/zhi-siyuan-api/src/index.ts new file mode 100644 index 00000000..de4df401 --- /dev/null +++ b/libs/zhi-siyuan-api/src/index.ts @@ -0,0 +1,10 @@ +import SiyuanApi from "./lib/zhi-siyuan-api" +import SiyuanKernelApi from "./lib/kernel/siyuanKernelApi" +import type { SiyuanData } from "./lib/kernel/ISiyuanKernelApi" +import SiyuanConfig from "./lib/config/siyuanConfig" +import SiYuanApiAdaptor from "./lib/adaptor/siYuanApiAdaptor" +import SiyuanConstants from "./lib/siyuanConstants" + +export { SiyuanApi } +export { SiyuanData, SiyuanKernelApi } +export { SiyuanConstants, SiyuanConfig, SiYuanApiAdaptor } diff --git a/libs/zhi-siyuan-api/src/lib/adaptor/siYuanApiAdaptor.spec.ts b/libs/zhi-siyuan-api/src/lib/adaptor/siYuanApiAdaptor.spec.ts new file mode 100644 index 00000000..6102a3b4 --- /dev/null +++ b/libs/zhi-siyuan-api/src/lib/adaptor/siYuanApiAdaptor.spec.ts @@ -0,0 +1,173 @@ +/* + * 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 { afterEach, beforeEach, describe, expect, it } from "vitest" +import SiYuanApiAdaptor from "./siYuanApiAdaptor" +import SiyuanConfig from "../config/siyuanConfig" +import path from "path" +import { MediaObject, Post } from "zhi-blog-api" +import fs from "fs" +import SiyuanKernelApi from "../kernel/siyuanKernelApi" + +describe("SiYuanApiAdaptor", async () => { + // appInstance + const appInstance: any = {} + const projectBase = path.resolve(__dirname, "../../..") + const moduleBase = path.resolve(__dirname, "../../../../..") + const zhiCommon = (await import(path.join(moduleBase, "libs/zhi-common/dist/index.js"))) as any + appInstance.zhiCommon = { + ZhiUtil: zhiCommon["ZhiUtil"], + } + // lute + require(path.join(moduleBase, "libs/zhi-common/public/libs/lute/lute-1.7.5-20230410.min.cjs")) + + beforeEach(async () => { + console.log("======test is starting...======") + }) + + afterEach(() => { + console.log("======test is finished.========") + }) + + it("test apiAdaptor", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const apiAdaptor = new SiYuanApiAdaptor(appInstance, siyuanConfig) + + expect(apiAdaptor).toBeTruthy() + }) + + it("test siyuan getUsersBlogs", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const apiAdaptor = new SiYuanApiAdaptor(appInstance, siyuanConfig) + + const usersBlogs = await apiAdaptor.getUsersBlogs() + console.log(usersBlogs) + }) + + it("test siyuan getRecentPostsCount", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const apiAdaptor = new SiYuanApiAdaptor(appInstance, siyuanConfig) + + const recentPostsCount = await apiAdaptor.getRecentPostsCount() + console.log(recentPostsCount) + }) + + it("test siyuan getRecentPosts", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const apiAdaptor = new SiYuanApiAdaptor(appInstance, siyuanConfig) + + const recentPosts = await apiAdaptor.getRecentPosts(10) + console.log(recentPosts) + }) + + it("test siyuan newPost", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + siyuanConfig.notebook = "20230506132031-qbtyjdk" + const apiAdaptor = new SiYuanApiAdaptor(appInstance, siyuanConfig) + + const post = new Post() + post.title = "自动发布的测试标题" + post.description = "自动发布的测试内容" + post.mt_keywords = "标签1,标签2" + post.categories = ["分类1", "分类2"] + // post.dateCreated = new Date() + const postid = await apiAdaptor.newPost(post) + console.log(postid) + }) + + it("test siyuan getPost", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const apiAdaptor = new SiYuanApiAdaptor(appInstance, siyuanConfig) + + const postid = "20230526221603-3mgotyw" + const post = await apiAdaptor.getPost(postid) + console.log(post) + }) + + it("test siyuan editPost", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const apiAdaptor = new SiYuanApiAdaptor(appInstance, siyuanConfig) + + const postid = "20230526221603-3mgotyw" + const post = new Post() + post.title = "自动发布的测试标题2" + post.description = "# 自动发布的测试内容2" + post.mt_keywords = "标签1,标签2" + post.categories = ["分类1", "分类2"] + // post.dateCreated = new Date() + const data = await apiAdaptor.editPost(postid, post) + console.log(data) + }) + + it("test siyuan deletePost", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + siyuanConfig.notebook = "20230506132031-qbtyjdk" + const apiAdaptor = new SiYuanApiAdaptor(appInstance, siyuanConfig) + + const postid = "20230526224518-oea9ey7" + const data = await apiAdaptor.deletePost(postid) + console.log(data) + }) + + it("test siyuan getCategories", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const apiAdaptor = new SiYuanApiAdaptor(appInstance, siyuanConfig) + + const data = await apiAdaptor.getCategories() + console.log(data) + }) + + it("test siyuan getPreviewUrl", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + siyuanConfig.home = "http://127.0.0.1:6806" + siyuanConfig.previewUrl = "siyuan://blocks/[postid]" + const apiAdaptor = new SiYuanApiAdaptor(appInstance, siyuanConfig) + + const postid = "20230526221603-3mgotyw" + const data = await apiAdaptor.getPreviewUrl(postid) + console.log(data) + }) + + it("test siyuan newMediaObject", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const apiAdaptor = new SiYuanApiAdaptor(appInstance, siyuanConfig) + const siyuanKernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + + const url = path.join(projectBase, "./testdata/photo.jpg") + const file = fs.readFileSync(url) + const mediaObject = new MediaObject("20220616-132401-001.jpg", "image/jpeg", file) + const data = await apiAdaptor.newMediaObject(mediaObject, async () => { + const FormData = require("form-data") + const formData = new FormData() + formData.append("file[]", mediaObject.bits, mediaObject.name) + formData.append("assetsDirPath", "/assets/") + + const data = await siyuanKernelApi.uploadAsset(formData) + console.log("uploadAsset=>", data) + return data + }) + console.log("test newMediaObject finished=>", data) + }) +}) diff --git a/libs/zhi-siyuan-api/src/lib/adaptor/siYuanApiAdaptor.ts b/libs/zhi-siyuan-api/src/lib/adaptor/siYuanApiAdaptor.ts new file mode 100644 index 00000000..b19501c8 --- /dev/null +++ b/libs/zhi-siyuan-api/src/lib/adaptor/siYuanApiAdaptor.ts @@ -0,0 +1,280 @@ +/* + * 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 { Attachment, BlogApi, CategoryInfo, MediaObject, Post, PostStatusEnum, UserBlog } from "zhi-blog-api" +import SiyuanKernelApi from "../kernel/siyuanKernelApi" +import SiyuanConfig from "../config/siyuanConfig" +import { NotImplementedException, simpleLogger } from "zhi-lib-base" + +/** + * 思源笔记API适配器 + * + * @author terwer + * @version 1.0.0 + * @since 1.0.0 + */ +class SiYuanApiAdaptor extends BlogApi { + private logger: any + private common: any + private readonly siyuanKernelApi + private readonly cfg + + /** + * 初始化思源 API 适配器 + * + * @param appInstance - 应用实例[必须包含zhiCommon.ZhiUtil] + * @param cfg 配置项 + */ + constructor(appInstance: any, cfg: SiyuanConfig) { + super() + + this.cfg = cfg + + this.logger = simpleLogger("zhi-siyuan-api", "siyuan-api-adaptor", false) + + this.siyuanKernelApi = new SiyuanKernelApi(appInstance, cfg) + } + + public override async getUsersBlogs(): Promise> { + const usersBlogs: UserBlog[] = [] + const userBlog = new UserBlog() + + userBlog.blogid = "siyuan-note" + userBlog.url = window.location.origin + userBlog.blogName = "思源笔记" + + usersBlogs.push(userBlog) + + return usersBlogs + } + + public override async getRecentPostsCount(keyword?: string): Promise { + return await this.siyuanKernelApi.getRootBlocksCount(keyword ?? "") + } + + public override async getRecentPosts(numOfPosts: number, page?: number, keyword?: string): Promise> { + const result: Post[] = [] + + let pg = 0 + if (page) { + pg = page + } + const k = keyword ?? "" + const siyuanPosts = await this.siyuanKernelApi.getRootBlocks(pg, numOfPosts, k) + // logUtil.logInfo(siyuanPosts) + + this.logger.debug("getRecentPosts from siyuan, get counts =>", siyuanPosts.length) + for (let i = 0; i < siyuanPosts.length; i++) { + const siyuanPost = siyuanPosts[i] + + // 某些属性详情页控制即可 + const attrs = await this.siyuanKernelApi.getBlockAttrs(siyuanPost.root_id) + const page = await this.getPost(siyuanPost.root_id, false, true) + + // // 发布状态 + // let isPublished = true + // const publishStatus = attrs["custom-publish-status"] || "draft" + // if (publishStatus == "secret") { + // isPublished = false; + // } + // + // // 访问密码 + // const postPassword = attrs["custom-publish-password"] || "" + + // 文章别名 + const customSlug = attrs["custom-slug"] || "" + + // 标题处理 + let title = siyuanPost.content ?? "" + if (this.cfg.fixTitle) { + title = this.common.htmlUtil.removeTitleNumber(title) + } + + // 适配公共属性 + const commonPost = new Post() + commonPost.postid = siyuanPost.root_id + commonPost.title = title + commonPost.permalink = + customSlug === "" + ? this.common.strUtil.appendStr("/post/", siyuanPost.root_id) + : this.common.strUtil.appendStr("/post/", customSlug, ".html") + // commonPost.isPublished = isPublished + commonPost.mt_keywords = page.mt_keywords + commonPost.description = page.description + result.push(commonPost) + } + + return result + } + + public override async newPost(post: Post, publish?: boolean): Promise { + return await this.siyuanKernelApi.createDocWithMd(this.cfg.notebook, `/${post.title}`, post.description) + } + + public override async getPost(postid: string, useSlug?: boolean, skipBody?: boolean): Promise { + let pid = postid + if (useSlug) { + const pidObj = await this.siyuanKernelApi.getRootBlockBySlug(postid) + if (pidObj) { + pid = pidObj.root_id + } + } + const siyuanPost = await this.siyuanKernelApi.getBlockByID(pid) + if (!siyuanPost) { + throw new Error("文章不存存在,postid=>" + pid) + } + + const attrs = await this.siyuanKernelApi.getBlockAttrs(pid) + + // 发布状态 + let isPublished = true + const publishStatus = attrs["custom-publish-status"] || "draft" + if (publishStatus === "secret") { + isPublished = false + } + + // 访问密码 + const postPassword = attrs["custom-post-password"] || "" + + // 访问密码 + const shortDesc = attrs["custom-desc"] || "" + + // 标题处理 + let title = siyuanPost.content ?? "" + if (this.cfg.fixTitle) { + title = this.common.htmlUtil.removeTitleNumber(title) + } + + // 渲染Markdown + let html + // 如果忽略 body,则不进行转换 + if (!skipBody) { + const md = await this.siyuanKernelApi.exportMdContent(pid) + html = await this.common.markdownUtil.renderHTML(md.content) + // 移除挂件html + html = this.common.htmlUtil.removeWidgetTag(html) + if (this.cfg.fixTitle) { + html = this.common.htmlUtil.removeH1(html) + } + } + + // 适配公共属性 + const commonPost = new Post() + commonPost.postid = siyuanPost.root_id || "" + commonPost.title = title || "" + commonPost.description = html || "" + commonPost.shortDesc = shortDesc || "" + commonPost.mt_keywords = attrs.tags || "" + commonPost.post_status = isPublished ? PostStatusEnum.PostStatusEnum_Publish : PostStatusEnum.PostStatusEnum_Draft + commonPost.wp_password = postPassword + // commonPost.dateCreated + + return commonPost + } + + public override async editPost(postid: string, post: Post, publish?: boolean): Promise { + return await super.editPost(postid, post, publish) + } + + public override async deletePost(postid: string): Promise { + await this.siyuanKernelApi.removeDoc(this.cfg.notebook, `/${postid}.sy`) + return true + } + + public override async getCategories(): Promise { + const cats = [] as CategoryInfo[] + + let notebooks: any[] = (await this.siyuanKernelApi.lsNotebooks()).notebooks + //用户指南不应该作为可以写入的笔记本 + const hiddenNotebook: Set = new Set(["思源笔记用户指南", "SiYuan User Guide"]) + notebooks = notebooks.filter((notebook) => !notebook.closed && !hiddenNotebook.has(notebook.name)) + this.logger.debug("siyuan notebooks=>", notebooks) + if (notebooks && notebooks.length > 0) { + notebooks.forEach((notebook) => { + const cat = new CategoryInfo() + cat.categoryId = notebook.id + cat.categoryName = notebook.name + cat.description = notebook.name + cat.categoryDescription = notebook.name + cats.push(cat) + }) + } + + return cats + } + + public override async getPreviewUrl(postid: string): Promise { + // 检查 previewUrl 是否包含 [postid] 参数 + if (!this.cfg.previewUrl?.includes("[postid]")) { + throw new Error("Missing [postid] parameter in preview URL") + } + + // 替换文章链接 + let previewUrl = this.cfg.previewUrl + if (previewUrl.includes("[home]")) { + previewUrl = previewUrl.replace("[home]", this.cfg.home ?? "") + } + return previewUrl.replace("[postid]", postid) + } + + public override async newMediaObject(mediaObject: MediaObject, customHandler?: any): Promise { + if (!customHandler) { + throw new NotImplementedException("You must implement custom handler for siyuan assets") + } + + this.logger.info("Using custom handler for mediaObject") + const data = await customHandler(mediaObject) + this.logger.info("newMediaObject finished, data=>", data) + const attachmentInfo = new Attachment({}) + if (data && data.succMap) { + const link = data.succMap[mediaObject.name] + attachmentInfo.attachment_id = this.extractFileName(link) + attachmentInfo.date_created_gmt = new Date() + attachmentInfo.file = mediaObject.name + attachmentInfo.id = attachmentInfo.attachment_id + attachmentInfo.link = link + attachmentInfo.parent = 0 + attachmentInfo.metadata.file = link + attachmentInfo.thumbnail = link + attachmentInfo.title = mediaObject.name + attachmentInfo.type = mediaObject.type + attachmentInfo.url = link + } + + return attachmentInfo + } + + private extractFileName(filePath: string): string { + const fileNameWithExt = filePath.split("/").pop()! + const fileNameWithoutExt = fileNameWithExt.split(".").slice(0, -1).join(".") + if (fileNameWithoutExt.indexOf("-") !== -1) { + return fileNameWithoutExt.split("-").slice(-2).join("-") + } else { + throw new Error(`Failed to extract file name from ${filePath}.`) + } + } +} + +export default SiYuanApiAdaptor diff --git a/libs/zhi-siyuan-api/src/lib/client/siyuanClientApi.ts b/libs/zhi-siyuan-api/src/lib/client/siyuanClientApi.ts new file mode 100644 index 00000000..56ab1262 --- /dev/null +++ b/libs/zhi-siyuan-api/src/lib/client/siyuanClientApi.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 SiyuanClientApi {} + +export default SiyuanClientApi diff --git a/libs/zhi-siyuan-api/src/lib/config/siyuanConfig.ts b/libs/zhi-siyuan-api/src/lib/config/siyuanConfig.ts new file mode 100644 index 00000000..d8ac0a2a --- /dev/null +++ b/libs/zhi-siyuan-api/src/lib/config/siyuanConfig.ts @@ -0,0 +1,87 @@ +/* + * 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, PasswordType } from "zhi-blog-api" +import SiyuanPlaceholder from "./siyuanPlaceholder" + +/** + * 思源笔记配置 + * + * @author terwer + * @since 1.0.0 + */ +class SiyuanConfig extends BlogConfig { + /** + * 主页 + */ + public override home = "" + + /** + * 思源笔记伺服地址 + */ + public override apiUrl = "" + + /** + * 思源笔记 API token + */ + public override password = "" + + /** + * 思源笔记操作提示 + * + * @protected + */ + public override placeholder = {} as SiyuanPlaceholder + + /** + * 是否修复标题 + * + * @protected + */ + public override fixTitle = true + + /** + * 预览链接 + */ + public override previewUrl = "" + + /** + * 默认笔记本 + */ + public notebook: string + + constructor(apiUrl?: string, password?: string) { + super() + this.home = "http://127.0.0.1:6806" + this.apiUrl = apiUrl ?? "http://127.0.0.1:6806" + this.passwordType = PasswordType.PasswordType_Token + this.password = password ?? "" + this.placeholder = new SiyuanPlaceholder() + this.fixTitle = true + this.previewUrl = "siyuan://blocks/[postid]" + this.notebook = "" + } +} + +export default SiyuanConfig diff --git a/libs/zhi-siyuan-api/src/lib/config/siyuanPlaceholder.ts b/libs/zhi-siyuan-api/src/lib/config/siyuanPlaceholder.ts new file mode 100644 index 00000000..a43d6389 --- /dev/null +++ b/libs/zhi-siyuan-api/src/lib/config/siyuanPlaceholder.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 { BlogPlaceholder } from "zhi-blog-api" + +/** + * 思源笔记操作提示 + */ +class SiyuanPlaceholder extends BlogPlaceholder {} + +export default SiyuanPlaceholder diff --git a/libs/zhi-siyuan-api/src/lib/kernel/ISiyuanKernelApi.ts b/libs/zhi-siyuan-api/src/lib/kernel/ISiyuanKernelApi.ts new file mode 100644 index 00000000..822da79d --- /dev/null +++ b/libs/zhi-siyuan-api/src/lib/kernel/ISiyuanKernelApi.ts @@ -0,0 +1,84 @@ +/* + * 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. + */ + +/** + * 思源 API 返回类型 + */ +interface SiyuanData { + /** + * 非 0 为异常情况 + */ + code: number + + /** + * 正常情况下是空字符串,异常情况下会返回错误文案 + */ + msg: string + + /** + * 可能为 \{\}、[] 或者 NULL,根据不同接口而不同 + */ + data: any[] | object | null | undefined +} + +/** + * 思源笔记内核接口定义 + * + * @see {@link https://github.com/siyuan-note/siyuan/blob/master/API_zh_CN.md#%E9%89%B4%E6%9D%83 siyuan-api} + */ +interface ISiyuanKernelApi { + // /api/notebook/lsNotebooks + lsNotebooks(): Promise + // /api/notebook/openNotebook + openNotebook(notebookId: string): Promise + // /api/notebook/closeNotebook + closeNotebook(notebookId: string): Promise + // /api/notebook/renameNotebook + renameNotebook(notebookId: string, name: string): Promise + // /api/notebook/createNotebook + createNotebook(name: string): Promise + // /api/notebook/removeNotebook + removeNotebook(notebookId: string): Promise + // /api/notebook/getNotebookConf + getNotebookConf(notebookId: string): Promise + // /api/notebook/setNotebookConf + setNotebookConf(notebookConf: object): Promise + + // /api/notification/pushMsg + pushMsg(msgObj: object): Promise + // /api/notification/pushErrMsg + pushErrMsg(msgObj: object): Promise + + // /api/filetree/createDocWithMd + createDocWithMd(notebook: string, path: string, md: string): Promise + // /api/filetree/removeDoc + removeDoc(notebook: string, path: string): Promise + + // /api/asset/upload + uploadAsset(formData: any): Promise +} + +export default ISiyuanKernelApi +export type { SiyuanData } diff --git a/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.spec.ts b/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.spec.ts new file mode 100644 index 00000000..fff216e6 --- /dev/null +++ b/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.spec.ts @@ -0,0 +1,145 @@ +/* + * 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 { describe, it } from "vitest" +import SiyuanKernelApi from "./siyuanKernelApi" +import SiyuanConfig from "../config/siyuanConfig" +import path from "path" + +describe("SiyuanKernelApi", async () => { + // appInstance + const appInstance: any = {} + const projectBase = path.resolve(__dirname, "../../..") + const moduleBase = path.resolve(__dirname, "../../../../..") + console.log(path.join(moduleBase, "libs/zhi-common/dist/index.js")) + + // lute + // require(path.join(moduleBase, "libs/zhi-common/public/libs/lute/lute-1.7.5-20230410.min.cjs")) + + it("sql using siyuanConfig", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const result = await kernelApi.sql("select 1 from blocks limit 1") + console.log("result=>", result) + }) + + it("getRootBlocksCount", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const result = await kernelApi.getRootBlocksCount("") + console.log("result=>", result) + }) + + it("lsNotebooks", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const result = await kernelApi.lsNotebooks() + console.log("result=>", result) + }) + + it("openNotebook", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const result = await kernelApi.openNotebook("20220718062546-2nbmy21") + console.log("result=>", result) + }) + + it("closeNotebook", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const result = await kernelApi.closeNotebook("20220718062546-2nbmy21") + console.log("result=>", result) + }) + + it("renameNotebook", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const result = await kernelApi.renameNotebook("20220621105123-dlyn6nl", "临时文档") + console.log("result=>", result) + }) + + it("createNotebook", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const result = await kernelApi.createNotebook("临时文档3") + console.log("result=>", result) + }) + + it("removeNotebook", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const result = await kernelApi.removeNotebook("20230401225851-4zgh677") + console.log("result=>", result) + }) + + it("getNotebookConf", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const result = await kernelApi.getNotebookConf("20220621105123-dlyn6nl") + console.log("result=>", result) + }) + + it("setNotebookConf", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const result = await kernelApi.setNotebookConf({ + notebook: "20220621105123-dlyn6nl", + conf: { + name: "测试笔记本", + closed: false, + refCreateSavePath: "", + createDocNameTemplate: "", + dailyNoteSavePath: '/daily note/{{now | date "2006/01"}}/{{now | date "2006-01-02"}}', + dailyNoteTemplatePath: "", + }, + }) + console.log("result=>", result) + }) + + it("pushMsg", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const result = await kernelApi.pushMsg({ + msg: "测试消息", + }) + console.log("result=>", result) + }) + + it("pushErrMsg", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const result = await kernelApi.pushErrMsg({ + msg: "测试错误消息", + }) + console.log("result=>", result) + }) + + it("getRootBlocks", async () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const result = await kernelApi.getRootBlocks(0, 10, "") + console.log("result=>", result) + }) +}) diff --git a/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.ts b/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.ts new file mode 100644 index 00000000..7b3e2d22 --- /dev/null +++ b/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.ts @@ -0,0 +1,520 @@ +/* + * 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 SiyuanConfig from "../config/siyuanConfig" +import ISiyuanKernelApi, { type SiyuanData } from "./ISiyuanKernelApi" +import { simpleLogger } from "zhi-lib-base/src" +import fetch from "cross-fetch" +import { StrUtil } from "zhi-common" + +/** + * 思源笔记服务端API v2.8.2 + * + * 1. 均是 POST 方法 + * + * 2. 需要带参的接口,参数为 JSON 字符串,放置到 body 里,标头 Content-Type 为 application/json + * + * 3. 鉴权:在 `设置` - `关于` 里查看 API token,请求标头:Authorization: Token xxx + * + * @public + * @author terwer + * @since 1.0.0 + * @see {@link https://github.com/siyuan-note/siyuan/blob/master/API_zh_CN.md#%E9%89%B4%E6%9D%83 siyuan-api} + */ +class SiyuanKernelApi implements ISiyuanKernelApi { + /** + * 思源笔记服务端API版本号 + */ + public readonly VERSION + + private logger + public readonly siyuanConfig + + /** + * 初始化思源服务端 API + * + * @param appInstance - 应用实例 + * @param cfg -配置项 + */ + constructor(appInstance: any, cfg: SiyuanConfig) { + this.VERSION = "1.0.0" + + this.siyuanConfig = cfg + this.logger = simpleLogger("zhi-siyuan-api", "siyuan-kernel-api", false) + } + + /** + * 分页获取根文档 + * + * @param keyword - 关键字 + */ + public async getRootBlocksCount(keyword: string): Promise { + const stmt = `SELECT COUNT(DISTINCT b1.root_id) as count + FROM blocks b1 + WHERE 1 = 1 + AND (b1.root_id ='${keyword}' OR (b1.content LIKE '%${keyword}%') OR (b1.tag LIKE '%${keyword}%') + )` + const data = (await this.sql(stmt)) as any[] + this.logger.debug("getRootBlocksCount data=>", data[0].count) + return data[0].count + } + + /** + * 分页获取根文档 + + * ```sql + * select DISTINCT b2.root_id,b2.parent_id,b2.content from blocks b2 + * WHERE 1==1 + * AND b2.id IN ( + * SELECT DISTINCT b1.root_id + * FROM blocks b1 + * WHERE 1 = 1 + * AND ((b1.content LIKE '%github%') OR (b1.tag LIKE '%github%')) + * ORDER BY b1.updated DESC,b1.created DESC LIMIT 0,10 + * ) + * ORDER BY b2.updated DESC,b2.created DESC + * ``` + * + * @param page 页码 + * @param pagesize 数目 + * @param keyword 可选,搜索关键字 + */ + public async getRootBlocks(page: number, pagesize: number, keyword: string): Promise { + const stmt = `select DISTINCT b2.root_id,b2.parent_id,b2.content from blocks b2 + WHERE 1==1 + AND b2.id IN ( + SELECT DISTINCT b1.root_id + FROM blocks b1 + WHERE 1 = 1 + AND (b1.root_id ='${keyword}' OR (b1.content LIKE '%${keyword}%') OR (b1.tag LIKE '%${keyword}%')) + ORDER BY b1.updated DESC,b1.created DESC LIMIT ${page * pagesize},${pagesize} + ) + ORDER BY b2.updated DESC,b2.created DESC` + return await this.sql(stmt) + } + + /** + * 获取该文档下面的子文档个数 + * + * ```sql + * SELECT COUNT(DISTINCT b1.root_id) AS count + * FROM blocks b1 + * WHERE b1.path LIKE '%/20220927094918-1d85uyp%'; + * ``` + * + * @param docId 文档ID + */ + public async getSubdocCount(docId: string): Promise { + const stmt = `SELECT COUNT(DISTINCT b1.root_id) AS count + FROM blocks b1 + WHERE b1.root_id='${docId}' OR b1.path LIKE '%/${docId}%'` + const data = (await this.sql(stmt)) as any[] + return data[0].count + } + + /** + * 分页获取子文档 + * + * ```sql + * SELECT DISTINCT b2.root_id,b2.content,b2.path FROM blocks b2 + * WHERE b2.id IN ( + * SELECT DISTINCT b1.root_id + * FROM blocks b1 + * WHERE b1.path like '%/20220927094918-1d85uyp%' + * AND ((b1.content LIKE '%文档%') OR (b1.tag LIKE '%文档%')) + * ORDER BY b1.updated DESC,b1.created DESC LIMIT 0,10 + * ) + * ORDER BY b2.updated DESC,b2.created DESC + * ``` + * + * @param docId 文档ID + * @param page 页码 + * @param pagesize 数目 + * @param keyword 关键字 + */ + public async getSubdocs(docId: string, page: number, pagesize: number, keyword: string): Promise { + const stmt = `SELECT DISTINCT b2.root_id,b2.content,b2.path FROM blocks b2 + WHERE b2.id IN ( + SELECT DISTINCT b1.root_id + FROM blocks b1 + WHERE b1.root_id='${docId}' OR b1.path like '%/${docId}%' + AND ((b1.content LIKE '%${keyword}%') OR (b1.tag LIKE '%${keyword}%')) + ORDER BY b1.updated DESC,b1.created DESC LIMIT ${page * pagesize},${pagesize} + ) + ORDER BY b2.content,b2.updated DESC,b2.created DESC,id` + + this.logger.debug("siyuanApi getSubdocs sql=>", stmt) + return await this.sql(stmt) + } + + /** + * 以id获取思源块信息 + * @param blockId 块ID + */ + public async getBlockByID(blockId: string): Promise { + const stmt = `select * + from blocks + where id = '${blockId}'` + const data = (await this.sql(stmt)) as any[] + if (!data || data.length === 0) { + throw new Error("通过ID查询块信息失败") + } + return data[0] + } + + /** + * 以slug获取思源块信息 + * @param slug 内容快别名 + */ + public async getRootBlockBySlug(slug: string): Promise { + const stmt = `select root_id from attributes where name='custom-slug' and value='${slug}' limit 1` + const data = (await this.sql(stmt)) as any[] + return data[0] + } + + /** + * 以内容块ID获取根块 + * + * @param blockID 内容块ID + */ + public async getRootBlock(blockID: string): Promise { + const stmt = `select root_id from blocks where id='${blockID}' limit 1` + const data = (await this.sql(stmt)) as any[] + return data[0] + } + + /** + * 导出markdown文本 + * @param docId 文档id + */ + public async exportMdContent(docId: string): Promise { + const data = { + id: docId, + } + const url = "/api/export/exportMdContent" + return await this.siyuanRequest(url, data) + } + + /** + * 以sql发送请求 + * + * @param sql - sql + */ + public async sql(sql: string): Promise { + const sqldata: { stmt: string } = { + stmt: sql, + } + const url = "/api/query/sql" + this.logger.debug("sql=>", sql) + return await this.siyuanRequest(url, sqldata) + } + + /** + * 向思源请求数据 + * + * @param url - url + * @param data - 数据 + */ + public async siyuanRequest(url: string, data: object): Promise { + const reqUrl = `${this.siyuanConfig.apiUrl}${url}` + + const fetchOps = { + body: JSON.stringify(data), + method: "POST", + } + if (!StrUtil.isEmptyString(this.siyuanConfig.password)) { + Object.assign(fetchOps, { + headers: { + Authorization: `Token ${this.siyuanConfig.password}`, + }, + }) + } + + this.logger.debug("开始向思源请求数据,reqUrl=>", reqUrl) + this.logger.debug("开始向思源请求数据,fetchOps=>", fetchOps) + + const response = await fetch(reqUrl, fetchOps) + const resJson = await response.json() + this.logger.debug("思源请求数据返回,resJson=>", resJson) + + if (resJson.code === -1) { + throw new Error(resJson.msg) + } + return resJson.code === 0 ? resJson.data : null + } + + public async siyuanRequestForm(url: string, formData: any): Promise { + const reqUrl = `${this.siyuanConfig.apiUrl}${url}` + const fetchOps = { + method: "POST", + body: formData, + } + if (!StrUtil.isEmptyString(this.siyuanConfig.password)) { + Object.assign(fetchOps, { + headers: { + Authorization: `Token ${this.siyuanConfig.password}`, + }, + }) + } + + const response = await fetch(reqUrl, fetchOps) + + if (!response.ok) { + throw new Error(`资源文件上传失败 => ${response.status} ${response.statusText}`) + } else { + const resJson = await response.json() + if (resJson.code === -1) { + throw new Error(resJson.msg) + } + return resJson.code === 0 ? resJson.data : null + } + } + + /** + * 列出笔记本 + */ + public async lsNotebooks(): Promise { + return await this.siyuanRequest("/api/notebook/lsNotebooks", {}) + } + + /** + * 打开笔记本 + * + * @param notebookId - 笔记本ID + */ + public async openNotebook(notebookId: string): Promise { + return await this.siyuanRequest("/api/notebook/openNotebook", { + notebook: notebookId, + }) + } + + /** + * 关闭笔记本 + * + * @param notebookId - 笔记本ID + */ + public async closeNotebook(notebookId: string): Promise { + return await this.siyuanRequest("/api/notebook/closeNotebook", { + notebook: notebookId, + }) + } + + /** + * 重命名笔记本 + * + * @param notebookId - 笔记本ID + * @param name - 新笔记本名称 + */ + public async renameNotebook(notebookId: string, name: string): Promise { + return await this.siyuanRequest("/api/notebook/renameNotebook", { + notebook: notebookId, + name: name, + }) + } + + /** + * 创建笔记本 + * + * @param name - 新笔记本名称 + */ + public async createNotebook(name: string): Promise { + return await this.siyuanRequest("/api/notebook/createNotebook", { + name: name, + }) + } + + /** + * 删除笔记本 + * + * @param notebookId - 笔记本ID + */ + public async removeNotebook(notebookId: string): Promise { + return await this.siyuanRequest("/api/notebook/removeNotebook", { + notebook: notebookId, + }) + } + + /** + * 获取笔记本配置 + * + * @param notebookId - 笔记本ID + */ + public async getNotebookConf(notebookId: string): Promise { + return await this.siyuanRequest("/api/notebook/getNotebookConf", { + notebook: notebookId, + }) + } + + /** + * 保存笔记本配置 + * + * ```json + * { + * "notebook": "20210817205410-2kvfpfn", + * "conf": { + * "name": "测试笔记本", + * "closed": false, + * "refCreateSavePath": "", + * "createDocNameTemplate": "", + * "dailyNoteSavePath": "/daily note/{{now | date \"2006/01\"}}/{{now | date \"2006-01-02\"}}", + * "dailyNoteTemplatePath": "" + * } + * } + * ``` + * @param notebookConf - 笔记本配置 + */ + public async setNotebookConf(notebookConf: object): Promise { + return await this.siyuanRequest("/api/notebook/setNotebookConf", notebookConf) + } + + /** + * 推送消息 + * + * 参数 + * + * ```json + * { + * "msg": "test", + * "timeout": 7000 + * } + * ``` + * + * timeout:消息持续显示时间,单位为毫秒。可以不传入该字段,默认为 7000 毫秒 + * + * 返回值 + * + * ``` + * { + * "code": 0, + * "msg": "", + * "data": { + * "id": "62jtmqi" + * } + * } + * + * id:消息 ID + * ``` + * + * @param msgObj 消息体 + */ + public async pushMsg(msgObj: object): Promise { + return await this.siyuanRequest("/api/notification/pushMsg", msgObj) + } + + /** + * 推送报错消息 + * + * 参数 + * + * ``` + * { + * "msg": "test", + * "timeout": 7000 + * } + * ``` + * + * timeout:消息持续显示时间,单位为毫秒。可以不传入该字段,默认为 7000 毫秒 + * + * 返回值 + * + * ``` + * { + * "code": 0, + * "msg": "", + * "data": { + * "id": "qc9znut" + * } + * } + * + * id:消息 ID + * ``` + * + * @param msgObj + */ + public async pushErrMsg(msgObj: object): Promise { + return await this.siyuanRequest("/api/notification/pushErrMsg", msgObj) + } + + /** + * 获取块属性 + * @param blockId + */ + public async getBlockAttrs(blockId: string): Promise { + const data = { + id: blockId, + } + const url = "/api/attr/getBlockAttrs" + return await this.siyuanRequest(url, data) + } + + /** + * 设置块属性 + * @param blockId + * @param attrs + */ + public async setBlockAttrs(blockId: string, attrs: any): Promise { + const url = "/api/attr/setBlockAttrs" + return await this.siyuanRequest(url, { + id: blockId, + attrs, + }) + } + + /** + * 通过 Markdown 创建文档 + * + * @param notebook - 笔记本 + * @param path - 路径 + * @param md - md + */ + public async createDocWithMd(notebook: string, path: string, md: string): Promise { + const params = { + notebook: notebook, + path: path, + markdown: md, + } + return await this.siyuanRequest("/api/filetree/createDocWithMd", params) + } + + /** + * 删除文档 + * + * @param notebook - 笔记本 + * @param path - 路径 + */ + public async removeDoc(notebook: string, path: string): Promise { + const params = { + notebook: notebook, + path: path, + } + return await this.siyuanRequest("/api/filetree/removeDoc", params) + } + + public async uploadAsset(formData: any): Promise { + return await this.siyuanRequestForm("/api/asset/upload", formData) + } +} + +export default SiyuanKernelApi diff --git a/libs/zhi-siyuan-api/src/lib/siyuanApi.ts b/libs/zhi-siyuan-api/src/lib/siyuanApi.ts new file mode 100644 index 00000000..6a937c6e --- /dev/null +++ b/libs/zhi-siyuan-api/src/lib/siyuanApi.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 SiyuanConfig from "./config/siyuanConfig" +import SiyuanKernelApi from "./kernel/siyuanKernelApi" +import SiyuanClientApi from "./client/siyuanClientApi" + +/** + * 思源笔记API + * + * @author terwer + * @since 1.0.0 + */ +class SiyuanApi { + /** + * 思源笔记内核API + */ + public readonly kernelApi + + /** + * 思源笔记客户端API + */ + public readonly clientApi + + /** + * 构造思源 API对象 + * + * @param appInstance - 应用实例 + * @param cfg - 配置项 + */ + constructor(appInstance: any, cfg: SiyuanConfig) { + this.kernelApi = new SiyuanKernelApi(appInstance, cfg) + this.clientApi = new SiyuanClientApi() + } +} + +export default SiyuanApi diff --git a/libs/zhi-siyuan-api/src/lib/siyuanConstants.ts b/libs/zhi-siyuan-api/src/lib/siyuanConstants.ts new file mode 100644 index 00000000..648d199a --- /dev/null +++ b/libs/zhi-siyuan-api/src/lib/siyuanConstants.ts @@ -0,0 +1,45 @@ +/* + * 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 1.0.0 + * @since 1.0.0 + */ +class SiyuanConstants { + /** + * 思源 API 伺服地址 + */ + public static VITE_SIYUAN_API_URL_KEY = "VITE_SIYUAN_API_URL" + + /** + * 思源 token + */ + public static VITE_SIYUAN_AUTH_TOKEN_KEY = "VITE_SIYUAN_AUTH_TOKEN" +} + +export default SiyuanConstants diff --git a/libs/zhi-siyuan-api/src/lib/zhi-siyuan-api.spec.ts b/libs/zhi-siyuan-api/src/lib/zhi-siyuan-api.spec.ts new file mode 100644 index 00000000..0f990972 --- /dev/null +++ b/libs/zhi-siyuan-api/src/lib/zhi-siyuan-api.spec.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 SiyuanApi from "./zhi-siyuan-api" +import { describe, expect, it } from "vitest" +import path from "path" +import SiyuanConfig from "./config/siyuanConfig" + +describe("zhiSiyuanApi", async () => { + // appInstance + const appInstance: any = {} + const projectBase = path.resolve(__dirname, "../../..") + const moduleBase = path.resolve(__dirname, "../../../../..") + // lute + require(path.join(moduleBase, "libs/zhi-common/public/libs/lute/lute-1.7.5-20230410.min.cjs")) + + it("siyuanApi", () => { + const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + const siyuanApi = new SiyuanApi(appInstance, siyuanConfig) + expect(siyuanApi).toBeTruthy() + }) +}) diff --git a/libs/zhi-siyuan-api/src/lib/zhi-siyuan-api.ts b/libs/zhi-siyuan-api/src/lib/zhi-siyuan-api.ts new file mode 100644 index 00000000..8de277f1 --- /dev/null +++ b/libs/zhi-siyuan-api/src/lib/zhi-siyuan-api.ts @@ -0,0 +1,27 @@ +/* + * 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 SiyuanApi from "./siyuanApi" +export default SiyuanApi diff --git a/libs/zhi-siyuan-api/tsconfig.json b/libs/zhi-siyuan-api/tsconfig.json new file mode 100644 index 00000000..76101700 --- /dev/null +++ b/libs/zhi-siyuan-api/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "Node", + // "allowImportingTsExtensions": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "preserve", + + /* Linting */ + "strict": false, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noFallthroughCasesInSwitch": false, + + "types": ["node", "vite/client"] + }, + "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/libs/zhi-siyuan-api/tsconfig.node.json b/libs/zhi-siyuan-api/tsconfig.node.json new file mode 100644 index 00000000..7065ca9a --- /dev/null +++ b/libs/zhi-siyuan-api/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/libs/zhi-siyuan-api/vite.config.ts b/libs/zhi-siyuan-api/vite.config.ts new file mode 100644 index 00000000..c1ef22a6 --- /dev/null +++ b/libs/zhi-siyuan-api/vite.config.ts @@ -0,0 +1,78 @@ +/// + +import { defineConfig } from "vite" +import { join } from "path" +import { viteStaticCopy } from "vite-plugin-static-copy" +import dts from "vite-plugin-dts" +import minimist from "minimist" +import livereload from "rollup-plugin-livereload" +import { nodePolyfills } from "vite-plugin-node-polyfills" + +const args = minimist(process.argv.slice(2)) +const isWatch = args.watch || args.w || false +const devDistDir = "/Users/terwer/Documents/mydocs/siyuan-plugins/siyuan-plugin-publisher/public/libs/zhi-siyuan-api" +const distDir = isWatch ? devDistDir : "./dist" +// const distDir = devDistDir + +console.log("isWatch=>", isWatch) +console.log("distDir=>", distDir) + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [ + dts({ + entryRoot: "src", + tsConfigFilePath: join(__dirname, "tsconfig.json"), + skipDiagnostics: true, + }), + + viteStaticCopy({ + targets: [ + { + src: "README.md", + dest: "./", + }, + { + src: "package.json", + dest: "./", + }, + ], + }), + + nodePolyfills({ + exclude: ["fs"], + protocolImports: true, + }), + ], + + // Configuration for building your library. + // See: https://vitejs.dev/guide/build.html#library-mode + build: { + // 输出路径 + outDir: distDir, + emptyOutDir: false, + + // 构建后是否生成 source map 文件 + sourcemap: false, + + lib: { + // Could also be a dictionary or array of multiple entry points. + entry: "src/index.ts", + fileName: "index", + // Change this to the formats you want to support. + // Don't forgot to update your package.json as well. + formats: ["es"], + }, + rollupOptions: { + plugins: [...(isWatch ? [livereload(devDistDir)] : [])], + // External packages that should not be bundled into your library. + external: [], + }, + }, + + test: { + globals: true, + environment: "jsdom", + include: ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 35304c51..23b31744 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -182,6 +182,34 @@ importers: specifier: workspace:* version: link:../../tools/vite-config-custom + libs/zhi-siyuan-api: + dependencies: + cross-fetch: + specifier: ^3.1.6 + version: 3.1.6 + zhi-blog-api: + specifier: workspace:* + version: link:../zhi-blog-api + zhi-common: + specifier: workspace:* + version: link:../zhi-common + zhi-lib-base: + specifier: workspace:* + version: link:../zhi-lib-base + devDependencies: + '@terwer/eslint-config-custom': + specifier: workspace:* + version: link:../../tools/eslint-config-custom + '@terwer/tsconfig': + specifier: workspace:* + version: link:../../tools/tsconfig + '@terwer/vite-config-custom': + specifier: workspace:* + version: link:../../tools/vite-config-custom + form-data: + specifier: ^4.0.0 + version: 4.0.0 + libs/zhi-xmlrpc-middleware: dependencies: zhi-common: @@ -2278,7 +2306,6 @@ packages: /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: false /at-least-node@1.0.0: resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} @@ -2649,7 +2676,6 @@ packages: engines: {node: '>= 0.8'} dependencies: delayed-stream: 1.0.0 - dev: false /commander@10.0.0: resolution: {integrity: sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==} @@ -2813,7 +2839,6 @@ packages: node-fetch: 2.6.11 transitivePeerDependencies: - encoding - dev: true /cross-spawn@5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} @@ -3001,7 +3026,6 @@ packages: /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - dev: false /des.js@1.1.0: resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} @@ -3940,7 +3964,6 @@ packages: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - dev: false /fs-extra@10.1.0: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} @@ -5206,14 +5229,12 @@ packages: /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} - dev: false /mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} dependencies: mime-db: 1.52.0 - dev: false /mimic-fn@1.2.0: resolution: {integrity: sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==} @@ -5363,7 +5384,6 @@ packages: optional: true dependencies: whatwg-url: 5.0.0 - dev: true /node-stdlib-browser@1.2.0: resolution: {integrity: sha512-VSjFxUhRhkyed8AtLwSCkMrJRfQ3e2lGtG3sP6FEgaLKBBbxM/dLfjRe1+iLhjvyLFW3tBQ8+c0pcOtXGbAZJg==} @@ -6782,7 +6802,6 @@ packages: /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: true /tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} @@ -7486,7 +7505,6 @@ packages: /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: true /webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} @@ -7527,7 +7545,6 @@ packages: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - dev: true /whatwg-url@7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} From b90fdf11d2782bfc4264febea9f39bfaa4abcb03 Mon Sep 17 00:00:00 2001 From: terwer Date: Mon, 12 Jun 2023 00:17:04 +0800 Subject: [PATCH 4/4] docs: update deps --- README.md | 5 +-- README_zh_CN.md | 3 +- libs/zhi-siyuan-api/CHANGELOG.md | 12 +++++++ libs/zhi-siyuan-api/package.json | 2 +- .../src/lib/kernel/siyuanKernelApi.spec.ts | 33 ++++++++----------- .../src/lib/kernel/siyuanKernelApi.ts | 19 +++++++++-- 6 files changed, 48 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index a8dc29e7..0f77992a 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ - [ ] [zhi-yuque-api](https://github.com/terwer/zhi/tree/main/libs/zhi-yuque-api) - [ ] [zhi-liandi-api](https://github.com/terwer/zhi/tree/main/libs/zhi-liandi-api) - [ ] [zhi-kms-api](https://github.com/terwer/zhi/tree/main/libs/zhi-kms-api) - - [ ] [zhi-siyuan-api](https://github.com/terwer/zhi/tree/main/libs/zhi-siyuan-api) + - [X] [zhi-siyuan-api](https://github.com/terwer/zhi/tree/main/libs/zhi-siyuan-api) - [ ] [zhi-wechat-api](https://github.com/terwer/zhi/tree/main/libs/zhi-wechat-api) - [ ] [zhi-http-api](https://github.com/terwer/zhi/tree/main/libs/zhi-http-api) - [ ] [zhi-zhihu-api](https://github.com/terwer/zhi/tree/main/libs/zhi-zhihu-api) @@ -66,4 +66,5 @@ - [siyuan-plugin-custom-slug](https://github.com/terwer/siyuan-plugin-custom-slug) - Document alias, a new alias index for document titles, convenient for quick search Available - [siyuan-plugin-code-block](https://github.com/terwer/siyuan-plugin-code-block) - Code block beautification, imitating Mac style code block style Available - [siyuan-plugin-2md](https://github.com/terwer/siyuan-plugin-2md) - Markdown batch conversion, batch conversion of documents of Siyuan notes into Markdown files, support conversion properties for Hexo, HUGO, Vitepress, Obsidian and other platforms corresponding to FrontFormatter In progress -- [siyuan-plugin-random-doc](https://github.com/terwer/siyuan-plugin-random-doc) - Random Doc In progress \ No newline at end of file +- [siyuan-plugin-random-doc](https://github.com/terwer/siyuan-plugin-random-doc) - Random Doc In progress +- [siyuan-plugin-picgo](https://github.com/terwer/siyuan-plugin-picgo) - PocGo In progress diff --git a/README_zh_CN.md b/README_zh_CN.md index ccd4a0a5..46853f09 100644 --- a/README_zh_CN.md +++ b/README_zh_CN.md @@ -34,7 +34,7 @@ - [ ] [zhi-yuque-api](https://github.com/terwer/zhi/tree/main/libs/zhi-yuque-api) - [ ] [zhi-liandi-api](https://github.com/terwer/zhi/tree/main/libs/zhi-liandi-api) - [ ] [zhi-kms-api](https://github.com/terwer/zhi/tree/main/libs/zhi-kms-api) - - [ ] [zhi-siyuan-api](https://github.com/terwer/zhi/tree/main/libs/zhi-siyuan-api) + - [X] [zhi-siyuan-api](https://github.com/terwer/zhi/tree/main/libs/zhi-siyuan-api) - [ ] [zhi-wechat-api](https://github.com/terwer/zhi/tree/main/libs/zhi-wechat-api) - [ ] [zhi-http-api](https://github.com/terwer/zhi/tree/main/libs/zhi-http-api) - [ ] [zhi-zhihu-api](https://github.com/terwer/zhi/tree/main/libs/zhi-zhihu-api) @@ -67,3 +67,4 @@ - [siyuan-plugin-code-block](https://github.com/terwer/siyuan-plugin-code-block) - 代码块美化,模仿Mac风格的代码块风格 已上架 - [siyuan-plugin-2md](https://github.com/terwer/siyuan-plugin-2md) - Markdown批量转换,将思源笔记的文档批量转换为Markdown文件,支持转换属性为Hexo、HUGO、Vitepress、Obsidian等平台对应的FrontFormatter 进行中 - [siyuan-plugin-random-doc](https://github.com/terwer/siyuan-plugin-random-doc) - 文档漫游 进行中 +- [siyuan-plugin-picgo](https://github.com/terwer/siyuan-plugin-picgo) - PocGo图床 进行中 diff --git a/libs/zhi-siyuan-api/CHANGELOG.md b/libs/zhi-siyuan-api/CHANGELOG.md index 20ddc9f0..0bf60d22 100644 --- a/libs/zhi-siyuan-api/CHANGELOG.md +++ b/libs/zhi-siyuan-api/CHANGELOG.md @@ -1,5 +1,17 @@ # zhi-siyuan-api +## 1.9.1 + +### Patch Changes + +- add get image blocks + +## 1.9.0 + +### Minor Changes + +- remove unused appinstance + ## 1.8.0 ### Minor Changes diff --git a/libs/zhi-siyuan-api/package.json b/libs/zhi-siyuan-api/package.json index b52256a7..8a315ec9 100644 --- a/libs/zhi-siyuan-api/package.json +++ b/libs/zhi-siyuan-api/package.json @@ -1,6 +1,6 @@ { "name": "zhi-siyuan-api", - "version": "1.8.0", + "version": "1.9.1", "type": "module", "description": "a siyuan-note api including both kernel and client", "main": "./dist/index.js", diff --git a/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.spec.ts b/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.spec.ts index fff216e6..0297ac20 100644 --- a/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.spec.ts +++ b/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.spec.ts @@ -26,84 +26,77 @@ import { describe, it } from "vitest" import SiyuanKernelApi from "./siyuanKernelApi" import SiyuanConfig from "../config/siyuanConfig" -import path from "path" describe("SiyuanKernelApi", async () => { - // appInstance - const appInstance: any = {} - const projectBase = path.resolve(__dirname, "../../..") - const moduleBase = path.resolve(__dirname, "../../../../..") - console.log(path.join(moduleBase, "libs/zhi-common/dist/index.js")) - // lute // require(path.join(moduleBase, "libs/zhi-common/public/libs/lute/lute-1.7.5-20230410.min.cjs")) it("sql using siyuanConfig", async () => { const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") - const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const kernelApi = new SiyuanKernelApi(siyuanConfig) const result = await kernelApi.sql("select 1 from blocks limit 1") console.log("result=>", result) }) it("getRootBlocksCount", async () => { const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") - const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const kernelApi = new SiyuanKernelApi(siyuanConfig) const result = await kernelApi.getRootBlocksCount("") console.log("result=>", result) }) it("lsNotebooks", async () => { const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") - const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const kernelApi = new SiyuanKernelApi(siyuanConfig) const result = await kernelApi.lsNotebooks() console.log("result=>", result) }) it("openNotebook", async () => { const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") - const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const kernelApi = new SiyuanKernelApi(siyuanConfig) const result = await kernelApi.openNotebook("20220718062546-2nbmy21") console.log("result=>", result) }) it("closeNotebook", async () => { const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") - const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const kernelApi = new SiyuanKernelApi(siyuanConfig) const result = await kernelApi.closeNotebook("20220718062546-2nbmy21") console.log("result=>", result) }) it("renameNotebook", async () => { const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") - const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const kernelApi = new SiyuanKernelApi(siyuanConfig) const result = await kernelApi.renameNotebook("20220621105123-dlyn6nl", "临时文档") console.log("result=>", result) }) it("createNotebook", async () => { const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") - const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const kernelApi = new SiyuanKernelApi(siyuanConfig) const result = await kernelApi.createNotebook("临时文档3") console.log("result=>", result) }) it("removeNotebook", async () => { const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") - const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const kernelApi = new SiyuanKernelApi(siyuanConfig) const result = await kernelApi.removeNotebook("20230401225851-4zgh677") console.log("result=>", result) }) it("getNotebookConf", async () => { const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") - const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const kernelApi = new SiyuanKernelApi(siyuanConfig) const result = await kernelApi.getNotebookConf("20220621105123-dlyn6nl") console.log("result=>", result) }) it("setNotebookConf", async () => { const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") - const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const kernelApi = new SiyuanKernelApi(siyuanConfig) const result = await kernelApi.setNotebookConf({ notebook: "20220621105123-dlyn6nl", conf: { @@ -120,7 +113,7 @@ describe("SiyuanKernelApi", async () => { it("pushMsg", async () => { const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") - const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const kernelApi = new SiyuanKernelApi(siyuanConfig) const result = await kernelApi.pushMsg({ msg: "测试消息", }) @@ -129,7 +122,7 @@ describe("SiyuanKernelApi", async () => { it("pushErrMsg", async () => { const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") - const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const kernelApi = new SiyuanKernelApi(siyuanConfig) const result = await kernelApi.pushErrMsg({ msg: "测试错误消息", }) @@ -138,7 +131,7 @@ describe("SiyuanKernelApi", async () => { it("getRootBlocks", async () => { const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") - const kernelApi = new SiyuanKernelApi(appInstance, siyuanConfig) + const kernelApi = new SiyuanKernelApi(siyuanConfig) const result = await kernelApi.getRootBlocks(0, 10, "") console.log("result=>", result) }) diff --git a/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.ts b/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.ts index 7b3e2d22..49a5544e 100644 --- a/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.ts +++ b/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.ts @@ -55,10 +55,9 @@ class SiyuanKernelApi implements ISiyuanKernelApi { /** * 初始化思源服务端 API * - * @param appInstance - 应用实例 * @param cfg -配置项 */ - constructor(appInstance: any, cfg: SiyuanConfig) { + constructor(cfg: SiyuanConfig) { this.VERSION = "1.0.0" this.siyuanConfig = cfg @@ -515,6 +514,22 @@ class SiyuanKernelApi implements ISiyuanKernelApi { public async uploadAsset(formData: any): Promise { return await this.siyuanRequestForm("/api/asset/upload", formData) } + + /** + * 以id获取所有图片块 + * + * @param blockId - 块ID + */ + public async getImageBlocksByID(blockId: string): Promise { + const stmt = `select * + from blocks + where root_id = '${blockId}' and markdown like '%![%'` + const data = await this.sql(stmt) + if (!data) { + throw new Error("通过ID查询图片块信息失败") + } + return data as any[] + } } export default SiyuanKernelApi