feat: Add complete Docker deployment with web-based setup wizard

Major additions:
- Web-based setup wizard (setup.php, setup_wizard.php, setup-wizard.js)
- Production Docker configuration (docker-compose.prod.yml, .env.production)
- Database initialization SQL files (deploy/init_settings.sql)
- Template builder system with drag-and-drop UI
- Advanced features (OAuth, CDN, enhanced analytics, monetization)
- Comprehensive documentation (deployment guides, quick start, feature docs)
- Design system with accessibility and responsive layout
- Deployment automation scripts (deploy.ps1, generate-secrets.ps1)

Setup wizard allows customization of:
- Platform name and branding
- Domain configuration
- Membership tiers and pricing
- Admin credentials
- Feature toggles

Database includes 270+ tables for complete video streaming platform with
advanced features for analytics, moderation, template building, and monetization.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
SamiAhmed7777
2025-10-26 01:42:31 -07:00
parent 0b7e2d0a5b
commit d22b3e1c0d
90 changed files with 22329 additions and 268 deletions

View File

@@ -0,0 +1,907 @@
/**
* Template Builder Styles
* Drag and drop template builder interface
*/
/* ==========================================================================
Variables
========================================================================== */
:root {
--tb-primary: #3b82f6;
--tb-success: #10b981;
--tb-danger: #ef4444;
--tb-warning: #f59e0b;
--tb-secondary: #6b7280;
--tb-bg: #ffffff;
--tb-bg-secondary: #f9fafb;
--tb-bg-tertiary: #f3f4f6;
--tb-border: #e5e7eb;
--tb-text: #111827;
--tb-text-secondary: #6b7280;
--tb-header-height: 60px;
--tb-sidebar-width: 280px;
--tb-toolbar-height: 50px;
--tb-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--tb-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
--tb-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
--tb-radius: 6px;
--tb-radius-sm: 4px;
--tb-radius-lg: 8px;
--tb-transition: all 0.2s ease;
}
/* Dark Mode */
[data-theme*="dark"] .template-builder,
.template-builder[data-theme*="dark"] {
--tb-bg: #1f2937;
--tb-bg-secondary: #111827;
--tb-bg-tertiary: #374151;
--tb-border: #374151;
--tb-text: #f9fafb;
--tb-text-secondary: #9ca3af;
}
/* ==========================================================================
Layout
========================================================================== */
.template-builder {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
background: var(--tb-bg);
color: var(--tb-text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
z-index: 9999;
}
.tb-header {
height: var(--tb-header-height);
background: var(--tb-bg-secondary);
border-bottom: 1px solid var(--tb-border);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
gap: 20px;
}
.tb-header-left,
.tb-header-center,
.tb-header-right {
display: flex;
align-items: center;
gap: 12px;
}
.tb-header-left {
flex: 1;
}
.tb-header-center {
flex: 0 0 auto;
}
.tb-header-right {
flex: 1;
justify-content: flex-end;
}
.tb-main {
flex: 1;
display: flex;
overflow: hidden;
}
/* ==========================================================================
Sidebars
========================================================================== */
.tb-sidebar {
width: var(--tb-sidebar-width);
background: var(--tb-bg-secondary);
border-right: 1px solid var(--tb-border);
display: flex;
flex-direction: column;
overflow: hidden;
transition: var(--tb-transition);
}
.tb-sidebar-right {
border-right: none;
border-left: 1px solid var(--tb-border);
}
.tb-sidebar.collapsed {
width: 0;
border: none;
}
.tb-sidebar-header {
padding: 16px 20px;
border-bottom: 1px solid var(--tb-border);
display: flex;
align-items: center;
justify-content: space-between;
}
.tb-sidebar-header h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
}
.tb-sidebar-toggle {
background: none;
border: none;
color: var(--tb-text-secondary);
cursor: pointer;
padding: 4px;
border-radius: var(--tb-radius-sm);
transition: var(--tb-transition);
}
.tb-sidebar-toggle:hover {
background: var(--tb-bg-tertiary);
color: var(--tb-text);
}
.tb-sidebar-content {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
}
/* ==========================================================================
Canvas
========================================================================== */
.tb-canvas-container {
flex: 1;
display: flex;
flex-direction: column;
background: var(--tb-bg-tertiary);
overflow: hidden;
}
.tb-canvas-toolbar {
height: var(--tb-toolbar-height);
background: var(--tb-bg-secondary);
border-bottom: 1px solid var(--tb-border);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
}
.tb-zoom-controls {
display: flex;
align-items: center;
gap: 8px;
}
.tb-zoom-level {
min-width: 50px;
text-align: center;
font-size: 14px;
color: var(--tb-text-secondary);
}
.tb-canvas-options {
display: flex;
gap: 16px;
}
.tb-canvas-wrapper {
flex: 1;
overflow: auto;
padding: 40px;
}
.tb-canvas {
background: white;
min-height: 100%;
margin: 0 auto;
box-shadow: var(--tb-shadow-lg);
position: relative;
transition: width 0.3s ease;
}
.tb-canvas[data-device="desktop"] {
width: 100%;
max-width: 1400px;
}
.tb-canvas[data-device="tablet"] {
width: 768px;
}
.tb-canvas[data-device="mobile"] {
width: 375px;
}
.tb-canvas.show-grid {
background-image:
linear-gradient(rgba(0, 0, 0, 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 0, 0, 0.03) 1px, transparent 1px);
background-size: 20px 20px;
}
.tb-sections-container {
min-height: 400px;
}
/* Drop Hint */
.tb-drop-hint {
padding: 80px 40px;
text-align: center;
color: var(--tb-text-secondary);
border: 2px dashed var(--tb-border);
border-radius: var(--tb-radius-lg);
margin: 40px;
}
.tb-drop-hint-content i {
font-size: 48px;
margin-bottom: 16px;
opacity: 0.5;
}
.tb-drop-hint-content p {
margin: 0;
font-size: 16px;
}
.tb-drop-hint.hidden {
display: none;
}
/* ==========================================================================
Sections
========================================================================== */
.tb-section {
position: relative;
padding: 20px;
margin: 20px 0;
border: 2px dashed transparent;
transition: var(--tb-transition);
}
.tb-section:hover {
border-color: var(--tb-border);
}
.tb-section.selected {
border-color: var(--tb-primary);
background: rgba(59, 130, 246, 0.05);
}
.tb-section.drag-over {
border-color: var(--tb-success);
background: rgba(16, 185, 129, 0.05);
}
.tb-section-controls {
position: absolute;
top: -12px;
right: 10px;
background: white;
border: 1px solid var(--tb-border);
border-radius: var(--tb-radius);
display: none;
gap: 4px;
padding: 4px;
box-shadow: var(--tb-shadow);
}
.tb-section:hover .tb-section-controls,
.tb-section.selected .tb-section-controls {
display: flex;
}
.tb-columns {
display: grid;
gap: 20px;
}
/* ==========================================================================
Blocks (Components)
========================================================================== */
.tb-block {
position: relative;
min-height: 50px;
border: 2px dashed transparent;
border-radius: var(--tb-radius);
transition: var(--tb-transition);
}
.tb-block:hover {
border-color: var(--tb-border);
}
.tb-block.selected {
border-color: var(--tb-primary);
background: rgba(59, 130, 246, 0.05);
}
.tb-block.dragging {
opacity: 0.5;
}
.tb-block-controls {
position: absolute;
top: -12px;
right: 10px;
background: white;
border: 1px solid var(--tb-border);
border-radius: var(--tb-radius);
display: none;
gap: 4px;
padding: 4px;
box-shadow: var(--tb-shadow);
z-index: 10;
}
.tb-block:hover .tb-block-controls,
.tb-block.selected .tb-block-controls {
display: flex;
}
/* ==========================================================================
Components List (Sidebar)
========================================================================== */
.tb-search-box {
padding: 16px;
position: relative;
}
.tb-search-box input {
width: 100%;
padding: 8px 12px 8px 36px;
border: 1px solid var(--tb-border);
border-radius: var(--tb-radius);
background: var(--tb-bg);
color: var(--tb-text);
font-size: 14px;
}
.tb-search-box i {
position: absolute;
left: 28px;
top: 50%;
transform: translateY(-50%);
color: var(--tb-text-secondary);
}
.tb-component-categories {
padding: 0 16px 16px;
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.tb-category-btn {
padding: 6px 12px;
border: 1px solid var(--tb-border);
background: var(--tb-bg);
color: var(--tb-text);
border-radius: var(--tb-radius);
font-size: 13px;
cursor: pointer;
transition: var(--tb-transition);
}
.tb-category-btn:hover {
background: var(--tb-bg-tertiary);
}
.tb-category-btn.active {
background: var(--tb-primary);
border-color: var(--tb-primary);
color: white;
}
.tb-components-list {
padding: 0 16px 16px;
}
.tb-component-item {
padding: 12px;
border: 1px solid var(--tb-border);
border-radius: var(--tb-radius);
margin-bottom: 12px;
cursor: grab;
background: var(--tb-bg);
transition: var(--tb-transition);
}
.tb-component-item:hover {
border-color: var(--tb-primary);
box-shadow: var(--tb-shadow);
}
.tb-component-item:active {
cursor: grabbing;
}
.tb-component-item.dragging {
opacity: 0.5;
}
.tb-component-thumb {
width: 100%;
height: 100px;
background: var(--tb-bg-tertiary);
border-radius: var(--tb-radius-sm);
margin-bottom: 8px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.tb-component-thumb img {
max-width: 100%;
max-height: 100%;
}
.tb-component-thumb i {
font-size: 32px;
color: var(--tb-text-secondary);
}
.tb-component-name {
font-size: 14px;
font-weight: 500;
margin: 0 0 4px;
}
.tb-component-desc {
font-size: 12px;
color: var(--tb-text-secondary);
margin: 0;
}
/* ==========================================================================
Properties Panel
========================================================================== */
.tb-no-selection {
padding: 40px 20px;
text-align: center;
color: var(--tb-text-secondary);
}
.tb-no-selection i {
font-size: 48px;
margin-bottom: 12px;
opacity: 0.5;
}
.tb-properties-section {
padding: 20px;
}
.tb-properties-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
.tb-properties-header h4 {
margin: 0;
font-size: 14px;
font-weight: 600;
}
/* Form Elements */
.tb-form-group {
margin-bottom: 16px;
}
.tb-form-group label {
display: block;
font-size: 13px;
font-weight: 500;
margin-bottom: 6px;
color: var(--tb-text);
}
.tb-input,
.tb-select,
.tb-textarea {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--tb-border);
border-radius: var(--tb-radius);
background: var(--tb-bg);
color: var(--tb-text);
font-size: 14px;
font-family: inherit;
transition: var(--tb-transition);
}
.tb-input:focus,
.tb-select:focus,
.tb-textarea:focus {
outline: none;
border-color: var(--tb-primary);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.tb-input-sm {
padding: 6px 8px;
font-size: 13px;
}
.tb-textarea {
resize: vertical;
min-height: 80px;
}
input[type="color"].tb-input {
height: 40px;
padding: 4px;
}
.tb-checkbox {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
cursor: pointer;
user-select: none;
}
.tb-checkbox input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
.tb-spacing-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
/* Collapsible */
.tb-collapsible {
border-top: 1px solid var(--tb-border);
margin-top: 16px;
padding-top: 16px;
}
.tb-collapsible-header {
width: 100%;
padding: 8px 0;
background: none;
border: none;
text-align: left;
font-size: 13px;
font-weight: 600;
color: var(--tb-text);
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
}
.tb-collapsible-header i {
transition: transform 0.2s;
}
.tb-collapsible.open .tb-collapsible-header i {
transform: rotate(180deg);
}
.tb-collapsible-content {
display: none;
padding-top: 12px;
}
.tb-collapsible.open .tb-collapsible-content {
display: block;
}
/* ==========================================================================
Buttons
========================================================================== */
.tb-btn {
padding: 8px 16px;
border: 1px solid transparent;
border-radius: var(--tb-radius);
font-size: 14px;
font-weight: 500;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 6px;
transition: var(--tb-transition);
white-space: nowrap;
}
.tb-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.tb-btn-primary {
background: var(--tb-primary);
color: white;
}
.tb-btn-primary:hover:not(:disabled) {
background: #2563eb;
}
.tb-btn-success {
background: var(--tb-success);
color: white;
}
.tb-btn-success:hover:not(:disabled) {
background: #059669;
}
.tb-btn-danger {
background: var(--tb-danger);
color: white;
}
.tb-btn-danger:hover:not(:disabled) {
background: #dc2626;
}
.tb-btn-secondary {
background: var(--tb-bg-tertiary);
color: var(--tb-text);
border-color: var(--tb-border);
}
.tb-btn-secondary:hover:not(:disabled) {
background: var(--tb-bg-secondary);
}
.tb-btn-icon {
padding: 8px;
min-width: 36px;
justify-content: center;
}
.tb-btn-sm {
padding: 6px 12px;
font-size: 13px;
}
.tb-btn i {
font-size: 16px;
}
/* Device Preview Buttons */
.tb-device-preview {
display: flex;
gap: 4px;
background: var(--tb-bg-tertiary);
padding: 4px;
border-radius: var(--tb-radius);
}
.tb-device-btn {
padding: 8px 12px;
background: transparent;
border: none;
color: var(--tb-text-secondary);
cursor: pointer;
border-radius: var(--tb-radius-sm);
transition: var(--tb-transition);
}
.tb-device-btn:hover {
color: var(--tb-text);
}
.tb-device-btn.active {
background: var(--tb-primary);
color: white;
}
/* Template Name Input */
.tb-template-name-input {
padding: 6px 12px;
border: 1px solid transparent;
border-radius: var(--tb-radius);
background: var(--tb-bg-tertiary);
color: var(--tb-text);
font-size: 15px;
font-weight: 500;
max-width: 300px;
transition: var(--tb-transition);
}
.tb-template-name-input:focus {
outline: none;
border-color: var(--tb-primary);
background: var(--tb-bg);
}
/* ==========================================================================
Modal
========================================================================== */
.tb-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 99999;
}
.tb-modal-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
}
.tb-modal-content {
position: relative;
background: var(--tb-bg);
max-width: 500px;
margin: 100px auto;
border-radius: var(--tb-radius-lg);
box-shadow: var(--tb-shadow-lg);
}
.tb-modal-large {
max-width: 90vw;
}
.tb-modal-header {
padding: 20px;
border-bottom: 1px solid var(--tb-border);
display: flex;
align-items: center;
justify-content: space-between;
}
.tb-modal-header h3 {
margin: 0;
font-size: 18px;
}
.tb-modal-close {
background: none;
border: none;
color: var(--tb-text-secondary);
cursor: pointer;
padding: 4px;
border-radius: var(--tb-radius-sm);
transition: var(--tb-transition);
}
.tb-modal-close:hover {
background: var(--tb-bg-tertiary);
color: var(--tb-text);
}
.tb-modal-body {
padding: 20px;
}
.tb-modal-footer {
padding: 20px;
border-top: 1px solid var(--tb-border);
display: flex;
justify-content: flex-end;
gap: 12px;
}
/* ==========================================================================
Loading
========================================================================== */
.tb-loading {
padding: 40px;
text-align: center;
color: var(--tb-text-secondary);
font-size: 14px;
}
/* ==========================================================================
Utilities
========================================================================== */
.hidden {
display: none !important;
}
/* Drag and Drop */
.tb-dragging {
opacity: 0.5;
}
.tb-drag-placeholder {
border: 2px dashed var(--tb-primary);
background: rgba(59, 130, 246, 0.05);
border-radius: var(--tb-radius);
min-height: 100px;
margin: 10px 0;
}
/* Scrollbar */
.tb-sidebar-content::-webkit-scrollbar,
.tb-canvas-wrapper::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.tb-sidebar-content::-webkit-scrollbar-track,
.tb-canvas-wrapper::-webkit-scrollbar-track {
background: var(--tb-bg-secondary);
}
.tb-sidebar-content::-webkit-scrollbar-thumb,
.tb-canvas-wrapper::-webkit-scrollbar-thumb {
background: var(--tb-border);
border-radius: 4px;
}
.tb-sidebar-content::-webkit-scrollbar-thumb:hover,
.tb-canvas-wrapper::-webkit-scrollbar-thumb:hover {
background: var(--tb-text-secondary);
}
/* ==========================================================================
Responsive
========================================================================== */
@media (max-width: 1200px) {
.tb-sidebar {
width: 240px;
}
}
@media (max-width: 768px) {
.tb-header {
padding: 0 12px;
}
.tb-sidebar {
position: absolute;
top: var(--tb-header-height);
bottom: 0;
z-index: 100;
box-shadow: var(--tb-shadow-lg);
}
.tb-sidebar-left {
left: 0;
}
.tb-sidebar-right {
right: 0;
}
.tb-canvas-wrapper {
padding: 20px;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,423 @@
/**
* EasyStream Setup Wizard - Frontend Logic
* Handles wizard navigation, form validation, and AJAX submissions
*/
let currentStep = 1;
const totalSteps = 9;
let setupData = {};
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
updateProgressBar();
attachEventListeners();
loadSavedData();
});
function attachEventListeners() {
// Color picker updates
document.getElementById('primaryColor').addEventListener('input', function(e) {
document.getElementById('primaryColorHex').textContent = e.target.value;
});
document.getElementById('secondaryColor').addEventListener('input', function(e) {
document.getElementById('secondaryColorHex').textContent = e.target.value;
});
// Save form data on input
const inputs = document.querySelectorAll('input, select, textarea');
inputs.forEach(input => {
input.addEventListener('change', saveFormData);
});
}
function changeStep(direction) {
const nextStep = currentStep + direction;
// Validate before moving forward
if (direction > 0) {
if (!validateCurrentStep()) {
return;
}
}
// Special handling for certain steps
if (currentStep === 7 && direction > 0) {
// Review step - show summary and install
showReviewSummary();
startInstallation();
}
if (nextStep >= 1 && nextStep <= totalSteps) {
// Hide current step
document.getElementById(`step${currentStep}`).classList.remove('active');
// Show next step
currentStep = nextStep;
document.getElementById(`step${currentStep}`).classList.add('active');
// Update progress bar
updateProgressBar();
// Update button visibility
updateButtons();
// Scroll to top
document.querySelector('.wizard-body').scrollTop = 0;
}
}
function updateProgressBar() {
const progress = ((currentStep - 1) / (totalSteps - 1)) * 100;
document.getElementById('progressBar').style.width = progress + '%';
}
function updateButtons() {
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
// Show/hide previous button
prevBtn.style.display = currentStep > 1 && currentStep < 8 ? 'block' : 'none';
// Update next button text and visibility
if (currentStep === 1) {
nextBtn.textContent = 'Get Started →';
} else if (currentStep === 7) {
nextBtn.textContent = 'Install Now →';
} else if (currentStep === 8 || currentStep === 9) {
nextBtn.style.display = 'none';
} else {
nextBtn.textContent = 'Next →';
}
}
function validateCurrentStep() {
clearAlert();
switch (currentStep) {
case 1:
// Welcome page, no validation needed
return true;
case 2:
// Platform configuration
const platformName = document.getElementById('platformName').value.trim();
const domainName = document.getElementById('domainName').value.trim();
const contactEmail = document.getElementById('contactEmail').value.trim();
if (!platformName) {
showAlert('error', 'Please enter a platform name');
return false;
}
if (!domainName) {
showAlert('error', 'Please enter a domain name');
return false;
}
if (!contactEmail || !isValidEmail(contactEmail)) {
showAlert('error', 'Please enter a valid contact email');
return false;
}
return true;
case 3:
// Branding, no strict validation
return true;
case 4:
// Membership tiers
const tier1Name = document.getElementById('tier1Name').value.trim();
const tier2Name = document.getElementById('tier2Name').value.trim();
const tier3Name = document.getElementById('tier3Name').value.trim();
if (!tier1Name || !tier2Name || !tier3Name) {
showAlert('error', 'Please enter names for all membership tiers');
return false;
}
return true;
case 5:
// Admin account
const username = document.getElementById('adminUsername').value.trim();
const email = document.getElementById('adminEmail').value.trim();
const password = document.getElementById('adminPassword').value;
const passwordConfirm = document.getElementById('adminPasswordConfirm').value;
if (!username || username.length < 4) {
showAlert('error', 'Username must be at least 4 characters');
return false;
}
if (!/^[a-zA-Z0-9_]+$/.test(username)) {
showAlert('error', 'Username can only contain letters, numbers, and underscores');
return false;
}
if (!email || !isValidEmail(email)) {
showAlert('error', 'Please enter a valid admin email');
return false;
}
if (!password || password.length < 8) {
showAlert('error', 'Password must be at least 8 characters long');
return false;
}
if (!/[A-Z]/.test(password) || !/[a-z]/.test(password) || !/[0-9]/.test(password)) {
showAlert('error', 'Password must contain uppercase, lowercase, and numbers');
return false;
}
if (password !== passwordConfirm) {
showAlert('error', 'Passwords do not match');
return false;
}
return true;
case 6:
// Features, no strict validation
return true;
default:
return true;
}
}
function isValidEmail(email) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email);
}
function saveFormData() {
// Save to localStorage for recovery
const formData = collectFormData();
localStorage.setItem('easystreamSetup', JSON.stringify(formData));
}
function loadSavedData() {
// Load from localStorage if available
const saved = localStorage.getItem('easystreamSetup');
if (saved) {
try {
const data = JSON.parse(saved);
// Populate form fields
Object.keys(data).forEach(key => {
const element = document.getElementById(key);
if (element) {
if (element.type === 'checkbox') {
element.checked = data[key];
} else {
element.value = data[key];
}
}
});
} catch (e) {
console.error('Failed to load saved data:', e);
}
}
}
function collectFormData() {
const formData = {};
// Platform config
formData.platformName = document.getElementById('platformName').value;
formData.platformTagline = document.getElementById('platformTagline').value;
formData.domainName = document.getElementById('domainName').value;
formData.contactEmail = document.getElementById('contactEmail').value;
formData.timezone = document.getElementById('timezone').value;
// Branding
formData.primaryColor = document.getElementById('primaryColor').value;
formData.secondaryColor = document.getElementById('secondaryColor').value;
formData.defaultTheme = document.getElementById('defaultTheme').value;
formData.enableTheming = document.getElementById('enableTheming').checked;
// Membership tiers
formData.tier1Name = document.getElementById('tier1Name').value;
formData.tier1Upload = document.getElementById('tier1Upload').value;
formData.tier1Storage = document.getElementById('tier1Storage').value;
formData.tier2Name = document.getElementById('tier2Name').value;
formData.tier2Upload = document.getElementById('tier2Upload').value;
formData.tier2Storage = document.getElementById('tier2Storage').value;
formData.tier2Price = document.getElementById('tier2Price').value;
formData.tier3Name = document.getElementById('tier3Name').value;
formData.tier3Upload = document.getElementById('tier3Upload').value;
formData.tier3Storage = document.getElementById('tier3Storage').value;
formData.tier3Price = document.getElementById('tier3Price').value;
// Admin account
formData.adminUsername = document.getElementById('adminUsername').value;
formData.adminEmail = document.getElementById('adminEmail').value;
formData.adminPassword = document.getElementById('adminPassword').value;
formData.adminPasswordConfirm = document.getElementById('adminPasswordConfirm').value;
formData.adminDisplayName = document.getElementById('adminDisplayName').value;
// Features
formData.enableRegistration = document.getElementById('enableRegistration').checked;
formData.enableEmailVerification = document.getElementById('enableEmailVerification').checked;
formData.enableLiveStreaming = document.getElementById('enableLiveStreaming').checked;
formData.enableComments = document.getElementById('enableComments').checked;
formData.enableDownloads = document.getElementById('enableDownloads').checked;
formData.enableMonetization = document.getElementById('enableMonetization').checked;
formData.enableTemplateBuilder = document.getElementById('enableTemplateBuilder').checked;
formData.enableAnalytics = document.getElementById('enableAnalytics').checked;
return formData;
}
function showReviewSummary() {
const data = collectFormData();
const summary = document.getElementById('reviewSummary');
summary.innerHTML = `
<div style="margin-bottom: 24px;">
<h3 style="color: #667eea; margin-bottom: 12px;">Platform Configuration</h3>
<p><strong>Name:</strong> ${data.platformName}</p>
<p><strong>Domain:</strong> ${data.domainName}</p>
<p><strong>Email:</strong> ${data.contactEmail}</p>
<p><strong>Timezone:</strong> ${data.timezone}</p>
</div>
<div style="margin-bottom: 24px;">
<h3 style="color: #667eea; margin-bottom: 12px;">Membership Tiers</h3>
<p><strong>${data.tier1Name}:</strong> ${data.tier1Upload}MB upload, ${data.tier1Storage}GB storage</p>
<p><strong>${data.tier2Name}:</strong> ${data.tier2Upload}MB upload, ${data.tier2Storage}GB storage ($${data.tier2Price}/month)</p>
<p><strong>${data.tier3Name}:</strong> ${data.tier3Upload}MB upload, ${data.tier3Storage}GB storage ($${data.tier3Price}/month)</p>
</div>
<div style="margin-bottom: 24px;">
<h3 style="color: #667eea; margin-bottom: 12px;">Admin Account</h3>
<p><strong>Username:</strong> ${data.adminUsername}</p>
<p><strong>Email:</strong> ${data.adminEmail}</p>
<p><strong>Display Name:</strong> ${data.adminDisplayName || 'Administrator'}</p>
</div>
<div>
<h3 style="color: #667eea; margin-bottom: 12px;">Enabled Features</h3>
<p>${data.enableRegistration ? '✓' : '✗'} User Registration</p>
<p>${data.enableLiveStreaming ? '✓' : '✗'} Live Streaming</p>
<p>${data.enableComments ? '✓' : '✗'} Video Comments</p>
<p>${data.enableMonetization ? '✓' : '✗'} Monetization</p>
<p>${data.enableTemplateBuilder ? '✓' : '✗'} Template Builder</p>
<p>${data.enableAnalytics ? '✓' : '✗'} Analytics</p>
</div>
`;
}
async function startInstallation() {
// Move to installation step
document.getElementById('step7').classList.remove('active');
currentStep = 8;
document.getElementById('step8').classList.add('active');
updateProgressBar();
updateButtons();
const data = collectFormData();
setupData = data;
try {
// Step 1: Save configuration
await installStep('Saving configuration...', async () => {
const response = await fetch('setup.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ action: 'save_configuration', ...data })
});
return await response.json();
});
// Step 2: Create admin user
await installStep('Creating admin account...', async () => {
const response = await fetch('setup.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ action: 'create_admin', ...data })
});
return await response.json();
});
// Step 3: Finalize setup
await installStep('Finalizing setup...', async () => {
const response = await fetch('setup.php', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ action: 'finalize', ...data })
});
return await response.json();
});
// Success! Move to final step
document.getElementById('step8').classList.remove('active');
currentStep = 9;
document.getElementById('step9').classList.add('active');
updateProgressBar();
// Populate final details
document.getElementById('finalPlatformName').textContent = data.platformName;
document.getElementById('finalDomainName').textContent = data.domainName;
document.getElementById('finalAdminUsername').textContent = data.adminUsername;
// Clear saved data
localStorage.removeItem('easystreamSetup');
} catch (error) {
document.getElementById('installStatus').textContent = 'Installation Failed';
document.getElementById('installStep').innerHTML = `
<div style="color: #e53e3e; padding: 20px; background: #fed7d7; border-radius: 8px;">
<strong>Error:</strong> ${error.message}
<br><br>
<button onclick="location.reload()" class="btn btn-primary">Try Again</button>
</div>
`;
}
}
async function installStep(message, action) {
document.getElementById('installStep').textContent = message;
// Add progress item
const progress = document.getElementById('installProgress');
const item = document.createElement('div');
item.style.cssText = 'padding: 12px; background: #f7fafc; border-radius: 6px; margin-bottom: 8px; display: flex; align-items: center;';
item.innerHTML = `<span class="spinner" style="width: 16px; height: 16px; margin-right: 12px; border-width: 2px;"></span><span>${message}</span>`;
progress.appendChild(item);
try {
const result = await action();
if (!result.success) {
throw new Error(result.error || 'Unknown error occurred');
}
// Update to success
item.innerHTML = `<span style="color: #48bb78; font-size: 20px; margin-right: 12px;">✓</span><span>${message} <strong style="color: #48bb78;">Done!</strong></span>`;
return result;
} catch (error) {
item.innerHTML = `<span style="color: #e53e3e; font-size: 20px; margin-right: 12px;">✗</span><span>${message} <strong style="color: #e53e3e;">Failed!</strong></span>`;
throw error;
}
}
function showAlert(type, message) {
const alert = document.getElementById('alert');
alert.className = `alert alert-${type} show`;
alert.textContent = message;
// Auto-hide after 5 seconds
setTimeout(() => {
alert.classList.remove('show');
}, 5000);
}
function clearAlert() {
const alert = document.getElementById('alert');
alert.classList.remove('show');
}