@xfilecom/core-sdk 1.3.23
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 +664 -0
- package/dist/core.module.d.ts +39 -0
- package/dist/core.module.js +188 -0
- package/dist/core.module.js.map +1 -0
- package/dist/database/database.constants.d.ts +2 -0
- package/dist/database/database.constants.js +6 -0
- package/dist/database/database.constants.js.map +1 -0
- package/dist/database/database.module.d.ts +19 -0
- package/dist/database/database.module.js +56 -0
- package/dist/database/database.module.js.map +1 -0
- package/dist/database/database.query.d.ts +103 -0
- package/dist/database/database.query.example.d.ts +36 -0
- package/dist/database/database.query.example.js +148 -0
- package/dist/database/database.query.example.js.map +1 -0
- package/dist/database/database.query.js +369 -0
- package/dist/database/database.query.js.map +1 -0
- package/dist/database/database.service.d.ts +18 -0
- package/dist/database/database.service.js +110 -0
- package/dist/database/database.service.js.map +1 -0
- package/dist/database/example-usage.d.ts +0 -0
- package/dist/database/example-usage.js +1 -0
- package/dist/database/example-usage.js.map +1 -0
- package/dist/decorators/public.decorator.d.ts +2 -0
- package/dist/decorators/public.decorator.js +8 -0
- package/dist/decorators/public.decorator.js.map +1 -0
- package/dist/decorators/roles.decorator.d.ts +2 -0
- package/dist/decorators/roles.decorator.js +8 -0
- package/dist/decorators/roles.decorator.js.map +1 -0
- package/dist/decorators/user.decorator.d.ts +7 -0
- package/dist/decorators/user.decorator.js +10 -0
- package/dist/decorators/user.decorator.js.map +1 -0
- package/dist/filters/exception.filter.d.ts +36 -0
- package/dist/filters/exception.filter.js +201 -0
- package/dist/filters/exception.filter.js.map +1 -0
- package/dist/guards/jwt-auth.guard.d.ts +14 -0
- package/dist/guards/jwt-auth.guard.js +103 -0
- package/dist/guards/jwt-auth.guard.js.map +1 -0
- package/dist/guards/roles.guard.d.ts +7 -0
- package/dist/guards/roles.guard.js +47 -0
- package/dist/guards/roles.guard.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +91 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptors/database-check.interceptor.d.ts +12 -0
- package/dist/interceptors/database-check.interceptor.js +60 -0
- package/dist/interceptors/database-check.interceptor.js.map +1 -0
- package/dist/interceptors/error-handling.interceptor.d.ts +8 -0
- package/dist/interceptors/error-handling.interceptor.js +33 -0
- package/dist/interceptors/error-handling.interceptor.js.map +1 -0
- package/dist/interceptors/logging.interceptor.d.ts +21 -0
- package/dist/interceptors/logging.interceptor.js +167 -0
- package/dist/interceptors/logging.interceptor.js.map +1 -0
- package/dist/interceptors/response-transform.interceptor.d.ts +5 -0
- package/dist/interceptors/response-transform.interceptor.js +30 -0
- package/dist/interceptors/response-transform.interceptor.js.map +1 -0
- package/dist/utils/auth.helpers.d.ts +19 -0
- package/dist/utils/auth.helpers.js +77 -0
- package/dist/utils/auth.helpers.js.map +1 -0
- package/dist/utils/config-loader.utils.d.ts +22 -0
- package/dist/utils/config-loader.utils.js +77 -0
- package/dist/utils/config-loader.utils.js.map +1 -0
- package/dist/utils/config.validator.d.ts +13 -0
- package/dist/utils/config.validator.js +82 -0
- package/dist/utils/config.validator.js.map +1 -0
- package/dist/utils/controller.helpers.d.ts +58 -0
- package/dist/utils/controller.helpers.js +104 -0
- package/dist/utils/controller.helpers.js.map +1 -0
- package/dist/utils/crypto.utils.d.ts +12 -0
- package/dist/utils/crypto.utils.js +53 -0
- package/dist/utils/crypto.utils.js.map +1 -0
- package/dist/utils/email-hash.utils.d.ts +7 -0
- package/dist/utils/email-hash.utils.js +42 -0
- package/dist/utils/email-hash.utils.js.map +1 -0
- package/dist/utils/env.utils.d.ts +8 -0
- package/dist/utils/env.utils.js +27 -0
- package/dist/utils/env.utils.js.map +1 -0
- package/dist/utils/error.utils.d.ts +6 -0
- package/dist/utils/error.utils.js +65 -0
- package/dist/utils/error.utils.js.map +1 -0
- package/dist/utils/hash-verification.utils.d.ts +35 -0
- package/dist/utils/hash-verification.utils.js +133 -0
- package/dist/utils/hash-verification.utils.js.map +1 -0
- package/dist/utils/logger.helpers.d.ts +71 -0
- package/dist/utils/logger.helpers.js +293 -0
- package/dist/utils/logger.helpers.js.map +1 -0
- package/dist/utils/logging.config.d.ts +6 -0
- package/dist/utils/logging.config.js +42 -0
- package/dist/utils/logging.config.js.map +1 -0
- package/dist/utils/service.helpers.d.ts +22 -0
- package/dist/utils/service.helpers.js +73 -0
- package/dist/utils/service.helpers.js.map +1 -0
- package/dist/utils/yaml-config.loader.d.ts +15 -0
- package/dist/utils/yaml-config.loader.js +219 -0
- package/dist/utils/yaml-config.loader.js.map +1 -0
- package/package.json +47 -0
- package/scripts/publish-to-gitlab.mjs +209 -0
package/README.md
ADDED
|
@@ -0,0 +1,664 @@
|
|
|
1
|
+
# @xfilecom/core-sdk
|
|
2
|
+
|
|
3
|
+
NestJS 마이크로서비스용 Core SDK — DB, 인증, 로깅, 공통 응답, 설정 로드, 해시 검증 등을 하나의 패키지로 제공합니다.
|
|
4
|
+
|
|
5
|
+
## 목차
|
|
6
|
+
|
|
7
|
+
- [설치](#설치)
|
|
8
|
+
- [설정 파일 위치 (표준)](#설정-파일-위치-표준)
|
|
9
|
+
- [설정 소스 (.env vs YAML)](#설정-소스-env-vs-yaml)
|
|
10
|
+
- [사용 범위 요약](#사용-범위-요약)
|
|
11
|
+
- [CoreModule](#coremodule)
|
|
12
|
+
- [설정 파일 (application.yml)](#설정-파일-applicationyml)
|
|
13
|
+
- [설정 루트·Config 로더](#설정-루트config-로더)
|
|
14
|
+
- [Crypto (비밀번호·조회용 해시)](#crypto-비밀번호조회용-해시)
|
|
15
|
+
- [Hash verification](#hash-verification)
|
|
16
|
+
- [기능 옵션](#기능-옵션)
|
|
17
|
+
- [Database](#database)
|
|
18
|
+
- [Controller / Service 예시](#controller--service-예시)
|
|
19
|
+
- [Guards · Decorators · Interceptors · Filters](#guards--decorators--interceptors--filters)
|
|
20
|
+
- [기타 유틸](#기타-유틸)
|
|
21
|
+
- [Database 연결 실패 시](#database-연결-실패-시)
|
|
22
|
+
- [수정 사항](#수정-사항)
|
|
23
|
+
- [라이선스](#라이선스)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 설치
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @xfilecom/core-sdk
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 설정 파일 위치 (표준)
|
|
36
|
+
|
|
37
|
+
다른 서비스/팀과 동일한 방식으로 사용하려면 **설정 파일 위치를 아래 규칙으로 통일**합니다.
|
|
38
|
+
|
|
39
|
+
| 항목 | 규칙 |
|
|
40
|
+
|------|------|
|
|
41
|
+
| **파일 경로** | `{프로젝트 루트}/application.yml` 또는 `application.yaml` |
|
|
42
|
+
| **프로젝트 루트** | 기본값 `process.cwd()`. 필요 시 `loadApplicationYaml({ rootDir })` 로 지정 |
|
|
43
|
+
| **파일명 우선순위** | `application.yml` → `application.yaml` (먼저 찾은 것 사용) |
|
|
44
|
+
| **환경별 병합** | 같은 루트에 `application-{env}.yml` (예: `application-development.yml`) 있으면 base 위에 deep merge |
|
|
45
|
+
|
|
46
|
+
코드에서 루트만 맞추면 항상 같은 위치에서 설정을 읽습니다.
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { loadApplicationYaml, DEFAULT_CONFIG_FILENAMES } from '@xfilecom/core-sdk';
|
|
50
|
+
|
|
51
|
+
// 기본: process.cwd() 에서 application.yml / application.yaml 탐색
|
|
52
|
+
const config = await loadApplicationYaml();
|
|
53
|
+
|
|
54
|
+
// 루트 지정 (예: 패키지 루트)
|
|
55
|
+
const config = await loadApplicationYaml({ rootDir: path.join(__dirname, '..') });
|
|
56
|
+
|
|
57
|
+
// 환경별 설정 병합
|
|
58
|
+
const config = await loadApplicationYaml({ env: process.env.NODE_ENV });
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- 파일이 없으면 예외 없이 빈 객체 `{}` 반환.
|
|
62
|
+
- 상수: `DEFAULT_CONFIG_FILENAMES`, `ENV_CONFIG_FILENAME_PATTERN` (index에서 export).
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 설정 소스 (.env vs YAML)
|
|
67
|
+
|
|
68
|
+
**YAML을 쓸지, 환경 변수(.env)만 쓸지, 또는 둘 다 쓸지**를 설정할 수 있습니다.
|
|
69
|
+
|
|
70
|
+
### 옵션 (configSource)
|
|
71
|
+
|
|
72
|
+
| 값 | 동작 |
|
|
73
|
+
|----|------|
|
|
74
|
+
| `yaml` | `application.yml`만 읽음 (기본값) |
|
|
75
|
+
| `env` | YAML을 읽지 않고, `process.env`(DB_*, JWT_*, HASH_* 등)로 config 객체 생성 |
|
|
76
|
+
| `yamlWithEnvOverrides` | YAML 로드 후, 같은 키가 환경 변수에 있으면 그 값으로 덮어씀 |
|
|
77
|
+
|
|
78
|
+
### 어디에 설정하나요?
|
|
79
|
+
|
|
80
|
+
**1) 코드에서 지정 (권장)**
|
|
81
|
+
|
|
82
|
+
`loadApplicationYaml()` 호출 시 옵션으로 넘깁니다.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { loadApplicationYaml, type ConfigSource } from '@xfilecom/core-sdk';
|
|
86
|
+
|
|
87
|
+
// YAML만 사용 (기본)
|
|
88
|
+
const config = await loadApplicationYaml();
|
|
89
|
+
|
|
90
|
+
// .env(환경 변수)만 사용 — application.yml 없이 DB_*, JWT_* 등으로 config 구성
|
|
91
|
+
const config = await loadApplicationYaml({ configSource: 'env' });
|
|
92
|
+
|
|
93
|
+
// YAML 먼저 로드하고, 환경 변수로 덮어쓰기
|
|
94
|
+
const config = await loadApplicationYaml({ configSource: 'yamlWithEnvOverrides', env: process.env.NODE_ENV });
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**2) 환경 변수로 지정**
|
|
98
|
+
|
|
99
|
+
코드는 그대로 두고, **실행 환경 또는 .env 파일**에 다음 키로 소스를 정합니다.
|
|
100
|
+
|
|
101
|
+
| 설정 위치 | 예시 |
|
|
102
|
+
|-----------|------|
|
|
103
|
+
| **.env 파일** | `CONFIG_SOURCE=env` 또는 `CONFIG_SOURCE=yamlWithEnvOverrides` |
|
|
104
|
+
| **쉘/실행 시** | `CONFIG_SOURCE=env node dist/main.js` |
|
|
105
|
+
|
|
106
|
+
- `CONFIG_SOURCE` 가 없으면 기본값 `yaml` 로 동작합니다.
|
|
107
|
+
- 상수 이름: `CONFIG_SOURCE_ENV_KEY` (`'CONFIG_SOURCE'`) — index에서 export.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# .env
|
|
111
|
+
CONFIG_SOURCE=env
|
|
112
|
+
DB_HOST=localhost
|
|
113
|
+
JWT_SECRET=my-secret
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
이렇게 하면 `loadApplicationYaml()` 호출 시 옵션에 `configSource`를 안 넘겨도, `process.env.CONFIG_SOURCE`를 읽어서 동일하게 동작합니다. (앱에서 dotenv 등으로 .env를 먼저 로드해야 함)
|
|
117
|
+
|
|
118
|
+
### configSource: 'env' 일 때 매핑
|
|
119
|
+
|
|
120
|
+
환경 변수만 쓸 때 아래 키들이 config 객체로 매핑됩니다.
|
|
121
|
+
|
|
122
|
+
- **database**: `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_NAME`(또는 `DB_DATABASE`), `DB_SSL`, `DB_CONNECTION_LIMIT`
|
|
123
|
+
- **jwt**: `JWT_SECRET`, `JWT_EXPIRES_IN`
|
|
124
|
+
- **hashVerification.default**: `HASH_VERIFICATION_SECRET`
|
|
125
|
+
- **hashVerification.profiles.email**: `EMAIL_HASH_SECRET` (없으면 `HASH_VERIFICATION_SECRET`)
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 사용 범위 요약
|
|
130
|
+
|
|
131
|
+
| 구분 | 내용 |
|
|
132
|
+
|------|------|
|
|
133
|
+
| **모듈** | `CoreModule.forRoot()`, `forMicroservice()`, `forHttpApi()`, `forHybrid()` |
|
|
134
|
+
| **설정** | `loadApplicationYaml()` / `loadApplicationYamlSync()` — 표준 경로 `application.yml` 또는 `configSource: 'env'` 로 환경 변수만 사용 |
|
|
135
|
+
| **설정 루트·로더** | `resolveConfigRootDir()` (모노레포 등 설정 디렉터리 탐색), `createConfigLoader()` (한 번 로드 + 캐시 + getFullConfig / getServiceConfig) |
|
|
136
|
+
| **Crypto** | `pbkdf2HashPassword` / `pbkdf2VerifyPassword` (saltHex:hashHex), `lookupHash` / `compareLookupHash` (키 파생 옵션), `deriveKeySha256` |
|
|
137
|
+
| **해시/검증** | `hashValue`, `compareValue`, `hashByKey`, `compareByKey`, `hashEmail`, `compareEmail`, `lookupHash`, `compareLookupHash` (YAML `hashVerification` + `keyDerivation` 연동) |
|
|
138
|
+
| **DB** | Drizzle ORM + MySQL, `DatabaseQuery`, `ServiceHelpers` (getDb, withTransaction, createPaginationResult 등) |
|
|
139
|
+
| **컨트롤러** | `ControllerHelpers` (parsePagination, parseSort, parseFilters, success, error) |
|
|
140
|
+
| **인증** | `JwtAuthGuard`, `RolesGuard`, `AuthHelpers`, `@Public()`, `@User()`, `@Roles()` |
|
|
141
|
+
| **인터셉터** | Logging, ErrorHandling, ResponseTransform, DatabaseCheck |
|
|
142
|
+
| **필터** | `AllExceptionsFilter` |
|
|
143
|
+
| **기타** | `ErrorUtils`, `LoggerHelpers`, `ConfigValidator`, `getLoggingConfigFromEnv` |
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## CoreModule
|
|
148
|
+
|
|
149
|
+
### 마이크로서비스
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { Module } from '@nestjs/common';
|
|
153
|
+
import { CoreModule } from '@xfilecom/core-sdk';
|
|
154
|
+
|
|
155
|
+
@Module({
|
|
156
|
+
imports: [CoreModule.forMicroservice()],
|
|
157
|
+
})
|
|
158
|
+
export class AppModule {}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### HTTP API
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
import { Module } from '@nestjs/common';
|
|
165
|
+
import { CoreModule } from '@xfilecom/core-sdk';
|
|
166
|
+
import { schema } from './database/schema';
|
|
167
|
+
|
|
168
|
+
@Module({
|
|
169
|
+
imports: [
|
|
170
|
+
CoreModule.forHttpApi({
|
|
171
|
+
database: { schema },
|
|
172
|
+
}),
|
|
173
|
+
],
|
|
174
|
+
})
|
|
175
|
+
export class AppModule {}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 하이브리드 (HTTP + TCP)
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { Module } from '@nestjs/common';
|
|
182
|
+
import { CoreModule } from '@xfilecom/core-sdk';
|
|
183
|
+
|
|
184
|
+
@Module({
|
|
185
|
+
imports: [CoreModule.forHybrid()],
|
|
186
|
+
})
|
|
187
|
+
export class AppModule {}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### 커스텀 설정 (forRoot)
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import { Module } from '@nestjs/common';
|
|
194
|
+
import { CoreModule } from '@xfilecom/core-sdk';
|
|
195
|
+
import { mySchema } from './database/schema';
|
|
196
|
+
|
|
197
|
+
@Module({
|
|
198
|
+
imports: [
|
|
199
|
+
CoreModule.forRoot({
|
|
200
|
+
database: { schema: mySchema },
|
|
201
|
+
jwt: { secret: process.env.JWT_SECRET, expiresIn: '7d' },
|
|
202
|
+
response: { commonResponse: true },
|
|
203
|
+
interceptors: { logging: true, databaseCheck: true },
|
|
204
|
+
guards: { jwt: true },
|
|
205
|
+
filters: { exception: true },
|
|
206
|
+
}),
|
|
207
|
+
],
|
|
208
|
+
})
|
|
209
|
+
export class AppModule {}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## 설정 파일 (application.yml)
|
|
215
|
+
|
|
216
|
+
- **위치**: [설정 파일 위치 (표준)](#설정-파일-위치-표준) 참고.
|
|
217
|
+
- **소스 선택**: [설정 소스 (.env vs YAML)](#설정-소스-env-vs-yaml) 참고 — `configSource` 또는 `CONFIG_SOURCE` 로 YAML / .env 선택 가능.
|
|
218
|
+
- **비동기**: `loadApplicationYaml(options?)`
|
|
219
|
+
- **동기**: `loadApplicationYamlSync(options?)` (bootstrap 전 등)
|
|
220
|
+
- **syncEnvKeys**: YAML 로드 후 config 일부를 `process.env`에 채울 수 있음. ConfigValidator / JwtAuthGuard 등이 env를 참조할 때 YAML만 써도 연동 가능.
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import { loadApplicationYaml, loadApplicationYamlSync } from '@xfilecom/core-sdk';
|
|
224
|
+
|
|
225
|
+
const config = await loadApplicationYaml();
|
|
226
|
+
const dbHost = config?.database?.host ?? process.env.DB_HOST;
|
|
227
|
+
|
|
228
|
+
const configWithEnv = await loadApplicationYaml({ env: process.env.NODE_ENV });
|
|
229
|
+
const configSync = loadApplicationYamlSync({ rootDir: path.join(__dirname, '..') });
|
|
230
|
+
|
|
231
|
+
// YAML 로드 후 JWT 등을 process.env에 동기화 (Guard/ConfigValidator와 연동)
|
|
232
|
+
const config = await loadApplicationYaml({
|
|
233
|
+
env: process.env.NODE_ENV,
|
|
234
|
+
syncEnvKeys: {
|
|
235
|
+
JWT_SECRET: 'jwt.secret',
|
|
236
|
+
JWT_EXPIRES_IN: 'jwt.expiresIn',
|
|
237
|
+
HASH_VERIFICATION_SECRET: 'hashVerification.default.secret',
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
설정 파일 안에는 DB, JWT, `hashVerification` 등 자유롭게 정의하고, 코드에서 `config?.database`, `config?.hashVerification` 등으로 접근하면 됩니다. JWT는 `CoreModule.forHttpApi({ jwt: config?.jwt })` 로 넘기면 `ConfigValidator.validateJwtConfig` 시 env 검사를 건너뜁니다.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## 설정 루트·Config 로더
|
|
247
|
+
|
|
248
|
+
모노레포·실행 위치와 관계없이 설정 디렉터리를 찾고, **한 번 로드 → env 동기화 → 전역 캐시 → getFullConfig / getServiceConfig** 패턴을 쓰려면 아래 API를 사용합니다.
|
|
249
|
+
|
|
250
|
+
### resolveConfigRootDir(options?)
|
|
251
|
+
|
|
252
|
+
`application.yml` / `application.yaml` 이 있는 디렉터리를 후보 순서대로 탐색해 반환합니다. 기본 후보는 모노레포 패턴(`libs/config/config`, `../../libs/config/config`, `../libs/config/config`, cwd)입니다.
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
import { resolveConfigRootDir, loadApplicationYamlSync, DEFAULT_CONFIG_ROOT_CANDIDATES } from '@xfilecom/core-sdk';
|
|
256
|
+
|
|
257
|
+
// 기본 (process.cwd() 기준 후보 탐색)
|
|
258
|
+
const rootDir = resolveConfigRootDir();
|
|
259
|
+
|
|
260
|
+
// 후보·파일명 지정
|
|
261
|
+
const rootDir2 = resolveConfigRootDir({
|
|
262
|
+
cwd: process.cwd(),
|
|
263
|
+
candidates: ['libs/config/config', 'config', '.'],
|
|
264
|
+
configFilenames: ['application.yml', 'application.yaml'],
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const config = loadApplicationYamlSync({ rootDir, env: process.env.NODE_ENV });
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### createConfigLoader(options)
|
|
271
|
+
|
|
272
|
+
한 번 로드한 config를 캐시하고, `getFullConfig()` / `getServiceConfig(serviceKey, overrides?)` 를 반환합니다. auth-service, mail-service, chat-service 등에서 동일한 패턴으로 사용할 수 있습니다.
|
|
273
|
+
|
|
274
|
+
- **getFullConfig()**: 이미 로드된 full config 반환 (최초 호출 시 1회 로드 후 저장).
|
|
275
|
+
- **getServiceConfig(serviceKey, overrides?)**: `full.common` + `full[serviceKey]` 병합. `overrides.portEnvKey` 가 있으면 `process.env[portEnvKey]` 로 `server.port` 덮어씀.
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
import { createConfigLoader, resolveConfigRootDir } from '@xfilecom/core-sdk';
|
|
279
|
+
|
|
280
|
+
const { getFullConfig, getServiceConfig } = createConfigLoader({
|
|
281
|
+
rootDir: resolveConfigRootDir(),
|
|
282
|
+
env: process.env.APP_PROFILE ?? (process.env.NODE_ENV === 'production' ? 'prod' : 'dev'),
|
|
283
|
+
syncEnvKeys: {
|
|
284
|
+
JWT_SECRET: 'auth-service.jwt.secret',
|
|
285
|
+
JWT_EXPIRES_IN: 'auth-service.jwt.expiresIn',
|
|
286
|
+
},
|
|
287
|
+
globalKey: 'auth-service',
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// 서비스별 config (common + auth-service 병합, 포트는 AUTH_SERVICE_PORT 로 덮어쓰기)
|
|
291
|
+
export const getAuthServiceConfig = () =>
|
|
292
|
+
getServiceConfig('auth-service', { portEnvKey: 'AUTH_SERVICE_PORT' });
|
|
293
|
+
export { getFullConfig };
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
- **rootDir** 없으면 `resolveConfigRootDir()` 로 자동 해석.
|
|
297
|
+
- **globalKey**: 동일 키로 캐시해 두어 재호출 시 같은 객체 반환.
|
|
298
|
+
- YAML 구조 예: `common: { ... }, auth-service: { server: { port: 3001 }, jwt: { ... } }, mail-service: { ... }`.
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Crypto (비밀번호·조회용 해시)
|
|
303
|
+
|
|
304
|
+
### PBKDF2 비밀번호 (usdt3·기존 DB 호환)
|
|
305
|
+
|
|
306
|
+
저장 형식 `saltHex:hashHex`, 기본 pbkdf2-sha512 100_000회·64바이트. bcrypt와 형식이 달라 기존 usdt3 DB와 호환하려면 아래 API 사용.
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
import {
|
|
310
|
+
pbkdf2HashPassword,
|
|
311
|
+
pbkdf2VerifyPassword,
|
|
312
|
+
PBKDF2_DEFAULT_ITERATIONS,
|
|
313
|
+
PBKDF2_DEFAULT_KEYLEN,
|
|
314
|
+
PBKDF2_DEFAULT_DIGEST,
|
|
315
|
+
type Pbkdf2Options,
|
|
316
|
+
} from '@xfilecom/core-sdk';
|
|
317
|
+
|
|
318
|
+
// 해시 (salt 미지정 시 랜덤 salt 자동 생성)
|
|
319
|
+
const stored = pbkdf2HashPassword('myPassword');
|
|
320
|
+
// 옵션 지정 시
|
|
321
|
+
const stored2 = pbkdf2HashPassword('myPassword', undefined, {
|
|
322
|
+
iterations: 100_000,
|
|
323
|
+
keylen: 64,
|
|
324
|
+
digest: 'sha512',
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
// 검증
|
|
328
|
+
const ok = pbkdf2VerifyPassword('myPassword', stored);
|
|
329
|
+
const ok2 = pbkdf2VerifyPassword('myPassword', stored2, { iterations: 100_000, keylen: 64, digest: 'sha512' });
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### 조회용 해시 (키 파생 — usdt3·기존 DB 호환)
|
|
333
|
+
|
|
334
|
+
기존 DB가 **HMAC 키 = sha256(secret)** 방식이면 `keyDerivation: 'sha256'` 사용. YAML 프로필 또는 `lookupHash`/`compareLookupHash` 로 동일한 해시 생성.
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
import { lookupHash, compareLookupHash, emailNormalizer, digitsNormalizer } from '@xfilecom/core-sdk';
|
|
338
|
+
|
|
339
|
+
// secret을 sha256(secret)으로 파생해 HMAC (usdt3 호환)
|
|
340
|
+
const emailHash = lookupHash('user@example.com', process.env.EMAIL_HASH_SECRET, {
|
|
341
|
+
keyDerivation: 'sha256',
|
|
342
|
+
normalizer: emailNormalizer,
|
|
343
|
+
});
|
|
344
|
+
const match = compareLookupHash('user@example.com', row.email_hash, process.env.EMAIL_HASH_SECRET, {
|
|
345
|
+
keyDerivation: 'sha256',
|
|
346
|
+
normalizer: emailNormalizer,
|
|
347
|
+
});
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
YAML에서 프로필별로 지정할 때는 `hashVerification.profiles.email` 에 `keyDerivation: sha256` 추가 후 `hashByKey('email', ...)` 사용하면 동일한 결과.
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
## Hash verification
|
|
355
|
+
|
|
356
|
+
이메일·전화번호·식별자 등 문자열을 YAML 설정 기반으로 HMAC 해시 후 저장/비교할 수 있습니다. **설정은 표준 위치의 application.yml** 에 두면 됩니다.
|
|
357
|
+
|
|
358
|
+
### YAML 구조 (application.yml 에 추가)
|
|
359
|
+
|
|
360
|
+
```yaml
|
|
361
|
+
hashVerification:
|
|
362
|
+
default:
|
|
363
|
+
algorithm: sha256
|
|
364
|
+
secret: ${HASH_VERIFICATION_SECRET}
|
|
365
|
+
encoding: hex
|
|
366
|
+
keyDerivation: sha256 # usdt3 등 기존 DB 호환 시: HMAC 키 = sha256(secret)
|
|
367
|
+
profiles:
|
|
368
|
+
email:
|
|
369
|
+
secret: ${EMAIL_HASH_SECRET}
|
|
370
|
+
normalizer: email
|
|
371
|
+
keyDerivation: sha256
|
|
372
|
+
phone:
|
|
373
|
+
secret: ${PHONE_HASH_SECRET}
|
|
374
|
+
normalizer: digits
|
|
375
|
+
keyDerivation: sha256
|
|
376
|
+
userId:
|
|
377
|
+
secret: ${USER_ID_HASH_SECRET}
|
|
378
|
+
tenantId:
|
|
379
|
+
secret: ${TENANT_HASH_SECRET}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
- **default**: 해당 키가 없을 때 사용하는 공통 설정.
|
|
383
|
+
- **profiles**: 키별 설정. 새 키만 YAML에 추가하면 코드 수정 없이 `hashByKey(key, value, hv)` 로 처리 가능.
|
|
384
|
+
- **normalizer**: `email`(trim+소문자), `digits`(숫자만). 생략 시 입력 그대로 해시.
|
|
385
|
+
- **keyDerivation**: `none`(기본)=secret 그대로 HMAC 키로 사용. `sha256`=sha256(secret)을 HMAC 키로 사용(usdt3·기존 DB 호환).
|
|
386
|
+
|
|
387
|
+
### 동적 키로 해시/비교 (권장)
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
import { loadApplicationYaml, hashByKey, compareByKey } from '@xfilecom/core-sdk';
|
|
391
|
+
|
|
392
|
+
const config = await loadApplicationYaml({ env: process.env.NODE_ENV });
|
|
393
|
+
const hv = config?.hashVerification;
|
|
394
|
+
|
|
395
|
+
const emailHash = hashByKey('email', 'user@example.com', hv);
|
|
396
|
+
const phoneHash = hashByKey('phone', '010-1234-5678', hv);
|
|
397
|
+
const userIdHash = hashByKey('userId', uid, hv);
|
|
398
|
+
|
|
399
|
+
const match = compareByKey('email', plainEmail, row.email_hash, hv);
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### 공통 API (config 직접 전달)
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
import { hashValue, compareValue } from '@xfilecom/core-sdk';
|
|
406
|
+
|
|
407
|
+
const hv = config?.hashVerification;
|
|
408
|
+
const emailHash = hashValue('user@example.com', hv?.profiles?.email ?? hv?.email);
|
|
409
|
+
const match = compareValue('user@example.com', row.email_hash, hv?.profiles?.email ?? hv?.email);
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### 이메일 전용
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
import { hashEmail, compareEmail } from '@xfilecom/core-sdk';
|
|
416
|
+
|
|
417
|
+
const emailHash = hashEmail('User@Example.com', config?.hashVerification?.profiles?.email);
|
|
418
|
+
const match = compareEmail('user@example.com', row.email_hash, config?.hashVerification?.profiles?.email);
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
- secret 없으면 `HASH_VERIFICATION_SECRET`(이메일 키는 `EMAIL_HASH_SECRET`) env 사용.
|
|
422
|
+
- 내장 normalizer: `email`, `digits`. `BUILT_IN_NORMALIZERS`, `getProfileConfig`, `emailNormalizer`, `digitsNormalizer` export 됨.
|
|
423
|
+
- **usdt3·기존 DB 호환**: 프로필에 `keyDerivation: sha256` 지정하거나, YAML 없이 `lookupHash`/`compareLookupHash(..., { keyDerivation: 'sha256' })` 사용.
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## 기능 옵션
|
|
428
|
+
|
|
429
|
+
CoreModule 옵션으로 기능별 on/off 가능. 미설정 시 **기본값 true**.
|
|
430
|
+
|
|
431
|
+
| 옵션 | 설명 | 기본값 |
|
|
432
|
+
|------|------|--------|
|
|
433
|
+
| `database.auto` | DB 모듈 사용 | `true` |
|
|
434
|
+
| `response.commonResponse` | `{ code, data, meta, error }` 형식 적용 | `true` |
|
|
435
|
+
| `interceptors.logging` | 요청/응답 로깅 | `true` |
|
|
436
|
+
| `interceptors.errorHandling` | 에러 인터셉터 | `true` |
|
|
437
|
+
| `interceptors.responseTransform` | 응답 변환(CommonResponse) | `true` |
|
|
438
|
+
| `interceptors.databaseCheck` | DB 연결 체크 | `true` |
|
|
439
|
+
| `guards.jwt` | JWT 인증 가드 (forHttpApi/forHybrid 기본 true) | context별 |
|
|
440
|
+
| `filters.exception` | AllExceptionsFilter | `true` |
|
|
441
|
+
|
|
442
|
+
예: CommonResponse 끄기, 로깅/DB체크 끄기
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
CoreModule.forHttpApi({
|
|
446
|
+
database: { schema },
|
|
447
|
+
response: { commonResponse: false },
|
|
448
|
+
interceptors: { logging: false, databaseCheck: false },
|
|
449
|
+
})
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
## Database
|
|
455
|
+
|
|
456
|
+
### 스키마 정의 및 주입
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
// database/schema.ts
|
|
460
|
+
import { mysqlTable, int, varchar, timestamp } from 'drizzle-orm/mysql-core';
|
|
461
|
+
|
|
462
|
+
export const users = mysqlTable('users', {
|
|
463
|
+
id: int('id').primaryKey().autoincrement(),
|
|
464
|
+
email: varchar('email', { length: 255 }).notNull().unique(),
|
|
465
|
+
password: varchar('password', { length: 255 }).notNull(),
|
|
466
|
+
createdAt: timestamp('created_at').defaultNow(),
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
export const schema = { users };
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### 환경 변수
|
|
473
|
+
|
|
474
|
+
`DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_NAME`, `DB_SSL`, `DB_CONNECTION_LIMIT`
|
|
475
|
+
|
|
476
|
+
### DatabaseQuery
|
|
477
|
+
|
|
478
|
+
`where`는 Drizzle `SQL` 또는 plain object(eq 조합) 지원.
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
// select(options)
|
|
482
|
+
const result = await this.dbQuery.select({
|
|
483
|
+
table: 'users',
|
|
484
|
+
fields: ['id', 'name', 'email'],
|
|
485
|
+
where: { email: 'test@example.com' },
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
// query() — pagination, sort, search
|
|
489
|
+
const result = await this.dbQuery.query({
|
|
490
|
+
table: 'users',
|
|
491
|
+
fields: ['id', 'email'],
|
|
492
|
+
sort: [{ field: 'createdAt', order: 'desc' }],
|
|
493
|
+
pagination: { page: 1, limit: 10 },
|
|
494
|
+
search: [{ keyword: 'test', fields: ['email', 'name'] }],
|
|
495
|
+
where: { status: 'active' },
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
// insert / update / delete
|
|
499
|
+
await this.dbQuery.insertOne({ table: 'users', fields: ['email', 'name'], values: data });
|
|
500
|
+
await this.dbQuery.updateOne({ table: 'users', values: data, where: eq(users.id, id) });
|
|
501
|
+
await this.dbQuery.deleteOne({ table: 'users', where: eq(users.id, id) });
|
|
502
|
+
|
|
503
|
+
// findOne / findById / count / exists
|
|
504
|
+
const user = await this.dbQuery.findOne('users', { where: { email: 'a@b.com' }, fields: ['id', 'email'] });
|
|
505
|
+
const user = await this.dbQuery.findById('users', 1);
|
|
506
|
+
const n = await this.dbQuery.count('users', { where: { status: 'active' } });
|
|
507
|
+
const ok = await this.dbQuery.exists('users', { where: { email: 'a@b.com' } });
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### ServiceHelpers (Drizzle 직접 사용)
|
|
511
|
+
|
|
512
|
+
```typescript
|
|
513
|
+
const db = this.helpers.getDb();
|
|
514
|
+
const [user] = await db.select().from(users).where(eq(users.email, email)).limit(1);
|
|
515
|
+
|
|
516
|
+
await this.helpers.withTransaction(async (tx) => {
|
|
517
|
+
await tx.insert(users).values({ email: 'a@b.com' });
|
|
518
|
+
await tx.insert(posts).values({ userId: 1, title: 'Hello' });
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
const paginationResult = this.helpers.createPaginationResult(items, total, page, limit);
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
---
|
|
525
|
+
|
|
526
|
+
## Controller / Service 예시
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
// Controller
|
|
530
|
+
import { Controller, Get, Request, UseGuards } from '@nestjs/common';
|
|
531
|
+
import { ControllerHelpers, Public, User, JwtUser, Roles, JwtAuthGuard, RolesGuard } from '@xfilecom/core-sdk';
|
|
532
|
+
|
|
533
|
+
@Controller('users')
|
|
534
|
+
export class UsersController {
|
|
535
|
+
constructor(private readonly helpers: ControllerHelpers) {}
|
|
536
|
+
|
|
537
|
+
@Public()
|
|
538
|
+
@Get()
|
|
539
|
+
async findAll(@Request() req: any) {
|
|
540
|
+
const { page, limit } = this.helpers.parsePagination(req.query);
|
|
541
|
+
const users = await this.userService.findAll(page, limit);
|
|
542
|
+
return this.helpers.success(users, 'Users retrieved');
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
@UseGuards(JwtAuthGuard)
|
|
546
|
+
@Get('me')
|
|
547
|
+
getMe(@User() user: JwtUser) {
|
|
548
|
+
return user;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
@UseGuards(JwtAuthGuard)
|
|
552
|
+
@Get('my-id')
|
|
553
|
+
getMyId(@User('sub') userId: string | number) {
|
|
554
|
+
return { userId };
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
@UseGuards(JwtAuthGuard, RolesGuard)
|
|
558
|
+
@Roles('admin')
|
|
559
|
+
@Get('admin')
|
|
560
|
+
adminOnly() {
|
|
561
|
+
return { message: 'admin only' };
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Service
|
|
566
|
+
@Injectable()
|
|
567
|
+
export class UsersService {
|
|
568
|
+
constructor(
|
|
569
|
+
private readonly helpers: ServiceHelpers,
|
|
570
|
+
private readonly dbQuery: DatabaseQuery,
|
|
571
|
+
) {}
|
|
572
|
+
|
|
573
|
+
async findAll(page: number, limit: number) {
|
|
574
|
+
return this.dbQuery.query({
|
|
575
|
+
table: 'users',
|
|
576
|
+
fields: ['id', 'email', 'name'],
|
|
577
|
+
pagination: { page, limit },
|
|
578
|
+
sort: [{ field: 'id', order: 'desc' }],
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
---
|
|
585
|
+
|
|
586
|
+
## Guards · Decorators · Interceptors · Filters
|
|
587
|
+
|
|
588
|
+
- **Guards**: `JwtAuthGuard`, `RolesGuard`
|
|
589
|
+
- **Decorators**: `@Public()`, `@User()`, `@User('sub')`, `@Roles('admin', 'user')`
|
|
590
|
+
- **Interceptors**: LoggingInterceptor, ErrorHandlingInterceptor, ResponseTransformInterceptor, DatabaseCheckInterceptor
|
|
591
|
+
- **Filters**: AllExceptionsFilter
|
|
592
|
+
- **응답 타입**: `CommonResponseDto`, `CommonErrorDto`, `PaginationMeta`, `ResponseMeta`
|
|
593
|
+
|
|
594
|
+
**JWT 인증**: `JwtAuthGuard`는 `process.env.JWT_SECRET`을 참조합니다. YAML만 쓸 경우 `loadApplicationYaml({ syncEnvKeys: { JWT_SECRET: 'jwt.secret' } })` 로 config 값을 env에 채우면 Guard와 자연스럽게 연동됩니다. `CoreModule.forHttpApi({ jwt: { secret, expiresIn } })` 로 넘기면 ConfigValidator는 env 검사를 건너뜁니다. `passport-jwt` / `@nestjs/passport`는 **선택 의존성**이며, 없으면 경고 후 인증을 스킵합니다 (개발용). 프로덕션에서는 설치 권장.
|
|
595
|
+
|
|
596
|
+
---
|
|
597
|
+
|
|
598
|
+
## 기타 유틸
|
|
599
|
+
|
|
600
|
+
- **ErrorUtils**: 에러 메시지/코드 처리
|
|
601
|
+
- **LoggerHelpers**, **ILogWriter**, **ConsoleLogWriter**, **FileLogWriter**, **getLoggingConfigFromEnv**: 로깅
|
|
602
|
+
- **ConfigValidator**: JWT/DB 설정 검증 (`validateJwtConfig(throwOnMissing?, jwtOptions?)` — `jwtOptions.secret` 이 있으면 env 검사 생략), **parseBoolean**, **parseNumber**, **parseArray** (쉼표 구분 문자열 → 배열)
|
|
603
|
+
- **Env**: `getAppEnv()`, `isDevelopment()`, `isProduction()`, `isStaging()`, `currentAppEnv`, `isDev`, `isProd` (APP_ENV / NODE_ENV 기반)
|
|
604
|
+
|
|
605
|
+
---
|
|
606
|
+
|
|
607
|
+
## Database 연결 실패 시
|
|
608
|
+
|
|
609
|
+
- 연결 실패해도 서버는 기동됩니다 (Graceful Degradation).
|
|
610
|
+
- `.env`에 `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASSWORD`, `DB_NAME` 설정 후 재시도.
|
|
611
|
+
- Config 직접 주입: `CoreModule.forHttpApi({ database: { config: { ... }, schema } })`.
|
|
612
|
+
|
|
613
|
+
---
|
|
614
|
+
|
|
615
|
+
## 수정 사항
|
|
616
|
+
|
|
617
|
+
### JWT 설정 검증 (ConfigValidator)
|
|
618
|
+
|
|
619
|
+
- `validateJwtConfig(throwOnMissing?, jwtOptions?)` 두 번째 인자 추가.
|
|
620
|
+
- `options.jwt?.secret` 이 있으면 `process.env.JWT_SECRET` 검사를 건너뛰어 불필요한 경고를 내지 않음.
|
|
621
|
+
- `CoreModule` 에서 JWT Guard 등록 시 `options.jwt` 를 넘기도록 변경 → YAML/옵션만으로 JWT 설정 시 경고 제거.
|
|
622
|
+
|
|
623
|
+
### 설정 로드 시 env 동기화 (syncEnvKeys)
|
|
624
|
+
|
|
625
|
+
- `loadApplicationYaml` / `loadApplicationYamlSync` 옵션에 `syncEnvKeys?: Record<string, string>` 추가.
|
|
626
|
+
- 로드한 config의 dot 경로(예: `jwt.secret`) 값을 지정한 환경 변수명(예: `JWT_SECRET`)으로 `process.env`에 채움.
|
|
627
|
+
- YAML만 사용해도 ConfigValidator, JwtAuthGuard 등이 `process.env`를 참조할 때 자연스럽게 연동 가능.
|
|
628
|
+
|
|
629
|
+
### 설정 루트·Config 로더 (resolveConfigRootDir, createConfigLoader)
|
|
630
|
+
|
|
631
|
+
- **resolveConfigRootDir(options?)**: application.yml 이 있는 디렉터리 탐색. 기본 후보: `libs/config/config`, `../../libs/config/config`, `../libs/config/config`, cwd. 모노레포·실행 위치 무관하게 동일 규칙 사용 가능.
|
|
632
|
+
- **createConfigLoader(options)**: 한 번 로드 → env 동기화 → 전역 캐시(globalKey) → `getFullConfig()` / `getServiceConfig(serviceKey, { portEnvKey? })` 제공. auth-service, mail-service 등에서 config-root.ts·load-config.ts 대신 core-sdk만으로 동일 패턴 사용 가능.
|
|
633
|
+
|
|
634
|
+
### 설정 소스 (configSource)
|
|
635
|
+
|
|
636
|
+
- `configSource: 'yaml' | 'env' | 'yamlWithEnvOverrides'` 옵션 및 `CONFIG_SOURCE` 환경 변수로 .env vs YAML 선택 가능.
|
|
637
|
+
- `configSource: 'env'` 시 YAML 없이 `process.env` 기반 config 객체 생성.
|
|
638
|
+
|
|
639
|
+
### 환경 유틸 (Env)
|
|
640
|
+
|
|
641
|
+
- `getAppEnv()`, `isDevelopment()`, `isProduction()`, `isStaging()`, `currentAppEnv`, `isDev`, `isProd` 추가 (APP_ENV / NODE_ENV 기반).
|
|
642
|
+
|
|
643
|
+
### Crypto (비밀번호·조회용 해시)
|
|
644
|
+
|
|
645
|
+
- **PBKDF2 비밀번호**: `pbkdf2HashPassword`, `pbkdf2VerifyPassword` — 저장 형식 `saltHex:hashHex`, 기본 100_000회·sha512·64바이트 (usdt3·기존 DB 호환).
|
|
646
|
+
- **조회용 해시 키 파생**: `HashVerificationConfig.keyDerivation: 'none' | 'sha256'` — `sha256` 이면 HMAC 키로 sha256(secret) 사용 (usdt3·기존 email_hash 등과 동일).
|
|
647
|
+
- **lookupHash / compareLookupHash**: YAML 없이 secret + keyDerivation 만으로 조회용 해시 생성/비교. `deriveKeySha256` export.
|
|
648
|
+
|
|
649
|
+
### Hash verification
|
|
650
|
+
|
|
651
|
+
- `hashVerification.profiles` + `hashByKey` / `compareByKey` 로 동적 키별 해시/비교 지원.
|
|
652
|
+
- `keyDerivation: 'sha256'` 로 기존 DB와 동일한 해시 생성 가능.
|
|
653
|
+
- `ConfigValidator.parseArray` 추가 (쉼표 구분 문자열 → 배열).
|
|
654
|
+
|
|
655
|
+
### 문서
|
|
656
|
+
|
|
657
|
+
- JWT 인증: `process.env` 참조, `syncEnvKeys` 연동, `passport-jwt` 선택 의존성 안내.
|
|
658
|
+
- Auth 클라이언트: auth 모듈이 패키지에 포함되면 `index`에서 export 예정.
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
## 라이선스
|
|
663
|
+
|
|
664
|
+
ISC
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { DynamicModule } from '@nestjs/common';
|
|
2
|
+
import { DatabaseConfig } from './database/database.module';
|
|
3
|
+
import { LoggingInterceptorOptions } from './interceptors/logging.interceptor';
|
|
4
|
+
import { ExceptionFilterOptions } from './filters/exception.filter';
|
|
5
|
+
export interface CoreModuleOptions {
|
|
6
|
+
database?: {
|
|
7
|
+
auto?: boolean;
|
|
8
|
+
config?: DatabaseConfig;
|
|
9
|
+
schema?: any;
|
|
10
|
+
};
|
|
11
|
+
jwt?: {
|
|
12
|
+
secret?: string;
|
|
13
|
+
expiresIn?: string;
|
|
14
|
+
issuer?: string;
|
|
15
|
+
audience?: string;
|
|
16
|
+
};
|
|
17
|
+
response?: {
|
|
18
|
+
commonResponse?: boolean;
|
|
19
|
+
};
|
|
20
|
+
interceptors?: {
|
|
21
|
+
logging?: boolean | LoggingInterceptorOptions;
|
|
22
|
+
errorHandling?: boolean;
|
|
23
|
+
responseTransform?: boolean;
|
|
24
|
+
databaseCheck?: boolean;
|
|
25
|
+
};
|
|
26
|
+
guards?: {
|
|
27
|
+
jwt?: boolean;
|
|
28
|
+
};
|
|
29
|
+
filters?: {
|
|
30
|
+
exception?: boolean | ExceptionFilterOptions;
|
|
31
|
+
};
|
|
32
|
+
serviceType?: 'microservice' | 'http-api' | 'hybrid';
|
|
33
|
+
}
|
|
34
|
+
export declare class CoreModule {
|
|
35
|
+
static forRoot(options?: CoreModuleOptions): DynamicModule;
|
|
36
|
+
static forMicroservice(options?: CoreModuleOptions): DynamicModule;
|
|
37
|
+
static forHttpApi(options?: CoreModuleOptions): DynamicModule;
|
|
38
|
+
static forHybrid(options?: CoreModuleOptions): DynamicModule;
|
|
39
|
+
}
|