Selaa lähdekoodia

fix: 历史记录接口加用户隔离 - 修复显示/可删他人创作

bug: history 列表与批量删除接口算出了 userId 却没用于查询,
where 条件不带用户过滤:
- GET /history 和 GET /history/list 返回全库所有人的音频记录
  → 历史页显示别人的创作(数据越权读)
- POST /batch-delete 和 DELETE /batch 按 audioId 删除不校验归属
  → 可删除他人记录(数据越权写)

修复: 四处查询/删除都加 OR[{userId:本人},{userId:null}] 过滤,
与书籍列表的隔离规则一致. AudioRecord.userId 字段已存在.
MyFramework User 2 kuukautta sitten
vanhempi
sitoutus
1752f0945b

+ 10 - 2
server/src/modules/history/history-batch.controller.ts

@@ -24,10 +24,14 @@ router.post('/batch-delete', optionalAuth, async (ctx: Context) => {
   }
 
   try {
-    // 删除 AudioRecord 记录
+    // 删除 AudioRecord 记录(仅限本人或游客记录,避免删除他人数据)
     const result = await prisma.audioRecord.deleteMany({
       where: {
         audioId: { in: body.ids },
+        OR: [
+          { userId: parseInt(userId as string) },
+          { userId: null },
+        ],
       },
     });
 
@@ -77,10 +81,14 @@ router.delete('/batch', optionalAuth, async (ctx: Context) => {
   }
 
   try {
-    // 删除 AudioRecord 记录
+    // 删除 AudioRecord 记录(仅限本人或游客记录,避免删除他人数据)
     const result = await prisma.audioRecord.deleteMany({
       where: {
         audioId: { in: ids },
+        OR: [
+          { userId: parseInt(userId as string) },
+          { userId: null },
+        ],
       },
     });
 

+ 16 - 2
server/src/modules/history/history.controller.ts

@@ -11,11 +11,18 @@ const router = new Router();
 router.get('/list', optionalAuth, async (ctx: Context) => {
   // 开发环境使用测试用户ID
   const userId = ctx.state.user?.userId || TEST_USER_ID;
+  const userIdNum = parseInt(userId as string);
   const page = parseInt(ctx.query.page as string) || 1;
   const pageSize = parseInt(ctx.query.pageSize as string) || 20;
   const startDate = ctx.query.startDate as string;
 
-  const where: any = {};
+  // 用户隔离:只返回本人(或游客 null)的音频记录
+  const where: any = {
+    OR: [
+      { userId: userIdNum },
+      { userId: null },
+    ],
+  };
   if (startDate) {
     where.createdAt = { gte: new Date(startDate) };
   }
@@ -67,11 +74,18 @@ router.get('/list', optionalAuth, async (ctx: Context) => {
 router.get('/', optionalAuth, async (ctx: Context) => {
   // 开发环境使用测试用户ID
   const userId = ctx.state.user?.userId || TEST_USER_ID;
+  const userIdNum = parseInt(userId as string);
   const page = parseInt(ctx.query.page as string) || 1;
   const pageSize = parseInt(ctx.query.pageSize as string) || 20;
   const startDate = ctx.query.startDate as string;
 
-  const where: any = {};
+  // 用户隔离:只返回本人(或游客 null)的音频记录,避免显示别人的创作
+  const where: any = {
+    OR: [
+      { userId: userIdNum },
+      { userId: null },
+    ],
+  };
   if (startDate) {
     where.createdAt = { gte: new Date(startDate) };
   }