new features
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { Request, Response } from 'express';
|
||||
import shopService from '../services/shop.service';
|
||||
import ausruestungsanfrageService from '../services/ausruestungsanfrage.service';
|
||||
import notificationService from '../services/notification.service';
|
||||
import logger from '../utils/logger';
|
||||
|
||||
class ShopController {
|
||||
class AusruestungsanfrageController {
|
||||
// -------------------------------------------------------------------------
|
||||
// Catalog Items
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -12,10 +12,10 @@ class ShopController {
|
||||
try {
|
||||
const kategorie = req.query.kategorie as string | undefined;
|
||||
const aktiv = req.query.aktiv !== undefined ? req.query.aktiv === 'true' : undefined;
|
||||
const items = await shopService.getItems({ kategorie, aktiv });
|
||||
const items = await ausruestungsanfrageService.getItems({ kategorie, aktiv });
|
||||
res.status(200).json({ success: true, data: items });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.getItems error', { error });
|
||||
logger.error('AusruestungsanfrageController.getItems error', { error });
|
||||
res.status(500).json({ success: false, message: 'Artikel konnten nicht geladen werden' });
|
||||
}
|
||||
}
|
||||
@@ -23,14 +23,14 @@ class ShopController {
|
||||
async getItemById(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = Number(req.params.id);
|
||||
const item = await shopService.getItemById(id);
|
||||
const item = await ausruestungsanfrageService.getItemById(id);
|
||||
if (!item) {
|
||||
res.status(404).json({ success: false, message: 'Artikel nicht gefunden' });
|
||||
return;
|
||||
}
|
||||
res.status(200).json({ success: true, data: item });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.getItemById error', { error });
|
||||
logger.error('AusruestungsanfrageController.getItemById error', { error });
|
||||
res.status(500).json({ success: false, message: 'Artikel konnte nicht geladen werden' });
|
||||
}
|
||||
}
|
||||
@@ -42,10 +42,10 @@ class ShopController {
|
||||
res.status(400).json({ success: false, message: 'Bezeichnung ist erforderlich' });
|
||||
return;
|
||||
}
|
||||
const item = await shopService.createItem(req.body, req.user!.id);
|
||||
const item = await ausruestungsanfrageService.createItem(req.body, req.user!.id);
|
||||
res.status(201).json({ success: true, data: item });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.createItem error', { error });
|
||||
logger.error('AusruestungsanfrageController.createItem error', { error });
|
||||
res.status(500).json({ success: false, message: 'Artikel konnte nicht erstellt werden' });
|
||||
}
|
||||
}
|
||||
@@ -53,14 +53,14 @@ class ShopController {
|
||||
async updateItem(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = Number(req.params.id);
|
||||
const item = await shopService.updateItem(id, req.body, req.user!.id);
|
||||
const item = await ausruestungsanfrageService.updateItem(id, req.body, req.user!.id);
|
||||
if (!item) {
|
||||
res.status(404).json({ success: false, message: 'Artikel nicht gefunden' });
|
||||
return;
|
||||
}
|
||||
res.status(200).json({ success: true, data: item });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.updateItem error', { error });
|
||||
logger.error('AusruestungsanfrageController.updateItem error', { error });
|
||||
res.status(500).json({ success: false, message: 'Artikel konnte nicht aktualisiert werden' });
|
||||
}
|
||||
}
|
||||
@@ -68,20 +68,20 @@ class ShopController {
|
||||
async deleteItem(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = Number(req.params.id);
|
||||
await shopService.deleteItem(id);
|
||||
await ausruestungsanfrageService.deleteItem(id);
|
||||
res.status(200).json({ success: true, message: 'Artikel gelöscht' });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.deleteItem error', { error });
|
||||
logger.error('AusruestungsanfrageController.deleteItem error', { error });
|
||||
res.status(500).json({ success: false, message: 'Artikel konnte nicht gelöscht werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async getCategories(_req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const categories = await shopService.getCategories();
|
||||
const categories = await ausruestungsanfrageService.getCategories();
|
||||
res.status(200).json({ success: true, data: categories });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.getCategories error', { error });
|
||||
logger.error('AusruestungsanfrageController.getCategories error', { error });
|
||||
res.status(500).json({ success: false, message: 'Kategorien konnten nicht geladen werden' });
|
||||
}
|
||||
}
|
||||
@@ -94,20 +94,20 @@ class ShopController {
|
||||
try {
|
||||
const status = req.query.status as string | undefined;
|
||||
const anfrager_id = req.query.anfrager_id as string | undefined;
|
||||
const requests = await shopService.getRequests({ status, anfrager_id });
|
||||
const requests = await ausruestungsanfrageService.getRequests({ status, anfrager_id });
|
||||
res.status(200).json({ success: true, data: requests });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.getRequests error', { error });
|
||||
logger.error('AusruestungsanfrageController.getRequests error', { error });
|
||||
res.status(500).json({ success: false, message: 'Anfragen konnten nicht geladen werden' });
|
||||
}
|
||||
}
|
||||
|
||||
async getMyRequests(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const requests = await shopService.getMyRequests(req.user!.id);
|
||||
const requests = await ausruestungsanfrageService.getMyRequests(req.user!.id);
|
||||
res.status(200).json({ success: true, data: requests });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.getMyRequests error', { error });
|
||||
logger.error('AusruestungsanfrageController.getMyRequests error', { error });
|
||||
res.status(500).json({ success: false, message: 'Anfragen konnten nicht geladen werden' });
|
||||
}
|
||||
}
|
||||
@@ -115,14 +115,14 @@ class ShopController {
|
||||
async getRequestById(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = Number(req.params.id);
|
||||
const request = await shopService.getRequestById(id);
|
||||
const request = await ausruestungsanfrageService.getRequestById(id);
|
||||
if (!request) {
|
||||
res.status(404).json({ success: false, message: 'Anfrage nicht gefunden' });
|
||||
return;
|
||||
}
|
||||
res.status(200).json({ success: true, data: request });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.getRequestById error', { error });
|
||||
logger.error('AusruestungsanfrageController.getRequestById error', { error });
|
||||
res.status(500).json({ success: false, message: 'Anfrage konnte nicht geladen werden' });
|
||||
}
|
||||
}
|
||||
@@ -150,10 +150,10 @@ class ShopController {
|
||||
}
|
||||
}
|
||||
|
||||
const request = await shopService.createRequest(req.user!.id, items, notizen);
|
||||
const request = await ausruestungsanfrageService.createRequest(req.user!.id, items, notizen);
|
||||
res.status(201).json({ success: true, data: request });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.createRequest error', { error });
|
||||
logger.error('AusruestungsanfrageController.createRequest error', { error });
|
||||
res.status(500).json({ success: false, message: 'Anfrage konnte nicht erstellt werden' });
|
||||
}
|
||||
}
|
||||
@@ -178,13 +178,13 @@ class ShopController {
|
||||
}
|
||||
|
||||
// Fetch request to get anfrager_id for notification
|
||||
const existing = await shopService.getRequestById(id);
|
||||
const existing = await ausruestungsanfrageService.getRequestById(id);
|
||||
if (!existing) {
|
||||
res.status(404).json({ success: false, message: 'Anfrage nicht gefunden' });
|
||||
return;
|
||||
}
|
||||
|
||||
const updated = await shopService.updateRequestStatus(id, status, admin_notizen, req.user!.id);
|
||||
const updated = await ausruestungsanfrageService.updateRequestStatus(id, status, admin_notizen, req.user!.id);
|
||||
|
||||
// Notify requester on status changes
|
||||
if (['genehmigt', 'abgelehnt', 'bestellt', 'erledigt'].includes(status)) {
|
||||
@@ -193,19 +193,19 @@ class ShopController {
|
||||
: `#${id}`;
|
||||
await notificationService.createNotification({
|
||||
user_id: existing.anfrager_id,
|
||||
typ: 'shop_anfrage',
|
||||
typ: 'ausruestung_anfrage',
|
||||
titel: status === 'genehmigt' ? 'Anfrage genehmigt' : status === 'abgelehnt' ? 'Anfrage abgelehnt' : `Anfrage ${status}`,
|
||||
nachricht: `Deine Shop-Anfrage ${orderLabel} wurde ${status === 'genehmigt' ? 'genehmigt' : status === 'abgelehnt' ? 'abgelehnt' : status}.`,
|
||||
nachricht: `Deine Ausrüstungsanfrage ${orderLabel} wurde ${status === 'genehmigt' ? 'genehmigt' : status === 'abgelehnt' ? 'abgelehnt' : status}.`,
|
||||
schwere: status === 'abgelehnt' ? 'warnung' : 'info',
|
||||
link: '/shop',
|
||||
link: '/ausruestungsanfrage',
|
||||
quell_id: String(id),
|
||||
quell_typ: 'shop_anfrage',
|
||||
quell_typ: 'ausruestung_anfrage',
|
||||
});
|
||||
}
|
||||
|
||||
res.status(200).json({ success: true, data: updated });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.updateRequestStatus error', { error });
|
||||
logger.error('AusruestungsanfrageController.updateRequestStatus error', { error });
|
||||
res.status(500).json({ success: false, message: 'Status konnte nicht aktualisiert werden' });
|
||||
}
|
||||
}
|
||||
@@ -213,10 +213,10 @@ class ShopController {
|
||||
async deleteRequest(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const id = Number(req.params.id);
|
||||
await shopService.deleteRequest(id);
|
||||
await ausruestungsanfrageService.deleteRequest(id);
|
||||
res.status(200).json({ success: true, message: 'Anfrage gelöscht' });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.deleteRequest error', { error });
|
||||
logger.error('AusruestungsanfrageController.deleteRequest error', { error });
|
||||
res.status(500).json({ success: false, message: 'Anfrage konnte nicht gelöscht werden' });
|
||||
}
|
||||
}
|
||||
@@ -227,10 +227,10 @@ class ShopController {
|
||||
|
||||
async getOverview(_req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const overview = await shopService.getOverview();
|
||||
const overview = await ausruestungsanfrageService.getOverview();
|
||||
res.status(200).json({ success: true, data: overview });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.getOverview error', { error });
|
||||
logger.error('AusruestungsanfrageController.getOverview error', { error });
|
||||
res.status(500).json({ success: false, message: 'Übersicht konnte nicht geladen werden' });
|
||||
}
|
||||
}
|
||||
@@ -249,10 +249,10 @@ class ShopController {
|
||||
return;
|
||||
}
|
||||
|
||||
await shopService.linkToOrder(anfrageId, bestellung_id);
|
||||
await ausruestungsanfrageService.linkToOrder(anfrageId, bestellung_id);
|
||||
res.status(200).json({ success: true, message: 'Verknüpfung erstellt' });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.linkToOrder error', { error });
|
||||
logger.error('AusruestungsanfrageController.linkToOrder error', { error });
|
||||
res.status(500).json({ success: false, message: 'Verknüpfung konnte nicht erstellt werden' });
|
||||
}
|
||||
}
|
||||
@@ -261,13 +261,13 @@ class ShopController {
|
||||
try {
|
||||
const anfrageId = Number(req.params.id);
|
||||
const bestellungId = Number(req.params.bestellungId);
|
||||
await shopService.unlinkFromOrder(anfrageId, bestellungId);
|
||||
await ausruestungsanfrageService.unlinkFromOrder(anfrageId, bestellungId);
|
||||
res.status(200).json({ success: true, message: 'Verknüpfung entfernt' });
|
||||
} catch (error) {
|
||||
logger.error('ShopController.unlinkFromOrder error', { error });
|
||||
logger.error('AusruestungsanfrageController.unlinkFromOrder error', { error });
|
||||
res.status(500).json({ success: false, message: 'Verknüpfung konnte nicht entfernt werden' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new ShopController();
|
||||
export default new AusruestungsanfrageController();
|
||||
@@ -67,13 +67,25 @@ class IssueController {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const userId = req.user!.id;
|
||||
const groups: string[] = (req.user as any).groups || [];
|
||||
const canManage = permissionService.hasPermission(groups, 'issues:manage');
|
||||
if (!canManage) {
|
||||
|
||||
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;
|
||||
if (!canManage && !isOwner) {
|
||||
res.status(403).json({ success: false, message: 'Keine Berechtigung' });
|
||||
return;
|
||||
}
|
||||
const issue = await issueService.updateIssue(id, req.body);
|
||||
|
||||
// Owners without manage permission can only change status
|
||||
const updateData = canManage ? req.body : { status: req.body.status };
|
||||
const issue = await issueService.updateIssue(id, updateData);
|
||||
if (!issue) {
|
||||
res.status(404).json({ success: false, message: 'Issue nicht gefunden' });
|
||||
return;
|
||||
|
||||
@@ -242,6 +242,64 @@ class PermissionController {
|
||||
res.status(500).json({ success: false, message: 'Fehler beim Laden der Benutzer' });
|
||||
}
|
||||
}
|
||||
/**
|
||||
* GET /api/permissions/debug/:userId
|
||||
* Returns debug info for a specific user: their groups, resolved permissions,
|
||||
* and maintenance flags. Admin only.
|
||||
*/
|
||||
async debugUser(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const userId = req.params.userId as string;
|
||||
|
||||
// Fetch user's Authentik groups from DB
|
||||
const { pool } = await import('../config/database');
|
||||
const userResult = await pool.query(
|
||||
'SELECT authentik_groups, email, name FROM users WHERE id = $1',
|
||||
[userId]
|
||||
);
|
||||
|
||||
if (userResult.rows.length === 0) {
|
||||
res.status(404).json({ success: false, message: 'Benutzer nicht gefunden' });
|
||||
return;
|
||||
}
|
||||
|
||||
const user = userResult.rows[0];
|
||||
const groups: string[] = user.authentik_groups ?? [];
|
||||
const isAdmin = groups.includes('dashboard_admin');
|
||||
|
||||
// Resolve permissions for those groups
|
||||
let permissions: string[];
|
||||
if (isAdmin) {
|
||||
const matrix = await permissionService.getMatrix();
|
||||
permissions = matrix.permissions.map(p => p.id);
|
||||
} else {
|
||||
permissions = permissionService.getEffectivePermissions(groups);
|
||||
}
|
||||
|
||||
// Maintenance flags
|
||||
const maintenance = permissionService.getMaintenanceFlags();
|
||||
const maintenanceActive = Object.entries(maintenance)
|
||||
.filter(([, active]) => active)
|
||||
.map(([featureGroup]) => featureGroup);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
userId,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
authentikGroups: groups,
|
||||
isAdmin,
|
||||
permissions,
|
||||
maintenance,
|
||||
maintenanceActiveFeatureGroups: maintenanceActive,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Failed to debug user permissions', { error, userId: req.params.userId });
|
||||
res.status(500).json({ success: false, message: 'Fehler beim Laden der Debug-Informationen' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new PermissionController();
|
||||
|
||||
Reference in New Issue
Block a user