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,347 @@
/**
* LiveComponent UI Helper
*
* Provides unified API for UI components (Dialogs, Modals, Notifications)
* integrated with LiveComponents.
*/
import { UIManager } from '../ui/UIManager.js';
export class LiveComponentUIHelper {
constructor(liveComponentManager) {
this.manager = liveComponentManager;
this.uiManager = UIManager;
this.activeDialogs = new Map(); // componentId → dialog instances
this.activeNotifications = new Map(); // componentId → notification instances
}
/**
* Show dialog from LiveComponent action
*
* @param {string} componentId - Component ID
* @param {Object} options - Dialog options
* @returns {Object} Dialog instance
*/
showDialog(componentId, options = {}) {
const {
title = '',
content = '',
size = 'medium',
buttons = [],
closeOnBackdrop = true,
closeOnEscape = true,
onClose = null,
onConfirm = null
} = options;
// Create dialog content
const dialogContent = this.createDialogContent(title, content, buttons, {
onClose,
onConfirm
});
// Show modal via UIManager
const modal = this.uiManager.open('modal', {
content: dialogContent,
className: `livecomponent-dialog livecomponent-dialog--${size}`,
onClose: () => {
if (onClose) {
onClose();
}
this.activeDialogs.delete(componentId);
}
});
// Store reference
this.activeDialogs.set(componentId, modal);
return modal;
}
/**
* Show confirmation dialog
*
* @param {string} componentId - Component ID
* @param {Object} options - Confirmation options
* @returns {Promise<boolean>} Promise resolving to true if confirmed
*/
showConfirm(componentId, options = {}) {
return new Promise((resolve) => {
const {
title = 'Confirm',
message = 'Are you sure?',
confirmText = 'Confirm',
cancelText = 'Cancel',
confirmClass = 'btn-primary',
cancelClass = 'btn-secondary'
} = options;
const buttons = [
{
text: cancelText,
class: cancelClass,
action: () => {
this.closeDialog(componentId);
resolve(false);
}
},
{
text: confirmText,
class: confirmClass,
action: () => {
this.closeDialog(componentId);
resolve(true);
}
}
];
this.showDialog(componentId, {
title,
content: `<p>${message}</p>`,
buttons,
size: 'small'
});
});
}
/**
* Show alert dialog
*
* @param {string} componentId - Component ID
* @param {Object} options - Alert options
*/
showAlert(componentId, options = {}) {
const {
title = 'Alert',
message = '',
buttonText = 'OK',
type = 'info' // info, success, warning, error
} = options;
const buttons = [
{
text: buttonText,
class: `btn-${type}`,
action: () => {
this.closeDialog(componentId);
}
}
];
return this.showDialog(componentId, {
title,
content: `<p class="alert-message alert-message--${type}">${message}</p>`,
buttons,
size: 'small'
});
}
/**
* Close dialog
*
* @param {string} componentId - Component ID
*/
closeDialog(componentId) {
const dialog = this.activeDialogs.get(componentId);
if (dialog && typeof dialog.close === 'function') {
dialog.close();
this.activeDialogs.delete(componentId);
}
}
/**
* Show notification
*
* @param {string} componentId - Component ID
* @param {Object} options - Notification options
*/
showNotification(componentId, options = {}) {
const {
message = '',
type = 'info', // info, success, warning, error
duration = 5000,
position = 'top-right',
action = null
} = options;
// Create notification element
const notification = document.createElement('div');
notification.className = `livecomponent-notification livecomponent-notification--${type} livecomponent-notification--${position}`;
notification.setAttribute('role', 'alert');
notification.setAttribute('aria-live', 'polite');
notification.innerHTML = `
<div class="notification-content">
<span class="notification-message">${message}</span>
${action ? `<button class="notification-action">${action.text}</button>` : ''}
<button class="notification-close" aria-label="Close">×</button>
</div>
`;
// Add styles
notification.style.cssText = `
position: fixed;
${position.includes('top') ? 'top' : 'bottom'}: 1rem;
${position.includes('left') ? 'left' : 'right'}: 1rem;
max-width: 400px;
padding: 1rem;
background: ${this.getNotificationColor(type)};
color: white;
border-radius: 0.5rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
z-index: 10000;
opacity: 0;
transform: translateY(${position.includes('top') ? '-20px' : '20px'});
transition: opacity 0.3s ease, transform 0.3s ease;
`;
// Add to DOM
document.body.appendChild(notification);
// Animate in
requestAnimationFrame(() => {
notification.style.opacity = '1';
notification.style.transform = 'translateY(0)';
});
// Setup close button
const closeBtn = notification.querySelector('.notification-close');
closeBtn.addEventListener('click', () => {
this.hideNotification(notification);
});
// Setup action button
if (action) {
const actionBtn = notification.querySelector('.notification-action');
actionBtn.addEventListener('click', () => {
if (action.handler) {
action.handler();
}
this.hideNotification(notification);
});
}
// Auto-hide after duration
if (duration > 0) {
setTimeout(() => {
this.hideNotification(notification);
}, duration);
}
// Store reference
this.activeNotifications.set(componentId, notification);
return notification;
}
/**
* Hide notification
*
* @param {HTMLElement|string} notificationOrComponentId - Notification element or component ID
*/
hideNotification(notificationOrComponentId) {
const notification = typeof notificationOrComponentId === 'string'
? this.activeNotifications.get(notificationOrComponentId)
: notificationOrComponentId;
if (!notification) {
return;
}
// Animate out
notification.style.opacity = '0';
notification.style.transform = `translateY(${notification.classList.contains('livecomponent-notification--top') ? '-20px' : '20px'})`;
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
// Remove from map
for (const [componentId, notif] of this.activeNotifications.entries()) {
if (notif === notification) {
this.activeNotifications.delete(componentId);
break;
}
}
}, 300);
}
/**
* Create dialog content HTML
*
* @param {string} title - Dialog title
* @param {string} content - Dialog content
* @param {Array} buttons - Button configurations
* @param {Object} callbacks - Callback functions
* @returns {string} HTML content
*/
createDialogContent(title, content, buttons, callbacks) {
const buttonsHtml = buttons.map((btn, index) => {
const btnClass = btn.class || 'btn-secondary';
const btnAction = btn.action || (() => {});
return `<button class="btn ${btnClass}" data-dialog-action="${index}">${btn.text || 'Button'}</button>`;
}).join('');
return `
<div class="livecomponent-dialog-content">
${title ? `<div class="dialog-header"><h3>${title}</h3></div>` : ''}
<div class="dialog-body">${content}</div>
${buttons.length > 0 ? `<div class="dialog-footer">${buttonsHtml}</div>` : ''}
</div>
`;
}
/**
* Get notification color by type
*
* @param {string} type - Notification type
* @returns {string} Color value
*/
getNotificationColor(type) {
const colors = {
info: '#3b82f6',
success: '#10b981',
warning: '#f59e0b',
error: '#ef4444'
};
return colors[type] || colors.info;
}
/**
* Show loading dialog
*
* @param {string} componentId - Component ID
* @param {string} message - Loading message
* @returns {Object} Dialog instance
*/
showLoadingDialog(componentId, message = 'Loading...') {
return this.showDialog(componentId, {
title: '',
content: `
<div class="loading-dialog">
<div class="spinner"></div>
<p>${message}</p>
</div>
`,
size: 'small',
buttons: [],
closeOnBackdrop: false,
closeOnEscape: false
});
}
/**
* Cleanup all UI components for component
*
* @param {string} componentId - Component ID
*/
cleanup(componentId) {
// Close dialogs
this.closeDialog(componentId);
// Hide notifications
this.hideNotification(componentId);
}
}