refactor: organize codebase and remove redundant files
- Removed all backup/duplicate files - Removed test files from root - Consolidated documentation to /docs/ - Moved scripts to /scripts/ - Renamed f_* directories (removed prefix) - Organized icons and assets - Removed unused vendor directories - Cleaned up redundant config files
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
# Continuous Delivery - Quick Start
|
||||
|
||||
## Start Auto-Deployment in 10 Seconds
|
||||
|
||||
```powershell
|
||||
.\start-cd.ps1 watch
|
||||
```
|
||||
|
||||
That's it! Your changes will now automatically commit and push to GitHub.
|
||||
|
||||
## What Happens Now?
|
||||
|
||||
1. Edit any file and save
|
||||
2. Wait 30 seconds after your last change
|
||||
3. Automatically commits with change summary
|
||||
4. Automatically pushes to GitHub
|
||||
5. Repeat!
|
||||
|
||||
## Common Commands
|
||||
|
||||
```powershell
|
||||
# Start file watcher (recommended)
|
||||
.\start-cd.ps1 watch
|
||||
|
||||
# Start timer mode (every 5 minutes)
|
||||
.\start-cd.ps1 start
|
||||
|
||||
# Commit and push once, right now
|
||||
.\start-cd.ps1 once
|
||||
|
||||
# Stop all CD processes
|
||||
.\start-cd.ps1 stop
|
||||
|
||||
# Check git status
|
||||
.\start-cd.ps1 status
|
||||
```
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
[14:23:45] Changed: setup.php
|
||||
[14:23:47] Changed: docker-compose.yml
|
||||
|
||||
Debounce period elapsed. Processing 2 changes...
|
||||
ℹ Found 2 changed files
|
||||
✓ Committed changes
|
||||
✓ Successfully pushed to GitHub
|
||||
|
||||
Continuing to watch for changes...
|
||||
```
|
||||
|
||||
## What Gets Auto-Committed?
|
||||
|
||||
- Application code (PHP, JS, CSS)
|
||||
- Configuration files
|
||||
- Documentation
|
||||
- SQL schemas
|
||||
|
||||
## What's Excluded?
|
||||
|
||||
- Session files (`f_data/data_sessions/`)
|
||||
- Cache files (`f_data/data_cache/`)
|
||||
- Database files (`db_data/`)
|
||||
- Log files (`*.log`)
|
||||
- Dependencies (`node_modules/`, `vendor/`)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Not detecting changes?**
|
||||
- Check if file is in `.gitignore`
|
||||
|
||||
**Need to stop?**
|
||||
- Press `Ctrl+C` or run `.\start-cd.ps1 stop`
|
||||
|
||||
**Want custom settings?**
|
||||
- Edit `.cd-config.json`
|
||||
|
||||
## Full Documentation
|
||||
|
||||
See [CONTINUOUS_DELIVERY_GUIDE.md](CONTINUOUS_DELIVERY_GUIDE.md) for complete documentation.
|
||||
|
||||
---
|
||||
|
||||
**Ready?** Run `.\start-cd.ps1 watch` and start coding!
|
||||
@@ -0,0 +1,22 @@
|
||||
Privacy, Data Export/Delete, and Admin Audit
|
||||
|
||||
This document outlines how to implement user privacy controls and admin auditing in EasyStream.
|
||||
|
||||
User Data Export
|
||||
- Endpoint: `api/privacy.php?action=export` (requires login)
|
||||
- Returns a JSON bundle of key user data (profile, uploads, subscriptions). The current implementation returns a stub template; extend to include all relevant fields.
|
||||
|
||||
User Data Delete (Account Deletion)
|
||||
- Endpoint: `api/privacy.php?action=delete` (requires login and CSRF token)
|
||||
- Performs a soft-delete or anonymization pass across user-owned content and PII. The current implementation is a stub returning 202; extend with real logic gated by configuration and admin review.
|
||||
|
||||
Admin Audit Logs
|
||||
- Enable database logging in `f_core/config.logging.php` via `logging_database_logging`.
|
||||
- The logger writes to `db_logs` with request id, user id, IP, and optional context.
|
||||
- Use `f_modules/m_backend/log_viewer.php` to browse logs; it supports search and time filtering.
|
||||
|
||||
Security Considerations
|
||||
- Require authentication and CSRF validation for destructive actions.
|
||||
- Enforce rate limiting via `VSecurity::checkRateLimit`.
|
||||
- Consider adding a review workflow for delete requests.
|
||||
|
||||
@@ -0,0 +1,428 @@
|
||||
# EasyStream Continuous Delivery Guide
|
||||
|
||||
Automatically commit and push your changes to GitHub with zero manual intervention.
|
||||
|
||||
## Features
|
||||
|
||||
- **File Watcher Mode**: Detects file changes in real-time and auto-commits after 30 seconds of inactivity
|
||||
- **Timer Mode**: Commits and pushes at regular intervals (default: 5 minutes)
|
||||
- **Smart Exclusions**: Automatically excludes temporary files, sessions, cache, and database files
|
||||
- **Retry Logic**: Automatically retries failed pushes with exponential backoff
|
||||
- **Change Summaries**: Generates detailed commit messages with file change lists
|
||||
- **Zero Configuration**: Works out of the box with sensible defaults
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Option 1: File Watcher Mode (Recommended)
|
||||
|
||||
This mode monitors your files and automatically commits changes 30 seconds after you stop editing:
|
||||
|
||||
```powershell
|
||||
.\start-cd.ps1 watch
|
||||
```
|
||||
|
||||
You'll see:
|
||||
```
|
||||
========================================
|
||||
EasyStream File Watcher Started
|
||||
========================================
|
||||
Watching: E:\repos\easystream-main
|
||||
Branch: dev
|
||||
Debounce: 30 seconds after last change
|
||||
========================================
|
||||
|
||||
✓ File watcher active. Monitoring for changes...
|
||||
```
|
||||
|
||||
When you save a file:
|
||||
```
|
||||
[14:23:45] Changed: setup.php
|
||||
[14:23:47] Changed: f_core/config.href.php
|
||||
[14:23:50] Changed: docker-compose.yml
|
||||
|
||||
Debounce period elapsed. Processing 3 changes...
|
||||
ℹ Found 3 changed files
|
||||
✓ Committed changes
|
||||
✓ Successfully pushed to GitHub
|
||||
```
|
||||
|
||||
### Option 2: Timer Mode
|
||||
|
||||
Commits and pushes at regular intervals (every 5 minutes by default):
|
||||
|
||||
```powershell
|
||||
.\start-cd.ps1 start
|
||||
```
|
||||
|
||||
Change the interval to 1 minute:
|
||||
```powershell
|
||||
.\start-cd.ps1 start -Interval 60
|
||||
```
|
||||
|
||||
### Option 3: One-Time Commit
|
||||
|
||||
Manually trigger a single commit and push:
|
||||
|
||||
```powershell
|
||||
.\start-cd.ps1 once
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Edit [.cd-config.json](.cd-config.json) to customize behavior:
|
||||
|
||||
```json
|
||||
{
|
||||
"intervalSeconds": 300,
|
||||
"branch": "dev",
|
||||
"commitPrefix": "auto:",
|
||||
"excludePatterns": [
|
||||
"f_data/data_sessions/*",
|
||||
"f_data/data_cache/_c_tpl/*",
|
||||
".setup_complete",
|
||||
"*.log",
|
||||
"db_data/*",
|
||||
"node_modules/*",
|
||||
"vendor/*"
|
||||
],
|
||||
"enableNotifications": true,
|
||||
"autoStart": false
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
|--------|-------------|---------|
|
||||
| `intervalSeconds` | Time between auto-commits (timer mode) | 300 (5 min) |
|
||||
| `branch` | Git branch to push to | "dev" |
|
||||
| `commitPrefix` | Prefix for auto-generated commit messages | "auto:" |
|
||||
| `excludePatterns` | File patterns to ignore | See above |
|
||||
| `enableNotifications` | Show Windows notifications (future) | true |
|
||||
| `autoStart` | Start CD on system boot (future) | false |
|
||||
|
||||
## Commands Reference
|
||||
|
||||
### start-cd.ps1 Commands
|
||||
|
||||
```powershell
|
||||
# Start file watcher mode
|
||||
.\start-cd.ps1 watch
|
||||
|
||||
# Start timer mode (default interval: 5 minutes)
|
||||
.\start-cd.ps1 start
|
||||
|
||||
# Start timer mode with custom interval (60 seconds)
|
||||
.\start-cd.ps1 start -Interval 60
|
||||
|
||||
# Run one-time commit and push
|
||||
.\start-cd.ps1 once
|
||||
|
||||
# Check git status
|
||||
.\start-cd.ps1 status
|
||||
|
||||
# Stop all running CD processes
|
||||
.\start-cd.ps1 stop
|
||||
|
||||
# Show help
|
||||
.\start-cd.ps1 help
|
||||
```
|
||||
|
||||
### auto-deploy.ps1 Advanced Usage
|
||||
|
||||
Direct script usage for advanced scenarios:
|
||||
|
||||
```powershell
|
||||
# File watcher mode with verbose output
|
||||
.\auto-deploy.ps1 -WatchMode -Verbose
|
||||
|
||||
# Timer mode with custom interval and branch
|
||||
.\auto-deploy.ps1 -IntervalSeconds 120 -Branch main -Verbose
|
||||
|
||||
# Custom commit prefix
|
||||
.\auto-deploy.ps1 -CommitPrefix "wip:" -IntervalSeconds 60
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### File Watcher Mode
|
||||
|
||||
1. **Monitors**: Watches all files in the repository using .NET FileSystemWatcher
|
||||
2. **Debounces**: Waits 30 seconds after the last file change to avoid committing partial edits
|
||||
3. **Excludes**: Filters out temporary files based on `.gitignore` and config patterns
|
||||
4. **Commits**: Stages all changes and creates a commit with file change summary
|
||||
5. **Pushes**: Uploads to GitHub with retry logic
|
||||
6. **Repeats**: Continues monitoring for the next change
|
||||
|
||||
### Timer Mode
|
||||
|
||||
1. **Checks**: Scans for changes at regular intervals
|
||||
2. **Stages**: Adds all modified, new, and deleted files
|
||||
3. **Commits**: Creates a timestamped commit with change summary
|
||||
4. **Pushes**: Uploads to GitHub if there are new commits
|
||||
5. **Waits**: Sleeps for the configured interval before next check
|
||||
|
||||
## Commit Message Format
|
||||
|
||||
Auto-generated commits include:
|
||||
|
||||
```
|
||||
auto: Update at 2025-10-26 14:30:00
|
||||
|
||||
Changed files:
|
||||
- setup.php
|
||||
- docker-compose.yml
|
||||
- f_core/config.href.php
|
||||
- ... and 5 more files
|
||||
|
||||
🤖 Generated with Claude Code Continuous Delivery
|
||||
|
||||
Co-Authored-By: Claude <noreply@anthropic.com>
|
||||
```
|
||||
|
||||
## Excluded Files
|
||||
|
||||
The following files are automatically excluded from commits:
|
||||
|
||||
- Session files: `f_data/data_sessions/sess_*`
|
||||
- Cache files: `f_data/data_cache/_c_tpl/*`
|
||||
- Setup marker: `.setup_complete`
|
||||
- Database files: `db_data/*`
|
||||
- Log files: `*.log`
|
||||
- Dependencies: `node_modules/*`, `vendor/*`
|
||||
- IDE files: `.vscode/*`, `.idea/*`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### CD not detecting changes
|
||||
|
||||
**Problem**: Files changed but no commit triggered
|
||||
|
||||
**Solution**:
|
||||
```powershell
|
||||
# Check if files are excluded
|
||||
git status
|
||||
|
||||
# Verify .gitignore doesn't over-exclude
|
||||
cat .gitignore
|
||||
|
||||
# Check CD config
|
||||
cat .cd-config.json
|
||||
```
|
||||
|
||||
### Push failed: Authentication required
|
||||
|
||||
**Problem**: Git asks for credentials
|
||||
|
||||
**Solution**:
|
||||
```powershell
|
||||
# Set up Git credential helper (Windows)
|
||||
git config --global credential.helper manager
|
||||
|
||||
# Or use SSH keys
|
||||
git remote set-url origin git@github.com:SamiAhmed7777/easystream-main.git
|
||||
```
|
||||
|
||||
### Multiple CD processes running
|
||||
|
||||
**Problem**: CD commits too frequently
|
||||
|
||||
**Solution**:
|
||||
```powershell
|
||||
# Stop all CD processes
|
||||
.\start-cd.ps1 stop
|
||||
|
||||
# Restart with single instance
|
||||
.\start-cd.ps1 watch
|
||||
```
|
||||
|
||||
### Large files rejected by GitHub
|
||||
|
||||
**Problem**: `db_data/*` files are too large
|
||||
|
||||
**Solution**:
|
||||
These files are already excluded in `.gitignore`. If they're already tracked:
|
||||
```powershell
|
||||
# Remove from git but keep locally
|
||||
git rm --cached -r db_data/
|
||||
git commit -m "Remove database files from tracking"
|
||||
git push
|
||||
```
|
||||
|
||||
## Running as Background Service
|
||||
|
||||
### Option 1: Windows Task Scheduler
|
||||
|
||||
1. Open Task Scheduler
|
||||
2. Create Basic Task
|
||||
3. Trigger: At system startup
|
||||
4. Action: Start a program
|
||||
- Program: `powershell.exe`
|
||||
- Arguments: `-WindowStyle Hidden -File "E:\repos\easystream-main\start-cd.ps1" watch`
|
||||
|
||||
### Option 2: PowerShell Background Job
|
||||
|
||||
```powershell
|
||||
# Start in background
|
||||
Start-Job -ScriptBlock {
|
||||
Set-Location "E:\repos\easystream-main"
|
||||
.\start-cd.ps1 watch
|
||||
}
|
||||
|
||||
# Check status
|
||||
Get-Job
|
||||
|
||||
# View output
|
||||
Receive-Job -Id 1 -Keep
|
||||
|
||||
# Stop
|
||||
Stop-Job -Id 1
|
||||
Remove-Job -Id 1
|
||||
```
|
||||
|
||||
### Option 3: Screen/Tmux (Windows Terminal)
|
||||
|
||||
```powershell
|
||||
# Open new Windows Terminal tab
|
||||
wt -w 0 nt -d E:\repos\easystream-main powershell -NoExit -Command ".\start-cd.ps1 watch"
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### What Gets Committed
|
||||
|
||||
- Application code (PHP, JavaScript, CSS)
|
||||
- Configuration files (docker-compose.yml, .env templates)
|
||||
- Documentation (Markdown files)
|
||||
- SQL schema files
|
||||
|
||||
### What's Excluded
|
||||
|
||||
- Session data (user sessions)
|
||||
- Cache (compiled templates)
|
||||
- Database runtime files (actual data)
|
||||
- Secrets (`.env` files are tracked but should only contain templates)
|
||||
|
||||
### Best Practices
|
||||
|
||||
1. **Review `.gitignore`**: Ensure sensitive files are excluded
|
||||
2. **Use environment variables**: Never commit actual passwords or API keys
|
||||
3. **Separate repos for secrets**: Use a private repo for production `.env` files
|
||||
4. **Monitor commits**: Occasionally review auto-commits for unwanted files
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### For Large Repositories
|
||||
|
||||
```json
|
||||
{
|
||||
"intervalSeconds": 600,
|
||||
"excludePatterns": [
|
||||
"f_data/*",
|
||||
"uploads/*",
|
||||
"*.mp4",
|
||||
"*.mkv"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### For Active Development
|
||||
|
||||
```json
|
||||
{
|
||||
"intervalSeconds": 180,
|
||||
"commitPrefix": "wip:"
|
||||
}
|
||||
```
|
||||
|
||||
## Integration with IDE
|
||||
|
||||
### VS Code
|
||||
|
||||
Add to `.vscode/tasks.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Start Continuous Delivery",
|
||||
"type": "shell",
|
||||
"command": ".\\start-cd.ps1 watch",
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated"
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Run with: `Ctrl+Shift+P` → `Tasks: Run Task` → `Start Continuous Delivery`
|
||||
|
||||
## FAQ
|
||||
|
||||
**Q: Will this commit every keystroke?**
|
||||
A: No. File watcher mode waits 30 seconds after the last change before committing.
|
||||
|
||||
**Q: Can I customize commit messages?**
|
||||
A: Yes, edit the commit message generation in [auto-deploy.ps1:176-188](auto-deploy.ps1#L176-L188)
|
||||
|
||||
**Q: Does this work with pull requests?**
|
||||
A: Yes, but you may want to squash auto-commits before merging. Consider using a feature branch.
|
||||
|
||||
**Q: Can I run this on Linux/Mac?**
|
||||
A: The scripts are PowerShell-specific. For Linux/Mac, use Git hooks or tools like `watchman` + bash scripts.
|
||||
|
||||
**Q: Will this conflict with manual commits?**
|
||||
A: No. The CD system checks for changes and only commits if there are uncommitted files.
|
||||
|
||||
**Q: How do I temporarily pause CD?**
|
||||
A: Press `Ctrl+C` in the CD terminal, or run `.\start-cd.ps1 stop`
|
||||
|
||||
## Examples
|
||||
|
||||
### Development Workflow
|
||||
|
||||
```powershell
|
||||
# Morning: Start CD
|
||||
.\start-cd.ps1 watch
|
||||
|
||||
# Work normally - changes auto-commit as you save
|
||||
|
||||
# End of day: Review commits
|
||||
git log --oneline --since="8 hours ago"
|
||||
|
||||
# Squash if needed before PR
|
||||
git rebase -i HEAD~20
|
||||
```
|
||||
|
||||
### Emergency Hotfix
|
||||
|
||||
```powershell
|
||||
# Stop CD
|
||||
.\start-cd.ps1 stop
|
||||
|
||||
# Make critical fix
|
||||
# ... edit files ...
|
||||
|
||||
# Manual commit with detailed message
|
||||
git add .
|
||||
git commit -m "fix: Critical security patch for XSS vulnerability"
|
||||
git push
|
||||
|
||||
# Resume CD
|
||||
.\start-cd.ps1 watch
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
- GitHub Issues: https://github.com/SamiAhmed7777/easystream-main/issues
|
||||
- Check logs in terminal output
|
||||
- Use `-Verbose` flag for detailed diagnostics
|
||||
|
||||
---
|
||||
|
||||
**Next Steps**: [Start using CD now!](#quick-start)
|
||||
@@ -0,0 +1,584 @@
|
||||
# EasyStream - Complete Docker Deployment Guide
|
||||
|
||||
## Table of Contents
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Quick Start (Development)](#quick-start-development)
|
||||
- [Production Deployment](#production-deployment)
|
||||
- [Folder Sync Setup](#folder-sync-setup)
|
||||
- [Database Management](#database-management)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
- [Security Checklist](#security-checklist)
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### System Requirements
|
||||
- **OS**: Windows 10/11, Linux, or macOS
|
||||
- **Docker**: Version 20.10 or higher
|
||||
- **Docker Compose**: Version 2.0 or higher
|
||||
- **RAM**: Minimum 4GB (8GB recommended)
|
||||
- **Disk**: Minimum 20GB free space
|
||||
|
||||
### Check Your Installation
|
||||
```bash
|
||||
docker --version
|
||||
docker-compose --version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Start (Development)
|
||||
|
||||
### 1. Clone or Navigate to Project
|
||||
```bash
|
||||
cd E:\repos\easystream-main
|
||||
```
|
||||
|
||||
### 2. Configure Environment
|
||||
```bash
|
||||
# Copy the example environment file
|
||||
copy .env.example .env
|
||||
|
||||
# Edit .env with your settings (optional for development)
|
||||
notepad .env
|
||||
```
|
||||
|
||||
### 3. Start All Services
|
||||
```bash
|
||||
# Start in detached mode
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
### 4. Wait for Database Initialization
|
||||
The database will automatically initialize with all tables and default data. This takes about 2-3 minutes.
|
||||
|
||||
```bash
|
||||
# Check database health
|
||||
docker-compose ps
|
||||
|
||||
# Watch database logs
|
||||
docker-compose logs -f db
|
||||
```
|
||||
|
||||
### 5. Access the Application
|
||||
- **Frontend**: http://localhost:8083
|
||||
- **Admin Panel**: http://localhost:8083/admin
|
||||
- **Default Admin Credentials**:
|
||||
- Username: `admin`
|
||||
- Password: `admin123` (⚠️ **CHANGE THIS IMMEDIATELY!**)
|
||||
|
||||
### 6. Test RTMP Streaming
|
||||
```bash
|
||||
# Stream URL (use in OBS or streaming software)
|
||||
rtmp://localhost:1935/live/testkey
|
||||
|
||||
# View HLS stream
|
||||
http://localhost:8083/hls/testkey/index.m3u8
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Step 1: Prepare Production Environment
|
||||
|
||||
#### 1.1 Copy Production Configuration
|
||||
```bash
|
||||
copy .env.production .env
|
||||
```
|
||||
|
||||
#### 1.2 Generate Secure Secrets
|
||||
Create the secrets directory:
|
||||
```bash
|
||||
mkdir secrets
|
||||
```
|
||||
|
||||
Generate secure random keys (use one of these methods):
|
||||
|
||||
**Method A: Using OpenSSL (Linux/Mac)**
|
||||
```bash
|
||||
openssl rand -hex 32 > secrets/api_key.txt
|
||||
openssl rand -hex 32 > secrets/jwt_secret.txt
|
||||
openssl rand -hex 32 > secrets/encryption_key.txt
|
||||
openssl rand -hex 32 > secrets/cron_secret.txt
|
||||
openssl rand -hex 24 > secrets/db_password.txt
|
||||
openssl rand -hex 24 > secrets/db_root_password.txt
|
||||
```
|
||||
|
||||
**Method B: Using PowerShell (Windows)**
|
||||
```powershell
|
||||
.\generate-secrets.ps1
|
||||
```
|
||||
|
||||
**Method C: Using Docker**
|
||||
```bash
|
||||
docker run --rm alpine sh -c "head -c 32 /dev/urandom | base64" > secrets/api_key.txt
|
||||
docker run --rm alpine sh -c "head -c 32 /dev/urandom | base64" > secrets/jwt_secret.txt
|
||||
docker run --rm alpine sh -c "head -c 32 /dev/urandom | base64" > secrets/encryption_key.txt
|
||||
docker run --rm alpine sh -c "head -c 32 /dev/urandom | base64" > secrets/cron_secret.txt
|
||||
docker run --rm alpine sh -c "head -c 24 /dev/urandom | base64" > secrets/db_password.txt
|
||||
docker run --rm alpine sh -c "head -c 24 /dev/urandom | base64" > secrets/db_root_password.txt
|
||||
```
|
||||
|
||||
#### 1.3 Update Production Configuration
|
||||
Edit `.env` and update these critical values:
|
||||
```env
|
||||
MAIN_URL=https://your-domain.com
|
||||
DB_PASS=<content of secrets/db_password.txt>
|
||||
API_KEY=<content of secrets/api_key.txt>
|
||||
JWT_SECRET=<content of secrets/jwt_secret.txt>
|
||||
ENCRYPTION_KEY=<content of secrets/encryption_key.txt>
|
||||
```
|
||||
|
||||
### Step 2: Set Up SSL/TLS
|
||||
|
||||
#### Option A: Let's Encrypt (Automatic - Recommended)
|
||||
Update your `Caddyfile`:
|
||||
```
|
||||
your-domain.com {
|
||||
encode gzip
|
||||
root * /srv/easystream
|
||||
php_fastcgi php:9000
|
||||
file_server
|
||||
}
|
||||
```
|
||||
|
||||
Caddy will automatically obtain and renew SSL certificates.
|
||||
|
||||
#### Option B: Custom Certificates
|
||||
Place your certificates in `./deploy/ssl/`:
|
||||
```bash
|
||||
mkdir -p deploy/ssl
|
||||
# Copy your certificate files
|
||||
copy your-cert.pem deploy/ssl/
|
||||
copy your-key.pem deploy/ssl/
|
||||
```
|
||||
|
||||
### Step 3: Create Production Volumes
|
||||
```bash
|
||||
# Create directories for persistent data
|
||||
mkdir -p /var/lib/easystream/db
|
||||
mkdir -p /var/lib/easystream/uploads
|
||||
mkdir -p /var/lib/easystream/recordings
|
||||
mkdir -p /var/log/easystream
|
||||
```
|
||||
|
||||
### Step 4: Deploy Production Stack
|
||||
```bash
|
||||
# Pull latest images
|
||||
docker-compose -f docker-compose.prod.yml pull
|
||||
|
||||
# Build custom images
|
||||
docker-compose -f docker-compose.prod.yml build
|
||||
|
||||
# Start services
|
||||
docker-compose -f docker-compose.prod.yml up -d
|
||||
|
||||
# Check status
|
||||
docker-compose -f docker-compose.prod.yml ps
|
||||
|
||||
# View logs
|
||||
docker-compose -f docker-compose.prod.yml logs -f
|
||||
```
|
||||
|
||||
### Step 5: Post-Deployment Verification
|
||||
```bash
|
||||
# Test database connection
|
||||
docker-compose -f docker-compose.prod.yml exec php php -r "new PDO('mysql:host=db;dbname=easystream', 'easystream', getenv('DB_PASS')); echo 'DB OK\n';"
|
||||
|
||||
# Test Redis connection
|
||||
docker-compose -f docker-compose.prod.yml exec php php -r "\$redis = new Redis(); \$redis->connect('redis', 6379); echo 'Redis OK\n';"
|
||||
|
||||
# Check all services are healthy
|
||||
docker-compose -f docker-compose.prod.yml ps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Folder Sync Setup
|
||||
|
||||
EasyStream includes an automatic folder sync tool to keep your development and Docker directories in sync.
|
||||
|
||||
### Windows Setup
|
||||
|
||||
#### One-Time Sync
|
||||
```bash
|
||||
# Navigate to project directory
|
||||
cd E:\repos\easystream-main
|
||||
|
||||
# Run one-time sync
|
||||
.\sync-to-docker-progs.bat
|
||||
```
|
||||
|
||||
#### Continuous Sync (Watch Mode)
|
||||
```bash
|
||||
# Start file watcher
|
||||
.\sync-to-docker-progs.bat watch
|
||||
|
||||
# This will continuously monitor E:\repos\easystream-main
|
||||
# and sync changes to E:\docker-progs\easystream-main
|
||||
```
|
||||
|
||||
#### Using PowerShell Directly
|
||||
```powershell
|
||||
# One-time sync
|
||||
.\sync-to-docker-progs.ps1
|
||||
|
||||
# Watch mode
|
||||
.\sync-to-docker-progs.ps1 -Watch
|
||||
|
||||
# Verbose mode
|
||||
.\sync-to-docker-progs.ps1 -Watch -Verbose
|
||||
|
||||
# Dry run (see what would be synced)
|
||||
.\sync-to-docker-progs.ps1 -DryRun
|
||||
```
|
||||
|
||||
### What Gets Synced
|
||||
- All source code files (PHP, CSS, JS, etc.)
|
||||
- Configuration files
|
||||
- Templates
|
||||
- Database schema files
|
||||
- Docker configuration
|
||||
|
||||
### What Gets Excluded
|
||||
- `.git` directory
|
||||
- `node_modules`
|
||||
- `vendor` (Composer dependencies)
|
||||
- Cache and temporary files
|
||||
- Log files
|
||||
- Uploaded media files
|
||||
- Session files
|
||||
|
||||
---
|
||||
|
||||
## Database Management
|
||||
|
||||
### Initial Setup
|
||||
The database is automatically initialized on first startup with:
|
||||
1. **Main Schema** (270 tables) - Core platform
|
||||
2. **Advanced Features** (40 tables) - API, analytics, monetization, etc.
|
||||
3. **Default Settings** - Site configuration
|
||||
4. **Default Admin User** - `admin` / `admin123`
|
||||
5. **Default Categories** - 10 video categories
|
||||
6. **Template Builder Components** - 7 pre-built components
|
||||
|
||||
### Manual Database Operations
|
||||
|
||||
#### Access Database CLI
|
||||
```bash
|
||||
# Development
|
||||
docker-compose exec db mysql -u easystream -peasystream easystream
|
||||
|
||||
# Production
|
||||
docker-compose -f docker-compose.prod.yml exec db mysql -u easystream -p easystream
|
||||
```
|
||||
|
||||
#### Backup Database
|
||||
```bash
|
||||
# Create backup directory
|
||||
mkdir -p backups
|
||||
|
||||
# Backup with compression
|
||||
docker-compose exec db mysqldump -u easystream -peasystream easystream | gzip > backups/easystream-$(date +%Y%m%d-%H%M%S).sql.gz
|
||||
|
||||
# Backup without compression
|
||||
docker-compose exec db mysqldump -u easystream -peasystream easystream > backups/easystream-$(date +%Y%m%d-%H%M%S).sql
|
||||
```
|
||||
|
||||
#### Restore Database
|
||||
```bash
|
||||
# From compressed backup
|
||||
gunzip -c backups/easystream-20250101-120000.sql.gz | docker-compose exec -T db mysql -u easystream -peasystream easystream
|
||||
|
||||
# From uncompressed backup
|
||||
docker-compose exec -T db mysql -u easystream -peasystream easystream < backups/easystream-20250101-120000.sql
|
||||
```
|
||||
|
||||
#### Reset Database
|
||||
```bash
|
||||
# Stop services
|
||||
docker-compose down
|
||||
|
||||
# Remove database volume
|
||||
docker volume rm easystream-main_db_data
|
||||
|
||||
# Start services (will re-initialize)
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Database Schema Updates
|
||||
|
||||
#### Apply New Tables
|
||||
If you have new SQL files to apply:
|
||||
```bash
|
||||
docker-compose exec -T db mysql -u easystream -peasystream easystream < new_schema.sql
|
||||
```
|
||||
|
||||
#### Check Table Count
|
||||
```bash
|
||||
docker-compose exec db mysql -u easystream -peasystream easystream -e "SELECT COUNT(*) as table_count FROM information_schema.tables WHERE table_schema = 'easystream';"
|
||||
```
|
||||
|
||||
#### List All Tables
|
||||
```bash
|
||||
docker-compose exec db mysql -u easystream -peasystream easystream -e "SHOW TABLES;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### 1. Database Container Won't Start
|
||||
```bash
|
||||
# Check logs
|
||||
docker-compose logs db
|
||||
|
||||
# Common causes:
|
||||
# - Volume mount errors (missing SQL files)
|
||||
# - Port 3306 already in use
|
||||
# - Insufficient memory
|
||||
|
||||
# Fix: Check if SQL files exist
|
||||
ls -la __install/easystream.sql
|
||||
ls -la __install/add_advanced_features.sql
|
||||
ls -la deploy/init_settings.sql
|
||||
```
|
||||
|
||||
#### 2. Port Already in Use
|
||||
```bash
|
||||
# Check what's using the port
|
||||
netstat -ano | findstr :8083 # Windows
|
||||
lsof -i :8083 # Linux/Mac
|
||||
|
||||
# Solution: Either stop the other service or change port in docker-compose.yml
|
||||
```
|
||||
|
||||
#### 3. PHP Container Can't Connect to Database
|
||||
```bash
|
||||
# Check if database is healthy
|
||||
docker-compose ps
|
||||
|
||||
# Wait for database to be ready (may take 2-3 minutes)
|
||||
docker-compose logs -f db
|
||||
|
||||
# Verify database connection from PHP container
|
||||
docker-compose exec php php -r "new PDO('mysql:host=db;dbname=easystream', 'easystream', 'easystream'); echo 'OK\n';"
|
||||
```
|
||||
|
||||
#### 4. Video Upload Not Working
|
||||
```bash
|
||||
# Check PHP upload limits
|
||||
docker-compose exec php php -i | grep upload_max_filesize
|
||||
docker-compose exec php php -i | grep post_max_size
|
||||
|
||||
# Check directory permissions
|
||||
docker-compose exec php ls -la /srv/easystream/f_data/uploads
|
||||
|
||||
# Fix permissions
|
||||
docker-compose exec php chown -R www-data:www-data /srv/easystream/f_data/uploads
|
||||
```
|
||||
|
||||
#### 5. RTMP Streaming Not Working
|
||||
```bash
|
||||
# Check SRS logs
|
||||
docker-compose logs srs
|
||||
|
||||
# Test RTMP connection
|
||||
docker-compose exec srs curl http://localhost:1985/api/v1/streams
|
||||
|
||||
# Verify HLS output directory
|
||||
docker-compose exec php ls -la /var/www/hls
|
||||
```
|
||||
|
||||
#### 6. Sync Script Not Working
|
||||
```bash
|
||||
# Check PowerShell execution policy
|
||||
Get-ExecutionPolicy
|
||||
|
||||
# If Restricted, allow scripts to run:
|
||||
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
|
||||
# Check if paths exist
|
||||
Test-Path E:\repos\easystream-main
|
||||
Test-Path E:\docker-progs\easystream-main
|
||||
```
|
||||
|
||||
### Service Management
|
||||
|
||||
#### View All Logs
|
||||
```bash
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
#### View Specific Service Logs
|
||||
```bash
|
||||
docker-compose logs -f php
|
||||
docker-compose logs -f db
|
||||
docker-compose logs -f caddy
|
||||
docker-compose logs -f srs
|
||||
```
|
||||
|
||||
#### Restart Specific Service
|
||||
```bash
|
||||
docker-compose restart php
|
||||
docker-compose restart caddy
|
||||
```
|
||||
|
||||
#### Rebuild Service
|
||||
```bash
|
||||
docker-compose up -d --build php
|
||||
```
|
||||
|
||||
#### Check Service Health
|
||||
```bash
|
||||
docker-compose ps
|
||||
docker-compose top
|
||||
```
|
||||
|
||||
### Performance Issues
|
||||
|
||||
#### Check Resource Usage
|
||||
```bash
|
||||
docker stats
|
||||
```
|
||||
|
||||
#### Optimize Database
|
||||
```bash
|
||||
docker-compose exec db mysql -u easystream -peasystream easystream -e "OPTIMIZE TABLE db_videofiles, db_accountuser, db_sessions;"
|
||||
```
|
||||
|
||||
#### Clear Cache
|
||||
```bash
|
||||
docker-compose exec php rm -rf /srv/easystream/f_data/cache/*
|
||||
docker-compose exec redis redis-cli FLUSHALL
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security Checklist
|
||||
|
||||
### Pre-Production Checklist
|
||||
|
||||
- [ ] **Changed default admin password** (`admin123` → strong password)
|
||||
- [ ] **Generated secure API keys** (not using defaults)
|
||||
- [ ] **Generated secure JWT secret** (not using defaults)
|
||||
- [ ] **Generated secure encryption key** (not using defaults)
|
||||
- [ ] **Changed database password** (not using `easystream`)
|
||||
- [ ] **Set up SSL/TLS certificates** (HTTPS enabled)
|
||||
- [ ] **Configured firewall rules** (only necessary ports exposed)
|
||||
- [ ] **Set up database backups** (automated daily backups)
|
||||
- [ ] **Configured email server** (for notifications)
|
||||
- [ ] **Set up monitoring** (health checks, alerts)
|
||||
- [ ] **Reviewed file permissions** (proper ownership)
|
||||
- [ ] **Enabled rate limiting** (API and login protection)
|
||||
- [ ] **Configured CORS properly** (only allow trusted domains)
|
||||
- [ ] **Set secure session cookies** (httpOnly, secure, sameSite)
|
||||
- [ ] **Disabled debug mode** (`DEBUG=false`)
|
||||
- [ ] **Set up log rotation** (prevent disk fill)
|
||||
- [ ] **Configured Redis password** (if exposed)
|
||||
- [ ] **Reviewed .env file** (no defaults in production)
|
||||
- [ ] **Set up CDN** (for static assets)
|
||||
- [ ] **Configured S3/object storage** (for user uploads)
|
||||
|
||||
### File Permissions (Linux/Mac)
|
||||
```bash
|
||||
# Set proper ownership
|
||||
chown -R www-data:www-data /srv/easystream
|
||||
|
||||
# Set secure permissions
|
||||
chmod 755 /srv/easystream
|
||||
chmod 644 /srv/easystream/.env
|
||||
chmod 600 /srv/easystream/secrets/*
|
||||
chmod 755 /srv/easystream/f_data/uploads
|
||||
chmod 755 /srv/easystream/f_data/cache
|
||||
```
|
||||
|
||||
### Network Security
|
||||
```bash
|
||||
# Only expose necessary ports to public
|
||||
# In production docker-compose.yml:
|
||||
# - Database: 127.0.0.1:3306 (localhost only)
|
||||
# - Redis: 127.0.0.1:6379 (localhost only)
|
||||
# - HTTP: 80 (public)
|
||||
# - HTTPS: 443 (public)
|
||||
# - RTMP: 1935 (public, if needed)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Maintenance Tasks
|
||||
|
||||
### Daily
|
||||
- Monitor application logs
|
||||
- Check disk space usage
|
||||
- Review error logs
|
||||
|
||||
### Weekly
|
||||
- Backup database
|
||||
- Review security logs
|
||||
- Check service health
|
||||
|
||||
### Monthly
|
||||
- Update Docker images
|
||||
- Review and optimize database
|
||||
- Test backup restoration
|
||||
- Security audit
|
||||
|
||||
### Backup Strategy
|
||||
```bash
|
||||
# Create automated backup script
|
||||
cat > backup.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
DATE=$(date +%Y%m%d-%H%M%S)
|
||||
BACKUP_DIR="/backups/easystream"
|
||||
mkdir -p $BACKUP_DIR
|
||||
|
||||
# Database backup
|
||||
docker-compose exec -T db mysqldump -u easystream -peasystream easystream | gzip > $BACKUP_DIR/db-$DATE.sql.gz
|
||||
|
||||
# Files backup (user uploads)
|
||||
tar czf $BACKUP_DIR/uploads-$DATE.tar.gz /var/lib/easystream/uploads
|
||||
|
||||
# Cleanup old backups (keep last 30 days)
|
||||
find $BACKUP_DIR -type f -mtime +30 -delete
|
||||
|
||||
echo "Backup completed: $DATE"
|
||||
EOF
|
||||
|
||||
chmod +x backup.sh
|
||||
|
||||
# Add to crontab (daily at 2 AM)
|
||||
# 0 2 * * * /path/to/backup.sh >> /var/log/easystream-backup.log 2>&1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- **Docker Documentation**: https://docs.docker.com/
|
||||
- **Caddy Web Server**: https://caddyserver.com/docs/
|
||||
- **SRS Streaming Server**: https://github.com/ossrs/srs
|
||||
- **MariaDB**: https://mariadb.org/documentation/
|
||||
- **Redis**: https://redis.io/documentation
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
For issues, questions, or contributions:
|
||||
- Check the troubleshooting section above
|
||||
- Review application logs
|
||||
- Check Docker container health
|
||||
- Consult the main README.md file
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-10-25
|
||||
**Version**: 2.0
|
||||
@@ -0,0 +1,427 @@
|
||||
# EasyStream - Docker Quick Start
|
||||
|
||||
**Get your video platform running in under 5 minutes!**
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker installed
|
||||
- Docker Compose installed
|
||||
- 2GB free disk space
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Step 1: Start the Platform
|
||||
|
||||
```bash
|
||||
docker-compose up -d --build
|
||||
```
|
||||
|
||||
This starts all required services:
|
||||
- **db** - MariaDB database
|
||||
- **php** - PHP-FPM 8.2 application server
|
||||
- **caddy** - Web server with automatic HTTPS
|
||||
- **srs** - Live streaming server (RTMP/HLS)
|
||||
- **cron** - Background jobs
|
||||
- **abr** - Adaptive bitrate transcoding
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Initialize Database (single SQL file)
|
||||
|
||||
Run this **once** after first launch:
|
||||
|
||||
```bash
|
||||
docker exec -i easystream-db mysql -u easystream -peasystream easystream < __install/easystream.sql
|
||||
```
|
||||
|
||||
This loads the full schema and default settings in one pass.
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Access Your Platform
|
||||
|
||||
- **Main Site:** http://localhost:8083
|
||||
- **Admin Panel:** http://localhost:8083/admin_login.php
|
||||
- **Settings:** http://localhost:8083/admin_settings.php
|
||||
|
||||
**Default Admin Login:**
|
||||
- Username: `admin`
|
||||
- Password: `admin123`
|
||||
|
||||
---
|
||||
|
||||
## First Steps After Installation
|
||||
|
||||
### 1. Configure Basic Settings
|
||||
|
||||
Go to: http://localhost:8083/admin_settings.php
|
||||
|
||||
**General Tab:**
|
||||
- Set your site name
|
||||
- Update admin email
|
||||
- Configure main URL (if not localhost)
|
||||
|
||||
**Modules Tab:**
|
||||
- Enable/disable features you want (videos, live, blogs, etc.)
|
||||
|
||||
**Branding Tab:**
|
||||
- Set your brand colors
|
||||
- Upload logo and favicon
|
||||
|
||||
---
|
||||
|
||||
### 2. Set Up Email (Optional)
|
||||
|
||||
**Email Tab:**
|
||||
- Configure SMTP settings for transactional emails
|
||||
- Test with providers like SendGrid, Mailgun, or Gmail
|
||||
|
||||
Example for Gmail:
|
||||
- Host: `smtp.gmail.com`
|
||||
- Port: `587`
|
||||
- Encryption: `TLS`
|
||||
- Username: Your Gmail address
|
||||
- Password: App-specific password ([Create here](https://myaccount.google.com/apppasswords))
|
||||
|
||||
---
|
||||
|
||||
### 3. Configure Payments (Optional)
|
||||
|
||||
**Payments Tab:**
|
||||
|
||||
**For PayPal:**
|
||||
- Add PayPal email
|
||||
- Add Client ID and Secret
|
||||
- Toggle test mode (disable for production)
|
||||
|
||||
**For Stripe:**
|
||||
- Enable Stripe
|
||||
- Add Publishable Key
|
||||
- Add Secret Key
|
||||
- Add Webhook Secret
|
||||
|
||||
---
|
||||
|
||||
### 4. Set Up Creator Payouts (Optional)
|
||||
|
||||
**Payouts Tab:**
|
||||
- Enable creator payout system
|
||||
- Set revenue share percentage (e.g., 70% to creators)
|
||||
- Set minimum payout amount
|
||||
- Choose payout schedule (monthly recommended)
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Create `.env` file to override defaults:
|
||||
|
||||
```env
|
||||
# Database
|
||||
DB_HOST=db
|
||||
DB_NAME=easystream
|
||||
DB_USER=easystream
|
||||
DB_PASS=easystream
|
||||
|
||||
# Application
|
||||
MAIN_URL=http://localhost:8083
|
||||
DEBUG_MODE=0
|
||||
|
||||
# Email (optional - can also configure via admin panel)
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=your-email@gmail.com
|
||||
SMTP_PASS=your-app-password
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Commands
|
||||
|
||||
### View Logs
|
||||
```bash
|
||||
# All services
|
||||
docker-compose logs -f
|
||||
|
||||
# Specific service
|
||||
docker-compose logs -f php
|
||||
docker-compose logs -f caddy
|
||||
docker-compose logs -f db
|
||||
```
|
||||
|
||||
### Restart Services
|
||||
```bash
|
||||
# All services
|
||||
docker-compose restart
|
||||
|
||||
# Specific service
|
||||
docker-compose restart php
|
||||
```
|
||||
|
||||
### Stop Platform
|
||||
```bash
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
### Stop and Remove Data
|
||||
```bash
|
||||
docker-compose down -v
|
||||
```
|
||||
|
||||
### Database Access
|
||||
```bash
|
||||
# MySQL shell
|
||||
docker exec -it easystream-db mysql -u easystream -peasystream easystream
|
||||
|
||||
# Run SQL file
|
||||
docker exec -i easystream-db mysql -u easystream -peasystream easystream < file.sql
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
easystream/
|
||||
├── docker-compose.yml # Container orchestration
|
||||
├── Caddyfile # Web server config
|
||||
├── __install/ # Installation scripts
|
||||
│ └── install_settings_system.sql # Settings installation
|
||||
├── admin/ # Admin panel
|
||||
│ └── admin_settings.php # Settings UI
|
||||
├── f_core/ # Core framework
|
||||
│ ├── f_classes/ # PHP classes
|
||||
│ └── config.*.php # Configuration files
|
||||
├── f_data/ # Runtime data (auto-created)
|
||||
│ ├── logs/ # Application logs
|
||||
│ └── cache/ # Cache files
|
||||
└── SETTINGS_GUIDE.md # Complete settings documentation
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Live Streaming
|
||||
|
||||
### Start Streaming
|
||||
|
||||
**Push RTMP stream to:**
|
||||
```
|
||||
rtmp://localhost:1935/live/YOUR_STREAM_KEY
|
||||
```
|
||||
|
||||
**View HLS stream at:**
|
||||
```
|
||||
http://localhost:8083/hls/live/YOUR_STREAM_KEY/index.m3u8
|
||||
```
|
||||
|
||||
### OBS Studio Setup
|
||||
|
||||
1. Open OBS Studio
|
||||
2. Settings → Stream
|
||||
3. Service: `Custom`
|
||||
4. Server: `rtmp://localhost:1935/live`
|
||||
5. Stream Key: `YOUR_STREAM_KEY`
|
||||
6. Click "Start Streaming"
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Database Connection Errors
|
||||
|
||||
```bash
|
||||
# Check if database is running
|
||||
docker-compose ps
|
||||
|
||||
# Restart database
|
||||
docker-compose restart db
|
||||
|
||||
# Check database logs
|
||||
docker-compose logs db
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Settings Not Loading
|
||||
|
||||
```bash
|
||||
# Re-run settings installation
|
||||
docker exec -i easystream-db mysql -u easystream -peasystream easystream < __install/easystream.sql
|
||||
|
||||
# Check if settings exist
|
||||
docker exec -it easystream-db mysql -u easystream -peasystream -e "SELECT COUNT(*) FROM easystream.db_settings;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Port Already in Use
|
||||
|
||||
If port 8083 is already in use, edit `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
caddy:
|
||||
ports:
|
||||
- "8084:80" # Change 8083 to 8084
|
||||
- "443:443"
|
||||
```
|
||||
|
||||
Then restart:
|
||||
```bash
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Permission Errors
|
||||
|
||||
```bash
|
||||
# Fix data directory permissions
|
||||
sudo chown -R $(whoami):$(whoami) f_data/
|
||||
|
||||
# Or make writable by all (less secure)
|
||||
chmod -R 777 f_data/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Email Not Sending
|
||||
|
||||
1. Check SMTP settings in admin panel
|
||||
2. Test credentials with a mail client
|
||||
3. Check firewall allows port 587/465
|
||||
4. Review application logs:
|
||||
```bash
|
||||
docker-compose logs php | grep -i mail
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### 1. Update Environment
|
||||
|
||||
Create `.env.production`:
|
||||
|
||||
```env
|
||||
DB_HOST=db
|
||||
DB_NAME=easystream
|
||||
DB_USER=easystream
|
||||
DB_PASS=STRONG_PASSWORD_HERE
|
||||
|
||||
MAIN_URL=https://yourdomain.com
|
||||
DEBUG_MODE=0
|
||||
```
|
||||
|
||||
### 2. Configure Domain
|
||||
|
||||
Edit `Caddyfile`:
|
||||
|
||||
```
|
||||
yourdomain.com {
|
||||
root * /var/www/html
|
||||
php_fastcgi php:9000
|
||||
file_server
|
||||
encode gzip
|
||||
}
|
||||
```
|
||||
|
||||
### 3. SSL/HTTPS
|
||||
|
||||
Caddy automatically handles HTTPS with Let's Encrypt. Just point your domain to your server's IP.
|
||||
|
||||
### 4. Security Checklist
|
||||
|
||||
- [ ] Change default admin password
|
||||
- [ ] Disable debug mode
|
||||
- [ ] Use strong database password
|
||||
- [ ] Enable HTTPS
|
||||
- [ ] Configure firewall
|
||||
- [ ] Set up regular backups
|
||||
- [ ] Review security settings in admin panel
|
||||
|
||||
### 5. Backups
|
||||
|
||||
```bash
|
||||
# Backup database
|
||||
docker exec easystream-db mysqldump -u easystream -peasystream easystream > backup_$(date +%Y%m%d).sql
|
||||
|
||||
# Backup settings to JSON
|
||||
# (from admin panel: Settings → Export)
|
||||
|
||||
# Backup uploaded files
|
||||
tar -czf uploads_backup.tar.gz f_data/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### 1. Enable Redis Caching
|
||||
|
||||
Settings automatically use Redis when available for 10-100x faster performance.
|
||||
|
||||
Add to `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
|
||||
volumes:
|
||||
redis_data:
|
||||
```
|
||||
|
||||
### 2. Enable OPcache
|
||||
|
||||
Already enabled in the Docker PHP image for optimal performance.
|
||||
|
||||
### 3. Database Optimization
|
||||
|
||||
```bash
|
||||
# Check database size
|
||||
docker exec -it easystream-db mysql -u easystream -peasystream -e "
|
||||
SELECT table_schema AS 'Database',
|
||||
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS 'Size (MB)'
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'easystream'
|
||||
GROUP BY table_schema;"
|
||||
|
||||
# Optimize tables
|
||||
docker exec -it easystream-db mysqlcheck -u easystream -peasystream --optimize easystream
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Read Full Documentation:** [SETTINGS_GUIDE.md](SETTINGS_GUIDE.md)
|
||||
2. **Configure Your Platform:** Use the admin settings panel
|
||||
3. **Upload Content:** Start creating videos, streams, or blogs
|
||||
4. **Invite Users:** Share your platform URL
|
||||
5. **Monitor Performance:** Check logs and system status regularly
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
- **Documentation:** See [SETTINGS_GUIDE.md](SETTINGS_GUIDE.md) for complete settings reference
|
||||
- **Logs:** Check `f_data/logs/` for error logs
|
||||
- **Admin Panel:** Use log viewer at `/admin/log_viewer.php`
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
EasyStream Proprietary License Agreement
|
||||
Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,871 @@
|
||||
# 📚 EasyStream REST API Documentation
|
||||
|
||||
## 🎯 **Complete Guide to Using the EasyStream API**
|
||||
|
||||
The EasyStream API is a comprehensive RESTful API that allows you to integrate with the EasyStream video platform. This API enables mobile app development, third-party integrations, content syndication, and automation.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 **Getting Started**
|
||||
|
||||
### **Base URL**
|
||||
```
|
||||
https://yourdomain.com/api/v1/
|
||||
```
|
||||
|
||||
### **Content Type**
|
||||
All requests and responses use JSON format:
|
||||
```
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
### **CORS Support**
|
||||
The API supports Cross-Origin Resource Sharing (CORS) for web applications.
|
||||
|
||||
---
|
||||
|
||||
## 🔐 **Authentication**
|
||||
|
||||
### **1. JWT Bearer Token (Recommended)**
|
||||
|
||||
**Step 1: Login to get JWT token**
|
||||
```bash
|
||||
curl -X POST "https://yourdomain.com/api/v1/auth/login" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"username": "your_username",
|
||||
"password": "your_password"
|
||||
}'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"data": {
|
||||
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
|
||||
"user": {
|
||||
"id": 123,
|
||||
"username": "your_username",
|
||||
"email": "user@example.com",
|
||||
"display_name": "Your Name"
|
||||
},
|
||||
"expires_in": 86400
|
||||
},
|
||||
"timestamp": 1642518000,
|
||||
"version": "v1"
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Use token in subsequent requests**
|
||||
```bash
|
||||
curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
|
||||
```
|
||||
|
||||
### **2. API Key Authentication**
|
||||
|
||||
**Generate API Key** (via admin panel or user settings)
|
||||
```bash
|
||||
curl -H "Authorization: ApiKey your_api_key_here"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 **Rate Limiting**
|
||||
|
||||
| Endpoint Type | Limit | Window |
|
||||
|---------------|-------|--------|
|
||||
| Authentication | 10 requests | 5 minutes |
|
||||
| Upload | 5 requests | 1 hour |
|
||||
| Default | 100 requests | 1 hour |
|
||||
|
||||
**Rate limit headers in response:**
|
||||
```
|
||||
X-RateLimit-Limit: 100
|
||||
X-RateLimit-Remaining: 95
|
||||
X-RateLimit-Reset: 1642521600
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎬 **Video Endpoints**
|
||||
|
||||
### **Get Videos**
|
||||
```bash
|
||||
GET /api/v1/videos
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `page` (int): Page number (default: 1)
|
||||
- `limit` (int): Items per page (max: 50, default: 20)
|
||||
- `category` (string): Filter by category
|
||||
- `sort` (string): Sort order (`recent`, `popular`, `rating`)
|
||||
- `search` (string): Search query
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X GET "https://yourdomain.com/api/v1/videos?limit=10&category=gaming&sort=popular" \
|
||||
-H "Authorization: Bearer your_token"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"data": {
|
||||
"videos": [
|
||||
{
|
||||
"id": "video_123",
|
||||
"title": "Epic Gaming Moments",
|
||||
"description": "Amazing highlights from today's stream",
|
||||
"views": 1250,
|
||||
"rating": 4.8,
|
||||
"duration": 300,
|
||||
"size": 52428800,
|
||||
"uploaded_at": "2025-01-18 10:30:00",
|
||||
"uploader": {
|
||||
"username": "gamer_pro",
|
||||
"display_name": "Pro Gamer"
|
||||
},
|
||||
"thumbnail_url": "/thumbnails/video_123_medium.jpg",
|
||||
"video_url": "/watch/video_123"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"limit": 10,
|
||||
"total": 150
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Get Single Video**
|
||||
```bash
|
||||
GET /api/v1/videos/{video_id}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X GET "https://yourdomain.com/api/v1/videos/video_123" \
|
||||
-H "Authorization: Bearer your_token"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"data": {
|
||||
"id": "video_123",
|
||||
"title": "Epic Gaming Moments",
|
||||
"description": "Amazing highlights from today's stream",
|
||||
"full_description": "Full detailed description with timestamps...",
|
||||
"views": 1250,
|
||||
"rating": 4.8,
|
||||
"duration": 300,
|
||||
"size": 52428800,
|
||||
"category": "gaming",
|
||||
"tags": ["gaming", "highlights", "stream"],
|
||||
"privacy": "public",
|
||||
"uploaded_at": "2025-01-18 10:30:00",
|
||||
"uploader": {
|
||||
"id": 456,
|
||||
"username": "gamer_pro",
|
||||
"display_name": "Pro Gamer"
|
||||
},
|
||||
"thumbnail_urls": {
|
||||
"small": "/thumbnails/video_123_small.jpg",
|
||||
"medium": "/thumbnails/video_123_medium.jpg",
|
||||
"large": "/thumbnails/video_123_large.jpg"
|
||||
},
|
||||
"video_files": {
|
||||
"1080p": "/videos/video_123_1080p.mp4",
|
||||
"720p": "/videos/video_123_720p.mp4",
|
||||
"480p": "/videos/video_123_480p.mp4"
|
||||
},
|
||||
"hls_url": "/hls/video_123/master.m3u8"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Upload Video**
|
||||
```bash
|
||||
POST /api/v1/videos
|
||||
```
|
||||
|
||||
**Multipart form data:**
|
||||
```bash
|
||||
curl -X POST "https://yourdomain.com/api/v1/videos" \
|
||||
-H "Authorization: Bearer your_token" \
|
||||
-F "video=@video.mp4" \
|
||||
-F "title=My Amazing Video" \
|
||||
-F "description=This is my video description" \
|
||||
-F "category=entertainment" \
|
||||
-F "tags=fun,awesome,video" \
|
||||
-F "privacy=public"
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": 201,
|
||||
"data": {
|
||||
"id": "video_456",
|
||||
"title": "My Amazing Video",
|
||||
"status": "processing",
|
||||
"upload_progress": 100,
|
||||
"processing_status": "queued"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Update Video**
|
||||
```bash
|
||||
PUT /api/v1/videos/{video_id}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```bash
|
||||
curl -X PUT "https://yourdomain.com/api/v1/videos/video_123" \
|
||||
-H "Authorization: Bearer your_token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"title": "Updated Video Title",
|
||||
"description": "Updated description",
|
||||
"privacy": "unlisted"
|
||||
}'
|
||||
```
|
||||
|
||||
### **Delete Video**
|
||||
```bash
|
||||
DELETE /api/v1/videos/{video_id}
|
||||
```
|
||||
|
||||
### **Like Video**
|
||||
```bash
|
||||
POST /api/v1/videos/{video_id}/like
|
||||
```
|
||||
|
||||
### **Comment on Video**
|
||||
```bash
|
||||
POST /api/v1/videos/{video_id}/comment
|
||||
```
|
||||
|
||||
**Body:**
|
||||
```json
|
||||
{
|
||||
"comment": "Great video! Love the content."
|
||||
}
|
||||
```
|
||||
|
||||
### **Get Video Comments**
|
||||
```bash
|
||||
GET /api/v1/videos/{video_id}/comments?page=1&limit=20
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 👤 **User Endpoints**
|
||||
|
||||
### **Get Users**
|
||||
```bash
|
||||
GET /api/v1/users?page=1&limit=20&search=username
|
||||
```
|
||||
|
||||
### **Get User Profile**
|
||||
```bash
|
||||
GET /api/v1/users/{user_id}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"data": {
|
||||
"id": 123,
|
||||
"username": "gamer_pro",
|
||||
"display_name": "Pro Gamer",
|
||||
"email": "gamer@example.com",
|
||||
"avatar_url": "/avatars/123.jpg",
|
||||
"bio": "Professional gamer and content creator",
|
||||
"followers_count": 1250,
|
||||
"following_count": 89,
|
||||
"videos_count": 45,
|
||||
"total_views": 125000,
|
||||
"joined_at": "2024-01-15 09:30:00",
|
||||
"is_verified": true,
|
||||
"social_links": {
|
||||
"twitter": "https://twitter.com/gamer_pro",
|
||||
"youtube": "https://youtube.com/c/gamerpro"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Update Profile**
|
||||
```bash
|
||||
PUT /api/v1/users/me
|
||||
```
|
||||
|
||||
**Body:**
|
||||
```json
|
||||
{
|
||||
"display_name": "New Display Name",
|
||||
"bio": "Updated bio",
|
||||
"social_links": {
|
||||
"twitter": "https://twitter.com/newhandle"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Follow User**
|
||||
```bash
|
||||
POST /api/v1/users/{user_id}/follow
|
||||
```
|
||||
|
||||
### **Unfollow User**
|
||||
```bash
|
||||
DELETE /api/v1/users/{user_id}/follow
|
||||
```
|
||||
|
||||
### **Get User's Videos**
|
||||
```bash
|
||||
GET /api/v1/users/{user_id}/videos?page=1&limit=20
|
||||
```
|
||||
|
||||
### **Get User's Followers**
|
||||
```bash
|
||||
GET /api/v1/users/{user_id}/followers?page=1&limit=20
|
||||
```
|
||||
|
||||
### **Get User's Following**
|
||||
```bash
|
||||
GET /api/v1/users/{user_id}/following?page=1&limit=20
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎥 **Live Streaming Endpoints**
|
||||
|
||||
### **Get Live Streams**
|
||||
```bash
|
||||
GET /api/v1/live?category=gaming&status=live
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"data": {
|
||||
"streams": [
|
||||
{
|
||||
"id": 789,
|
||||
"title": "Live Gaming Session",
|
||||
"description": "Playing the latest games",
|
||||
"category": "gaming",
|
||||
"status": "live",
|
||||
"viewer_count": 45,
|
||||
"started_at": "2025-01-18 14:30:00",
|
||||
"streamer": {
|
||||
"username": "live_gamer",
|
||||
"display_name": "Live Gamer"
|
||||
},
|
||||
"thumbnail_url": "/live/thumbnails/789.jpg",
|
||||
"hls_url": "https://yourdomain.com/live/hls/stream_789.m3u8"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Create Live Stream**
|
||||
```bash
|
||||
POST /api/v1/live
|
||||
```
|
||||
|
||||
**Body:**
|
||||
```json
|
||||
{
|
||||
"title": "My Live Stream",
|
||||
"description": "Live gaming session",
|
||||
"category": "gaming",
|
||||
"privacy": "public"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": 201,
|
||||
"data": {
|
||||
"stream_id": 789,
|
||||
"stream_key": "stream_123_abc456_1642518000",
|
||||
"rtmp_url": "rtmp://yourdomain.com:1935/live",
|
||||
"stream_url": "rtmp://yourdomain.com:1935/live/stream_123_abc456_1642518000",
|
||||
"hls_url": "https://yourdomain.com/live/hls/stream_123_abc456_1642518000.m3u8",
|
||||
"dashboard_url": "/live/dashboard/789"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Get Stream Details**
|
||||
```bash
|
||||
GET /api/v1/live/{stream_id}
|
||||
```
|
||||
|
||||
### **Start Stream**
|
||||
```bash
|
||||
POST /api/v1/live/{stream_id}/start
|
||||
```
|
||||
|
||||
### **Stop Stream**
|
||||
```bash
|
||||
POST /api/v1/live/{stream_id}/stop
|
||||
```
|
||||
|
||||
### **Update Stream**
|
||||
```bash
|
||||
PUT /api/v1/live/{stream_id}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 **Search Endpoint**
|
||||
|
||||
### **Search Content**
|
||||
```bash
|
||||
GET /api/v1/search?q=gaming&type=videos&limit=20
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `q` (string): Search query
|
||||
- `type` (string): Content type (`videos`, `users`, `channels`, `all`)
|
||||
- `category` (string): Filter by category
|
||||
- `sort` (string): Sort order (`relevance`, `recent`, `popular`)
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"data": {
|
||||
"query": "gaming",
|
||||
"results": {
|
||||
"videos": [...],
|
||||
"users": [...],
|
||||
"total_results": 150
|
||||
},
|
||||
"suggestions": ["gaming highlights", "gaming tutorial", "gaming review"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 **Analytics Endpoints**
|
||||
|
||||
### **Get Platform Overview**
|
||||
```bash
|
||||
GET /api/v1/analytics/overview?start_date=2025-01-01&end_date=2025-01-31
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": 200,
|
||||
"data": {
|
||||
"users": {
|
||||
"total_users": 10000,
|
||||
"active_users": 2500,
|
||||
"new_users": 150
|
||||
},
|
||||
"content": {
|
||||
"total_videos": 5000,
|
||||
"new_videos": 45,
|
||||
"total_views": 1000000
|
||||
},
|
||||
"engagement": {
|
||||
"total_likes": 50000,
|
||||
"total_comments": 25000,
|
||||
"total_shares": 5000
|
||||
},
|
||||
"streaming": {
|
||||
"total_streams": 500,
|
||||
"live_streams": 12,
|
||||
"total_viewers": 1500
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### **Get Video Analytics**
|
||||
```bash
|
||||
GET /api/v1/analytics/videos/{video_id}?period=30d
|
||||
```
|
||||
|
||||
### **Get User Analytics**
|
||||
```bash
|
||||
GET /api/v1/analytics/users/{user_id}?period=7d
|
||||
```
|
||||
|
||||
### **Generate Report**
|
||||
```bash
|
||||
POST /api/v1/analytics/reports
|
||||
```
|
||||
|
||||
**Body:**
|
||||
```json
|
||||
{
|
||||
"report_type": "user_growth",
|
||||
"parameters": {
|
||||
"start_date": "2025-01-01",
|
||||
"end_date": "2025-01-31",
|
||||
"format": "json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 **Error Handling**
|
||||
|
||||
### **HTTP Status Codes**
|
||||
- `200` - Success
|
||||
- `201` - Created
|
||||
- `400` - Bad Request
|
||||
- `401` - Unauthorized
|
||||
- `403` - Forbidden
|
||||
- `404` - Not Found
|
||||
- `405` - Method Not Allowed
|
||||
- `429` - Rate Limited
|
||||
- `500` - Internal Server Error
|
||||
|
||||
### **Error Response Format**
|
||||
```json
|
||||
{
|
||||
"status": 400,
|
||||
"data": {
|
||||
"error": "Invalid request parameters",
|
||||
"details": {
|
||||
"field": "title",
|
||||
"message": "Title is required"
|
||||
}
|
||||
},
|
||||
"timestamp": 1642518000,
|
||||
"version": "v1"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💻 **Code Examples**
|
||||
|
||||
### **JavaScript/Node.js**
|
||||
```javascript
|
||||
// Login and get token
|
||||
const login = async (username, password) => {
|
||||
const response = await fetch('https://yourdomain.com/api/v1/auth/login', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ username, password })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return data.data.token;
|
||||
};
|
||||
|
||||
// Get videos
|
||||
const getVideos = async (token, limit = 20) => {
|
||||
const response = await fetch(`https://yourdomain.com/api/v1/videos?limit=${limit}`, {
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
return data.data.videos;
|
||||
};
|
||||
|
||||
// Upload video
|
||||
const uploadVideo = async (token, videoFile, title, description) => {
|
||||
const formData = new FormData();
|
||||
formData.append('video', videoFile);
|
||||
formData.append('title', title);
|
||||
formData.append('description', description);
|
||||
|
||||
const response = await fetch('https://yourdomain.com/api/v1/videos', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': `Bearer ${token}` },
|
||||
body: formData
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
```
|
||||
|
||||
### **Python**
|
||||
```python
|
||||
import requests
|
||||
|
||||
class EasyStreamAPI:
|
||||
def __init__(self, base_url):
|
||||
self.base_url = base_url.rstrip('/')
|
||||
self.token = None
|
||||
|
||||
def login(self, username, password):
|
||||
response = requests.post(f'{self.base_url}/api/v1/auth/login', json={
|
||||
'username': username,
|
||||
'password': password
|
||||
})
|
||||
|
||||
if response.status_code == 200:
|
||||
self.token = response.json()['data']['token']
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_videos(self, limit=20, category=None):
|
||||
headers = {'Authorization': f'Bearer {self.token}'}
|
||||
params = {'limit': limit}
|
||||
if category:
|
||||
params['category'] = category
|
||||
|
||||
response = requests.get(f'{self.base_url}/api/v1/videos',
|
||||
headers=headers, params=params)
|
||||
|
||||
if response.status_code == 200:
|
||||
return response.json()['data']['videos']
|
||||
return []
|
||||
|
||||
def upload_video(self, video_path, title, description):
|
||||
headers = {'Authorization': f'Bearer {self.token}'}
|
||||
|
||||
with open(video_path, 'rb') as video_file:
|
||||
files = {'video': video_file}
|
||||
data = {'title': title, 'description': description}
|
||||
|
||||
response = requests.post(f'{self.base_url}/api/v1/videos',
|
||||
headers=headers, files=files, data=data)
|
||||
|
||||
return response.json()
|
||||
|
||||
# Usage
|
||||
api = EasyStreamAPI('https://yourdomain.com')
|
||||
api.login('username', 'password')
|
||||
videos = api.get_videos(limit=10, category='gaming')
|
||||
```
|
||||
|
||||
### **PHP**
|
||||
```php
|
||||
class EasyStreamAPI {
|
||||
private $baseUrl;
|
||||
private $token;
|
||||
|
||||
public function __construct($baseUrl) {
|
||||
$this->baseUrl = rtrim($baseUrl, '/');
|
||||
}
|
||||
|
||||
public function login($username, $password) {
|
||||
$data = json_encode(['username' => $username, 'password' => $password]);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $this->baseUrl . '/api/v1/auth/login',
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => $data,
|
||||
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
|
||||
CURLOPT_RETURNTRANSFER => true
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
$result = json_decode($response, true);
|
||||
if ($result['status'] === 200) {
|
||||
$this->token = $result['data']['token'];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getVideos($limit = 20, $category = null) {
|
||||
$url = $this->baseUrl . '/api/v1/videos?limit=' . $limit;
|
||||
if ($category) {
|
||||
$url .= '&category=' . urlencode($category);
|
||||
}
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $this->token],
|
||||
CURLOPT_RETURNTRANSFER => true
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
$result = json_decode($response, true);
|
||||
return $result['status'] === 200 ? $result['data']['videos'] : [];
|
||||
}
|
||||
}
|
||||
|
||||
// Usage
|
||||
$api = new EasyStreamAPI('https://yourdomain.com');
|
||||
$api->login('username', 'password');
|
||||
$videos = $api->getVideos(10, 'gaming');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 **Content Syndication Use Cases**
|
||||
|
||||
### **1. Cross-Platform Posting**
|
||||
Extract video metadata and post to YouTube, TikTok, Twitter, Facebook:
|
||||
|
||||
```python
|
||||
# Get video data
|
||||
videos = api.get_videos(limit=10, category='gaming')
|
||||
|
||||
for video in videos:
|
||||
# Extract metadata
|
||||
title = video['title']
|
||||
description = video['description']
|
||||
tags = video.get('tags', [])
|
||||
|
||||
# Post to YouTube
|
||||
youtube_api.upload_video(title, description, tags)
|
||||
|
||||
# Post to Twitter
|
||||
tweet = f"🎥 {title[:100]}... Watch: {video['video_url']}"
|
||||
twitter_api.post_tweet(tweet)
|
||||
|
||||
# Post to Facebook
|
||||
facebook_api.post_video_link(title, description, video['video_url'])
|
||||
```
|
||||
|
||||
### **2. Content Aggregation**
|
||||
Build content aggregation services:
|
||||
|
||||
```javascript
|
||||
// Aggregate content from multiple EasyStream instances
|
||||
const instances = [
|
||||
'https://gaming.example.com',
|
||||
'https://music.example.com',
|
||||
'https://education.example.com'
|
||||
];
|
||||
|
||||
const aggregatedContent = [];
|
||||
|
||||
for (const instance of instances) {
|
||||
const api = new EasyStreamAPI(instance);
|
||||
const videos = await api.getVideos(20);
|
||||
aggregatedContent.push(...videos);
|
||||
}
|
||||
|
||||
// Sort by popularity and recency
|
||||
aggregatedContent.sort((a, b) => b.views - a.views);
|
||||
```
|
||||
|
||||
### **3. Analytics Dashboard**
|
||||
Build external analytics dashboards:
|
||||
|
||||
```python
|
||||
# Collect analytics from multiple channels
|
||||
analytics_data = []
|
||||
|
||||
for user_id in user_ids:
|
||||
user_analytics = api.get_user_analytics(user_id, period='30d')
|
||||
analytics_data.append(user_analytics)
|
||||
|
||||
# Generate reports
|
||||
generate_performance_report(analytics_data)
|
||||
send_weekly_summary_email(analytics_data)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ **SDK and Tools**
|
||||
|
||||
### **Official SDKs** (Coming Soon)
|
||||
- JavaScript/TypeScript SDK
|
||||
- Python SDK
|
||||
- PHP SDK
|
||||
- Mobile SDKs (iOS/Android)
|
||||
|
||||
### **Postman Collection**
|
||||
Import the EasyStream API collection for easy testing:
|
||||
```
|
||||
https://yourdomain.com/api/v1/postman-collection.json
|
||||
```
|
||||
|
||||
### **OpenAPI Specification**
|
||||
Full API specification available at:
|
||||
```
|
||||
https://yourdomain.com/api/v1/openapi.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔒 **Security Best Practices**
|
||||
|
||||
### **1. Token Management**
|
||||
- Store JWT tokens securely
|
||||
- Refresh tokens before expiration
|
||||
- Never expose tokens in client-side code
|
||||
|
||||
### **2. Rate Limiting**
|
||||
- Implement client-side rate limiting
|
||||
- Handle 429 responses gracefully
|
||||
- Use exponential backoff for retries
|
||||
|
||||
### **3. Input Validation**
|
||||
- Validate all input data
|
||||
- Sanitize user-generated content
|
||||
- Use HTTPS for all requests
|
||||
|
||||
### **4. Error Handling**
|
||||
- Handle all HTTP status codes
|
||||
- Implement proper error logging
|
||||
- Provide user-friendly error messages
|
||||
|
||||
---
|
||||
|
||||
## 📞 **Support and Resources**
|
||||
|
||||
### **API Status**
|
||||
Check API status and uptime:
|
||||
```
|
||||
https://yourdomain.com/api/v1/status
|
||||
```
|
||||
|
||||
### **Rate Limit Status**
|
||||
Check your current rate limit status:
|
||||
```
|
||||
https://yourdomain.com/api/v1/rate-limit-status
|
||||
```
|
||||
|
||||
### **Documentation Updates**
|
||||
This documentation is updated regularly. Check the version and last updated date:
|
||||
```json
|
||||
{
|
||||
"version": "v1",
|
||||
"last_updated": "2025-01-18",
|
||||
"documentation_version": "1.0.0"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎉 **Conclusion**
|
||||
|
||||
The EasyStream API provides comprehensive access to all platform features, enabling you to:
|
||||
|
||||
✅ **Build mobile applications** with full video platform functionality
|
||||
✅ **Create content syndication tools** for cross-platform posting
|
||||
✅ **Develop analytics dashboards** with real-time metrics
|
||||
✅ **Integrate live streaming** into your applications
|
||||
✅ **Automate content management** workflows
|
||||
✅ **Build third-party integrations** and tools
|
||||
|
||||
**The API is production-ready and designed for scalability, security, and ease of use.**
|
||||
|
||||
For additional support, examples, or feature requests, please refer to the platform documentation or contact the development team.
|
||||
|
||||
**🚀 Start building amazing applications with the EasyStream API today!**
|
||||
@@ -0,0 +1,104 @@
|
||||
# Enable PowerShell Script Execution
|
||||
|
||||
You need to enable PowerShell script execution to use the Continuous Delivery system.
|
||||
|
||||
## Quick Fix (Recommended)
|
||||
|
||||
Run this **one-time command** in PowerShell **as Administrator**:
|
||||
|
||||
```powershell
|
||||
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
```
|
||||
|
||||
This allows you to run local scripts while still protecting against remote scripts.
|
||||
|
||||
### Step-by-Step:
|
||||
|
||||
1. **Right-click** the **Start Menu** or press `Win+X`
|
||||
2. Select **"Windows PowerShell (Admin)"** or **"Terminal (Admin)"**
|
||||
3. When UAC prompts, click **Yes**
|
||||
4. Run: `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser`
|
||||
5. Type `Y` and press Enter
|
||||
6. Close the admin window
|
||||
7. Go back to your normal PowerShell terminal
|
||||
8. Run: `.\start-cd.ps1 watch`
|
||||
|
||||
## Alternative: No Admin Required (Bypass Method)
|
||||
|
||||
If you **can't get admin access**, use the bypass method for each run:
|
||||
|
||||
```powershell
|
||||
# Instead of:
|
||||
.\start-cd.ps1 watch
|
||||
|
||||
# Use this:
|
||||
powershell -ExecutionPolicy Bypass -File .\start-cd.ps1 watch
|
||||
```
|
||||
|
||||
### Create a Shortcut (No Admin)
|
||||
|
||||
Create `run-cd-watch.bat` with this content:
|
||||
|
||||
```batch
|
||||
@echo off
|
||||
powershell -ExecutionPolicy Bypass -File "%~dp0start-cd.ps1" watch
|
||||
pause
|
||||
```
|
||||
|
||||
Then just double-click `run-cd-watch.bat` to start CD!
|
||||
|
||||
## Verify It's Working
|
||||
|
||||
After enabling, run:
|
||||
|
||||
```powershell
|
||||
Get-ExecutionPolicy
|
||||
```
|
||||
|
||||
Should show: `RemoteSigned` or `Bypass`
|
||||
|
||||
Then test the CD system:
|
||||
|
||||
```powershell
|
||||
.\start-cd.ps1 watch
|
||||
```
|
||||
|
||||
## Security Notes
|
||||
|
||||
**What does RemoteSigned mean?**
|
||||
- ✅ You can run scripts you create locally
|
||||
- ✅ You can run scripts from your organization
|
||||
- ⚠️ Downloaded scripts must be signed by a trusted publisher
|
||||
- ✅ This is the Microsoft-recommended policy for developers
|
||||
|
||||
**Is this safe?**
|
||||
Yes! `RemoteSigned` is the recommended policy for developers. It protects you from running malicious scripts downloaded from the internet while allowing you to run your own scripts.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Still getting "UnauthorizedAccess"?**
|
||||
|
||||
Try this sequence:
|
||||
|
||||
```powershell
|
||||
# Check current policy
|
||||
Get-ExecutionPolicy -List
|
||||
|
||||
# Set for current user only (no admin needed)
|
||||
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
|
||||
# If that fails, use bypass method
|
||||
powershell -ExecutionPolicy Bypass -File .\start-cd.ps1 watch
|
||||
```
|
||||
|
||||
**Group Policy is blocking?**
|
||||
|
||||
Some corporate environments block script execution via Group Policy. If you see "cannot be changed" errors, you must use the bypass method:
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File .\start-cd.ps1 watch
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Once enabled**, you're ready to use the CD system! See [CD_QUICK_START.md](CD_QUICK_START.md) for usage.
|
||||
@@ -0,0 +1,739 @@
|
||||
# EasyStream Design System - Integration Snippets
|
||||
|
||||
Quick copy-paste snippets to integrate the new design system into EasyStream templates.
|
||||
|
||||
## Table of Contents
|
||||
1. [HTML Head Updates](#html-head-updates)
|
||||
2. [Skip Links](#skip-links)
|
||||
3. [Theme Switcher UI](#theme-switcher-ui)
|
||||
4. [Accessibility Improvements](#accessibility-improvements)
|
||||
5. [Responsive Components](#responsive-components)
|
||||
|
||||
---
|
||||
|
||||
## HTML Head Updates
|
||||
|
||||
### Add to Smarty Template Headers
|
||||
|
||||
**For frontend templates** ([f_templates/tpl_frontend/tpl_head_min.tpl](f_templates/tpl_frontend/tpl_head_min.tpl)):
|
||||
|
||||
```smarty
|
||||
{* Add after existing CSS includes *}
|
||||
|
||||
<!-- Design System v2.0 -->
|
||||
<link rel="stylesheet" href="{$main_url}/f_scripts/shared/design-system.css">
|
||||
<link rel="stylesheet" href="{$main_url}/f_scripts/shared/accessibility.css">
|
||||
<link rel="stylesheet" href="{$main_url}/f_scripts/shared/responsive.css">
|
||||
|
||||
<!-- Meta tags for PWA -->
|
||||
<meta name="theme-color" content="#06a2cb">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="EasyStream">
|
||||
|
||||
<!-- Manifest -->
|
||||
<link rel="manifest" href="{$main_url}/manifest.json">
|
||||
|
||||
<!-- Preload critical fonts -->
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin>
|
||||
```
|
||||
|
||||
### Add to Footer Scripts
|
||||
|
||||
**For frontend templates** ([f_templates/tpl_frontend/tpl_footerjs_min.tpl](f_templates/tpl_frontend/tpl_footerjs_min.tpl)):
|
||||
|
||||
```smarty
|
||||
{* Add before closing body tag *}
|
||||
|
||||
<!-- Theme Switcher -->
|
||||
<script src="{$main_url}/f_scripts/shared/theme-switcher.js"></script>
|
||||
|
||||
<!-- Service Worker Registration (already in index.js but ensure it's loaded) -->
|
||||
<script>
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/sw.js?v=2')
|
||||
.then(reg => console.log('[SW] Registered'))
|
||||
.catch(err => console.error('[SW] Registration failed:', err));
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Skip Links
|
||||
|
||||
### Add to Body Start
|
||||
|
||||
**Add to** ([f_templates/tpl_frontend/tpl_body.tpl](f_templates/tpl_frontend/tpl_body.tpl:1)) at the very beginning:
|
||||
|
||||
```smarty
|
||||
<body class="fe media-width-768 is-fw{if $is_mobile eq 1} is-mobile{/if}" data-theme="{$theme_name|default:'blue'}">
|
||||
|
||||
{* Skip links for accessibility *}
|
||||
<div class="skip-links" role="navigation" aria-label="Skip links">
|
||||
<a href="#main-content" class="skip-to-content">Skip to main content</a>
|
||||
<a href="#navigation" class="skip-to-content">Skip to navigation</a>
|
||||
<a href="#search" class="skip-to-content">Skip to search</a>
|
||||
</div>
|
||||
|
||||
{* Rest of body content *}
|
||||
```
|
||||
|
||||
### Add Main Content ID
|
||||
|
||||
**Update main wrapper** in [f_templates/tpl_frontend/tpl_body_main.tpl](f_templates/tpl_frontend/tpl_body_main.tpl):
|
||||
|
||||
```smarty
|
||||
<main id="main-content" role="main" class="container">
|
||||
{* Your main content *}
|
||||
</main>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Theme Switcher UI
|
||||
|
||||
### Option 1: Add to Header Navigation
|
||||
|
||||
**Add to** [f_templates/tpl_frontend/tpl_header/tpl_headernav_yt.tpl](f_templates/tpl_frontend/tpl_header/tpl_headernav_yt.tpl):
|
||||
|
||||
```smarty
|
||||
{* Add to header navigation area *}
|
||||
<div class="header-controls">
|
||||
{* Theme toggle button *}
|
||||
<button
|
||||
id="theme-toggle"
|
||||
class="btn btn-secondary touch-target"
|
||||
aria-label="Toggle dark mode"
|
||||
title="Toggle dark mode">
|
||||
<i class="icon-moon"></i>
|
||||
<span class="sr-only">Toggle theme</span>
|
||||
</button>
|
||||
|
||||
{* Existing notification bell, user menu, etc. *}
|
||||
</div>
|
||||
```
|
||||
|
||||
### Option 2: Full Theme Picker Modal
|
||||
|
||||
Create new template: `f_templates/tpl_frontend/tpl_theme_picker.tpl`
|
||||
|
||||
```smarty
|
||||
{* Theme Picker Modal *}
|
||||
<div id="theme-picker-modal" class="modal" role="dialog" aria-labelledby="theme-modal-title" aria-hidden="true">
|
||||
<div class="modal-backdrop" data-dismiss="modal"></div>
|
||||
<div class="modal-content card">
|
||||
<div class="modal-header">
|
||||
<h2 id="theme-modal-title" class="text-xl font-semibold">Appearance Settings</h2>
|
||||
<button class="modal-close" data-dismiss="modal" aria-label="Close">
|
||||
<i class="icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="modal-body p-lg">
|
||||
{* Theme mode toggle *}
|
||||
<div class="theme-setting">
|
||||
<label class="theme-label flex justify-between items-center">
|
||||
<span class="font-medium">Theme Mode</span>
|
||||
<button id="theme-toggle" class="btn btn-secondary touch-target" aria-label="Toggle theme mode">
|
||||
<i class="icon-moon"></i>
|
||||
</button>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="hr m-y-md"></div>
|
||||
|
||||
{* Color picker *}
|
||||
<div class="theme-setting">
|
||||
<span class="theme-label font-medium block m-b-sm">Color Theme</span>
|
||||
<div class="color-options flex gap-sm flex-wrap" role="group" aria-label="Color themes">
|
||||
<button class="color-btn color-blue touch-target" data-color-theme="blue" aria-label="Blue theme" title="Blue">
|
||||
<span class="sr-only">Blue</span>
|
||||
</button>
|
||||
<button class="color-btn color-red touch-target" data-color-theme="red" aria-label="Red theme" title="Red">
|
||||
<span class="sr-only">Red</span>
|
||||
</button>
|
||||
<button class="color-btn color-cyan touch-target" data-color-theme="cyan" aria-label="Cyan theme" title="Cyan">
|
||||
<span class="sr-only">Cyan</span>
|
||||
</button>
|
||||
<button class="color-btn color-green touch-target" data-color-theme="green" aria-label="Green theme" title="Green">
|
||||
<span class="sr-only">Green</span>
|
||||
</button>
|
||||
<button class="color-btn color-orange touch-target" data-color-theme="orange" aria-label="Orange theme" title="Orange">
|
||||
<span class="sr-only">Orange</span>
|
||||
</button>
|
||||
<button class="color-btn color-pink touch-target" data-color-theme="pink" aria-label="Pink theme" title="Pink">
|
||||
<span class="sr-only">Pink</span>
|
||||
</button>
|
||||
<button class="color-btn color-purple touch-target" data-color-theme="purple" aria-label="Purple theme" title="Purple">
|
||||
<span class="sr-only">Purple</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.color-btn {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: var(--border-radius-full);
|
||||
border: 3px solid transparent;
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-base);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.color-btn:hover {
|
||||
transform: scale(1.1);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
.color-btn.active {
|
||||
border-color: var(--color-text-primary);
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
|
||||
.color-btn.active::after {
|
||||
content: '✓';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 1.25rem;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.color-blue { background: #06a2cb; }
|
||||
.color-red { background: #dd1e2f; }
|
||||
.color-cyan { background: #00997a; }
|
||||
.color-green { background: #199900; }
|
||||
.color-orange { background: #f28410; }
|
||||
.color-pink { background: #ec7ab9; }
|
||||
.color-purple { background: #b25c8b; }
|
||||
</style>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Accessibility Improvements
|
||||
|
||||
### Form Labels
|
||||
|
||||
**Before:**
|
||||
```html
|
||||
<input type="text" name="username" placeholder="Username">
|
||||
```
|
||||
|
||||
**After:**
|
||||
```html
|
||||
<label for="username" class="font-medium m-b-xs">
|
||||
Username
|
||||
<span class="required" aria-label="required">*</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="username"
|
||||
name="username"
|
||||
class="input"
|
||||
aria-required="true"
|
||||
aria-describedby="username-error">
|
||||
<div id="username-error" class="error-message" role="alert" style="display: none;">
|
||||
Please enter a username
|
||||
</div>
|
||||
```
|
||||
|
||||
### Image Alt Text
|
||||
|
||||
**Before:**
|
||||
```smarty
|
||||
<img src="{$video.thumbnail}">
|
||||
```
|
||||
|
||||
**After:**
|
||||
```smarty
|
||||
<img
|
||||
src="{$video.thumbnail}"
|
||||
alt="{$video.title|escape} - Thumbnail"
|
||||
loading="lazy"
|
||||
width="320"
|
||||
height="180">
|
||||
```
|
||||
|
||||
### Button Accessibility
|
||||
|
||||
**Before:**
|
||||
```html
|
||||
<button onclick="likeVideo()">
|
||||
<i class="icon-like"></i>
|
||||
</button>
|
||||
```
|
||||
|
||||
**After:**
|
||||
```html
|
||||
<button
|
||||
onclick="likeVideo()"
|
||||
class="btn btn-secondary touch-target"
|
||||
aria-label="Like this video"
|
||||
aria-pressed="false">
|
||||
<i class="icon-like" aria-hidden="true"></i>
|
||||
<span class="sr-only">Like</span>
|
||||
</button>
|
||||
```
|
||||
|
||||
### Heading Hierarchy
|
||||
|
||||
**Before:**
|
||||
```html
|
||||
<div class="title">Featured Videos</div>
|
||||
<div class="video-title">My Video</div>
|
||||
```
|
||||
|
||||
**After:**
|
||||
```html
|
||||
<h2 class="content-title text-2xl font-semibold">Featured Videos</h2>
|
||||
<h3 class="video-title text-lg">My Video</h3>
|
||||
```
|
||||
|
||||
### ARIA Landmarks
|
||||
|
||||
**Add to templates:**
|
||||
|
||||
```smarty
|
||||
<header role="banner">
|
||||
{* Header content *}
|
||||
</header>
|
||||
|
||||
<nav role="navigation" aria-label="Main navigation">
|
||||
{* Navigation menu *}
|
||||
</nav>
|
||||
|
||||
<main role="main" id="main-content">
|
||||
{* Main content *}
|
||||
</main>
|
||||
|
||||
<aside role="complementary" aria-label="Sidebar">
|
||||
{* Sidebar content *}
|
||||
</aside>
|
||||
|
||||
<footer role="contentinfo">
|
||||
{* Footer content *}
|
||||
</footer>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Responsive Components
|
||||
|
||||
### Video Grid
|
||||
|
||||
**Before:**
|
||||
```smarty
|
||||
<div class="thumbs-wrapper">
|
||||
{foreach from=$videos item=video}
|
||||
<div class="vs-column">
|
||||
{* Video thumbnail *}
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
```
|
||||
|
||||
**After:**
|
||||
```smarty
|
||||
<div class="video-grid">
|
||||
{foreach from=$videos item=video}
|
||||
<article class="video-card">
|
||||
<a href="{$video.url}" class="video-link">
|
||||
<div class="aspect-video">
|
||||
<img
|
||||
src="{$video.thumbnail}"
|
||||
alt="{$video.title|escape} - Thumbnail"
|
||||
loading="lazy"
|
||||
class="video-thumbnail">
|
||||
</div>
|
||||
<div class="video-info p-sm">
|
||||
<h3 class="video-title text-md font-medium">{$video.title}</h3>
|
||||
<p class="video-meta text-sm text-secondary">
|
||||
<span>{$video.views} views</span>
|
||||
<span aria-hidden="true">•</span>
|
||||
<span>{$video.date}</span>
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</article>
|
||||
{/foreach}
|
||||
</div>
|
||||
```
|
||||
|
||||
### Responsive Container
|
||||
|
||||
**Before:**
|
||||
```html
|
||||
<div class="inner-block">
|
||||
{* Content *}
|
||||
</div>
|
||||
```
|
||||
|
||||
**After:**
|
||||
```html
|
||||
<div class="container">
|
||||
{* Content auto-sizes with padding *}
|
||||
</div>
|
||||
```
|
||||
|
||||
### Flex Layout
|
||||
|
||||
**Before:**
|
||||
```html
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<div>{$title}</div>
|
||||
<div>{$actions}</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**After:**
|
||||
```html
|
||||
<div class="flex justify-between items-center gap-md">
|
||||
<div>{$title}</div>
|
||||
<div>{$actions}</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Responsive Text
|
||||
|
||||
**Before:**
|
||||
```html
|
||||
<h1 style="font-size: 36px;">{$title}</h1>
|
||||
```
|
||||
|
||||
**After:**
|
||||
```html
|
||||
<h1 class="text-responsive-xl font-bold">{$title}</h1>
|
||||
```
|
||||
|
||||
### Responsive Spacing
|
||||
|
||||
**Before:**
|
||||
```html
|
||||
<div style="padding: 16px; margin-bottom: 24px;">
|
||||
{* Content *}
|
||||
</div>
|
||||
```
|
||||
|
||||
**After:**
|
||||
```html
|
||||
<div class="p-responsive m-b-lg">
|
||||
{* Content *}
|
||||
</div>
|
||||
```
|
||||
|
||||
### Card Component
|
||||
|
||||
**New pattern:**
|
||||
```html
|
||||
<div class="card shadow-md">
|
||||
<div class="card-header p-md">
|
||||
<h2 class="text-lg font-semibold">Card Title</h2>
|
||||
</div>
|
||||
<div class="card-body p-lg">
|
||||
{* Card content *}
|
||||
</div>
|
||||
<div class="card-footer p-md">
|
||||
<button class="btn btn-primary">Action</button>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Alert Messages
|
||||
|
||||
**Before:**
|
||||
```smarty
|
||||
{if $error_message}
|
||||
<div class="error-message-text">{$error_message}</div>
|
||||
{/if}
|
||||
```
|
||||
|
||||
**After:**
|
||||
```smarty
|
||||
{if $error_message}
|
||||
<div class="alert alert-error" role="alert">
|
||||
<i class="icon-warning" aria-hidden="true"></i>
|
||||
<span>{$error_message}</span>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{if $success_message}
|
||||
<div class="alert alert-success" role="alert">
|
||||
<i class="icon-check" aria-hidden="true"></i>
|
||||
<span>{$success_message}</span>
|
||||
</div>
|
||||
{/if}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## JavaScript Enhancements
|
||||
|
||||
### Theme Switcher Events
|
||||
|
||||
```javascript
|
||||
// Listen for theme changes
|
||||
document.addEventListener('easystream:theme-change', (e) => {
|
||||
console.log('Theme changed:', e.detail);
|
||||
// Update other components if needed
|
||||
});
|
||||
|
||||
// Programmatically change theme
|
||||
window.themeSwitcher.toggleMode(); // Toggle light/dark
|
||||
window.themeSwitcher.setColor('red'); // Change color
|
||||
|
||||
// Get current theme
|
||||
const theme = window.themeSwitcher.getCurrentTheme();
|
||||
console.log(theme); // { mode: 'dark', color: 'blue', ... }
|
||||
```
|
||||
|
||||
### Service Worker Updates
|
||||
|
||||
```javascript
|
||||
// Update service worker
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.register('/sw.js?v=2')
|
||||
.then(reg => {
|
||||
// Check for updates
|
||||
reg.update();
|
||||
|
||||
// Listen for updates
|
||||
reg.addEventListener('updatefound', () => {
|
||||
const newWorker = reg.installing;
|
||||
newWorker.addEventListener('statechange', () => {
|
||||
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
|
||||
// New version available
|
||||
if (confirm('New version available! Reload to update?')) {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Offline Detection
|
||||
|
||||
```javascript
|
||||
// Detect online/offline
|
||||
window.addEventListener('online', () => {
|
||||
console.log('Back online!');
|
||||
// Show success message
|
||||
showNotification('You are back online', 'success');
|
||||
});
|
||||
|
||||
window.addEventListener('offline', () => {
|
||||
console.log('Gone offline');
|
||||
// Show warning message
|
||||
showNotification('You are offline. Some features may be unavailable.', 'warning');
|
||||
});
|
||||
|
||||
function showNotification(message, type) {
|
||||
const alert = document.createElement('div');
|
||||
alert.className = `alert alert-${type}`;
|
||||
alert.textContent = message;
|
||||
alert.role = 'alert';
|
||||
document.body.appendChild(alert);
|
||||
|
||||
setTimeout(() => alert.remove(), 5000);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Snippets
|
||||
|
||||
### Check Accessibility
|
||||
|
||||
```javascript
|
||||
// Check for images without alt text
|
||||
const imagesWithoutAlt = document.querySelectorAll('img:not([alt])');
|
||||
console.log('Images missing alt text:', imagesWithoutAlt.length);
|
||||
|
||||
// Check for buttons without labels
|
||||
const buttonsWithoutLabel = document.querySelectorAll('button:not([aria-label]):not(:has(.sr-only))');
|
||||
console.log('Buttons missing labels:', buttonsWithoutLabel.length);
|
||||
|
||||
// Check heading hierarchy
|
||||
const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
|
||||
headings.forEach(h => console.log(h.tagName, h.textContent.substring(0, 50)));
|
||||
```
|
||||
|
||||
### Check Contrast Ratios
|
||||
|
||||
```javascript
|
||||
// Check text contrast (simplified)
|
||||
function checkContrast(element) {
|
||||
const style = getComputedStyle(element);
|
||||
const color = style.color;
|
||||
const bgColor = style.backgroundColor;
|
||||
console.log(`Element: ${element.tagName}`, { color, bgColor });
|
||||
}
|
||||
|
||||
// Check all text elements
|
||||
document.querySelectorAll('p, h1, h2, h3, h4, h5, h6, a, button, span').forEach(checkContrast);
|
||||
```
|
||||
|
||||
### Test Keyboard Navigation
|
||||
|
||||
```javascript
|
||||
// Highlight focusable elements
|
||||
document.querySelectorAll('a, button, input, select, textarea, [tabindex]').forEach(el => {
|
||||
el.style.outline = '2px solid red';
|
||||
});
|
||||
|
||||
// Tab order test
|
||||
let tabIndex = 0;
|
||||
document.addEventListener('focus', (e) => {
|
||||
console.log(`Tab ${++tabIndex}:`, e.target);
|
||||
}, true);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Modal Dialog
|
||||
|
||||
```html
|
||||
<div id="my-modal" class="modal" role="dialog" aria-labelledby="modal-title" aria-hidden="true">
|
||||
<div class="modal-backdrop" data-dismiss="modal"></div>
|
||||
<div class="modal-content card shadow-xl">
|
||||
<div class="modal-header p-md flex justify-between items-center">
|
||||
<h2 id="modal-title" class="text-xl font-semibold">Modal Title</h2>
|
||||
<button class="modal-close btn btn-secondary" data-dismiss="modal" aria-label="Close">
|
||||
<i class="icon-close"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body p-lg">
|
||||
{* Modal content *}
|
||||
</div>
|
||||
<div class="modal-footer p-md flex justify-end gap-sm">
|
||||
<button class="btn btn-secondary" data-dismiss="modal">Cancel</button>
|
||||
<button class="btn btn-primary">Confirm</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Dropdown Menu
|
||||
|
||||
```html
|
||||
<div class="dropdown">
|
||||
<button
|
||||
class="btn btn-secondary dropdown-toggle touch-target"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
id="dropdown-menu-btn">
|
||||
Menu <i class="icon-chevron-down"></i>
|
||||
</button>
|
||||
<ul
|
||||
class="dropdown-menu"
|
||||
role="menu"
|
||||
aria-labelledby="dropdown-menu-btn"
|
||||
hidden>
|
||||
<li role="none">
|
||||
<a href="#" role="menuitem" class="dropdown-item">Option 1</a>
|
||||
</li>
|
||||
<li role="none">
|
||||
<a href="#" role="menuitem" class="dropdown-item">Option 2</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Loading Spinner
|
||||
|
||||
```html
|
||||
<div class="loading-spinner" role="status" aria-live="polite">
|
||||
<i class="icon-spinner spinner"></i>
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Breadcrumbs
|
||||
|
||||
```html
|
||||
<nav aria-label="Breadcrumb">
|
||||
<ol class="breadcrumb flex gap-xs items-center">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li aria-hidden="true">/</li>
|
||||
<li><a href="/videos">Videos</a></li>
|
||||
<li aria-hidden="true">/</li>
|
||||
<li aria-current="page">Current Page</li>
|
||||
</ol>
|
||||
</nav>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Class Names Cheat Sheet
|
||||
|
||||
**Spacing:**
|
||||
- `m-xs`, `m-sm`, `m-md`, `m-lg` - Margins
|
||||
- `p-xs`, `p-sm`, `p-md`, `p-lg` - Padding
|
||||
|
||||
**Typography:**
|
||||
- `text-xs`, `text-sm`, `text-md`, `text-lg`, `text-xl` - Font sizes
|
||||
- `font-light`, `font-normal`, `font-medium`, `font-bold` - Font weights
|
||||
|
||||
**Layout:**
|
||||
- `flex`, `flex-col`, `flex-wrap` - Flexbox
|
||||
- `grid`, `grid-cols-{n}` - Grid
|
||||
- `container` - Responsive container
|
||||
|
||||
**Display:**
|
||||
- `hidden`, `block`, `flex` - Display
|
||||
- `xs:hidden`, `md:block` - Responsive display
|
||||
|
||||
**Colors:**
|
||||
- `text-primary`, `text-secondary` - Text colors
|
||||
- `bg-primary`, `bg-secondary` - Background colors
|
||||
|
||||
**Borders:**
|
||||
- `rounded-sm`, `rounded-md`, `rounded-lg`, `rounded-full` - Border radius
|
||||
|
||||
**Shadows:**
|
||||
- `shadow-sm`, `shadow-md`, `shadow-lg` - Box shadows
|
||||
|
||||
**Accessibility:**
|
||||
- `sr-only` - Screen reader only
|
||||
- `touch-target` - Minimum touch size
|
||||
- `focus-visible` - Focus indicator
|
||||
|
||||
---
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
- [ ] Include new CSS files in templates
|
||||
- [ ] Include theme-switcher.js
|
||||
- [ ] Add skip links to body
|
||||
- [ ] Add main content ID
|
||||
- [ ] Update navigation with theme toggle
|
||||
- [ ] Replace inline styles with utility classes
|
||||
- [ ] Add alt text to all images
|
||||
- [ ] Add ARIA labels to buttons
|
||||
- [ ] Add labels to form inputs
|
||||
- [ ] Fix heading hierarchy
|
||||
- [ ] Add ARIA landmarks
|
||||
- [ ] Test keyboard navigation
|
||||
- [ ] Test with screen reader
|
||||
- [ ] Test on mobile devices
|
||||
- [ ] Test all theme combinations
|
||||
- [ ] Run Lighthouse audit
|
||||
|
||||
---
|
||||
|
||||
**Next Steps:** See [DESIGN_SYSTEM_GUIDE.md](DESIGN_SYSTEM_GUIDE.md) for complete documentation.
|
||||
@@ -0,0 +1,205 @@
|
||||
# EasyStream - Quick Start Guide
|
||||
|
||||
## 🚀 Get Started in 3 Minutes
|
||||
|
||||
### Option 1: Automated Deployment (Recommended)
|
||||
|
||||
```powershell
|
||||
# Test configuration
|
||||
.\deploy.ps1 -Mode test
|
||||
|
||||
# Deploy for development
|
||||
.\deploy.ps1 -Mode dev
|
||||
|
||||
# Deploy for production (after configuring secrets)
|
||||
.\deploy.ps1 -Mode prod
|
||||
```
|
||||
|
||||
That's it! Access at **http://localhost:8083**
|
||||
|
||||
---
|
||||
|
||||
### Option 2: Manual Deployment
|
||||
|
||||
#### Step 1: Start Services
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
#### Step 2: Wait for Database (2-3 minutes)
|
||||
```bash
|
||||
docker-compose logs -f db
|
||||
```
|
||||
Wait until you see: "ready for connections"
|
||||
|
||||
#### Step 3: Access Application
|
||||
- Frontend: http://localhost:8083
|
||||
- Admin: http://localhost:8083/admin
|
||||
- Login: `admin` / `admin123` (⚠️ change immediately!)
|
||||
|
||||
---
|
||||
|
||||
## 📁 Folder Sync (Repos ↔ Docker-Progs)
|
||||
|
||||
### One-Time Sync
|
||||
```bash
|
||||
.\sync-to-docker-progs.bat
|
||||
```
|
||||
|
||||
### Continuous Sync (Watch Mode)
|
||||
```bash
|
||||
.\sync-to-docker-progs.bat watch
|
||||
```
|
||||
|
||||
This keeps `E:\repos\easystream-main` and `E:\docker-progs\easystream-main` in sync automatically.
|
||||
|
||||
---
|
||||
|
||||
## 🔑 Production Setup
|
||||
|
||||
### 1. Generate Secrets
|
||||
```powershell
|
||||
.\generate-secrets.ps1
|
||||
```
|
||||
|
||||
### 2. Configure Environment
|
||||
```bash
|
||||
copy .env.production .env
|
||||
# Edit .env with your domain and settings
|
||||
```
|
||||
|
||||
### 3. Deploy
|
||||
```powershell
|
||||
.\deploy.ps1 -Mode prod
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Common Commands
|
||||
|
||||
### View Logs
|
||||
```bash
|
||||
docker-compose logs -f # All services
|
||||
docker-compose logs -f php # PHP only
|
||||
docker-compose logs -f db # Database only
|
||||
```
|
||||
|
||||
### Check Status
|
||||
```bash
|
||||
docker-compose ps
|
||||
docker-compose top
|
||||
```
|
||||
|
||||
### Restart Service
|
||||
```bash
|
||||
docker-compose restart php
|
||||
docker-compose restart caddy
|
||||
```
|
||||
|
||||
### Stop Everything
|
||||
```bash
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
### Database Access
|
||||
```bash
|
||||
docker-compose exec db mysql -u easystream -peasystream easystream
|
||||
```
|
||||
|
||||
### Backup Database
|
||||
```bash
|
||||
docker-compose exec db mysqldump -u easystream -peasystream easystream | gzip > backup.sql.gz
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎥 Streaming Setup
|
||||
|
||||
### RTMP URL (for OBS/Streaming Software)
|
||||
```
|
||||
Server: rtmp://localhost:1935/live
|
||||
Stream Key: testkey
|
||||
```
|
||||
|
||||
### View Live Stream
|
||||
```
|
||||
HLS: http://localhost:8083/hls/testkey/index.m3u8
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 What's Included
|
||||
|
||||
- ✅ **270+ Database Tables** - Full schema auto-loaded
|
||||
- ✅ **Default Admin Account** - Ready to use
|
||||
- ✅ **10 Categories** - Pre-configured
|
||||
- ✅ **Template Builder** - 7 pre-built components
|
||||
- ✅ **RTMP + HLS Streaming** - Live streaming ready
|
||||
- ✅ **Redis Caching** - Performance optimized
|
||||
- ✅ **Queue System** - Background job processing
|
||||
- ✅ **Cron Jobs** - Automated tasks
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Troubleshooting
|
||||
|
||||
### Port Already in Use
|
||||
```bash
|
||||
# Change port in docker-compose.yml
|
||||
ports:
|
||||
- "8084:80" # Change 8083 to 8084
|
||||
```
|
||||
|
||||
### Database Not Ready
|
||||
```bash
|
||||
# Check health
|
||||
docker-compose ps
|
||||
|
||||
# View initialization progress
|
||||
docker-compose logs -f db
|
||||
```
|
||||
|
||||
### Upload Not Working
|
||||
```bash
|
||||
# Check permissions
|
||||
docker-compose exec php ls -la /srv/easystream/f_data/uploads
|
||||
|
||||
# Fix if needed
|
||||
docker-compose exec php chown -R www-data:www-data /srv/easystream/f_data
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Full Documentation
|
||||
|
||||
- **[DOCKER_DEPLOYMENT_GUIDE.md](DOCKER_DEPLOYMENT_GUIDE.md)** - Complete deployment guide
|
||||
- **[TEMPLATE_BUILDER_GUIDE.md](TEMPLATE_BUILDER_GUIDE.md)** - Template builder documentation
|
||||
- **[DESIGN_SYSTEM_GUIDE.md](DESIGN_SYSTEM_GUIDE.md)** - Design system usage
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Security Checklist
|
||||
|
||||
Before going to production:
|
||||
|
||||
- [ ] Change default admin password
|
||||
- [ ] Generate secure secrets (`.\generate-secrets.ps1`)
|
||||
- [ ] Update `.env` with production values
|
||||
- [ ] Enable HTTPS/SSL
|
||||
- [ ] Change database password
|
||||
- [ ] Configure firewall rules
|
||||
- [ ] Set up backups
|
||||
- [ ] Review [DOCKER_DEPLOYMENT_GUIDE.md](DOCKER_DEPLOYMENT_GUIDE.md#security-checklist)
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Need Help?
|
||||
|
||||
1. Check logs: `docker-compose logs -f`
|
||||
2. Verify services: `docker-compose ps`
|
||||
3. Review: [DOCKER_DEPLOYMENT_GUIDE.md](DOCKER_DEPLOYMENT_GUIDE.md#troubleshooting)
|
||||
4. Test configuration: `.\deploy.ps1 -Mode test`
|
||||
|
||||
---
|
||||
|
||||
**Version**: 2.0 | **Last Updated**: 2025-10-25
|
||||
@@ -0,0 +1,742 @@
|
||||
# EasyStream Settings System - Complete Guide
|
||||
|
||||
**Configure your entire platform through the admin panel - no code changes required!**
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Quick Installation](#quick-installation)
|
||||
2. [Accessing Settings](#accessing-settings)
|
||||
3. [Settings Categories](#settings-categories)
|
||||
4. [Usage Examples](#usage-examples)
|
||||
5. [Settings Reference](#settings-reference)
|
||||
6. [Advanced Features](#advanced-features)
|
||||
7. [Troubleshooting](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## Quick Installation
|
||||
|
||||
### Step 1: Run the SQL Migration
|
||||
|
||||
**Using Docker:**
|
||||
```bash
|
||||
docker exec -i easystream-db mysql -u easystream -peasystream easystream < __install/install_settings_system.sql
|
||||
```
|
||||
|
||||
**Using MySQL directly:**
|
||||
```bash
|
||||
mysql -u easystream -p easystream < __install/install_settings_system.sql
|
||||
```
|
||||
|
||||
### Step 2: Access the Settings Panel
|
||||
|
||||
1. Log in to admin panel: `http://yoursite.com/admin_login.php`
|
||||
2. Navigate to **Settings** in the sidebar
|
||||
3. Or go directly to: `http://yoursite.com/admin_settings.php`
|
||||
|
||||
### Step 3: Start Configuring
|
||||
|
||||
Choose from 8 comprehensive categories and configure your platform without touching any code!
|
||||
|
||||
---
|
||||
|
||||
## Accessing Settings
|
||||
|
||||
### Admin Panel UI
|
||||
|
||||
The settings panel provides a beautiful, organized interface with:
|
||||
|
||||
- **8 Tabbed Categories** - General, Modules, Branding, Payments, Email, Payouts, SEO, Security
|
||||
- **Live Search** - Press `Ctrl+K` (or `Cmd+K` on Mac) to instantly search settings
|
||||
- **Category Filtering** - Filter by category to focus on specific configuration areas
|
||||
- **Visual Feedback** - Color-coded inputs, helpful descriptions, validation messages
|
||||
- **Bulk Save** - Save multiple settings at once with a single click
|
||||
|
||||
### Search & Filter Features
|
||||
|
||||
- **Keyboard Shortcuts:**
|
||||
- `Ctrl+K` or `Cmd+K` - Focus search box
|
||||
- `Escape` - Clear search
|
||||
|
||||
- **Search Capabilities:**
|
||||
- Search by setting name
|
||||
- Search by label text
|
||||
- Search by help text
|
||||
- Real-time highlighting of matches
|
||||
- Result count display
|
||||
|
||||
---
|
||||
|
||||
## Settings Categories
|
||||
|
||||
### 1. General Settings
|
||||
Configure your site's basic identity and system settings.
|
||||
|
||||
**Key Settings:**
|
||||
- Site name and title
|
||||
- Admin email address
|
||||
- Main website URL
|
||||
- Debug mode toggle
|
||||
- Maintenance mode
|
||||
|
||||
**Example:**
|
||||
```php
|
||||
$settings = new Settings($pdo);
|
||||
$settings->set('website_shortname', 'MyTube');
|
||||
$settings->set('head_title', 'MyTube - Video Sharing Platform');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Modules Management
|
||||
Enable or disable platform features with visual toggle switches.
|
||||
|
||||
**Available Modules:**
|
||||
- Video uploads
|
||||
- Live streaming
|
||||
- Shorts (TikTok-style)
|
||||
- Images
|
||||
- Audio
|
||||
- Documents
|
||||
- Blogs
|
||||
- Paid memberships
|
||||
- Token economy
|
||||
|
||||
**Example:**
|
||||
```php
|
||||
$settings = new Settings($pdo);
|
||||
|
||||
// Disable blogs and documents
|
||||
$settings->setModuleEnabled('blog', false);
|
||||
$settings->setModuleEnabled('document', false);
|
||||
|
||||
// Check if a module is enabled
|
||||
if ($settings->isModuleEnabled('video')) {
|
||||
// Show video upload interface
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Branding & Appearance
|
||||
Customize your platform's look and feel.
|
||||
|
||||
**Customizable Elements:**
|
||||
- Primary color (buttons, links, accents)
|
||||
- Secondary color (highlights, hover states)
|
||||
- Logo URL
|
||||
- Favicon URL
|
||||
- Custom footer text
|
||||
|
||||
**Example:**
|
||||
```php
|
||||
$settings = new Settings($pdo);
|
||||
$settings->setMultiple([
|
||||
'branding_primary_color' => '#FF0000',
|
||||
'branding_secondary_color' => '#282828',
|
||||
'branding_logo_url' => 'https://mysite.com/logo.png',
|
||||
'branding_favicon_url' => 'https://mysite.com/favicon.ico'
|
||||
]);
|
||||
```
|
||||
|
||||
**Live Color Picker:** The admin UI includes a built-in color picker for instant visual feedback.
|
||||
|
||||
---
|
||||
|
||||
### 4. Payment Gateways
|
||||
Configure PayPal and Stripe for payments.
|
||||
|
||||
**PayPal Settings:**
|
||||
- PayPal email
|
||||
- Client ID
|
||||
- Secret key
|
||||
- Test mode toggle
|
||||
|
||||
**Stripe Settings:**
|
||||
- Publishable key
|
||||
- Secret key
|
||||
- Webhook secret
|
||||
- Enable/disable toggle
|
||||
|
||||
**Example:**
|
||||
```php
|
||||
$settings = new Settings($pdo);
|
||||
|
||||
// Enable Stripe payments
|
||||
$settings->setMultiple([
|
||||
'stripe_enabled' => '1',
|
||||
'stripe_publishable_key' => 'pk_live_xxxxx',
|
||||
'stripe_secret_key' => 'sk_live_xxxxx',
|
||||
'stripe_webhook_secret' => 'whsec_xxxxx',
|
||||
'payment_methods' => 'Paypal,Stripe'
|
||||
]);
|
||||
|
||||
// Access payment config in your code
|
||||
$paymentConfig = $settings->getPaymentConfig();
|
||||
if ($paymentConfig['stripe']['enabled']) {
|
||||
// Initialize Stripe SDK
|
||||
\Stripe\Stripe::setApiKey($paymentConfig['stripe']['secret_key']);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. Email Configuration
|
||||
Set up SMTP for transactional emails.
|
||||
|
||||
**SMTP Settings:**
|
||||
- Host (e.g., smtp.gmail.com)
|
||||
- Port (587 for TLS, 465 for SSL)
|
||||
- Username
|
||||
- Password
|
||||
- Encryption type (TLS/SSL)
|
||||
- Authentication enabled
|
||||
|
||||
**Example:**
|
||||
```php
|
||||
$settings = new Settings($pdo);
|
||||
|
||||
// Configure SendGrid SMTP
|
||||
$settings->setMultiple([
|
||||
'mail_type' => 'smtp',
|
||||
'mail_smtp_host' => 'smtp.sendgrid.net',
|
||||
'mail_smtp_port' => '587',
|
||||
'mail_smtp_username' => 'apikey',
|
||||
'mail_smtp_password' => 'your-sendgrid-api-key',
|
||||
'mail_smtp_auth' => 'true',
|
||||
'mail_smtp_prefix' => 'tls',
|
||||
'backend_email' => 'noreply@mysite.com',
|
||||
'backend_email_fromname' => 'MyTube Team'
|
||||
]);
|
||||
|
||||
// Access email config
|
||||
$emailConfig = $settings->getEmailConfig();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. Creator Payouts
|
||||
Configure revenue sharing with content creators.
|
||||
|
||||
**Payout Settings:**
|
||||
- Enable/disable payout system
|
||||
- Revenue share percentage (0-100%)
|
||||
- Minimum payout amount
|
||||
- Payout schedule (weekly, monthly, quarterly, manual)
|
||||
- Default payout method (PayPal, Stripe, bank transfer)
|
||||
|
||||
**Example:**
|
||||
```php
|
||||
$settings = new Settings($pdo);
|
||||
|
||||
// Enable creator payouts with 80% revenue share
|
||||
$settings->setMultiple([
|
||||
'creator_payout_enabled' => '1',
|
||||
'creator_payout_percentage' => '80',
|
||||
'minimum_payout_amount' => '100.00',
|
||||
'payout_schedule' => 'monthly',
|
||||
'payout_method' => 'paypal'
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. SEO & Marketing
|
||||
Optimize your site for search engines.
|
||||
|
||||
**SEO Settings:**
|
||||
- Meta description
|
||||
- Meta keywords
|
||||
- Site title optimization
|
||||
|
||||
**Example:**
|
||||
```php
|
||||
$settings = new Settings($pdo);
|
||||
$settings->setMultiple([
|
||||
'head_title' => 'MyTube - Best Video Sharing Platform',
|
||||
'metaname_description' => 'Upload, share, and watch videos on MyTube. Join millions of creators.',
|
||||
'metaname_keywords' => 'video sharing, streaming, upload videos, watch videos'
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8. Security Settings
|
||||
Configure user registration and authentication requirements.
|
||||
|
||||
**Security Controls:**
|
||||
- Minimum/maximum age for signup
|
||||
- Username length requirements
|
||||
- Password length requirements
|
||||
- Username format (strict/relaxed)
|
||||
- Remember me feature
|
||||
|
||||
**Example:**
|
||||
```php
|
||||
$settings = new Settings($pdo);
|
||||
$settings->setMultiple([
|
||||
'signup_min_age' => '13',
|
||||
'signup_max_age' => '120',
|
||||
'signup_min_username' => '3',
|
||||
'signup_max_username' => '20',
|
||||
'signup_min_password' => '8',
|
||||
'signup_max_password' => '50',
|
||||
'username_format' => 'relaxed'
|
||||
]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Method 1: Using the Settings Class (Recommended)
|
||||
|
||||
```php
|
||||
<?php
|
||||
require_once 'f_core/f_classes/class.settings.php';
|
||||
|
||||
// Initialize
|
||||
$settings = new Settings($pdo);
|
||||
|
||||
// Get a single setting
|
||||
$siteName = $settings->get('website_shortname', 'EasyStream');
|
||||
|
||||
// Set a single setting
|
||||
$settings->set('website_shortname', 'My Awesome Site');
|
||||
|
||||
// Get multiple settings at once
|
||||
$emailConfig = $settings->getEmailConfig();
|
||||
$paymentConfig = $settings->getPaymentConfig();
|
||||
|
||||
// Set multiple settings at once (bulk operation)
|
||||
$settings->setMultiple([
|
||||
'website_shortname' => 'My Site',
|
||||
'head_title' => 'My Site - Video Platform',
|
||||
'branding_primary_color' => '#FF0000'
|
||||
]);
|
||||
|
||||
// Get all settings in a category
|
||||
$brandingSettings = $settings->getByPrefix('branding_');
|
||||
|
||||
// Check if a module is enabled
|
||||
if ($settings->isModuleEnabled('video')) {
|
||||
// Show video upload interface
|
||||
}
|
||||
|
||||
// Set module status
|
||||
$settings->setModuleEnabled('blog', false);
|
||||
```
|
||||
|
||||
### Method 2: Using Data Providers (Admin Panel)
|
||||
|
||||
```php
|
||||
<?php
|
||||
require_once 'admin/includes/data_providers.php';
|
||||
|
||||
// Get a single setting
|
||||
$siteName = admin_get_setting($pdo, 'website_shortname');
|
||||
|
||||
// Save a setting
|
||||
admin_save_setting($pdo, 'website_shortname', 'New Site Name');
|
||||
|
||||
// Save multiple settings
|
||||
admin_save_multiple_settings($pdo, [
|
||||
'website_shortname' => 'New Site',
|
||||
'head_title' => 'New Title'
|
||||
]);
|
||||
|
||||
// Get module status
|
||||
$modules = admin_fetch_module_status($pdo);
|
||||
|
||||
// Toggle a module
|
||||
admin_toggle_module($pdo, 'video_module', true);
|
||||
```
|
||||
|
||||
### Method 3: Legacy Database Class
|
||||
|
||||
```php
|
||||
<?php
|
||||
global $class_database;
|
||||
|
||||
// Load specific settings
|
||||
$class_database->getConfigurations('website_shortname,head_title,backend_email');
|
||||
|
||||
// Access via $cfg array
|
||||
global $cfg;
|
||||
echo $cfg['website_shortname']; // Site name
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Settings Reference
|
||||
|
||||
### Complete Settings List
|
||||
|
||||
| Category | Setting Name | Description | Type | Default |
|
||||
|----------|--------------|-------------|------|---------|
|
||||
| **General** | `website_shortname` | Short site name | text | EasyStream |
|
||||
| | `head_title` | Browser tab title | text | EasyStream |
|
||||
| | `backend_email` | Admin email | email | admin@example.com |
|
||||
| | `main_url` | Main website URL | url | http://localhost:8083 |
|
||||
| | `debug_mode` | Enable debug mode | boolean | 0 |
|
||||
| **Modules** | `video_module` | Enable videos | boolean | 1 |
|
||||
| | `live_module` | Enable live streaming | boolean | 1 |
|
||||
| | `short_module` | Enable shorts | boolean | 1 |
|
||||
| | `image_module` | Enable images | boolean | 1 |
|
||||
| | `audio_module` | Enable audio | boolean | 1 |
|
||||
| | `document_module` | Enable documents | boolean | 1 |
|
||||
| | `blog_module` | Enable blogs | boolean | 1 |
|
||||
| | `paid_memberships` | Enable memberships | boolean | 0 |
|
||||
| | `token_system_enabled` | Enable tokens | boolean | 1 |
|
||||
| **Branding** | `branding_primary_color` | Primary UI color | color | #1a73e8 |
|
||||
| | `branding_secondary_color` | Secondary UI color | color | #34a853 |
|
||||
| | `branding_logo_url` | Logo URL | url | (empty) |
|
||||
| | `branding_favicon_url` | Favicon URL | url | (empty) |
|
||||
| | `branding_footer_text` | Custom footer | text | (empty) |
|
||||
| **Payments** | `paypal_email` | PayPal email | email | (empty) |
|
||||
| | `paypal_test` | PayPal test mode | boolean | 1 |
|
||||
| | `paypal_client_id` | PayPal client ID | text | (empty) |
|
||||
| | `paypal_secret` | PayPal secret (encrypted) | password | (empty) |
|
||||
| | `stripe_enabled` | Enable Stripe | boolean | 0 |
|
||||
| | `stripe_publishable_key` | Stripe public key | text | (empty) |
|
||||
| | `stripe_secret_key` | Stripe secret (encrypted) | password | (empty) |
|
||||
| | `stripe_webhook_secret` | Webhook secret (encrypted) | password | (empty) |
|
||||
| **Email** | `mail_type` | Mailer type | select | smtp |
|
||||
| | `mail_smtp_host` | SMTP hostname | text | smtp.gmail.com |
|
||||
| | `mail_smtp_port` | SMTP port | number | 587 |
|
||||
| | `mail_smtp_username` | SMTP username | text | (empty) |
|
||||
| | `mail_smtp_password` | SMTP password (encrypted) | password | (empty) |
|
||||
| | `mail_smtp_auth` | SMTP authentication | boolean | true |
|
||||
| | `mail_smtp_prefix` | Encryption type | select | tls |
|
||||
| **Payouts** | `creator_payout_enabled` | Enable payouts | boolean | 0 |
|
||||
| | `creator_payout_percentage` | Revenue share % | number | 70 |
|
||||
| | `minimum_payout_amount` | Minimum payout | decimal | 50.00 |
|
||||
| | `payout_schedule` | Payout frequency | select | monthly |
|
||||
| | `payout_method` | Default method | select | paypal |
|
||||
| **SEO** | `metaname_description` | Meta description | textarea | (empty) |
|
||||
| | `metaname_keywords` | Meta keywords | textarea | (empty) |
|
||||
| **Security** | `signup_min_age` | Minimum age | number | 18 |
|
||||
| | `signup_max_age` | Maximum age | number | 70 |
|
||||
| | `signup_min_username` | Min username length | number | 5 |
|
||||
| | `signup_max_username` | Max username length | number | 15 |
|
||||
| | `signup_min_password` | Min password length | number | 5 |
|
||||
| | `signup_max_password` | Max password length | number | 15 |
|
||||
| | `username_format` | Username format | select | strict |
|
||||
| | `login_remember` | Enable remember me | boolean | 1 |
|
||||
|
||||
---
|
||||
|
||||
## Advanced Features
|
||||
|
||||
### 1. Redis Caching (10-100x Faster)
|
||||
|
||||
The Settings class automatically uses Redis when available:
|
||||
|
||||
```php
|
||||
$settings = new Settings($pdo);
|
||||
|
||||
// First call - queries database
|
||||
$siteName = $settings->get('website_shortname');
|
||||
|
||||
// Subsequent calls - served from Redis cache (milliseconds instead of seconds)
|
||||
$siteName = $settings->get('website_shortname');
|
||||
|
||||
// Cache is automatically cleared when settings are updated
|
||||
$settings->set('website_shortname', 'New Name'); // Invalidates cache
|
||||
```
|
||||
|
||||
**Cache Details:**
|
||||
- TTL: 1 hour (3600 seconds)
|
||||
- Automatic invalidation on updates
|
||||
- Uses existing `VRedis` singleton
|
||||
- Falls back to database if Redis unavailable
|
||||
|
||||
---
|
||||
|
||||
### 2. Input Validation
|
||||
|
||||
All settings are validated before being saved:
|
||||
|
||||
```php
|
||||
$settings = new Settings($pdo);
|
||||
|
||||
try {
|
||||
// Valid email required
|
||||
$settings->set('backend_email', 'admin@example.com'); // ✅ Valid
|
||||
|
||||
// Invalid email throws exception
|
||||
$settings->set('backend_email', 'not-an-email'); // ❌ Throws exception
|
||||
|
||||
// Port must be 1-65535
|
||||
$settings->set('mail_smtp_port', 587); // ✅ Valid
|
||||
$settings->set('mail_smtp_port', 99999); // ❌ Throws exception
|
||||
|
||||
// Colors must be valid hex
|
||||
$settings->set('branding_primary_color', '#FF0000'); // ✅ Valid
|
||||
$settings->set('branding_primary_color', 'red'); // ❌ Throws exception
|
||||
|
||||
} catch (InvalidArgumentException $e) {
|
||||
echo "Validation error: " . $e->getMessage();
|
||||
}
|
||||
```
|
||||
|
||||
**Validation Rules:**
|
||||
- `email` - Valid email format
|
||||
- `url` - Valid URL format
|
||||
- `color` - Valid hex color (#RRGGBB)
|
||||
- `int` - Integer with optional min/max
|
||||
- `float` - Decimal number with optional min/max
|
||||
- `bool` - Boolean (0/1, true/false, yes/no)
|
||||
- `string` - Text with optional min/max length
|
||||
|
||||
---
|
||||
|
||||
### 3. Encryption for Sensitive Data
|
||||
|
||||
Sensitive settings are automatically encrypted:
|
||||
|
||||
```php
|
||||
$settings = new Settings($pdo);
|
||||
|
||||
// These are automatically encrypted in the database:
|
||||
$settings->set('paypal_secret', 'secret_key_here');
|
||||
$settings->set('stripe_secret_key', 'sk_live_xxxxx');
|
||||
$settings->set('mail_smtp_password', 'smtp_password');
|
||||
$settings->set('google_client_secret', 'oauth_secret');
|
||||
|
||||
// When you retrieve them, they're automatically decrypted:
|
||||
$secret = $settings->get('paypal_secret'); // Returns decrypted value
|
||||
```
|
||||
|
||||
**Encrypted Settings:**
|
||||
- PayPal secret
|
||||
- Stripe secret key
|
||||
- Stripe webhook secret
|
||||
- SMTP password
|
||||
- Google client secret
|
||||
- Facebook app secret
|
||||
- Twitter API secret
|
||||
|
||||
**Encryption Method:** AES-256-CBC using keys from `config.define.php` (ENC_FIRSTKEY, ENC_SECONDKEY)
|
||||
|
||||
---
|
||||
|
||||
### 4. Audit Trail & Change History
|
||||
|
||||
Every settings change is tracked with complete audit trail:
|
||||
|
||||
```php
|
||||
$settings = new Settings($pdo);
|
||||
|
||||
// Change a setting
|
||||
$settings->set('website_shortname', 'New Site Name');
|
||||
|
||||
// View change history (last 20 changes)
|
||||
$history = $settings->getHistory('website_shortname');
|
||||
foreach ($history as $change) {
|
||||
echo "Changed on: " . $change['changed_at'] . "\n";
|
||||
echo "Changed by: User #" . $change['changed_by'] . "\n";
|
||||
echo "Old value: " . $change['old_value'] . "\n";
|
||||
echo "New value: " . $change['new_value'] . "\n";
|
||||
echo "IP address: " . $change['ip_address'] . "\n";
|
||||
}
|
||||
|
||||
// Rollback to a previous value
|
||||
$settings->rollback('website_shortname', $historyId);
|
||||
```
|
||||
|
||||
**Tracked Information:**
|
||||
- Setting name
|
||||
- Old value
|
||||
- New value
|
||||
- User who made the change
|
||||
- Timestamp
|
||||
- IP address
|
||||
- User agent
|
||||
- Optional change reason
|
||||
|
||||
---
|
||||
|
||||
### 5. Bulk Operations
|
||||
|
||||
Efficiently update multiple settings at once:
|
||||
|
||||
```php
|
||||
$settings = new Settings($pdo);
|
||||
|
||||
// Atomic transaction - all or nothing
|
||||
$settings->setMultiple([
|
||||
'website_shortname' => 'MyTube',
|
||||
'head_title' => 'MyTube - Video Platform',
|
||||
'branding_primary_color' => '#FF0000',
|
||||
'branding_secondary_color' => '#282828',
|
||||
'backend_email' => 'admin@mytube.com'
|
||||
]);
|
||||
|
||||
// If any setting fails validation, the entire operation is rolled back
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. Export & Import
|
||||
|
||||
Backup and restore your configuration:
|
||||
|
||||
```php
|
||||
$settings = new Settings($pdo);
|
||||
|
||||
// Export all settings to JSON
|
||||
$backup = $settings->exportJSON();
|
||||
file_put_contents('settings_backup.json', $backup);
|
||||
|
||||
// Import settings from JSON
|
||||
$json = file_get_contents('settings_backup.json');
|
||||
$settings->importJSON($json);
|
||||
```
|
||||
|
||||
**Use Cases:**
|
||||
- Backup before major changes
|
||||
- Duplicate configuration across environments
|
||||
- Version control your settings
|
||||
- Disaster recovery
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Settings Not Saving
|
||||
|
||||
**Symptoms:** Changes don't persist after clicking Save
|
||||
|
||||
**Solutions:**
|
||||
1. Check database connection in `f_core/config.database.php`
|
||||
2. Verify PDO is initialized in `admin/includes/bootstrap.php`
|
||||
3. Check error logs: `f_data/data_logs/log_error/`
|
||||
4. Ensure `db_settings` table exists
|
||||
5. Check file permissions on log directories
|
||||
|
||||
---
|
||||
|
||||
### Settings Not Loading
|
||||
|
||||
**Symptoms:** Settings page shows errors or empty values
|
||||
|
||||
**Solutions:**
|
||||
1. Clear Redis cache (if using Redis)
|
||||
2. Verify `db_settings` table exists
|
||||
3. Run the installation script: `__install/install_settings_system.sql`
|
||||
4. Check that settings were inserted: `SELECT COUNT(*) FROM db_settings;`
|
||||
5. Review PHP error logs
|
||||
|
||||
---
|
||||
|
||||
### Validation Errors
|
||||
|
||||
**Symptoms:** "Invalid value" errors when saving
|
||||
|
||||
**Solutions:**
|
||||
1. Check the error message for specific validation requirements
|
||||
2. Ensure email addresses are properly formatted
|
||||
3. Verify URLs include protocol (http:// or https://)
|
||||
4. Check number ranges (e.g., SMTP port must be 1-65535)
|
||||
5. Use hex color codes for color settings (#RRGGBB)
|
||||
|
||||
---
|
||||
|
||||
### PayPal/Stripe Not Working
|
||||
|
||||
**Symptoms:** Payments fail or aren't processed
|
||||
|
||||
**Solutions:**
|
||||
1. Verify API keys are correct (check for spaces/typos)
|
||||
2. Ensure test mode is disabled for production
|
||||
3. Check webhook URLs are configured in payment gateway dashboard
|
||||
4. Review payment gateway API documentation
|
||||
5. Test API keys using gateway's test tools
|
||||
6. Check error logs for detailed error messages
|
||||
|
||||
---
|
||||
|
||||
### Email Not Sending
|
||||
|
||||
**Symptoms:** No emails received from the platform
|
||||
|
||||
**Solutions:**
|
||||
1. Test SMTP connection manually
|
||||
2. Verify SMTP credentials are correct
|
||||
3. Check firewall allows outbound connections on port 587 (TLS) or 465 (SSL)
|
||||
4. Try a different SMTP provider (SendGrid, Mailgun, etc.)
|
||||
5. Review email logs in `f_data/data_logs/`
|
||||
6. Test with a simple mail client first
|
||||
7. Check spam/junk folders
|
||||
|
||||
---
|
||||
|
||||
### Search Not Working
|
||||
|
||||
**Symptoms:** Settings search doesn't filter results
|
||||
|
||||
**Solutions:**
|
||||
1. Clear browser cache
|
||||
2. Check JavaScript console for errors (F12)
|
||||
3. Ensure `admin/includes/settings_search.php` is included
|
||||
4. Verify settings have `data-setting-*` attributes
|
||||
5. Try a different browser
|
||||
|
||||
---
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
### 1. Protect Sensitive Settings
|
||||
|
||||
```php
|
||||
// ✅ Good - Use environment variables for production secrets
|
||||
$settings->set('stripe_secret_key', getenv('STRIPE_SECRET_KEY'));
|
||||
|
||||
// ❌ Bad - Don't hardcode secrets
|
||||
$settings->set('stripe_secret_key', 'sk_live_abc123');
|
||||
```
|
||||
|
||||
### 2. Regular Backups
|
||||
|
||||
```php
|
||||
// Backup before major changes
|
||||
$backup = $settings->exportJSON();
|
||||
file_put_contents("backup_" . date('Y-m-d') . ".json", $backup);
|
||||
```
|
||||
|
||||
### 3. Access Control
|
||||
|
||||
- Settings page requires admin authentication
|
||||
- Regular users cannot access `/admin_settings.php`
|
||||
- Use role-based access control (RBAC)
|
||||
|
||||
### 4. Audit Review
|
||||
|
||||
Regularly review change history:
|
||||
```php
|
||||
$history = $settings->getHistory('stripe_secret_key', 50);
|
||||
// Review who changed sensitive settings and when
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File Reference
|
||||
|
||||
### Core Files
|
||||
- [admin_settings.php](admin_settings.php) - Main settings UI
|
||||
- [f_core/f_classes/class.settings.php](f_core/f_classes/class.settings.php) - Settings management class
|
||||
- [admin/includes/data_providers.php](admin/includes/data_providers.php) - Settings data providers (lines 759-1041)
|
||||
- [admin/includes/settings_search.php](admin/includes/settings_search.php) - Search & filter UI component
|
||||
- [admin/includes/layout.php](admin/includes/layout.php) - Shared UI with sidebar navigation
|
||||
- [__install/install_settings_system.sql](__install/install_settings_system.sql) - Complete installation script
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
This settings system is part of EasyStream and subject to the EasyStream Proprietary License Agreement.
|
||||
|
||||
Copyright (c) 2025 Sami Ahmed. All rights reserved.
|
||||
@@ -0,0 +1,569 @@
|
||||
# EasyStream Template Builder Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The Template Builder is a powerful drag-and-drop interface that allows users to create custom page layouts for their EasyStream installation. Users can visually design templates using pre-built components without writing any code.
|
||||
|
||||
## Features
|
||||
|
||||
✨ **Drag and Drop Interface** - Intuitive visual builder
|
||||
🎨 **Pre-built Components** - Video grids, heroes, text blocks, and more
|
||||
📱 **Responsive Preview** - Test on desktop, tablet, and mobile
|
||||
💾 **Auto-save** - Never lose your work
|
||||
📝 **Version History** - Track changes over time
|
||||
🎯 **Custom Settings** - Configure each component's appearance
|
||||
🔄 **Template Management** - Create, edit, duplicate, and delete templates
|
||||
👁️ **Live Preview** - See your changes in real-time
|
||||
|
||||
## Installation
|
||||
|
||||
### 1. Database Setup
|
||||
|
||||
Run the SQL migration to create required tables:
|
||||
|
||||
```bash
|
||||
mysql -u your_user -p your_database < __install/add_template_builder.sql
|
||||
```
|
||||
|
||||
This creates the following tables:
|
||||
- `db_templatebuilder_templates` - Stores user templates
|
||||
- `db_templatebuilder_components` - Component library
|
||||
- `db_templatebuilder_assignments` - Page assignments
|
||||
- `db_templatebuilder_versions` - Version history
|
||||
- `db_templatebuilder_user_prefs` - User preferences
|
||||
|
||||
### 2. File Structure
|
||||
|
||||
The template builder consists of:
|
||||
|
||||
```
|
||||
f_core/f_classes/
|
||||
└── class.templatebuilder.php # Backend logic
|
||||
|
||||
f_templates/tpl_frontend/tpl_builder/
|
||||
└── tpl_builder_main.tpl # Builder UI
|
||||
|
||||
f_templates/tpl_backend/
|
||||
└── tpl_template_manager.tpl # Template list view
|
||||
|
||||
f_scripts/fe/css/builder/
|
||||
└── builder.css # Builder styles
|
||||
|
||||
f_scripts/fe/js/builder/
|
||||
├── builder-core.js # Main application
|
||||
├── builder-components.js # Component logic (future)
|
||||
└── builder-ui.js # UI helpers (future)
|
||||
|
||||
f_modules/m_frontend/
|
||||
└── templatebuilder_ajax.php # AJAX handler
|
||||
|
||||
f_modules/m_backend/
|
||||
└── template_manager.php # Management interface
|
||||
```
|
||||
|
||||
### 3. Include in Navigation
|
||||
|
||||
Add a link to the template manager in your user account navigation:
|
||||
|
||||
```smarty
|
||||
<a href="/f_modules/m_backend/template_manager.php">
|
||||
<i class="icon-layout"></i> My Templates
|
||||
</a>
|
||||
```
|
||||
|
||||
## User Guide
|
||||
|
||||
### Creating a New Template
|
||||
|
||||
1. Navigate to **My Templates** in your account
|
||||
2. Click **Create New Template**
|
||||
3. You'll be taken to the builder interface
|
||||
|
||||
### Builder Interface
|
||||
|
||||
The builder consists of three main areas:
|
||||
|
||||
#### Left Sidebar - Component Library
|
||||
- **Search**: Find components quickly
|
||||
- **Categories**: Filter by component type
|
||||
- **Component List**: Drag components to canvas
|
||||
|
||||
#### Center Canvas - Preview Area
|
||||
- **Toolbar**: Zoom, grid, and view options
|
||||
- **Device Preview**: Switch between desktop/tablet/mobile
|
||||
- **Canvas**: Drag and drop components here
|
||||
|
||||
#### Right Sidebar - Properties Panel
|
||||
- **Page Settings**: Template-wide settings
|
||||
- **Component Settings**: Configure selected components
|
||||
- **Section Settings**: Adjust section layout
|
||||
|
||||
### Adding Components
|
||||
|
||||
1. Find a component in the left sidebar
|
||||
2. Drag it onto the canvas
|
||||
3. Drop it where you want it
|
||||
4. Configure its settings in the right sidebar
|
||||
|
||||
### Component Types
|
||||
|
||||
#### Video Grid (4 Columns)
|
||||
Displays videos in a responsive grid layout.
|
||||
|
||||
**Settings:**
|
||||
- Columns (1-6)
|
||||
- Gap between items
|
||||
- Padding
|
||||
|
||||
#### Hero Banner
|
||||
Large banner with background image and call-to-action.
|
||||
|
||||
**Settings:**
|
||||
- Background image
|
||||
- Title and subtitle
|
||||
- Button text and link
|
||||
- Overlay opacity
|
||||
- Height
|
||||
|
||||
#### Video Horizontal List
|
||||
Scrollable horizontal list of videos.
|
||||
|
||||
**Settings:**
|
||||
- Section title
|
||||
- Gap between items
|
||||
- Padding
|
||||
|
||||
#### Sidebar Widget
|
||||
Customizable sidebar container.
|
||||
|
||||
**Settings:**
|
||||
- Widget title
|
||||
- Background color
|
||||
- Padding and border radius
|
||||
|
||||
#### Text Block
|
||||
Rich text content with heading.
|
||||
|
||||
**Settings:**
|
||||
- Heading text and size
|
||||
- Content
|
||||
- Text alignment
|
||||
- Colors and spacing
|
||||
|
||||
#### Image Block
|
||||
Image with optional caption.
|
||||
|
||||
**Settings:**
|
||||
- Image URL
|
||||
- Alt text
|
||||
- Caption
|
||||
- Alignment
|
||||
- Max width
|
||||
|
||||
#### Custom HTML
|
||||
Advanced users can add custom HTML/Smarty code.
|
||||
|
||||
**Settings:**
|
||||
- HTML content
|
||||
- Padding
|
||||
|
||||
### Sections
|
||||
|
||||
Components are organized into **sections**. Each section can have:
|
||||
|
||||
- **Multiple columns** (1-4)
|
||||
- **Custom gap** between columns
|
||||
- **Background color**
|
||||
- **Padding** (top, right, bottom, left)
|
||||
|
||||
### Editing Components
|
||||
|
||||
1. Click on a component in the canvas
|
||||
2. The right sidebar shows its settings
|
||||
3. Modify any setting
|
||||
4. Changes apply immediately
|
||||
|
||||
### Moving Components
|
||||
|
||||
- **Drag and drop** within sections
|
||||
- Use **section controls** to move sections up/down
|
||||
- **Duplicate** components or sections
|
||||
- **Delete** unwanted elements
|
||||
|
||||
### Responsive Design
|
||||
|
||||
Click the device icons in the header to preview:
|
||||
|
||||
- 🖥️ **Desktop** (full width)
|
||||
- 📱 **Tablet** (768px)
|
||||
- 📱 **Mobile** (375px)
|
||||
|
||||
### Saving Templates
|
||||
|
||||
- **Auto-save**: Automatically saves every 3 seconds
|
||||
- **Manual save**: Click "Save" button
|
||||
- **Versioning**: Each save creates a version history entry
|
||||
|
||||
### Publishing Templates
|
||||
|
||||
1. Click **Publish** when ready
|
||||
2. Sets the template as active
|
||||
3. Template becomes available for use
|
||||
|
||||
## Developer Guide
|
||||
|
||||
### Creating Custom Components
|
||||
|
||||
Add components to the database:
|
||||
|
||||
```sql
|
||||
INSERT INTO `db_templatebuilder_components`
|
||||
(`component_name`, `component_slug`, `component_category`, `component_html`,
|
||||
`component_css`, `component_settings_schema`, `is_system`, `description`)
|
||||
VALUES
|
||||
('My Component', 'my_component', 'custom',
|
||||
'<div class="my-component">{{content}}</div>',
|
||||
'.my-component { padding: {{padding}}px; }',
|
||||
'{"content": {"type": "textarea", "default": "Hello"}, "padding": {"type": "number", "default": 20}}',
|
||||
1,
|
||||
'Custom component description');
|
||||
```
|
||||
|
||||
### Component Settings Schema
|
||||
|
||||
The `component_settings_schema` is a JSON object defining configurable settings:
|
||||
|
||||
```json
|
||||
{
|
||||
"setting_name": {
|
||||
"type": "number|text|textarea|color|boolean|select|image|code",
|
||||
"default": "default_value",
|
||||
"min": 0,
|
||||
"max": 100,
|
||||
"step": 5,
|
||||
"options": ["option1", "option2"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Supported Types:**
|
||||
- `number` - Numeric input with optional min/max/step
|
||||
- `text` - Single line text
|
||||
- `textarea` - Multi-line text
|
||||
- `color` - Color picker
|
||||
- `boolean` - Checkbox
|
||||
- `select` - Dropdown with options
|
||||
- `image` - Image URL input
|
||||
- `code` - Code editor
|
||||
|
||||
### Template Variables
|
||||
|
||||
Component HTML can use placeholders:
|
||||
|
||||
```html
|
||||
<div class="hero" style="background: {{background_color}};">
|
||||
<h1>{{title}}</h1>
|
||||
{if {{show_button}}}
|
||||
<a href="{{button_link}}">{{button_text}}</a>
|
||||
{/if}
|
||||
</div>
|
||||
```
|
||||
|
||||
Placeholders are replaced with:
|
||||
1. Component settings values
|
||||
2. Global data passed to renderer
|
||||
|
||||
### Rendering Templates
|
||||
|
||||
#### PHP
|
||||
|
||||
```php
|
||||
$templateBuilder = new VTemplateBuilder();
|
||||
|
||||
// Render by ID
|
||||
$html = $templateBuilder->renderTemplate(123);
|
||||
|
||||
// Render by slug
|
||||
$html = $templateBuilder->renderTemplate('my-template-slug');
|
||||
|
||||
// Render with data
|
||||
$html = $templateBuilder->renderTemplate(123, [
|
||||
'video_items' => $videos,
|
||||
'user_name' => $userName
|
||||
]);
|
||||
|
||||
echo $html;
|
||||
```
|
||||
|
||||
#### Smarty Template
|
||||
|
||||
```smarty
|
||||
{* Include template builder output *}
|
||||
<div class="template-container">
|
||||
{$template_html}
|
||||
</div>
|
||||
```
|
||||
|
||||
### AJAX API
|
||||
|
||||
All AJAX requests go to `/f_modules/m_frontend/templatebuilder_ajax.php`
|
||||
|
||||
**Get Components:**
|
||||
```javascript
|
||||
GET /templatebuilder_ajax.php?action=get_components
|
||||
GET /templatebuilder_ajax.php?action=get_components&category=video_grid
|
||||
```
|
||||
|
||||
**Create Template:**
|
||||
```javascript
|
||||
POST /templatebuilder_ajax.php
|
||||
{
|
||||
"action": "create_template",
|
||||
"template_name": "My Template",
|
||||
"template_type": "homepage",
|
||||
"template_structure": "{...}"
|
||||
}
|
||||
```
|
||||
|
||||
**Update Template:**
|
||||
```javascript
|
||||
POST /templatebuilder_ajax.php
|
||||
{
|
||||
"action": "update_template",
|
||||
"template_id": 123,
|
||||
"template_structure": "{...}",
|
||||
"change_note": "Updated hero section"
|
||||
}
|
||||
```
|
||||
|
||||
**Get Template:**
|
||||
```javascript
|
||||
GET /templatebuilder_ajax.php?action=get_template&template_id=123
|
||||
```
|
||||
|
||||
**Preview Template:**
|
||||
```javascript
|
||||
GET /templatebuilder_ajax.php?action=preview&template_id=123
|
||||
```
|
||||
|
||||
### Template Structure Format
|
||||
|
||||
Templates are stored as JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"sections": [
|
||||
{
|
||||
"id": "section-1",
|
||||
"columns": 1,
|
||||
"gap": 20,
|
||||
"styles": {
|
||||
"background_color": "#f5f5f5",
|
||||
"padding": "40px 20px"
|
||||
},
|
||||
"blocks": [
|
||||
{
|
||||
"id": "block-1",
|
||||
"component": "hero_banner",
|
||||
"settings": {
|
||||
"title": "Welcome",
|
||||
"subtitle": "To my site",
|
||||
"height": 400,
|
||||
"overlay_opacity": 0.5
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"layout_type": "flex",
|
||||
"max_width": 1200
|
||||
}
|
||||
```
|
||||
|
||||
### Extending the Builder
|
||||
|
||||
#### Add New Component Category
|
||||
|
||||
1. Update SQL insert in `add_template_builder.sql`
|
||||
2. Add category button in `tpl_builder_main.tpl`
|
||||
3. Add icon mapping in `builder-core.js` `getCategoryIcon()`
|
||||
|
||||
#### Custom CSS for Components
|
||||
|
||||
Add component-specific CSS in the component definition:
|
||||
|
||||
```css
|
||||
.component-custom {
|
||||
/* Your styles */
|
||||
padding: {{padding}}px;
|
||||
color: {{text_color}};
|
||||
}
|
||||
```
|
||||
|
||||
Variables are replaced during rendering.
|
||||
|
||||
#### Custom JavaScript
|
||||
|
||||
Advanced components can include JavaScript:
|
||||
|
||||
```javascript
|
||||
// In component's custom_js field
|
||||
document.querySelectorAll('.my-component').forEach(el => {
|
||||
el.addEventListener('click', () => {
|
||||
console.log('Clicked!');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Security Considerations
|
||||
|
||||
1. **Input Validation**: All user input is sanitized via `VDatabase::sanitizeInput()`
|
||||
2. **Ownership Verification**: Users can only edit their own templates
|
||||
3. **HTML Sanitization**: Custom HTML should be sanitized before rendering
|
||||
4. **XSS Prevention**: Use Smarty's `|escape` modifier for user content
|
||||
5. **SQL Injection**: All queries use prepared statements via VDatabase
|
||||
|
||||
### Performance Optimization
|
||||
|
||||
1. **Caching**: Rendered templates can be cached
|
||||
2. **Lazy Loading**: Load components on demand
|
||||
3. **Minification**: Minify CSS/JS before production
|
||||
4. **Database Indexes**: Indexes already added for performance
|
||||
5. **JSON Validation**: Validate structure before saving
|
||||
|
||||
## Keyboard Shortcuts
|
||||
|
||||
- **Ctrl/Cmd + S** - Save template
|
||||
- **Ctrl/Cmd + Z** - Undo
|
||||
- **Ctrl/Cmd + Shift + Z** - Redo
|
||||
- **Delete** - Delete selected element
|
||||
- **Esc** - Deselect element
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Components Not Loading
|
||||
|
||||
Check:
|
||||
1. Database has component records
|
||||
2. AJAX endpoint is accessible
|
||||
3. JavaScript console for errors
|
||||
|
||||
### Template Not Saving
|
||||
|
||||
Check:
|
||||
1. User is logged in
|
||||
2. Template name is provided
|
||||
3. JSON structure is valid
|
||||
4. Database connection is active
|
||||
|
||||
### Preview Not Working
|
||||
|
||||
Check:
|
||||
1. Template is saved first
|
||||
2. Template ID is correct
|
||||
3. Rendering logic has no errors
|
||||
|
||||
### Styles Not Applying
|
||||
|
||||
Check:
|
||||
1. CSS files are loaded
|
||||
2. Builder CSS path is correct
|
||||
3. Theme compatibility
|
||||
|
||||
## Best Practices
|
||||
|
||||
### For Users
|
||||
|
||||
1. **Name templates clearly** - Use descriptive names
|
||||
2. **Save regularly** - Use auto-save but manually save major changes
|
||||
3. **Test responsiveness** - Check all device sizes
|
||||
4. **Use sections wisely** - Group related content
|
||||
5. **Keep it simple** - Don't overcomplicate layouts
|
||||
|
||||
### For Developers
|
||||
|
||||
1. **Component reusability** - Create flexible components
|
||||
2. **Settings validation** - Validate all settings in schema
|
||||
3. **Documentation** - Document custom components
|
||||
4. **Testing** - Test templates on different browsers
|
||||
5. **Version control** - Use version history for major changes
|
||||
|
||||
## API Reference
|
||||
|
||||
### VTemplateBuilder Class
|
||||
|
||||
#### Methods
|
||||
|
||||
**`createTemplate($data)`**
|
||||
- Creates new template
|
||||
- Returns: `['success' => bool, 'template_id' => int, 'slug' => string]`
|
||||
|
||||
**`updateTemplate($template_id, $data, $change_note = null)`**
|
||||
- Updates existing template
|
||||
- Returns: `['success' => bool]`
|
||||
|
||||
**`deleteTemplate($template_id)`**
|
||||
- Deletes template
|
||||
- Returns: `['success' => bool]`
|
||||
|
||||
**`getTemplate($template_id, $check_ownership = true)`**
|
||||
- Gets template by ID
|
||||
- Returns: `array|null`
|
||||
|
||||
**`getTemplateBySlug($slug)`**
|
||||
- Gets template by slug
|
||||
- Returns: `array|null`
|
||||
|
||||
**`getUserTemplates($filters = [])`**
|
||||
- Gets all user templates
|
||||
- Returns: `array`
|
||||
|
||||
**`renderTemplate($template_identifier, $data = [])`**
|
||||
- Renders template HTML
|
||||
- Returns: `string`
|
||||
|
||||
**`getComponents($category = null)`**
|
||||
- Gets available components
|
||||
- Returns: `array`
|
||||
|
||||
**`duplicateTemplate($template_id, $new_name = null)`**
|
||||
- Duplicates template
|
||||
- Returns: `['success' => bool, 'template_id' => int]`
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
|
||||
1. Check this documentation
|
||||
2. Review code comments
|
||||
3. Check database logs via `VLogger`
|
||||
4. Inspect browser console
|
||||
5. Review version history for changes
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements:
|
||||
|
||||
- [ ] Template marketplace/sharing
|
||||
- [ ] More component types
|
||||
- [ ] Advanced grid system
|
||||
- [ ] Animation options
|
||||
- [ ] A/B testing support
|
||||
- [ ] Template import/export
|
||||
- [ ] Collaboration features
|
||||
- [ ] Mobile app builder
|
||||
- [ ] Component library expansion
|
||||
- [ ] AI-powered suggestions
|
||||
|
||||
## Credits
|
||||
|
||||
Built for EasyStream by the EasyStream team.
|
||||
|
||||
## License
|
||||
|
||||
Same license as EasyStream platform.
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2025-01-22
|
||||
**Version:** 1.0.0
|
||||
Reference in New Issue
Block a user