feat: 优化项目目录结构

This commit is contained in:
2025-12-12 17:25:26 +08:00
parent ae627d0496
commit b89f83291e
235 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,137 @@
import {
BadRequestException,
Body,
Controller,
Get,
Param,
ParseUUIDPipe,
Post,
Query,
Req,
UseGuards,
} from '@nestjs/common';
import { BlogService } from './blog.service';
import { OptionalAuthGuard } from 'src/auth/strategies/OptionalAuthGuard';
import { UserService } from 'src/user/user.service';
import { createBlogCommentDto } from './dto/create.blogcomment.dto';
import { Throttle, ThrottlerGuard } from '@nestjs/throttler';
import { BlogPermission } from './blog.permission.enum';
@Controller('blog')
export class BlogController {
constructor(
private readonly blogService: BlogService,
private readonly userService: UserService,
) {}
@Get()
getBlogs() {
return this.blogService.list();
}
@Get(':id')
async getBlog(
@Param('id', new ParseUUIDPipe({ version: '4' })) id: string,
@Query('p') password?: string,
) {
const blog = await this.blogService.findById(id);
if (!blog) throw new BadRequestException('文章不存在或无权限访问');
if (!blog.permissions.includes(BlogPermission.Public)) {
// 无公开权限,则进一步检查是否有密码保护
if (!blog.permissions.includes(BlogPermission.ByPassword)) {
throw new BadRequestException('文章不存在或无权限访问');
} else {
// 判断密码是否正确
if (
!password ||
this.blogService.hashPassword(password) !== blog.password_hash
) {
throw new BadRequestException('文章不存在或无权限访问');
}
}
}
const blogDataRes = await fetch(`${blog.contentUrl}`);
const blogContent = await blogDataRes.text();
await this.blogService.incrementViewCount(id);
return {
id: blog.id,
title: blog.title,
createdAt: blog.createdAt,
content: blogContent,
};
}
@Get(':id/comments')
async getBlogComments(
@Param('id', new ParseUUIDPipe({ version: '4' })) id: string,
) {
const blog = await this.blogService.findById(id);
if (!blog) throw new BadRequestException('文章不存在');
/** @todo 对文章可读性进行更详细的判定 */
if (
!blog.permissions.includes(BlogPermission.Public) &&
!blog.permissions.includes(BlogPermission.ByPassword)
) {
throw new BadRequestException('文章不存在或未公开');
}
return await this.blogService.getComments(blog);
}
// 该接口允许匿名评论但仍需验证userId合法性
@UseGuards(ThrottlerGuard, OptionalAuthGuard)
@Throttle({ default: { limit: 5, ttl: 60000 } })
@Post(':id/comment')
async createBlogComment(
@Param('id', new ParseUUIDPipe({ version: '4' })) id: string,
@Body() commentData: createBlogCommentDto,
@Req() req,
) {
const { userId } = req.user || {};
const blog = await this.blogService.findById(id);
if (!blog) throw new BadRequestException('文章不存在');
if (!blog.permissions.includes(BlogPermission.AllowComments)) {
throw new BadRequestException('作者关闭了该文章的评论功能');
}
const user = userId ? await this.userService.findById(userId) : null;
const ip = req.headers['x-forwarded-for'] || req.ip;
// 获取IP归属地
let address = '未知';
if (!['::1'].includes(ip)) {
const addressRes = await (
await fetch(
`https://mesh.if.iqiyi.com/aid/ip/info?version=1.1.1&ip=${ip}`,
)
).json();
if (addressRes?.code == 0) {
const country: string = addressRes?.data?.countryCN || '未知';
const province: string = addressRes?.data?.provinceCN || '中国';
if (country !== '中国') {
// 非中国,显示国家
address = country;
} else {
// 中国,显示省份
address = province;
}
}
}
const comment = {
...commentData,
blog,
user,
ip,
address,
};
return await this.blogService.createComment(comment);
}
}