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
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:
322
resources/js/modules/event-bus/EventBus.js
Normal file
322
resources/js/modules/event-bus/EventBus.js
Normal file
@@ -0,0 +1,322 @@
|
||||
/**
|
||||
* Event Bus Module
|
||||
*
|
||||
* Provides centralized event system for cross-module communication.
|
||||
* Features:
|
||||
* - Pub/sub pattern
|
||||
* - Namespaced events
|
||||
* - Event filtering
|
||||
* - Event history
|
||||
* - Integration with LiveComponents
|
||||
* - Integration with SSE
|
||||
*/
|
||||
|
||||
import { Logger } from '../../core/logger.js';
|
||||
|
||||
/**
|
||||
* EventBus - Centralized event system
|
||||
*/
|
||||
export class EventBus {
|
||||
constructor(config = {}) {
|
||||
this.config = {
|
||||
enableHistory: config.enableHistory ?? false,
|
||||
maxHistorySize: config.maxHistorySize || 100,
|
||||
enableWildcards: config.enableWildcards ?? true,
|
||||
...config
|
||||
};
|
||||
|
||||
this.subscribers = new Map(); // Map<eventName, Set<callback>>
|
||||
this.history = [];
|
||||
this.middleware = [];
|
||||
|
||||
Logger.info('[EventBus] Initialized', {
|
||||
enableHistory: this.config.enableHistory,
|
||||
enableWildcards: this.config.enableWildcards
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new EventBus instance
|
||||
*/
|
||||
static create(config = {}) {
|
||||
return new EventBus(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to an event
|
||||
*/
|
||||
on(eventName, callback, options = {}) {
|
||||
if (typeof callback !== 'function') {
|
||||
throw new Error('Callback must be a function');
|
||||
}
|
||||
|
||||
if (!this.subscribers.has(eventName)) {
|
||||
this.subscribers.set(eventName, new Set());
|
||||
}
|
||||
|
||||
const subscriber = {
|
||||
callback,
|
||||
once: options.once ?? false,
|
||||
priority: options.priority || 0,
|
||||
filter: options.filter || null
|
||||
};
|
||||
|
||||
this.subscribers.get(eventName).add(subscriber);
|
||||
|
||||
// Sort by priority
|
||||
const subscribers = Array.from(this.subscribers.get(eventName));
|
||||
subscribers.sort((a, b) => b.priority - a.priority);
|
||||
this.subscribers.set(eventName, new Set(subscribers));
|
||||
|
||||
// Return unsubscribe function
|
||||
return () => {
|
||||
const callbacks = this.subscribers.get(eventName);
|
||||
if (callbacks) {
|
||||
callbacks.delete(subscriber);
|
||||
if (callbacks.size === 0) {
|
||||
this.subscribers.delete(eventName);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to an event (once)
|
||||
*/
|
||||
once(eventName, callback, options = {}) {
|
||||
return this.on(eventName, callback, { ...options, once: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from an event
|
||||
*/
|
||||
off(eventName, callback) {
|
||||
if (!this.subscribers.has(eventName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const callbacks = this.subscribers.get(eventName);
|
||||
for (const subscriber of callbacks) {
|
||||
if (subscriber.callback === callback) {
|
||||
callbacks.delete(subscriber);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (callbacks.size === 0) {
|
||||
this.subscribers.delete(eventName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit an event
|
||||
*/
|
||||
emit(eventName, data = null, options = {}) {
|
||||
// Apply middleware
|
||||
let processedData = data;
|
||||
for (const middleware of this.middleware) {
|
||||
const result = middleware(eventName, processedData, options);
|
||||
if (result === null || result === false) {
|
||||
return; // Middleware blocked the event
|
||||
}
|
||||
if (result !== undefined) {
|
||||
processedData = result;
|
||||
}
|
||||
}
|
||||
|
||||
// Add to history
|
||||
if (this.config.enableHistory) {
|
||||
this.addToHistory(eventName, processedData, options);
|
||||
}
|
||||
|
||||
// Get subscribers for exact event name
|
||||
const subscribers = this.subscribers.get(eventName);
|
||||
if (subscribers) {
|
||||
this.notifySubscribers(subscribers, eventName, processedData, options);
|
||||
}
|
||||
|
||||
// Handle wildcards
|
||||
if (this.config.enableWildcards) {
|
||||
this.handleWildcards(eventName, processedData, options);
|
||||
}
|
||||
|
||||
// Handle namespaced events (e.g., 'user:created' triggers 'user:*')
|
||||
this.handleNamespacedEvents(eventName, processedData, options);
|
||||
|
||||
Logger.debug('[EventBus] Event emitted', { eventName, data: processedData });
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify subscribers
|
||||
*/
|
||||
notifySubscribers(subscribers, eventName, data, options) {
|
||||
const subscribersArray = Array.from(subscribers);
|
||||
|
||||
for (const subscriber of subscribersArray) {
|
||||
// Apply filter
|
||||
if (subscriber.filter && !subscriber.filter(data, options)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
subscriber.callback(data, eventName, options);
|
||||
} catch (error) {
|
||||
Logger.error('[EventBus] Subscriber error', error);
|
||||
}
|
||||
|
||||
// Remove if once
|
||||
if (subscriber.once) {
|
||||
subscribers.delete(subscriber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle wildcard subscriptions
|
||||
*/
|
||||
handleWildcards(eventName, data, options) {
|
||||
// Check for '*' subscribers
|
||||
const wildcardSubscribers = this.subscribers.get('*');
|
||||
if (wildcardSubscribers) {
|
||||
this.notifySubscribers(wildcardSubscribers, eventName, data, options);
|
||||
}
|
||||
|
||||
// Check for pattern matches (e.g., 'user:*' matches 'user:created')
|
||||
for (const [pattern, subscribers] of this.subscribers.entries()) {
|
||||
if (pattern.includes('*') && this.matchesPattern(eventName, pattern)) {
|
||||
this.notifySubscribers(subscribers, eventName, data, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle namespaced events
|
||||
*/
|
||||
handleNamespacedEvents(eventName, data, options) {
|
||||
const parts = eventName.split(':');
|
||||
if (parts.length > 1) {
|
||||
// Emit namespace wildcard (e.g., 'user:created' triggers 'user:*')
|
||||
const namespacePattern = parts[0] + ':*';
|
||||
const namespaceSubscribers = this.subscribers.get(namespacePattern);
|
||||
if (namespaceSubscribers) {
|
||||
this.notifySubscribers(namespaceSubscribers, eventName, data, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if event name matches pattern
|
||||
*/
|
||||
matchesPattern(eventName, pattern) {
|
||||
const regex = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$');
|
||||
return regex.test(eventName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add middleware
|
||||
*/
|
||||
use(middleware) {
|
||||
if (typeof middleware !== 'function') {
|
||||
throw new Error('Middleware must be a function');
|
||||
}
|
||||
this.middleware.push(middleware);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event to history
|
||||
*/
|
||||
addToHistory(eventName, data, options) {
|
||||
this.history.push({
|
||||
eventName,
|
||||
data,
|
||||
options,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
// Limit history size
|
||||
if (this.history.length > this.config.maxHistorySize) {
|
||||
this.history.shift();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get event history
|
||||
*/
|
||||
getHistory(filter = null) {
|
||||
if (!filter) {
|
||||
return [...this.history];
|
||||
}
|
||||
|
||||
if (typeof filter === 'string') {
|
||||
// Filter by event name
|
||||
return this.history.filter(event => event.eventName === filter);
|
||||
}
|
||||
|
||||
if (typeof filter === 'function') {
|
||||
// Custom filter function
|
||||
return this.history.filter(filter);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear event history
|
||||
*/
|
||||
clearHistory() {
|
||||
this.history = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all event names
|
||||
*/
|
||||
getEventNames() {
|
||||
return Array.from(this.subscribers.keys());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subscriber count for an event
|
||||
*/
|
||||
getSubscriberCount(eventName) {
|
||||
const subscribers = this.subscribers.get(eventName);
|
||||
return subscribers ? subscribers.size : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all subscribers for an event
|
||||
*/
|
||||
removeAllListeners(eventName = null) {
|
||||
if (eventName) {
|
||||
this.subscribers.delete(eventName);
|
||||
} else {
|
||||
this.subscribers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy event bus
|
||||
*/
|
||||
destroy() {
|
||||
this.subscribers.clear();
|
||||
this.history = [];
|
||||
this.middleware = [];
|
||||
|
||||
Logger.info('[EventBus] Destroyed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a global event bus instance
|
||||
*/
|
||||
let globalEventBus = null;
|
||||
|
||||
/**
|
||||
* Get or create global event bus
|
||||
*/
|
||||
export function getGlobalEventBus(config = {}) {
|
||||
if (!globalEventBus) {
|
||||
globalEventBus = EventBus.create(config);
|
||||
}
|
||||
return globalEventBus;
|
||||
}
|
||||
|
||||
93
resources/js/modules/event-bus/index.js
Normal file
93
resources/js/modules/event-bus/index.js
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Event Bus Module
|
||||
*
|
||||
* Provides centralized event system for cross-module communication.
|
||||
*
|
||||
* Usage:
|
||||
* - Add data-module="event-bus" to enable global event bus
|
||||
* - Or import and use directly: import { EventBus } from './modules/event-bus/index.js'
|
||||
*
|
||||
* Features:
|
||||
* - Pub/sub pattern
|
||||
* - Namespaced events
|
||||
* - Event filtering
|
||||
* - Event history
|
||||
* - Integration with LiveComponents
|
||||
* - Integration with SSE
|
||||
*/
|
||||
|
||||
import { Logger } from '../../core/logger.js';
|
||||
import { EventBus, getGlobalEventBus } from './EventBus.js';
|
||||
|
||||
const EventBusModule = {
|
||||
name: 'event-bus',
|
||||
eventBus: null,
|
||||
|
||||
init(config = {}, state = null) {
|
||||
Logger.info('[EventBusModule] Module initialized');
|
||||
|
||||
// Create global event bus
|
||||
this.eventBus = getGlobalEventBus(config);
|
||||
|
||||
// Expose globally for easy access
|
||||
if (typeof window !== 'undefined') {
|
||||
window.EventBus = this.eventBus;
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get event bus instance
|
||||
*/
|
||||
getEventBus() {
|
||||
return this.eventBus || getGlobalEventBus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Emit an event
|
||||
*/
|
||||
emit(eventName, data = null, options = {}) {
|
||||
const bus = this.getEventBus();
|
||||
bus.emit(eventName, data, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Subscribe to an event
|
||||
*/
|
||||
on(eventName, callback, options = {}) {
|
||||
const bus = this.getEventBus();
|
||||
return bus.on(eventName, callback, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Unsubscribe from an event
|
||||
*/
|
||||
off(eventName, callback) {
|
||||
const bus = this.getEventBus();
|
||||
bus.off(eventName, callback);
|
||||
},
|
||||
|
||||
destroy() {
|
||||
if (this.eventBus) {
|
||||
this.eventBus.destroy();
|
||||
this.eventBus = null;
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && window.EventBus) {
|
||||
delete window.EventBus;
|
||||
}
|
||||
|
||||
Logger.info('[EventBusModule] Module destroyed');
|
||||
}
|
||||
};
|
||||
|
||||
// Export for direct usage
|
||||
export { EventBus, getGlobalEventBus };
|
||||
|
||||
// Export as default for module system
|
||||
export default EventBusModule;
|
||||
|
||||
// Export init function for module system
|
||||
export const init = EventBusModule.init.bind(EventBusModule);
|
||||
|
||||
Reference in New Issue
Block a user