feat(Production): Complete production deployment infrastructure

- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
This commit is contained in:
2025-10-25 19:18:37 +02:00
parent caa85db796
commit fc3d7e6357
83016 changed files with 378904 additions and 20919 deletions

View File

@@ -1,26 +1,33 @@
/**
* CSRF Token Auto-Refresh System
*
*
* Automatically refreshes CSRF tokens before they expire to prevent
* form submission errors when users keep forms open for extended periods.
*
*
* Features:
* - Automatic token refresh every 105 minutes (15 minutes before expiry)
* - Support for both regular forms and LiveComponents
* - Visual feedback when tokens are refreshed
* - Graceful error handling with fallback strategies
* - Page visibility optimization (pause when tab is inactive)
* - Multiple form support
*
*
* Usage:
* import { CsrfAutoRefresh } from './modules/csrf-auto-refresh.js';
*
*
* // Initialize for contact form
* const csrfRefresh = new CsrfAutoRefresh({
* formId: 'contact_form',
* refreshInterval: 105 * 60 * 1000 // 105 minutes
* });
*
* // Or auto-detect all forms with CSRF tokens
*
* // Initialize for a LiveComponent
* const liveRefresh = new CsrfAutoRefresh({
* formId: 'counter:demo', // Component ID
* refreshInterval: 105 * 60 * 1000
* });
*
* // Or auto-detect all forms and LiveComponents with CSRF tokens
* CsrfAutoRefresh.initializeAll();
*/
@@ -185,11 +192,13 @@ export class CsrfAutoRefresh {
/**
* Update CSRF token in all forms on the page
* Supports both regular forms and LiveComponent data-csrf-token attributes
*/
updateTokenInForms(newToken) {
const tokenInputs = document.querySelectorAll(this.config.tokenSelector);
let updatedCount = 0;
// Update regular form input tokens
const tokenInputs = document.querySelectorAll(this.config.tokenSelector);
tokenInputs.forEach(input => {
// Check if this token belongs to our form
const form = input.closest('form');
@@ -202,10 +211,27 @@ export class CsrfAutoRefresh {
}
});
this.log(`Updated ${updatedCount} token input(s)`);
// Update LiveComponent data-csrf-token attributes
// LiveComponents use form ID format: "livecomponent:{componentId}"
const liveComponentFormId = 'livecomponent:' + this.config.formId.replace(/^livecomponent:/, '');
const liveComponents = document.querySelectorAll('[data-live-component][data-csrf-token]');
liveComponents.forEach(component => {
// Check if this component uses our form ID
const componentId = component.dataset.liveComponent;
const expectedFormId = 'livecomponent:' + componentId;
if (expectedFormId === liveComponentFormId || this.config.formId === componentId) {
component.dataset.csrfToken = newToken;
updatedCount++;
this.log(`Updated LiveComponent token: ${componentId}`);
}
});
this.log(`Updated ${updatedCount} token(s) (forms + LiveComponents)`);
if (updatedCount === 0) {
console.warn('CsrfAutoRefresh: No token inputs found to update. Check your selectors.');
console.warn('CsrfAutoRefresh: No tokens found to update. Check your selectors and formId.');
}
return updatedCount;
@@ -336,12 +362,13 @@ export class CsrfAutoRefresh {
/**
* Static method to initialize auto-refresh for all forms with CSRF tokens
* Supports both regular forms and LiveComponents
*/
static initializeAll() {
const tokenInputs = document.querySelectorAll('input[name="_token"]');
const formIds = new Set();
// Collect unique form IDs
// Collect unique form IDs from regular forms
const tokenInputs = document.querySelectorAll('input[name="_token"]');
tokenInputs.forEach(input => {
const form = input.closest('form');
if (form) {
@@ -352,14 +379,24 @@ export class CsrfAutoRefresh {
}
});
// Initialize auto-refresh for each unique form ID
// Collect unique component IDs from LiveComponents
const liveComponents = document.querySelectorAll('[data-live-component][data-csrf-token]');
liveComponents.forEach(component => {
const componentId = component.dataset.liveComponent;
if (componentId) {
// Use the component ID directly (without "livecomponent:" prefix for config)
formIds.add(componentId);
}
});
// Initialize auto-refresh for each unique form/component ID
const instances = [];
formIds.forEach(formId => {
const instance = new CsrfAutoRefresh({ formId });
instances.push(instance);
});
console.log(`CsrfAutoRefresh: Initialized for ${instances.length} forms:`, Array.from(formIds));
console.log(`CsrfAutoRefresh: Initialized for ${instances.length} forms/components:`, Array.from(formIds));
return instances;
}
}