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>
289 lines
8.7 KiB
PowerShell
289 lines
8.7 KiB
PowerShell
# ============================================================================
|
|
# EasyStream - Folder Sync Script (Repos -> Docker-Progs)
|
|
# ============================================================================
|
|
# This script syncs changes from E:\repos\easystream-main to E:\docker-progs\easystream-main
|
|
#
|
|
# Usage:
|
|
# .\sync-to-docker-progs.ps1 # One-time sync
|
|
# .\sync-to-docker-progs.ps1 -Watch # Continuous monitoring
|
|
# .\sync-to-docker-progs.ps1 -Verbose # Detailed output
|
|
#
|
|
# Requirements: PowerShell 5.0 or higher
|
|
# ============================================================================
|
|
|
|
param(
|
|
[switch]$Watch = $false,
|
|
[switch]$Verbose = $false,
|
|
[switch]$DryRun = $false
|
|
)
|
|
|
|
# Configuration
|
|
$SourcePath = "E:\repos\easystream-main"
|
|
$DestPath = "E:\docker-progs\easystream-main"
|
|
$LogFile = "E:\repos\easystream-main\sync.log"
|
|
|
|
# Exclusions (paths to ignore)
|
|
$Exclusions = @(
|
|
".git",
|
|
".gitignore",
|
|
"node_modules",
|
|
"vendor",
|
|
"f_data\cache",
|
|
"f_data\tmp",
|
|
"f_data\logs",
|
|
"f_data\sessions",
|
|
"f_data\uploads",
|
|
"*.log",
|
|
"sync.log",
|
|
"sync-to-docker-progs.ps1"
|
|
)
|
|
|
|
# ============================================================================
|
|
# Functions
|
|
# ============================================================================
|
|
|
|
function Write-Log {
|
|
param([string]$Message, [string]$Level = "INFO")
|
|
|
|
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
|
$logMessage = "[$timestamp] [$Level] $Message"
|
|
|
|
# Write to console
|
|
switch ($Level) {
|
|
"ERROR" { Write-Host $logMessage -ForegroundColor Red }
|
|
"WARN" { Write-Host $logMessage -ForegroundColor Yellow }
|
|
"SUCCESS" { Write-Host $logMessage -ForegroundColor Green }
|
|
default { Write-Host $logMessage -ForegroundColor White }
|
|
}
|
|
|
|
# Write to log file
|
|
Add-Content -Path $LogFile -Value $logMessage
|
|
}
|
|
|
|
function Test-ShouldExclude {
|
|
param([string]$Path)
|
|
|
|
foreach ($exclusion in $Exclusions) {
|
|
if ($Path -like "*$exclusion*") {
|
|
return $true
|
|
}
|
|
}
|
|
return $false
|
|
}
|
|
|
|
function Sync-Folder {
|
|
param([bool]$InitialSync = $false)
|
|
|
|
try {
|
|
# Check if source exists
|
|
if (-not (Test-Path $SourcePath)) {
|
|
Write-Log "Source path does not exist: $SourcePath" "ERROR"
|
|
return $false
|
|
}
|
|
|
|
# Create destination if it doesn't exist
|
|
if (-not (Test-Path $DestPath)) {
|
|
Write-Log "Creating destination directory: $DestPath" "INFO"
|
|
if (-not $DryRun) {
|
|
New-Item -ItemType Directory -Path $DestPath -Force | Out-Null
|
|
}
|
|
}
|
|
|
|
# Build robocopy exclusion parameters
|
|
$excludeDirs = @()
|
|
$excludeFiles = @()
|
|
|
|
foreach ($exclusion in $Exclusions) {
|
|
if ($exclusion.Contains("\")) {
|
|
$excludeDirs += $exclusion
|
|
} elseif ($exclusion.StartsWith("*")) {
|
|
$excludeFiles += $exclusion
|
|
} else {
|
|
$excludeDirs += $exclusion
|
|
}
|
|
}
|
|
|
|
# Build robocopy command
|
|
$robocopyArgs = @(
|
|
$SourcePath,
|
|
$DestPath,
|
|
"/MIR", # Mirror (delete files in dest that don't exist in source)
|
|
"/R:3", # Retry 3 times
|
|
"/W:5", # Wait 5 seconds between retries
|
|
"/MT:8", # Multi-threaded (8 threads)
|
|
"/NFL", # No file list
|
|
"/NDL", # No directory list
|
|
"/NP", # No progress
|
|
"/BYTES" # Show sizes in bytes
|
|
)
|
|
|
|
# Add exclusions
|
|
if ($excludeDirs.Count -gt 0) {
|
|
$robocopyArgs += "/XD"
|
|
$robocopyArgs += $excludeDirs
|
|
}
|
|
|
|
if ($excludeFiles.Count -gt 0) {
|
|
$robocopyArgs += "/XF"
|
|
$robocopyArgs += $excludeFiles
|
|
}
|
|
|
|
# Add verbose flag if requested
|
|
if ($Verbose) {
|
|
$robocopyArgs = $robocopyArgs | Where-Object { $_ -ne "/NFL" -and $_ -ne "/NDL" }
|
|
}
|
|
|
|
# Execute sync
|
|
if ($InitialSync) {
|
|
Write-Log "Starting initial sync..." "INFO"
|
|
} else {
|
|
Write-Log "Syncing changes..." "INFO"
|
|
}
|
|
|
|
if ($DryRun) {
|
|
Write-Log "DRY RUN - Would execute: robocopy $($robocopyArgs -join ' ')" "WARN"
|
|
return $true
|
|
}
|
|
|
|
$result = & robocopy $robocopyArgs
|
|
$exitCode = $LASTEXITCODE
|
|
|
|
# Robocopy exit codes:
|
|
# 0 = No files copied
|
|
# 1 = Files copied successfully
|
|
# 2 = Extra files or directories detected
|
|
# 3 = Files copied + extra files detected
|
|
# 4+ = Error
|
|
|
|
if ($exitCode -ge 8) {
|
|
Write-Log "Sync failed with exit code: $exitCode" "ERROR"
|
|
return $false
|
|
} elseif ($exitCode -gt 0) {
|
|
Write-Log "Sync completed successfully (Exit code: $exitCode)" "SUCCESS"
|
|
return $true
|
|
} else {
|
|
if ($Verbose) {
|
|
Write-Log "No changes detected" "INFO"
|
|
}
|
|
return $true
|
|
}
|
|
|
|
} catch {
|
|
Write-Log "Sync error: $($_.Exception.Message)" "ERROR"
|
|
return $false
|
|
}
|
|
}
|
|
|
|
function Start-Watcher {
|
|
Write-Log "Starting file system watcher..." "INFO"
|
|
Write-Log "Monitoring: $SourcePath" "INFO"
|
|
Write-Log "Syncing to: $DestPath" "INFO"
|
|
Write-Log "Press Ctrl+C to stop..." "WARN"
|
|
|
|
# Create file system watcher
|
|
$watcher = New-Object System.IO.FileSystemWatcher
|
|
$watcher.Path = $SourcePath
|
|
$watcher.IncludeSubdirectories = $true
|
|
$watcher.EnableRaisingEvents = $true
|
|
|
|
# Filters
|
|
$watcher.NotifyFilter = [System.IO.NotifyFilters]::FileName -bor
|
|
[System.IO.NotifyFilters]::DirectoryName -bor
|
|
[System.IO.NotifyFilters]::LastWrite -bor
|
|
[System.IO.NotifyFilters]::Size
|
|
|
|
# Debounce mechanism (prevent multiple syncs for rapid changes)
|
|
$script:lastSync = Get-Date
|
|
$script:syncPending = $false
|
|
$debounceSeconds = 2
|
|
|
|
# Event handler
|
|
$onChange = {
|
|
param($sender, $e)
|
|
|
|
# Check if file should be excluded
|
|
if (Test-ShouldExclude -Path $e.FullPath) {
|
|
return
|
|
}
|
|
|
|
$now = Get-Date
|
|
$timeSinceLastSync = ($now - $script:lastSync).TotalSeconds
|
|
|
|
if ($timeSinceLastSync -gt $debounceSeconds) {
|
|
Write-Log "Change detected: $($e.ChangeType) - $($e.Name)" "INFO"
|
|
Sync-Folder | Out-Null
|
|
$script:lastSync = Get-Date
|
|
} else {
|
|
if (-not $script:syncPending) {
|
|
$script:syncPending = $true
|
|
Write-Log "Changes detected, sync scheduled..." "INFO"
|
|
}
|
|
}
|
|
}
|
|
|
|
# Register events
|
|
Register-ObjectEvent -InputObject $watcher -EventName Changed -Action $onChange | Out-Null
|
|
Register-ObjectEvent -InputObject $watcher -EventName Created -Action $onChange | Out-Null
|
|
Register-ObjectEvent -InputObject $watcher -EventName Deleted -Action $onChange | Out-Null
|
|
Register-ObjectEvent -InputObject $watcher -EventName Renamed -Action $onChange | Out-Null
|
|
|
|
# Timer for debounced syncs
|
|
$timer = New-Object System.Timers.Timer
|
|
$timer.Interval = $debounceSeconds * 1000
|
|
$timer.AutoReset = $true
|
|
|
|
$onTimer = {
|
|
if ($script:syncPending) {
|
|
Sync-Folder | Out-Null
|
|
$script:syncPending = $false
|
|
$script:lastSync = Get-Date
|
|
}
|
|
}
|
|
|
|
Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action $onTimer | Out-Null
|
|
$timer.Start()
|
|
|
|
# Keep script running
|
|
try {
|
|
while ($true) {
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
} finally {
|
|
# Cleanup
|
|
$watcher.EnableRaisingEvents = $false
|
|
$watcher.Dispose()
|
|
$timer.Stop()
|
|
$timer.Dispose()
|
|
Get-EventSubscriber | Unregister-Event
|
|
Write-Log "Watcher stopped" "INFO"
|
|
}
|
|
}
|
|
|
|
# ============================================================================
|
|
# Main Execution
|
|
# ============================================================================
|
|
|
|
Write-Host ""
|
|
Write-Host "============================================================================" -ForegroundColor Cyan
|
|
Write-Host " EasyStream Folder Sync - Repos to Docker-Progs" -ForegroundColor Cyan
|
|
Write-Host "============================================================================" -ForegroundColor Cyan
|
|
Write-Host ""
|
|
|
|
# Initial sync
|
|
$syncResult = Sync-Folder -InitialSync $true
|
|
|
|
if (-not $syncResult) {
|
|
Write-Log "Initial sync failed. Exiting." "ERROR"
|
|
exit 1
|
|
}
|
|
|
|
# Watch mode
|
|
if ($Watch) {
|
|
Write-Host ""
|
|
Start-Watcher
|
|
} else {
|
|
Write-Host ""
|
|
Write-Log "Single sync completed. Use -Watch for continuous monitoring." "INFO"
|
|
Write-Host ""
|
|
}
|