Major additions: - Web-based setup wizard (setup.php, setup_wizard.php, setup-wizard.js) - Production Docker configuration (docker-compose.prod.yml, .env.production) - Database initialization SQL files (deploy/init_settings.sql) - Template builder system with drag-and-drop UI - Advanced features (OAuth, CDN, enhanced analytics, monetization) - Comprehensive documentation (deployment guides, quick start, feature docs) - Design system with accessibility and responsive layout - Deployment automation scripts (deploy.ps1, generate-secrets.ps1) Setup wizard allows customization of: - Platform name and branding - Domain configuration - Membership tiers and pricing - Admin credentials - Feature toggles Database includes 270+ tables for complete video streaming platform with advanced features for analytics, moderation, template building, and monetization. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
219 lines
6.2 KiB
PHP
219 lines
6.2 KiB
PHP
<?php
|
|
/**
|
|
* EasyStream CDN Integration
|
|
* Multi-CDN support: Cloudflare, AWS CloudFront, Bunny CDN
|
|
* Version: 1.0
|
|
*/
|
|
|
|
defined('_ISVALID') or header('Location: /error');
|
|
|
|
class VCDN {
|
|
private static $db;
|
|
private static $active_cdn = null;
|
|
|
|
public static function init() {
|
|
self::$db = VDatabase::getInstance();
|
|
self::loadActiveCDN();
|
|
}
|
|
|
|
/**
|
|
* Load active CDN configuration
|
|
*/
|
|
private static function loadActiveCDN() {
|
|
$sql = "SELECT * FROM db_cdn_config
|
|
WHERE is_active = 1
|
|
ORDER BY priority ASC
|
|
LIMIT 1";
|
|
|
|
$result = self::$db->execute($sql);
|
|
if ($result && $result->RecordCount() > 0) {
|
|
self::$active_cdn = $result->FetchRow();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get CDN URL for file
|
|
* @param string $file_path Original file path
|
|
* @param string $file_type Type (video, image, audio, etc.)
|
|
* @return string CDN URL or original path
|
|
*/
|
|
public static function getCDNUrl($file_path, $file_type = 'video') {
|
|
self::init();
|
|
|
|
if (!self::$active_cdn || empty(self::$active_cdn['base_url'])) {
|
|
return $file_path; // Return original if no CDN
|
|
}
|
|
|
|
$base_url = rtrim(self::$active_cdn['base_url'], '/');
|
|
$file_path = ltrim($file_path, '/');
|
|
|
|
return "$base_url/$file_path";
|
|
}
|
|
|
|
/**
|
|
* Purge cache for specific file
|
|
* @param string $file_path File path to purge
|
|
* @return bool Success
|
|
*/
|
|
public static function purgeCache($file_path) {
|
|
self::init();
|
|
|
|
if (!self::$active_cdn) {
|
|
return false;
|
|
}
|
|
|
|
$provider = self::$active_cdn['provider'];
|
|
|
|
switch ($provider) {
|
|
case 'cloudflare':
|
|
return self::purgeCloudflare($file_path);
|
|
|
|
case 'bunny':
|
|
return self::purgeBunny($file_path);
|
|
|
|
case 'aws':
|
|
return self::purgeAWS($file_path);
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Purge Cloudflare cache
|
|
*/
|
|
private static function purgeCloudflare($file_path) {
|
|
$zone_id = self::$active_cdn['zone_id'];
|
|
$api_key = self::$active_cdn['api_key'];
|
|
|
|
if (empty($zone_id) || empty($api_key)) {
|
|
return false;
|
|
}
|
|
|
|
$url = "https://api.cloudflare.com/client/v4/zones/$zone_id/purge_cache";
|
|
|
|
$cdn_url = self::getCDNUrl($file_path);
|
|
|
|
$data = json_encode(['files' => [$cdn_url]]);
|
|
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'Authorization: Bearer ' . $api_key,
|
|
'Content-Type: application/json',
|
|
'Content-Length: ' . strlen($data)
|
|
]);
|
|
|
|
$result = curl_exec($ch);
|
|
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
return $status_code == 200;
|
|
}
|
|
|
|
/**
|
|
* Purge Bunny CDN cache
|
|
*/
|
|
private static function purgeBunny($file_path) {
|
|
$api_key = self::$active_cdn['api_key'];
|
|
$zone_id = self::$active_cdn['zone_id']; // Pull zone ID
|
|
|
|
if (empty($api_key) || empty($zone_id)) {
|
|
return false;
|
|
}
|
|
|
|
$cdn_url = self::getCDNUrl($file_path);
|
|
|
|
$url = "https://api.bunny.net/pullzone/$zone_id/purgeCache";
|
|
|
|
$data = json_encode(['url' => $cdn_url]);
|
|
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'AccessKey: ' . $api_key,
|
|
'Content-Type: application/json'
|
|
]);
|
|
|
|
$result = curl_exec($ch);
|
|
$status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
return $status_code == 200 || $status_code == 204;
|
|
}
|
|
|
|
/**
|
|
* Purge AWS CloudFront cache
|
|
*/
|
|
private static function purgeAWS($file_path) {
|
|
// AWS CloudFront invalidation would require AWS SDK
|
|
// For now, return false (implement if AWS SDK available)
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Track CDN statistics
|
|
* @param string $file_key File key
|
|
* @param int $bandwidth Bandwidth in MB
|
|
* @param int $requests Number of requests
|
|
*/
|
|
public static function trackStats($file_key, $bandwidth, $requests) {
|
|
self::init();
|
|
|
|
if (!self::$active_cdn) {
|
|
return;
|
|
}
|
|
|
|
$file_key_safe = VDatabase::escape($file_key);
|
|
$provider = VDatabase::escape(self::$active_cdn['provider']);
|
|
$bandwidth = (float)$bandwidth;
|
|
$requests = (int)$requests;
|
|
$date = date('Y-m-d');
|
|
|
|
$sql = "INSERT INTO db_cdn_stats
|
|
(file_key, cdn_provider, bandwidth_mb, requests, date)
|
|
VALUES ('$file_key_safe', '$provider', $bandwidth, $requests, '$date')
|
|
ON DUPLICATE KEY UPDATE
|
|
bandwidth_mb = bandwidth_mb + $bandwidth,
|
|
requests = requests + $requests";
|
|
|
|
self::$db->execute($sql);
|
|
}
|
|
|
|
/**
|
|
* Get CDN statistics
|
|
* @param string $file_key File key
|
|
* @param string $date_from Start date
|
|
* @param string $date_to End date
|
|
* @return array Stats
|
|
*/
|
|
public static function getStats($file_key, $date_from, $date_to) {
|
|
self::init();
|
|
|
|
$file_key_safe = VDatabase::escape($file_key);
|
|
$date_from_safe = VDatabase::escape($date_from);
|
|
$date_to_safe = VDatabase::escape($date_to);
|
|
|
|
$sql = "SELECT cdn_provider, SUM(bandwidth_mb) as total_bandwidth, SUM(requests) as total_requests
|
|
FROM db_cdn_stats
|
|
WHERE file_key = '$file_key_safe'
|
|
AND date BETWEEN '$date_from_safe' AND '$date_to_safe'
|
|
GROUP BY cdn_provider";
|
|
|
|
$result = self::$db->execute($sql);
|
|
$stats = [];
|
|
|
|
if ($result) {
|
|
while ($row = $result->FetchRow()) {
|
|
$stats[] = $row;
|
|
}
|
|
}
|
|
|
|
return $stats;
|
|
}
|
|
}
|