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

@@ -13,6 +13,8 @@
*/
import { Core } from '../core.js';
import { PerformanceProfiler, LiveComponentsProfiler } from '../performance-profiler/profiler.js';
import { FlamegraphVisualizer, TimelineVisualizer } from '../performance-profiler/visualizer.js';
export class LiveComponentDevTools {
constructor() {
@@ -26,12 +28,21 @@ export class LiveComponentDevTools {
this.domBadges = new Map(); // Track DOM badges for cleanup
this.badgesEnabled = true; // Badge visibility toggle
// Performance profiling state
// Performance profiling state - using PerformanceProfiler
this.profiler = new PerformanceProfiler({
enabled: true,
maxSamples: 1000,
samplingInterval: 10,
autoStart: false
});
this.flamegraphVisualizer = null;
this.timelineVisualizer = null;
this.isRecording = false;
this.performanceRecording = [];
this.performanceRecording = []; // Legacy data for backward compatibility
this.componentRenderTimes = new Map();
this.actionExecutionTimes = new Map();
this.memorySnapshots = [];
this.componentProfilers = new Map(); // Track profilers for each component
this.overlay = null;
this.isEnabled = this.checkIfEnabled();
@@ -163,8 +174,14 @@ export class LiveComponentDevTools {
<button class="lc-devtools__btn lc-devtools__btn--small" data-action="clear-performance">
Clear
</button>
<button class="lc-devtools__btn lc-devtools__btn--small" data-action="export-performance">
Export
</button>
</div>
<div class="lc-devtools__metrics" data-content="performance">
<div id="lc-flamegraph-container" style="margin-bottom: 16px;"></div>
<div id="lc-timeline-container" style="margin-bottom: 16px;"></div>
</div>
<div class="lc-devtools__metrics" data-content="performance"></div>
</div>
<div class="lc-devtools__pane" data-pane="network">
@@ -500,6 +517,10 @@ export class LiveComponentDevTools {
this.clearPerformanceData();
});
this.overlay.querySelector('[data-action="export-performance"]')?.addEventListener('click', () => {
this.exportPerformanceData();
});
this.overlay.querySelector('[data-action="clear-network"]')?.addEventListener('click', () => {
this.clearNetworkLog();
});
@@ -616,6 +637,11 @@ export class LiveComponentDevTools {
pane.classList.toggle('lc-devtools__pane--active', pane.dataset.pane === tabName);
});
// Initialize visualizers when switching to performance tab
if (tabName === 'performance') {
this.initializePerformanceVisualizers();
}
// Refresh content for active tab
this.refreshActiveTab();
}
@@ -956,14 +982,80 @@ export class LiveComponentDevTools {
* Clear performance data
*/
clearPerformanceData() {
// Clear profiler data
this.profiler.clear();
// Clear legacy data
this.performanceData = [];
this.performanceRecording = [];
this.componentRenderTimes.clear();
this.actionExecutionTimes.clear();
this.memorySnapshots = [];
// Clear visualizers
if (this.flamegraphVisualizer) {
this.flamegraphVisualizer.clear();
}
if (this.timelineVisualizer) {
this.timelineVisualizer.clear();
}
this.renderPerformanceData();
}
/**
* Export performance data
*/
exportPerformanceData() {
const status = this.profiler.getStatus();
if (status.measuresCount === 0 && status.marksCount === 0 && status.samplesCount === 0) {
alert('No performance data to export. Start recording first.');
return;
}
// Export Chrome DevTools trace format
const traceData = this.profiler.exportToChromeTrace();
// Create download
const blob = new Blob([JSON.stringify(traceData, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `livecomponent-performance-${Date.now()}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
console.log('[LiveComponent DevTools] Performance data exported');
}
/**
* Initialize performance visualizers
*/
initializePerformanceVisualizers() {
const flamegraphContainer = this.overlay.querySelector('#lc-flamegraph-container');
const timelineContainer = this.overlay.querySelector('#lc-timeline-container');
if (flamegraphContainer && !this.flamegraphVisualizer) {
this.flamegraphVisualizer = new FlamegraphVisualizer(flamegraphContainer, {
width: flamegraphContainer.clientWidth || 750,
height: 300,
barHeight: 20,
colorScheme: 'category'
});
}
if (timelineContainer && !this.timelineVisualizer) {
this.timelineVisualizer = new TimelineVisualizer(timelineContainer, {
width: timelineContainer.clientWidth || 750,
height: 200,
trackHeight: 30
});
}
}
/**
* Toggle performance recording
*/
@@ -989,6 +1081,11 @@ export class LiveComponentDevTools {
*/
startPerformanceRecording() {
console.log('[LiveComponent DevTools] Performance recording started');
// Start the profiler
this.profiler.start();
// Clear legacy data
this.performanceRecording = [];
this.memorySnapshots = [];
@@ -1011,6 +1108,12 @@ export class LiveComponentDevTools {
stopPerformanceRecording() {
console.log('[LiveComponent DevTools] Performance recording stopped');
// Stop the profiler and get results
const results = this.profiler.stop();
if (results) {
console.log('[LiveComponent DevTools] Performance results:', results);
}
if (this.memorySnapshotInterval) {
clearInterval(this.memorySnapshotInterval);
}
@@ -1027,6 +1130,31 @@ export class LiveComponentDevTools {
* Record action execution for performance profiling
*/
recordActionExecution(componentId, actionName, duration, startTime, endTime) {
// Use profiler marks and measures if recording
if (this.profiler.isRecording) {
// Create synthetic marks based on the actual times
// Note: The profiler uses performance.now() internally, so we need to adjust
const markStart = `action:${componentId}:${actionName}:start`;
const markEnd = `action:${componentId}:${actionName}:end`;
const measureName = `action:${componentId}:${actionName}`;
// Record start mark (using current time as reference)
this.profiler.mark(markStart, {
componentId,
actionName,
type: 'action',
originalStartTime: startTime,
originalEndTime: endTime
});
// Record end mark immediately (profiler will calculate duration from marks)
this.profiler.mark(markEnd);
// Create measure - the profiler will calculate duration from the marks
this.profiler.measure(measureName, markStart, markEnd);
}
// Legacy recording for backward compatibility
this.performanceRecording.push({
type: 'action',
componentId,
@@ -1054,8 +1182,30 @@ export class LiveComponentDevTools {
* Record component render for performance profiling
*/
recordComponentRender(componentId, duration, startTime, endTime) {
if (!this.isRecording) return;
if (!this.isRecording && !this.profiler.isRecording) return;
// Use profiler marks and measures if recording
if (this.profiler.isRecording) {
const markStart = `render:${componentId}:start`;
const markEnd = `render:${componentId}:end`;
const measureName = `render:${componentId}`;
// Record start mark
this.profiler.mark(markStart, {
componentId,
type: 'render',
originalStartTime: startTime,
originalEndTime: endTime
});
// Record end mark immediately
this.profiler.mark(markEnd);
// Create measure
this.profiler.measure(measureName, markStart, markEnd);
}
// Legacy recording for backward compatibility
this.performanceRecording.push({
type: 'render',
componentId,
@@ -1103,40 +1253,99 @@ export class LiveComponentDevTools {
const container = this.overlay.querySelector('[data-content="performance"]');
if (!container) return;
if (this.performanceRecording.length === 0 && this.memorySnapshots.length === 0) {
container.innerHTML = `
<div style="color: #969696; text-align: center; padding: 40px;">
// Get profiler status
const status = this.profiler.getStatus();
const hasData = status.samplesCount > 0 || status.marksCount > 0 || status.measuresCount > 0;
const hasLegacyData = this.performanceRecording.length > 0 || this.memorySnapshots.length > 0;
// Initialize visualizers if not already done
this.initializePerformanceVisualizers();
// Get containers for visualizers (they should already exist in the DOM)
const flamegraphContainer = this.overlay.querySelector('#lc-flamegraph-container');
const timelineContainer = this.overlay.querySelector('#lc-timeline-container');
if (!hasData && !hasLegacyData) {
// Clear visualizers
if (this.flamegraphVisualizer) {
this.flamegraphVisualizer.clear();
}
if (this.timelineVisualizer) {
this.timelineVisualizer.clear();
}
// Show empty state but preserve visualizer containers
const existingContent = container.querySelector('.lc-perf-empty-state');
if (!existingContent) {
const emptyState = document.createElement('div');
emptyState.className = 'lc-perf-empty-state';
emptyState.style.cssText = 'color: #969696; text-align: center; padding: 40px;';
emptyState.innerHTML = `
<p>No performance data recorded</p>
<p style="font-size: 12px; margin-top: 12px;">Click "● Record" to start profiling</p>
</div>
`;
`;
container.insertBefore(emptyState, flamegraphContainer);
}
return;
}
let html = '<div class="lc-performance-view" style="padding: 0;">';
// Performance Summary
html += this.renderPerformanceSummary();
// Flamegraph
html += this.renderFlamegraph();
// Timeline
html += this.renderPerformanceTimeline();
// Memory Usage Chart
if (this.memorySnapshots.length > 0) {
html += this.renderMemoryChart();
// Remove empty state if present
const emptyState = container.querySelector('.lc-perf-empty-state');
if (emptyState) {
emptyState.remove();
}
html += '</div>';
container.innerHTML = html;
// Render summary
const summaryHtml = this.renderPerformanceSummary();
// Insert or update summary (before visualizer containers)
const existingSummary = container.querySelector('.lc-perf-summary');
if (existingSummary) {
existingSummary.outerHTML = summaryHtml;
} else {
container.insertAdjacentHTML('afterbegin', summaryHtml);
}
// Render flamegraph if we have data
if (this.flamegraphVisualizer && hasData && flamegraphContainer) {
const flamegraphData = this.profiler.generateFlamegraph();
if (flamegraphData) {
this.flamegraphVisualizer.render(flamegraphData);
} else {
this.flamegraphVisualizer.clear();
}
}
// Render timeline if we have data
if (this.timelineVisualizer && hasData && timelineContainer) {
const timelineData = this.profiler.generateTimeline();
if (timelineData && timelineData.length > 0) {
this.timelineVisualizer.render(timelineData);
} else {
this.timelineVisualizer.clear();
}
}
// Render legacy memory chart if available
if (this.memorySnapshots.length > 0) {
const existingMemoryChart = container.querySelector('.lc-memory-chart');
const memoryChartHtml = this.renderMemoryChart();
if (existingMemoryChart) {
existingMemoryChart.outerHTML = memoryChartHtml;
} else {
container.insertAdjacentHTML('beforeend', memoryChartHtml);
}
}
}
/**
* Render performance summary
*/
renderPerformanceSummary() {
const status = this.profiler.getStatus();
const summary = this.profiler.generateSummary();
// Legacy data counts
const actionCount = this.performanceRecording.filter(r => r.type === 'action').length;
const renderCount = this.performanceRecording.filter(r => r.type === 'render').length;
@@ -1144,15 +1353,9 @@ export class LiveComponentDevTools {
? this.performanceRecording
.filter(r => r.type === 'action')
.reduce((sum, r) => sum + r.duration, 0) / actionCount
: 0;
: (summary.avgDuration || 0);
const avgRenderTime = renderCount > 0
? this.performanceRecording
.filter(r => r.type === 'render')
.reduce((sum, r) => sum + r.duration, 0) / renderCount
: 0;
const totalDuration = this.performanceRecording.reduce((sum, r) => sum + r.duration, 0);
const totalDuration = summary.totalDuration || this.performanceRecording.reduce((sum, r) => sum + r.duration, 0);
return `
<div class="lc-perf-summary" style="padding: 16px; border-bottom: 1px solid #3c3c3c; background: #252526;">
@@ -1160,18 +1363,18 @@ export class LiveComponentDevTools {
<div style="display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; font-size: 12px;">
<div>
<div style="color: #858585; margin-bottom: 4px;">Total Events</div>
<div style="color: #4ec9b0; font-size: 18px; font-weight: 600;">${this.performanceRecording.length}</div>
<div style="color: #4ec9b0; font-size: 18px; font-weight: 600;">${status.timelineEventsCount || this.performanceRecording.length}</div>
</div>
<div>
<div style="color: #858585; margin-bottom: 4px;">Actions</div>
<div style="color: #dcdcaa; font-size: 18px; font-weight: 600;">${actionCount}</div>
<div style="color: #858585; margin-bottom: 4px;">Measures</div>
<div style="color: #dcdcaa; font-size: 18px; font-weight: 600;">${status.measuresCount || actionCount}</div>
</div>
<div>
<div style="color: #858585; margin-bottom: 4px;">Renders</div>
<div style="color: #569cd6; font-size: 18px; font-weight: 600;">${renderCount}</div>
<div style="color: #858585; margin-bottom: 4px;">Samples</div>
<div style="color: #569cd6; font-size: 18px; font-weight: 600;">${status.samplesCount}</div>
</div>
<div>
<div style="color: #858585; margin-bottom: 4px;">Avg Action</div>
<div style="color: #858585; margin-bottom: 4px;">Avg Duration</div>
<div style="color: #dcdcaa; font-size: 18px; font-weight: 600;">${avgActionTime.toFixed(2)}ms</div>
</div>
<div>
@@ -1179,6 +1382,13 @@ export class LiveComponentDevTools {
<div style="color: #f48771; font-size: 18px; font-weight: 600;">${totalDuration.toFixed(2)}ms</div>
</div>
</div>
${summary.percentiles ? `
<div style="margin-top: 12px; padding-top: 12px; border-top: 1px solid #3c3c3c; font-size: 11px; color: #858585;">
<strong>Percentiles:</strong> P50: ${summary.percentiles.p50?.toFixed(2) || 'N/A'}ms |
P90: ${summary.percentiles.p90?.toFixed(2) || 'N/A'}ms |
P99: ${summary.percentiles.p99?.toFixed(2) || 'N/A'}ms
</div>
` : ''}
</div>
`;
}