mirror of
https://github.com/LogicLabs-OU/OpenArchiver.git
synced 2026-04-06 00:31:57 +02:00
* adding exports to backend package, page icons update * Integrity report PDF generation * Fixed inline attachment images not displaying in the email preview by modifying `EmailPreview.svelte`. The email HTML references embedded images via `cid:` URIs (e.g., `src="cid:ii_19c6d5f8d5eee7bd6d91"`), but the component never resolved those `cid:` references to actual image data, even though `postal-mime` already parses inline attachments with their `contentId` and binary `content`. The `emailHtml` derived value now calls `resolveContentIdReferences()` before rendering, so inline/embedded images display correctly in the iframe preview. * feat: strip non-inline attachments from EML before storage Add nodemailer dependency and emlUtils helper to remove non-inline attachments from .eml buffers during ingestion. This avoids double-storing attachment data since attachments are already stored separately. * upload error handing for file based ingestion * Use Postgres for sync session management * Google workspace / MS 365 duplicate check, avoid extra API call when previous ingestion fails * OpenAPI specs for API docs * code formatting * ran duplicate check for IMAP import, optimize message listing * Version update
100 lines
3.3 KiB
TypeScript
100 lines
3.3 KiB
TypeScript
import { Request, Response } from 'express';
|
|
import { ArchivedEmailService } from '../../services/ArchivedEmailService';
|
|
import { UserService } from '../../services/UserService';
|
|
import { checkDeletionEnabled } from '../../helpers/deletionGuard';
|
|
|
|
export class ArchivedEmailController {
|
|
private userService = new UserService();
|
|
public getArchivedEmails = async (req: Request, res: Response): Promise<Response> => {
|
|
try {
|
|
const { ingestionSourceId } = req.params;
|
|
const page = parseInt(req.query.page as string, 10) || 1;
|
|
const limit = parseInt(req.query.limit as string, 10) || 10;
|
|
const userId = req.user?.sub;
|
|
|
|
if (!userId) {
|
|
return res.status(401).json({ message: req.t('errors.unauthorized') });
|
|
}
|
|
|
|
const result = await ArchivedEmailService.getArchivedEmails(
|
|
ingestionSourceId,
|
|
page,
|
|
limit,
|
|
userId
|
|
);
|
|
return res.status(200).json(result);
|
|
} catch (error) {
|
|
console.error('Get archived emails error:', error);
|
|
return res.status(500).json({ message: req.t('errors.internalServerError') });
|
|
}
|
|
};
|
|
|
|
public getArchivedEmailById = async (req: Request, res: Response): Promise<Response> => {
|
|
try {
|
|
const { id } = req.params;
|
|
const userId = req.user?.sub;
|
|
|
|
if (!userId) {
|
|
return res.status(401).json({ message: req.t('errors.unauthorized') });
|
|
}
|
|
const actor = await this.userService.findById(userId);
|
|
if (!actor) {
|
|
return res.status(401).json({ message: req.t('errors.unauthorized') });
|
|
}
|
|
|
|
const email = await ArchivedEmailService.getArchivedEmailById(
|
|
id,
|
|
userId,
|
|
actor,
|
|
req.ip || 'unknown'
|
|
);
|
|
if (!email) {
|
|
return res.status(404).json({ message: req.t('archivedEmail.notFound') });
|
|
}
|
|
return res.status(200).json(email);
|
|
} catch (error) {
|
|
console.error(`Get archived email by id ${req.params.id} error:`, error);
|
|
return res.status(500).json({ message: req.t('errors.internalServerError') });
|
|
}
|
|
};
|
|
|
|
public deleteArchivedEmail = async (req: Request, res: Response): Promise<Response> => {
|
|
// Guard: return 400 if deletion is disabled in system settings before touching anything else
|
|
try {
|
|
checkDeletionEnabled();
|
|
} catch (error) {
|
|
return res.status(400).json({
|
|
message: error instanceof Error ? error.message : req.t('errors.deletionDisabled'),
|
|
});
|
|
}
|
|
|
|
const { id } = req.params;
|
|
const userId = req.user?.sub;
|
|
if (!userId) {
|
|
return res.status(401).json({ message: req.t('errors.unauthorized') });
|
|
}
|
|
const actor = await this.userService.findById(userId);
|
|
if (!actor) {
|
|
return res.status(401).json({ message: req.t('errors.unauthorized') });
|
|
}
|
|
|
|
try {
|
|
await ArchivedEmailService.deleteArchivedEmail(id, actor, req.ip || 'unknown');
|
|
return res.status(204).send();
|
|
} catch (error) {
|
|
console.error(`Delete archived email ${req.params.id} error:`, error);
|
|
if (error instanceof Error) {
|
|
if (error.message === 'Archived email not found') {
|
|
return res.status(404).json({ message: req.t('archivedEmail.notFound') });
|
|
}
|
|
// Retention policy / legal hold blocks are user-facing 400 errors
|
|
if (error.message.startsWith('Deletion blocked by retention policy')) {
|
|
return res.status(400).json({ message: error.message });
|
|
}
|
|
return res.status(500).json({ message: error.message });
|
|
}
|
|
return res.status(500).json({ message: req.t('errors.internalServerError') });
|
|
}
|
|
};
|
|
}
|