Skip to content

Commit 8d87b53

Browse files
refactor: migrate DTOs from class-validator to Zod (#2542)
* feat: init zod * refactor(draft, meta-preset): migrate DTOs to Zod schemas and clean up models - Replaced class-validator DTOs with Zod schemas for CreateDraft, UpdateDraft, and related models in the Draft module. - Removed unused draft.dto.ts and meta-preset.dto.ts files, consolidating validation logic into draft.schema.ts and meta-preset.schema.ts. - Updated service and controller files to reference the new Zod-based DTOs. - Cleaned up model definitions by removing unnecessary decorators and imports. Signed-off-by: Innei <[email protected]> * chore: update zod dependency and refactor validation logic - Upgraded zod from version 3.25.76 to 4.3.5 across various modules and schemas. - Removed deprecated class-validator dependencies and validation pipes, consolidating validation logic using Zod. - Updated schemas to utilize z.enum for enum types instead of z.nativeEnum for better compatibility with Zod v4. - Cleaned up imports and adjusted related service and controller files to align with the new validation approach. Signed-off-by: Innei <[email protected]> * feat(post): add category filtering to post pagination and update PostPagerDto - Enhanced the PostController to support category-based filtering in post queries. - Updated PostPagerDto to include categoryIds, allowing for flexible filtering of posts by categories. - Refactored the selection logic in the aggregation pipeline for improved readability and performance. Signed-off-by: Innei <[email protected]> * feat(draft): enhance draft management with published version tracking - Added `publishedVersion` property to `DraftModel` to track the version of the draft when it was published. - Updated `DraftService` to include `markAsPublished` method for marking drafts as published and synchronizing their version. - Modified `NoteService`, `PageService`, and `PostService` to handle draft publication during note, page, and post creation and updates, respectively. - Introduced `draftId` field in schemas for Note, Page, and Post to associate drafts with published content. Signed-off-by: Innei <[email protected]> * chore: update schemas to include missing imports and refactor validation logic - Added missing imports in comment.schema.ts, configs.schema.ts, markdown.schema.ts, and webhook.schema.ts for consistency. - Refactored BaseCrudFactory in crud-factor.transformer.ts to simplify DTO class definitions, removing PartialType and clarifying validation handling. - Updated test files to replace ExtendedValidationPipe with extendedZodValidationPipeInstance for improved validation consistency. - Adjusted e2e tests for OptionController to reflect changes in the API endpoint and response structure. Signed-off-by: Innei <[email protected]> * chore: remove cls-hooked dependency and implement AsyncLocalStorage for request context management - Removed cls-hooked from package.json and pnpm-lock.yaml to simplify dependency management. - Refactored RequestContext to utilize AsyncLocalStorage for storing request context, enhancing performance and reducing complexity. - Updated RequestContextMiddleware to use the new AsyncLocalStorage implementation. - Added Redis server installation step in CI workflow and improved Redis mock setup to locate the system binary. Signed-off-by: Innei <[email protected]> * test: add end-to-end and unit tests for RequestContext management - Introduced e2e tests in request.context.e2e-spec.ts to verify that the RequestContext maintains consistency throughout a request lifecycle and does not leak context across concurrent requests. - Added unit tests in request.context.spec.ts to ensure proper context management during asynchronous operations and isolation of concurrent request contexts. Signed-off-by: Innei <[email protected]> * Potential fix for code scanning alert no. 66: Unvalidated dynamic method call Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Signed-off-by: Innei <[email protected]> * chore: replace lodash with es-toolkit and update dependencies - Removed lodash imports across various modules and replaced them with equivalent functions from es-toolkit/compat. - Added es-toolkit as a dependency in package.json and pnpm-lock.yaml. - Updated related configuration files to reflect the removal of lodash. Signed-off-by: Innei <[email protected]> * fix(snippet): update SnippetModel type property to include enum validation - Modified the `type` property in SnippetModel to include an enum validation for SnippetType, ensuring that only valid types can be assigned. - Removed outdated snapshot tests for NoteController to streamline test suite and reduce clutter. Signed-off-by: Innei <[email protected]> * refactor: update API routes to use dynamic prefix and enhance SnippetModel type validation - Refactored various controller tests to utilize a dynamic API route prefix for improved maintainability. - Updated the `type` property in SnippetModel to ensure enum validation is based on `Object.values(SnippetType)` for better type safety. Signed-off-by: Innei <[email protected]> --------- Signed-off-by: Innei <[email protected]> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
1 parent d03a881 commit 8d87b53

File tree

211 files changed

+6091
-6630
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

211 files changed

+6091
-6630
lines changed

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,14 @@ jobs:
9999
uses: ./.github/actions/setup-node
100100
with:
101101
node-version: '22.x'
102+
- name: Install Redis Server
103+
run: |
104+
sudo apt-get update
105+
sudo apt-get install -y redis-server
102106
- name: Run Lint
103107
run: npm run lint
104108
- name: Run Tests
105109
run: npm run test
106110
env:
107111
CI: true
112+
REDIS_BINARY_PATH: /usr/bin/redis-server

apps/core/CHANGELOG.md

Lines changed: 1762 additions & 3327 deletions
Large diffs are not rendered by default.

apps/core/external/readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
This is folder used to override dependencies.
1+
This is folder used to override dependencies.

apps/core/package.json

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,15 +85,12 @@
8585
"bcryptjs": "^3.0.3",
8686
"blurhash": "2.0.5",
8787
"cache-manager": "7.2.8",
88-
"class-transformer": "0.5.1",
89-
"class-validator": "0.13.2",
90-
"class-validator-jsonschema": "npm:@innei/[email protected]",
91-
"cls-hooked": "^4.2.2",
9288
"commander": "14.0.2",
9389
"dayjs": "1.11.19",
9490
"dotenv": "^17.2.3",
9591
"dotenv-expand": "^12.0.3",
9692
"ejs": "3.1.10",
93+
"es-toolkit": "^1.44.0",
9794
"form-data": "4.0.5",
9895
"inquirer": "^12.9.6",
9996
"isbot": "5.1.32",
@@ -103,7 +100,6 @@
103100
"jszip": "3.10.1",
104101
"keyv": "5.5.5",
105102
"linkedom": "0.18.12",
106-
"lodash": "^4.17.21",
107103
"lru-cache": "11.2.4",
108104
"marked": "16.3.0",
109105
"mime-types": "^3.0.1",
@@ -114,6 +110,7 @@
114110
"mongoose-lean-getters": "2.2.2",
115111
"mongoose-lean-virtuals": "2.0.1",
116112
"mongoose-paginate-v2": "1.9.1",
113+
"nestjs-zod": "^5.1.1",
117114
"node-machine-id": "1.1.12",
118115
"nodemailer": "7.0.12",
119116
"openai": "5.23.2",
@@ -131,6 +128,7 @@
131128
"vm2": "3.9.19",
132129
"wildcard-match": "5.1.4",
133130
"xss": "1.0.15",
131+
"zod": "4.3.5",
134132
"zx-cjs": "7.0.7-0"
135133
},
136134
"devDependencies": {
@@ -139,11 +137,9 @@
139137
"@nestjs/testing": "11.1.12",
140138
"@swc/core": "1.15.8",
141139
"@types/babel__core": "7.20.5",
142-
"@types/cls-hooked": "^4.3.9",
143140
"@types/ejs": "3.1.5",
144141
"@types/get-image-colors": "4.0.5",
145142
"@types/js-yaml": "4.0.9",
146-
"@types/lodash": "4.17.23",
147143
"@types/mime-types": "3.0.1",
148144
"@types/node": "25.0.9",
149145
"@types/nodemailer": "6.4.21",
@@ -165,4 +161,4 @@
165161
"vite-tsconfig-paths": "6.0.4",
166162
"vitest": "1.5.2"
167163
}
168-
}
164+
}

apps/core/readme.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ pnpm dev
111111
## 应用结构
112112

113113
- 请求处理流程
114-
115114
1. request:收到请求
116115
1. middleware:中间件过滤爬虫 PHP 肉鸡扫描路径,记录访问历史
117116
1. guard:守卫过滤(鉴权)和角色附加
@@ -129,7 +128,6 @@ ResponseInterceptor -> ResponseFilterInterceptor -> JSONTransformInterceptor ->
129128
```
130129

131130
- [业务逻辑模块](https://github.com/mx-space/mx-server/tree/master/src/modules)
132-
133131
1. [Aggregate] 聚合
134132
1. [Analyze] 数据统计
135133
1. [Auth] 认证

apps/core/src/bootstrap.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { fastifyApp } from './common/adapters/fastify.adapter'
1313
import { RedisIoAdapter } from './common/adapters/socket.adapter'
1414
import { SpiderGuard } from './common/guards/spider.guard'
1515
import { LoggingInterceptor } from './common/interceptors/logging.interceptor'
16-
import { ExtendedValidationPipe } from './common/pipes/validation.pipe'
16+
import { extendedZodValidationPipeInstance } from './common/zod'
1717
import { logger } from './global/consola.global'
1818
import { isMainProcess, isTest } from './global/env.global'
1919
import { checkInit } from './utils/check-init.util'
@@ -78,7 +78,7 @@ export async function bootstrap() {
7878
app.useGlobalInterceptors(new LoggingInterceptor())
7979
}
8080

81-
app.useGlobalPipes(ExtendedValidationPipe.shared)
81+
app.useGlobalPipes(extendedZodValidationPipeInstance)
8282
app.useGlobalGuards(new SpiderGuard())
8383
!isTest && app.useWebSocketAdapter(new RedisIoAdapter(app))
8484

apps/core/src/common/contexts/request.context.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
/* eslint-disable dot-notation */
22
// @reference https://github.com/ever-co/ever-gauzy/blob/d36b4f40b1446f3c33d02e0ba00b53a83109d950/packages/core/src/core/context/request-context.ts
3+
import { AsyncLocalStorage } from 'node:async_hooks'
34
import type { ServerResponse } from 'node:http'
45
import { UnauthorizedException } from '@nestjs/common'
56
import type { UserModel } from '~/modules/user/user.model'
67
import type { BizIncomingMessage } from '~/transformers/get-req.transformer'
7-
import * as cls from 'cls-hooked'
88

99
type Nullable<T> = T | null
1010
export class RequestContext {
11+
private static readonly storage = new AsyncLocalStorage<RequestContext>()
12+
1113
readonly id: number
1214
request: BizIncomingMessage
1315
response: ServerResponse
@@ -18,13 +20,12 @@ export class RequestContext {
1820
this.response = response
1921
}
2022

21-
static currentRequestContext(): Nullable<RequestContext> {
22-
const session = cls.getNamespace(RequestContext.name)
23-
if (session && session.active) {
24-
return session.get(RequestContext.name)
25-
}
23+
static run<T>(requestContext: RequestContext, callback: () => T): T {
24+
return RequestContext.storage.run(requestContext, callback)
25+
}
2626

27-
return null
27+
static currentRequestContext(): Nullable<RequestContext> {
28+
return RequestContext.storage.getStore() ?? null
2829
}
2930

3031
static currentRequest(): Nullable<BizIncomingMessage> {
Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,29 @@
1-
import { Transform } from 'class-transformer'
1+
import { createParamDecorator, type ExecutionContext } from '@nestjs/common'
22

3-
export const TransformBoolean = () =>
4-
Transform(({ value }) => value === '1' || value === 'true')
3+
/**
4+
* Transform string query parameter to boolean
5+
* '1' or 'true' becomes true, all other values become false
6+
*
7+
* Note: This decorator is primarily used for query parameter transformation.
8+
* For Zod schemas, use z.preprocess() or z.coerce instead.
9+
*/
10+
export function transformBoolean(value: unknown): boolean {
11+
return value === '1' || value === 'true' || value === true
12+
}
13+
14+
/**
15+
* @deprecated Use Zod schema with z.preprocess() instead
16+
* Example:
17+
* ```typescript
18+
* const schema = z.object({
19+
* enabled: z.preprocess((val) => val === '1' || val === 'true', z.boolean())
20+
* })
21+
* ```
22+
*/
23+
export const TransformBoolean = createParamDecorator(
24+
(data: string, ctx: ExecutionContext): boolean => {
25+
const request = ctx.switchToHttp().getRequest()
26+
const value = request.query?.[data] ?? request.body?.[data]
27+
return transformBoolean(value)
28+
},
29+
)

apps/core/src/common/exceptions/cant-find.exception.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { NotFoundException } from '@nestjs/common'
2-
import { sample } from 'lodash'
2+
import { sample } from 'es-toolkit/compat'
33

44
export const NotFoundMessage = [
55
'真不巧,内容走丢了 o(╥﹏╥)o',

apps/core/src/common/guards/auth.guard.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { UserModel } from '~/modules/user/user.model'
66
import { UserService } from '~/modules/user/user.service'
77
import type { FastifyBizRequest } from '~/transformers/get-req.transformer'
88
import { getNestExecutionContextRequest } from '~/transformers/get-req.transformer'
9-
import { isJWT } from 'class-validator'
9+
import { isJWT } from '~/utils/validator.util'
1010

1111
/**
1212
* JWT auth guard

0 commit comments

Comments
 (0)