docs: update docs

This commit is contained in:
chenos 2021-09-11 18:54:03 +08:00
parent c6b68f2b10
commit d109732757
46 changed files with 562 additions and 3866 deletions

View File

@ -58,7 +58,7 @@
.@{prefix}-menu-logo {
display: inline-block;
width: 66px;
width: 70%;
height: 65px;
background: url(@img-logo) no-repeat 0 / contain;
}

View File

@ -1,10 +0,0 @@
---
title: 介绍
order: 0
toc: menu
nav:
title: 核心
order: 2
---
# 介绍

View File

@ -1,135 +0,0 @@
---
title: 数据库迁移
order: 1
toc: menu
nav:
title: 核心
order: 2
---
# 数据库迁移
NocoBase 通过配置来生成数据表和字段,如:
```ts
const db = new Database();
db.table({
name: 'examples',
fields: [
{ type: 'string', name: 'name' },
{ type: 'string', name: 'title' },
],
});
```
如果需要生成数据表和字段,直接执行 `db.sync()` 操作就可以了。
```ts
// 只更新不删除
await db.sync();
// 全部重建
await db.sync({
force: true,
alter: {
drop: true,
},
});
// 更多参数说明
interface SyncOptions {
/**
* The tables should be created or updated.
*/
tables?: string[] | Table[] | Map<string, Table>;
/**
* If force is true, each DAO will do DROP TABLE IF EXISTS ..., before it tries to create its own table
*/
force?: boolean;
/**
* If alter is true, each DAO will do ALTER TABLE ... CHANGE ...
* Alters tables to fit models. Provide an object for additional configuration. Not recommended for production use. If not further configured deletes data in columns that were removed or had their type changed in the model.
*/
alter?: boolean | SyncAlterOptions;
/**
* Match a regex against the database name before syncing, a safety check for cases where force: true is
* used in tests but not live code
*/
match?: RegExp;
/**
* The schema that the tables should be created in. This can be overridden for each table in sequelize.define
*/
schema?: string;
/**
* An optional parameter to specify the schema search_path (Postgres only)
*/
searchPath?: string;
}
```
如果后续有新增或删除字段操作,直接修改配置即可,如:
```ts
db.table({
name: 'examples',
fields: [
{ type: 'string', name: 'name' },
// { type: 'string', name: 'title' }, // 去掉 title 字段
{ type: 'text', name: 'content' }, // 新增 content 字段
],
});
```
接着执行 `db.sync()` 操作就同步给数据库了。
但是,如果需要更改字段名怎么操作呢,比如将 name 改为 name2需要将配置修一下
```ts
db.table({
name: 'examples',
fields: [
{ type: 'string', name: 'name2' },
],
});
```
这时,执行 `db.sync()` 操作并不会把 name 改为 name2而是又生成了一个新的 name2 字段。如果要更改字段名称,需要配合 migration 来处理,如:
```ts
await queryInterface.renameColumn('examples', 'name', 'name2');
```
这个办法能解决问题,但是需要写另外的 migration 文件来处理。更好的办法,可以给字段一个显式的 uid
```ts
db.table({
name: 'examples',
fields: [
{ uid: 'f0nyq8tg3j1y', type: 'string', name: 'name' },
],
});
```
更改字段配置时uid 不变,修改其他值即可
```ts
db.table({
name: 'examples',
fields: [
{ uid: 'f0nyq8tg3j1y', type: 'string', name: 'name2' },
],
});
```
需要同步给数据库时,接着执行 `db.sync()` 方法。
<Alert title="注意" type="warning">
uid 的方案适用于 table、field、index但是还并未实现。暂时可以结合 migration 来管理数据库结构变更。
</Alert>

View File

@ -1,426 +0,0 @@
---
title: '@nocobase/actions'
order: 4
# toc: menu
---
# @nocobase/actions
## 介绍
为资源提供了几种默认方法:
- list查看列表
- get查看详情
- create创建数据
- update更新数据
- destroy删除数据
- set建立关联
- add附加关联
- remove删除关联
- toggle附加或删除关联
- sort排序
## 安装
```bash
yarn add @nocobase/actions
```
<Alert title="注意" type="warning">
@nocobase/actions 不能单独使用,依赖于 @nocobase/database@nocobase/resourcer
</Alert>
## Usage
```ts
import Koa from 'koa';
import Database from '@nocobase/database';
import { actions, middlewares } from '@nocobase/actions';
// 配置数据表
const db = new Database({
// 省略
});
const table = db.table({
name: 'posts',
fields: [
{type: 'string', name: 'title'},
],
});
await table.sync();
// 配置资源
const resourcer = new Resourcer();
resourcer.registerActionHandlers({ ...actions.common });
resourcer.define({
name: 'posts',
});
// 使用 Koa
const app = new Koa();
app.use(async (ctx, next) => {
ctx.db = database;
await next();
});
app.use(resourcer.middleware({
prefix: '/api',
}));
app.listen(3000);
```
创建一条 posts 数据
```bash
curl -d '{"title": "title 1"}' -H 'Content-Type: application/json' http://localhost:3000/api/posts
```
## Actions
### list
查询列表数据
API
```bash
# 常规
GET /api/<resourceName>?filter[col1]=val1&fields=col1,col2&sort=-created_at&page=2&perPage=10
# 关系资源
GET /api/<associatedName>/<associatedKey>/<resourceName>?filter[col1]=val1&fields=col1,col2&sort=-created_at&page=2&perPage=10
```
SDK
```ts
// 常规
api.resource('<resourceName>').list({
filter,
fields,
sort,
page,
perPage,
});
// 关系资源
api.resource('<associatedName>.<resourceName>').list({
associatedKey,
filter,
fields,
sort,
page,
perPage,
});
```
### get
查询详情数据
API
```bash
# 常规
GET /api/<resourceName>/<resourceKey>?filter[col1]=val1&fields=col1,col2&sort=-created_at&page=2&perPage=10
# 关系资源
GET /api/<associatedName>/<associatedKey>/<resourceName>/<resourceKey>?filter[col1]=val1&fields=col1,col2&sort=-created_at&page=2&perPage=10
```
SDK
```ts
// 常规
api.resource('<resourceName>').get({
resourceKey,
filter,
fields,
sort,
page,
perPage,
});
// 关系资源
api.resource('<associatedName>.<resourceName>').get({
associatedKey,
resourceKey,
filter,
fields,
sort,
page,
perPage,
});
```
### create
新增数据
API
```bash
# 常规
POST /api/<resourceName>?fields=col1,col2
# or 关系资源
POST /api/<associatedName>/<associatedKey>/<resourceName>?fields=col1,col2
values # JSON 格式
```
SDK
```ts
// 常规
api.resource('<resourceName>').create({
fields,
values
});
// 关系资源
api.resource('<associatedName>.<resourceName>').create({
associatedKey,
fields,
values,
});
```
### update
更新数据
API
```bash
# 常规
PUT /api/<resourceName>/<resourceKey>?fields=col1,col2
# 关系资源
PUT /api/<associatedName>/<associatedKey>/<resourceName>/<resourceKey>?fields=col1,col2
values # JSON 格式
```
SDK
```ts
// 常规
api.resource('<resourceName>').update({
resourceKey,
fields,
values,
});
// 关系资源
api.resource('<associatedName>.<resourceName>').update({
associatedKey,
resourceKey,
fields,
values,
});
```
### destroy
删除数据
API
```bash
# 常规
DELETE /api/<resourceName>/<resourceKey>
# 关系资源
DELETE /api/<associatedName>/<associatedKey>/<resourceName>/<resourceKey>
# 常规,通过 filter 参数
DELETE /api/<resourceName>?filter=
# 关系资源,通过 filter 参数
DELETE /api/<associatedName>/<associatedKey>/<resourceName>?filter=
```
SDK
```ts
// 常规
api.resource('<resourceName>').destroy({
resourceKey,
filter,
});
// 关系资源
api.resource('<associatedName>.<resourceName>').destroy({
associatedKey,
resourceKey,
filter,
});
```
### set
建立关联,旧关联会解除。此操作需要显式声明 actionName。
API
```bash
POST /api/<associatedName>/<associatedKey>/<resourceName>:set/<resourceKey>
values
```
SDK
```ts
api.resource('<associatedName>.<resourceName>').set({
associatedKey,
resourceKey,
values,
});
```
### add
关联的附加操作,此操作需要显式声明 actionName。
API
```bash
POST /api/<associatedName>/<associatedKey>/<resourceName>:add/<resourceKey>
values
```
SDK
```ts
api.resource('<associatedName>.<resourceName>').add({
associatedKey,
resourceKey,
values,
});
```
### remove
移除关联,此操作需要显式声明 actionName。
API
```bash
POST /api/<associatedName>/<associatedKey>/<resourceName>:remove/<resourceKey>
```
SDK
```ts
// 常规
api.resource('<resourceName>').remove({
resourceKey,
});
// 关系资源
api.resource('<associatedName>.<resourceName>').remove({
associatedKey,
resourceKey,
});
```
### toggle
API
```bash
POST /api/<associatedName>/<associatedKey>/<resourceName>:toggle/<resourceKey>
```
SDK
```ts
// 常规
api.resource('<resourceName>').list({
resourceKey,
fields,
});
// 关系资源
api.resource('<associatedName>.<resourceName>').list({
associatedKey,
resourceKey,
fields,
});
```
### sort
API
```bash
# 常规
GET /api/<resourceName>:sort/<resourceKey>
# 关系资源
GET /api/<associatedName>/<associatedKey>/<resourceName>:sort/<resourceKey>
```
SDK
```ts
// 常规
api.resource('<resourceName>').sort({
resourceKey,
values: {
field, // 默认为 sort 字段
target: {
id: 5,
},
},
});
// 关系资源
api.resource('<associatedName>.<resourceName>').list({
associatedKey,
resourceKey,
values: {
field, // 默认为 sort 字段
target: {
id: 5,
},
},
});
```
## Middlewares
### associated
注入 associated 实例,同时会提供 resourceField 字段。
<Alert title="resourceField 和 resourceName 的区别?" type="warning">
关系资源 `<associatedName>.<resourceName>`resourceName 并不一定是真实的 tableName而是 fieldName。resource 的 tableName 为 resourceField.target。
</Alert>
### dataWrapping
输出的 JSON 会用 data 包裹。
用法:
```ts
app.use(dataWrapping);
```
用例:
```ts
ctx.body = [];
```
最终输出的 response body 为:
```ts
{
data: [],
}
```

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +0,0 @@
---
title: '@nocobase/migrator'
order: 8
# toc: menu
---
# @nocobase/migrator <Badge>未实现</Badge>
NocoBase 的 Database 是基于 SequelizeSequelize 虽然提供了 sequelize-cli包括 migrations 和 seeders 但是并不适用于 NocoBase。
- Sequelize 的 migration 只有 queryInterface但是 NocoBase 考虑的更多是配置层面问题
- 要考虑 Upgrade 层面的问题,比如 0.3 升级 0.6、0.4 升级 0.6、0.5 升级 0.6 等等一系列问题
- 需要考虑插件里的 migrations
目前已知的开源项目里Ghost 团队实现的 [knex-migrator](https://github.com/TryGhost/knex-migrator) 非常接近我们的需求。Ghost 的 [migrations](https://github.com/TryGhost/Ghost/tree/v4.2.2/core/server/data/migrations) 例子。
虽然非常接近,但也存在一些差异:
- NocoBase 的 database 用的是 Sequelize 而不是 Knex
- NocoBase 的配置更复杂,我们要为此提供一些简易的 API 辅助完成 migrate

View File

@ -1,570 +0,0 @@
---
title: '@nocobase/resourcer'
order: 2
# toc: menu
---
# @nocobase/resourcer
## 介绍
提供数据资源操作方法,可单独使用。
基于资源resource和操作方法action设计将 REST 和 RPC 思想融合起来为资源提供操作方法执行方法时提供相关参数params
特点:
- 可注册的 Middlewares 和 Actions
- 提供灵活的多来源 Action Params 合并方案
- 不局限于 Koa 框架
- 不局限于 HTTP
<Alert title="何为多来源 Action Params" type="info">
以 filter 为例,某数据表的过滤条件可能来自:
- 客户端请求参数
- 视图限定了筛选范围
- 权限也可能限定了筛选范围
- 通过中间件注入定制化的过滤条件
resourcer 提供了非常方便的接口用于处理参数的合并(包括自定义合并或替换规则)
</Alert>
资源有五种类型:
- single 独立资源
- hasOne 一对一的关系资源
- hasMany 一对多的关系资源
- belongsTo 一对一的关系资源
- belongsToMany 多对多的关系资源
举例说明:
- users独立资源
- posts独立资源
- users.profile关系资源一对一关系 User.hasOne(Profile),资源为 profile属于 users
- posts.user关系资源多对一关系 Post.belongsTo(User),资源为 user属于 posts
- users.posts关系资源一对多关系 User.hasMany(Post),资源为 posts属于 users
- posts.tags关系资源多对多关系 Post.belongsToMany(Tag),资源为 tags属于 posts。
资源名称的格式:
- resourceName独立资源
- associatedName.resourceName关系资源
操作名称的格式:
- create全局操作
- resourceName:create隶属某个资源的操作
- associatedName.resourceName:create隶属某个关系资源的操作
这种资源和操作的格式,在 SDK 的协助下,看起来会更加直观,如:
```ts
api.resource('users').login({
// 省略具体参数
});
```
<Alert title="为什么不是 REST 或 GraphQL" type="info">
Resourcer 是基于资源设计的,可以看做是 REST 的扩展(兼容的同时,但有些许不同),提供了类 REST 的 Resource Action API[点此查看细节](#resourcerkoarestapimiddleware)),非常灵活,同时弥补了 REST 的一些缺陷。不用 GraphQL 的原因是因为还不够完善,尤其为了追求 GraphQL 需要写很多代码,并不适合作为常规 API 开放给大众。后续如果反应强烈会考虑适配 GraphQL其他感兴趣的开发也可以自由发挥来完善 GraphQL API。
</Alert>
## 安装
```bash
yarn add @nocobase/resourcer
```
## Usage
```ts
import Resourcer from '@nocobase/resourcer';
const resourcer = new Resourcer();
resourcer.registerActions({
async list(ctx, next) {
ctx.arr.push(3);
await next();
ctx.arr.push(4);
},
async create(ctx, next) {
ctx.arr.push(5);
await next();
ctx.arr.push(6);
},
});
resourcer.define({
name: 'users',
});
const context = {
arr: [],
};
await resourcer.execute({
resource: 'users',
action: 'list',
}, context);
console.log(context.arr);
// [1,3,4,2]
```
## API
### context.action.params
action.params 分为三类:
- 用于定位资源和操作的参数(这些暂时也放在 action.params 里了)
- actionName
- resourceName
- associatedName
- 定位资源 ID 相关参数
- resourceKey
- associatedKey
- request 的 query 和 body 相关参数
- filter
- fields
- sort
- page
- perPage
- values 对应为 request.body
- 其他 query params
#### actionName
资源操作名称
#### resourceName
资源名称
#### associatedName
所属资源名称
#### resourceKey
资源 ID
#### associatedKey
所属资源 ID
#### filter
过滤条件
#### fields
字段
#### sort
排序
#### page
分页
#### perPage
每页显示条数
#### values
body 数据
#### 其他自定义参数
待补充...
### context.action.mergeParams
为 action.params 提供的多来源参数合并的方法
```ts
action.mergeParams({
filter: {col1: 'val1'},
});
```
<Alert title="注意" type="warning">
后续可能会使用 [deepmerge](https://www.npmjs.com/package/deepmerge) 重构,以便处理更灵活的自定义合并规则。
</Alert>
### resourcer.define
配置资源
用例:
```ts
resourcer.define({
name: 'posts',
middlewares: [
async (ctx, next) => {
await next();
},
{
only: [],
except: [],
handler: async (ctx, next) => {
await next();
},
},
],
actions: {
async get(ctx, next) {
await next();
},
list: {
fields,
filter,
sort,
page,
perPage,
middlewares: [],
handler: async (ctx, next) => {
await next();
},
},
},
});
```
### resourcer.execute <Badge>实验性</Badge>
注入 context
### resourcer.import
批量导入已配置的资源
### resourcer.isDefined
判断资源是否已定义
### resourcer.koaRestApiMiddleware <Badge>待完善</Badge>
原 resourcer.middleware
为 Koa 提供的类 REST API 中间件。提供了标准的 REST API 映射Resource Action 与 Request Method 的对应关系如下:
| Resource Action | Request Method |
| :-------------- | :---------------------- |
| list | GET \<collection URL\> |
| get | GET \<resource URL\> |
| create | POST \<collection URL\> |
| update | PUT \<resource URL\> |
| destroy | DELETE \<resource URL\> |
标准的 REST API 映射下actionName 可以缺失,但也可以显式声明,当指定 actionName 时,将不受 Request Method 影响。相关 HTTP 格式如下:
```bash
# 独立资源
<requestMethod> /api/<resourceName>:<actionName>?<queryString>
<requestMethod> /api/<resourceName>:<actionName>/<resourceKey>?<queryString>
# 关系资源
<requestMethod> /api/<associatedName>/<associatedKey>/<resourceName>:<actionName>?<queryString>
<requestMethod> /api/<associatedName>/<associatedKey>/<resourceName>:<actionName>/<resourceKey>?<queryString>
<body> # 非 GET 请求时,可以提供 body 数据,一般为 JSON 格式
```
为了让大家更理解 Resource Action API 设计,我们接下来举几个具体的例子:
##### 查看文章列表
```bash
GET /api/posts?filter={"col1": "val1"}&fields=col1,col2&sort=-created_at
```
对应的 context.action.params 为:
```ts
{
actionName: 'list',
resourceName: 'posts',
filter: {'col1': 'val1'},
fields: ['col1', 'col2'],
sort: ['-created_at'],
}
```
##### 新增文章
```bash
POST /api/posts
{"title": "title1"}
```
对应的 context.action.params 为:
```ts
{
resourceName: 'posts',
actionName: 'create',
values: {
title: 'title1',
},
}
```
##### 查看文章详情
```bash
GET /api/posts/1?fields=col1,col2
```
对应的 context.action.params 为:
```ts
{
resourceName: 'posts',
resourceKey: 1,
actionName: 'get',
fields: ['col1', 'col2'],
}
```
##### 更新文章
```bash
PUT /api/posts/1
{"title": "title1"}
```
对应的 context.action.params 为:
```ts
{
resourceName: 'posts',
resourceKey: 1,
actionName: 'update',
values: {
title: 'title1',
},
}
```
##### 删除文章
```bash
DELETE /api/posts/1
```
对应的 context.action.params 为:
```ts
{
resourceName: 'posts',
resourceKey: 1,
actionName: 'destroy',
}
```
##### 文章评论列表
```bash
GET /api/posts/1/comments?filter={"col1": "val1"}&fields=col1,col2&sort=-created_at
```
对应的 context.action.params 为:
```ts
{
associatedName: 'posts',
associatedKey: 1,
resourceName: 'comments',
actionName: 'list',
filter: {'col1': 'val1'},
fields: ['col1', 'col2'],
sort: ['-created_at'],
}
```
##### 文章评论详情
```bash
GET /api/posts/1/comments/2
```
对应的 context.action.params 为:
```ts
{
associatedName: 'posts',
associatedKey: 1,
resourceName: 'comments',
resourceKey: 2,
actionName: 'get',
}
```
##### 显式声明 actionName 的例子
```bash
POST /api/users:login
{"username": "admin", "password": "password"}
```
对应的 context.action.params 为:
```ts
{
resourceName: 'users',
actionName: 'login',
values: {
username: 'admin',
password: 'password',
},
}
```
当不指定 actionName 时,默认为 create对应的是「新建用户」操作但是指定 actionName=login 时,就变为了「用户登录」操作了。
<Alert title="注意" type="warning">
在 Resource Action API 的设计理念里,即使类似 login、register、logout 等非标准的 REST API 也可以非常方便的扩展。大家可以更专注于 action 本身,而不必纠结于 request method 和 route 应该如何设计或派发,也不需要考虑 routes 优先级等问题。
</Alert>
### resourcer.registerAction(name: ActionName, options: ActionOptions) <Badge>待完善</Badge>
原 resourcer.registerActionHandler
- name操作名称
- options操作配置
可用于注册全局的或某资源特有的 action。
<Alert title="注意" type="warning">
与 resourcer.define 不同registerAction 的 actionName 支持三种格式:
- `<actionName>` 全局操作
- `<resourceName>:<actionName>` 某资源特有操作
- `<associatedName>.<resourceName>:<actionName>` 某关系资源特有操作
更复杂判断条件,需要结合 `resourcer.use` 方法一起处理
</Alert>
示例
```ts
resourcer.registerAction('actionName', async (ctx, next) => {
await next();
});
// 带配置
resourcer.registerAction('actionName', {
filter,
fields,
middlewares: [],
handler: async (ctx, next) => {
await next();
},
});
resourcer.registerAction('resourceName:actionName', async (ctx, next) => {
await next();
});
resourcer.registerAction('associatedName.resourceName:actionName', async (ctx, next) => {
await next();
});
```
<Alert title="注意" type="warning">
resourcer 使用 koa-compress 来处理中间件,是一种洋葱圈模型,因此在 action handler 里也不要忘了 `next()`,不然会影响后置逻辑的处理。
</Alert>
更复杂的情况:
```ts
resourcer.use(async (ctx, next) => {
const { actionName, resourceName } = ctx.action.params;
if (actionName === 'foo') {
// 其他判断条件
// 不符合条件的 404 处理
ctx.throw(404);
}
await next();
});
```
### resourcer.registerActions(actions) <Badge>待完善</Badge>
原 resourcer.registerActionHandlers
批量注册 actions用法同 [resourcer.registerAction](#resourcerregisterActionname-actionname-handler-handlertype)
示例:
```ts
resourcer.registerActions({
async foo(ctx, next) {
await next();
},
async bar(ctx, next) {
await next();
},
});
```
### resourcer.registerActionMiddleware(actionName, handler) <Badge>未实现</Badge>
为某操作action注册特有的 middleware
### resourcer.registerResourceMiddleware(resourceName, options) <Badge>未实现</Badge>
为某资源resource注册特有的 middleware
### resourcer.use
注册 resourcer 全局 middleware
```ts
resourcer.use(async (ctx, next) => {
// code...
await next();
})
```
<Alert title="为什么要提供不同的中间件注册方法?" type="warning">
虽然大部分框架都提供了中间件,但是中间件的执行顺序(优先级)依赖于编码顺序,这种方式非常不利于插件化管理。因此,在 Resourcer 设计思想里,将中间件做了分层,不同层级的 middlewares 不依赖于编码顺序,而是如下顺序:
1. 首先koa 层:`koa.use`
2. 其次resourcer 层:`resourcer.use`
3. 再次resource 层(每个资源独立):`resourcer.registerActionMiddleware`
4. 最后action 层:`resourcer.registerResourceMiddleware`
不过,每层的中间件执行顺序还依赖于编码顺序,如有需要再进行更细微的改进。
</Alert>

View File

@ -1,87 +0,0 @@
---
title: '@nocobase/sdk'
order: 5
# toc: menu
---
# @nocobase/sdk <Badge>待完善</Badge>
## 介绍
## 安装
```bash
yarn add @nocobase/sdk
```
## Usage
SDK
```ts
import API from '@nocobase/sdk';
const api = new API({
baseUrl: 'http://localhost:3000/api'
});
// 细节待补充
await api.resource('demos').list();
await api.resource('demos').create();
await api.resource('demos').get();
await api.resource('demos').update();
await api.resource('demos').destroy();
```
HTTP API
```bash
GET http://localhost:3000/api/demos
POST http://localhost:3000/api/demos
GET http://localhost:3000/api/demos/1
PUT http://localhost:3000/api/demos/1
DELETE http://localhost:3000/api/demos/1
```
## API
```bash
# 独立资源
<requestMethod> /api/<resourceName>:<actionName>?<queryString>
<requestMethod> /api/<resourceName>:<actionName>/<resourceKey>?<queryString>
# 关系资源
<requestMethod> /api/<associatedName>/<associatedKey>/<resourceName>:<actionName>?<queryString>
<requestMethod> /api/<associatedName>/<associatedKey>/<resourceName>:<actionName>/<resourceKey>?<queryString>
<body> # 非 GET 请求时,可以提供 body 数据,一般为 JSON 格式
```
## SDK
与 context.action.params 参数大体一致:
```ts
api.resource('<resourceName>').<actionName>({
resourceKey,
filter,
fields,
sort,
page,
perPage,
values,
});
// 关系资源
api.resource('<associatedName>.<resourceName>').<actionName>({
associatedKey,
resourceKey,
filter,
fields,
sort,
page,
perPage,
values,
});
```

View File

@ -1,146 +0,0 @@
---
title: '@nocobase/server'
order: 5
# toc: menu
---
# @nocobase/server
## 介绍
提供最小核心的 NocoBase 服务
## 安装
```bash
yarn add @nocobase/server
```
## Usage
```ts
import { Application } from '@nocobase/server';
const api = new Application({
database: {},
resourcer: {},
});
// 配置数据表
api.database.table({
name: 'demos',
fields: [
{ type: 'string', name: 'name' },
],
});
await api.database.sync();
app.listen(3000);
```
HTTP API
```bash
GET http://localhost:3000/api/demos
POST http://localhost:3000/api/demos
GET http://localhost:3000/api/demos/1
PUT http://localhost:3000/api/demos/1
DELETE http://localhost:3000/api/demos/1
```
SDK
```ts
import API from '@nocobase/sdk';
const api = new API({
baseUrl: 'http://localhost:3000/api'
});
// 细节待定
api.resource('demos').list();
api.resource('demos').create();
api.resource('demos').get();
api.resource('demos').update();
api.resource('demos').destroy();
```
## Middlewares
### initializeActionParams
初始化 action.params
### appDistServe
为 app dist 提供静态文件代理服务
### dbResourceRouter
resource 动态初始化,如果 resource 不存在,从 database 里同步。
<Alert title="注意" type="warning">
与 resourcer.koaRestApiMiddleware 方法存在大量重复,需要把 database 与 resource 的同步逻辑提炼出来
</Alert>
### demoBlacklistedActions
actions 黑名单
```ts
app.use(demoBlacklistedActions({
blacklist: [],
}));
```
## API
### Server
Server 继承 Koa更多用法可查阅 Koa API
#### server.constructor
初始化 server 实例
#### server.database
当前 server 实例的 database
#### server.resourcer
当前 server 实例的 resourcer
#### server.pluginManager <Badge>未实现</Badge>
当前 server 实例的 pluginManager
### PluginManager <Badge>未实现</Badge>
插件管理器
<Alert title="注意" type="warning">
不同 server 实例也可能需要 PluginManager后续 CLI 和后台可管理也都需要,插件管理器独立出来处理比较合适。
</Alert>
#### pluginManager.register(name, options)
注册插件
#### pluginManager.has(name)
判断插件是否存在
#### pluginManager.get(name)
获取当前插件实例
#### pluginManager.load()
加载插件
#### pluginManager.reload()
重载插件

View File

@ -1,72 +0,0 @@
---
title: '@nocobase/test'
order: 7
---
# @nocobase/test <Badge>未实现</Badge>
## mockDatabase
为 database 提供的测试套件,同时提供数据 mock使用 mockjs
```ts
import { mockDatabase } from '@nocobase/test';
describe('test', () => {
let db;
beforeEach(async () => {
db = mockDatabase({});
db.table({
name: 'examples',
fields: [{
name: 'name',
type: 'string',
mock: {
"1-10": "★"
},
}],
});
await db.sync();
});
afterEach(async () => {
await db.close();
});
it('test model', () => {
const Test = db.getModel('tests');
Test.mockCreate({});
Test.mockBulkCreate([{}]);
});
});
```
## mockServer
为 server 提供的测试套件
```ts
import { mockServer } from '@nocobase/test';
describe('test', () => {
let api;
beforeEach(async () => {
api = mockServer({});
await api.database.sync();
});
afterEach(async () => {
await api.database.close();
});
it('test resource', () => {
await api.resource('demos').get();
});
it('test request', () => {
await api.request().get('/');
});
});
```

View File

@ -1,13 +0,0 @@
---
title: '@nocobase/ui-schema'
order: 4
---
# @nocobase/ui-schema <Badge>未实现</Badge>
v0.5 前端完全重构了,减少了对 umijs 的直接依赖,前端后续会拆分成四部分
- ui-schema 核心组件库
- ui-router 前端路由
- sdk 供客户端使用
- create-nocobase-app 项目脚手架,基于 umijs

View File

@ -1,88 +0,0 @@
---
title: WEB API 设计
order: 2
toc: menu
nav:
title: 核心
order: 2
---
# WEB API 设计
<Alert title="注意" type="warning">
以下各代码片段,仅供阅读参考,可能与实际代码略有偏差。
</Alert>
和常规的 MVC 思路不同NocoBase 的 Server 非常简单,先来一段简单例子代码感受一下:
```ts
import { Application } from '@nocobase/server';
const api = new Application({
database: {},
resourcer: {},
});
// 配置数据表
api.database.table({
name: 'users',
fields: [
{ type: 'string', name: 'username' },
{ type: 'password', name: 'password' },
],
});
api.listen(3000);
```
相对应的 HTTP API
```bash
GET http://localhost:3000/api/users
POST http://localhost:3000/api/users
GET http://localhost:3000/api/users/1
PUT http://localhost:3000/api/users/1
DELETE http://localhost:3000/api/users/1
```
内置了基础的 REST API除此之外还可以自行扩展
```ts
api.resourcer.registerAction('users:login', async (ctx, next) => {
// 代码省略
});
```
HTTP API 新增了扩展
```bash
POST http://localhost:3000/api/users:login
```
配合 SDK 就是这样的了
```ts
// 常用的 REST API
// GET http://localhost:3000/api/users
api.resource('users').list();
// POST http://localhost:3000/api/users
api.resource('users').create();
// GET http://localhost:3000/api/users/1
api.resource('users').get();
// PUT http://localhost:3000/api/users/1
api.resource('users').update();
// DELETE http://localhost:3000/api/users/1
api.resource('users').destroy();
// 扩展的非 REST 风格的 API
// POST http://localhost:3000/api/users:login
api.resource('users').login();
// POST http://localhost:3000/api/users:register
api.resource('users').register();
// POST http://localhost:3000/api/users:logout
api.resource('users').logout();
// POST http://localhost:3000/api/users:export
api.resource('users').export();
```

View File

@ -1,118 +0,0 @@
---
title: 配置文件
order: 3
toc: menu
---
# 配置文件
<Alert title="注意" type="warning">
暂时只支持 <code>.env</code> 环境变量配置,这种方式有些局限,接下来会提供更完整的 <code>.nocobaserc.ts</code> 文件,用于支持更灵活的配置,包括但不局限于数据库信息、插件等。
</Alert>
## .env
### 数据库信息
<Alert title="注意" type="warning">
暂时只提供了核心的一些配置参数,后续还需优化
</Alert>
#### DB_DIALECT
#### DB_HOST
#### DB_PORT
#### DB_DATABASE
#### DB_USER
#### DB_PASSWORD
#### DB_LOG_SQL
### APP 信息
<Alert title="注意" type="warning">
暂时只提供了核心的一些配置参数,后续还需优化
</Alert>
#### NOCOBASE_ENV
NOCOBASE 环境
#### API_PREFIX
API 前缀
#### API_PORT
API 端口
#### APP_DIST
APP 前端静态文件路径
#### APP_USE_STATIC_SERVER
APP 前端静态文件是否使用 koa-static 代理。如不需要,你也可以使用 nginx 等服务。
### ADMIN 账号
<Alert title="注意" type="warning">
仅用于初始化配置,后续会集成到 cli 里,安装时用户可输入。
</Alert>
#### ADMIN_EMAIL
初始化的管理员邮箱
#### ADMIN_PASSWORD
初始化的管理员密码
### 文件管理器插件
<Alert title="注意" type="warning">
仅用于初始化,目前还未提供插件管理面板,如果有修改,需要去 storages 表里修改。后续优化会集成到 cli 里,在插件安装时由用户输入,或通过插件管理器修改。
</Alert>
#### STORAGE_TYPE
目前已支持的 storage 有:
- `local` 本地
- `ali-oss` 阿里云 OSS
#### LOCAL_STORAGE_USE_STATIC_SERVER
文件静态文件是否使用 koa-static 代理。如不需要,你也可以使用 nginx 等服务。
#### LOCAL_STORAGE_BASE_URL
静态文件地址前缀
#### ALI_OSS_REGION
#### ALI_OSS_ACCESS_KEY_ID
#### ALI_OSS_ACCESS_KEY_SECRET
#### ALI_OSS_BUCKET
#### ALI_OSS_STORAGE_BASE_URL
## .nocobaserc.ts
<Alert title="注意" type="warning">
暂不支持 .nocobaserc 配置
</Alert>
### 数据库
待补充...
### APP 信息
待补充...
### 配置插件
待补充...

View File

@ -1,196 +0,0 @@
---
title: 快速上手
order: 2
toc: menu
---
# 快速上手
为大家提供了三种方式安装并运行 NocoBase
- [Npm & Yarn](#npm--yarn):仅用于无代码平台体验
- [Docker](#docker):仅用于无代码平台体验
- [Git](#git):参与项目开发
<Alert title="安装前准备" type="warning">
**OS**
- Docker
- Linux
- MacOS仅开发环境
- Windows仅开发环境没有在 win 平台测试过,可能存在一些小问题
**Node**
- Node.js 12.x or 14.x
**Database任选其一**
现阶段还是以关系型数据库为主,接下来陆续兼容更多的关系型数据库。未来 @nocobase/database 稳定之后,会考虑适配 MongoDB。因为有几处用到了 JSON 类型字段,不同数据库的 JSON 字段有差异,现只兼容了 PostgreSQL 数据库的查询。
- PostgreSQL 10.x+(推荐)
- MySQL 5.7.x+JSON 类型查询有问题,后续会提供支持)
- MariaDB 10.x未知后续会提供支持
- SQLite 3未知后续会提供支持
</Alert>
## Npm & Yarn
创建并进入新目录
```bash
mkdir my-nocobase-project && cd my-nocobase-project
```
使用 yarn 或 npm 包管理器进行初始化
```bash
yarn init # or npm init
```
安装 NocoBase 依赖
```bash
yarn add @nocobase/api @nocobase/app
```
复制并配置 .env[环境变量说明点此查看](config.md#.env)
```bash
cp -r node_modules/@nocobase/api/.env.example .env
```
数据库初始化
```bash
yarn nocobase db-init
```
启动应用
```bash
yarn nocobase start
```
<Alert title="注意" type="warning">
为什么是 @nocobase/api@nocobase/app,而不是 create-nocobase-project
早之前想提供 create-nocobase-project 的,但是有些细节还没想清楚,暂时不会提供,目前只提供了封装度较高的 @nocobase/api@nocobase/app 两个前后端包,如果有二次开发需要,可 fork 相关 api 或 app 源码,再重新构建。
</Alert>
## Docker
<Alert title="注意" type="warning">
Docker 镜像暂未发布
</Alert>
为了更方便的安装与部署NocoBase 也发布了 Docker 镜像 `nocobase/nocobase`,创建并配置 `docker-compose.yml` 文件,样例如下:
```yaml
version: "3"
services:
postgres:
image: postgres:10
networks:
- nocobase
environment:
POSTGRES_USER: nocobase
POSTGRES_DB: nocobase
POSTGRES_PASSWORD: nocobase
nocobase:
image: nocobase/nocobase:latest
networks:
- nocobase
ports:
- 23000:23000
environment:
DB_DIALECT: postgres
DB_HOST: postgres
DB_PORT: 5432
DB_DATABASE: nocobase
DB_USER: nocobase
DB_PASSWORD: nocobase
API_PORT: 23000
ADMIN_EMAIL: admin@nocobase.com
ADMIN_PASSWORD: admin
networks:
nocobase:
driver: bridge
```
初始化并启动 NocoBase 应用
```bash
docker-compose up -d
```
## Git
通过 git 克隆 nocobase 仓库
```bash
git clone https://github.com/nocobase/nocobase.git my-nocobase-project
```
进入项目目录
```bash
cd my-nocobase-project
```
复制并配置 .env[环境变量说明点此查看](#)
```bash
cp .env.example .env
```
安装依赖
```bash
yarn install
```
构建 packages 依赖
```bash
yarn bootstrap
yarn build
```
数据库初始化
```bash
yarn db-migrate init
```
启动应用
```bash
yarn start
```
开发过程中其他常用的命令还有:
```bash
### 测试 ###
yarn test
# or
yarn test packages/<name> packages/<name>
### 打包 ###
yarn build
# or
yarn build <package_name_1> <package_name_2> ...
```

View File

@ -1,49 +0,0 @@
---
title: 介绍
order: 1
toc: menu
nav:
title: 指南
order: 1
---
## NocoBase 是什么?
NocoBase 是一个开源免费的无代码、低代码开发平台。 无论是不懂编程的业务主管,还是精通编程的开发人员,都可以快速搭建各类定制化、私有部署的协作平台、管理系统。
## 架构
<img src="../nocobase.png" style="max-width: 800px;width:100%"/>
### 微内核
NocoBase 采用微内核架构,框架只保留核心的概念,具体各类功能都以插件的形式扩展。各个包可以拆出来单独或组合使用,也可用于现有项目中。
### 插件化
所有的功能需求都通过插件形式扩展,除了现有的几个核心插件以外,开发者还可以自由的扩展,包括但不局限于:
- Collection - 数据表
- CollectionField - 字段
- DataType - 存储类型
- UI Schema/Component - 组件
- Template - 模板
- Model/Repository - 模型
- Schema Builder - 构造器
- Resource/Service - 资源/服务
- Action - 方法
- Hook/Event - 事件
- Middleware - 中间件
- Resource Middleware
- Action Middleware
- UI
- UI Schema/Component - 前端组件
- Router - 前端路由
### 配置化
配置化驱动,为了方便各场景配置需求,配置有三种写法:
- 直接写在代码里,多用于处理动态配置
- 保存在文件里,多用于系统表配置或纯开发配置
- 保存在数据表里,多用于业务表配置

View File

@ -1,20 +1,563 @@
---
title: NocoBase - An open source and free no-code development platform
hero:
title: NocoBase
desc: An open source and free no-code development platform
actions:
- text: Getting Started
link: /guide
features:
- icon: https://gw.alipayobjects.com/zos/bmw-prod/881dc458-f20b-407b-947a-95104b5ec82b/k79dm8ih_w144_h144.png
title: Feature 1
desc: Balabala
- icon: https://gw.alipayobjects.com/zos/bmw-prod/d60657df-0822-4631-9d7c-e7a869c2f21c/k79dmz3q_w126_h126.png
title: Feature 2
desc: Balabala
- icon: https://gw.alipayobjects.com/zos/bmw-prod/d1ee0c6f-5aed-4a45-a507-339a4bfe076c/k7bjsocq_w144_h144.png
title: Feature 3
desc: Balabala
footer: Copyright © 2020-2021 NocoBase. All rights reserved.
title: 基础概念
toc: menu
---
# NocoBase
考虑到大家是初次接触 NocoBase开发文档的第一篇从宏观的角度带大家了解 NocoBase 的基础概念。NocoBase 采用微内核的架构,框架只保留核心概念,各类功能都以插件形式扩展。
## 微服务 - Microservices
先来个例子一睹为快,新建一个 server.js 文件,代码如下:
```ts
const Server = require('@nocobase/server');
const server = new Server();
server.collection({
name: 'users',
fields: [
{ type: 'string', name: 'username' },
{ type: 'password', name: 'password' },
],
});
server.start(process.argv);
```
终端运行
```bash
# 根据配置生成数据库表结构
node server.js db sync
# 启动应用
node server.js start --port=3000
```
相关 users 表的 REST API 就生成了
```bash
GET http://localhost:3000/api/users
POST http://localhost:3000/api/users
GET http://localhost:3000/api/users/1
PUT http://localhost:3000/api/users/1
DELETE http://localhost:3000/api/users/1
```
除了内置的 REST API 以外,还可以自定义其他操作,如登录、注册、注销等。
```ts
server.registerActions({
async login(ctx, next) {},
async register(ctx, next) {},
async logout(ctx, next) {},
}, {
resourceName: 'users',
});
```
以上操作的 HTTP API 为:
```bash
POST http://localhost:3000/api/users:login
POST http://localhost:3000/api/users:register
POST http://localhost:3000/api/users:logout
```
自定义的 HTTP API 依旧保持 REST API 的风格,以 `<resourceName>:<actionName>` 格式表示。REST API 也可以显式指定 actionName当指定了 actionName 时,无所谓使用什么 Request Method
```bash
GET http://localhost:3000/api/users:list
POST http://localhost:3000/api/users:create
GET http://localhost:3000/api/users:get/1
POST http://localhost:3000/api/users:update/1
POST http://localhost:3000/api/users:destroy/1
```
结合客户端 SDK 是这样的:
```ts
const { ClientSDK } = require('@nocobase/client');
const client = new ClientSDK();
await client.resource('users').list();
await client.resource('users').create();
await client.resource('users').get();
await client.resource('users').update();
await client.resource('users').destroy();
await client.resource('users').login();
await client.resource('users').register();
await client.resource('users').logout();
```
## 数据集 - Collection
上述例子,通过 `server.collection()` 方法定义数据的 SchemaSchema 的核心为字段配置,字段类型包括:
Attribute 属性
- Boolean 布尔型
- String 字符串
- Text 长文本
- Integer 整数型
- Float 浮点型
- Decimal 货币
- Json/Jsonb/Array 不同数据库的 JSON 类型不一致,存在兼容性问题
- Time 时间
- Date 日期
- Virtual 虚拟字段
- Reference 引用
- Formula 计算公式
- Context 上下文
- Password 密码
- Sort 排序
Association/Realtion 关系
- HasOne 一对一
- HasMany 一对多
- BelongsTo 多对一
- BelongsToMany 多对多
- Polymorphic 多态
比如一个微型博客的表结构可以这样设计:
```ts
// 用户
server.collection({
name: 'users',
fields: [
{ type: 'string', name: 'username', unique: true },
{ type: 'password', name: 'password', unique: true },
{ type: 'hasMany', name: 'posts' },
],
});
// 文章
server.collection({
name: 'posts',
fields: [
{ type: 'string', name: 'title' },
{ type: 'text', name: 'content' },
{ type: 'belongsToMany', name: 'tags' },
{ type: 'hasMany', name: 'comments' },
{ type: 'belongsTo', name: 'author', target: 'users' },
],
});
// 标签
server.collection({
name: 'tags',
fields: [
{ type: 'string', name: 'name' },
{ type: 'belongsToMany', name: 'posts' },
],
});
// 评论
server.collection({
name: 'comments',
fields: [
{ type: 'text', name: 'content' },
{ type: 'belongsTo', name: 'user' },
],
});
```
存在外键关联时,也无需顾虑建表和字段的顺序,`db sync` 时会自动处理。为了方便开发,提供了一些有用的属性或方法:
- `server.db` 数据库实例
- `server.db.getModel()` 获取 Model
- `server.db.getTable()` 获取 Schema Table
## 资源 & 操作 - Resource & Action
不同于常规的 MVC + RouterNocoBase 的路由Resourcer基于资源Resource和操作Action设计将 REST 和 RPC 结合起来,提供更为灵活且统一的 Resource Action APIAction 不局限于增删改查。资源可以通过 `server.resource` 方法定义,如:
```ts
server.resource({
name: 'users',
actions: {
list: {
fields: ['id', 'username'], // 只输出 id 和 username 字段
filter: {
'username.$ne': 'admin', // 数据范围筛选过滤 username != admin
},
sort: ['-created_at'], // 创建时间倒序
perPage: 50,
},
get: {
fields: ['id', 'username'], // 只输出 id 和 username 字段
filter: {
'username.$ne': 'admin', // 数据范围筛选过滤 username != admin
},
},
create: {
fields: ['username'], // 白名单
},
update: {
fields: ['username'], // 白名单
},
destroy: {
filter: { // 不能删除 admin
'username.$ne': 'admin',
},
},
},
});
```
`server.collection()``server.resource()` 的区别?
- `server.collection()` 定义数据的 Schema结构和关系
- `server.resource()` 定义数据的 Action操作方法
一般情况无需显式声明 collection 的 resource因为已定义的 collection 会自动同步给 resource。
## 事件 - Event
在操作执行前、后都放置了相关的事件监听器,可以通过 `server.db.on``server.on` 添加。区别在于:
- `server.db.on` 添加数据库的监听器
- `server.on` 添加服务器的监听器
`users:login` 为例,在数据库里为「查询」操作,在服务器里为「登录」操作。如果需要记录登录操作日志,需要在 `server.on` 里处理。
```ts
// 创建数据时,执行 User.create() 时触发
server.db.on('users:beforeCreate', async (model) => {});
// 客户端 `POST /api/users:login` 时触发
server.on('users:beforeLogin', async (ctx, next) => {});
// 客户端 `POST /api/users` 时触发
server.on('users:beforeCreate', async (ctx, next) => {});
```
## 中间件 - Middleware
Server 基于 Koa所有 Koa 的插件(中间件)都可以直接使用,可以通过 server.use 添加。如:
```ts
server.use(async (ctx, next) => {
const token = ctx.get('Authorization').replace(/^Bearer\s+/gi, '');
if (token !== '123456') {
return ctx.throw(401, 'Unauthorized');
}
return next();
});
```
弥补 koa.use 的不足,提供了更完善的 middleware 适配器
```ts
import { middleware } from '@nocobase/server';
server.use(middleware(async (ctx, next) => {}, {
name: 'middlewareName1',
resourceNames: [],
actionNames: [],
insertBefore: '',
insertAfter: '',
}));
```
## 命令行 - CLI
除此之外Server 还集成了 commander可用于 cli 场景。目前内置的有:
- `db sync --force` 用于配置与数据库表结构同步
- `start --port` 启动应用
自定义:
```ts
server.command('foo').action(async () => {
console.log('foo...');
});
```
## 插件 - Plugin
上文,讲述了核心的扩展接口,包括但不局限于:
- Database/Collection
- `server.db` database 实例
- `server.collection()` 等同于 `server.db.table()`
- Resource/Action
- `server.resource()` 等同于 `server.resourcer.define()`
- `server.registerActions()` 等同于 `server.resourcer.registerActions()`
- Hook/Event
- `server.on()` 添加服务器监听器
- `server.db.on()` 添加数据库监听器
- Middleware
- `server.use()` 添加中间件
- CLI
- `server.cli` commander 实例
- `server.command()` 等同于 `server.cli.command()`
- Plugin
- `server.pluginManager` 插件管理器
- `server.plugin` 等同于 `server.pluginManager.add()`
基于以上扩展接口,提供模块化、可插拔的插件,可以通过 `server.plugin()` 添加。
**最简单的插件**
```ts
server.plugin(function pluginName1() {
});
```
**JSON 风格**
包括安装、激活、载入、禁用、卸载流程的配置
```ts
server.plugin({
async install() {},
async activate() {},
async bootstrap() {},
async deactivate() {},
async unstall() {},
}, {
name: 'pluginName1',
displayName: '插件名称',
version: '1.2.3',
dependencies: {
pluginName2: '1.x',
pluginName3: '1.x',
},
});
```
**OOP 风格**
```ts
class MyPlugin extends Plugin {
async bootstrap() {}
async install() {}
async unstall() {}
async activate() {}
async deactivate() {}
}
server.plugin(MyPlugin, {
name: 'pluginName1',
displayName: '插件名称',
version: '1.2.3',
dependencies: {
pluginName2: '1.x',
pluginName3: '1.x',
},
});
```
**引用独立的 Package**
```ts
server.plugin('@nocobase/plugin-action-logs');
```
插件信息也可以直接写在 `package.json`
```js
{
name: 'pluginName1',
displayName: '插件名称',
version: '1.2.3',
dependencies: {
pluginName2: '1.x',
pluginName3: '1.x',
},
}
```
通过 `server.plugin()` 添加的插件需要激活才能使用
```ts
await server.pluginManager.activate(['pluginName1', 'pluginName2']);
```
**插件 CLI**
```bash
plugin install pluginName1
plugin unstall pluginName1
plugin activate pluginName1
plugin deactivate pluginName1
```
目前已有的插件:
- @nocobase/plugin-collections 提供数据表配置接口,可通过 HTTP API 管理数据表。
- @nocobase/plugin-action-logs 操作日志
- @nocobase/plugin-automations 自动化(未升级 v0.5,暂不能使用)
- @nocobase/plugin-china-region 中国行政区
- @nocobase/plugin-client 提供客户端,无代码的可视化配置界面,需要与 @nocobase/client 配合使用
- @nocobase/plugin-export 导出
- @nocobase/plugin-file-manager 文件管理器
- @nocobase/plugin-permissions 角色和权限
- @nocobase/plugin-system-settings 系统配置
- @nocobase/plugin-ui-router 前端路由配置
- @nocobase/plugin-ui-schema ui 配置
- @nocobase/plugin-users 用户模块
## 测试 - Testing
有代码就需要测试,@nocobase/test 提供了 mockDatabase 和 mockServer 用于数据库和服务器的测试,如:
```ts
import { mockServer, MockServer } from '@nocobase/test';
describe('mock server', () => {
let api: MockServer;
beforeEach(() => {
api = mockServer({
dataWrapping: false,
});
api.registerActions({
list: async (ctx, next) => {
ctx.body = [1, 2];
await next();
},
});
api.resource({
name: 'test',
});
});
afterEach(async () => {
return api.destroy();
});
it('agent.get', async () => {
const response = await api.agent().get('/test');
expect(response.body).toEqual([1, 2]);
});
it('agent.resource', async () => {
const response = await api.agent().resource('test').list();
expect(response.body).toEqual([1, 2]);
});
});
```
## 客户端 - Client
为了让更多非开发人员也能参与进来NocoBase 提供了配套的客户端插件 —— 无代码的可视化配置界面。客户端插件需要与 @nocobase/client 配合使用,可以直接使用,也可以自行改造。
插件配置
```ts
server.plugin('@nocobase/plugin-client', {
// 自定义 dist 路径
dist: path.resolve(__dirname, './node_modules/@nocobase/client/app'),
});
```
为了满足各类场景需求,客户端 `@nocobase/client` 提供了丰富的基础组件:
- Action - 操作
- Action.Window 当前浏览器窗口/标签里打开
- Action.Drawer 打开抽屉(默认右侧划出)
- Action.Modal 打开对话框
- Action.Dropdown 下拉菜单
- Action.Popover 气泡卡片
- Action.Group 按钮分组
- Action.Bar 操作栏
- AddNew 「添加」模块
- AddNew.CardItem - 添加区块
- AddNew.PaneItem - 添加区块(查看面板,与当前查看的数据相关)
- AddNew.FormItem - 添加字段
- BlockItem/CardItem/FormItem - 装饰器
- BlockItem - 普通装饰器(无包装效果)
- CardItem - 卡片装饰器
- FormItem - 字段装饰器
- Calendar - 日历
- Cascader - 级联选择
- Chart - 图表
- Checkbox - 勾选
- Checkbox.Group - 多选框
- Collection - 数据表配置
- Collection.Field - 数据表字段
- ColorSelect - 颜色选择器
- DatePicker - 日期选择器
- DesignableBar - 配置工具栏
- Filter - 筛选器
- Form - 表单
- Grid - 栅格布局
- IconPicker - 图标选择器
- Input - 输入框
- Input.TextArea - 多行输入框
- InputNumber - 数字框
- Kanban - 看板
- ListPicker - 列表选择器(用于选择、展示关联数据)
- Markdown 编辑器
- Menu - 菜单
- Password - 密码
- Radio - 单选框
- Select - 选择器
- Table - 表格
- Tabs - 标签页
- TimePicker - 时间选择器
- Upload - 上传
开发们也可以自行扩展,以上组件基于 Formily 构建,怎么自定义组件大家查看相关组件源码或 Formily 文档吧,这里说点不一样的。
- 如何扩展数据库字段?
- 如何将第三方区块添加到 AddNew 模块中?
- 如何在操作栏里添加更多的内置操作?
- 如何自定义配置工具栏?
除了组件具备灵活的扩展以外,客户端也可以在任意前端框架中使用,可以自定义 Request 和 Router
<pre lang="tsx">
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { ClientSDK, Application } from '@nocobase/client';
// 初始化 client 实例
const client = new ClientSDK({
request: (options) => Promise.resolve({}),
});
// 适配 Route Component
const RouteSwitch = createRouteSwitch({
components: {
AdminLayout,
AuthLayout,
RouteSchemaRenderer,
},
});
ReactDOM.render(
<ClientProvider client={client}>
<MemoryRouter initialEntries={['/admin']}>
<RouteSwitch routes={[]}/>
</MemoryRouter>
</ClientProvider>,
document.getElementById('root'),
);
</pre>
更多细节,可以通过 create-nocobase-app 初始化项目脚手架并体验。
```bash
yarn create nocobase-app my-nocobase-project
```
nocobase-app 默认使用 umijs 作为项目构建工具,并集成了 Server 作数据接口,初始化的目录结构如下:
```bash
|- src
|- pages
|- apis
|- .env
|- .umirc.ts
|- package.json
```

View File

@ -1,20 +0,0 @@
---
title: NocoBase - An open source and free no-code development platform
hero:
title: NocoBase
desc: An open source and free no-code development platform
actions:
- text: 快速上手
link: /zh-CN/guide
features:
- icon: https://gw.alipayobjects.com/zos/bmw-prod/881dc458-f20b-407b-947a-95104b5ec82b/k79dm8ih_w144_h144.png
title: 特性 1
desc: Balabala
- icon: https://gw.alipayobjects.com/zos/bmw-prod/d60657df-0822-4631-9d7c-e7a869c2f21c/k79dmz3q_w126_h126.png
title: 特性 2
desc: Balabala
- icon: https://gw.alipayobjects.com/zos/bmw-prod/d1ee0c6f-5aed-4a45-a507-339a4bfe076c/k7bjsocq_w144_h144.png
title: 特性 3
desc: Balabala
footer: Copyright © 2020-2021 NocoBase. All rights reserved.
---

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 KiB

View File

@ -1,24 +0,0 @@
---
title: Actions - 操作方法
group:
order: 2
title: 概念
---
# Actions - 操作方法
与 resourcer.registerAction 用法一致
```ts
export async function get(ctx, next) {
await next();
}
export const list = {
filter,
fields, // 初始化的参数
async handler(ctx, next) {
await next();
}
}
```

View File

@ -1,5 +0,0 @@
---
title: Collection Fields - 数据表字段
---
# Collection Fields - 数据表字段

View File

@ -1,25 +0,0 @@
---
title: Collections - 数据集
---
# Collections - 数据集
与 database.table 用法一致
```ts
export default {
name: 'examples',
fields: [],
}
```
配置扩展
```ts
import { extend } from '@nocobase/database';
export default extend({
name: 'examples',
fields: [],
});
```

View File

@ -1,9 +0,0 @@
---
title: Hooks - 钩子
---
# Hooks - 钩子
目前支持的 hook 有两类 database.addHook 和 Model.addHook
未完待续...

View File

@ -1,5 +0,0 @@
---
title: Locales - 国际化
---
# Locales - 国际化 <Badge>未实现</Badge>

View File

@ -1,9 +0,0 @@
---
title: Middlewares - 中间件
---
# Middlewares - 中间件
## koa
## resourcer
## action

View File

@ -1,5 +0,0 @@
---
title: Migrations - 迁移
---
# Migrations - 迁移

View File

@ -1,33 +0,0 @@
---
title: Models - 模型
---
# Models - 模型
将 models 统一放在 `/src/models`、`/lib/models` 目录下,将自动导入 database。
database.table 提供的数据表配置支持指定特殊 model
```ts
import { Model } from '@nocobase/database';
class Test extends Model {
// 在这个类里可以为 Test Model 扩展其他 API
static hello() {
}
}
export default {
name: 'tests',
model: Test,
};
```
调用 Model
```ts
const Test = db.getModel('tests');
// Test 可以调用 hello 方法了
Test.hello();
```

View File

@ -1,7 +0,0 @@
---
title: Resources - 资源
---
# Resources - 资源
用法同 resourcer.define

View File

@ -1,5 +0,0 @@
---
title: UI Schema - 组件
---
# UI Schema - 组件

View File

@ -1,17 +0,0 @@
---
title: '@nocobase/plugin-action-logs'
order: 5
group:
order: 3
title: 官方插件
---
# @nocobase/plugin-action-logs
操作记录
## 安装
```bash
yarn nocobase pull action-logs --start
```

View File

@ -1,101 +0,0 @@
---
title: '@nocobase/plugin-automations'
order: 4
---
# @nocobase/plugin-automations
提供自动化模块
## 安装
```bash
yarn nocobase pull automations --start
```
## 用例
```ts
// 省略上文
database.table({
name: 'tests',
fields: [
{
type: 'string',
name: 'name1',
},
{
type: 'string',
name: 'name2',
},
],
});
database.table({
name: 'demos',
fields: [
{
type: 'string',
name: 'col1',
},
{
type: 'string',
name: 'col2',
},
],
});
const [Automation, Test] = database.getModels(['automations', 'tests']);
const automation = await Automation.create({
title: 'a1',
enabled: true,
type: 'collections:afterCreate',
collection_name: 'tests',
});
automation.startJob('test', async (result, options) => {
// job 代码
});
// 使用内置的 job
await automation.updateAssociations({
jobs: [
{
title: 'j1',
enabled: true,
type: 'create',
collection_name: 'demos',
values: [
{
column: 'col1',
op: 'eq',
value: 'n1'
},
{
column: 'col2',
op: 'ref',
value: 'name2'
},
],
}
],
});
// tests 表新增数据会触发上面执行 job 任务
await Test.create({
name1: 'n11',
name2: 'n22',
});
```
## Model API
### Automation.load()
### automation.loadJobs()
### automation.startJob(jobName: string, callback: any)
### automation.cancelJob(jobName: string)
### Job.bootstrap()
### Job.process(result?: any, options?: any)
### Job.cancel()

View File

@ -1,5 +0,0 @@
---
title: '@nocobase/plugin-china-region'
---
# @nocobase/plugin-china-region

View File

@ -1,14 +0,0 @@
---
title: '@nocobase/plugin-collections'
order: 1
---
# @nocobase/plugin-collections
提供数据表配置接口,可通过 HTTP API 管理数据表。
## 安装
```bash
yarn nocobase pull collections --start
```

View File

@ -1,27 +0,0 @@
---
title: '@nocobase/plugin-export'
---
# @nocobase/plugin-export
提供导出功能
<Alert title="注意" type="warning">
暂时只支持 excel 导出
</Alert>
## 安装
```bash
yarn nocobase pull export --start
```
## Action API
### export
参数和 list 一致,暂时只支持 excel 导出
```ts
api.resource(resourceName).export(params);
```

View File

@ -1,45 +0,0 @@
---
title: '@nocobase/plugin-file-manager'
---
# @nocobase/plugin-file-manager
文件管理器
## 安装
```bash
yarn nocobase pull file-manager --start
```
## Field Interfaces
### attachment
附件字段
## Action API
### upload
文件上传
```ts
// 文件管理器接口
await api.resource('attachments').upload({});
// 附件字段接口
await api.resource('users.avatar').upload({
associatedKey: 1,
});
```
## Storages
### local
本地存储
### ali-oss
阿里云 OSS

View File

@ -1,69 +0,0 @@
---
title: '@nocobase/plugin-notifications'
---
# @nocobase/plugin-notifications
提供通知模块
<Alert title="注意" type="warning">
暂时只实现了核心三步骤:
- 通知模板:包括主题、内容、接收人配置、发送服务等等
- 通知服务:可以是短信、邮件等等
- 通知日志:记录通知状态
</Alert>
## 安装
```bash
yarn nocobase pull notifications --start
```
## 示例
```ts
const Notification = db.getModel('notifications');
const notification = await Notification.create({
subject: 'Subject',
body: 'hell world',
receiver_options: {
data: 'to@nocobase.com',
fromTable: 'users',
filter: {},
dataField: 'email',
},
});
await notification.updateAssociations({
service: {
type: 'email',
title: '阿里云邮件推送',
options: {
host: "smtpdm.aliyun.com",
port: 465,
secure: true,
auth: {
user: 'from@nocobase.com',
pass: 'pass',
},
from: 'NocoBase<from@nocobase.com>',
},
},
});
await notification.send();
```
## Action API
### notifications:send
发送通知
```ts
await api.resource('notifications').send({
resourceKey: 1,
to: 'demo@nocobase.com',
});
```

View File

@ -1,14 +0,0 @@
---
title: '@nocobase/plugin-pages'
order: 1
---
# @nocobase/plugin-pages
将客户端的组件参数交由服务端管理,由服务端控制输出客户端所需的 ui schema。开发者可以随意适配任意前端组件。
## 安装
```bash
yarn nocobase pull pages --start
```

View File

@ -1,55 +0,0 @@
---
title: '@nocobase/plugin-permissions'
order: 1
---
# @nocobase/plugin-permissions
提供权限模块
## 安装
```bash
yarn nocobase pull permissions --start
```
## API
<Alert title="还需改进的一些细节" type="warning">
- 提供 Permission Model 相关快捷数据操作 API
- 需要支持从代码层面快捷配置权限,无需经由后台
系统表相关操作权限可能是直接通过权限 api 配置或者 permission model api直接写入数据库
- 提供 ui schema 需要的 fields/actions/pages 相关 permission api
现在有个 ac.can 可用,实际体验不够直接
- 数据表权限设置只开放了业务表,系统表权限需要开发自行处理,但无提供相关 api
如操作记录表、中国行政区表、附件表的情况:
- 操作记录表只开放查看,只能查看自己有权限能查看的数据表的操作
- 中国行政区的省市区等数据仅登录用户可查看
- 附件暂时没做权限限制
</Alert>
### context.ac.isRoot
是否为 root 权限
### context.ac.can(collection)
判断当前用户权限,支持链式操作。
#### ac.can(collection).permissions()
获取当前 collection 的所有权限配置
#### ac.can(collection).act(actionName).any()
是否允许 collection:actionName 操作,允许则返回相关配置
#### ac.can(collection).act(actionName).one(resourceKey)
具体 resourceKey 值的 collection是否允许 collection:actionName 操作,允许则返回相关配置
#### ac.as(roles).can(collection)
指定 roles 的权限判断

View File

@ -1,5 +0,0 @@
---
title: '@nocobase/plugin-system-settings'
---
# @nocobase/plugin-system-settings

View File

@ -1,5 +0,0 @@
---
title: '@nocobase/plugin-ui-router'
---
# @nocobase/plugin-ui-router

View File

@ -1,5 +0,0 @@
---
title: '@nocobase/plugin-ui-schema'
---
# @nocobase/plugin-ui-schema

View File

@ -1,146 +0,0 @@
---
title: '@nocobase/plugin-users'
---
# @nocobase/plugin-users
提供用户模块
<Alert title="注意" type="warning">
用户模块目前的实现较简单
</Alert>
## 安装
```bash
yarn nocobase pull users --start
```
## Action API
### users:check
检查用户是否已登录
```ts
await api.resource('users').check();
```
### users:login
登录
```ts
await api.resource('users').login({
values: {
email,
password,
},
});
```
### users:register
注册
```ts
await api.resource('users').register({
values: {
email,
password,
...others,
},
});
```
### users:logout
注销
<Alert title="注意" type="warning">
注销后端暂无任何处理,实际需要清除 token。
</Alert>
```ts
await api.resource('users').logout();
```
### users:lostpassword
忘记密码
```ts
await api.resource('users').lostpassword({
values: {
email,
}
});
```
### users:resetpassword
重置密码
<Alert title="注意" type="warning">
未实现邮件发送
</Alert>
```ts
await api.resource('users').lostpassword({
values: {
email,
password,
reset_token,
}
});
```
### users:getUserByResetToken
根据 reset token 获取用户信息
```ts
await api.resource('users').getUserByResetToken({
values: {
reset_token,
}
});
```
## Fields Types
### context <Badge>未实现</Badge>
上下文类型,可以从 app.context 里获取信息,如 UA、Client IP 等。利用 context 类型createdBy/updatedBy 的实现也变得更简单了:
createdBy
```ts
{
name: 'created_by_id',
type: 'context',
dataIndex: 'state.currentUser.id',
createOnly: true,
}
```
updatedBy
```ts
{
name: 'updated_by_id',
type: 'context',
dataIndex: 'state.currentUser.id',
}
```
## Field Interfaces
### createdBy
创建人
### updatedBy
最后更新人

View File

@ -1,12 +0,0 @@
---
title: 开发者模式
order: 3
toc: menu
---
# 开发者模式 <Badge>待完善</Badge>
开发者模式是专门为开发者提供的修改、调试复杂配置项的运行环境。在开发者模式下:
- 可以查看系统配置表
- 配置开放 JSON Editor可以修改和调试更多隐藏配置项

View File

@ -1,67 +0,0 @@
---
title: 了解插件
order: 1
nav:
title: 插件
order: 3
path: /plugins
group:
order: 1
title: 教程
---
# 了解插件
NocoBase 核心提供了丰富的 API 用于处理扩展,但是直接调用底层 API 成本较高。因此,又提供了更为灵活、便捷的插件化管理方式,用户只需要将代码放在约定的几个目录里即可。
<Alert title="重要提示" type="warning">
NocoBase 插件之间是平行的,不存在直接的依赖关系,不过插件在加载时可能有优先级。
</Alert>
## 目录结构 <Badge>未实现</Badge>
```bash
|- @nocobase/plugin-[name] 或 nocobase-plugin-[name]
|- src
|- actions
|- collections
|- fields
|- hooks
|- interfaces
|- middlewares
|- models
|- resources
|- blocks
```
<Alert title="注意" type="warning">
目前 v0.4 版本的插件还十分简陋,只提供了一个非常原生态的函数扩展,其他的都需要开发者根据情况调用核心 API 来完成各类功能扩展,并未提供约定式目录,也没有完整的生命周期机制。没有安装/卸载、激活/禁用,加载即激活。
</Alert>
## PluginManager <Badge>未实现</Badge>
插件的几个状态
- 下载
- 启动
- 停止
- 重启
- 删除
### API <Badge>未实现</Badge>
- `pluginManager.pull()`
- `pluginManager.start()`
- `pluginManager.stop()`
- `pluginManager.restart()`
- `pluginManager.remove()`
### CLI <Badge>未实现</Badge>
- `yarn nocobase pull <name>`
- `yarn nocobase start <name>`
- `yarn nocobase stop <name>`
- `yarn nocobase restart <name>`
- `yarn nocobase remove <name>`

View File

@ -1,23 +0,0 @@
---
title: 插件命令行操作
order: 2
toc: menu
---
# 插件命令行操作 <Badge>未实现</Badge>
## pull - 拉取
将插件包从远程下载到本地列表
## start - 启动
启动插件
## stop - 停止
停止插件
## rm - 移除
插件将从本地列表移除

View File

@ -1,74 +0,0 @@
---
title: NocoBase 生命周期
order: 2
toc: menu
---
# NocoBase 生命周期
每个 NocoBase 实例在被创建时都要经过一系列的初始化过程在这个过程中某些节点会运行一些函数这些节点就是生命周期的钩子有了钩子的存在代码不论按什么顺序书写都会按照既定的顺序执行。NocoBase 的生命周期大概分为四个环节:
1. 初始化实例
2. 加载配置
3. 数据库操作
4. 每次请求中
## 初始化实例
- koanocobase 实例
- database数据库实例
- resourcerkoa 的分支,负责 resource router
- pluginManager插件管理器实例
现阶段的设计 sever 直接继承了 koa application其他三个作为 server 实例的成员存在。
<Alert title="注意" type="warning">
pluginManager 的初始化存在较大缺陷,在初始化时直接加载了配置,暂时无法处理数据库操作。
</Alert>
## 加载配置
- table hooks表配置事件
- table options表配置
- model hooksmodel 事件
系统表配置从文件目录里导入,业务表配置从数据库里导入,部分开放的系统表从文件目录导入之后,又从数据库里更新,如用户表。
<Alert title="注意" type="warning">
resourcer 配置是运行时初始化,在 koa middleware 中。hooks 分 table、model、plugin、resourcer 四类,暂时并不统一。
另外,还有个非常重要的细节,生命周期解决了大结构的执行顺序,但并未解决同一挂载点多钩子之间的执行顺序。
</Alert>
## 数据库操作
- initialize初始化app 启动或重启时都执行
- install安装操作只执行一次
- upgrade更新操作只执行一次
- uninstall卸载操作只执行一次
<Alert title="注意" type="warning">
目前还不支持,这部分的钩子主要用于管理插件。
</Alert>
## 每次请求中
- koa middleware
- resourcer middleware
- resource middleware
- action middleware
- action handler
客户端请求时,都会执行。
虽然大部分框架都提供了中间件,但是中间件的执行顺序(优先级)依赖于编码顺序,这种方式非常不利于插件化管理。因此,在 Resourcer 设计思想里,将中间件做了分层,不同层级的 middlewares 不依赖于编码顺序,而是如下顺序:
1. 首先koa 层:`koa.use`
2. 其次resourcer 层:`resourcer.use`
3. 再次resource 层(每个资源独立):`resourcer.registerActionMiddleware`
4. 然后action 层:`resourcer.registerResourceMiddleware`
5. 最后,执行 action handler
不过,每个层次的中间件执行顺序还依赖于编码顺序,如有需要再进行更细微的改进。

View File

@ -1,34 +0,0 @@
---
title: 如何编写测试
order: 4
toc: menu
---
# 如何编写测试 <Badge>未实现</Badge>
NocoBase 提供了 @nocobase/test 用于编写和调试插件。
```ts
import { mockServer } from '@nocobase/test';
describe('test', () => {
let api;
beforeEach(async () => {
api = mockServer({});
await api.database.sync();
});
afterEach(async () => {
await api.database.close();
});
it('test resource', () => {
await app.resource('demos').get();
});
it('test request', () => {
await app.request().get('/');
});
});
```