fix: Gitea Traefik routing and connection pool optimization
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled

- Remove middleware reference from Gitea Traefik labels (caused routing issues)
- Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s)
- Add explicit service reference in Traefik labels
- Fix intermittent 504 timeouts by improving PostgreSQL connection handling

Fixes Gitea unreachability via git.michaelschiemer.de
This commit is contained in:
2025-11-09 14:46:15 +01:00
parent 85c369e846
commit 36ef2a1e2c
1366 changed files with 104925 additions and 28719 deletions

View File

@@ -0,0 +1,113 @@
/**
* Route Guard
*
* Provides route-level access control and guards.
*/
import { Logger } from '../../core/logger.js';
/**
* RouteGuard - Route access control
*/
export class RouteGuard {
constructor(name, guardFn) {
this.name = name;
this.guardFn = guardFn;
}
/**
* Create a new RouteGuard
*/
static create(name, guardFn) {
return new RouteGuard(name, guardFn);
}
/**
* Execute guard
*/
async execute(to, from, context = {}) {
try {
const result = await this.guardFn(to, from, context);
return {
allowed: result !== false && result !== null,
redirect: typeof result === 'string' ? result : null,
reason: typeof result === 'object' && result.reason ? result.reason : null
};
} catch (error) {
Logger.error(`[RouteGuard] Guard "${this.name}" error:`, error);
return {
allowed: false,
redirect: null,
reason: error.message
};
}
}
}
/**
* Built-in guards
*/
export const BuiltInGuards = {
/**
* Require authentication
*/
auth: RouteGuard.create('auth', async (to, from) => {
// Check if user is authenticated
// This would need to be implemented based on your auth system
const isAuthenticated = checkAuth(); // Placeholder
if (!isAuthenticated) {
return '/login';
}
return true;
}),
/**
* Require guest (not authenticated)
*/
guest: RouteGuard.create('guest', async (to, from) => {
const isAuthenticated = checkAuth(); // Placeholder
if (isAuthenticated) {
return '/';
}
return true;
}),
/**
* Require specific role
*/
role: (requiredRole) => RouteGuard.create('role', async (to, from) => {
const userRole = getUserRole(); // Placeholder
if (userRole !== requiredRole) {
return '/unauthorized';
}
return true;
}),
/**
* Require permission
*/
permission: (requiredPermission) => RouteGuard.create('permission', async (to, from) => {
const hasPermission = checkPermission(requiredPermission); // Placeholder
if (!hasPermission) {
return '/unauthorized';
}
return true;
})
};
// Placeholder functions (would be implemented based on auth system)
function checkAuth() {
// Implementation depends on auth system
return false;
}
function getUserRole() {
// Implementation depends on auth system
return null;
}
function checkPermission(permission) {
// Implementation depends on auth system
return false;
}

View File

@@ -0,0 +1,78 @@
/**
* Route Middleware
*
* Provides route-level middleware for cross-cutting concerns.
*/
import { Logger } from '../../core/logger.js';
/**
* RouteMiddleware - Route middleware
*/
export class RouteMiddleware {
constructor(name, middlewareFn) {
this.name = name;
this.middlewareFn = middlewareFn;
}
/**
* Create a new RouteMiddleware
*/
static create(name, middlewareFn) {
return new RouteMiddleware(name, middlewareFn);
}
/**
* Execute middleware
*/
async execute(to, from, next, context = {}) {
try {
await this.middlewareFn(to, from, next, context);
} catch (error) {
Logger.error(`[RouteMiddleware] Middleware "${this.name}" error:`, error);
next(false); // Block navigation
}
}
}
/**
* Built-in middleware
*/
export const BuiltInMiddleware = {
/**
* Analytics middleware
*/
analytics: RouteMiddleware.create('analytics', async (to, from, next) => {
// Track page view
if (typeof window !== 'undefined' && window.analytics) {
window.analytics.track('page_view', {
path: to.path,
title: to.title
});
}
next();
}),
/**
* Loading middleware
*/
loading: RouteMiddleware.create('loading', async (to, from, next) => {
// Show loading indicator
document.body.classList.add('route-loading');
next();
// Hide loading indicator after navigation
setTimeout(() => {
document.body.classList.remove('route-loading');
}, 100);
}),
/**
* Scroll to top middleware
*/
scrollToTop: RouteMiddleware.create('scroll-to-top', async (to, from, next) => {
next();
window.scrollTo({ top: 0, behavior: 'smooth' });
})
};

View File

@@ -0,0 +1,378 @@
/**
* Enhanced Router
*
* Provides enhanced routing with guards, middleware, lazy loading, and analytics.
*/
import { Logger } from '../../core/logger.js';
import { RouteGuard, BuiltInGuards } from './RouteGuard.js';
import { RouteMiddleware, BuiltInMiddleware } from './RouteMiddleware.js';
/**
* Router - Enhanced routing system
*/
export class Router {
constructor(config = {}) {
this.config = {
mode: config.mode || 'history', // 'history' | 'hash'
base: config.base || '/',
enableAnalytics: config.enableAnalytics ?? true,
...config
};
this.routes = new Map();
this.guards = new Map();
this.middleware = [];
this.beforeEachHooks = [];
this.afterEachHooks = [];
this.currentRoute = null;
this.analytics = {
navigations: [],
errors: []
};
// Initialize
this.init();
}
/**
* Create a new Router instance
*/
static create(config = {}) {
return new Router(config);
}
/**
* Initialize router
*/
init() {
// Handle browser navigation
window.addEventListener('popstate', (event) => {
this.handlePopState(event);
});
Logger.info('[Router] Initialized', {
mode: this.config.mode,
base: this.config.base
});
}
/**
* Register a route
*/
route(path, config) {
const route = {
path: this.normalizePath(path),
component: config.component,
name: config.name || path,
title: config.title || null,
meta: config.meta || {},
guards: config.guards || [],
middleware: config.middleware || [],
lazy: config.lazy ?? false,
...config
};
this.routes.set(route.path, route);
Logger.debug('[Router] Route registered', route);
return this;
}
/**
* Register multiple routes
*/
routes(routesConfig) {
routesConfig.forEach(route => {
this.route(route.path, route);
});
return this;
}
/**
* Register a guard
*/
guard(name, guardFn) {
const guard = RouteGuard.create(name, guardFn);
this.guards.set(name, guard);
return this;
}
/**
* Register middleware
*/
use(middleware) {
if (typeof middleware === 'function') {
this.middleware.push(RouteMiddleware.create('anonymous', middleware));
} else if (middleware instanceof RouteMiddleware) {
this.middleware.push(middleware);
} else if (typeof middleware === 'string' && BuiltInMiddleware[middleware]) {
this.middleware.push(BuiltInMiddleware[middleware]);
}
return this;
}
/**
* Add beforeEach hook
*/
beforeEach(hook) {
this.beforeEachHooks.push(hook);
return this;
}
/**
* Add afterEach hook
*/
afterEach(hook) {
this.afterEachHooks.push(hook);
return this;
}
/**
* Navigate to a route
*/
async navigate(path, options = {}) {
const normalizedPath = this.normalizePath(path);
const route = this.routes.get(normalizedPath);
if (!route) {
Logger.warn('[Router] Route not found', normalizedPath);
return false;
}
const from = this.currentRoute;
const to = {
path: normalizedPath,
name: route.name,
title: route.title,
meta: route.meta,
component: route.component
};
// Execute beforeEach hooks
for (const hook of this.beforeEachHooks) {
const result = await hook(to, from);
if (result === false || typeof result === 'string') {
if (typeof result === 'string') {
return await this.navigate(result);
}
return false;
}
}
// Execute guards
for (const guardName of route.guards) {
const guard = this.guards.get(guardName) || BuiltInGuards[guardName];
if (!guard) {
Logger.warn('[Router] Guard not found', guardName);
continue;
}
const result = await guard.execute(to, from);
if (!result.allowed) {
if (result.redirect) {
return await this.navigate(result.redirect);
}
Logger.warn('[Router] Navigation blocked by guard', guardName);
return false;
}
}
// Execute middleware
let middlewareBlocked = false;
for (const middleware of [...this.middleware, ...route.middleware]) {
await new Promise((resolve) => {
middleware.execute(to, from, (allowed) => {
if (allowed === false) {
middlewareBlocked = true;
}
resolve();
});
});
if (middlewareBlocked) {
return false;
}
}
// Load component if lazy
if (route.lazy && typeof route.component === 'function') {
try {
route.component = await route.component();
} catch (error) {
Logger.error('[Router] Failed to load lazy component', error);
return false;
}
}
// Update current route
this.currentRoute = to;
// Update browser history
if (this.config.mode === 'history') {
history.pushState({ route: to }, route.title || '', normalizedPath);
} else {
window.location.hash = normalizedPath;
}
// Update page title
if (route.title) {
document.title = route.title;
}
// Track analytics
if (this.config.enableAnalytics) {
this.trackNavigation(to, from);
}
// Execute afterEach hooks
for (const hook of this.afterEachHooks) {
await hook(to, from);
}
// Render component
await this.renderComponent(route, options);
Logger.info('[Router] Navigated', { to: normalizedPath });
return true;
}
/**
* Render component
*/
async renderComponent(route, options = {}) {
const container = options.container || document.querySelector('main');
if (!container) {
Logger.error('[Router] Container not found');
return;
}
if (typeof route.component === 'function') {
// Component is a function (render function)
const content = await route.component(route, options);
if (typeof content === 'string') {
container.innerHTML = content;
} else if (content instanceof HTMLElement) {
container.innerHTML = '';
container.appendChild(content);
}
} else if (typeof route.component === 'string') {
// Component is a selector or HTML
const element = document.querySelector(route.component);
if (element) {
container.innerHTML = '';
container.appendChild(element.cloneNode(true));
} else {
container.innerHTML = route.component;
}
}
// Re-initialize modules in new content
this.reinitializeModules(container);
}
/**
* Re-initialize modules in container
*/
reinitializeModules(container) {
// Trigger module re-initialization
const event = new CustomEvent('router:content-updated', {
detail: { container },
bubbles: true
});
document.dispatchEvent(event);
}
/**
* Handle popstate event
*/
handlePopState(event) {
const path = this.config.mode === 'history'
? window.location.pathname
: window.location.hash.slice(1);
this.navigate(path, { updateHistory: false });
}
/**
* Normalize path
*/
normalizePath(path) {
// Remove base if present
if (path.startsWith(this.config.base)) {
path = path.slice(this.config.base.length);
}
// Ensure leading slash
if (!path.startsWith('/')) {
path = '/' + path;
}
// Remove trailing slash (except root)
if (path !== '/' && path.endsWith('/')) {
path = path.slice(0, -1);
}
return path;
}
/**
* Track navigation for analytics
*/
trackNavigation(to, from) {
const navigation = {
to: to.path,
from: from?.path || null,
timestamp: Date.now(),
duration: from ? Date.now() - (from.timestamp || Date.now()) : 0
};
this.analytics.navigations.push(navigation);
// Limit analytics size
if (this.analytics.navigations.length > 100) {
this.analytics.navigations.shift();
}
// Trigger analytics event
const event = new CustomEvent('router:navigation', {
detail: navigation,
bubbles: true
});
window.dispatchEvent(event);
}
/**
* Get current route
*/
getCurrentRoute() {
return this.currentRoute;
}
/**
* Get analytics
*/
getAnalytics() {
return {
...this.analytics,
totalNavigations: this.analytics.navigations.length,
totalErrors: this.analytics.errors.length
};
}
/**
* Destroy router
*/
destroy() {
this.routes.clear();
this.guards.clear();
this.middleware = [];
this.beforeEachHooks = [];
this.afterEachHooks = [];
this.currentRoute = null;
Logger.info('[Router] Destroyed');
}
}

View File

@@ -0,0 +1,87 @@
/**
* Router Enhancement Module
*
* Provides enhanced routing with guards, middleware, lazy loading, and analytics.
*
* Usage:
* - Add data-module="router" to enable enhanced router
* - Or import and use directly: import { Router } from './modules/router/index.js'
*
* Features:
* - Route guards (auth, permissions)
* - Route middleware
* - Lazy route loading
* - Route analytics
* - Integration with LiveComponents
*/
import { Logger } from '../../core/logger.js';
import { Router } from './Router.js';
import { RouteGuard, BuiltInGuards } from './RouteGuard.js';
import { RouteMiddleware, BuiltInMiddleware } from './RouteMiddleware.js';
const RouterModule = {
name: 'router',
router: null,
init(config = {}, state = null) {
Logger.info('[RouterModule] Module initialized');
// Create router
this.router = Router.create(config);
// Expose globally for easy access
if (typeof window !== 'undefined') {
window.Router = this.router;
}
return this;
},
/**
* Get router instance
*/
getRouter() {
return this.router || Router.create();
},
/**
* Register routes
*/
routes(routesConfig) {
const router = this.getRouter();
router.routes(routesConfig);
return this;
},
/**
* Navigate to route
*/
async navigate(path, options = {}) {
const router = this.getRouter();
return await router.navigate(path, options);
},
destroy() {
if (this.router) {
this.router.destroy();
this.router = null;
}
if (typeof window !== 'undefined' && window.Router) {
delete window.Router;
}
Logger.info('[RouterModule] Module destroyed');
}
};
// Export for direct usage
export { Router, RouteGuard, RouteMiddleware, BuiltInGuards, BuiltInMiddleware };
// Export as default for module system
export default RouterModule;
// Export init function for module system
export const init = RouterModule.init.bind(RouterModule);