diff --git a/docs/zh-CN/api/acl/acl-role.md b/docs/zh-CN/api/acl/acl-role.md index 708131702..c6d8a1bbe 100644 --- a/docs/zh-CN/api/acl/acl-role.md +++ b/docs/zh-CN/api/acl/acl-role.md @@ -1,4 +1,4 @@ -# ACL Role +# ACLRole ACLRole,ACL 系统中的用户角色类。在 ACL 系统中,通常使用 `acl.define` 定义角色。 diff --git a/docs/zh-CN/api/acl/acl.md b/docs/zh-CN/api/acl/acl.md index dc55f7fca..bdaa1d968 100644 --- a/docs/zh-CN/api/acl/acl.md +++ b/docs/zh-CN/api/acl/acl.md @@ -1,8 +1,29 @@ # ACL +## 概览 + ACL 为 Nocobase 中的权限控制模块。在 ACL 中注册角色、资源以及配置相应权限之后,即可对角色进行权限判断。 -## 概念解释 +### 基本使用 + +```javascript +const { ACL } = require('@nocobase/acl'); + +const acl = new ACL(); + +// 定义一个名称为 member 的角色 +const memberRole = acl.define({ + role: 'member', +}); + +// 使 member 角色拥有 posts 资源的 list 权限 +memberRole.grantAction('posts:list'); + +acl.can('member', 'posts:list'); // true +acl.can('member', 'posts:edit'); // null +``` + +### 概念解释 * 角色 (`ACLRole`):权限判断的对象 * 资源 (`ACLResource`):在 Nocobase ACL 中,资源通常对应一个数据库表,概念上可类比为 Restful API 中的 Resource。 diff --git a/docs/zh-CN/api/actions.md b/docs/zh-CN/api/actions.md index 4f64fb1cd..c752fc74e 100644 --- a/docs/zh-CN/api/actions.md +++ b/docs/zh-CN/api/actions.md @@ -1,20 +1,27 @@ # 内置常用资源操作 +## 概览 + 针对常用的 CRUD 等数据资源的操作,NocoBase 内置了对应操作方法,并通过数据表资源自动映射相关的操作。 -所有的操作方法都是注册在 resourcer 实例上,也是标准兼容 Koa 的中间件函数(`(ctx, next) => Promise`)。操作的参数由路由解析后附加在 `ctx.action` 对象上,后续参数相关介绍均基于此对象。 +```javascript +import { Application } from "@nocobase/server"; -通常情况下无需直接调用内置的 action 方法,在需要扩展默认操作行为时,可以在自定义的操作方法内调用默认方法。 +const app = new Application({ + database: { + dialect: 'sqlite', + storage: './db.sqlite', + }, + registerActions: true // 注册内置资源操作,默认为 True +}); -## 包结构 - -可通过以下方式引入相关实体: - -```ts -import actions from '@nocobase/actions'; ``` -## 单一数据资源操作 + +内置的操作方法都是注册在 `application` 中的 `resourcer` 实例上。 +通常情况下无需直接调用内置的 action 方法,在需要扩展默认操作行为时,可以在自定义的操作方法内调用默认方法。 + +## 资源操作 ### `list()` diff --git a/docs/zh-CN/api/database/collection.md b/docs/zh-CN/api/database/collection.md index 8be6651ea..67f9918dd 100644 --- a/docs/zh-CN/api/database/collection.md +++ b/docs/zh-CN/api/database/collection.md @@ -1,13 +1,40 @@ # Collection -数据表结构管理类。 +## 概览 -大部分接口通常不会直接由开发者调用,除非进行较底层的扩展开发。 +`Collection` 用于定义系统中的数据模型,如模型名称、字段、索引、关联等信息。 +一般通过 `Database` 实例的 `collection` 方法作为代理入口调用。 + +```javascript +const { Database } = require('@nocobase/database') + +// 创建数据库实例 +const db = new Database({...}); + +// 定义数据模型 +db.collection({ + name: 'users', + // 定义模型字段 + fields: [ + // 标量字段 + { + name: 'name', + type: 'string', + }, + + // 关联字段 + { + name: 'profile', + type: 'hasOne' // 'hasMany', 'belongsTo', 'belongsToMany' + } + ], +}); +``` + +更多字段类型请参考 [Fields](/api/database/field.md)。 ## 构造函数 -通常不会直接使用,主要通过 `Database` 实例的 `collection` 方法作为代理入口调用。 - **签名** * `constructor(options: CollectionOptions, context: CollectionContext)` diff --git a/docs/zh-CN/api/database/field.md b/docs/zh-CN/api/database/field.md index 340b97cde..c868aff3f 100644 --- a/docs/zh-CN/api/database/field.md +++ b/docs/zh-CN/api/database/field.md @@ -1,7 +1,11 @@ # Field +## 概览 + 数据表字段管理类(抽象类)。同时是所有字段类型的基类,其他任意字段类型均通过继承该类来实现。 +如何自定义字段可参考[扩展字段类型](/development/guide/collections-fields#扩展字段类型) + ## 构造函数 通常不会直接由开发者调用,主要通过 `db.collection({ fields: [] })` 方法作为代理入口调用。 diff --git a/docs/zh-CN/api/database/index.md b/docs/zh-CN/api/database/index.md index 975fa52aa..2ec633ebc 100644 --- a/docs/zh-CN/api/database/index.md +++ b/docs/zh-CN/api/database/index.md @@ -1,23 +1,106 @@ # Database -NocoBase 内置的数据库访问类,通过封装 [Sequelize](https://sequelize.org/) 提供了更加简单的数据库访问接口和统一化的 JSON 数据库表配置方式,同时也提供了扩展字段类型和查询操作符的能力。 +## 概览 -Database 类继承自 EventEmitter,可以通过 `db.on('event', callback)` 监听数据库事件,以及 `db.off('event', callback)` 移除监听。 +Database 是 Nocobase 提供的数据库交互工具,为无代码、低代码应用提供了非常方便的数据库交互功能。目前支持的数据库为: -## 包结构 +* SQLite 3.8.8+ +* MySQL 8.0.17+ +* PostgreSQL 10.0+ -可通过以下方式引入相关实体: -```ts -import Database, { - Field, - Collection, - Repository, - RelationRepository, - extend -} from '@nocobase/database'; +### 连接数据库 + +在 `Database` 构造函数中,可以通过传入 `options` 参数来配置数据库连接。 + +```javascript +const { Database } = require('@nocobase/database'); + +// SQLite 数据库配置参数 +const database = new Database({ + dialect: 'sqlite', + storage: 'path/to/database.sqlite' +}) + +// MySQL \ PostgreSQL 数据库配置参数 +const database = new Database({ + dialect: /* 'postgres' 或者 'mysql' */, + database: 'database', + username: 'username', + password: 'password', + host: 'localhost', + port: 'port' +}) + ``` +详细的配置参数请参考 [构造函数](#构造函数)。 + +### 数据模型定义 + +`Database` 通过 `Collection` 定义数据库结构,一个 `Collection` 对象代表了数据库中的一张表。 + +```javascript +// 定义 Collection +const UserCollection = database.collection({ + name: 'users', + fields: [ + { + name: 'name', + type: 'string', + }, + { + name: 'age', + type: 'integer', + }, + ], +}); + +``` + +数据库结构定义完成之后,可使用 `sync()` 方法来同步数据库结构。 + +```javascript +await database.sync(); +``` + +更加详细的 `Collection` 使用方法请参考 [Collection](/api/database/collection.md)。 + +### 数据读写 + +`Database` 通过 `Repository` 对数据进行操作。 + +```javascript + +const UserRepository = UserCollection.repository(); + +// 创建 +await UserRepository.create({ + name: '张三', + age: 18, +}); + +// 查询 +const user = await UserRepository.findOne({ + filter: { + name: '张三', + }, +}); + +// 修改 +await UserRepository.update({ + values: { + age: 20, + }, +}); + +// 删除 +await UserRepository.destroy(user.id); +``` + +更加详细的数据 CRUD 使用方法请参考 [Repository](/api/database/repository.md)。 + + ## 构造函数 **签名** @@ -28,7 +111,6 @@ import Database, { **参数** -`options` 参数与 [Sequelize 的构造参数](https://sequelize.org/api/v6/class/src/sequelize.js~sequelize#instance-constructor-constructor)一致的部分会透传至 Sequelize,同时 NocoBase 也会使用一些额外的参数: | 参数名 | 类型 | 默认值 | 描述 | | --- | --- | --- | --- | @@ -44,46 +126,6 @@ import Database, { | `options.tablePrefix?` | `string` | `''` | NocoBase 扩展,表名前缀 | | `options.migrator?` | `UmzugOptions` | `{}` | NocoBase 扩展,迁移管理器相关参数,参考 [Umzug](https://github.com/sequelize/umzug/blob/main/src/types.ts#L15) 实现 | -**示例** - -```ts -import Database from '@nocobase/database'; - -const app = new Database({ - dialect: 'mysql', - host: 'localhost', - port: 3306, - username: 'root', - password: '123456', - database: 'test', - tablePrefix: 'my_' -}); -``` - -## 实例成员 - -### `sequelize` - -初始化后的 Sequelize 实例,在需要使用 sequelize 底层方法时可以调用,相关信息可以直接参考 sequelize 的文档。 - -### `options` - -初始化的配置参数,包含了 Sequelize 的配置参数和 NocoBase 的额外配置参数。 - -### `version` - -连接的数据库的版本信息对象,可通过 `await db.version.satisfies()` 检查是否满足特定数据库版本要求。 - -**示例** - -```ts -const r = await this.db.version.satisfies({ - mysql: '>=8.0.17', - sqlite: '3.x', - postgres: '>=10', -}); -``` - ## 迁移相关方法 ### `addMigration()` diff --git a/docs/zh-CN/api/database/operators.md b/docs/zh-CN/api/database/operators.md index 468d5f0e4..7bb84f1ab 100644 --- a/docs/zh-CN/api/database/operators.md +++ b/docs/zh-CN/api/database/operators.md @@ -1,6 +1,6 @@ # Filter Operators -用于 Repository 的 find、findOne、findAndCount、count 等 API 的 filter 参数里。如: +用于 Repository 的 find、findOne、findAndCount、count 等 API 的 filter 参数中: ```ts const repository = db.getRepository('books'); @@ -14,9 +14,7 @@ repository.find({ }); ``` -相当于 Sequelize Where 查询的 [Op](https://sequelize.org/docs/v6/core-concepts/model-querying-basics/#operators) 对象。 - -为了支持 JSON 化,NocoBase 中将查询运算符转换为以 $ 为前缀的字符串标识。 +为了支持 JSON 化,NocoBase 中将查询运算符以 $ 为前缀的字符串标识。 另外,NocoBase 也提供了扩展运算符的 API,详见 [`db.registerOperators()`](../database#registeroperators)。 diff --git a/docs/zh-CN/api/database/relation-repository/belongs-to-repository.md b/docs/zh-CN/api/database/relation-repository/belongs-to-repository.md index 15824a3d2..30373bba0 100644 --- a/docs/zh-CN/api/database/relation-repository/belongs-to-repository.md +++ b/docs/zh-CN/api/database/relation-repository/belongs-to-repository.md @@ -1,3 +1,4 @@ ## BelongsToRepository -`BelongsToRepository` 是用于处理 `BelongsTo` 关系的 `Repository`,它提供了一些便捷的方法来处理 `BelongsTo` 关系。其接口与 [HasOneRepository](./has-one-repository.md) 一致。 +其接口与 [HasOneRepository](./has-one-repository.md) 一致。 +`BelongsToRepository` 是用于处理 `BelongsTo` 关系的 `Repository`,它提供了一些便捷的方法来处理 `BelongsTo` 关系。 diff --git a/docs/zh-CN/api/database/relation-repository/has-one-repository.md b/docs/zh-CN/api/database/relation-repository/has-one-repository.md index fd1037604..66ea4a65e 100644 --- a/docs/zh-CN/api/database/relation-repository/has-one-repository.md +++ b/docs/zh-CN/api/database/relation-repository/has-one-repository.md @@ -1,4 +1,6 @@ # HasOneRepository +## 概览 + `HasOneRepository` 为 `HasOne` 类型的关联 Repository。 ```typescript @@ -19,8 +21,14 @@ const user = await User.repository.create({ values: { name: 'u1' }, }); -// 创建 HasOneRepository 实例 -const userProfileRepository = new HasOneRepository(User, 'profile', user.get('id')); + +// 获取到关联 Repository +const userProfileRepository = User.repository.relation('profile').of(user.get('id')); + +// 也可直接初始化 +new HasOneRepository(User, 'profile', user.get('id')); + + ``` diff --git a/docs/zh-CN/api/database/repository.md b/docs/zh-CN/api/database/repository.md index f80fde92f..d7ca19c95 100644 --- a/docs/zh-CN/api/database/repository.md +++ b/docs/zh-CN/api/database/repository.md @@ -1,6 +1,331 @@ # Repository -数据表数据仓库管理类。大部分基于数据表的数据存取等操作均通过该类实现。 +## 概览 + +在一个给定的 `Collection` 对象上,可以获取到它的 `Repository` 对象来对数据表进行读写操作。 + +```javascript +const { UserCollection } = require("./collections"); + +const UserRepository = UserCollection.repository; + +const user = await UserRepository.findOne({ + filter: { + id: 1 + }, +}); + +user.name = "new name"; +await user.save(); +``` + +### 查询 + +#### 基础查询 + +在 `Repository` 对象上,调用 `find*` 相关方法,可执行查询操作,查询方法都支持传入 `filter` 参数,用于过滤数据。 + +```javascript +// SELECT * FROM users WHERE id = 1 +userRepository.find({ + filter: { + id: 1 + } +}); + +``` +#### 操作符 + +`Repository` 中的 `filter` 参数,还提供了多种操作符,执行更加多样的查询操作。 + +```javascript +// SELECT * FROM users WHERE age > 18 +userRepository.find({ + filter: { + age: { + $gt: 18 + } + } +}); + +// SELECT * FROM users WHERE age > 18 OR name LIKE '%张%' +userRepository.find({ + filter: { + $or: [ + { age: { $gt: 18 } }, + { name: { $like: "%张%" } } + ] + } +}); + +``` + +操作符的更多详细信息请参考 [Filter Operators](/api/database/operators)。 + +#### 字段控制 + +在查询操作时,通过 `fields`, `except`, `appends` 参数可以控制输出字段。 + +* `fields`: 指定输出字段 +* `except`: 排除输出字段 +* `appends`: 追加输出关联字段 + +```javascript +// 获取的结果只包含 id 和 name 字段 +userRepository.find({ + fields: ["id", "name"], +}); + +// 获取的结果不包含 password 字段 +userRepository.find({ + except: ["password"], +}); + +// 获取的结果会包含关联对象 posts 的数据 +userRepository.find({ + appends: ["posts"], +}); +``` + +#### 关联字段查询 + +`filter` 参数支持按关联字段进行过滤,例如: + +```javascript +// 查询 user 对象,其所关联的 posts 存在 title 为 'title1' 的对象 +userRepository.find({ + filter: { + "posts.title": "post title" + } +}); +``` + +关联字段也可进行嵌套 + +```javascript +// 查询 user 对象,查询结果满足其 posts 的 comments 包含 keywords +await userRepository.find({ + filter: { + "posts.comments.content": { + $like: "%keywords%" + } + } +}); +``` + +#### 排序 + +通过 `sort` 参数,可以对查询结果进行排序。 + +```javascript + +// SELECT * FROM users ORDER BY age +await userRepository.find({ + sort: 'age' +}); + + +// SELECT * FROM users ORDER BY age DESC +await userRepository.find({ + sort: '-age' +}); + +// SELECT * FROM users ORDER BY age DESC, name ASC +await userRepository.find({ + sort: ['-age', "name"], +}); +``` + +也可按照关联对象的字段进行排序 + +```javascript +await userRepository.find({ + sort: 'profile.createdAt' +}); +``` + +### 创建 + +#### 基础创建 + +通过 `Repository` 创建新的数据对象。 + +```javascript + +await userRepository.create({ + name: "张三", + age: 18, +}); +// INSERT INTO users (name, age) VALUES ('张三', 18) + + +// 支持批量创建 +await userRepository.create([ + { + name: "张三", + age: 18, + }, + { + name: "李四", + age: 20, + }, +]) + +``` + +#### 创建关联 + +创建时可以同时创建关联对象,和查询类似,也支持关联对象的嵌套使用,例如: + +```javascript +await userRepository.create({ + name: "张三", + age: 18, + posts: [ + { + title: "post title", + content: "post content", + tags: [ + { + name: "tag1", + }, + { + name: "tag2", + }, + ], + }, + ], +}); +// 创建用户的同时,创建 post 与用户关联,创建 tags 与 post 相关联。 +``` +若关联对象已在数据库中,可传入其ID,创建时会建立与关联对象的关联关系。 + +```javascript +const tag1 = await tagRepository.findOne({ + filter: { + name: "tag1" + }, +}); + +await userRepository.create({ + name: "张三", + age: 18, + posts: [ + { + title: "post title", + content: "post content", + tags: [ + { + id: tag1.id, // 建立与已存在关联对象的关联关系 + }, + { + name: "tag2", + }, + ], + }, + ], +}); +``` + +### 更新 + +#### 基础更新 + +获取到数据对象后,可直接在数据对象(`Model`)上修改属性,然后调用 `save` 方法保存修改。 + +```javascript +const user = await userRepository.findOne({ + filter: { + name: "张三", + }, +}); + + +user.age = 20; +await user.save(); +``` + +数据对象 `Model` 继承自 Sequelize Model,对 `Model` 的操作可参考 [Sequelize Model](https://sequelize.org/master/manual/model-basics.html)。 + +也可通过 `Repository` 更新数据: + +```javascript +// 修改满足筛选条件的数据记录 +await userRepository.update({ + filter: { + name: "张三", + }, + values: { + age: 20, + }, +}); +``` + +更新时,可以通过 `whitelist` 、`blacklist` 参数控制更新字段,例如: + +```javascript +await userRepository.update({ + filter: { + name: "张三", + }, + values: { + age: 20, + name: "李四", + }, + whitelist: ["age"], // 仅更新 age 字段 +}); +```` + +#### 更新关联字段 + +在更新时,可以设置关联对象,例如: + +```javascript +const tag1 = tagRepository.findOne({ + filter: { + id: 1 + }, +}); + +await postRepository.update({ + filter: { + id: 1 + }, + values: { + title: "new post title", + tags: [ + { + id: tag1.id // 与 tag1 建立关联 + }, + { + name: "tag2", // 创建新的 tag 并建立关联 + }, + ], + }, +}); + + +await postRepository.update({ + filter: { + id: 1 + }, + values: { + tags: null // 解除 post 与 tags 的关联 + }, +}) +``` + +### 删除 + +可调用 `Repository` 中的 `destroy()`方法进行删除操作。删除时需指定筛选条件: + +```javascript +await userRepository.destroy({ + filter: { + status: "blocked", + }, +}); +``` ## 构造函数 @@ -56,7 +381,7 @@ await books.myQuery('SELECT * FROM books;'); ### `find()` -从数据库查询特定条件的数据集。相当于 Sequelize 中的 `Model.findAll()`。 +从数据库查询数据集,可指定筛选条件、排序等。 **签名** diff --git a/docs/zh-CN/api/index.md b/docs/zh-CN/api/index.md index cb8199c15..f23665749 100644 --- a/docs/zh-CN/api/index.md +++ b/docs/zh-CN/api/index.md @@ -1,8 +1,8 @@ # 概览 -| 模块 | 包名 | 描述 | -| --------------------------------- | --------------------- | ------------------- | -| [Server](/api/server) | `@nocobase/server` | 服务端应用 | +| 模块 | 包名 | 描述 | +|-----------------------------------| --------------------- | ------------------- | +| [Server](/api/server/application) | `@nocobase/server` | 服务端应用 | | [Database](/api/database) | `@nocobase/database` | 数据库访问层 | | [Resourcer](/api/resourcer) | `@nocobase/resourcer` | 资源与路由映射 | | [ACL](/api/acl) | `@nocobase/acl` | 访问控制表 | diff --git a/docs/zh-CN/api/resourcer/index.md b/docs/zh-CN/api/resourcer/index.md index d1eb0c49b..f617c29c9 100644 --- a/docs/zh-CN/api/resourcer/index.md +++ b/docs/zh-CN/api/resourcer/index.md @@ -1,25 +1,57 @@ # Resourcer -Resourcer 主要用于管理 API 资源与路由,也是 NocoBase 的内置模块,app 默认会自动创建一个 Resourcer 实例,大部分情况你可以通过 `app.resourcer` 访问。 +## 概览 -资源路由管理器主要通过 [资源](/api/server/resourcer/resource) + [操作](/api/server/resourcer/action) 的概念定义服务端 API 接口,与 RESTful 的概念相似。大部分资源通过映射数据库表生成,包含常规的 CRUD 操作,以覆盖常见场景。但如果有额外需求,也可以在此基础上扩展更多的资源类型和操作类型。 +Nocobase 中的接口遵循面向资源的设计模式。Resourcer 主要用于管理 API 资源与路由。 -## 包结构 +```javascript +const Koa = require('koa'); +const { Resourcer } = require('@nocobase/resourcer'); -可通过以下方式引入相关实体: +const resourcer = new Resourcer(); -```ts -import Resourcer, { - Resource, - Action, - Middleware, - branch -} from '@nocobase/resourcer'; +// 定义一个资源接口 +resourcer.define({ + name: 'users', + actions: { + async list(ctx) { + ctx.body = [ + { + name: "u1", + age: 18 + }, + { + name: "u2", + age: 20 + } + ] + } + }, +}); + +const app = new Koa(); + +// 可在 koa 实例中使用 +app.use( + resourcer.middleware({ + prefix: '/api', // resourcer 路由前缀 + }), +); + +app.listen(3000); ``` +启动服务后,使用`curl`发起请求: +```bash +>$ curl localhost:3000/api/users +[{"name":"u1","age":18},{"name":"u2","age":20}] +``` +更多 Resourcer 的使用说明可参考[资源与操作](/development/guide/resources-actions)。 Resourcer 内置于 [NocoBase Application](/api/server/application#resourcer) ,可以通过 `app.resourcer` 访问。 + + ## 构造函数 -用于创建 Resourcer 管理器实例。由于 app 默认创建一个内置实例,所以通常不会直接使用构造函数。 +用于创建 Resourcer 管理器实例。 **签名** diff --git a/docs/zh-CN/api/server/application.md b/docs/zh-CN/api/server/application.md index 8aac7a008..2c8b1d5c4 100644 --- a/docs/zh-CN/api/server/application.md +++ b/docs/zh-CN/api/server/application.md @@ -1,6 +1,136 @@ # Application -基于 [Koa](https://koajs.com/) 实现的 WEB 框架,兼容所有的 Koa 插件。 +## 概览 + +### Web服务 +Nocobase Application 是基于 [Koa](https://koajs.com/) 实现的 WEB 框架,兼容 Koa 的 API。 + +```javascript +// index.js +const { Application } = require('@nocobase/server'); + +// 创建App实例,并配置数据库连接信息 +const app = new Application({ + database: { + dialect: 'sqlite', + storage: ':memory:', + } +}); + +// 注册中间件 响应请求 +app.use(async ctx => { + ctx.body = 'Hello World'; +}); + +// 以命令行模式启动 +app.runAsCLI(); +``` + +在命令行中运行 `node index.js start` 启动服务后,使用 `curl` 请求服务。 + +```bash +$> curl localhost:3000 +Hello World +``` + +### 命令行工具 +Nocobase Application 中也内置了 `cli commander`,可以当作命令行工具运行。 + +```javascript +// cmd.js +const {Application} = require('@nocobase/server'); +const app = new Application({ + database: { + dialect: 'sqlite', + storage: ':memory:', + } +}); + +app.cli.command('hello').action(async () => { + console.log("hello world") +}); + +app.runAsCLI() +``` + +在命令行中运行 + +```bash +$> node cmd.js hello +hello world +``` + +### 插件注入 + +Nocobase Application 被设计为高度可扩展的框架,可以编写插件注入到应用中扩展功能。 +例如上面的 Web 服务可以替换为插件形式。 + +```javascript +const { Application, Plugin } = require('@nocobase/server'); + +// 通过继承 Plugin 类来编写插件 +class HelloWordPlugin extends Plugin { + load() { + this.app.use(async (ctx, next) => { + ctx.body = "Hello World"; + }) + } +} + +const app = new Application({ + database: { + dialect: 'sqlite', + storage: ':memory:', + } +}); + +// 注入插件 +app.plugin(HelloWordPlugin, { name: 'hello-world-plugin'} ); + +app.runAsCLI() +``` + +### 更多示例 + +更加详细的插件开发文档请参考 [插件开发](./plugin.md)。 +Application 类的更多示例可参考 [examples](https://github.com/nocobase/nocobase/blob/main/examples/index.md) + +## 生命周期 + +根据不同运行模式,Application 有三种生命周期: + +### 安装 +使用 `cli` 中的 `install` 命令调用安装。 +一般来说,插件在使用之前若需要在数据库中写入新表或者数据,都需要在安装时执行。在初次使用 Nocobase 时也需要调用安装。 + +* 调用 `load` 方法,载入已注册的插件。 +* 触发 `beforeInstall` 事件。 +* 调用 `db.sync` 方法,同步数据库。 +* 调用 `pm.install` 方法,执行已注册插件的 `install` 方法。 +* 写入 `nocobase` 版本。 +* 触发 `afterInstall`。 +* 调用 `stop` 方法,结束安装。 + +### 启动 +使用 `cli` 中的 `start` 命令来启动 Nocobase Web 服务。 + +* 调用 `load` 方法,载入已注册的插件。 +* 调用 `start` 方法 + * 触发 `beforeStart` + * 启动端口监听 + * 触发 `afterStart` + +### 更新 + +当需要更新 Nocobase 时,可使用 `cli` 中的 `upgrade` 命令。 + +* 调用 `load` 方法,载入已注册的插件。 +* 触发 `beforeUpgrade`。 +* 调用 `db.migrator.up` 方法,执行数据库迁移。 +* 调用 `db.sync` 方法,同步数据库。 +* 调用 `version.update` 方法,更新 `nocobase` 版本。 +* 触发 `afterUpgrade`。 +* 调用 `stop` 方法,结束更新。 ## 构造函数 @@ -32,29 +162,6 @@ interface ApplicationOptions { } ``` -**示例** - -```ts -import Application from '@nocobase/server'; - -const app = new Application({ - database: { - dialect: 'mysql', - host: 'localhost', - port: 3306, - username: 'root', - password: '123456', - database: 'test', - }, - resourcer: { - prefix: '/api', - }, - cors: { - origin: '*', - } -}); -``` - ## 实例成员 ### `cli` diff --git a/docs/zh-CN/api/server/plugin.md b/docs/zh-CN/api/server/plugin.md index 2f7a9705c..4bb5aaf9c 100644 --- a/docs/zh-CN/api/server/plugin.md +++ b/docs/zh-CN/api/server/plugin.md @@ -1,51 +1,40 @@ # Plugin -## 示例 +## 概览 -```ts -const app = new Application(); +Nocobase 中的插件为 `Class` 的形式。如需自定义插件,需要继承 `Plugin` 类。 + +```typescript +import { Plugin } from '@nocobase/server'; class MyPlugin extends Plugin { - afterAdd() {} - beforeLoad() {} - load() {} - install() {} - afterEnable() {} - afterDisable() {} - remove() {} + // ... } app.plugin(MyPlugin, { name: 'my-plugin' }); ``` -## 属性 +## 插件生命周期 -### `options` - -插件配置信息 - -### `name` - -插件标识,只读 - -## 实例方法 - -### `afterAdd()` - -插件 add/addStatic 之后 +每个插件都包含生命周期方法,你可以重写这些方法,以便于在运行过程中特定的阶段执行这些方法。 +生命周期方法将由 `Application` 在特定阶段调用,可参考 [`Application`生命周期](./application.md)。 ### `beforeLoad()` -插件加载前,如事件或类注册 +插件加载前,如事件或类注册,在此可访问核心接口,其他插件此阶段不可用。 ### `load()` -加载插件,配置之类 +加载插件,配置之类。在 `load` 中可调用其他插件实例,而在 `beforeLoad` 中是不行的。 ### `install()` 插件安装逻辑,如初始化数据 +### `afterAdd()` + +插件 add/addStatic 之后 + ### `afterEnable()` 插件激活之后的逻辑 @@ -56,4 +45,4 @@ app.plugin(MyPlugin, { name: 'my-plugin' }); ### `remove()` -用于实现插件删除逻辑 \ No newline at end of file +用于实现插件删除逻辑 diff --git a/docs/zh-CN/development/guide/collections-fields.md b/docs/zh-CN/development/guide/collections-fields.md index c10bd0ae0..1690b47b8 100644 --- a/docs/zh-CN/development/guide/collections-fields.md +++ b/docs/zh-CN/development/guide/collections-fields.md @@ -229,11 +229,10 @@ export extend({ 假设我们需要直接应用 Snowflake ID 生成算法,扩展出一个 `snowflake` 字段类型,我们可以创建一个 `fields/snowflake.ts` 文件: ```ts -import { DataTypes } from 'sequelize'; // 引入算法工具包 import { Snowflake } from 'nodejs-snowflake'; // 引入字段类型基类 -import { Field, BaseColumnFieldOptions } from '@nocobase/database'; +import { DataTypes, Field, BaseColumnFieldOptions } from '@nocobase/database'; export interface SnowflakeFieldOptions extends BaseColumnFieldOptions { type: 'snowflake'; diff --git a/packages/core/server/src/application.ts b/packages/core/server/src/application.ts index 2c7fa6a98..5be6a87d2 100644 --- a/packages/core/server/src/application.ts +++ b/packages/core/server/src/application.ts @@ -43,7 +43,6 @@ export interface ApplicationOptions { export interface DefaultState extends KoaDefaultState { currentUser?: any; - [key: string]: any; } @@ -209,7 +208,6 @@ export class Application exten this._eventsCount = []; this.removeAllListeners(); this.middleware = new Toposort(); - // this.context = Object.create(context); this.plugins = new Map(); this._acl = createACL(); if (this._db) { diff --git a/packages/core/server/src/plugin-manager/PluginManager.ts b/packages/core/server/src/plugin-manager/PluginManager.ts index 62dfef9b3..d12402f23 100644 --- a/packages/core/server/src/plugin-manager/PluginManager.ts +++ b/packages/core/server/src/plugin-manager/PluginManager.ts @@ -175,6 +175,7 @@ export class PluginManager { throw new Error(`plugin name invalid`); } } + const instance = new plugin(this.app, { name, enabled: true,