feat(admin): centralize tool & module settings in Werkzeuge tab with per-tool permissions, DB-backed config, connection tests, and cog-button navigation

This commit is contained in:
Matthias Hochmeister
2026-04-17 08:37:29 +02:00
parent 6ead698294
commit 6614fbaa68
28 changed files with 2472 additions and 1426 deletions

View File

@@ -1,6 +1,6 @@
import axios from 'axios';
import httpClient from '../config/httpClient';
import environment from '../config/environment';
import toolConfigService, { VikunjaConfig } from './toolConfig.service';
import logger from '../utils/logger';
export interface VikunjaTask {
@@ -58,23 +58,23 @@ function isValidServiceUrl(raw: string): boolean {
return true;
}
function buildHeaders(): Record<string, string> {
function buildHeaders(config: VikunjaConfig): Record<string, string> {
return {
'Authorization': `Bearer ${environment.vikunja.apiToken}`,
'Authorization': `Bearer ${config.apiToken}`,
'Content-Type': 'application/json',
};
}
async function getMyTasks(): Promise<VikunjaTask[]> {
const { vikunja } = environment;
if (!vikunja.url || !isValidServiceUrl(vikunja.url)) {
const config = await toolConfigService.getVikunjaConfig();
if (!config.url || !isValidServiceUrl(config.url)) {
throw new Error('VIKUNJA_URL is not configured or is not a valid service URL');
}
try {
const response = await httpClient.get<VikunjaTask[]>(
`${vikunja.url}/api/v1/tasks/all`,
{ headers: buildHeaders() },
`${config.url}/api/v1/tasks/all`,
{ headers: buildHeaders(config) },
);
return (response.data ?? []).filter((t) => !t.done);
} catch (error) {
@@ -99,15 +99,15 @@ async function getOverdueTasks(): Promise<VikunjaTask[]> {
}
async function getProjects(): Promise<VikunjaProject[]> {
const { vikunja } = environment;
if (!vikunja.url || !isValidServiceUrl(vikunja.url)) {
const config = await toolConfigService.getVikunjaConfig();
if (!config.url || !isValidServiceUrl(config.url)) {
throw new Error('VIKUNJA_URL is not configured or is not a valid service URL');
}
try {
const response = await httpClient.get<VikunjaProject[]>(
`${vikunja.url}/api/v1/projects`,
{ headers: buildHeaders() },
`${config.url}/api/v1/projects`,
{ headers: buildHeaders(config) },
);
return response.data ?? [];
} catch (error) {
@@ -123,8 +123,8 @@ async function getProjects(): Promise<VikunjaProject[]> {
}
async function createTask(projectId: number, title: string, dueDate?: string): Promise<VikunjaTask> {
const { vikunja } = environment;
if (!vikunja.url || !isValidServiceUrl(vikunja.url)) {
const config = await toolConfigService.getVikunjaConfig();
if (!config.url || !isValidServiceUrl(config.url)) {
throw new Error('VIKUNJA_URL is not configured or is not a valid service URL');
}
@@ -134,9 +134,9 @@ async function createTask(projectId: number, title: string, dueDate?: string): P
body.due_date = dueDate;
}
const response = await httpClient.put<VikunjaTask>(
`${vikunja.url}/api/v1/projects/${projectId}/tasks`,
`${config.url}/api/v1/projects/${projectId}/tasks`,
body,
{ headers: buildHeaders() },
{ headers: buildHeaders(config) },
);
return response.data;
} catch (error) {