feat: add examples (#718)
* feat: add examples * fix: tsconfig-paths/register * feat: more examples * fix: test errors * feat: update examples * docs: example list * fix: updates * feat: example for test case * feat: api client * fix: updates * feat: tree structures * fix: adjacency-list
This commit is contained in:
parent
7fde444a60
commit
28b73d7143
25
examples/api-client/api.request.ts
Normal file
25
examples/api-client/api.request.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
# 客户端常规请求
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1: 启动服务器
|
||||||
|
yarn run:example api-client/server start
|
||||||
|
|
||||||
|
Step 2: 客户端常规请求 —— api.request()
|
||||||
|
yarn run:example api-client/api.request
|
||||||
|
*/
|
||||||
|
import { APIClient } from '@nocobase/sdk';
|
||||||
|
|
||||||
|
const api = new APIClient({
|
||||||
|
baseURL: 'http://localhost:13000/api',
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const response = await api.request({
|
||||||
|
url: 'test:list',
|
||||||
|
});
|
||||||
|
// 等价于
|
||||||
|
// const response = await api.resource('test').list();
|
||||||
|
console.log(response.data);
|
||||||
|
})();
|
25
examples/api-client/api.resource.ts
Normal file
25
examples/api-client/api.resource.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
# 客户端资源请求
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1: 启动服务器
|
||||||
|
yarn run:example api-client/server start
|
||||||
|
|
||||||
|
Step 2: 客户端资源请求 —— api.resource(name).action(params)
|
||||||
|
yarn run:example api-client/api.resource
|
||||||
|
*/
|
||||||
|
import { APIClient } from '@nocobase/sdk';
|
||||||
|
|
||||||
|
const api = new APIClient({
|
||||||
|
baseURL: 'http://localhost:13000/api',
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const response = await api.resource('test').list();
|
||||||
|
// 等价于
|
||||||
|
// const response = await api.request({
|
||||||
|
// url: 'test:list',
|
||||||
|
// });
|
||||||
|
console.log(response.data);
|
||||||
|
})();
|
51
examples/api-client/server.ts
Normal file
51
examples/api-client/server.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
# 客户端请求
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1: 启动服务器
|
||||||
|
yarn run:example api-client/server start
|
||||||
|
|
||||||
|
Step 2: 客户端常规请求 —— api.request()
|
||||||
|
yarn run:example api-client/api.request
|
||||||
|
|
||||||
|
Step 3: 客户端资源请求 —— api.resource(name).action(params)
|
||||||
|
yarn run:example api-client/api.resource
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// 定义了一个 test 资源,并提供了相对应的 list 方法
|
||||||
|
app.resource({
|
||||||
|
name: 'test',
|
||||||
|
actions: {
|
||||||
|
async list(ctx, next) {
|
||||||
|
ctx.body = 'test list';
|
||||||
|
await next();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
33
examples/app/__tests__/app.test.ts
Normal file
33
examples/app/__tests__/app.test.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
# 编写 Application 测试用例
|
||||||
|
|
||||||
|
# 执行测试
|
||||||
|
yarn jest examples/app/__tests__/app.test.ts
|
||||||
|
*/
|
||||||
|
import { MockServer, mockServer } from '@nocobase/test';
|
||||||
|
|
||||||
|
describe('app test', () => {
|
||||||
|
let app: MockServer;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
app = mockServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('test1', async () => {
|
||||||
|
app.resource({
|
||||||
|
name: 'test',
|
||||||
|
actions: {
|
||||||
|
async list(ctx, next) {
|
||||||
|
ctx.body = 'test list';
|
||||||
|
await next();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const response = await app.agent().resource('test').list();
|
||||||
|
expect(response.body).toEqual({ data: 'test list' });
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await app.destroy();
|
||||||
|
});
|
||||||
|
});
|
69
examples/app/acl.ts
Normal file
69
examples/app/acl.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/acl start
|
||||||
|
|
||||||
|
Step 2:
|
||||||
|
curl http://localhost:13000/api/test:export
|
||||||
|
curl --location --request GET 'http://localhost:13000/api/test:export' --header 'X-Role: admin'
|
||||||
|
curl --location --request GET 'http://localhost:13000/api/test:import' --header 'X-Role: admin'
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.acl.define({
|
||||||
|
role: 'admin',
|
||||||
|
actions: {
|
||||||
|
'test:export': {
|
||||||
|
fields: ['a1', 'b1']
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
app.resourcer.use(async (ctx, next) => {
|
||||||
|
ctx.state.currentRole = ctx.get('X-Role');
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.resourcer.use(app.acl.middleware());
|
||||||
|
|
||||||
|
app.resource({
|
||||||
|
name: 'test',
|
||||||
|
actions: {
|
||||||
|
async export(ctx, next) {
|
||||||
|
ctx.body = {
|
||||||
|
'ctx.action.params': ctx.action.params,
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
},
|
||||||
|
async import(ctx, next) {
|
||||||
|
ctx.body = {
|
||||||
|
'ctx.action.params': ctx.action.params,
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
91
examples/app/association2resource.ts
Normal file
91
examples/app/association2resource.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
Step 1: 将 collections 同步给数据库(建表和字段)
|
||||||
|
yarn run:example app/association2resource db:sync
|
||||||
|
|
||||||
|
Step 2:
|
||||||
|
yarn run:example app/association2resource start
|
||||||
|
|
||||||
|
Step 3: Create article
|
||||||
|
curl --location --request POST 'http://localhost:13000/api/articles:create' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--data-raw '{
|
||||||
|
"title": "My first article",
|
||||||
|
"content": "Hello NocoBase!",
|
||||||
|
"user": {
|
||||||
|
"name": "user 1"
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
|
||||||
|
Step 4: View article user
|
||||||
|
curl http://localhost:13000/api/articles/1/user
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: 'a2r_',
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// 已定义的 collection 会自动转为同名 resource
|
||||||
|
app.collection({
|
||||||
|
name: 'articles',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'content',
|
||||||
|
},
|
||||||
|
// 自动转为 articles.user 资源
|
||||||
|
{
|
||||||
|
type: 'belongsTo',
|
||||||
|
name: 'user',
|
||||||
|
// 以下参数缺失时,自动处理
|
||||||
|
// target: 'users',
|
||||||
|
// foreignKey: 'userId',
|
||||||
|
// targetKey: 'id',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// 已定义的 collection 会自动转为同名 resource
|
||||||
|
app.collection({
|
||||||
|
name: 'users',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'name',
|
||||||
|
},
|
||||||
|
// 自动转为 users.articles 资源
|
||||||
|
{
|
||||||
|
type: 'hasMany',
|
||||||
|
name: 'articles',
|
||||||
|
// 以下参数缺失时,自动处理
|
||||||
|
// target: 'articles',
|
||||||
|
// foreignKey: 'userId',
|
||||||
|
// sourceKey: 'id',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
59
examples/app/collection2resource.ts
Normal file
59
examples/app/collection2resource.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
Step 1: 将 collections 同步给数据库(建表和字段)
|
||||||
|
yarn run:example app/collection2resource db:sync
|
||||||
|
|
||||||
|
Step 2: 启动应用
|
||||||
|
yarn run:example app/collection2resource start
|
||||||
|
|
||||||
|
Step 3: Create article
|
||||||
|
curl --location --request POST 'http://localhost:13000/api/articles:create' \
|
||||||
|
--header 'Content-Type: application/json' \
|
||||||
|
--data-raw '{
|
||||||
|
"title": "My first article",
|
||||||
|
"content": "Hello NocoBase!"
|
||||||
|
}'
|
||||||
|
|
||||||
|
Step 4: View article list
|
||||||
|
curl http://localhost:13000/api/articles:list
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: 'c2r_',
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// 已定义的 collection 会自动转为同名 resource
|
||||||
|
app.collection({
|
||||||
|
name: 'articles',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'content',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
77
examples/app/context/ctx.action.mergeParams.ts
Normal file
77
examples/app/context/ctx.action.mergeParams.ts
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
# ctx.action.mergeParams 的用法
|
||||||
|
|
||||||
|
一般是在中间件里通过 ctx.action.mergeParams() 方法合并参数
|
||||||
|
|
||||||
|
# 步骤:
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/context/ctx.action.mergeParams start
|
||||||
|
|
||||||
|
Step 2:
|
||||||
|
curl http://localhost:13000/api/test:list?filter%5Ba%5D=a2&fields=col1
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
async function list(ctx, next) {
|
||||||
|
ctx.body = {
|
||||||
|
'action.resourceName': ctx.action.resourceName,
|
||||||
|
'action.resourceOf': ctx.action.resourceOf,
|
||||||
|
'action.actionName': ctx.action.actionName,
|
||||||
|
'action.params': ctx.action.params,
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function get(ctx, next) {
|
||||||
|
ctx.body = {
|
||||||
|
'action.resourceName': ctx.action.resourceName,
|
||||||
|
'action.resourceOf': ctx.action.resourceOf,
|
||||||
|
'action.actionName': ctx.action.actionName,
|
||||||
|
'action.params': ctx.action.params,
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.resourcer.use(async (ctx, next) => {
|
||||||
|
// 在 middleware 里修改 action.params
|
||||||
|
ctx.action.mergeParams({
|
||||||
|
filter: {
|
||||||
|
a: 'a1',
|
||||||
|
},
|
||||||
|
fields: ['col1', 'col2'],
|
||||||
|
});
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.resource({
|
||||||
|
name: 'test',
|
||||||
|
actions: {
|
||||||
|
list,
|
||||||
|
get,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
75
examples/app/context/ctx.action.ts
Normal file
75
examples/app/context/ctx.action.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
# ctx.action 的重要参数说明
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/context/ctx.action start
|
||||||
|
|
||||||
|
Step 2:
|
||||||
|
curl http://localhost:13000/api/test:list
|
||||||
|
curl http://localhost:13000/api/test/1/nest:get
|
||||||
|
curl http://localhost:13000/api/test:list?filter%5Ba%5D=a&fields=a,b&sort=a,b
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
async function list(ctx, next) {
|
||||||
|
ctx.body = {
|
||||||
|
'action.resourceName': ctx.action.resourceName,
|
||||||
|
'action.resourceOf': ctx.action.resourceOf,
|
||||||
|
'action.actionName': ctx.action.actionName,
|
||||||
|
'action.params': ctx.action.params,
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function get(ctx, next) {
|
||||||
|
ctx.body = {
|
||||||
|
'action.resourceName': ctx.action.resourceName,
|
||||||
|
'action.resourceOf': ctx.action.resourceOf,
|
||||||
|
'action.actionName': ctx.action.actionName,
|
||||||
|
'action.params': ctx.action.params,
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.resource({
|
||||||
|
name: 'test',
|
||||||
|
actions: {
|
||||||
|
list,
|
||||||
|
get,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
app.resource({
|
||||||
|
name: 'test.nest',
|
||||||
|
actions: {
|
||||||
|
list,
|
||||||
|
get,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
56
examples/app/context/ctx.db.ts
Normal file
56
examples/app/context/ctx.db.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
# ctx.db 用法
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/context/ctx.db start
|
||||||
|
|
||||||
|
Step 2:
|
||||||
|
curl http://localhost:13000/
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.collection({
|
||||||
|
name: 'articles',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
name: 'content',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(async (ctx, next) => {
|
||||||
|
ctx.body = ctx.db.getCollection('articles').options;
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
75
examples/app/context/ctx.i18n.ts
Normal file
75
examples/app/context/ctx.i18n.ts
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
# 国际化多语言设置
|
||||||
|
|
||||||
|
主要介绍 app.i18n 和 ctx.i18n 的区别
|
||||||
|
|
||||||
|
# 步骤:
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/context/ctx.i18n start
|
||||||
|
|
||||||
|
Step 2:
|
||||||
|
curl http://localhost:13000/?locale=en-US
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
i18n: {
|
||||||
|
defaultNS: 'test',
|
||||||
|
resources: {
|
||||||
|
'en-US': {
|
||||||
|
test: {
|
||||||
|
hello: 'Hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'zh-CN': {
|
||||||
|
test: {
|
||||||
|
hello: '你好',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
app.i18n.addResources('zh-CN', 'test', {
|
||||||
|
world: '世界',
|
||||||
|
});
|
||||||
|
|
||||||
|
app.i18n.addResources('en-US', 'test', {
|
||||||
|
world: 'World',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 改变全局 app.i18n 的多语言,一般用于 cli 环境的多语言切换
|
||||||
|
app.i18n.changeLanguage('zh-CN');
|
||||||
|
|
||||||
|
app.use(async (ctx, next) => {
|
||||||
|
// ctx.i18n 是 app.i18n 的 clone instance
|
||||||
|
ctx.body = {
|
||||||
|
'ctx.i18n': ctx.i18n.language,
|
||||||
|
'app.i18n': app.i18n.language,
|
||||||
|
'ctx.t': ctx.t('hello') + ' ' + ctx.t('world'),
|
||||||
|
'app.i18n.t': app.i18n.t('hello') + ' ' + app.i18n.t('world'),
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
39
examples/app/custom-command.ts
Normal file
39
examples/app/custom-command.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
# 自定义命令行
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/custom-command hello
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.command('hello').action(() => {
|
||||||
|
console.log('hello cli');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
||||||
|
|
57
examples/app/custom-plugin.ts
Normal file
57
examples/app/custom-plugin.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
# 编写一个最简单的插件
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1: Start app
|
||||||
|
yarn run:example plugins/custom-plugin start
|
||||||
|
|
||||||
|
Step 2: View test list
|
||||||
|
http://localhost:13000/api/test:list
|
||||||
|
*/
|
||||||
|
import { Application, Plugin } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Encapsulate modules into a plugin
|
||||||
|
class MyPlugin extends Plugin {
|
||||||
|
getName() {
|
||||||
|
return 'MyPlugin';
|
||||||
|
}
|
||||||
|
async load() {
|
||||||
|
app.resource({
|
||||||
|
name: 'test',
|
||||||
|
actions: {
|
||||||
|
async list(ctx) {
|
||||||
|
ctx.body = 'test list';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register plugin
|
||||||
|
app.plugin(MyPlugin);
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
72
examples/app/i18n.ts
Normal file
72
examples/app/i18n.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
# 国际化多语言
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/i18n start
|
||||||
|
|
||||||
|
Step 2:
|
||||||
|
curl http://localhost:13000/api/test:get
|
||||||
|
curl http://localhost:13000/api/test:get?locale=en-US
|
||||||
|
curl --location --request GET 'http://localhost:13000/api/test:get' --header 'X-Locale: en-US'
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
i18n: {
|
||||||
|
defaultNS: 'test',
|
||||||
|
resources: {
|
||||||
|
'en-US': {
|
||||||
|
test: {
|
||||||
|
hello: 'Hello',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'zh-CN': {
|
||||||
|
test: {
|
||||||
|
hello: '你好',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
app.i18n.addResources('zh-CN', 'test', {
|
||||||
|
world: '世界',
|
||||||
|
});
|
||||||
|
|
||||||
|
app.i18n.addResources('en-US', 'test', {
|
||||||
|
world: 'World',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 改变全局 app.i18n 的多语言,一般用于 cli 环境的多语言
|
||||||
|
app.i18n.changeLanguage('zh-CN');
|
||||||
|
|
||||||
|
app.resource({
|
||||||
|
name: 'test',
|
||||||
|
actions: {
|
||||||
|
async get(ctx, next) {
|
||||||
|
ctx.body = ctx.t('hello') + ' ' + ctx.t('world');
|
||||||
|
await next();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
87
examples/app/middleware/acl.ts
Normal file
87
examples/app/middleware/acl.ts
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
# app.acl.use 用法
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/middleware/acl start
|
||||||
|
|
||||||
|
Step 2:
|
||||||
|
curl http://localhost:13000/api/test:export
|
||||||
|
curl http://localhost:13000/api/test:export?skip=1
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.acl.define({
|
||||||
|
role: 'admin',
|
||||||
|
actions: {
|
||||||
|
'test:export': {
|
||||||
|
fields: ['a1', 'b1'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
app.acl.use(async (ctx, next) => {
|
||||||
|
ctx.permission = {
|
||||||
|
// 是否跳过 acl 判断
|
||||||
|
skip: !!ctx.request.query.skip,
|
||||||
|
// 如果 skip=true 不处理
|
||||||
|
// 如果 skip=false,can.params 会通过 ctx.action.mergeParams() 合并到 ctx.action.params
|
||||||
|
can: {
|
||||||
|
params: {
|
||||||
|
fields: ['a1', 'b1', 'b3'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
// acl 中间件里也可以直接给 body 赋值
|
||||||
|
ctx.body = {
|
||||||
|
test: 'test',
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.resourcer.use(async (ctx, next) => {
|
||||||
|
// 当前角色
|
||||||
|
ctx.state.currentRole = ctx.get('X-Role');
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.resourcer.use(app.acl.middleware());
|
||||||
|
|
||||||
|
app.resource({
|
||||||
|
name: 'test',
|
||||||
|
actions: {
|
||||||
|
async export(ctx, next) {
|
||||||
|
ctx.body = {
|
||||||
|
...ctx.body,
|
||||||
|
'ctx.action.params': ctx.action.params,
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
43
examples/app/middleware/app.ts
Normal file
43
examples/app/middleware/app.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
# app.use 用法,与 Koa 相同
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/middleware/app start
|
||||||
|
|
||||||
|
Step 2:
|
||||||
|
curl http://localhost:13000/
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Same as Koa
|
||||||
|
app.use(async (ctx, next) => {
|
||||||
|
ctx.body = 'Hello NocoBase';
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
60
examples/app/middleware/resourcer.ts
Normal file
60
examples/app/middleware/resourcer.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
# app.resourcer.use 用法
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/middleware/resourcer start
|
||||||
|
|
||||||
|
Step 2:
|
||||||
|
curl http://localhost:13000/api/test:list
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.resource({
|
||||||
|
name: 'test',
|
||||||
|
actions: {
|
||||||
|
async list(ctx, next) {
|
||||||
|
ctx.body = ctx.body || [];
|
||||||
|
ctx.body.push('test list');
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
app.resourcer.use(async (ctx, next) => {
|
||||||
|
ctx.body = ctx.body || [];
|
||||||
|
ctx.body.push('resourcer middleware');
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(async (ctx, next) => {
|
||||||
|
ctx.body = ctx.body || [];
|
||||||
|
ctx.body.push('app middleware');
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
62
examples/app/migrations/add-migration.ts
Normal file
62
examples/app/migrations/add-migration.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
# Application Migration
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/migrations/add-migration migrator up
|
||||||
|
|
||||||
|
Step 2:
|
||||||
|
yarn run:example app/migrations/add-migration migrator down
|
||||||
|
*/
|
||||||
|
import { DataTypes } from '@nocobase/database';
|
||||||
|
import { Application, Migration } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
class MyMigration extends Migration {
|
||||||
|
async up() {
|
||||||
|
/*
|
||||||
|
可用的属性
|
||||||
|
this.app;
|
||||||
|
this.db;
|
||||||
|
this.queryInterface;
|
||||||
|
this.sequelize;
|
||||||
|
*/
|
||||||
|
await this.queryInterface.createTable('test', {
|
||||||
|
name: DataTypes.STRING,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async down() {
|
||||||
|
await this.queryInterface.dropTable('test');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.db.addMigration({
|
||||||
|
name: 'my-migration',
|
||||||
|
migration: MyMigration,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
@ -1,3 +1,11 @@
|
|||||||
|
/*
|
||||||
|
# 支持多应用(子应用)
|
||||||
|
|
||||||
|
yarn run:example app/multi-app start
|
||||||
|
|
||||||
|
curl http://localhost:13000/api/test:list
|
||||||
|
curl http://localhost:13000/sub1/api/test:list
|
||||||
|
*/
|
||||||
import { Application } from '@nocobase/server';
|
import { Application } from '@nocobase/server';
|
||||||
import { IncomingMessage } from 'http';
|
import { IncomingMessage } from 'http';
|
||||||
|
|
||||||
@ -20,10 +28,6 @@ const app = new Application({
|
|||||||
plugins: [],
|
plugins: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (require.main === module) {
|
|
||||||
app.runAsCLI();
|
|
||||||
}
|
|
||||||
|
|
||||||
const subApp1 = app.appManager.createApplication('sub1', {
|
const subApp1 = app.appManager.createApplication('sub1', {
|
||||||
database: app.db,
|
database: app.db,
|
||||||
resourcer: {
|
resourcer: {
|
||||||
@ -56,7 +60,8 @@ app.appManager.setAppSelector((req: IncomingMessage) => {
|
|||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
export default app;
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
// http://localhost:13000/api/test:list
|
export default app;
|
||||||
// http://localhost:13000/sub1/api/test:list
|
|
||||||
|
91
examples/app/resource-actions/action-merge-params.ts
Normal file
91
examples/app/resource-actions/action-merge-params.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
# Action 参数的多来源合并
|
||||||
|
|
||||||
|
# 步骤:
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/resource-actions/action-merge-params start
|
||||||
|
|
||||||
|
Step 2: 客户端请求时提供参数也是一种来源
|
||||||
|
curl http://localhost:13000/api/test:list?filter%5Ba%5D=a2&fields=col1
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 来源1:resourcer.use 中间件里直接 action.mergeParams
|
||||||
|
app.resourcer.use(async (ctx, next) => {
|
||||||
|
// 在 middleware 里修改 action.params
|
||||||
|
ctx.action.mergeParams({
|
||||||
|
filter: {
|
||||||
|
col1: 'val1',
|
||||||
|
},
|
||||||
|
fields: ['col1', 'col2', 'col4'],
|
||||||
|
});
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 来源2:app.acl.use 中间件里的 ctx.permission.can.params
|
||||||
|
app.acl.use(async (ctx, next) => {
|
||||||
|
ctx.permission = {
|
||||||
|
// 是否跳过 acl 判断
|
||||||
|
skip: !!ctx.request.query.skip,
|
||||||
|
// 如果 skip=true 不处理
|
||||||
|
// 如果 skip=false,can.params 会通过 ctx.action.mergeParams() 合并到 ctx.action.params
|
||||||
|
can: {
|
||||||
|
params: {
|
||||||
|
filter: {
|
||||||
|
col1: 'val2',
|
||||||
|
},
|
||||||
|
fields: ['col1', 'col2', 'col3'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.resourcer.use(app.acl.middleware());
|
||||||
|
|
||||||
|
app.resource({
|
||||||
|
name: 'test',
|
||||||
|
actions: {
|
||||||
|
// 来源 3:直接配置在 resource 的 action 里
|
||||||
|
list: {
|
||||||
|
filter: {
|
||||||
|
col1: 'val3',
|
||||||
|
},
|
||||||
|
fields: ['col1', 'col2', 'col3', 'col4', 'col5'],
|
||||||
|
handler: async (ctx, next) => {
|
||||||
|
ctx.body = {
|
||||||
|
'action.resourceName': ctx.action.resourceName,
|
||||||
|
'action.resourceOf': ctx.action.resourceOf,
|
||||||
|
'action.actionName': ctx.action.actionName,
|
||||||
|
'action.params': ctx.action.params,
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
54
examples/app/resource-actions/action-with-default-options.ts
Normal file
54
examples/app/resource-actions/action-with-default-options.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
# 带默认参数的 Action
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/resource-actions/action-with-default-options start
|
||||||
|
|
||||||
|
Step 2:
|
||||||
|
curl http://localhost:13000/api/test:list
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.resource({
|
||||||
|
name: 'test',
|
||||||
|
actions: {
|
||||||
|
find: {
|
||||||
|
filter: {
|
||||||
|
field1: 'value1',
|
||||||
|
},
|
||||||
|
handler: async (ctx, next) => {
|
||||||
|
ctx.body = {
|
||||||
|
'ctx.action.params': ctx.action.params,
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
68
examples/app/resource-actions/global-action.ts
Normal file
68
examples/app/resource-actions/global-action.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
# 使用全局 Action
|
||||||
|
|
||||||
|
全局 action 可用于任意 resource 中
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/resource-actions/global-action start
|
||||||
|
|
||||||
|
Step 2: test:export 的 action.params 带 fields
|
||||||
|
curl http://localhost:13000/api/test:export
|
||||||
|
|
||||||
|
Step 3: test:import 有效
|
||||||
|
curl http://localhost:13000/api/test:import
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.resourcer.registerActionHandlers({
|
||||||
|
async import(ctx, next) {
|
||||||
|
ctx.body = {
|
||||||
|
'ctx.action.params': ctx.action.params,
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
},
|
||||||
|
async export(ctx, next) {
|
||||||
|
ctx.body = {
|
||||||
|
'ctx.action.params': ctx.action.params,
|
||||||
|
};
|
||||||
|
await next();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
app.resource({
|
||||||
|
name: 'test',
|
||||||
|
// 全局的 actions 如果有默认参数可以在 actions 里配置
|
||||||
|
actions: {
|
||||||
|
export: {
|
||||||
|
fields: ['field1', 'field2'],
|
||||||
|
},
|
||||||
|
// 如果没有默认参数,可以不配置,如 import action
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
47
examples/app/resource-actions/simple.ts
Normal file
47
examples/app/resource-actions/simple.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
# 最简单的 resource actions
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/resource-actions/simple start
|
||||||
|
|
||||||
|
Step 2:
|
||||||
|
curl http://localhost:13000/api/test:list
|
||||||
|
*/
|
||||||
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
|
const app = new Application({
|
||||||
|
database: {
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: process.env.DB_TABLE_PREFIX,
|
||||||
|
},
|
||||||
|
resourcer: {
|
||||||
|
prefix: '/api',
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.resource({
|
||||||
|
name: 'test',
|
||||||
|
actions: {
|
||||||
|
async list(ctx, next) {
|
||||||
|
ctx.body = 'test list';
|
||||||
|
await next();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
app.runAsCLI();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default app;
|
@ -1,3 +1,14 @@
|
|||||||
|
/*
|
||||||
|
# 最简单的单应用
|
||||||
|
|
||||||
|
# 步骤
|
||||||
|
|
||||||
|
Step 1:
|
||||||
|
yarn run:example app/single-app start
|
||||||
|
|
||||||
|
Step 2:
|
||||||
|
curl http://localhost:13000/api/test:list
|
||||||
|
*/
|
||||||
import { Application } from '@nocobase/server';
|
import { Application } from '@nocobase/server';
|
||||||
|
|
||||||
const app = new Application({
|
const app = new Application({
|
||||||
@ -19,11 +30,13 @@ const app = new Application({
|
|||||||
plugins: [],
|
plugins: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 定义了一个 test 资源,并提供了相对应的 list 方法
|
||||||
app.resource({
|
app.resource({
|
||||||
name: 'test',
|
name: 'test',
|
||||||
actions: {
|
actions: {
|
||||||
async list(ctx) {
|
async list(ctx, next) {
|
||||||
ctx.body = 'test list';
|
ctx.body = 'test list';
|
||||||
|
await next();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -33,5 +46,3 @@ if (require.main === module) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default app;
|
export default app;
|
||||||
|
|
||||||
// http://localhost:13000/api/test:list
|
|
54
examples/database/collections/tree/adjacency-list.ts
Normal file
54
examples/database/collections/tree/adjacency-list.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
# 树结构设计 —— 邻接表
|
||||||
|
|
||||||
|
yarn run:example database/collections/tree/adjacency-list
|
||||||
|
*/
|
||||||
|
import { Database } from '@nocobase/database';
|
||||||
|
import { uid } from '@nocobase/utils';
|
||||||
|
|
||||||
|
const db = new Database({
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: `t_${uid()}_`,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.collection({
|
||||||
|
name: 'categories',
|
||||||
|
tree: 'adjacency-list',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'belongsTo',
|
||||||
|
name: 'parent',
|
||||||
|
target: 'categories',
|
||||||
|
foreignKey: 'parentId',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'hasMany',
|
||||||
|
name: 'children',
|
||||||
|
target: 'categories',
|
||||||
|
foreignKey: 'parentId',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await db.sync();
|
||||||
|
await db.close();
|
||||||
|
})();
|
||||||
|
|
||||||
|
export default db;
|
50
examples/database/collections/tree/closure-table.ts
Normal file
50
examples/database/collections/tree/closure-table.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
# 树结构设计 —— 闭包表
|
||||||
|
|
||||||
|
yarn run:example database/collections/tree/closure-table
|
||||||
|
*/
|
||||||
|
import { Database } from '@nocobase/database';
|
||||||
|
import { uid } from '@nocobase/utils';
|
||||||
|
|
||||||
|
const db = new Database({
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: `t_${uid()}_`,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.collection({
|
||||||
|
name: 'categories',
|
||||||
|
tree: 'closure-table',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'treeParent',
|
||||||
|
name: 'parent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'treeChildren',
|
||||||
|
name: 'children',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await db.sync();
|
||||||
|
await db.close();
|
||||||
|
})();
|
||||||
|
|
||||||
|
export default db;
|
50
examples/database/collections/tree/materialized-path.ts
Normal file
50
examples/database/collections/tree/materialized-path.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
# 树结构设计 —— 路径枚举
|
||||||
|
|
||||||
|
yarn run:example database/collections/tree/materialized-path
|
||||||
|
*/
|
||||||
|
import { Database } from '@nocobase/database';
|
||||||
|
import { uid } from '@nocobase/utils';
|
||||||
|
|
||||||
|
const db = new Database({
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: `t_${uid()}_`,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.collection({
|
||||||
|
name: 'categories',
|
||||||
|
tree: 'materialized-path',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'treeParent',
|
||||||
|
name: 'parent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'treeChildren',
|
||||||
|
name: 'children',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await db.sync();
|
||||||
|
await db.close();
|
||||||
|
})();
|
||||||
|
|
||||||
|
export default db;
|
50
examples/database/collections/tree/nested-set.ts
Normal file
50
examples/database/collections/tree/nested-set.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
# 树结构设计 —— 嵌套集
|
||||||
|
|
||||||
|
yarn run:example database/collections/tree/nested-set
|
||||||
|
*/
|
||||||
|
import { Database } from '@nocobase/database';
|
||||||
|
import { uid } from '@nocobase/utils';
|
||||||
|
|
||||||
|
const db = new Database({
|
||||||
|
logging: process.env.DB_LOGGING === 'on' ? console.log : false,
|
||||||
|
dialect: process.env.DB_DIALECT as any,
|
||||||
|
storage: process.env.DB_STORAGE,
|
||||||
|
username: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT as any,
|
||||||
|
timezone: process.env.DB_TIMEZONE,
|
||||||
|
tablePrefix: `t_${uid()}_`,
|
||||||
|
});
|
||||||
|
|
||||||
|
db.collection({
|
||||||
|
name: 'categories',
|
||||||
|
tree: 'nested-set',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'treeParent',
|
||||||
|
name: 'parent',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'treeChildren',
|
||||||
|
name: 'children',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
await db.sync();
|
||||||
|
await db.close();
|
||||||
|
})();
|
||||||
|
|
||||||
|
export default db;
|
47
examples/index.md
Normal file
47
examples/index.md
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# 示例
|
||||||
|
|
||||||
|
## Application
|
||||||
|
|
||||||
|
- [最简单的单应用](./app/single-app.ts)
|
||||||
|
- [支持多应用(子应用)](./app/multi-app.ts)
|
||||||
|
- 配置 Resources 和 Actions
|
||||||
|
- [最简单的 resource actions](./app/resource-actions/simple.ts)
|
||||||
|
- [带默认参数的 Action](./app/resource-actions/action-with-default-options.ts)
|
||||||
|
- [使用全局 Action](./app/resource-actions/global-action.ts)
|
||||||
|
- [Action 参数的多来源合并](./app/resource-actions/action-merge-params.ts)
|
||||||
|
- 内置 Actions 的用法
|
||||||
|
- [Collection 自动转 Resource](./app/collection2resource.ts)
|
||||||
|
- [Association 自动转 Resource](./app/association2resource.ts)
|
||||||
|
- Context
|
||||||
|
- [ctx.db 示例](./app/context/ctx.db.ts)
|
||||||
|
- [ctx.action 的重要参数示例](./app/context/ctx.action.ts)
|
||||||
|
- [ctx.action.mergeParams() 示例](./app/context/ctx.action.mergeParams.ts)
|
||||||
|
- [ctx.i18n & ctx.t() 示例](./app/context/ctx.i18n.ts)
|
||||||
|
- 中间件
|
||||||
|
- [app.use 用法](./app/middleware/app.ts)
|
||||||
|
- [app.resourcer.use 用法](./app/middleware/resourcer.ts)
|
||||||
|
- [app.acl.use 用法](./app/middleware/acl.ts)
|
||||||
|
- [ACL](./app/acl.ts)
|
||||||
|
- [国际化多语言](./app/i18n.ts)
|
||||||
|
- [自定义命令行](./app/custom-command.ts)
|
||||||
|
- [编写一个最简单的插件](./app/custom-plugin.ts)
|
||||||
|
- Application Migration
|
||||||
|
- [编写一个新的 Migration 文件](./app/migrations/add-migration.ts)
|
||||||
|
- 编写 Application 测试用例
|
||||||
|
- [最简单的测试用例](./app/__tests__/app.test.ts)
|
||||||
|
- 客户端 SDK(APIClient)示例
|
||||||
|
- [客户端常规请求 —— api.request()](./api-client/api.request.ts)
|
||||||
|
- [客户端资源请求 —— api.resource().action()](./api-client/api.resource.ts)
|
||||||
|
|
||||||
|
## Database
|
||||||
|
|
||||||
|
- 配置 Collections & Fields
|
||||||
|
- 通过 Repository 增删改查数据
|
||||||
|
- 通过 Model 增删改查数据
|
||||||
|
- 关系数据的增删改查
|
||||||
|
- 关系数据的关联操作
|
||||||
|
- 扩展字段
|
||||||
|
- 数据库事件
|
||||||
|
- 数据库迁移
|
||||||
|
|
||||||
|
## Client
|
14
examples/index.ts
Normal file
14
examples/index.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import Database from '@nocobase/database';
|
||||||
|
import Application from '@nocobase/server';
|
||||||
|
|
||||||
|
const argv = process.argv;
|
||||||
|
const path = argv.splice(2, 1).shift();
|
||||||
|
const app = require(`./${path}`).default;
|
||||||
|
|
||||||
|
if (app instanceof Application) {
|
||||||
|
app.runAsCLI(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app instanceof Database) {
|
||||||
|
console.log('Table prefix: ', app.getTablePrefix());
|
||||||
|
}
|
@ -24,7 +24,7 @@
|
|||||||
"version:alpha": "lerna version prerelease --preid alpha --force-publish=* --no-git-tag-version -m \"chore(versions): publish packages %s\"",
|
"version:alpha": "lerna version prerelease --preid alpha --force-publish=* --no-git-tag-version -m \"chore(versions): publish packages %s\"",
|
||||||
"release:force": "lerna publish from-package --yes",
|
"release:force": "lerna publish from-package --yes",
|
||||||
"release": "lerna publish",
|
"release": "lerna publish",
|
||||||
"run:example": "ts-node-dev -r dotenv/config"
|
"run:example": "ts-node-dev -r dotenv/config -r tsconfig-paths/register ./examples/index.ts"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/react": "^17.0.0",
|
"@types/react": "^17.0.0",
|
||||||
|
@ -127,7 +127,7 @@ export class ACL extends EventEmitter {
|
|||||||
return this.configResources.includes(name);
|
return this.configResources.includes(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAvailableAction(name: string, options: AvailableActionOptions) {
|
setAvailableAction(name: string, options: AvailableActionOptions = {}) {
|
||||||
this.availableActions.set(name, new AclAvailableAction(name, options));
|
this.availableActions.set(name, new AclAvailableAction(name, options));
|
||||||
|
|
||||||
if (options.aliases) {
|
if (options.aliases) {
|
||||||
|
@ -21,7 +21,7 @@ import { Collection, CollectionOptions, RepositoryType } from './collection';
|
|||||||
import { ImporterReader, ImportFileExtension } from './collection-importer';
|
import { ImporterReader, ImportFileExtension } from './collection-importer';
|
||||||
import * as FieldTypes from './fields';
|
import * as FieldTypes from './fields';
|
||||||
import { Field, FieldContext, RelationField } from './fields';
|
import { Field, FieldContext, RelationField } from './fields';
|
||||||
import { Migrations } from './migration';
|
import { MigrationItem, Migrations } from './migration';
|
||||||
import { Model } from './model';
|
import { Model } from './model';
|
||||||
import { ModelHook } from './model-hook';
|
import { ModelHook } from './model-hook';
|
||||||
import extendOperators from './operators';
|
import extendOperators from './operators';
|
||||||
@ -205,7 +205,7 @@ export class Database extends EventEmitter implements AsyncEmitter {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addMigration(item) {
|
addMigration(item: MigrationItem) {
|
||||||
return this.migrations.add(item);
|
return this.migrations.add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export { ModelCtor, Op, SyncOptions } from 'sequelize';
|
export { DataTypes, ModelCtor, Op, SyncOptions } from 'sequelize';
|
||||||
export * from './collection';
|
export * from './collection';
|
||||||
export * from './database';
|
export * from './database';
|
||||||
export { Database as default } from './database';
|
export { Database as default } from './database';
|
||||||
|
@ -206,7 +206,7 @@ describe('koa middleware', () => {
|
|||||||
sort: '-id',
|
sort: '-id',
|
||||||
});
|
});
|
||||||
expect(response.body).toMatchObject({
|
expect(response.body).toMatchObject({
|
||||||
sort: '-id',
|
sort: ['-id'],
|
||||||
filter: {
|
filter: {
|
||||||
$and: [
|
$and: [
|
||||||
{ col1: 'val1', col2: 'val2' },
|
{ col1: 'val1', col2: 'val2' },
|
||||||
|
@ -32,6 +32,9 @@ function getKeys(target: any) {
|
|||||||
export const mergeStrategies = new Map<MergeStrategyType, MergeStrategyFunc>();
|
export const mergeStrategies = new Map<MergeStrategyType, MergeStrategyFunc>();
|
||||||
|
|
||||||
mergeStrategies.set('overwrite', (_, y) => {
|
mergeStrategies.set('overwrite', (_, y) => {
|
||||||
|
if (typeof y === 'string') {
|
||||||
|
y = y.split(',');
|
||||||
|
}
|
||||||
return y;
|
return y;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ describe('i18next', () => {
|
|||||||
});
|
});
|
||||||
const response1 = await agent.get('/api/tests:get');
|
const response1 = await agent.get('/api/tests:get');
|
||||||
expect(response1.text).toEqual('Hello');
|
expect(response1.text).toEqual('Hello');
|
||||||
const response2 = await agent.get('/api/tests:get').set('Accept-Language', 'zh-CN');
|
const response2 = await agent.get('/api/tests:get').set('X-Locale', 'zh-CN');
|
||||||
expect(response2.text).toEqual('你好');
|
expect(response2.text).toEqual('你好');
|
||||||
const response3 = await agent.get('/api/tests:get?locale=zh-CN');
|
const response3 = await agent.get('/api/tests:get?locale=zh-CN');
|
||||||
expect(response3.text).toEqual('你好');
|
expect(response3.text).toEqual('你好');
|
||||||
|
@ -55,7 +55,12 @@ export function registerMiddlewares(app: Application, options: ApplicationOption
|
|||||||
const i18n = app.i18n.cloneInstance({ initImmediate: false });
|
const i18n = app.i18n.cloneInstance({ initImmediate: false });
|
||||||
ctx.i18n = i18n;
|
ctx.i18n = i18n;
|
||||||
ctx.t = i18n.t.bind(i18n);
|
ctx.t = i18n.t.bind(i18n);
|
||||||
const lng = ctx.get('X-Locale') || (ctx.request.query.locale as string) || ctx.acceptsLanguages().shift() || 'en-US';
|
const lng =
|
||||||
|
ctx.get('X-Locale') ||
|
||||||
|
(ctx.request.query.locale as string) ||
|
||||||
|
app.i18n.language ||
|
||||||
|
ctx.acceptsLanguages().shift() ||
|
||||||
|
'en-US';
|
||||||
if (lng !== '*' && lng) {
|
if (lng !== '*' && lng) {
|
||||||
i18n.changeLanguage(lng);
|
i18n.changeLanguage(lng);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user