feat(mail): E-Mails mit Leserecht als ignoriert markieren
Der Button „Als ignoriert markieren“ war für alle mit VIEW_MAIL sichtbar, der Aufruf (PATCH :id/status) verlangte aber MANAGE_ALL und scheiterte mit 403. Neuer Endpunkt POST /api/emails/:id/ignore mit VIEW_MAIL, der nur Status 3 setzen kann; bereits verarbeitete E-Mails werden abgelehnt. Das allgemeine Statusändern bleibt Admins vorbehalten. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
f97177943d
commit
3cea3fb239
4 files changed
+85
-2
No files matched your search
@@ -7,6 +7,9 @@ import { Attachment } from '../database/entities/attachment.entity';
|
||||
import { Content } from '../database/entities/content.entity';
|
||||
import { PaperlessService } from '../paperless/paperless.service';
|
||||
import { ImapFolderService } from './imap-folder.service';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { PERMISSIONS_KEY } from '../auth/permissions.decorator';
|
||||
import { Permission } from '../auth/permissions.enum';
|
||||
|
||||
const mockEmails: Partial<Email>[] = [
|
||||
{
|
||||
@@ -173,6 +176,47 @@ describe('EmailController', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('ignore', () => {
|
||||
it.each([
|
||||
['Neu', 0],
|
||||
['Fehler', 2],
|
||||
['zur Nachbearbeitung', 4],
|
||||
])(
|
||||
'markiert eine E-Mail mit Status %s als ignoriert',
|
||||
async (_l, status) => {
|
||||
emailRepo.findOneOrFail.mockResolvedValue({ Id: 1, Status: status });
|
||||
|
||||
const result = await controller.ignore('1', { user: {} });
|
||||
|
||||
expect(emailRepo.save).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ Id: 1, Status: 3 }),
|
||||
);
|
||||
expect(result).toEqual(expect.objectContaining({ Status: 3 }));
|
||||
},
|
||||
);
|
||||
|
||||
it('lehnt eine bereits verarbeitete E-Mail ab', async () => {
|
||||
emailRepo.findOneOrFail.mockResolvedValue({ Id: 1, Status: 1 });
|
||||
|
||||
await expect(controller.ignore('1', { user: {} })).rejects.toThrow(
|
||||
BadRequestException,
|
||||
);
|
||||
expect(emailRepo.save).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('ist für alle mit VIEW_MAIL freigegeben', () => {
|
||||
const handler = Object.getOwnPropertyDescriptor(
|
||||
EmailController.prototype,
|
||||
'ignore',
|
||||
)?.value as () => unknown;
|
||||
const permissions = new Reflector().get<Permission[]>(
|
||||
PERMISSIONS_KEY,
|
||||
handler,
|
||||
);
|
||||
expect(permissions).toEqual([Permission.VIEW_MAIL]);
|
||||
});
|
||||
});
|
||||
|
||||
it('updateStatus speichert den neuen Status', async () => {
|
||||
await controller.updateStatus('1', 2);
|
||||
expect(emailRepo.save).toHaveBeenCalledWith(
|
||||
|
||||
@@ -32,6 +32,8 @@ import { Permission } from '../auth/permissions.enum';
|
||||
* würde sie sonst sofort wieder auf "Verarbeitet" setzen.
|
||||
*/
|
||||
const STATUS_NEU = 0;
|
||||
const STATUS_VERARBEITET = 1;
|
||||
const STATUS_IGNORIERT = 3;
|
||||
const STATUS_NACHBEARBEITUNG = 4;
|
||||
|
||||
@Controller('api/emails')
|
||||
@@ -123,6 +125,39 @@ export class EmailController {
|
||||
return { message: 'Status aktualisiert' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Markiert eine E-Mail als ignoriert. Anders als `PATCH :id/status` darf das
|
||||
* jeder mit Zugriff aufs Mailpostfach – der Endpunkt kann aber nur Status 3
|
||||
* setzen. Bereits verarbeitete E-Mails bleiben unangetastet.
|
||||
*/
|
||||
@Post(':id/ignore')
|
||||
@RequirePermissions(Permission.VIEW_MAIL)
|
||||
async ignore(
|
||||
@Param('id') id: string,
|
||||
@Request() req: { user?: { email?: string; userId?: string } },
|
||||
) {
|
||||
const email = await this.emailRepo.findOneOrFail({
|
||||
where: { Id: parseInt(id, 10) },
|
||||
});
|
||||
|
||||
if (email.Status === STATUS_VERARBEITET) {
|
||||
throw new BadRequestException(
|
||||
'Eine bereits verarbeitete E-Mail kann nicht ignoriert werden.',
|
||||
);
|
||||
}
|
||||
|
||||
const vorherigerStatus = email.Status;
|
||||
email.Status = STATUS_IGNORIERT;
|
||||
await this.emailRepo.save(email);
|
||||
|
||||
this.logger.log(
|
||||
`E-Mail ${id} von Status ${vorherigerStatus} als ignoriert markiert ` +
|
||||
`(durch ${req.user?.email ?? req.user?.userId ?? 'unbekannt'})`,
|
||||
);
|
||||
|
||||
return email;
|
||||
}
|
||||
|
||||
@Post(':id/reimport')
|
||||
@RequirePermissions(Permission.MANAGE_ALL)
|
||||
async reimport(
|
||||
|
||||
Reference in new issue
Block a user