docs: update api docs (#973)

* docs: application

* docs: application lifecycle

* docs: database

* docs: update

* docs: repository

* docs: repository

* docs: plugin

* docs: resourcer

* docs: database

* docs: acl

* docs: actions

* docs: repository

* docs: api

* docs: api

* docs: api
This commit is contained in:
ChengLei Shao 2022-10-31 20:10:52 +08:00 committed by GitHub
parent a6eebb940f
commit 3aada8fe07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 706 additions and 147 deletions

View File

@ -1,4 +1,4 @@
# ACL Role
# ACLRole
ACLRoleACL 系统中的用户角色类。在 ACL 系统中,通常使用 `acl.define` 定义角色。

View File

@ -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。

View File

@ -1,20 +1,27 @@
# 内置常用资源操作
## 概览
针对常用的 CRUD 等数据资源的操作NocoBase 内置了对应操作方法,并通过数据表资源自动映射相关的操作。
所有的操作方法都是注册在 resourcer 实例上,也是标准兼容 Koa 的中间件函数(`(ctx, next) => Promise<void>`)。操作的参数由路由解析后附加在 `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()`

View File

@ -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)`

View File

@ -1,7 +1,11 @@
# Field
## 概览
数据表字段管理类(抽象类)。同时是所有字段类型的基类,其他任意字段类型均通过继承该类来实现。
如何自定义字段可参考[扩展字段类型](/development/guide/collections-fields#扩展字段类型)
## 构造函数
通常不会直接由开发者调用,主要通过 `db.collection({ fields: [] })` 方法作为代理入口调用。

View File

@ -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(<sem_ver>)` 检查是否满足特定数据库版本要求。
**示例**
```ts
const r = await this.db.version.satisfies({
mysql: '>=8.0.17',
sqlite: '3.x',
postgres: '>=10',
});
```
## 迁移相关方法
### `addMigration()`

View File

@ -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)。

View File

@ -1,3 +1,4 @@
## BelongsToRepository
`BelongsToRepository` 是用于处理 `BelongsTo` 关系的 `Repository`,它提供了一些便捷的方法来处理 `BelongsTo` 关系。其接口与 [HasOneRepository](./has-one-repository.md) 一致。
其接口与 [HasOneRepository](./has-one-repository.md) 一致。
`BelongsToRepository` 是用于处理 `BelongsTo` 关系的 `Repository`,它提供了一些便捷的方法来处理 `BelongsTo` 关系。

View File

@ -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'));
```

View File

@ -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()`
从数据库查询数据集,可指定筛选条件、排序等
**签名**

View File

@ -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` | 访问控制表 |

View File

@ -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 管理器实例。
**签名**

View File

@ -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`

View File

@ -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()`
插件激活之后的逻辑

View File

@ -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';

View File

@ -43,7 +43,6 @@ export interface ApplicationOptions {
export interface DefaultState extends KoaDefaultState {
currentUser?: any;
[key: string]: any;
}
@ -209,7 +208,6 @@ export class Application<StateT = DefaultState, ContextT = DefaultContext> exten
this._eventsCount = [];
this.removeAllListeners();
this.middleware = new Toposort<any>();
// this.context = Object.create(context);
this.plugins = new Map<string, Plugin>();
this._acl = createACL();
if (this._db) {

View File

@ -175,6 +175,7 @@ export class PluginManager {
throw new Error(`plugin name invalid`);
}
}
const instance = new plugin(this.app, {
name,
enabled: true,