feat: update docs

This commit is contained in:
chenos 2022-11-13 23:00:59 +08:00
parent c465f0c427
commit 15cbad30b4
20 changed files with 858 additions and 626 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

@ -152,6 +152,17 @@ DB_TABLE_PREFIX=nocobase_
DB_LOGGING=on
```
### LOGGER_TRANSPORT
日志 transport默认值 `console,dailyRotateFile`,可选项
- `console`
- `dailyRotateFile`
### DAILY_ROTATE_FILE_DIRNAME
`dailyRotateFile` 日志的存储路径,默认为 `storage/logs`
## 临时环境变量
安装 NocoBase 时,可以通过设置临时的环境变量来辅助安装,如:

View File

@ -1,142 +0,0 @@
# Action API
## Common
---
Collection and Association resources are common.
### `create`
```bash
POST /api/users:create?whitelist=a,b&blacklist=c,d
{} # Request Body
```
- Parameters
- whitelist White list
- blacklist Black list
- Request body: JSON data to be inserted
- Response body data: Created data JSON
#### Add a User
```bash
POST /api/users:create
Request Body
{
"email": "demo@nocobase.com",
"name": "Admin"
}
Response 200 (application/json)
{
"data": {},
}
```
#### Add a user's article
```bash
POST /api/users/1/posts:create
Request Body
{
"title": "My first post"
}
Response 200 (application/json)
{
"data": {},
}
```
#### Association in Request Body
```bash
POST /api/posts:create
Request Body
{
"title": "My first post",
"user": 1
}
Response 200 (application/json)
{
"data": {
"id": 1,
"title": "My first post",
"userId": 1,
"user": {
"id": 1
}
}
}
```
### `update`
```bash
POST /api/users:create?filterByTk=1&whitelist=a,b&blacklist=c,d
{} # Request Body
```
- Parameters
- whitelist White list
- blacklist Black list
- filterByTk Filter by tk field, by default tk is the primary key of the data table
- filter Filtersupport json string
- Request body: JSON data to be updated
#### Association in Request Body
```bash
POST /api/posts:update/1
Request Body
{
"title": "My first post 2",
"user": 2
}
Response 200 (application/json)
{
"data": [
{
"id": 1,
"title": "My first post 2",
"userId": 2,
"user": {
"id": 2
}
}
]
}
```
### `list`
### `get`
### `destroy`
### `move`
## Association
---
### `add`
### `set`
### `remove`
### `toggle`

View File

@ -1,59 +0,0 @@
# Filter operators
## Common
- $eq
- $ne
- $gte
- $gt
- $lte
- $lt
- $not
- $is
- $in
- $notIn
- $like
- $notLike
- $iLike
- $notILike
- $and
- $or
- $empty
- $notEmpty
## array
- $match
- $notMatch
- $anyOf
- $noneOf
- $arrayEmpty
- $arrayNotEmpty
## association
- $exists
- $notExists
## boolean
- $isTruthy
- $isFalsy
## date
- $dateOn
- $dateNotOn
- $dateBefore
- $dateNotBefore
- $dateAfter
- $dateNotAfter
## string
- $includes
- $notIncludes
- $startsWith
- $notStartsWith
- $endWith
- $notEndWith

View File

@ -1,281 +0,0 @@
# JavaScript SDK
## APIClient
```ts
class APIClient {
// axios instance
axios: AxiosInstance;
// constructors
constructor(instance?: AxiosInstance | AxiosRequestConfig);
// Client-side requests, support for AxiosRequestConfig and ResourceActionOptions
request<T = any, R = AxiosResponse<T>, D = any>(config: AxiosRequestConfig<D> | ResourceActionOptions): Promise<R>;
// Get Resources
resource<R = IResource>(name: string, of?: any): R;
}
```
Initialize instance
```ts
import axios from 'axios';
import { APIClient } from '@nocobase/sdk';
// Provide AxiosRequestConfig configuration parameters
const api = new APIClient({
baseURL: 'https://localhost:8000/api',
});
// Provide AxiosInstance
const instance = axios.create({
baseURL: 'https://localhost:8000/api',
});
const api = new APIClient(instance);
```
## Mock
```ts
import { APIClient } from '@nocobase/sdk';
import MockAdapter from 'axios-mock-adapter';
const api = new APIClient({
baseURL: 'https://localhost:8000/api',
});
const mock = new MockAdapter(api.axios);
mock.onGet('users:get').reply(200, {
data: { id: 1, name: 'John Smith' },
});
await api.request({ url: 'users:get' });
```
## Storage
APIClient uses localStorage by default, you can also custom storage.
```ts
import { Storage } from '@nocobase/sdk';
class MemoryStorage extends Storage {
items = new Map();
clear() {
this.items.clear();
}
getItem(key: string) {
return this.items.get(key);
}
setItem(key: string, value: string) {
return this.items.set(key, value);
}
removeItem(key: string) {
return this.items.delete(key);
}
}
const api = new APIClient({
baseURL: 'https://localhost:8000/api',
storageClass: CustomStorage,
});
```
## Auth
```ts
// sign in and remember the current token
api.auth.signIn({ email, password });
// sign out and delete the token
api.auth.signOut();
// set the token
api.auth.setToken('123');
// set the role (multiple roles)
api.auth.setRole('admin');
// set the locale (multiple languages)
api.auth.setLocale('zh-CN');
```
Custom Auth
```ts
import { Auth } from '@nocobase/sdk';
class CustomAuth extends Auth {
}
const api = new APIClient({
baseURL: 'https://localhost:8000/api',
authClass: CustomAuth,
});
```
## Request
```ts
// url
await api.request({
url: 'users:list',
// request params
params: {
filter: {
'email.$includes': 'noco',
},
},
// request body
data,
});
// resource & action
await api.request({
resource: 'users',
action: 'list',
// action params
params: {
filter: {
'email.$includes': 'noco',
},
page: 1,
},
});
```
## Resource action
```ts
await api.resource('collection')[action]();
await api.resource('collection.association', collectionId)[action]();
```
## Action API
```ts
await api.resource('collection').create();
await api.resource('collection').get();
await api.resource('collection').list();
await api.resource('collection').update();
await api.resource('collection').destroy();
await api.resource('collection.association', collectionId).create();
await api.resource('collection.association', collectionId).get();
await api.resource('collection.association', collectionId).list();
await api.resource('collection.association', collectionId).update();
await api.resource('collection.association', collectionId).destroy();
```
### `get`
```ts
interface Resource {
get: (options?: GetActionOptions) => Promise<any>;
}
interface GetActionOptions {
filter?: any;
filterByTk?: any;
fields?: string || string[];
appends?: string || string[];
expect?: string || string[];
sort?: string[];
}
```
### `list`
```ts
interface Resource {
list: (options?: ListActionOptions) => Promise<any>;
}
interface ListActionOptions {
filter?: any;
filterByTk?: any;
fields?: string || string[];
appends?: string || string[];
expect?: string || string[];
sort?: string[];
page?: number;
pageSize?: number;
paginate?: boolean;
}
```
### `create`
```ts
interface Resource {
create: (options?: CreateActionOptions) => Promise<any>;
}
interface CreateActionOptions {
whitelist?: string[];
blacklist?: string[];
values?: {[key: sting]: any};
}
```
### `update`
```ts
interface Resource {
update: (options?: UpdateActionOptions) => Promise<any>;
}
interface UpdateActionOptions {
filter?: any;
filterByTk?: any;
whitelist?: string[];
blacklist?: string[];
values?: {[key: sting]: any};
}
```
### `destroy`
```ts
interface Resource {
destroy: (options?: DestroyActionOptions) => Promise<any>;
}
interface DestroyActionOptions {
filter?: any;
filterByTk?: any;
}
```
### `move`
```ts
interface Resource {
move: (options?: MoveActionOptions) => Promise<any>;
}
interface MoveActionOptions {
sourceId: any;
targetId?: any;
/** @default 'sort' */
sortField?: any;
targetScope?: {[key: string]: any};
sticky?: boolean;
method?: 'insertAfter' | 'prepend';
}
```
### `<custom>`
```ts
interface AttachmentResource {
}
interface UploadActionOptions {
}
api.resource<AttachmentResource>('attachments').upload();
api.resource('attachments').upload<UploadActionOptions>();
```

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` 方法,结束更新。
## 构造函数
@ -18,6 +148,7 @@
| --- | --- | --- | --- |
| `options.database` | `IDatabaseOptions` or `Database` | `{}` | 数据库配置 |
| `options.resourcer` | `ResourcerOptions` | `{}` | 资源路由配置 |
| `options.logger` | `AppLoggerOptions` | `{}` | 日志 |
| `options.cors` | [`CorsOptions`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/koa__cors/index.d.ts#L24) | `{}` | 跨域配置,参考 [@koa/cors](https://npmjs.com/package/@koa/cors) |
| `options.dataWrapping` | `boolean` | `true` | 是否包装响应数据,`true` 则将把通常的 `ctx.body` 包装为 `{ data, meta }` 的结构。 |
| `options.registerActions` | `boolean` | `true` | 是否注册默认的 [actions](#) |
@ -32,29 +163,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`
@ -73,6 +181,11 @@ const app = new Application({
ACL 实例,相关 API 参考 [ACL](/api/acl)。
### `logger`
Winston 实例,相关 API 参考 [Winston](https://github.com/winstonjs/winston#table-of-contents)。
### `i18n`
I18next 实例,相关 API 参考 [I18next](https://www.i18next.com/overview/api)。
@ -89,11 +202,12 @@ I18next 实例,相关 API 参考 [I18next](https://www.i18next.com/overview/ap
内置的中间件有:
- logger
- i18next
- bodyParser
- cors
- dataWrapping
- collection2resource
- db2resource
- restApiMiddleware
### `context`
@ -108,6 +222,7 @@ NocoBase 默认对 context 注入了以下成员,可以在请求处理函数
| `ctx.db` | `Database` | 数据库实例 |
| `ctx.resourcer` | `Resourcer` | 资源路由管理器实例 |
| `ctx.action` | `Action` | 资源操作相关对象实例 |
| `ctx.logger` | `Winston` | 日志实例 |
| `ctx.i18n` | `I18n` | 国际化实例 |
| `ctx.t` | `i18n.t` | 国际化翻译函数快捷方式 |
| `ctx.getBearerToken()` | `Function` | 获取请求头中的 bearer token |

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

@ -0,0 +1,67 @@
# v0.8.1NocoBase 的 Logging 系统
## `@nocobase/logger`
基于 Winston 实现,提供了便捷的创建 logger 实例的方法。
```ts
const logger = createLogger();
logger.info('Hello distributed log files!');
const { instance, middleware } = createAppLogger(); // 用于 @nocobase/server
app.logger = instance;
app.use(middleware);
```
## 新增的环境变量
logger 相关环境变量有:
- [LOGGER_TRANSPORT](/api/env#logger_transport)
- [DAILY_ROTATE_FILE_DIRNAME](/api/env#daily_rotate_file_dirname)
## Application 的 logger 配置
```ts
const app = new Application({
logger: {
async skip(ctx) {
return false;
},
requestWhitelist: [],
responseWhitelist: [],
transports: ['console', 'dailyRotateFile'],
},
})
```
更多配置项参考 [Winston 文档](https://github.com/winstonjs/winston#table-of-contents)
## app.logger & ctx.logger
ctx.logger 带有 reqId整个 ctx 周期里都是一个 reqId
```ts
ctx.logger = app.logger.child({ reqId: ctx.reqId });
```
`app.logger``ctx.logger` 都是 Winston 实例,详细用法参考 [Winston 文档](https://github.com/winstonjs/winston#table-of-contents)
## 自定义 Transports
除了 Winston 的方式以外NocoBase 还提供了一种更便捷的方式
```ts
import { Transports } from '@nocobase/logger';
Transports['custom'] = () => {
return new winston.transports.Console();
};
const app = new Application({
logger: {
transports: ['custom'],
},
})
```

View File

@ -0,0 +1,67 @@
# v0.8.1NocoBase 的 Logging 系统
## `@nocobase/logger`
基于 Winston 实现,提供了便捷的创建 logger 实例的方法。
```ts
const logger = createLogger();
logger.info('Hello distributed log files!');
const { instance, middleware } = createAppLogger(); // 用于 @nocobase/server
app.logger = instance;
app.use(middleware);
```
## 新增的环境变量
logger 相关环境变量有:
- [LOGGER_TRANSPORT](/api/env#logger_transport)
- [DAILY_ROTATE_FILE_DIRNAME](/api/env#daily_rotate_file_dirname)
## Application 的 logger 配置
```ts
const app = new Application({
logger: {
async skip(ctx) {
return false;
},
requestWhitelist: [],
responseWhitelist: [],
transports: ['console', 'dailyRotateFile'],
},
})
```
更多配置项参考 [Winston 文档](https://github.com/winstonjs/winston#table-of-contents)
## app.logger & ctx.logger
ctx.logger 带有 reqId整个 ctx 周期里都是一个 reqId
```ts
ctx.logger = app.logger.child({ reqId: ctx.reqId });
```
`app.logger``ctx.logger` 都是 Winston 实例,详细用法参考 [Winston 文档](https://github.com/winstonjs/winston#table-of-contents)
## 自定义 Transports
除了 Winston 的方式以外NocoBase 还提供了一种更便捷的方式
```ts
import { Transports } from '@nocobase/logger';
Transports['custom'] = () => {
return new winston.transports.Console();
};
const app = new Application({
logger: {
transports: ['custom'],
},
})
```