resolve issues with new features

This commit is contained in:
Matthias Hochmeister
2026-03-12 11:37:25 +01:00
parent d5be68ca63
commit 71a04aee89
38 changed files with 699 additions and 108 deletions

View File

@@ -7,6 +7,7 @@ import logger from '../utils/logger';
import auditService, { AuditAction, AuditResourceType } from '../services/audit.service';
import { extractIp, extractUserAgent } from '../middleware/audit.middleware';
import { getUserRole } from '../middleware/rbac.middleware';
import pool from '../config/database';
/**
* Extract given_name and family_name from Authentik userinfo.
@@ -372,10 +373,17 @@ class AuthController {
// Generate new access token
const role = await getUserRole(user.id);
// Fetch groups from DB so refreshed tokens retain group info
const groupsResult = await pool.query(
'SELECT authentik_groups FROM users WHERE id = $1',
[user.id]
);
const groups: string[] = groupsResult.rows[0]?.authentik_groups ?? [];
const accessToken = tokenService.generateToken({
userId: user.id,
email: user.email,
authentikSub: user.authentik_sub,
groups,
role,
});

View File

@@ -1,13 +1,23 @@
import { Request, Response } from 'express';
import environment from '../config/environment';
import settingsService from '../services/settings.service';
class ConfigController {
async getExternalLinks(_req: Request, res: Response): Promise<void> {
const links: Record<string, string> = {};
if (environment.nextcloudUrl) links.nextcloud = environment.nextcloudUrl;
if (environment.bookstack.url) links.bookstack = environment.bookstack.url;
if (environment.vikunja.url) links.vikunja = environment.vikunja.url;
res.status(200).json({ success: true, data: links });
const envLinks: Record<string, string> = {};
if (environment.nextcloudUrl) envLinks.nextcloud = environment.nextcloudUrl;
if (environment.bookstack.url) envLinks.bookstack = environment.bookstack.url;
if (environment.vikunja.url) envLinks.vikunja = environment.vikunja.url;
const customLinks = await settingsService.getExternalLinks();
res.status(200).json({
success: true,
data: {
...envLinks,
customLinks,
},
});
}
}

View File

@@ -115,7 +115,12 @@ class NextcloudController {
}
const messages = await nextcloudService.getMessages(token, credentials.loginName, credentials.appPassword);
res.status(200).json({ success: true, data: messages });
} catch (error) {
} catch (error: any) {
if (error?.code === 'NEXTCLOUD_AUTH_INVALID') {
await userService.clearNextcloudCredentials(req.user!.id);
res.status(200).json({ success: true, data: { connected: false } });
return;
}
logger.error('getMessages error', { error });
res.status(500).json({ success: false, message: 'Nachrichten konnten nicht geladen werden' });
}
@@ -140,7 +145,12 @@ class NextcloudController {
}
await nextcloudService.sendMessage(token, message.trim(), credentials.loginName, credentials.appPassword);
res.status(200).json({ success: true, data: null });
} catch (error) {
} catch (error: any) {
if (error?.code === 'NEXTCLOUD_AUTH_INVALID') {
await userService.clearNextcloudCredentials(req.user!.id);
res.status(200).json({ success: true, data: { connected: false } });
return;
}
logger.error('sendMessage error', { error });
res.status(500).json({ success: false, message: 'Nachricht konnte nicht gesendet werden' });
}
@@ -160,7 +170,12 @@ class NextcloudController {
}
await nextcloudService.markAsRead(token, credentials.loginName, credentials.appPassword);
res.status(200).json({ success: true, data: null });
} catch (error) {
} catch (error: any) {
if (error?.code === 'NEXTCLOUD_AUTH_INVALID') {
await userService.clearNextcloudCredentials(req.user!.id);
res.status(200).json({ success: true, data: { connected: false } });
return;
}
logger.error('markRoomAsRead error', { error });
res.status(500).json({ success: false, message: 'Raum konnte nicht als gelesen markiert werden' });
}

View File

@@ -0,0 +1,62 @@
import { Request, Response } from 'express';
import { z } from 'zod';
import settingsService from '../services/settings.service';
import logger from '../utils/logger';
const updateSchema = z.object({
value: z.any(),
});
const externalLinkSchema = z.array(z.object({
name: z.string().min(1).max(200),
url: z.string().url().max(500),
}));
class SettingsController {
async getAll(_req: Request, res: Response): Promise<void> {
try {
const settings = await settingsService.getAll();
res.json({ success: true, data: settings });
} catch (error) {
logger.error('Failed to get settings', { error });
res.status(500).json({ success: false, message: 'Failed to get settings' });
}
}
async get(req: Request, res: Response): Promise<void> {
try {
const setting = await settingsService.get(req.params.key as string);
if (!setting) {
res.status(404).json({ success: false, message: 'Setting not found' });
return;
}
res.json({ success: true, data: setting });
} catch (error) {
logger.error('Failed to get setting', { error });
res.status(500).json({ success: false, message: 'Failed to get setting' });
}
}
async update(req: Request, res: Response): Promise<void> {
try {
const { value } = updateSchema.parse(req.body);
// Validate external_links specifically
if ((req.params.key as string) === 'external_links') {
externalLinkSchema.parse(value);
}
const setting = await settingsService.set(req.params.key as string, value, req.user!.id);
res.json({ success: true, data: setting });
} catch (error) {
if (error instanceof z.ZodError) {
res.status(400).json({ success: false, message: 'Invalid input', errors: error.issues });
return;
}
logger.error('Failed to update setting', { error });
res.status(500).json({ success: false, message: 'Failed to update setting' });
}
}
}
export default new SettingsController();