diff --git a/apps/api/src/activities/activities.service.ts b/apps/api/src/activities/activities.service.ts index 9cd2382..75e7b6b 100644 --- a/apps/api/src/activities/activities.service.ts +++ b/apps/api/src/activities/activities.service.ts @@ -99,7 +99,9 @@ function canCollaborateActivity( /** فقط dueAt / description → تعویق و ثبت نظر */ function isCollaboratorPatch(dto: UpdateActivityRequest): boolean { - const keys = Object.keys(dto); + const keys = Object.keys(dto).filter( + (key) => (dto as Record)[key] !== undefined, + ); return keys.length > 0 && keys.every((key) => key === 'dueAt' || key === 'description'); } @@ -1199,8 +1201,9 @@ export class ActivitiesService { assignees: { select: { id: true } }, }, }); - if (!gate || !canMutateActivity(actor, gate)) { - throw new ForbiddenException('شما مجوز ویرایش این فعالیت را ندارید'); + // پیوست در مسیر ثبت نظر — مسئولان هم مجازند حتی با editPolicy=creator + if (!gate || !canCollaborateActivity(actor, gate)) { + throw new ForbiddenException('شما مجوز ثبت نظر یا پیوست برای این فعالیت را ندارید'); } if (!file?.buffer?.length) { throw new BadRequestException('فایلی ارسال نشده است'); @@ -1305,13 +1308,18 @@ export class ActivitiesService { assignees: { select: { id: true } }, }, }); - if (!gate || !canMutateActivity(actor, gate)) { - throw new ForbiddenException('شما مجوز ویرایش این فعالیت را ندارید'); - } const row = await this.prisma.activityImage.findFirst({ where: { id: imageId, activityId, organizationId }, }); if (!row) throw new NotFoundException('فایل پیدا نشد'); + const canMutate = !!gate && canMutateActivity(actor, gate); + const canRemoveOwn = + !!gate && + canCollaborateActivity(actor, gate) && + row.uploadedById === actor.userId; + if (!canMutate && !canRemoveOwn) { + throw new ForbiddenException('شما مجوز حذف این پیوست را ندارید'); + } await this.files.remove(row.storageKey); await this.prisma.activityImage.delete({ where: { id: row.id } }); await this.storage.addLocal(organizationId, -row.sizeBytes);