582 lines
24 KiB
TypeScript
582 lines
24 KiB
TypeScript
import { Request, Response } from 'express';
|
|
import issueService from '../services/issue.service';
|
|
import { permissionService } from '../services/permission.service';
|
|
import logger from '../utils/logger';
|
|
|
|
const param = (req: Request, key: string): string => req.params[key] as string;
|
|
|
|
class IssueController {
|
|
async getIssues(req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const userId = req.user!.id;
|
|
const groups: string[] = (req.user as any).groups || [];
|
|
const canViewAll = permissionService.hasPermission(groups, 'issues:view_all');
|
|
|
|
// Parse filter query params
|
|
const filters: {
|
|
typ_id?: number[];
|
|
prioritaet?: string[];
|
|
status?: string[];
|
|
erstellt_von?: string;
|
|
zugewiesen_an?: string;
|
|
} = {};
|
|
|
|
if (req.query.typ_id) {
|
|
filters.typ_id = String(req.query.typ_id).split(',').map(Number).filter((n) => !isNaN(n));
|
|
}
|
|
if (req.query.prioritaet) {
|
|
filters.prioritaet = String(req.query.prioritaet).split(',');
|
|
}
|
|
if (req.query.status) {
|
|
filters.status = String(req.query.status).split(',');
|
|
}
|
|
if (req.query.erstellt_von) {
|
|
filters.erstellt_von = req.query.erstellt_von as string;
|
|
}
|
|
if (req.query.zugewiesen_an) {
|
|
filters.zugewiesen_an =
|
|
req.query.zugewiesen_an === 'me' ? userId : (req.query.zugewiesen_an as string);
|
|
}
|
|
|
|
const issues = await issueService.getIssues({ userId, canViewAll, filters });
|
|
res.status(200).json({ success: true, data: issues });
|
|
} catch (error) {
|
|
logger.error('IssueController.getIssues error', { error });
|
|
res.status(500).json({ success: false, message: 'Issues konnten nicht geladen werden' });
|
|
}
|
|
}
|
|
|
|
async getIssue(req: Request, res: Response): Promise<void> {
|
|
const id = parseInt(param(req, 'id'), 10);
|
|
if (isNaN(id)) {
|
|
res.status(400).json({ success: false, message: 'Ungültige ID' });
|
|
return;
|
|
}
|
|
try {
|
|
const issue = await issueService.getIssueById(id);
|
|
if (!issue) {
|
|
res.status(404).json({ success: false, message: 'Issue nicht gefunden' });
|
|
return;
|
|
}
|
|
const userId = req.user!.id;
|
|
const groups: string[] = (req.user as any).groups || [];
|
|
const canViewAll = permissionService.hasPermission(groups, 'issues:view_all');
|
|
if (!canViewAll && issue.erstellt_von !== userId && issue.zugewiesen_an !== userId) {
|
|
res.status(403).json({ success: false, message: 'Kein Zugriff' });
|
|
return;
|
|
}
|
|
res.status(200).json({ success: true, data: issue });
|
|
} catch (error) {
|
|
logger.error('IssueController.getIssue error', { error });
|
|
res.status(500).json({ success: false, message: 'Issue konnte nicht geladen werden' });
|
|
}
|
|
}
|
|
|
|
async createIssue(req: Request, res: Response): Promise<void> {
|
|
const { titel } = req.body;
|
|
if (!titel || typeof titel !== 'string' || titel.trim().length === 0) {
|
|
res.status(400).json({ success: false, message: 'Titel ist erforderlich' });
|
|
return;
|
|
}
|
|
try {
|
|
const issue = await issueService.createIssue(req.body, req.user!.id);
|
|
res.status(201).json({ success: true, data: issue });
|
|
} catch (error) {
|
|
logger.error('IssueController.createIssue error', { error });
|
|
res.status(500).json({ success: false, message: 'Issue konnte nicht erstellt werden' });
|
|
}
|
|
}
|
|
|
|
async updateIssue(req: Request, res: Response): Promise<void> {
|
|
const id = parseInt(param(req, 'id'), 10);
|
|
if (isNaN(id)) {
|
|
res.status(400).json({ success: false, message: 'Ungültige ID' });
|
|
return;
|
|
}
|
|
try {
|
|
const userId = req.user!.id;
|
|
const groups: string[] = (req.user as any).groups || [];
|
|
const canEdit = permissionService.hasPermission(groups, 'issues:edit');
|
|
const canChangeStatus = permissionService.hasPermission(groups, 'issues:change_status');
|
|
|
|
const existing = await issueService.getIssueById(id);
|
|
if (!existing) {
|
|
res.status(404).json({ success: false, message: 'Issue nicht gefunden' });
|
|
return;
|
|
}
|
|
|
|
const isOwner = existing.erstellt_von === userId;
|
|
const isAssignee = existing.zugewiesen_an === userId;
|
|
|
|
// Determine what update data is allowed
|
|
let updateData: Record<string, any>;
|
|
|
|
if (canEdit) {
|
|
// Full edit access
|
|
updateData = { ...req.body };
|
|
// Explicit null for unassign is handled by 'zugewiesen_an' in data check in service
|
|
} else if (canChangeStatus || isAssignee) {
|
|
// Can change status and priority (+ kommentar is handled separately)
|
|
updateData = {};
|
|
if (req.body.status !== undefined) updateData.status = req.body.status;
|
|
if (req.body.prioritaet !== undefined) updateData.prioritaet = req.body.prioritaet;
|
|
} else if (isOwner) {
|
|
// Owner without change_status: can only close own issue or reopen from terminal status
|
|
updateData = {};
|
|
if (req.body.status !== undefined) {
|
|
const newStatus = req.body.status;
|
|
const allStatuses = await issueService.getIssueStatuses();
|
|
const targetDef = allStatuses.find((s: any) => s.schluessel === newStatus);
|
|
const currentDef = allStatuses.find((s: any) => s.schluessel === existing.status);
|
|
|
|
if (targetDef?.ist_abschluss) {
|
|
// Owner can close with any terminal status
|
|
updateData.status = newStatus;
|
|
} else if (targetDef?.ist_initial && currentDef?.ist_abschluss) {
|
|
// Owner can reopen from terminal → initial (requires kommentar)
|
|
if (!req.body.kommentar || typeof req.body.kommentar !== 'string' || req.body.kommentar.trim().length === 0) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: 'Beim Wiedereröffnen ist ein Kommentar erforderlich',
|
|
});
|
|
return;
|
|
}
|
|
updateData.status = newStatus;
|
|
} else {
|
|
res.status(403).json({ success: false, message: 'Keine Berechtigung für diese Statusänderung' });
|
|
return;
|
|
}
|
|
} else {
|
|
// Owner trying to change non-status fields without edit permission
|
|
res.status(403).json({ success: false, message: 'Keine Berechtigung' });
|
|
return;
|
|
}
|
|
} else {
|
|
res.status(403).json({ success: false, message: 'Keine Berechtigung' });
|
|
return;
|
|
}
|
|
|
|
// Validate: if setting status to 'abgelehnt', check if type allows it
|
|
if (updateData.status === 'abgelehnt' && existing.typ_id) {
|
|
if (!existing.typ_erlaubt_abgelehnt) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: 'Dieser Issue-Typ erlaubt den Status "Abgelehnt" nicht',
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
const issue = await issueService.updateIssue(id, updateData);
|
|
if (!issue) {
|
|
res.status(404).json({ success: false, message: 'Issue nicht gefunden' });
|
|
return;
|
|
}
|
|
|
|
// Log history entries for detected changes
|
|
const fieldLabels: Record<string, string> = {
|
|
status: 'Status geändert',
|
|
prioritaet: 'Priorität geändert',
|
|
zugewiesen_an: 'Zuweisung geändert',
|
|
titel: 'Titel geändert',
|
|
beschreibung: 'Beschreibung geändert',
|
|
typ_id: 'Typ geändert',
|
|
faellig_am: 'Fälligkeitsdatum geändert',
|
|
};
|
|
for (const [field, label] of Object.entries(fieldLabels)) {
|
|
if (field in updateData && updateData[field] !== existing[field]) {
|
|
const details: Record<string, unknown> = { von: existing[field], zu: updateData[field] };
|
|
if (field === 'zugewiesen_an') {
|
|
details.von_name = existing.zugewiesen_an_name || null;
|
|
details.zu_name = issue.zugewiesen_an_name || null;
|
|
}
|
|
if (field === 'status') {
|
|
details.von_label = existing.status;
|
|
details.zu_label = issue.status;
|
|
}
|
|
issueService.addHistoryEntry(id, label, details, userId);
|
|
}
|
|
}
|
|
|
|
// Handle reopen comment (owner reopen flow: terminal → initial)
|
|
if (isOwner && !canChangeStatus && updateData.status && req.body.kommentar) {
|
|
const allStatusesForComment = await issueService.getIssueStatuses();
|
|
const targetForComment = allStatusesForComment.find((s: any) => s.schluessel === updateData.status);
|
|
const currentForComment = allStatusesForComment.find((s: any) => s.schluessel === existing.status);
|
|
if (targetForComment?.ist_initial && currentForComment?.ist_abschluss) {
|
|
await issueService.addComment(id, userId, `[Wiedereröffnet] ${req.body.kommentar.trim()}`);
|
|
} else if (req.body.kommentar && updateData.status) {
|
|
await issueService.addComment(id, userId, req.body.kommentar.trim());
|
|
}
|
|
} else if (req.body.kommentar && updateData.status) {
|
|
// If kommentar was provided alongside a status change (non-owner flow)
|
|
await issueService.addComment(id, userId, req.body.kommentar.trim());
|
|
}
|
|
|
|
// Re-fetch to include any new comment info
|
|
const updated = await issueService.getIssueById(id);
|
|
res.status(200).json({ success: true, data: updated });
|
|
} catch (error) {
|
|
logger.error('IssueController.updateIssue error', { error });
|
|
res.status(500).json({ success: false, message: 'Issue konnte nicht aktualisiert werden' });
|
|
}
|
|
}
|
|
|
|
async deleteIssue(req: Request, res: Response): Promise<void> {
|
|
const id = parseInt(param(req, 'id'), 10);
|
|
if (isNaN(id)) {
|
|
res.status(400).json({ success: false, message: 'Ungültige ID' });
|
|
return;
|
|
}
|
|
try {
|
|
const issue = await issueService.getIssueById(id);
|
|
if (!issue) {
|
|
res.status(404).json({ success: false, message: 'Issue nicht gefunden' });
|
|
return;
|
|
}
|
|
const userId = req.user!.id;
|
|
const groups: string[] = (req.user as any).groups || [];
|
|
const canDelete = permissionService.hasPermission(groups, 'issues:delete');
|
|
if (!canDelete && issue.erstellt_von !== userId) {
|
|
res.status(403).json({ success: false, message: 'Keine Berechtigung' });
|
|
return;
|
|
}
|
|
await issueService.deleteIssue(id);
|
|
res.status(200).json({ success: true, message: 'Issue gelöscht' });
|
|
} catch (error) {
|
|
logger.error('IssueController.deleteIssue error', { error });
|
|
res.status(500).json({ success: false, message: 'Issue konnte nicht gelöscht werden' });
|
|
}
|
|
}
|
|
|
|
async getComments(req: Request, res: Response): Promise<void> {
|
|
const issueId = parseInt(param(req, 'id'), 10);
|
|
if (isNaN(issueId)) {
|
|
res.status(400).json({ success: false, message: 'Ungültige ID' });
|
|
return;
|
|
}
|
|
try {
|
|
const issue = await issueService.getIssueById(issueId);
|
|
if (!issue) {
|
|
res.status(404).json({ success: false, message: 'Issue nicht gefunden' });
|
|
return;
|
|
}
|
|
const userId = req.user!.id;
|
|
const groups: string[] = (req.user as any).groups || [];
|
|
const canViewAll = permissionService.hasPermission(groups, 'issues:view_all');
|
|
if (!canViewAll && issue.erstellt_von !== userId && issue.zugewiesen_an !== userId) {
|
|
res.status(403).json({ success: false, message: 'Kein Zugriff' });
|
|
return;
|
|
}
|
|
const comments = await issueService.getComments(issueId);
|
|
res.status(200).json({ success: true, data: comments });
|
|
} catch (error) {
|
|
logger.error('IssueController.getComments error', { error });
|
|
res.status(500).json({ success: false, message: 'Kommentare konnten nicht geladen werden' });
|
|
}
|
|
}
|
|
|
|
async addComment(req: Request, res: Response): Promise<void> {
|
|
const issueId = parseInt(param(req, 'id'), 10);
|
|
if (isNaN(issueId)) {
|
|
res.status(400).json({ success: false, message: 'Ungültige ID' });
|
|
return;
|
|
}
|
|
const { inhalt } = req.body;
|
|
if (!inhalt || typeof inhalt !== 'string' || inhalt.trim().length === 0) {
|
|
res.status(400).json({ success: false, message: 'Kommentar darf nicht leer sein' });
|
|
return;
|
|
}
|
|
try {
|
|
const issue = await issueService.getIssueById(issueId);
|
|
if (!issue) {
|
|
res.status(404).json({ success: false, message: 'Issue nicht gefunden' });
|
|
return;
|
|
}
|
|
const userId = req.user!.id;
|
|
const groups: string[] = (req.user as any).groups || [];
|
|
const isOwner = issue.erstellt_von === userId;
|
|
const isAssignee = issue.zugewiesen_an === userId;
|
|
const canChangeStatus = permissionService.hasPermission(groups, 'issues:change_status');
|
|
const canEdit = permissionService.hasPermission(groups, 'issues:edit');
|
|
|
|
// Authorization: owner, assignee, change_status, or edit can comment
|
|
if (!isOwner && !isAssignee && !canChangeStatus && !canEdit) {
|
|
res.status(403).json({ success: false, message: 'Keine Berechtigung zum Kommentieren' });
|
|
return;
|
|
}
|
|
|
|
const comment = await issueService.addComment(issueId, userId, inhalt.trim());
|
|
res.status(201).json({ success: true, data: comment });
|
|
} catch (error) {
|
|
logger.error('IssueController.addComment error', { error });
|
|
res.status(500).json({ success: false, message: 'Kommentar konnte nicht erstellt werden' });
|
|
}
|
|
}
|
|
|
|
// --- Type management ---
|
|
|
|
async getHistory(req: Request, res: Response): Promise<void> {
|
|
const issueId = parseInt(param(req, 'id'), 10);
|
|
if (isNaN(issueId)) {
|
|
res.status(400).json({ success: false, message: 'Ungültige ID' });
|
|
return;
|
|
}
|
|
try {
|
|
const history = await issueService.getHistory(issueId);
|
|
res.status(200).json({ success: true, data: history });
|
|
} catch (error) {
|
|
logger.error('IssueController.getHistory error', { error });
|
|
res.status(500).json({ success: false, message: 'Historie konnte nicht geladen werden' });
|
|
}
|
|
}
|
|
|
|
async getTypes(_req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const types = await issueService.getTypes();
|
|
res.status(200).json({ success: true, data: types });
|
|
} catch (error) {
|
|
logger.error('IssueController.getTypes error', { error });
|
|
res.status(500).json({ success: false, message: 'Issue-Typen konnten nicht geladen werden' });
|
|
}
|
|
}
|
|
|
|
async createType(req: Request, res: Response): Promise<void> {
|
|
const { name } = req.body;
|
|
if (!name || typeof name !== 'string' || name.trim().length === 0) {
|
|
res.status(400).json({ success: false, message: 'Name ist erforderlich' });
|
|
return;
|
|
}
|
|
try {
|
|
const type = await issueService.createType(req.body);
|
|
res.status(201).json({ success: true, data: type });
|
|
} catch (error) {
|
|
logger.error('IssueController.createType error', { error });
|
|
res.status(500).json({ success: false, message: 'Issue-Typ konnte nicht erstellt werden' });
|
|
}
|
|
}
|
|
|
|
async updateType(req: Request, res: Response): Promise<void> {
|
|
const id = parseInt(param(req, 'id'), 10);
|
|
if (isNaN(id)) {
|
|
res.status(400).json({ success: false, message: 'Ungültige ID' });
|
|
return;
|
|
}
|
|
try {
|
|
const type = await issueService.updateType(id, req.body);
|
|
if (!type) {
|
|
res.status(404).json({ success: false, message: 'Issue-Typ nicht gefunden' });
|
|
return;
|
|
}
|
|
res.status(200).json({ success: true, data: type });
|
|
} catch (error) {
|
|
logger.error('IssueController.updateType error', { error });
|
|
res.status(500).json({ success: false, message: 'Issue-Typ konnte nicht aktualisiert werden' });
|
|
}
|
|
}
|
|
|
|
async deleteType(req: Request, res: Response): Promise<void> {
|
|
const id = parseInt(param(req, 'id'), 10);
|
|
if (isNaN(id)) {
|
|
res.status(400).json({ success: false, message: 'Ungültige ID' });
|
|
return;
|
|
}
|
|
try {
|
|
const type = await issueService.deactivateType(id);
|
|
if (!type) {
|
|
res.status(404).json({ success: false, message: 'Issue-Typ nicht gefunden' });
|
|
return;
|
|
}
|
|
res.status(200).json({ success: true, data: type });
|
|
} catch (error) {
|
|
logger.error('IssueController.deleteType error', { error });
|
|
res.status(500).json({ success: false, message: 'Issue-Typ konnte nicht deaktiviert werden' });
|
|
}
|
|
}
|
|
|
|
async getMembers(_req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const members = await issueService.getAssignableMembers();
|
|
res.status(200).json({ success: true, data: members });
|
|
} catch (error) {
|
|
logger.error('IssueController.getMembers error', { error });
|
|
res.status(500).json({ success: false, message: 'Mitglieder konnten nicht geladen werden' });
|
|
}
|
|
}
|
|
|
|
async getWidgetSummary(_req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const counts = await issueService.getIssueCounts();
|
|
res.status(200).json({ success: true, data: counts });
|
|
} catch (error) {
|
|
logger.error('IssueController.getWidgetSummary error', { error });
|
|
res.status(500).json({ success: false, message: 'Issue-Counts konnten nicht geladen werden' });
|
|
}
|
|
}
|
|
|
|
async getIssueStatuses(_req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const items = await issueService.getIssueStatuses();
|
|
res.status(200).json({ success: true, data: items });
|
|
} catch (error) {
|
|
logger.error('IssueController.getIssueStatuses error', { error });
|
|
res.status(500).json({ success: false, message: 'Issue-Status konnten nicht geladen werden' });
|
|
}
|
|
}
|
|
|
|
async createIssueStatus(req: Request, res: Response): Promise<void> {
|
|
const { schluessel, bezeichnung } = req.body;
|
|
if (!schluessel?.trim() || !bezeichnung?.trim()) {
|
|
res.status(400).json({ success: false, message: 'Schlüssel und Bezeichnung sind erforderlich' });
|
|
return;
|
|
}
|
|
try {
|
|
const item = await issueService.createIssueStatus(req.body);
|
|
res.status(201).json({ success: true, data: item });
|
|
} catch (error) {
|
|
logger.error('IssueController.createIssueStatus error', { error });
|
|
res.status(500).json({ success: false, message: 'Issue-Status konnte nicht erstellt werden' });
|
|
}
|
|
}
|
|
|
|
async updateIssueStatus(req: Request, res: Response): Promise<void> {
|
|
const id = parseInt(param(req, 'id'), 10);
|
|
if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; }
|
|
try {
|
|
const item = await issueService.updateIssueStatus(id, req.body);
|
|
if (!item) { res.status(404).json({ success: false, message: 'Nicht gefunden' }); return; }
|
|
res.status(200).json({ success: true, data: item });
|
|
} catch (error) {
|
|
logger.error('IssueController.updateIssueStatus error', { error });
|
|
res.status(500).json({ success: false, message: 'Issue-Status konnte nicht aktualisiert werden' });
|
|
}
|
|
}
|
|
|
|
async deleteIssueStatus(req: Request, res: Response): Promise<void> {
|
|
const id = parseInt(param(req, 'id'), 10);
|
|
if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; }
|
|
try {
|
|
const item = await issueService.deleteIssueStatus(id);
|
|
if (!item) { res.status(404).json({ success: false, message: 'Nicht gefunden' }); return; }
|
|
res.status(200).json({ success: true, data: item });
|
|
} catch (error) {
|
|
logger.error('IssueController.deleteIssueStatus error', { error });
|
|
res.status(500).json({ success: false, message: 'Issue-Status konnte nicht deaktiviert werden' });
|
|
}
|
|
}
|
|
|
|
async getIssuePriorities(_req: Request, res: Response): Promise<void> {
|
|
try {
|
|
const items = await issueService.getIssuePriorities();
|
|
res.status(200).json({ success: true, data: items });
|
|
} catch (error) {
|
|
logger.error('IssueController.getIssuePriorities error', { error });
|
|
res.status(500).json({ success: false, message: 'Prioritäten konnten nicht geladen werden' });
|
|
}
|
|
}
|
|
|
|
async createIssuePriority(req: Request, res: Response): Promise<void> {
|
|
const { schluessel, bezeichnung } = req.body;
|
|
if (!schluessel?.trim() || !bezeichnung?.trim()) {
|
|
res.status(400).json({ success: false, message: 'Schlüssel und Bezeichnung sind erforderlich' });
|
|
return;
|
|
}
|
|
try {
|
|
const item = await issueService.createIssuePriority(req.body);
|
|
res.status(201).json({ success: true, data: item });
|
|
} catch (error) {
|
|
logger.error('IssueController.createIssuePriority error', { error });
|
|
res.status(500).json({ success: false, message: 'Priorität konnte nicht erstellt werden' });
|
|
}
|
|
}
|
|
|
|
async updateIssuePriority(req: Request, res: Response): Promise<void> {
|
|
const id = parseInt(param(req, 'id'), 10);
|
|
if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; }
|
|
try {
|
|
const item = await issueService.updateIssuePriority(id, req.body);
|
|
if (!item) { res.status(404).json({ success: false, message: 'Nicht gefunden' }); return; }
|
|
res.status(200).json({ success: true, data: item });
|
|
} catch (error) {
|
|
logger.error('IssueController.updateIssuePriority error', { error });
|
|
res.status(500).json({ success: false, message: 'Priorität konnte nicht aktualisiert werden' });
|
|
}
|
|
}
|
|
|
|
async deleteIssuePriority(req: Request, res: Response): Promise<void> {
|
|
const id = parseInt(param(req, 'id'), 10);
|
|
if (isNaN(id)) { res.status(400).json({ success: false, message: 'Ungültige ID' }); return; }
|
|
try {
|
|
const item = await issueService.deleteIssuePriority(id);
|
|
if (!item) { res.status(404).json({ success: false, message: 'Nicht gefunden' }); return; }
|
|
res.status(200).json({ success: true, data: item });
|
|
} catch (error) {
|
|
logger.error('IssueController.deleteIssuePriority error', { error });
|
|
res.status(500).json({ success: false, message: 'Priorität konnte nicht deaktiviert werden' });
|
|
}
|
|
}
|
|
|
|
// --- File management ---
|
|
|
|
async uploadFile(req: Request, res: Response): Promise<void> {
|
|
const issueId = parseInt(param(req, 'id'), 10);
|
|
if (isNaN(issueId)) {
|
|
res.status(400).json({ success: false, message: 'Ungültige Issue-ID' });
|
|
return;
|
|
}
|
|
const file = req.file as Express.Multer.File | undefined;
|
|
if (!file) {
|
|
res.status(400).json({ success: false, message: 'Keine Datei hochgeladen' });
|
|
return;
|
|
}
|
|
try {
|
|
const fileRecord = await issueService.addFile(issueId, {
|
|
dateiname: file.originalname,
|
|
dateipfad: file.path,
|
|
dateityp: file.mimetype,
|
|
dateigroesse: file.size,
|
|
}, req.user!.id);
|
|
res.status(201).json({ success: true, data: fileRecord });
|
|
} catch (error) {
|
|
logger.error('IssueController.uploadFile error', { error });
|
|
res.status(500).json({ success: false, message: 'Datei konnte nicht hochgeladen werden' });
|
|
}
|
|
}
|
|
|
|
async getFiles(req: Request, res: Response): Promise<void> {
|
|
const issueId = parseInt(param(req, 'id'), 10);
|
|
if (isNaN(issueId)) {
|
|
res.status(400).json({ success: false, message: 'Ungültige ID' });
|
|
return;
|
|
}
|
|
try {
|
|
const files = await issueService.getFiles(issueId);
|
|
res.status(200).json({ success: true, data: files });
|
|
} catch (error) {
|
|
logger.error('IssueController.getFiles error', { error });
|
|
res.status(500).json({ success: false, message: 'Dateien konnten nicht geladen werden' });
|
|
}
|
|
}
|
|
|
|
async deleteFile(req: Request, res: Response): Promise<void> {
|
|
const fileId = param(req, 'fileId');
|
|
if (!fileId) {
|
|
res.status(400).json({ success: false, message: 'Ungültige Datei-ID' });
|
|
return;
|
|
}
|
|
try {
|
|
const result = await issueService.deleteFile(fileId, req.user!.id);
|
|
if (!result) {
|
|
res.status(404).json({ success: false, message: 'Datei nicht gefunden' });
|
|
return;
|
|
}
|
|
res.status(200).json({ success: true, message: 'Datei gelöscht' });
|
|
} catch (error) {
|
|
logger.error('IssueController.deleteFile error', { error });
|
|
res.status(500).json({ success: false, message: 'Datei konnte nicht gelöscht werden' });
|
|
}
|
|
}
|
|
}
|
|
|
|
export default new IssueController();
|