Browse Source

✨ feat(posts): add post detail and interaction APIs

- add GET /posts/[id] to retrieve post details
- add PUT /posts/[id] to update posts
- add DELETE /posts/[id] to delete posts
- add POST /posts/[id]/like to like posts
- add DELETE /posts/[id]/like to unlike posts
- implement OpenAPI schema validation for all endpoints
- add auth middleware to protect post operations
- handle error cases with appropriate status codes and messages
yourname 5 tháng trước cách đây
mục cha
commit
f0d36448ea

+ 77 - 0
src/server/api/posts/[id]/delete.ts

@@ -0,0 +1,77 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { AppDataSource } from '@/server/data-source';
+import { PostService } from '@/server/modules/posts/post.service';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+
+// 参数Schema
+const ParamsSchema = z.object({
+  id: z.coerce.number().int().positive().openapi({
+    param: { name: 'id', in: 'path' },
+    example: 1,
+    description: '帖子ID'
+  })
+});
+
+// 响应Schema
+const SuccessSchema = z.object({
+  success: z.boolean().openapi({
+    example: true,
+    description: '操作是否成功'
+  }),
+  message: z.string().openapi({
+    example: '删除成功',
+    description: '操作结果消息'
+  })
+});
+
+// 路由定义
+const routeDef = createRoute({
+  method: 'delete',
+  path: '/{id}',
+  middleware: [authMiddleware],
+  request: {
+    params: ParamsSchema
+  },
+  responses: {
+    200: {
+      description: '成功删除帖子',
+      content: { 'application/json': { schema: SuccessSchema } }
+    },
+    400: {
+      description: '请求参数错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '帖子不存在或没有权限',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '服务器错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 路由实现
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { id } = c.req.valid('param');
+    const user = c.get('user');
+    const postService = new PostService(AppDataSource);
+    
+    const result = await postService.deletePost(id, user.id);
+    if (!result) {
+      return c.json({ code: 404, message: '帖子不存在或没有权限' }, 404);
+    }
+    
+    return c.json({ success: true, message: '删除成功' }, 200);
+  } catch (error) {
+    const { code = 500, message = '删除帖子失败' } = error as Error & { code?: number };
+    return c.json({ code, message }, code as unknown as 400 | 404 | 500);
+  }
+});
+
+export default app;

+ 65 - 0
src/server/api/posts/[id]/get.ts

@@ -0,0 +1,65 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { AppDataSource } from '@/server/data-source';
+import { PostService } from '@/server/modules/posts/post.service';
+import { PostSchema } from '@/server/modules/posts/post.entity';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+
+// 参数Schema
+const ParamsSchema = z.object({
+  id: z.coerce.number().int().positive().openapi({
+    param: { name: 'id', in: 'path' },
+    example: 1,
+    description: '帖子ID'
+  })
+});
+
+// 路由定义
+const routeDef = createRoute({
+  method: 'get',
+  path: '/{id}',
+  middleware: [authMiddleware],
+  request: {
+    params: ParamsSchema
+  },
+  responses: {
+    200: {
+      description: '成功获取帖子详情',
+      content: { 'application/json': { schema: PostSchema } }
+    },
+    400: {
+      description: '请求参数错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '帖子不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '服务器错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 路由实现
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { id } = c.req.valid('param');
+    const postService = new PostService(AppDataSource);
+    
+    const post = await postService.getPostById(id);
+    if (!post) {
+      return c.json({ code: 404, message: '帖子不存在' }, 404);
+    }
+    
+    return c.json(post, 200);
+  } catch (error) {
+    const { code = 500, message = '获取帖子详情失败' } = error as Error & { code?: number };
+    return c.json({ code, message }, code as unknown as 400 | 404 | 500);
+  }
+});
+
+export default app;

+ 61 - 0
src/server/api/posts/[id]/like/delete.ts

@@ -0,0 +1,61 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { AppDataSource } from '@/server/data-source';
+import { PostService } from '@/server/modules/posts/post.service';
+import { PostSchema } from '@/server/modules/posts/post.entity';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+
+// 参数Schema
+const ParamsSchema = z.object({
+  id: z.coerce.number().int().positive().openapi({
+    param: { name: 'id', in: 'path' },
+    example: 1,
+    description: '帖子ID'
+  })
+});
+
+// 路由定义
+const routeDef = createRoute({
+  method: 'delete',
+  path: '/{id}/like',
+  middleware: [authMiddleware],
+  request: {
+    params: ParamsSchema
+  },
+  responses: {
+    200: {
+      description: '成功取消点赞帖子',
+      content: { 'application/json': { schema: PostSchema } }
+    },
+    400: {
+      description: '请求参数错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '帖子不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '服务器错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 路由实现
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { id } = c.req.valid('param');
+    const postService = new PostService(AppDataSource);
+    
+    const result = await postService.unlikePost(id);
+    return c.json(result, 200);
+  } catch (error) {
+    const { code = 500, message = '取消点赞失败' } = error as Error & { code?: number };
+    return c.json({ code, message }, code as unknown as 400 | 404 | 500);
+  }
+});
+
+export default app;

+ 61 - 0
src/server/api/posts/[id]/like/post.ts

@@ -0,0 +1,61 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { AppDataSource } from '@/server/data-source';
+import { PostService } from '@/server/modules/posts/post.service';
+import { PostSchema } from '@/server/modules/posts/post.entity';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+
+// 参数Schema
+const ParamsSchema = z.object({
+  id: z.coerce.number().int().positive().openapi({
+    param: { name: 'id', in: 'path' },
+    example: 1,
+    description: '帖子ID'
+  })
+});
+
+// 路由定义
+const routeDef = createRoute({
+  method: 'post',
+  path: '/{id}/like',
+  middleware: [authMiddleware],
+  request: {
+    params: ParamsSchema
+  },
+  responses: {
+    200: {
+      description: '成功点赞帖子',
+      content: { 'application/json': { schema: PostSchema } }
+    },
+    400: {
+      description: '请求参数错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '帖子不存在',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '服务器错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 路由实现
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { id } = c.req.valid('param');
+    const postService = new PostService(AppDataSource);
+    
+    const result = await postService.likePost(id);
+    return c.json(result, 200);
+  } catch (error) {
+    const { code = 500, message = '点赞失败' } = error as Error & { code?: number };
+    return c.json({ code, message }, code as unknown as 400 | 404 | 500);
+  }
+});
+
+export default app;

+ 72 - 0
src/server/api/posts/[id]/put.ts

@@ -0,0 +1,72 @@
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { AppDataSource } from '@/server/data-source';
+import { PostService } from '@/server/modules/posts/post.service';
+import { UpdatePostDto, PostSchema } from '@/server/modules/posts/post.entity';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+
+// 参数Schema
+const ParamsSchema = z.object({
+  id: z.coerce.number().int().positive().openapi({
+    param: { name: 'id', in: 'path' },
+    example: 1,
+    description: '帖子ID'
+  })
+});
+
+// 路由定义
+const routeDef = createRoute({
+  method: 'put',
+  path: '/{id}',
+  middleware: [authMiddleware],
+  request: {
+    params: ParamsSchema,
+    body: {
+      content: {
+        'application/json': { schema: UpdatePostDto }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '成功更新帖子',
+      content: { 'application/json': { schema: PostSchema } }
+    },
+    400: {
+      description: '请求参数错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    404: {
+      description: '帖子不存在或没有权限',
+      content: { 'application/json': { schema: ErrorSchema } }
+    },
+    500: {
+      description: '服务器错误',
+      content: { 'application/json': { schema: ErrorSchema } }
+    }
+  }
+});
+
+// 路由实现
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { id } = c.req.valid('param');
+    const data = await c.req.json();
+    const user = c.get('user');
+    const postService = new PostService(AppDataSource);
+    
+    const result = await postService.updatePost(id, user.id, data);
+    if (!result) {
+      return c.json({ code: 404, message: '帖子不存在或没有权限' }, 404);
+    }
+    
+    return c.json(result, 200);
+  } catch (error) {
+    const { code = 500, message = '更新帖子失败' } = error as Error & { code?: number };
+    return c.json({ code, message }, code as unknown as 400 | 404 | 500);
+  }
+});
+
+export default app;