@hz-9/a5-tenant 0.2.0-alpha.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -0
- package/dist/all.d.ts +176 -0
- package/dist/bos/index.d.ts +1 -0
- package/dist/bos/index.js +18 -0
- package/dist/bos/tenant-payload.bo.d.ts +9 -0
- package/dist/bos/tenant-payload.bo.js +3 -0
- package/dist/const/index.d.ts +21 -0
- package/dist/const/index.js +25 -0
- package/dist/core/a5-tenant.guard.d.ts +24 -0
- package/dist/core/a5-tenant.guard.js +86 -0
- package/dist/core/a5-tenant.module.d.ts +6 -0
- package/dist/core/a5-tenant.module.js +37 -0
- package/dist/core/a5-tenant.service.d.ts +20 -0
- package/dist/core/a5-tenant.service.js +3 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +21 -0
- package/dist/core/module-definition.d.ts +10 -0
- package/dist/core/module-definition.js +28 -0
- package/dist/decorators/a5-tenant-current-tenant-id.decorator.d.ts +4 -0
- package/dist/decorators/a5-tenant-current-tenant-id.decorator.js +14 -0
- package/dist/decorators/a5-tenant-current-tenant.decorator.d.ts +4 -0
- package/dist/decorators/a5-tenant-current-tenant.decorator.js +14 -0
- package/dist/decorators/index.d.ts +2 -0
- package/dist/decorators/index.js +19 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +27 -0
- package/dist/interfaces/index.d.ts +2 -0
- package/dist/interfaces/index.js +19 -0
- package/dist/interfaces/module.d.ts +26 -0
- package/dist/interfaces/module.js +3 -0
- package/dist/interfaces/tenant.d.ts +31 -0
- package/dist/interfaces/tenant.js +4 -0
- package/dist/test/integration/__import-reflect-metadata.d.ts +4 -0
- package/dist/test/integration/__import-reflect-metadata.js +7 -0
- package/dist/test/integration/a5-tenant-module.integration.spec.d.ts +1 -0
- package/dist/test/integration/a5-tenant-module.integration.spec.js +445 -0
- package/dist/test/integration/a5-tenant-module.service.d.ts +10 -0
- package/dist/test/integration/a5-tenant-module.service.js +33 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/tenant.util.d.ts +6 -0
- package/dist/utils/tenant.util.js +23 -0
- package/package.json +73 -0
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# @hz-9/a5-tenant
|
|
2
|
+
|
|
3
|
+
Tenant module for the @hz-9/a5-* series of repositories.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ 租户检查 Guard
|
|
8
|
+
- ✅ 从 headers 和 route params 中提取租户 ID
|
|
9
|
+
- ✅ 可配置的租户 Service
|
|
10
|
+
- ✅ TypeScript 完整类型支持
|
|
11
|
+
- ✅ 全局模块支持
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @hz-9/a5-tenant
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### 1. 实现租户 Service
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { Injectable } from '@nestjs/common'
|
|
25
|
+
import { A5TenantService, A5TenantCheckResult } from '@hz-9/a5-tenant'
|
|
26
|
+
|
|
27
|
+
@Injectable()
|
|
28
|
+
export class MyTenantService implements A5TenantService {
|
|
29
|
+
async checkTenant(tenantId: string, userId?: string): Promise<A5TenantCheckResult> {
|
|
30
|
+
// 在此实现你的租户检查逻辑
|
|
31
|
+
const tenant = await this.db.tenant.findUnique({ where: { id: tenantId } })
|
|
32
|
+
|
|
33
|
+
if (!tenant) {
|
|
34
|
+
return {
|
|
35
|
+
valid: false,
|
|
36
|
+
message: 'Tenant not found',
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
valid: true,
|
|
42
|
+
context: {
|
|
43
|
+
tenantId,
|
|
44
|
+
userId,
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 2. 注册模块
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { Module } from '@nestjs/common'
|
|
55
|
+
import { A5TenantModule } from '@hz-9/a5-tenant'
|
|
56
|
+
import { MyTenantService } from './my-tenant.service'
|
|
57
|
+
|
|
58
|
+
@Module({
|
|
59
|
+
imports: [
|
|
60
|
+
A5TenantModule.forRoot({
|
|
61
|
+
tenantService: new MyTenantService(),
|
|
62
|
+
isGlobal: true,
|
|
63
|
+
}),
|
|
64
|
+
],
|
|
65
|
+
})
|
|
66
|
+
export class AppModule {}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 3. 使用 Guard
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { Controller, Get, UseGuards } from '@nestjs/common'
|
|
73
|
+
import { A5TenantGuard } from '@hz-9/a5-tenant'
|
|
74
|
+
|
|
75
|
+
@Controller('/api/data')
|
|
76
|
+
@UseGuards(A5TenantGuard)
|
|
77
|
+
export class DataController {
|
|
78
|
+
@Get()
|
|
79
|
+
getData() {
|
|
80
|
+
return { data: 'example' }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## 获取租户 ID
|
|
86
|
+
|
|
87
|
+
在控制器中可以通过 request 对象获取租户信息:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { Controller, Get, Req, UseGuards } from '@nestjs/common'
|
|
91
|
+
import { FastifyRequest } from '@hz-9/a5-core/interface/http'
|
|
92
|
+
import { A5TenantGuard } from '@hz-9/a5-tenant'
|
|
93
|
+
|
|
94
|
+
@Controller('/api/data')
|
|
95
|
+
@UseGuards(A5TenantGuard)
|
|
96
|
+
export class DataController {
|
|
97
|
+
@Get()
|
|
98
|
+
getData(@Req() request: FastifyRequest) {
|
|
99
|
+
const tenantId = (request as any).tenantId
|
|
100
|
+
const tenantContext = (request as any).tenantContext
|
|
101
|
+
return { tenantId, tenantContext }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## 租户 ID 来源
|
|
107
|
+
|
|
108
|
+
Guard 会依次从以下位置尝试获取租户 ID:
|
|
109
|
+
|
|
110
|
+
1. **Headers**: `x-tenant-id` header
|
|
111
|
+
2. **Route Params**: `tenantId` 路由参数
|
|
112
|
+
|
|
113
|
+
## 配置选项
|
|
114
|
+
|
|
115
|
+
### A5TenantModuleOptions
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
interface A5TenantModuleOptions {
|
|
119
|
+
// 租户 Service 提供者
|
|
120
|
+
tenantService: A5TenantService | ProviderConfig
|
|
121
|
+
|
|
122
|
+
// 是否为全局模块(默认:true)
|
|
123
|
+
isGlobal?: boolean
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
MIT
|
package/dist/all.d.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* A5 Tenant Module - Tenant management and validation for A5 framework
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { A5TenantGuardConstructorOptions as A5TenantGuardConstructorOptions_2 } from '../interfaces';
|
|
7
|
+
import { CanActivate } from '@nestjs/common';
|
|
8
|
+
import { ConfigurableModuleCls } from '@nestjs/common';
|
|
9
|
+
import { ExecutionContext } from '@nestjs/common';
|
|
10
|
+
import type { FastifyRequest } from '@hz-9/a5-core/interface/http';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
export declare const A5_REQUEST_TENANT_ID: "A5_REQUEST_TENANT_ID";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export declare const A5_REQUEST_TENANT_PAYLOAD: "A5_REQUEST_TENANT_PAYLOAD";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
export declare const A5_TENANT_ID_HEADER: "x-tenant-id";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
export declare const A5_TENANT_ID_PARAM: "tenantId";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @public
|
|
34
|
+
*/
|
|
35
|
+
export declare const A5_TENANT_MODULE_OPTIONS: string | symbol;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Tenant Service Token
|
|
39
|
+
* @public
|
|
40
|
+
*/
|
|
41
|
+
export declare const A5_TENANT_SERVICE_TOKEN: "A5_TENANT_SERVICE_TOKEN";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @public
|
|
45
|
+
*/
|
|
46
|
+
export declare const A5TenantConfigurableModule: ConfigurableModuleCls<A5TenantGuardConstructorOptions_2, "forRoot", "createOptions", {
|
|
47
|
+
isGlobal: boolean;
|
|
48
|
+
}>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @public
|
|
52
|
+
*/
|
|
53
|
+
export declare const A5TenantCurrentTenant: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
58
|
+
export declare const A5TenantCurrentTenantId: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 租户检查 Guard
|
|
62
|
+
*
|
|
63
|
+
* 从 headers 或 route params 中获取租户 ID,并检查租户是否存在
|
|
64
|
+
*
|
|
65
|
+
* @public
|
|
66
|
+
*/
|
|
67
|
+
export declare class A5TenantGuard implements CanActivate {
|
|
68
|
+
private readonly tenantService;
|
|
69
|
+
private readonly options;
|
|
70
|
+
constructor(options: A5TenantGuardConstructorOptions, tenantService: A5TenantService);
|
|
71
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
72
|
+
private parseFromHeaders;
|
|
73
|
+
/**
|
|
74
|
+
* 从 headers 中获取租户 ID
|
|
75
|
+
*
|
|
76
|
+
* @param request - FastifyRequest 对象
|
|
77
|
+
* @returns 租户 ID 或 undefined
|
|
78
|
+
*/
|
|
79
|
+
private getTenantIdFromHeaders;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @public
|
|
84
|
+
*/
|
|
85
|
+
export declare interface A5TenantGuardConstructorOptions {
|
|
86
|
+
/**
|
|
87
|
+
* 是否启用空白自动转换为默认租户功能
|
|
88
|
+
*
|
|
89
|
+
* @defaultValue true
|
|
90
|
+
*/
|
|
91
|
+
emptyToDefault?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* 默认租户 ID
|
|
94
|
+
*
|
|
95
|
+
* @defaultValue '-'
|
|
96
|
+
*/
|
|
97
|
+
defaultTenantId?: string;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @public
|
|
102
|
+
*/
|
|
103
|
+
export declare type A5TenantGuardOptions = Required<A5TenantGuardConstructorOptions>;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @public
|
|
107
|
+
*/
|
|
108
|
+
export declare class A5TenantModule extends A5TenantConfigurableModule {
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* A5 缓存模块异步配置选项接口
|
|
113
|
+
*
|
|
114
|
+
* @public
|
|
115
|
+
*/
|
|
116
|
+
export declare interface A5TenantModuleAsyncOptions {
|
|
117
|
+
/**
|
|
118
|
+
* 工厂函数,返回 A5TenantModuleOptions 配置对象
|
|
119
|
+
*/
|
|
120
|
+
useFactory?: (...args: unknown[]) => Promise<A5TenantModuleOptions> | A5TenantModuleOptions;
|
|
121
|
+
/**
|
|
122
|
+
* 需要注入到 useFactory 函数中的依赖项
|
|
123
|
+
*/
|
|
124
|
+
inject?: unknown[];
|
|
125
|
+
/**
|
|
126
|
+
* 需要导入的模块列表
|
|
127
|
+
*/
|
|
128
|
+
imports?: unknown[];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* A5 缓存模块配置选项
|
|
133
|
+
*
|
|
134
|
+
* @public
|
|
135
|
+
*/
|
|
136
|
+
export declare type A5TenantModuleOptions = A5TenantGuardConstructorOptions;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @public
|
|
140
|
+
*
|
|
141
|
+
* 载荷信息
|
|
142
|
+
*
|
|
143
|
+
*/
|
|
144
|
+
export declare interface A5TenantPayloadBo {
|
|
145
|
+
tenantId: string;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @public
|
|
150
|
+
*/
|
|
151
|
+
export declare interface A5TenantService {
|
|
152
|
+
/**
|
|
153
|
+
* 获取租户信息,如果没有,则返回 null
|
|
154
|
+
*
|
|
155
|
+
* @param tenantId - 租户 ID
|
|
156
|
+
* @returns 检查结果
|
|
157
|
+
*/
|
|
158
|
+
getTenant(tenantId: string): Promise<A5TenantPayloadBo | null>;
|
|
159
|
+
/**
|
|
160
|
+
* 返回默认租户信息
|
|
161
|
+
*
|
|
162
|
+
* @param tenantId - 租户 ID
|
|
163
|
+
* @returns 检查结果
|
|
164
|
+
*/
|
|
165
|
+
getDefaultTenant(defaultTenantId: string): Promise<A5TenantPayloadBo>;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* @public
|
|
170
|
+
*/
|
|
171
|
+
export declare type FastifyRequestWithTenant = FastifyRequest & {
|
|
172
|
+
[A5_REQUEST_TENANT_ID]?: string;
|
|
173
|
+
[A5_REQUEST_TENANT_PAYLOAD]?: A5TenantPayloadBo;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export { }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './tenant-payload.bo';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./tenant-payload.bo"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tenant Service Token
|
|
3
|
+
* @public
|
|
4
|
+
*/
|
|
5
|
+
export declare const A5_TENANT_SERVICE_TOKEN: "A5_TENANT_SERVICE_TOKEN";
|
|
6
|
+
/**
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export declare const A5_TENANT_ID_HEADER: "x-tenant-id";
|
|
10
|
+
/**
|
|
11
|
+
* @public
|
|
12
|
+
*/
|
|
13
|
+
export declare const A5_TENANT_ID_PARAM: "tenantId";
|
|
14
|
+
/**
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
export declare const A5_REQUEST_TENANT_ID: "A5_REQUEST_TENANT_ID";
|
|
18
|
+
/**
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
export declare const A5_REQUEST_TENANT_PAYLOAD: "A5_REQUEST_TENANT_PAYLOAD";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.A5_REQUEST_TENANT_PAYLOAD = exports.A5_REQUEST_TENANT_ID = exports.A5_TENANT_ID_PARAM = exports.A5_TENANT_ID_HEADER = exports.A5_TENANT_SERVICE_TOKEN = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Tenant Service Token
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
exports.A5_TENANT_SERVICE_TOKEN = 'A5_TENANT_SERVICE_TOKEN';
|
|
9
|
+
/**
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
exports.A5_TENANT_ID_HEADER = 'x-tenant-id';
|
|
13
|
+
/**
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
exports.A5_TENANT_ID_PARAM = 'tenantId';
|
|
17
|
+
/**
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
exports.A5_REQUEST_TENANT_ID = 'A5_REQUEST_TENANT_ID';
|
|
21
|
+
/**
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
exports.A5_REQUEST_TENANT_PAYLOAD = 'A5_REQUEST_TENANT_PAYLOAD';
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { CanActivate, ExecutionContext } from '@nestjs/common';
|
|
2
|
+
import type { A5TenantGuardConstructorOptions } from '../interfaces/tenant';
|
|
3
|
+
import type { A5TenantService } from './a5-tenant.service';
|
|
4
|
+
/**
|
|
5
|
+
* 租户检查 Guard
|
|
6
|
+
*
|
|
7
|
+
* 从 headers 或 route params 中获取租户 ID,并检查租户是否存在
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export declare class A5TenantGuard implements CanActivate {
|
|
12
|
+
private readonly tenantService;
|
|
13
|
+
private readonly options;
|
|
14
|
+
constructor(options: A5TenantGuardConstructorOptions, tenantService: A5TenantService);
|
|
15
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
16
|
+
private parseFromHeaders;
|
|
17
|
+
/**
|
|
18
|
+
* 从 headers 中获取租户 ID
|
|
19
|
+
*
|
|
20
|
+
* @param request - FastifyRequest 对象
|
|
21
|
+
* @returns 租户 ID 或 undefined
|
|
22
|
+
*/
|
|
23
|
+
private getTenantIdFromHeaders;
|
|
24
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.A5TenantGuard = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const const_1 = require("../const");
|
|
18
|
+
const module_definition_1 = require("./module-definition");
|
|
19
|
+
const optionsWithDefaults = (options) => ({
|
|
20
|
+
emptyToDefault: true,
|
|
21
|
+
defaultTenantId: '-',
|
|
22
|
+
...options,
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* 租户检查 Guard
|
|
26
|
+
*
|
|
27
|
+
* 从 headers 或 route params 中获取租户 ID,并检查租户是否存在
|
|
28
|
+
*
|
|
29
|
+
* @public
|
|
30
|
+
*/
|
|
31
|
+
let A5TenantGuard = class A5TenantGuard {
|
|
32
|
+
constructor(options, tenantService) {
|
|
33
|
+
this.tenantService = tenantService;
|
|
34
|
+
this.options = optionsWithDefaults(options);
|
|
35
|
+
}
|
|
36
|
+
async canActivate(context) {
|
|
37
|
+
const request = context.switchToHttp().getRequest();
|
|
38
|
+
// TODO 后续可以基于 metadata 信息,对解析方案进行自定义优化
|
|
39
|
+
return this.parseFromHeaders(request);
|
|
40
|
+
}
|
|
41
|
+
async parseFromHeaders(request) {
|
|
42
|
+
// 尝试从 headers 中获取租户 ID
|
|
43
|
+
let tenantId = this.getTenantIdFromHeaders(request);
|
|
44
|
+
if (!tenantId) {
|
|
45
|
+
if (this.options.emptyToDefault) {
|
|
46
|
+
tenantId = this.options.defaultTenantId;
|
|
47
|
+
const tenant = await this.tenantService.getDefaultTenant(tenantId);
|
|
48
|
+
// 将租户信息存储到 request 对象中供后续使用
|
|
49
|
+
// eslint-disable-next-line require-atomic-updates
|
|
50
|
+
request[const_1.A5_REQUEST_TENANT_ID] = tenantId;
|
|
51
|
+
// eslint-disable-next-line require-atomic-updates
|
|
52
|
+
request[const_1.A5_REQUEST_TENANT_PAYLOAD] = tenant;
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
throw new common_1.UnauthorizedException('No tenant ID found');
|
|
56
|
+
}
|
|
57
|
+
// 检查租户是否存在和有效
|
|
58
|
+
const tenant = await this.tenantService.getTenant(tenantId);
|
|
59
|
+
if (!tenant) {
|
|
60
|
+
throw new common_1.ForbiddenException('tenant ID is not valid');
|
|
61
|
+
}
|
|
62
|
+
// 将租户信息存储到 request 对象中供后续使用
|
|
63
|
+
// eslint-disable-next-line require-atomic-updates
|
|
64
|
+
request[const_1.A5_REQUEST_TENANT_ID] = tenantId;
|
|
65
|
+
// eslint-disable-next-line require-atomic-updates
|
|
66
|
+
request[const_1.A5_REQUEST_TENANT_PAYLOAD] = tenant;
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 从 headers 中获取租户 ID
|
|
71
|
+
*
|
|
72
|
+
* @param request - FastifyRequest 对象
|
|
73
|
+
* @returns 租户 ID 或 undefined
|
|
74
|
+
*/
|
|
75
|
+
getTenantIdFromHeaders(request) {
|
|
76
|
+
return request.headers[const_1.A5_TENANT_ID_HEADER];
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
exports.A5TenantGuard = A5TenantGuard;
|
|
80
|
+
exports.A5TenantGuard = A5TenantGuard = __decorate([
|
|
81
|
+
(0, common_1.Injectable)(),
|
|
82
|
+
__param(0, (0, common_1.Inject)(module_definition_1.A5_TENANT_MODULE_OPTIONS)),
|
|
83
|
+
__param(1, (0, common_1.Inject)(const_1.A5_TENANT_SERVICE_TOKEN)),
|
|
84
|
+
__metadata("design:paramtypes", [Object, Object])
|
|
85
|
+
], A5TenantGuard);
|
|
86
|
+
//# sourceMappingURL=a5-tenant.guard.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.A5TenantModule = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const core_1 = require("@nestjs/core");
|
|
12
|
+
const a5_tenant_guard_1 = require("./a5-tenant.guard");
|
|
13
|
+
const module_definition_1 = require("./module-definition");
|
|
14
|
+
/**
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
let A5TenantModule = class A5TenantModule extends module_definition_1.A5TenantConfigurableModule {
|
|
18
|
+
};
|
|
19
|
+
exports.A5TenantModule = A5TenantModule;
|
|
20
|
+
exports.A5TenantModule = A5TenantModule = __decorate([
|
|
21
|
+
(0, common_1.Global)(),
|
|
22
|
+
(0, common_1.Module)({
|
|
23
|
+
providers: [
|
|
24
|
+
{
|
|
25
|
+
provide: core_1.APP_GUARD,
|
|
26
|
+
useClass: a5_tenant_guard_1.A5TenantGuard,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
// exports: [
|
|
30
|
+
// {
|
|
31
|
+
// provide: APP_GUARD,
|
|
32
|
+
// useClass: A5TenantGuard,
|
|
33
|
+
// },
|
|
34
|
+
// ],
|
|
35
|
+
})
|
|
36
|
+
], A5TenantModule);
|
|
37
|
+
//# sourceMappingURL=a5-tenant.module.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { A5TenantPayloadBo } from '../bos/tenant-payload.bo';
|
|
2
|
+
/**
|
|
3
|
+
* @public
|
|
4
|
+
*/
|
|
5
|
+
export interface A5TenantService {
|
|
6
|
+
/**
|
|
7
|
+
* 获取租户信息,如果没有,则返回 null
|
|
8
|
+
*
|
|
9
|
+
* @param tenantId - 租户 ID
|
|
10
|
+
* @returns 检查结果
|
|
11
|
+
*/
|
|
12
|
+
getTenant(tenantId: string): Promise<A5TenantPayloadBo | null>;
|
|
13
|
+
/**
|
|
14
|
+
* 返回默认租户信息
|
|
15
|
+
*
|
|
16
|
+
* @param tenantId - 租户 ID
|
|
17
|
+
* @returns 检查结果
|
|
18
|
+
*/
|
|
19
|
+
getDefaultTenant(defaultTenantId: string): Promise<A5TenantPayloadBo>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./a5-tenant.guard"), exports);
|
|
18
|
+
__exportStar(require("./a5-tenant.module"), exports);
|
|
19
|
+
__exportStar(require("./a5-tenant.service"), exports);
|
|
20
|
+
__exportStar(require("./module-definition"), exports);
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @public
|
|
3
|
+
*/
|
|
4
|
+
export declare const A5TenantConfigurableModule: import("@nestjs/common").ConfigurableModuleCls<import("../interfaces").A5TenantGuardConstructorOptions, "forRoot", "createOptions", {
|
|
5
|
+
isGlobal: boolean;
|
|
6
|
+
}>;
|
|
7
|
+
/**
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
export declare const A5_TENANT_MODULE_OPTIONS: string | symbol;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.A5_TENANT_MODULE_OPTIONS = exports.A5TenantConfigurableModule = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
// eslint-disable-next-line @rushstack/typedef-var
|
|
6
|
+
const moduleDefinition = new common_1.ConfigurableModuleBuilder({
|
|
7
|
+
moduleName: 'A5Tenant',
|
|
8
|
+
})
|
|
9
|
+
.setExtras({
|
|
10
|
+
isGlobal: true,
|
|
11
|
+
}, (definition, options) => ({
|
|
12
|
+
...definition,
|
|
13
|
+
global: options.isGlobal,
|
|
14
|
+
}))
|
|
15
|
+
.setClassMethodName('forRoot')
|
|
16
|
+
.setFactoryMethodName('createOptions')
|
|
17
|
+
.build();
|
|
18
|
+
/**
|
|
19
|
+
* @public
|
|
20
|
+
*/
|
|
21
|
+
// eslint-disable-next-line @rushstack/typedef-var
|
|
22
|
+
exports.A5TenantConfigurableModule = moduleDefinition.ConfigurableModuleClass;
|
|
23
|
+
/**
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
// eslint-disable-next-line @rushstack/typedef-var
|
|
27
|
+
exports.A5_TENANT_MODULE_OPTIONS = moduleDefinition.MODULE_OPTIONS_TOKEN;
|
|
28
|
+
//# sourceMappingURL=module-definition.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.A5TenantCurrentTenantId = void 0;
|
|
4
|
+
/* eslint-disable @rushstack/typedef-var */
|
|
5
|
+
const common_1 = require("@nestjs/common");
|
|
6
|
+
const tenant_util_1 = require("../utils/tenant.util");
|
|
7
|
+
/**
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
exports.A5TenantCurrentTenantId = (0, common_1.createParamDecorator)((data, ctx) => {
|
|
11
|
+
const request = ctx.switchToHttp().getRequest();
|
|
12
|
+
return tenant_util_1.A5TenantUtil.getTenantIdFromRequest(request);
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=a5-tenant-current-tenant-id.decorator.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.A5TenantCurrentTenant = void 0;
|
|
4
|
+
/* eslint-disable @rushstack/typedef-var */
|
|
5
|
+
const common_1 = require("@nestjs/common");
|
|
6
|
+
const tenant_util_1 = require("../utils/tenant.util");
|
|
7
|
+
/**
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
exports.A5TenantCurrentTenant = (0, common_1.createParamDecorator)((data, ctx) => {
|
|
11
|
+
const request = ctx.switchToHttp().getRequest();
|
|
12
|
+
return tenant_util_1.A5TenantUtil.getTenantFromRequest(request);
|
|
13
|
+
});
|
|
14
|
+
//# sourceMappingURL=a5-tenant-current-tenant.decorator.js.map
|