feat: Add comprehensive documentation suite and reorganize project structure

- Created complete documentation in docs/ directory
- Added PROJECT_OVERVIEW.md with feature highlights and getting started guide
- Added ARCHITECTURE.md with system design and technical details
- Added SECURITY.md with comprehensive security implementation guide
- Added DEVELOPMENT.md with development workflows and best practices
- Added DEPLOYMENT.md with production deployment instructions
- Added API.md with complete REST API documentation
- Added CONTRIBUTING.md with contribution guidelines
- Added CHANGELOG.md with version history and migration notes
- Reorganized all documentation files into docs/ directory for better organization
- Updated README.md with proper documentation links and quick navigation
- Enhanced project structure with professional documentation standards
This commit is contained in:
SamiAhmed7777
2025-10-21 00:39:45 -07:00
commit 0b7e2d0a5b
6080 changed files with 1332936 additions and 0 deletions

View File

@@ -0,0 +1,686 @@
<?php
/*******************************************************************************************************************
| Software Name : EasyStream
| Software Description : High End YouTube Clone Script with Videos, Shorts, Streams, Images, Audio, Documents, Blogs
| Software Author : (c) Sami Ahmed
|*******************************************************************************************************************
|
|*******************************************************************************************************************
| This source file is subject to the EasyStream Proprietary License Agreement.
|
| By using this software, you acknowledge having read this Agreement and agree to be bound thereby.
|*******************************************************************************************************************
| Copyright (c) 2025 Sami Ahmed. All rights reserved.
|*******************************************************************************************************************/
defined('_ISVALID') or header('Location: /error');
/**
* VPrivacy - Comprehensive Privacy and Access Control System
*
* This class handles all aspects of privacy and access control:
* - Site-wide access modes (public, members-only, invite-only)
* - Content privacy levels
* - User privacy preferences
* - Geographic restrictions
* - Age verification
* - GDPR compliance
* - Access logging
*/
class VPrivacy
{
private static $instance = null;
private $branding;
private $currentUser = null;
private $userPrivacyLevel = 'guest';
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct()
{
$this->branding = VBranding::getInstance();
$this->initializeUser();
}
/**
* Initialize current user and privacy level
*/
private function initializeUser()
{
if (isset($_SESSION['USER_ID']) && $_SESSION['USER_ID'] > 0) {
$this->currentUser = $_SESSION['USER_ID'];
$this->userPrivacyLevel = $this->getUserPrivacyLevel($_SESSION['USER_ID']);
} else {
$this->userPrivacyLevel = 'guest';
}
}
/**
* Get user's privacy level
*/
private function getUserPrivacyLevel($userId)
{
global $db;
try {
// Check if user is admin
$sql = "SELECT usr_status FROM db_accountuser WHERE usr_id = ?";
$result = $db->Execute($sql, [$userId]);
if ($result && !$result->EOF) {
$status = $result->fields['usr_status'];
if ($status == 'admin' || $status == 'administrator') {
return 'admin';
}
if ($status == 'premium') {
return 'premium';
}
if ($status == 'verified') {
return 'verified';
}
return 'member';
}
} catch (Exception $e) {
$logger = VLogger::getInstance();
$logger->logError('Failed to get user privacy level: ' . $e->getMessage());
}
return 'guest';
}
/**
* Check if site access is allowed
*/
public function checkSiteAccess()
{
$accessMode = $this->branding->get('site_access_mode', 'public');
$maintenanceMode = $this->branding->get('maintenance_mode', false);
// Check maintenance mode first
if ($maintenanceMode) {
return $this->checkMaintenanceAccess();
}
switch ($accessMode) {
case 'public':
return ['allowed' => true];
case 'members_only':
if ($this->userPrivacyLevel === 'guest') {
return [
'allowed' => false,
'reason' => 'members_only',
'message' => $this->branding->get('members_only_message', 'This site is available to members only.')
];
}
return ['allowed' => true];
case 'invite_only':
if ($this->userPrivacyLevel === 'guest' || !$this->hasValidInvite()) {
return [
'allowed' => false,
'reason' => 'invite_only',
'message' => 'This site is invite-only. Please contact an administrator for access.'
];
}
return ['allowed' => true];
default:
return ['allowed' => true];
}
}
/**
* Check maintenance mode access
*/
private function checkMaintenanceAccess()
{
// Allow admin access
if ($this->userPrivacyLevel === 'admin') {
return ['allowed' => true];
}
// Check allowed IPs
$allowedIPs = $this->branding->get('maintenance_allowed_ips', '');
if (!empty($allowedIPs)) {
$userIP = $_SERVER['REMOTE_ADDR'];
$allowedList = array_map('trim', explode(',', $allowedIPs));
if (in_array($userIP, $allowedList)) {
return ['allowed' => true];
}
}
// Check bypass key
$bypassKey = $this->branding->get('maintenance_bypass_key', '');
if (!empty($bypassKey) && isset($_GET['bypass']) && $_GET['bypass'] === $bypassKey) {
return ['allowed' => true];
}
return [
'allowed' => false,
'reason' => 'maintenance',
'message' => $this->branding->get('maintenance_message', 'Site is currently under maintenance.')
];
}
/**
* Check content access permissions
*/
public function checkContentAccess($contentType, $contentId, $contentData = null)
{
// Get content privacy settings
$privacySettings = $this->getContentPrivacySettings($contentType, $contentId);
if (!$privacySettings) {
// Use default privacy level
$privacyLevel = $this->branding->get("default_{$contentType}_privacy", 'public');
} else {
$privacyLevel = $privacySettings['privacy_level'];
}
// Check basic privacy level
$accessCheck = $this->checkPrivacyLevel($privacyLevel);
if (!$accessCheck['allowed']) {
$this->logAccess($contentType, $contentId, false, $accessCheck['reason']);
return $accessCheck;
}
// Check additional restrictions
if ($privacySettings) {
// Password protection
if ($privacySettings['password_protected']) {
if (!$this->checkContentPassword($contentType, $contentId, $privacySettings['content_password'])) {
return [
'allowed' => false,
'reason' => 'password_required',
'message' => 'This content is password protected.'
];
}
}
// Age restrictions
if ($privacySettings['age_restricted']) {
$ageCheck = $this->checkAgeRestriction();
if (!$ageCheck['allowed']) {
return $ageCheck;
}
}
// Geographic restrictions
if ($privacySettings['geo_restricted']) {
$geoCheck = $this->checkGeographicRestriction($privacySettings);
if (!$geoCheck['allowed']) {
return $geoCheck;
}
}
// Scheduled content
if ($privacySettings['scheduled_public'] && strtotime($privacySettings['scheduled_public']) > time()) {
if ($this->userPrivacyLevel !== 'admin') {
return [
'allowed' => false,
'reason' => 'scheduled',
'message' => 'This content is not yet available.'
];
}
}
// Expired content
if ($privacySettings['expires_at'] && strtotime($privacySettings['expires_at']) < time()) {
return [
'allowed' => false,
'reason' => 'expired',
'message' => 'This content is no longer available.'
];
}
}
$this->logAccess($contentType, $contentId, true);
return ['allowed' => true];
}
/**
* Check privacy level access
*/
private function checkPrivacyLevel($privacyLevel)
{
switch ($privacyLevel) {
case 'public':
return ['allowed' => true];
case 'members_only':
if ($this->userPrivacyLevel === 'guest') {
return [
'allowed' => false,
'reason' => 'login_required',
'message' => $this->branding->get('login_required_message', 'Please log in to view this content.')
];
}
return ['allowed' => true];
case 'verified_only':
if (!in_array($this->userPrivacyLevel, ['verified', 'premium', 'admin'])) {
return [
'allowed' => false,
'reason' => 'verification_required',
'message' => 'This content is available to verified users only.'
];
}
return ['allowed' => true];
case 'premium_only':
if (!in_array($this->userPrivacyLevel, ['premium', 'admin'])) {
return [
'allowed' => false,
'reason' => 'premium_required',
'message' => 'This content is available to premium members only.'
];
}
return ['allowed' => true];
case 'admin_only':
if ($this->userPrivacyLevel !== 'admin') {
return [
'allowed' => false,
'reason' => 'admin_required',
'message' => 'This content is restricted to administrators.'
];
}
return ['allowed' => true];
case 'private':
return [
'allowed' => false,
'reason' => 'private',
'message' => $this->branding->get('private_content_message', 'This content is private.')
];
case 'unlisted':
// Unlisted content can be viewed if you have the direct link
return ['allowed' => true];
default:
return ['allowed' => true];
}
}
/**
* Get content privacy settings
*/
private function getContentPrivacySettings($contentType, $contentId)
{
global $db;
try {
$sql = "SELECT * FROM db_content_privacy WHERE content_type = ? AND content_id = ?";
$result = $db->Execute($sql, [$contentType, $contentId]);
if ($result && !$result->EOF) {
return $result->fields;
}
} catch (Exception $e) {
$logger = VLogger::getInstance();
$logger->logError('Failed to get content privacy settings: ' . $e->getMessage());
}
return null;
}
/**
* Check content password
*/
private function checkContentPassword($contentType, $contentId, $hashedPassword)
{
$sessionKey = "content_password_{$contentType}_{$contentId}";
// Check if password already verified in session
if (isset($_SESSION[$sessionKey]) && $_SESSION[$sessionKey] === true) {
return true;
}
// Check submitted password
if (isset($_POST['content_password'])) {
$submittedPassword = $_POST['content_password'];
if (password_verify($submittedPassword, $hashedPassword)) {
$_SESSION[$sessionKey] = true;
return true;
}
}
return false;
}
/**
* Check age restriction
*/
private function checkAgeRestriction()
{
if (!$this->branding->get('enable_age_verification', false)) {
return ['allowed' => true];
}
// Check if user has verified age
if ($this->currentUser) {
$ageVerified = $this->getUserPreference($this->currentUser, 'age_verified');
if ($ageVerified) {
return ['allowed' => true];
}
}
// Check session for age verification
if (isset($_SESSION['age_verified']) && $_SESSION['age_verified'] === true) {
return ['allowed' => true];
}
return [
'allowed' => false,
'reason' => 'age_verification_required',
'message' => $this->branding->get('age_restricted_message', 'This content is age-restricted.')
];
}
/**
* Check geographic restriction
*/
private function checkGeographicRestriction($privacySettings)
{
if (!$this->branding->get('enable_geo_blocking', false)) {
return ['allowed' => true];
}
$userCountry = $this->getUserCountry();
// Check blocked countries
if (!empty($privacySettings['blocked_countries'])) {
$blockedCountries = array_map('trim', explode(',', $privacySettings['blocked_countries']));
if (in_array($userCountry, $blockedCountries)) {
return [
'allowed' => false,
'reason' => 'geo_blocked',
'message' => $this->branding->get('geo_block_message', 'This content is not available in your region.')
];
}
}
// Check allowed countries
if (!empty($privacySettings['allowed_countries'])) {
$allowedCountries = array_map('trim', explode(',', $privacySettings['allowed_countries']));
if (!in_array($userCountry, $allowedCountries)) {
return [
'allowed' => false,
'reason' => 'geo_blocked',
'message' => $this->branding->get('geo_block_message', 'This content is not available in your region.')
];
}
}
return ['allowed' => true];
}
/**
* Get user's country from IP
*/
private function getUserCountry()
{
// This is a simplified version - in production, you'd use a GeoIP service
$ip = $_SERVER['REMOTE_ADDR'];
// For localhost/development
if ($ip === '127.0.0.1' || $ip === '::1') {
return 'US'; // Default for development
}
// You would integrate with a GeoIP service here
// For now, return a default
return 'US';
}
/**
* Set content privacy settings
*/
public function setContentPrivacy($contentType, $contentId, $settings)
{
global $db;
try {
$sql = "INSERT INTO db_content_privacy
(content_type, content_id, privacy_level, password_protected, content_password,
age_restricted, geo_restricted, allowed_countries, blocked_countries,
scheduled_public, expires_at, custom_message)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
privacy_level = VALUES(privacy_level),
password_protected = VALUES(password_protected),
content_password = VALUES(content_password),
age_restricted = VALUES(age_restricted),
geo_restricted = VALUES(geo_restricted),
allowed_countries = VALUES(allowed_countries),
blocked_countries = VALUES(blocked_countries),
scheduled_public = VALUES(scheduled_public),
expires_at = VALUES(expires_at),
custom_message = VALUES(custom_message),
updated_at = NOW()";
$password = null;
if (!empty($settings['password'])) {
$password = password_hash($settings['password'], PASSWORD_DEFAULT);
}
return $db->Execute($sql, [
$contentType,
$contentId,
$settings['privacy_level'] ?? 'public',
$settings['password_protected'] ?? false,
$password,
$settings['age_restricted'] ?? false,
$settings['geo_restricted'] ?? false,
$settings['allowed_countries'] ?? '',
$settings['blocked_countries'] ?? '',
$settings['scheduled_public'] ?? null,
$settings['expires_at'] ?? null,
$settings['custom_message'] ?? ''
]);
} catch (Exception $e) {
$logger = VLogger::getInstance();
$logger->logError('Failed to set content privacy: ' . $e->getMessage());
return false;
}
}
/**
* Get user privacy preference
*/
public function getUserPreference($userId, $key)
{
global $db;
try {
$sql = "SELECT preference_value FROM db_user_privacy_preferences WHERE usr_id = ? AND preference_key = ?";
$result = $db->Execute($sql, [$userId, $key]);
if ($result && !$result->EOF) {
return $result->fields['preference_value'];
}
} catch (Exception $e) {
$logger = VLogger::getInstance();
$logger->logError('Failed to get user preference: ' . $e->getMessage());
}
return null;
}
/**
* Set user privacy preference
*/
public function setUserPreference($userId, $key, $value)
{
global $db;
try {
$sql = "INSERT INTO db_user_privacy_preferences (usr_id, preference_key, preference_value)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE preference_value = VALUES(preference_value), updated_at = NOW()";
return $db->Execute($sql, [$userId, $key, $value]);
} catch (Exception $e) {
$logger = VLogger::getInstance();
$logger->logError('Failed to set user preference: ' . $e->getMessage());
return false;
}
}
/**
* Log access attempt
*/
private function logAccess($contentType, $contentId, $granted, $reason = null)
{
global $db;
try {
$sql = "INSERT INTO db_privacy_access_logs
(usr_id, ip_address, user_agent, access_type, content_type, content_id,
access_granted, denial_reason, privacy_level_required, user_privacy_level)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
$db->Execute($sql, [
$this->currentUser,
$_SERVER['REMOTE_ADDR'],
$_SERVER['HTTP_USER_AGENT'] ?? '',
'view',
$contentType,
$contentId,
$granted ? 1 : 0,
$reason,
'', // Would be filled based on content settings
$this->userPrivacyLevel
]);
} catch (Exception $e) {
// Fail silently for logging
}
}
/**
* Check if user has valid invite
*/
private function hasValidInvite()
{
// This would check for valid invite codes
// Implementation depends on your invite system
return false;
}
/**
* Generate privacy-aware content list
*/
public function filterContentList($contentList, $contentType = 'video')
{
$filteredList = [];
foreach ($contentList as $content) {
$accessCheck = $this->checkContentAccess($contentType, $content['id'], $content);
if ($accessCheck['allowed']) {
$filteredList[] = $content;
} else {
// Optionally include placeholder for restricted content
if ($this->branding->get('show_content_previews', true)) {
$content['restricted'] = true;
$content['restriction_reason'] = $accessCheck['reason'];
$content['restriction_message'] = $accessCheck['message'];
$filteredList[] = $content;
}
}
}
return $filteredList;
}
/**
* Get privacy statistics for admin
*/
public function getPrivacyStatistics()
{
global $db;
$stats = [];
try {
// Access attempts in last 24 hours
$sql = "SELECT COUNT(*) as total,
SUM(access_granted) as granted,
SUM(CASE WHEN access_granted = 0 THEN 1 ELSE 0 END) as denied
FROM db_privacy_access_logs
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)";
$result = $db->Execute($sql);
if ($result && !$result->EOF) {
$stats['access_attempts_24h'] = [
'total' => (int) $result->fields['total'],
'granted' => (int) $result->fields['granted'],
'denied' => (int) $result->fields['denied']
];
}
// Content privacy distribution
$sql = "SELECT privacy_level, COUNT(*) as count
FROM db_content_privacy
GROUP BY privacy_level";
$result = $db->Execute($sql);
$privacyDistribution = [];
if ($result) {
while (!$result->EOF) {
$privacyDistribution[$result->fields['privacy_level']] = (int) $result->fields['count'];
$result->MoveNext();
}
}
$stats['privacy_distribution'] = $privacyDistribution;
} catch (Exception $e) {
$logger = VLogger::getInstance();
$logger->logError('Failed to get privacy statistics: ' . $e->getMessage());
}
return $stats;
}
/**
* Get current user's privacy level
*/
public function getCurrentUserPrivacyLevel()
{
return $this->userPrivacyLevel;
}
/**
* Check if feature is accessible
*/
public function checkFeatureAccess($featureName)
{
$featureSettings = [
'search' => $this->branding->get('search_requires_login', false),
'trending' => $this->branding->get('trending_requires_login', false),
'categories' => $this->branding->get('categories_require_login', false),
'recommendations' => $this->branding->get('recommendations_require_login', true),
'upload' => true, // Always requires login
'comments' => true, // Always requires login
];
$requiresLogin = $featureSettings[$featureName] ?? false;
if ($requiresLogin && $this->userPrivacyLevel === 'guest') {
return [
'allowed' => false,
'reason' => 'login_required',
'message' => $this->branding->get('login_required_message', 'Please log in to use this feature.')
];
}
return ['allowed' => true];
}
}