librelad d39852aa3d refactor(webui): reorganize into components/ + core/ taxonomy
Final modularization layout (user-chosen): every page is a self-contained
folder under components/<id>/ (controllers + CSS + its html fragment), and all
shared/framework code folds into core/:
  core/kernel  (feature-registry, lifecycle, services, spa)
  core/boot    (auth, system-loader/orchestrator, setup, loaders)
  core/lib     (data-loader, router, helpers, the task kernel, shared modules)
  core/ui      (topbar, modal, notifications, … + topbar.html)
  core/css     (all shared stylesheets)
  core/icons
Top level is now just: components/, core/, themes/, index.html (+ runtime data/).

Every path reference rewritten (index.html, scripts arrays, fetch()/
loadFragment()/loadScript() literals, system-loader + config-manager controller
paths, kernel manifest URL, feature.json, backend FEATURES_DIR). The
/api/features/list endpoint NAME is unchanged (it now scans components/).
Deleted 3 dead files (app-content.html, apps-content.html, html-cache.js).
Verified: 0 stale prefixes, 0 double-rewrites, all JS/JSON valid.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 07:13:52 +01:00

249 lines
8.5 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Config Validation System for LibrePortal Web UI
// Validates JSON config files before loading the main interface
// Global validator instance
window.ConfigValidator = function() {
let validationResults = null;
let hasValidated = false;
// Validate all config files
this.validateAllConfigs = async function() {
// Use client-side validation directly
return this.fallbackValidation();
};
// Fallback client-side validation
this.fallbackValidation = async function() {
const results = {
valid: true,
errors: [],
warnings: [],
suggestions: []
};
// Check if unified config file exists (file existence check only)
const configFiles = [
{ name: 'Unified System Config', path: '/data/config/generated/configs.json' }
];
for (const config of configFiles) {
try {
const response = await fetch(config.path, { method: 'HEAD' });
if (!response.ok) {
results.valid = false;
results.errors.push(`Config file '${config.name}' not found: ${config.path}`);
continue;
}
//console.log(`Config file exists: ${config.name}`);
} catch (error) {
results.valid = false;
results.errors.push(`Failed to check ${config.name}: ${error.message}`);
}
}
// Add suggestions if there are issues
if (!results.valid) {
results.suggestions = [
"Run 'libreportal run' to fix configuration issues",
"Check config file permissions and ownership",
"Ensure Docker is running and accessible"
];
}
validationResults = results;
hasValidated = true;
return results;
};
// Show validation error message
this.showValidationError = function() {
if (!hasValidated) {
return;
}
if (validationResults.valid) {
return; // No error to show
}
// Create error overlay
const errorOverlay = document.createElement('div');
errorOverlay.className = 'config-validation-overlay';
errorOverlay.innerHTML = `
<div class="config-validation-dialog">
<div class="validation-header">
<h3>⚠️ Configuration Issues Detected</h3>
<button class="close-btn" onclick="this.parentElement.parentElement.remove()">×</button>
</div>
<div class="validation-content">
<div class="validation-errors">
<h4>Errors:</h4>
<ul>
${validationResults.errors.map(error => `<li>${this.escapeHtml(error)}</li>`).join('')}
</ul>
</div>
${validationResults.warnings.length > 0 ? `
<div class="validation-warnings">
<h4>Warnings:</h4>
<ul>
${validationResults.warnings.map(warning => `<li>${this.escapeHtml(warning)}</li>`).join('')}
</ul>
</div>
` : ''}
<div class="validation-suggestions">
<h4>Suggestions:</h4>
<ul>
${validationResults.suggestions.map(suggestion => `<li>${this.escapeHtml(suggestion)}</li>`).join('')}
</ul>
</div>
<div class="validation-actions">
<button class="primary-btn" onclick="window.location.reload()">Reload</button>
<button class="secondary-btn" onclick="window.open('https://github.com/your-repo/wiki/troubleshooting', '_blank')">Help</button>
</div>
</div>
</div>
`;
// Add styles
errorOverlay.innerHTML += `
<style>
.config-validation-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
display: flex;
align-items: center;
justify-content: center;
z-index: 10000;
}
.config-validation-dialog {
background: var(--surface-color);
border-radius: 12px;
padding: 24px;
max-width: 600px;
width: 90%;
max-height: 80vh;
overflow-y: auto;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
}
.validation-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
padding-bottom: 16px;
border-bottom: 1px solid var(--border-color);
}
.validation-header h3 {
margin: 0;
color: var(--error-color);
font-size: 18px;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: var(--text-color);
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
.validation-errors h4 {
color: var(--error-color);
margin-bottom: 8px;
}
.validation-warnings h4 {
color: var(--warning-color);
margin-bottom: 8px;
}
.validation-content ul {
margin: 0 0 16px 0;
padding-left: 20px;
}
.validation-content li {
margin-bottom: 8px;
line-height: 1.4;
}
.validation-suggestions {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid var(--border-color);
}
.validation-suggestions h4 {
color: var(--success-color);
margin-bottom: 8px;
}
.validation-actions {
display: flex;
gap: 12px;
margin-top: 24px;
justify-content: flex-end;
}
.primary-btn, .secondary-btn {
padding: 10px 20px;
border-radius: 6px;
border: none;
cursor: pointer;
font-size: 14px;
font-weight: 500;
}
.primary-btn {
background: var(--primary-color);
color: white;
}
.secondary-btn {
background: var(--surface-color);
color: var(--text-color);
border: 1px solid var(--border-color);
}
</style>
`;
document.body.appendChild(errorOverlay);
};
// Escape HTML to prevent XSS
this.escapeHtml = function(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
};
// Get validation status
this.isValid = function() {
return hasValidated && validationResults.valid;
};
// Get validation errors
this.getErrors = function() {
return hasValidated ? validationResults.errors : [];
};
// Get validation warnings
this.getWarnings = function() {
return hasValidated ? validationResults.warnings : [];
};
};