@svton/cli 1.2.2 → 1.2.4

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.
Files changed (102) hide show
  1. package/dist/index.js +58 -27
  2. package/dist/index.mjs +58 -27
  3. package/package.json +1 -3
  4. package/templates/apps/admin/next-env.d.ts +0 -2
  5. package/templates/apps/admin/next.config.js +0 -15
  6. package/templates/apps/admin/package.json.tpl +0 -54
  7. package/templates/apps/admin/postcss.config.js +0 -6
  8. package/templates/apps/admin/src/app/globals.css +0 -37
  9. package/templates/apps/admin/src/app/layout.tsx +0 -19
  10. package/templates/apps/admin/src/app/login/page.tsx +0 -96
  11. package/templates/apps/admin/src/app/page.tsx +0 -8
  12. package/templates/apps/admin/src/app/users/page.tsx +0 -165
  13. package/templates/apps/admin/src/components/ui/switch.tsx +0 -29
  14. package/templates/apps/admin/src/hooks/useAPI.ts +0 -130
  15. package/templates/apps/admin/src/lib/api-client.ts +0 -112
  16. package/templates/apps/admin/src/lib/api-server.ts +0 -95
  17. package/templates/apps/admin/tailwind.config.js +0 -54
  18. package/templates/apps/admin/tsconfig.json +0 -22
  19. package/templates/apps/backend/.env.example +0 -29
  20. package/templates/apps/backend/nest-cli.json +0 -8
  21. package/templates/apps/backend/package.json.tpl +0 -57
  22. package/templates/apps/backend/prisma/schema.prisma +0 -72
  23. package/templates/apps/backend/prisma/seed.ts +0 -32
  24. package/templates/apps/backend/src/app.controller.ts +0 -15
  25. package/templates/apps/backend/src/app.module.ts +0 -85
  26. package/templates/apps/backend/src/app.service.ts +0 -12
  27. package/templates/apps/backend/src/auth/auth.controller.ts +0 -31
  28. package/templates/apps/backend/src/auth/auth.module.ts +0 -27
  29. package/templates/apps/backend/src/auth/auth.service.ts +0 -89
  30. package/templates/apps/backend/src/auth/jwt-auth.guard.ts +0 -5
  31. package/templates/apps/backend/src/auth/jwt.strategy.ts +0 -27
  32. package/templates/apps/backend/src/config/env.schema.ts +0 -35
  33. package/templates/apps/backend/src/main.ts +0 -51
  34. package/templates/apps/backend/src/object-storage/object-storage.controller.ts +0 -114
  35. package/templates/apps/backend/src/object-storage/object-storage.module.ts +0 -7
  36. package/templates/apps/backend/src/prisma/prisma.module.ts +0 -9
  37. package/templates/apps/backend/src/prisma/prisma.service.ts +0 -13
  38. package/templates/apps/backend/src/user/user.controller.ts +0 -50
  39. package/templates/apps/backend/src/user/user.module.ts +0 -12
  40. package/templates/apps/backend/src/user/user.service.ts +0 -117
  41. package/templates/apps/backend/tsconfig.json +0 -23
  42. package/templates/apps/mobile/babel.config.js +0 -8
  43. package/templates/apps/mobile/config/index.ts +0 -65
  44. package/templates/apps/mobile/package.json.tpl +0 -48
  45. package/templates/apps/mobile/project.config.json.tpl +0 -17
  46. package/templates/apps/mobile/src/app.config.ts +0 -9
  47. package/templates/apps/mobile/src/app.scss +0 -4
  48. package/templates/apps/mobile/src/app.ts +0 -8
  49. package/templates/apps/mobile/src/hooks/useAPI.ts +0 -285
  50. package/templates/apps/mobile/src/pages/index/index.scss +0 -7
  51. package/templates/apps/mobile/src/pages/index/index.tsx +0 -49
  52. package/templates/apps/mobile/src/services/api.ts +0 -155
  53. package/templates/apps/mobile/src/services/upload.service.ts +0 -41
  54. package/templates/apps/mobile/tsconfig.json +0 -21
  55. package/templates/configs/authz.config.ts +0 -10
  56. package/templates/configs/cache.config.ts +0 -14
  57. package/templates/configs/oauth.config.ts +0 -20
  58. package/templates/configs/payment.config.ts +0 -44
  59. package/templates/configs/queue.config.ts +0 -21
  60. package/templates/configs/rate-limit.config.ts +0 -16
  61. package/templates/configs/sms.config.ts +0 -11
  62. package/templates/configs/storage.config.ts +0 -14
  63. package/templates/examples/README.md +0 -258
  64. package/templates/examples/authz/README.md +0 -273
  65. package/templates/examples/authz/roles.guard.ts +0 -37
  66. package/templates/examples/authz/user.controller.ts +0 -116
  67. package/templates/examples/cache/README.md +0 -82
  68. package/templates/examples/cache/user.controller.ts +0 -42
  69. package/templates/examples/cache/user.service.ts +0 -78
  70. package/templates/examples/oauth/README.md +0 -192
  71. package/templates/examples/oauth/auth.controller.ts +0 -99
  72. package/templates/examples/oauth/auth.service.ts +0 -97
  73. package/templates/examples/payment/README.md +0 -151
  74. package/templates/examples/payment/order.controller.ts +0 -56
  75. package/templates/examples/payment/order.service.ts +0 -132
  76. package/templates/examples/payment/webhook.controller.ts +0 -73
  77. package/templates/examples/queue/README.md +0 -134
  78. package/templates/examples/queue/email.controller.ts +0 -34
  79. package/templates/examples/queue/email.processor.ts +0 -68
  80. package/templates/examples/queue/email.service.ts +0 -64
  81. package/templates/examples/rate-limit/README.md +0 -249
  82. package/templates/examples/rate-limit/api.controller.ts +0 -113
  83. package/templates/examples/sms/README.md +0 -121
  84. package/templates/examples/sms/sms.service.ts +0 -69
  85. package/templates/examples/sms/verification.controller.ts +0 -100
  86. package/templates/examples/storage/README.md +0 -224
  87. package/templates/examples/storage/upload.controller.ts +0 -117
  88. package/templates/examples/storage/upload.service.ts +0 -123
  89. package/templates/packages/types/package.json.tpl +0 -16
  90. package/templates/packages/types/src/api.ts +0 -88
  91. package/templates/packages/types/src/common.ts +0 -89
  92. package/templates/packages/types/src/index.ts +0 -3
  93. package/templates/packages/types/tsconfig.json +0 -16
  94. package/templates/skills/authz.skill.md +0 -42
  95. package/templates/skills/base.skill.md +0 -57
  96. package/templates/skills/cache.skill.md +0 -88
  97. package/templates/skills/oauth.skill.md +0 -41
  98. package/templates/skills/payment.skill.md +0 -129
  99. package/templates/skills/queue.skill.md +0 -140
  100. package/templates/skills/rate-limit.skill.md +0 -38
  101. package/templates/skills/sms.skill.md +0 -39
  102. package/templates/skills/storage.skill.md +0 -42
@@ -1,57 +0,0 @@
1
- {
2
- "name": "{{ORG_NAME}}/backend",
3
- "version": "1.0.0",
4
- "description": "{{PROJECT_NAME}} 后端 API",
5
- "prisma": {
6
- "seed": "ts-node prisma/seed.ts"
7
- },
8
- "scripts": {
9
- "build": "nest build",
10
- "dev": "NODE_ENV=development nest start --watch",
11
- "start": "NODE_ENV=production node dist/main",
12
- "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
13
- "test": "jest",
14
- "type-check": "tsc --noEmit",
15
- "prisma:generate": "prisma generate",
16
- "prisma:migrate": "prisma migrate dev",
17
- "prisma:migrate:deploy": "prisma migrate deploy",
18
- "prisma:seed": "ts-node prisma/seed.ts",
19
- "prisma:studio": "prisma studio",
20
- "db:init": "pnpm prisma:generate && pnpm prisma:migrate && pnpm prisma:seed",
21
- "clean": "rm -rf dist"
22
- },
23
- "dependencies": {
24
- "@nestjs/common": "^10.3.0",
25
- "@nestjs/config": "^3.1.1",
26
- "@nestjs/core": "^10.3.0",
27
- "@nestjs/jwt": "^10.2.0",
28
- "@nestjs/passport": "^10.0.3",
29
- "@nestjs/platform-express": "^10.3.0",
30
- "@nestjs/swagger": "^7.1.17",
31
- "@prisma/client": "^5.7.1",
32
- "{{ORG_NAME}}/types": "workspace:*",
33
- "bcrypt": "^5.1.1",
34
- "class-transformer": "^0.5.1",
35
- "class-validator": "^0.14.0",
36
- "ioredis": "^5.3.2",
37
- "passport": "^0.7.0",
38
- "passport-jwt": "^4.0.1",
39
- "reflect-metadata": "^0.2.1",
40
- "rxjs": "^7.8.1"
41
- },
42
- "devDependencies": {
43
- "@nestjs/cli": "^10.2.1",
44
- "@nestjs/schematics": "^10.0.3",
45
- "@nestjs/testing": "^10.2.10",
46
- "@types/bcrypt": "^5.0.2",
47
- "@types/express": "^4.17.21",
48
- "@types/jest": "^29.5.11",
49
- "@types/node": "^20.10.0",
50
- "@types/passport-jwt": "^3.0.13",
51
- "jest": "^29.7.0",
52
- "prisma": "^5.7.0",
53
- "ts-jest": "^29.1.1",
54
- "ts-node": "^10.9.2",
55
- "typescript": "^5.3.0"
56
- }
57
- }
@@ -1,72 +0,0 @@
1
- generator client {
2
- provider = "prisma-client-js"
3
- }
4
-
5
- datasource db {
6
- provider = "mysql"
7
- url = env("DATABASE_URL")
8
- }
9
-
10
- // 用户模型
11
- model User {
12
- id Int @id @default(autoincrement())
13
- phone String @unique
14
- password String
15
- nickname String
16
- avatar String?
17
- email String?
18
- bio String? @db.Text
19
- role String @default("user") // user, admin, super_admin
20
- status Int @default(1) // 0: 禁用, 1: 启用
21
- lastLoginAt DateTime?
22
- createdAt DateTime @default(now())
23
- updatedAt DateTime @updatedAt
24
-
25
- contents Content[]
26
- comments Comment[]
27
-
28
- @@index([phone])
29
- @@index([role])
30
- @@index([status])
31
- @@map("users")
32
- }
33
-
34
- // 内容模型
35
- model Content {
36
- id Int @id @default(autoincrement())
37
- title String
38
- content String @db.Text
39
- images String? @db.Text // JSON array
40
- status String @default("draft") // draft, pending, published, rejected
41
- viewCount Int @default(0)
42
- likeCount Int @default(0)
43
- commentCount Int @default(0)
44
- authorId Int
45
- author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
46
- createdAt DateTime @default(now())
47
- updatedAt DateTime @updatedAt
48
-
49
- comments Comment[]
50
-
51
- @@index([authorId])
52
- @@index([status])
53
- @@index([createdAt])
54
- @@map("contents")
55
- }
56
-
57
- // 评论模型
58
- model Comment {
59
- id Int @id @default(autoincrement())
60
- content String @db.Text
61
- contentId Int
62
- content_ Content @relation(fields: [contentId], references: [id], onDelete: Cascade)
63
- authorId Int
64
- author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
65
- likeCount Int @default(0)
66
- createdAt DateTime @default(now())
67
- updatedAt DateTime @updatedAt
68
-
69
- @@index([contentId])
70
- @@index([authorId])
71
- @@map("comments")
72
- }
@@ -1,32 +0,0 @@
1
- import { PrismaClient } from '@prisma/client';
2
-
3
- const prisma = new PrismaClient();
4
-
5
- async function main() {
6
- console.log('🌱 开始初始化数据...');
7
-
8
- // 创建管理员用户
9
- const admin = await prisma.user.upsert({
10
- where: { phone: '13800000000' },
11
- update: {},
12
- create: {
13
- phone: '13800000000',
14
- password: '$2b$10$example', // 需要用 bcrypt 生成
15
- nickname: '管理员',
16
- role: 'admin',
17
- status: 1,
18
- },
19
- });
20
-
21
- console.log('✅ 管理员用户:', admin);
22
- console.log('🎉 数据初始化完成!');
23
- }
24
-
25
- main()
26
- .catch((e) => {
27
- console.error(e);
28
- process.exit(1);
29
- })
30
- .finally(async () => {
31
- await prisma.$disconnect();
32
- });
@@ -1,15 +0,0 @@
1
- import { Controller, Get } from '@nestjs/common';
2
- import { ApiTags, ApiOperation } from '@nestjs/swagger';
3
- import { AppService } from './app.service';
4
-
5
- @ApiTags('健康检查')
6
- @Controller()
7
- export class AppController {
8
- constructor(private readonly appService: AppService) {}
9
-
10
- @Get()
11
- @ApiOperation({ summary: '健康检查' })
12
- getHealth() {
13
- return this.appService.getHealth();
14
- }
15
- }
@@ -1,85 +0,0 @@
1
- import { Module } from '@nestjs/common';
2
- import { ConfigModule, ConfigService } from '@nestjs/config';
3
- import { AppController } from './app.controller';
4
- import { AppService } from './app.service';
5
- import { PrismaModule } from './prisma/prisma.module';
6
- import { AuthModule } from './auth/auth.module';
7
- import { UserModule } from './user/user.module';
8
-
9
- // Svton 基础设施包(按需启用)
10
- // import { createZodValidate } from '@svton/nestjs-config-schema';
11
- // import { HttpModule } from '@svton/nestjs-http';
12
- // import { LoggerModule } from '@svton/nestjs-logger';
13
- // import { RedisModule } from '@svton/nestjs-redis';
14
- // import { AuthzModule } from '@svton/nestjs-authz';
15
- // import { ObjectStorageModule } from '@svton/nestjs-object-storage';
16
- // import { createQiniuAdapter } from '@svton/nestjs-object-storage-qiniu-kodo';
17
- // import { envSchema } from './config/env.schema';
18
-
19
- @Module({
20
- imports: [
21
- // 配置模块(可启用 schema 验证)
22
- ConfigModule.forRoot({
23
- isGlobal: true,
24
- // validate: createZodValidate(envSchema),
25
- }),
26
-
27
- // HTTP 规范化(统一响应/异常格式)
28
- // HttpModule.forRoot({
29
- // successCode: 0,
30
- // successMessage: 'success',
31
- // excludePaths: ['/health', '/api-docs'],
32
- // }),
33
-
34
- // 日志模块
35
- // LoggerModule.forRootAsync({
36
- // imports: [ConfigModule],
37
- // inject: [ConfigService],
38
- // useFactory: (config: ConfigService) => ({
39
- // appName: 'backend',
40
- // env: config.get('NODE_ENV'),
41
- // prettyPrint: config.get('NODE_ENV') !== 'production',
42
- // }),
43
- // }),
44
-
45
- // Redis 模块
46
- // RedisModule.forRootAsync({
47
- // imports: [ConfigModule],
48
- // inject: [ConfigService],
49
- // useFactory: (config: ConfigService) => ({
50
- // url: config.get('REDIS_URL'),
51
- // keyPrefix: 'app:',
52
- // }),
53
- // }),
54
-
55
- // RBAC 权限模块
56
- // AuthzModule.forRoot({
57
- // userRoleField: 'role',
58
- // enableGlobalGuard: false,
59
- // }),
60
-
61
- // 对象存储模块(七牛云示例)
62
- // ObjectStorageModule.forRootAsync({
63
- // imports: [ConfigModule],
64
- // inject: [ConfigService],
65
- // useFactory: (config: ConfigService) => ({
66
- // defaultBucket: config.get('QINIU_BUCKET'),
67
- // publicBaseUrl: config.get('QINIU_CDN_URL'),
68
- // adapter: createQiniuAdapter({
69
- // accessKey: config.get('QINIU_ACCESS_KEY'),
70
- // secretKey: config.get('QINIU_SECRET_KEY'),
71
- // bucket: config.get('QINIU_BUCKET'),
72
- // region: config.get('QINIU_REGION'),
73
- // publicDomain: config.get('QINIU_CDN_URL'),
74
- // }),
75
- // }),
76
- // }),
77
-
78
- PrismaModule,
79
- AuthModule,
80
- UserModule,
81
- ],
82
- controllers: [AppController],
83
- providers: [AppService],
84
- })
85
- export class AppModule {}
@@ -1,12 +0,0 @@
1
- import { Injectable } from '@nestjs/common';
2
-
3
- @Injectable()
4
- export class AppService {
5
- getHealth() {
6
- return {
7
- status: 'ok',
8
- timestamp: new Date().toISOString(),
9
- environment: process.env.NODE_ENV || 'development',
10
- };
11
- }
12
- }
@@ -1,31 +0,0 @@
1
- import { Controller, Post, Body, Get, UseGuards, Request } from '@nestjs/common';
2
- import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
3
- import { AuthService } from './auth.service';
4
- import { JwtAuthGuard } from './jwt-auth.guard';
5
- import type { LoginDto, RegisterDto } from '{{ORG_NAME}}/types';
6
-
7
- @ApiTags('认证')
8
- @Controller('auth')
9
- export class AuthController {
10
- constructor(private authService: AuthService) {}
11
-
12
- @Post('login')
13
- @ApiOperation({ summary: '登录' })
14
- async login(@Body() dto: LoginDto) {
15
- return this.authService.login(dto);
16
- }
17
-
18
- @Post('register')
19
- @ApiOperation({ summary: '注册' })
20
- async register(@Body() dto: RegisterDto) {
21
- return this.authService.register(dto);
22
- }
23
-
24
- @Get('me')
25
- @UseGuards(JwtAuthGuard)
26
- @ApiBearerAuth()
27
- @ApiOperation({ summary: '获取当前用户信息' })
28
- async getProfile(@Request() req: any) {
29
- return this.authService.validateUser(req.user.sub);
30
- }
31
- }
@@ -1,27 +0,0 @@
1
- import { Module } from '@nestjs/common';
2
- import { JwtModule } from '@nestjs/jwt';
3
- import { PassportModule } from '@nestjs/passport';
4
- import { ConfigModule, ConfigService } from '@nestjs/config';
5
- import { AuthController } from './auth.controller';
6
- import { AuthService } from './auth.service';
7
- import { JwtStrategy } from './jwt.strategy';
8
- import { PrismaModule } from '../prisma/prisma.module';
9
-
10
- @Module({
11
- imports: [
12
- PrismaModule,
13
- PassportModule.register({ defaultStrategy: 'jwt' }),
14
- JwtModule.registerAsync({
15
- imports: [ConfigModule],
16
- useFactory: async (configService: ConfigService) => ({
17
- secret: configService.get<string>('JWT_SECRET'),
18
- signOptions: { expiresIn: '7d' },
19
- }),
20
- inject: [ConfigService],
21
- }),
22
- ],
23
- controllers: [AuthController],
24
- providers: [AuthService, JwtStrategy],
25
- exports: [AuthService],
26
- })
27
- export class AuthModule {}
@@ -1,89 +0,0 @@
1
- import { Injectable, UnauthorizedException, ConflictException } from '@nestjs/common';
2
- import { JwtService } from '@nestjs/jwt';
3
- import * as bcrypt from 'bcrypt';
4
- import { PrismaService } from '../prisma/prisma.service';
5
- import type { LoginDto, RegisterDto, LoginVo } from '{{ORG_NAME}}/types';
6
-
7
- @Injectable()
8
- export class AuthService {
9
- constructor(
10
- private prisma: PrismaService,
11
- private jwtService: JwtService,
12
- ) {}
13
-
14
- async login(dto: LoginDto): Promise<LoginVo> {
15
- const user = await this.prisma.user.findUnique({
16
- where: { phone: dto.phone },
17
- });
18
-
19
- if (!user) {
20
- throw new UnauthorizedException('用户不存在');
21
- }
22
-
23
- const isPasswordValid = await bcrypt.compare(dto.password, user.password);
24
- if (!isPasswordValid) {
25
- throw new UnauthorizedException('密码错误');
26
- }
27
-
28
- if (user.status === 0) {
29
- throw new UnauthorizedException('账号已被禁用');
30
- }
31
-
32
- const payload = { sub: user.id, phone: user.phone, role: user.role };
33
- const accessToken = this.jwtService.sign(payload);
34
- const refreshToken = this.jwtService.sign(payload, { expiresIn: '30d' });
35
-
36
- return {
37
- accessToken,
38
- refreshToken,
39
- user: {
40
- id: user.id,
41
- phone: user.phone,
42
- nickname: user.nickname,
43
- avatar: user.avatar,
44
- role: user.role as any,
45
- status: user.status as any,
46
- createdAt: user.createdAt.toISOString(),
47
- updatedAt: user.updatedAt.toISOString(),
48
- },
49
- };
50
- }
51
-
52
- async register(dto: RegisterDto): Promise<LoginVo> {
53
- const existingUser = await this.prisma.user.findUnique({
54
- where: { phone: dto.phone },
55
- });
56
-
57
- if (existingUser) {
58
- throw new ConflictException('手机号已被注册');
59
- }
60
-
61
- const hashedPassword = await bcrypt.hash(dto.password, 10);
62
-
63
- const user = await this.prisma.user.create({
64
- data: {
65
- phone: dto.phone,
66
- password: hashedPassword,
67
- nickname: dto.nickname,
68
- role: 'user',
69
- status: 1,
70
- },
71
- });
72
-
73
- return this.login({ phone: dto.phone, password: dto.password });
74
- }
75
-
76
- async validateUser(userId: number) {
77
- return this.prisma.user.findUnique({
78
- where: { id: userId },
79
- select: {
80
- id: true,
81
- phone: true,
82
- nickname: true,
83
- avatar: true,
84
- role: true,
85
- status: true,
86
- },
87
- });
88
- }
89
- }
@@ -1,5 +0,0 @@
1
- import { Injectable } from '@nestjs/common';
2
- import { AuthGuard } from '@nestjs/passport';
3
-
4
- @Injectable()
5
- export class JwtAuthGuard extends AuthGuard('jwt') {}
@@ -1,27 +0,0 @@
1
- import { Injectable, UnauthorizedException } from '@nestjs/common';
2
- import { PassportStrategy } from '@nestjs/passport';
3
- import { ExtractJwt, Strategy } from 'passport-jwt';
4
- import { ConfigService } from '@nestjs/config';
5
- import { AuthService } from './auth.service';
6
-
7
- @Injectable()
8
- export class JwtStrategy extends PassportStrategy(Strategy) {
9
- constructor(
10
- private configService: ConfigService,
11
- private authService: AuthService,
12
- ) {
13
- super({
14
- jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
15
- ignoreExpiration: false,
16
- secretOrKey: configService.get<string>('JWT_SECRET'),
17
- });
18
- }
19
-
20
- async validate(payload: any) {
21
- const user = await this.authService.validateUser(payload.sub);
22
- if (!user) {
23
- throw new UnauthorizedException();
24
- }
25
- return { sub: payload.sub, phone: payload.phone, role: payload.role };
26
- }
27
- }
@@ -1,35 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- /**
4
- * 环境变量 Schema
5
- * 使用 @svton/nestjs-config-schema 进行验证
6
- */
7
- export const envSchema = z.object({
8
- // 应用配置
9
- NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
10
- PORT: z.coerce.number().default(3000),
11
-
12
- // 数据库
13
- DATABASE_URL: z.string().url(),
14
-
15
- // Redis(可选)
16
- REDIS_URL: z.string().url().optional(),
17
-
18
- // JWT
19
- JWT_SECRET: z.string().min(32),
20
- JWT_EXPIRES_IN: z.string().default('7d'),
21
-
22
- // 七牛云对象存储(可选)
23
- QINIU_ACCESS_KEY: z.string().optional(),
24
- QINIU_SECRET_KEY: z.string().optional(),
25
- QINIU_BUCKET: z.string().optional(),
26
- QINIU_REGION: z.string().optional(),
27
- QINIU_CDN_URL: z.string().url().optional(),
28
-
29
- // 阿里云短信(可选)
30
- ALIYUN_ACCESS_KEY_ID: z.string().optional(),
31
- ALIYUN_ACCESS_KEY_SECRET: z.string().optional(),
32
- SMS_SIGN_NAME: z.string().optional(),
33
- });
34
-
35
- export type EnvConfig = z.infer<typeof envSchema>;
@@ -1,51 +0,0 @@
1
- import { NestFactory } from '@nestjs/core';
2
- import { ValidationPipe } from '@nestjs/common';
3
- import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
4
- import * as express from 'express';
5
- import { AppModule } from './app.module';
6
-
7
- async function bootstrap() {
8
- const app = await NestFactory.create(AppModule, {
9
- // 如果使用 @svton/nestjs-logger,启用 bufferLogs
10
- // bufferLogs: true,
11
- });
12
-
13
- // 如果使用 @svton/nestjs-logger,设置 logger
14
- // app.useLogger(app.get(Logger));
15
-
16
- // Raw body 中间件(用于对象存储回调验签)
17
- // 仅对特定路径启用,避免影响全局 JSON 解析
18
- app.use('/object-storage/callback', express.raw({ type: '*/*' }));
19
-
20
- // 全局验证管道
21
- app.useGlobalPipes(
22
- new ValidationPipe({
23
- whitelist: true,
24
- transform: true,
25
- forbidNonWhitelisted: true,
26
- }),
27
- );
28
-
29
- // CORS
30
- app.enableCors({
31
- origin: true,
32
- credentials: true,
33
- });
34
-
35
- // Swagger 文档
36
- const config = new DocumentBuilder()
37
- .setTitle('API 文档')
38
- .setDescription('项目 API 接口文档')
39
- .setVersion('1.0')
40
- .addBearerAuth()
41
- .build();
42
- const document = SwaggerModule.createDocument(app, config);
43
- SwaggerModule.setup('api-docs', app, document);
44
-
45
- const port = process.env.PORT || 3000;
46
- await app.listen(port);
47
- console.log(`🚀 Server running on http://localhost:${port}`);
48
- console.log(`📚 API Docs: http://localhost:${port}/api-docs`);
49
- }
50
-
51
- bootstrap();
@@ -1,114 +0,0 @@
1
- import {
2
- Controller,
3
- Post,
4
- Get,
5
- Query,
6
- Req,
7
- UnauthorizedException,
8
- BadRequestException,
9
- Logger,
10
- } from '@nestjs/common';
11
- import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
12
- import { Request } from 'express';
13
- // import { InjectObjectStorage, ObjectStorageClient } from '@svton/nestjs-object-storage';
14
-
15
- /**
16
- * 对象存储控制器示例
17
- * 演示如何使用 @svton/nestjs-object-storage 进行文件上传和回调验签
18
- *
19
- * 使用前请先:
20
- * 1. 安装依赖:pnpm add @svton/nestjs-object-storage @svton/nestjs-object-storage-qiniu-kodo
21
- * 2. 在 AppModule 中启用 ObjectStorageModule
22
- * 3. 配置环境变量
23
- */
24
- @ApiTags('对象存储')
25
- @Controller('object-storage')
26
- export class ObjectStorageController {
27
- private readonly logger = new Logger(ObjectStorageController.name);
28
-
29
- // constructor(
30
- // @InjectObjectStorage() private readonly storage: ObjectStorageClient,
31
- // ) {}
32
-
33
- /**
34
- * 获取上传预签名 URL
35
- * 前端使用此 URL 直接上传文件到云存储
36
- */
37
- @ApiBearerAuth()
38
- @Get('presign')
39
- @ApiOperation({ summary: '获取上传预签名 URL' })
40
- async getPresignedUrl(
41
- @Query('key') key: string,
42
- @Query('contentType') contentType?: string,
43
- ) {
44
- if (!key) {
45
- throw new BadRequestException('key is required');
46
- }
47
-
48
- // 示例代码(启用 ObjectStorageModule 后取消注释)
49
- // const result = await this.storage.presign({
50
- // key,
51
- // method: 'PUT',
52
- // expiresIn: 3600,
53
- // contentType,
54
- // callback: {
55
- // url: 'https://your-domain.com/object-storage/callback',
56
- // body: 'key=$(key)&hash=$(etag)&bucket=$(bucket)&fsize=$(fsize)',
57
- // },
58
- // });
59
- // return result;
60
-
61
- return {
62
- message: 'ObjectStorageModule not enabled. See app.module.ts for setup instructions.',
63
- };
64
- }
65
-
66
- /**
67
- * 对象存储回调接口
68
- * 云存储上传完成后会调用此接口进行通知
69
- *
70
- * 注意:需要配置 raw body 中间件才能正确验签
71
- * 参见 main.ts 中的 rawBody 配置
72
- */
73
- @Post('callback')
74
- @ApiOperation({ summary: '对象存储回调(云存储调用)' })
75
- async handleCallback(@Req() req: Request) {
76
- // 获取 rawBody(需要在 main.ts 中配置)
77
- const rawBody = (req as Request & { rawBody?: Buffer }).rawBody;
78
-
79
- if (!rawBody) {
80
- this.logger.warn('rawBody not available. Please configure raw body middleware.');
81
- throw new BadRequestException('rawBody not available');
82
- }
83
-
84
- // 示例代码(启用 ObjectStorageModule 后取消注释)
85
- // const result = await this.storage.verifyCallback({
86
- // method: req.method,
87
- // path: req.path,
88
- // query: req.query as Record<string, string | string[]>,
89
- // headers: req.headers as Record<string, string | string[]>,
90
- // rawBody,
91
- // });
92
-
93
- // if (!result.isValid) {
94
- // this.logger.warn('Invalid callback signature');
95
- // throw new UnauthorizedException('Invalid callback signature');
96
- // }
97
-
98
- // this.logger.log(`File uploaded: ${result.key}, size: ${result.size}`);
99
-
100
- // // 在这里处理业务逻辑,例如:
101
- // // - 保存文件记录到数据库
102
- // // - 触发后续处理(如图片压缩、视频转码等)
103
-
104
- // return {
105
- // success: true,
106
- // key: result.key,
107
- // etag: result.etag,
108
- // };
109
-
110
- return {
111
- message: 'ObjectStorageModule not enabled. See app.module.ts for setup instructions.',
112
- };
113
- }
114
- }
@@ -1,7 +0,0 @@
1
- import { Module } from '@nestjs/common';
2
- import { ObjectStorageController } from './object-storage.controller';
3
-
4
- @Module({
5
- controllers: [ObjectStorageController],
6
- })
7
- export class ObjectStorageExampleModule {}
@@ -1,9 +0,0 @@
1
- import { Global, Module } from '@nestjs/common';
2
- import { PrismaService } from './prisma.service';
3
-
4
- @Global()
5
- @Module({
6
- providers: [PrismaService],
7
- exports: [PrismaService],
8
- })
9
- export class PrismaModule {}