P8 平台轻量化改造 + SSG修复 + 编程导师 + 文档完善
- Prisma: Tool 模型加 affiliateLink;免费用户沙盒 10→5 次/日 - 后端: Tools API + /admin/tools CRUD 5 端点;Practices 完整模块 - 导航: 主菜单隐藏企业版/社区(URL 可访问) - 首页: 重定位为 AI 工具指南;新增精选工具区块;Feature 重写 - 工具页: affiliateLink 绿色推荐 Badge - SSG 修复: config.ts 构建时直连 localhost:4000,页面 108→127 - 沙盒: 新增编程导师场景(苏格拉底教学法) - 练习系统: Practices 多场景练习(含结构化评分) - 技能广场: 6 个付费 Skill(标题大师/回款助手等) - 管理后台: Models/Posts/Practices CRUD 页面 - 文档: README + progress.md 全面更新;AGENTS.md 同步定位 - 清理: .env.example 移除;tsbuildinfo gitignore
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Controller, Post, Get, Body, UseGuards, Req } from '@nestjs/common';
|
||||
import { Controller, Post, Get, Body, UseGuards, Req, Res } from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { Throttle } from '@nestjs/throttler';
|
||||
import { AuthService } from './auth.service';
|
||||
import { RegisterDto } from './dto/register.dto';
|
||||
import { LoginDto } from './dto/login.dto';
|
||||
@@ -10,19 +11,39 @@ import { LoginDto } from './dto/login.dto';
|
||||
export class AuthController {
|
||||
constructor(private authService: AuthService) {}
|
||||
|
||||
private setTokenCookie(res: any, accessToken: string) {
|
||||
const isProd = process.env.NODE_ENV === 'production';
|
||||
res.cookie('token', accessToken, {
|
||||
httpOnly: true,
|
||||
secure: isProd,
|
||||
sameSite: 'lax',
|
||||
path: '/',
|
||||
maxAge: 7 * 24 * 60 * 60 * 1000,
|
||||
});
|
||||
}
|
||||
|
||||
@Post('register')
|
||||
async register(@Body() body: RegisterDto) {
|
||||
return this.authService.register(body);
|
||||
@Throttle({ default: { limit: 3, ttl: 60000 } })
|
||||
async register(@Body() body: RegisterDto, @Res({ passthrough: true }) res: any) {
|
||||
const tokens = await this.authService.register(body);
|
||||
this.setTokenCookie(res, tokens.accessToken);
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Post('login')
|
||||
async login(@Body() body: LoginDto) {
|
||||
return this.authService.login(body.account, body.password);
|
||||
@Throttle({ default: { limit: 5, ttl: 60000 } })
|
||||
async login(@Body() body: LoginDto, @Res({ passthrough: true }) res: any) {
|
||||
const tokens = await this.authService.login(body.account, body.password);
|
||||
this.setTokenCookie(res, tokens.accessToken);
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Post('refresh')
|
||||
async refresh(@Body() body: { accessToken: string }) {
|
||||
return this.authService.refreshAccessToken(body.accessToken);
|
||||
@Throttle({ default: { limit: 10, ttl: 60000 } })
|
||||
async refresh(@Body() body: { accessToken: string }, @Res({ passthrough: true }) res: any) {
|
||||
const tokens = await this.authService.refreshAccessToken(body.accessToken);
|
||||
this.setTokenCookie(res, tokens.accessToken);
|
||||
return tokens;
|
||||
}
|
||||
|
||||
@Get('profile')
|
||||
@@ -31,4 +52,17 @@ export class AuthController {
|
||||
async profile(@Req() req: any) {
|
||||
return this.authService.getProfile(req.user.userId);
|
||||
}
|
||||
|
||||
@Get('verify')
|
||||
@UseGuards(AuthGuard('jwt'))
|
||||
async verify(@Req() req: any) {
|
||||
const profile = await this.authService.getProfile(req.user.userId);
|
||||
return { ...profile, accessToken: req.headers.authorization?.replace('Bearer ', '') || '' };
|
||||
}
|
||||
|
||||
@Post('logout')
|
||||
async logout(@Res({ passthrough: true }) res: any) {
|
||||
res.cookie('token', '', { httpOnly: true, maxAge: 0, path: '/' });
|
||||
return { message: '已退出' };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,15 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
private config: ConfigService,
|
||||
private prisma: PrismaService,
|
||||
) {
|
||||
const secret = config.get<string>('JWT_SECRET') || 'yuzhiran-ai-default-secret';
|
||||
const secret = config.get<string>('JWT_SECRET');
|
||||
if (!secret) {
|
||||
throw new Error('JWT_SECRET environment variable is required');
|
||||
}
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
jwtFromRequest: ExtractJwt.fromExtractors([
|
||||
ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
(req) => req?.cookies?.token || null,
|
||||
]),
|
||||
ignoreExpiration: false,
|
||||
secretOrKey: secret,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user