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:
245
resources/js/modules/livecomponent/StateSerializer.js
Normal file
245
resources/js/modules/livecomponent/StateSerializer.js
Normal file
@@ -0,0 +1,245 @@
|
||||
/**
|
||||
* State Serializer for LiveComponents
|
||||
*
|
||||
* Provides type-safe state serialization/deserialization with versioning
|
||||
* and validation.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Serialize component state
|
||||
*
|
||||
* @param {LiveComponentState} state - Component state
|
||||
* @returns {string} Serialized state JSON
|
||||
*/
|
||||
export function serializeState(state) {
|
||||
// Validate state structure
|
||||
if (!state || typeof state !== 'object') {
|
||||
throw new Error('State must be an object');
|
||||
}
|
||||
|
||||
if (!state.id || typeof state.id !== 'string') {
|
||||
throw new Error('State must have a valid id');
|
||||
}
|
||||
|
||||
if (!state.component || typeof state.component !== 'string') {
|
||||
throw new Error('State must have a valid component name');
|
||||
}
|
||||
|
||||
if (typeof state.version !== 'number' || state.version < 1) {
|
||||
throw new Error('State must have a valid version number');
|
||||
}
|
||||
|
||||
// Ensure data is an object
|
||||
const data = state.data || {};
|
||||
if (typeof data !== 'object' || Array.isArray(data)) {
|
||||
throw new Error('State data must be an object');
|
||||
}
|
||||
|
||||
// Create serializable state
|
||||
const serializableState = {
|
||||
id: state.id,
|
||||
component: state.component,
|
||||
data: data,
|
||||
version: state.version
|
||||
};
|
||||
|
||||
// Serialize to JSON
|
||||
try {
|
||||
return JSON.stringify(serializableState);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to serialize state: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize component state
|
||||
*
|
||||
* @param {string} json - Serialized state JSON
|
||||
* @returns {LiveComponentState} Deserialized state
|
||||
*/
|
||||
export function deserializeState(json) {
|
||||
if (typeof json !== 'string') {
|
||||
throw new Error('State JSON must be a string');
|
||||
}
|
||||
|
||||
if (json.trim() === '') {
|
||||
// Return empty state
|
||||
return {
|
||||
id: '',
|
||||
component: '',
|
||||
data: {},
|
||||
version: 1
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(json);
|
||||
|
||||
// Validate structure
|
||||
if (!parsed || typeof parsed !== 'object') {
|
||||
throw new Error('Invalid state structure');
|
||||
}
|
||||
|
||||
// Extract state data
|
||||
const id = parsed.id || '';
|
||||
const component = parsed.component || '';
|
||||
const data = parsed.data || {};
|
||||
const version = typeof parsed.version === 'number' ? parsed.version : 1;
|
||||
|
||||
// Validate data is an object
|
||||
if (typeof data !== 'object' || Array.isArray(data)) {
|
||||
throw new Error('State data must be an object');
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
component,
|
||||
data,
|
||||
version
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof SyntaxError) {
|
||||
throw new Error(`Invalid JSON format: ${error.message}`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create state diff (changes between two states)
|
||||
*
|
||||
* @param {LiveComponentState} oldState - Previous state
|
||||
* @param {LiveComponentState} newState - New state
|
||||
* @returns {Object} State diff
|
||||
*/
|
||||
export function createStateDiff(oldState, newState) {
|
||||
const diff = {
|
||||
changed: false,
|
||||
added: {},
|
||||
removed: {},
|
||||
modified: {},
|
||||
versionChange: newState.version - oldState.version
|
||||
};
|
||||
|
||||
const oldData = oldState.data || {};
|
||||
const newData = newState.data || {};
|
||||
|
||||
// Find added keys
|
||||
for (const key in newData) {
|
||||
if (!(key in oldData)) {
|
||||
diff.added[key] = newData[key];
|
||||
diff.changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Find removed keys
|
||||
for (const key in oldData) {
|
||||
if (!(key in newData)) {
|
||||
diff.removed[key] = oldData[key];
|
||||
diff.changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Find modified keys
|
||||
for (const key in newData) {
|
||||
if (key in oldData) {
|
||||
const oldValue = oldData[key];
|
||||
const newValue = newData[key];
|
||||
|
||||
// Deep comparison for objects
|
||||
if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) {
|
||||
diff.modified[key] = {
|
||||
old: oldValue,
|
||||
new: newValue
|
||||
};
|
||||
diff.changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge state changes
|
||||
*
|
||||
* @param {LiveComponentState} baseState - Base state
|
||||
* @param {Object} changes - State changes to apply
|
||||
* @returns {LiveComponentState} Merged state
|
||||
*/
|
||||
export function mergeStateChanges(baseState, changes) {
|
||||
const baseData = baseState.data || {};
|
||||
const mergedData = { ...baseData, ...changes };
|
||||
|
||||
return {
|
||||
...baseState,
|
||||
data: mergedData,
|
||||
version: baseState.version + 1
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate state structure
|
||||
*
|
||||
* @param {LiveComponentState} state - State to validate
|
||||
* @returns {boolean} True if valid
|
||||
*/
|
||||
export function validateState(state) {
|
||||
if (!state || typeof state !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!state.id || typeof state.id !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!state.component || typeof state.component !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof state.version !== 'number' || state.version < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state.data && (typeof state.data !== 'object' || Array.isArray(state.data))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get state from DOM element
|
||||
*
|
||||
* @param {HTMLElement} element - Component element
|
||||
* @returns {LiveComponentState|null} State or null if not found
|
||||
*/
|
||||
export function getStateFromElement(element) {
|
||||
const stateJson = element.dataset.state;
|
||||
if (!stateJson) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return deserializeState(stateJson);
|
||||
} catch (error) {
|
||||
console.error('[StateSerializer] Failed to deserialize state from element:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set state on DOM element
|
||||
*
|
||||
* @param {HTMLElement} element - Component element
|
||||
* @param {LiveComponentState} state - State to set
|
||||
*/
|
||||
export function setStateOnElement(element, state) {
|
||||
if (!validateState(state)) {
|
||||
throw new Error('Invalid state structure');
|
||||
}
|
||||
|
||||
const stateJson = serializeState(state);
|
||||
element.dataset.state = stateJson;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user