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/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/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 1279a3fd..c2346232 100644
--- a/libs/zhi-device/src/lib/browserUtil.ts
+++ b/libs/zhi-device/src/lib/browserUtil.ts
@@ -79,7 +79,8 @@ class BrowserUtil {
* @since 0.0.1
*/
public static getQueryParam = (key: string) => {
- if (BrowserUtil.isInBrowser) {
+ // 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-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..0bf60d22
--- /dev/null
+++ b/libs/zhi-siyuan-api/CHANGELOG.md
@@ -0,0 +1,19 @@
+# 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
+
+- 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..8a315ec9
--- /dev/null
+++ b/libs/zhi-siyuan-api/package.json
@@ -0,0 +1,45 @@
+{
+ "name": "zhi-siyuan-api",
+ "version": "1.9.1",
+ "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..0297ac20
--- /dev/null
+++ b/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.spec.ts
@@ -0,0 +1,138 @@
+/*
+ * 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"
+
+describe("SiyuanKernelApi", async () => {
+ // 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(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(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(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(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(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(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(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(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(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(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(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(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(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..49a5544e
--- /dev/null
+++ b/libs/zhi-siyuan-api/src/lib/kernel/siyuanKernelApi.ts
@@ -0,0 +1,535 @@
+/*
+ * 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 cfg -配置项
+ */
+ constructor(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)
+ }
+
+ /**
+ * 以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
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/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",
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==}