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,302 @@
/**
* Unified Animation System
*
* Consolidates all scroll animation modules into a single, unified system.
* Replaces: scrollfx, parallax, scroll-timeline, scroll-loop, scroll-dependent, sticky-fade, sticky-steps
*/
import { Logger } from '../../core/logger.js';
import { ScrollAnimation } from './ScrollAnimation.js';
import { TimelineAnimation } from './TimelineAnimation.js';
/**
* AnimationSystem - Unified animation system
*/
export class AnimationSystem {
constructor(config = {}) {
this.config = {
enabled: config.enabled ?? true,
useIntersectionObserver: config.useIntersectionObserver ?? true,
throttleDelay: config.throttleDelay || 16, // ~60fps
...config
};
this.animations = new Map(); // Map<element, Animation>
this.observers = new Map(); // Map<element, IntersectionObserver>
this.scrollHandler = null;
this.isScrolling = false;
// Initialize
if (this.config.enabled) {
this.init();
}
Logger.info('[AnimationSystem] Initialized', {
enabled: this.config.enabled,
useIntersectionObserver: this.config.useIntersectionObserver
});
}
/**
* Create a new AnimationSystem instance
*/
static create(config = {}) {
return new AnimationSystem(config);
}
/**
* Initialize animation system
*/
init() {
// Set up scroll handler
if (!this.config.useIntersectionObserver) {
this.setupScrollHandler();
}
// Auto-initialize elements with data attributes
this.autoInitialize();
}
/**
* Set up scroll handler
*/
setupScrollHandler() {
let ticking = false;
this.scrollHandler = () => {
if (!ticking) {
window.requestAnimationFrame(() => {
this.updateAnimations();
ticking = false;
});
ticking = true;
}
};
window.addEventListener('scroll', this.scrollHandler, { passive: true });
}
/**
* Auto-initialize elements with data attributes
*/
autoInitialize() {
// Fade in on scroll (scrollfx)
this.initializeFadeIn();
// Parallax
this.initializeParallax();
// Scroll timeline
this.initializeTimeline();
// Sticky fade
this.initializeStickyFade();
// Sticky steps
this.initializeStickySteps();
}
/**
* Initialize fade-in animations (scrollfx)
*/
initializeFadeIn() {
const elements = document.querySelectorAll('.fade-in-on-scroll, .zoom-in, [data-animate="fade-in"]');
elements.forEach(element => {
this.registerAnimation(element, {
type: 'fade-in',
offset: parseFloat(element.dataset.offset) || 0.85,
delay: parseFloat(element.dataset.delay) || 0,
once: element.dataset.once !== 'false'
});
});
}
/**
* Initialize parallax animations
*/
initializeParallax() {
const elements = document.querySelectorAll('[data-parallax], .parallax');
elements.forEach(element => {
const speed = parseFloat(element.dataset.parallax || element.dataset.speed) || 0.5;
this.registerAnimation(element, {
type: 'parallax',
speed
});
});
}
/**
* Initialize timeline animations (scroll-timeline)
*/
initializeTimeline() {
const elements = document.querySelectorAll('[data-scroll-timeline], [data-scroll-step]');
elements.forEach(element => {
this.registerAnimation(element, {
type: 'timeline',
steps: element.dataset.scrollSteps ? parseInt(element.dataset.scrollSteps) : null,
triggerPoint: parseFloat(element.dataset.triggerPoint) || 0.4
});
});
}
/**
* Initialize sticky fade animations
*/
initializeStickyFade() {
const elements = document.querySelectorAll('[data-sticky-fade], .sticky-fade');
elements.forEach(element => {
this.registerAnimation(element, {
type: 'sticky-fade',
fadeStart: parseFloat(element.dataset.fadeStart) || 0,
fadeEnd: parseFloat(element.dataset.fadeEnd) || 1
});
});
}
/**
* Initialize sticky steps animations
*/
initializeStickySteps() {
const elements = document.querySelectorAll('[data-sticky-steps], .sticky-steps');
elements.forEach(element => {
const steps = element.dataset.stickySteps ? parseInt(element.dataset.stickySteps) : 3;
this.registerAnimation(element, {
type: 'sticky-steps',
steps
});
});
}
/**
* Register an animation
*/
registerAnimation(element, config) {
if (this.animations.has(element)) {
Logger.warn('[AnimationSystem] Animation already registered for element', element);
return;
}
let animation;
switch (config.type) {
case 'fade-in':
case 'zoom-in':
animation = new ScrollAnimation(element, {
type: config.type,
offset: config.offset || 0.85,
delay: config.delay || 0,
once: config.once !== false
});
break;
case 'parallax':
animation = new ScrollAnimation(element, {
type: 'parallax',
speed: config.speed || 0.5
});
break;
case 'timeline':
animation = new TimelineAnimation(element, {
steps: config.steps,
triggerPoint: config.triggerPoint || 0.4
});
break;
case 'sticky-fade':
animation = new ScrollAnimation(element, {
type: 'sticky-fade',
fadeStart: config.fadeStart || 0,
fadeEnd: config.fadeEnd || 1
});
break;
case 'sticky-steps':
animation = new ScrollAnimation(element, {
type: 'sticky-steps',
steps: config.steps || 3
});
break;
default:
Logger.warn('[AnimationSystem] Unknown animation type', config.type);
return;
}
this.animations.set(element, animation);
// Set up observer if using IntersectionObserver
if (this.config.useIntersectionObserver) {
this.setupObserver(element, animation);
}
Logger.debug('[AnimationSystem] Animation registered', { element, type: config.type });
}
/**
* Set up IntersectionObserver for element
*/
setupObserver(element, animation) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
animation.enter();
} else if (!animation.config.once) {
animation.exit();
}
});
}, {
threshold: animation.config.offset || 0.85,
rootMargin: '0px'
});
observer.observe(element);
this.observers.set(element, observer);
}
/**
* Update all animations (for scroll-based updates)
*/
updateAnimations() {
this.animations.forEach((animation, element) => {
if (animation.needsUpdate) {
animation.update();
}
});
}
/**
* Remove animation
*/
removeAnimation(element) {
const animation = this.animations.get(element);
if (animation) {
animation.destroy();
this.animations.delete(element);
}
const observer = this.observers.get(element);
if (observer) {
observer.disconnect();
this.observers.delete(element);
}
}
/**
* Destroy animation system
*/
destroy() {
// Remove all animations
this.animations.forEach((animation, element) => {
this.removeAnimation(element);
});
// Remove scroll handler
if (this.scrollHandler) {
window.removeEventListener('scroll', this.scrollHandler);
}
Logger.info('[AnimationSystem] Destroyed');
}
}

View File

@@ -0,0 +1,211 @@
/**
* Scroll Animation
*
* Handles various scroll-based animations: fade-in, zoom-in, parallax, sticky-fade, sticky-steps
*/
import { Logger } from '../../core/logger.js';
/**
* ScrollAnimation - Individual scroll animation
*/
export class ScrollAnimation {
constructor(element, config = {}) {
this.element = element;
this.config = {
type: config.type || 'fade-in',
offset: config.offset || 0.85,
delay: config.delay || 0,
once: config.once !== false,
speed: config.speed || 0.5,
fadeStart: config.fadeStart || 0,
fadeEnd: config.fadeEnd || 1,
steps: config.steps || 3,
...config
};
this.triggered = false;
this.needsUpdate = true;
// Initialize based on type
this.init();
}
/**
* Initialize animation
*/
init() {
switch (this.config.type) {
case 'fade-in':
case 'zoom-in':
this.initFadeIn();
break;
case 'parallax':
this.initParallax();
break;
case 'sticky-fade':
this.initStickyFade();
break;
case 'sticky-steps':
this.initStickySteps();
break;
}
}
/**
* Initialize fade-in animation
*/
initFadeIn() {
this.element.style.opacity = '0';
this.element.style.transition = `opacity 0.6s ease, transform 0.6s ease`;
this.element.style.transitionDelay = `${this.config.delay}s`;
if (this.config.type === 'zoom-in') {
this.element.style.transform = 'scale(0.9)';
}
}
/**
* Initialize parallax animation
*/
initParallax() {
// Parallax doesn't need initial setup
this.needsUpdate = true;
}
/**
* Initialize sticky fade animation
*/
initStickyFade() {
this.element.style.position = 'sticky';
this.element.style.top = '0';
}
/**
* Initialize sticky steps animation
*/
initStickySteps() {
this.element.style.position = 'sticky';
this.element.style.top = '0';
}
/**
* Enter animation (element enters viewport)
*/
enter() {
if (this.triggered && this.config.once) {
return;
}
this.triggered = true;
switch (this.config.type) {
case 'fade-in':
this.element.style.opacity = '1';
this.element.classList.add('visible', 'entered');
break;
case 'zoom-in':
this.element.style.opacity = '1';
this.element.style.transform = 'scale(1)';
this.element.classList.add('visible', 'entered');
break;
}
}
/**
* Exit animation (element exits viewport)
*/
exit() {
if (this.config.once) {
return;
}
this.triggered = false;
switch (this.config.type) {
case 'fade-in':
this.element.style.opacity = '0';
this.element.classList.remove('visible', 'entered');
break;
case 'zoom-in':
this.element.style.opacity = '0';
this.element.style.transform = 'scale(0.9)';
this.element.classList.remove('visible', 'entered');
break;
}
}
/**
* Update animation (for scroll-based animations)
*/
update() {
const rect = this.element.getBoundingClientRect();
const viewportHeight = window.innerHeight;
const scrollY = window.scrollY;
switch (this.config.type) {
case 'parallax':
this.updateParallax(rect, scrollY);
break;
case 'sticky-fade':
this.updateStickyFade(rect, viewportHeight);
break;
case 'sticky-steps':
this.updateStickySteps(rect, viewportHeight, scrollY);
break;
}
}
/**
* Update parallax animation
*/
updateParallax(rect, scrollY) {
const elementTop = rect.top + scrollY;
const scrolled = scrollY - elementTop;
const translateY = scrolled * this.config.speed;
this.element.style.transform = `translateY(${translateY}px)`;
}
/**
* Update sticky fade animation
*/
updateStickyFade(rect, viewportHeight) {
const progress = Math.max(0, Math.min(1, (viewportHeight - rect.top) / viewportHeight));
const opacity = this.config.fadeStart + (this.config.fadeEnd - this.config.fadeStart) * progress;
this.element.style.opacity = opacity.toString();
}
/**
* Update sticky steps animation
*/
updateStickySteps(rect, viewportHeight, scrollY) {
const elementTop = rect.top + scrollY - viewportHeight;
const scrollProgress = Math.max(0, Math.min(1, scrollY / (rect.height + viewportHeight)));
const step = Math.floor(scrollProgress * this.config.steps);
this.element.setAttribute('data-step', step.toString());
this.element.classList.remove('step-0', 'step-1', 'step-2', 'step-3', 'step-4', 'step-5');
this.element.classList.add(`step-${step}`);
}
/**
* Destroy animation
*/
destroy() {
// Reset styles
this.element.style.opacity = '';
this.element.style.transform = '';
this.element.style.transition = '';
this.element.style.transitionDelay = '';
this.element.style.position = '';
this.element.style.top = '';
// Remove classes
this.element.classList.remove('visible', 'entered');
this.triggered = false;
}
}

View File

@@ -0,0 +1,157 @@
/**
* Timeline Animation
*
* Handles scroll timeline animations (scroll-timeline, scroll-loop)
*/
import { Logger } from '../../core/logger.js';
/**
* TimelineAnimation - Scroll timeline animation
*/
export class TimelineAnimation {
constructor(element, config = {}) {
this.element = element;
this.config = {
steps: config.steps || null,
triggerPoint: config.triggerPoint || 0.4,
loop: config.loop ?? false,
...config
};
this.currentStep = 0;
this.triggered = false;
// Initialize
this.init();
}
/**
* Initialize timeline animation
*/
init() {
// Set initial step
this.element.setAttribute('data-scroll-step', '0');
this.element.classList.add('scroll-timeline');
}
/**
* Enter animation
*/
enter() {
if (this.triggered && !this.config.loop) {
return;
}
this.triggered = true;
this.update();
}
/**
* Exit animation
*/
exit() {
if (this.config.loop) {
this.currentStep = 0;
this.update();
}
}
/**
* Update animation based on scroll position
*/
update() {
const rect = this.element.getBoundingClientRect();
const viewportHeight = window.innerHeight;
const triggerY = viewportHeight * this.config.triggerPoint;
if (rect.top < triggerY && rect.bottom > 0) {
// Calculate progress
const progress = Math.max(0, Math.min(1, (triggerY - rect.top) / (rect.height + viewportHeight)));
if (this.config.steps) {
// Step-based animation
const step = Math.floor(progress * this.config.steps);
this.setStep(step);
} else {
// Continuous animation
this.setProgress(progress);
}
}
}
/**
* Set step
*/
setStep(step) {
if (step === this.currentStep) {
return;
}
this.currentStep = step;
this.element.setAttribute('data-scroll-step', step.toString());
// Remove old step classes
for (let i = 0; i <= this.config.steps; i++) {
this.element.classList.remove(`step-${i}`);
}
// Add new step class
this.element.classList.add(`step-${step}`);
// Trigger event
this.triggerStepEvent(step);
}
/**
* Set progress (continuous)
*/
setProgress(progress) {
this.element.setAttribute('data-scroll-progress', progress.toString());
this.element.style.setProperty('--scroll-progress', progress.toString());
// Trigger event
this.triggerProgressEvent(progress);
}
/**
* Trigger step event
*/
triggerStepEvent(step) {
const event = new CustomEvent('scroll-timeline:step', {
detail: { step, element: this.element },
bubbles: true
});
this.element.dispatchEvent(event);
}
/**
* Trigger progress event
*/
triggerProgressEvent(progress) {
const event = new CustomEvent('scroll-timeline:progress', {
detail: { progress, element: this.element },
bubbles: true
});
this.element.dispatchEvent(event);
}
/**
* Destroy animation
*/
destroy() {
this.element.removeAttribute('data-scroll-step');
this.element.removeAttribute('data-scroll-progress');
this.element.style.removeProperty('--scroll-progress');
this.element.classList.remove('scroll-timeline');
// Remove step classes
for (let i = 0; i <= 10; i++) {
this.element.classList.remove(`step-${i}`);
}
this.currentStep = 0;
this.triggered = false;
}
}

View File

@@ -0,0 +1,92 @@
/**
* Animation System Module
*
* Unified animation system consolidating all scroll animation modules.
*
* Replaces:
* - scrollfx
* - parallax
* - scroll-timeline
* - scroll-loop
* - scroll-dependent
* - sticky-fade
* - sticky-steps
*
* Usage:
* - Add data-module="animation-system" to enable animations
* - Or import and use directly: import { AnimationSystem } from './modules/animation-system/index.js'
*
* Features:
* - Fade-in animations
* - Parallax effects
* - Scroll timeline animations
* - Sticky fade animations
* - Sticky steps animations
* - IntersectionObserver support
* - Backward compatibility with old modules
*/
import { Logger } from '../../core/logger.js';
import { AnimationSystem } from './AnimationSystem.js';
import { ScrollAnimation } from './ScrollAnimation.js';
import { TimelineAnimation } from './TimelineAnimation.js';
const AnimationSystemModule = {
name: 'animation-system',
animationSystem: null,
init(config = {}, state = null) {
Logger.info('[AnimationSystemModule] Module initialized');
// Create animation system
this.animationSystem = AnimationSystem.create(config);
// Expose globally for easy access
if (typeof window !== 'undefined') {
window.AnimationSystem = this.animationSystem;
}
return this;
},
/**
* Get animation system instance
*/
getAnimationSystem() {
return this.animationSystem || AnimationSystem.create();
},
/**
* Register animation
*/
registerAnimation(element, config) {
const system = this.getAnimationSystem();
system.registerAnimation(element, config);
},
destroy() {
if (this.animationSystem) {
this.animationSystem.destroy();
this.animationSystem = null;
}
if (typeof window !== 'undefined' && window.AnimationSystem) {
delete window.AnimationSystem;
}
Logger.info('[AnimationSystemModule] Module destroyed');
}
};
// Export for direct usage
export { AnimationSystem, ScrollAnimation, TimelineAnimation };
// Export as default for module system
export default AnimationSystemModule;
// Export init function for module system
export const init = AnimationSystemModule.init.bind(AnimationSystemModule);
// Backward compatibility exports
export { createTrigger } from '../scrollfx/index.js';