diff --git a/packages/zhi-core/src/lib/util/ZhiCoreUtil.ts b/packages/zhi-core/src/lib/util/ZhiCoreUtil.ts index a1fdb7fd..56fa57d4 100644 --- a/packages/zhi-core/src/lib/util/ZhiCoreUtil.ts +++ b/packages/zhi-core/src/lib/util/ZhiCoreUtil.ts @@ -25,6 +25,7 @@ import { Env } from "@siyuan-community/zhi-env" import { ZhiUtil } from "@siyuan-community/zhi-common" +import { SiyuanKernelApi } from "@siyuan-community/zhi-siyuan-api" /** * 工具类统一入口,每个应用自己实现 @@ -34,12 +35,21 @@ import { ZhiUtil } from "@siyuan-community/zhi-common" * @since 1.0.0 */ class ZhiCoreUtil extends ZhiUtil { + private static kApi: SiyuanKernelApi | undefined + public static override zhiEnv(): Env { if (!this.env) { this.env = new Env(import.meta.env) } return this.env } + + public static kernelApi() { + if (!this.kApi) { + this.kApi = new SiyuanKernelApi(this.zhiEnv()) + } + return this.kApi + } } export default ZhiCoreUtil diff --git a/packages/zhi-core/src/lib/zhi.ts b/packages/zhi-core/src/lib/zhi.ts index 1fe812f1..98559a3d 100644 --- a/packages/zhi-core/src/lib/zhi.ts +++ b/packages/zhi-core/src/lib/zhi.ts @@ -31,6 +31,7 @@ import Bootstrap from "./core/Bootstrap" class Zhi { private readonly logger private readonly common + private readonly kernelApi private readonly runAs @@ -54,6 +55,7 @@ class Zhi { constructor(runAs: DeviceTypeEnum) { this.logger = ZhiCoreUtil.zhiLog("zhi-core") this.common = ZhiCoreUtil.zhiCommon() + this.kernelApi = ZhiCoreUtil.kernelApi() this.runAs = runAs ?? DeviceTypeEnum.DeviceType_Node } @@ -90,9 +92,9 @@ class Zhi { this.SUPPORTED_THEME_VERSION ) this.logger.error(errMsg) - // this.kernelApi.pushErrMsg({ - // msg: errMsg, - // }) + this.kernelApi.pushErrMsg({ + msg: errMsg, + }) return } @@ -103,9 +105,9 @@ class Zhi { this.SUPPORTED_KERNEL_VERSION ) this.logger.warn(warnMsg) - // this.kernelApi.pushMsg({ - // msg: warnMsg, - // }) + this.kernelApi.pushMsg({ + msg: warnMsg, + }) return } diff --git a/packages/zhi-lib-blog-api/package.json b/packages/zhi-lib-blog-api/package.json index 42903c1a..76fee649 100644 --- a/packages/zhi-lib-blog-api/package.json +++ b/packages/zhi-lib-blog-api/package.json @@ -1,10 +1,10 @@ { "name": "@siyuan-community/zhi-blog-api", - "version": "0.1.0", + "version": "1.1.1", "type": "module", "description": "a common blog interface", - "main": "./dist/index.js", - "typings": "./dist/index.d.ts", + "main": "./packages/zhi-lib-blog-api/src/index.js", + "typings": "./index.d.ts", "repository": "terwer/zhi", "homepage": "https://github.com/terwer/zhi/tree/main/packages/zhi-lib-blog-api", "author": "terwer", diff --git a/packages/zhi-lib-blog-api/src/lib/blogApi.ts b/packages/zhi-lib-blog-api/src/lib/blogApi.ts index cda7ba1f..18588561 100644 --- a/packages/zhi-lib-blog-api/src/lib/blogApi.ts +++ b/packages/zhi-lib-blog-api/src/lib/blogApi.ts @@ -53,7 +53,7 @@ class BlogApi implements IBlogApi { */ constructor(apiAdaptor: IBlogApi) { this.logger = ZhiBlogApiUtil.zhiLog("zhi-blog-api") - this.VERSION = "1.0.0" + this.VERSION = "1.1.1" this.apiAdaptor = apiAdaptor } diff --git a/packages/zhi-lib-blog-api/src/lib/zhi-lib-blog-api.ts b/packages/zhi-lib-blog-api/src/lib/zhi-lib-blog-api.ts index df8c1c21..79d8702f 100644 --- a/packages/zhi-lib-blog-api/src/lib/zhi-lib-blog-api.ts +++ b/packages/zhi-lib-blog-api/src/lib/zhi-lib-blog-api.ts @@ -24,4 +24,15 @@ */ import BlogApi from "./blogApi" +import CategoryInfo from "./models/categoryInfo" +import type { IBlogApi } from "./IBlogApi" +import MediaObject from "./models/mediaObject" +import Post from "./models/post" +import PostStatusEnum from "./enums/postStatusEnum" +import UserBlog from "./models/userBlog" +import BlogConfig, { PasswordType } from "./blogConfig" +import BlogPlaceholder from "./blogPlaceholder" + export { BlogApi } +export { CategoryInfo, IBlogApi, MediaObject, Post, PostStatusEnum, UserBlog } +export { BlogConfig, PasswordType, BlogPlaceholder } diff --git a/packages/zhi-lib-siyuan-api/.eslintrc.json b/packages/zhi-lib-siyuan-api/.eslintrc.json new file mode 100644 index 00000000..9d9c0db5 --- /dev/null +++ b/packages/zhi-lib-siyuan-api/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/packages/zhi-lib-siyuan-api/README.md b/packages/zhi-lib-siyuan-api/README.md new file mode 100644 index 00000000..1640951b --- /dev/null +++ b/packages/zhi-lib-siyuan-api/README.md @@ -0,0 +1,14 @@ +# zhi-lib-siyuan-api +a siyuan-note api including both kernel and client + +## Dev + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build zhi-lib-siyuan-api` to build the library. + +## Running unit tests + +Run `nx test zhi-lib-siyuan-api` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/packages/zhi-lib-siyuan-api/package.json b/packages/zhi-lib-siyuan-api/package.json new file mode 100644 index 00000000..e6385d6b --- /dev/null +++ b/packages/zhi-lib-siyuan-api/package.json @@ -0,0 +1,18 @@ +{ + "name": "@siyuan-community/zhi-siyuan-api", + "version": "0.1.0", + "type": "module", + "description": "a siyuan-note api including both kernel and client", + "main": "./dist/index.js", + "typings": "./index.d.ts", + "repository": "terwer/zhi", + "homepage": "https://github.com/terwer/zhi/tree/main/packages/zhi-lib-siyuan-api", + "author": "terwer", + "license": "GPL", + "keywords": [ + "zhi", + "siyuan", + "blog", + "api" + ] +} diff --git a/packages/zhi-lib-siyuan-api/project.json b/packages/zhi-lib-siyuan-api/project.json new file mode 100644 index 00000000..c45368e2 --- /dev/null +++ b/packages/zhi-lib-siyuan-api/project.json @@ -0,0 +1,35 @@ +{ + "name": "zhi-lib-siyuan-api", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/zhi-lib-siyuan-api/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nx/vite:build", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/packages/zhi-lib-siyuan-api" + } + }, + "publish": { + "command": "node tools/scripts/publish.mjs zhi-lib-siyuan-api {args.ver} {args.tag}", + "dependsOn": ["build"] + }, + "test": { + "executor": "@nx/vite:test", + "outputs": ["coverage/packages/zhi-lib-siyuan-api"], + "options": { + "passWithNoTests": true, + "reportsDirectory": "../../coverage/packages/zhi-lib-siyuan-api" + } + }, + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["packages/zhi-lib-siyuan-api/**/*.ts"] + } + } + }, + "tags": [] +} diff --git a/packages/zhi-lib-siyuan-api/src/env.d.ts b/packages/zhi-lib-siyuan-api/src/env.d.ts new file mode 100644 index 00000000..244ec48c --- /dev/null +++ b/packages/zhi-lib-siyuan-api/src/env.d.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. + */ + +interface ImportMeta { + readonly env: ImportMetaEnv +} diff --git a/packages/zhi-lib-siyuan-api/src/index.ts b/packages/zhi-lib-siyuan-api/src/index.ts new file mode 100644 index 00000000..c0a255c5 --- /dev/null +++ b/packages/zhi-lib-siyuan-api/src/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023, Terwer . All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Terwer designates this + * particular file as subject to the "Classpath" exception as provided + * by Terwer in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com + * or visit www.terwer.space if you need additional information or have any + * questions. + */ + +export * from "./lib/zhi-lib-siyuan-api" diff --git a/packages/zhi-lib-siyuan-api/src/lib/ISiyuanKernelApi.ts b/packages/zhi-lib-siyuan-api/src/lib/ISiyuanKernelApi.ts new file mode 100644 index 00000000..95204e9a --- /dev/null +++ b/packages/zhi-lib-siyuan-api/src/lib/ISiyuanKernelApi.ts @@ -0,0 +1,77 @@ +/* + * 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} + * @see {@link https://github.com/leolee9086/noob-core/blob/master/frontEnd/noobApi/util/kernelApi.js kernelApi} + */ +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 +} + +export default ISiyuanKernelApi +export { SiyuanData } diff --git a/packages/zhi-lib-siyuan-api/src/lib/siYuanApiAdaptor.spec.ts b/packages/zhi-lib-siyuan-api/src/lib/siYuanApiAdaptor.spec.ts new file mode 100644 index 00000000..42af9aac --- /dev/null +++ b/packages/zhi-lib-siyuan-api/src/lib/siYuanApiAdaptor.spec.ts @@ -0,0 +1,36 @@ +/* + * 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, expect } from "vitest" +import SiYuanApiAdaptor from "./siYuanApiAdaptor" +import SiyuanConfig from "./siyuanConfig" + +describe("SiYuanApiAdaptor", () => { + it("should work", function () { + // const siyuanConfig = new SiyuanConfig("http://127.0.0.1:6806", "") + // const apiAdaptor = new SiYuanApiAdaptor(siyuanConfig) + // expect(apiAdaptor).toBeTruthy() + }) +}) diff --git a/packages/zhi-lib-siyuan-api/src/lib/siYuanApiAdaptor.ts b/packages/zhi-lib-siyuan-api/src/lib/siYuanApiAdaptor.ts new file mode 100644 index 00000000..d933e255 --- /dev/null +++ b/packages/zhi-lib-siyuan-api/src/lib/siYuanApiAdaptor.ts @@ -0,0 +1,207 @@ +/* + * 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 { CategoryInfo, IBlogApi, MediaObject, Post, PostStatusEnum, UserBlog } from "@siyuan-community/zhi-blog-api" +import SiyuanKernelApi from "./siyuanKernelApi" +import { Env } from "@siyuan-community/zhi-env" +import SiyuanConfig from "./siyuanConfig" +import ZhiSiyuanApiUtil from "./util/ZhiSiyuanApiUtil" + +/** + * 思源笔记API适配器 + * + * @author terwer + * @version 1.0.0 + * @since 1.0.0 + */ +class SiYuanApiAdaptor implements IBlogApi { + private readonly logger + private readonly common + private readonly siyuanKernelApi + private readonly cfg + + /** + * 初始化思源 API 适配器 + * + * @param cfg - 环境变量 或者 配置项 + */ + constructor(cfg: Env | SiyuanConfig) { + this.logger = ZhiSiyuanApiUtil.zhiLog("siyuan-api-adaptor") + this.common = ZhiSiyuanApiUtil.zhiCommon() + + this.siyuanKernelApi = new SiyuanKernelApi(cfg) + this.cfg = this.siyuanKernelApi.siyuanConfig + } + + async deletePost(postid: string): Promise { + return Promise.resolve(false) + } + + async editPost(postid: string, post: Post, publish?: boolean): Promise { + return Promise.resolve(false) + } + + async getCategories(): Promise { + return Promise.resolve([]) + } + + public 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 + } + + async getPreviewUrl(postid: string): Promise { + return Promise.resolve("") + } + + public 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 async getRecentPostsCount(keyword?: string): Promise { + return await this.siyuanKernelApi.getRootBlocksCount(keyword ?? "") + } + + async getUsersBlogs(): Promise> { + return Promise.resolve([]) + } + + newMediaObject(mediaObject: MediaObject): Promise { + return Promise.resolve({} as MediaObject) + } + + async newPost(post: Post, publish?: boolean): Promise { + return Promise.resolve("") + } +} + +export default SiYuanApiAdaptor diff --git a/packages/zhi-lib-siyuan-api/src/lib/siyuanApi.ts b/packages/zhi-lib-siyuan-api/src/lib/siyuanApi.ts new file mode 100644 index 00000000..fe1eabd0 --- /dev/null +++ b/packages/zhi-lib-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 { Env } from "@siyuan-community/zhi-env" +import SiyuanConfig from "./siyuanConfig" +import SiyuanKernelApi from "./siyuanKernelApi" +import SiyuanClientApi from "./siyuanClientApi" + +/** + * 思源笔记API + * + * @author terwer + * @since 1.0.0 + */ +class SiyuanApi { + /** + * 思源笔记内核API + */ + public readonly kernelApi + + /** + * 思源笔记客户端API + */ + public readonly clientApi + + /** + * 构造思源 API对象 + * + * @param cfg - 环境变量 或者 配置项 + */ + constructor(cfg: Env | SiyuanConfig) { + this.kernelApi = new SiyuanKernelApi(cfg) + this.clientApi = new SiyuanClientApi() + } +} + +export default SiyuanApi diff --git a/packages/zhi-lib-siyuan-api/src/lib/siyuanClientApi.ts b/packages/zhi-lib-siyuan-api/src/lib/siyuanClientApi.ts new file mode 100644 index 00000000..56ab1262 --- /dev/null +++ b/packages/zhi-lib-siyuan-api/src/lib/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/packages/zhi-lib-siyuan-api/src/lib/siyuanConfig.ts b/packages/zhi-lib-siyuan-api/src/lib/siyuanConfig.ts new file mode 100644 index 00000000..c7357946 --- /dev/null +++ b/packages/zhi-lib-siyuan-api/src/lib/siyuanConfig.ts @@ -0,0 +1,69 @@ +/* + * 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 "@siyuan-community/zhi-blog-api" +import SiyuanPlaceholder from "./siyuanPlaceholder" + +/** + * 思源笔记配置 + * + * @author terwer + * @since 1.0.0 + */ +class SiyuanConfig extends BlogConfig { + /** + * 思源笔记伺服地址 + */ + public override apiUrl: string + + /** + * 思源笔记 API token + */ + public override password: string + + /** + * 思源笔记操作提示 + * + * @protected + */ + public override placeholder: SiyuanPlaceholder + + /** + * 是否修复标题 + * + * @protected + */ + public override fixTitle: boolean + + constructor(apiUrl?: string, password?: string) { + super() + this.apiUrl = apiUrl ?? "http://127.0.0.1:6806" + this.passwordType = PasswordType.PasswordType_Token + this.password = password ?? "" + this.placeholder = new SiyuanPlaceholder() + this.fixTitle = true + } +} + +export default SiyuanConfig diff --git a/packages/zhi-lib-siyuan-api/src/lib/siyuanConstants.ts b/packages/zhi-lib-siyuan-api/src/lib/siyuanConstants.ts new file mode 100644 index 00000000..648d199a --- /dev/null +++ b/packages/zhi-lib-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/packages/zhi-lib-siyuan-api/src/lib/siyuanKernelApi.spec.ts b/packages/zhi-lib-siyuan-api/src/lib/siyuanKernelApi.spec.ts new file mode 100644 index 00000000..ee63fb61 --- /dev/null +++ b/packages/zhi-lib-siyuan-api/src/lib/siyuanKernelApi.spec.ts @@ -0,0 +1,149 @@ +/* + * 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, expect } from "vitest" +import SiyuanKernelApi from "./siyuanKernelApi" +import Env from "zhi-env" +import SiyuanConfig from "./siyuanConfig" + +describe("SiyuanKernelApi", () => { + it("constructor", function () { + const env = new Env(import.meta.env) + const kernelApi = new SiyuanKernelApi(env) + expect(kernelApi).toBeTruthy() + }) + + it("sql using .env", async () => { + const env = new Env(import.meta.env) + const kernelApi = new SiyuanKernelApi(env) + const result = await kernelApi.sql("select 1 from blocks limit 1") + console.log("result=>", result) + }) + + 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 env = new Env(import.meta.env) + const kernelApi = new SiyuanKernelApi(env) + const result = await kernelApi.getRootBlocksCount("") + console.log("result=>", result) + }) + + it("lsNotebooks", async () => { + const env = new Env(import.meta.env) + const kernelApi = new SiyuanKernelApi(env) + const result = await kernelApi.lsNotebooks() + console.log("result=>", result) + }) + + it("openNotebook", async () => { + const env = new Env(import.meta.env) + const kernelApi = new SiyuanKernelApi(env) + const result = await kernelApi.openNotebook("20220718062546-2nbmy21") + console.log("result=>", result) + }) + + it("closeNotebook", async () => { + const env = new Env(import.meta.env) + const kernelApi = new SiyuanKernelApi(env) + const result = await kernelApi.closeNotebook("20220718062546-2nbmy21") + console.log("result=>", result) + }) + + it("renameNotebook", async () => { + const env = new Env(import.meta.env) + const kernelApi = new SiyuanKernelApi(env) + const result = await kernelApi.renameNotebook("20220621105123-dlyn6nl", "临时文档") + console.log("result=>", result) + }) + + it("createNotebook", async () => { + const env = new Env(import.meta.env) + const kernelApi = new SiyuanKernelApi(env) + const result = await kernelApi.createNotebook("临时文档3") + console.log("result=>", result) + }) + + it("removeNotebook", async () => { + const env = new Env(import.meta.env) + const kernelApi = new SiyuanKernelApi(env) + const result = await kernelApi.removeNotebook("20230401225851-4zgh677") + console.log("result=>", result) + }) + + it("getNotebookConf", async () => { + const env = new Env(import.meta.env) + const kernelApi = new SiyuanKernelApi(env) + const result = await kernelApi.getNotebookConf("20220621105123-dlyn6nl") + console.log("result=>", result) + }) + + it("setNotebookConf", async () => { + const env = new Env(import.meta.env) + const kernelApi = new SiyuanKernelApi(env) + 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 env = new Env(import.meta.env) + const kernelApi = new SiyuanKernelApi(env) + const result = await kernelApi.pushMsg({ + msg: "测试消息", + }) + console.log("result=>", result) + }) + + it("pushErrMsg", async () => { + const env = new Env(import.meta.env) + const kernelApi = new SiyuanKernelApi(env) + const result = await kernelApi.pushErrMsg({ + msg: "测试错误消息", + }) + console.log("result=>", result) + }) + + it("getRootBlocks", async () => { + const env = new Env(import.meta.env) + const kernelApi = new SiyuanKernelApi(env) + const result = await kernelApi.getRootBlocks(0, 10, "") + console.log("result=>", result) + }) +}) diff --git a/packages/zhi-lib-siyuan-api/src/lib/siyuanKernelApi.ts b/packages/zhi-lib-siyuan-api/src/lib/siyuanKernelApi.ts new file mode 100644 index 00000000..3c4552f6 --- /dev/null +++ b/packages/zhi-lib-siyuan-api/src/lib/siyuanKernelApi.ts @@ -0,0 +1,482 @@ +/* + * 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 { Env } from "@siyuan-community/zhi-env" +import SiyuanConfig from "./siyuanConfig" +import { DefaultLogger, EnvHelper, LogFactory, LogLevelEnum } from "@siyuan-community/zhi-log" +import { ZhiCommon } from "@siyuan-community/zhi-common" +import SiyuanConstants from "./siyuanConstants" +import ISiyuanKernelApi, { type SiyuanData } from "./ISiyuanKernelApi" +import ZhiSiyuanApiUtil from "./util/ZhiSiyuanApiUtil" + +/** + * 思源笔记服务端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} + * @see {@link https://github.com/leolee9086/noob-core/blob/master/frontEnd/noobApi/util/kernelApi.js kernelApi} + */ +class SiyuanKernelApi implements ISiyuanKernelApi { + /** + * 思源笔记服务端API版本号 + */ + public readonly VERSION + + private readonly logger: DefaultLogger + private readonly env: Env + private readonly common: ZhiCommon + public readonly siyuanConfig + + /** + * 初始化思源服务端 API + * + * @param cfg - 环境变量 或者 配置项 + */ + constructor(cfg: Env | SiyuanConfig) { + this.VERSION = "1.0.0" + this.env = ZhiSiyuanApiUtil.zhiEnv() + this.common = new ZhiCommon() + + if (cfg instanceof SiyuanConfig) { + this.siyuanConfig = cfg + + this.logger = LogFactory.customLogFactory(LogLevelEnum.LOG_LEVEL_DEBUG, "zhi").getLogger("siyuan-kernel-api") + } else { + const env = cfg + const logLevel = EnvHelper.getEnvLevel(env) + const siyuanApiUrl = env.getStringEnv(SiyuanConstants.VITE_SIYUAN_API_URL_KEY) + const siyuanApiToken = env.getStringEnv(SiyuanConstants.VITE_SIYUAN_AUTH_TOKEN_KEY) + this.siyuanConfig = new SiyuanConfig(siyuanApiUrl, siyuanApiToken) + + this.logger = LogFactory.customLogFactory(logLevel, "siyuan-kernel-api", env).getLogger(SiyuanKernelApi.name) + } + } + + /** + * 分页获取根文档 + * + * @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" + if (this.env.isDev()) { + this.logger.trace("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 (!this.common.strUtil.isEmptyString(this.siyuanConfig.password)) { + Object.assign(fetchOps, { + headers: { + Authorization: `Token ${this.siyuanConfig.password}`, + }, + }) + } + + if (this.env.isDev()) { + this.logger.trace("开始向思源请求数据,reqUrl=>", reqUrl) + this.logger.trace("开始向思源请求数据,fetchOps=>", fetchOps) + } + + const response = await fetch(reqUrl, fetchOps) + const resJson = await response.json() + if (this.env.isDev()) { + this.logger.trace("思源请求数据返回,resJson=>", resJson) + } + + 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, + }) + } +} + +export default SiyuanKernelApi diff --git a/packages/zhi-lib-siyuan-api/src/lib/siyuanPlaceholder.ts b/packages/zhi-lib-siyuan-api/src/lib/siyuanPlaceholder.ts new file mode 100644 index 00000000..b51a94cf --- /dev/null +++ b/packages/zhi-lib-siyuan-api/src/lib/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 "@siyuan-community/zhi-blog-api" + +/** + * 思源笔记操作提示 + */ +class SiyuanPlaceholder extends BlogPlaceholder {} + +export default SiyuanPlaceholder diff --git a/packages/zhi-lib-siyuan-api/src/lib/util/ZhiSiyuanApiUtil.ts b/packages/zhi-lib-siyuan-api/src/lib/util/ZhiSiyuanApiUtil.ts new file mode 100644 index 00000000..20ef79c8 --- /dev/null +++ b/packages/zhi-lib-siyuan-api/src/lib/util/ZhiSiyuanApiUtil.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. + */ + +import { Env } from "@siyuan-community/zhi-env" +import { ZhiUtil } from "@siyuan-community/zhi-common" + +/** + * 工具类统一入口,每个应用自己实现 + * + * @public + * @author terwer + * @since 1.0.0 + */ +class ZhiSiyuanApiUtil extends ZhiUtil { + public static override zhiEnv(): Env { + if (!this.env) { + this.env = new Env(import.meta.env) + } + return this.env + } +} + +export default ZhiSiyuanApiUtil diff --git a/packages/zhi-lib-siyuan-api/src/lib/zhi-lib-siyuan-api.spec.ts b/packages/zhi-lib-siyuan-api/src/lib/zhi-lib-siyuan-api.spec.ts new file mode 100644 index 00000000..5b47210b --- /dev/null +++ b/packages/zhi-lib-siyuan-api/src/lib/zhi-lib-siyuan-api.spec.ts @@ -0,0 +1,35 @@ +/* + * 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 ZhiSiyuanApiUtil from "./util/ZhiSiyuanApiUtil" +import SiyuanApi from "./siyuanApi" + +describe("zhiSiyuanApi", () => { + it("siyuanApi", () => { + // const env = ZhiSiyuanApiUtil.zhiEnv() + // const siyuanApi = new SiyuanApi(env) + // expect(siyuanApi).toBeTruthy() + }) +}) diff --git a/packages/zhi-lib-siyuan-api/src/lib/zhi-lib-siyuan-api.ts b/packages/zhi-lib-siyuan-api/src/lib/zhi-lib-siyuan-api.ts new file mode 100644 index 00000000..2dfe4770 --- /dev/null +++ b/packages/zhi-lib-siyuan-api/src/lib/zhi-lib-siyuan-api.ts @@ -0,0 +1,35 @@ +/* + * 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" +import SiyuanKernelApi from "./siyuanKernelApi" +import type { SiyuanData } from "./ISiyuanKernelApi" +import SiyuanConfig from "./siyuanConfig" +import SiYuanApiAdaptor from "./siYuanApiAdaptor" +import SiyuanConstants from "./siyuanConstants" + +export { SiyuanApi } +export { SiyuanData, SiyuanKernelApi } +export { SiyuanConstants, SiyuanConfig, SiYuanApiAdaptor } diff --git a/packages/zhi-lib-siyuan-api/tsconfig.json b/packages/zhi-lib-siyuan-api/tsconfig.json new file mode 100644 index 00000000..839952da --- /dev/null +++ b/packages/zhi-lib-siyuan-api/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "types": ["vitest"] + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/packages/zhi-lib-siyuan-api/tsconfig.lib.json b/packages/zhi-lib-siyuan-api/tsconfig.lib.json new file mode 100644 index 00000000..33eca2c2 --- /dev/null +++ b/packages/zhi-lib-siyuan-api/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/packages/zhi-lib-siyuan-api/tsconfig.spec.json b/packages/zhi-lib-siyuan-api/tsconfig.spec.json new file mode 100644 index 00000000..6d3be742 --- /dev/null +++ b/packages/zhi-lib-siyuan-api/tsconfig.spec.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"] + }, + "include": [ + "vite.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ] +} diff --git a/packages/zhi-lib-siyuan-api/vite.config.ts b/packages/zhi-lib-siyuan-api/vite.config.ts new file mode 100644 index 00000000..895e2930 --- /dev/null +++ b/packages/zhi-lib-siyuan-api/vite.config.ts @@ -0,0 +1,65 @@ +/// +import { defineConfig } from "vite" + +import viteTsConfigPaths from "vite-tsconfig-paths" +import dts from "vite-plugin-dts" +import { join } from "path" +import noBundlePlugin from "vite-plugin-no-bundle" +import { viteStaticCopy } from "vite-plugin-static-copy" + +const isTest = process.env["npm_command"] === "test" +console.log("isTest=>", isTest) +export default defineConfig({ + cacheDir: "../../node_modules/.vite/zhi-lib-siyuan-api", + + plugins: [ + dts({ + entryRoot: "src", + tsConfigFilePath: join(__dirname, "tsconfig.lib.json"), + skipDiagnostics: true, + }), + + !isTest && + viteTsConfigPaths({ + root: "../../", + }), + + !isTest && noBundlePlugin(), + + viteStaticCopy({ + targets: [ + { + src: "README.md", + dest: ".", + }, + ], + }), + ], + + // Configuration for building your library. + // See: https://vitejs.dev/guide/build.html#library-mode + build: { + lib: { + // Could also be a dictionary or array of multiple entry points. + entry: "src/index.ts", + name: "zhi-lib-siyuan-api", + fileName: "index", + // Change this to the formats you want to support. + // Don't forgot to update your package.json as well. + formats: ["es"], + }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: [], + }, + }, + + test: { + globals: true, + cache: { + dir: "../../node_modules/.vitest", + }, + environment: "jsdom", + include: ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], + }, +}) diff --git a/tsconfig.base.json b/tsconfig.base.json index ce57bfdf..fc3fed44 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -21,6 +21,7 @@ "@siyuan-community/zhi-device": ["packages/zhi-lib-device/src/index.ts"], "@siyuan-community/zhi-env": ["packages/zhi-lib-env/src/index.ts"], "@siyuan-community/zhi-log": ["packages/zhi-lib-log/src/index.ts"], + "@siyuan-community/zhi-siyuan-api": ["packages/zhi-lib-siyuan-api/src/index.ts"], "@zhi/zhi-core": ["packages/zhi-core/src/index.ts"], "@zhi/zhi-loader": ["packages/zhi-loader/src/index.ts"] }