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:
684
f_core/f_classes/class.api.php
Normal file
684
f_core/f_classes/class.api.php
Normal file
@@ -0,0 +1,684 @@
|
||||
<?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');
|
||||
|
||||
/**
|
||||
* RESTful API System for Mobile Apps and Third-Party Integration
|
||||
*/
|
||||
class VAPI
|
||||
{
|
||||
private $db;
|
||||
private $logger;
|
||||
private $rbac;
|
||||
private $version = 'v1';
|
||||
private $rateLimiter;
|
||||
|
||||
// API response codes
|
||||
const SUCCESS = 200;
|
||||
const CREATED = 201;
|
||||
const BAD_REQUEST = 400;
|
||||
const UNAUTHORIZED = 401;
|
||||
const FORBIDDEN = 403;
|
||||
const NOT_FOUND = 404;
|
||||
const METHOD_NOT_ALLOWED = 405;
|
||||
const RATE_LIMITED = 429;
|
||||
const SERVER_ERROR = 500;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->db = VDatabase::getInstance();
|
||||
$this->logger = VLogger::getInstance();
|
||||
$this->rbac = VRBAC::getInstance();
|
||||
$this->rateLimiter = VSecurity::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle API request
|
||||
* @param string $method HTTP method
|
||||
* @param string $endpoint API endpoint
|
||||
* @param array $data Request data
|
||||
* @param array $headers Request headers
|
||||
* @return array API response
|
||||
*/
|
||||
public function handleRequest($method, $endpoint, $data = [], $headers = [])
|
||||
{
|
||||
try {
|
||||
// Set CORS headers
|
||||
$this->setCORSHeaders();
|
||||
|
||||
// Handle preflight requests
|
||||
if ($method === 'OPTIONS') {
|
||||
return $this->response(['message' => 'OK'], self::SUCCESS);
|
||||
}
|
||||
|
||||
// Rate limiting
|
||||
$clientId = $this->getClientIdentifier($headers);
|
||||
if (!$this->checkRateLimit($clientId, $endpoint)) {
|
||||
return $this->response(['error' => 'Rate limit exceeded'], self::RATE_LIMITED);
|
||||
}
|
||||
|
||||
// Authentication
|
||||
$user = $this->authenticateRequest($headers);
|
||||
|
||||
// Route request
|
||||
$response = $this->routeRequest($method, $endpoint, $data, $user);
|
||||
|
||||
// Log API request
|
||||
$this->logAPIRequest($method, $endpoint, $user['id'] ?? null, $response['status']);
|
||||
|
||||
return $response;
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error('API request failed', [
|
||||
'method' => $method,
|
||||
'endpoint' => $endpoint,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
|
||||
return $this->response(['error' => 'Internal server error'], self::SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route API request to appropriate handler
|
||||
* @param string $method HTTP method
|
||||
* @param string $endpoint API endpoint
|
||||
* @param array $data Request data
|
||||
* @param array|null $user Authenticated user
|
||||
* @return array API response
|
||||
*/
|
||||
private function routeRequest($method, $endpoint, $data, $user)
|
||||
{
|
||||
$parts = explode('/', trim($endpoint, '/'));
|
||||
$resource = $parts[0] ?? '';
|
||||
$id = $parts[1] ?? null;
|
||||
$action = $parts[2] ?? null;
|
||||
|
||||
switch ($resource) {
|
||||
case 'auth':
|
||||
return $this->handleAuth($method, $id, $data);
|
||||
|
||||
case 'videos':
|
||||
return $this->handleVideos($method, $id, $action, $data, $user);
|
||||
|
||||
case 'users':
|
||||
return $this->handleUsers($method, $id, $action, $data, $user);
|
||||
|
||||
case 'live':
|
||||
return $this->handleLiveStreams($method, $id, $action, $data, $user);
|
||||
|
||||
case 'search':
|
||||
return $this->handleSearch($method, $data, $user);
|
||||
|
||||
case 'upload':
|
||||
return $this->handleUpload($method, $data, $user);
|
||||
|
||||
case 'analytics':
|
||||
return $this->handleAnalytics($method, $id, $action, $data, $user);
|
||||
|
||||
default:
|
||||
return $this->response(['error' => 'Endpoint not found'], self::NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle authentication endpoints
|
||||
* @param string $method HTTP method
|
||||
* @param string $action Action
|
||||
* @param array $data Request data
|
||||
* @return array API response
|
||||
*/
|
||||
private function handleAuth($method, $action, $data)
|
||||
{
|
||||
switch ($action) {
|
||||
case 'login':
|
||||
if ($method !== 'POST') {
|
||||
return $this->response(['error' => 'Method not allowed'], self::METHOD_NOT_ALLOWED);
|
||||
}
|
||||
return $this->login($data);
|
||||
|
||||
case 'register':
|
||||
if ($method !== 'POST') {
|
||||
return $this->response(['error' => 'Method not allowed'], self::METHOD_NOT_ALLOWED);
|
||||
}
|
||||
return $this->register($data);
|
||||
|
||||
case 'refresh':
|
||||
if ($method !== 'POST') {
|
||||
return $this->response(['error' => 'Method not allowed'], self::METHOD_NOT_ALLOWED);
|
||||
}
|
||||
return $this->refreshToken($data);
|
||||
|
||||
case 'logout':
|
||||
if ($method !== 'POST') {
|
||||
return $this->response(['error' => 'Method not allowed'], self::METHOD_NOT_ALLOWED);
|
||||
}
|
||||
return $this->logout($data);
|
||||
|
||||
default:
|
||||
return $this->response(['error' => 'Auth action not found'], self::NOT_FOUND);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle video endpoints
|
||||
* @param string $method HTTP method
|
||||
* @param string $id Video ID
|
||||
* @param string $action Action
|
||||
* @param array $data Request data
|
||||
* @param array|null $user Authenticated user
|
||||
* @return array API response
|
||||
*/
|
||||
private function handleVideos($method, $id, $action, $data, $user)
|
||||
{
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
if ($id) {
|
||||
if ($action === 'comments') {
|
||||
return $this->getVideoComments($id, $data);
|
||||
} elseif ($action === 'related') {
|
||||
return $this->getRelatedVideos($id, $data);
|
||||
} else {
|
||||
return $this->getVideo($id, $user);
|
||||
}
|
||||
} else {
|
||||
return $this->getVideos($data, $user);
|
||||
}
|
||||
|
||||
case 'POST':
|
||||
if ($id && $action === 'like') {
|
||||
return $this->likeVideo($id, $user);
|
||||
} elseif ($id && $action === 'comment') {
|
||||
return $this->commentVideo($id, $data, $user);
|
||||
} else {
|
||||
return $this->createVideo($data, $user);
|
||||
}
|
||||
|
||||
case 'PUT':
|
||||
if ($id) {
|
||||
return $this->updateVideo($id, $data, $user);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
if ($id) {
|
||||
return $this->deleteVideo($id, $user);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->response(['error' => 'Method not allowed'], self::METHOD_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle user endpoints
|
||||
* @param string $method HTTP method
|
||||
* @param string $id User ID
|
||||
* @param string $action Action
|
||||
* @param array $data Request data
|
||||
* @param array|null $user Authenticated user
|
||||
* @return array API response
|
||||
*/
|
||||
private function handleUsers($method, $id, $action, $data, $user)
|
||||
{
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
if ($id) {
|
||||
if ($action === 'videos') {
|
||||
return $this->getUserVideos($id, $data);
|
||||
} elseif ($action === 'followers') {
|
||||
return $this->getUserFollowers($id, $data);
|
||||
} elseif ($action === 'following') {
|
||||
return $this->getUserFollowing($id, $data);
|
||||
} else {
|
||||
return $this->getUser($id, $user);
|
||||
}
|
||||
} else {
|
||||
return $this->getUsers($data, $user);
|
||||
}
|
||||
|
||||
case 'POST':
|
||||
if ($id && $action === 'follow') {
|
||||
return $this->followUser($id, $user);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'PUT':
|
||||
if ($id === 'me' || ($user && $id == $user['id'])) {
|
||||
return $this->updateProfile($data, $user);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
if ($id && $action === 'follow') {
|
||||
return $this->unfollowUser($id, $user);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->response(['error' => 'Method not allowed'], self::METHOD_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle live stream endpoints
|
||||
* @param string $method HTTP method
|
||||
* @param string $id Stream ID
|
||||
* @param string $action Action
|
||||
* @param array $data Request data
|
||||
* @param array|null $user Authenticated user
|
||||
* @return array API response
|
||||
*/
|
||||
private function handleLiveStreams($method, $id, $action, $data, $user)
|
||||
{
|
||||
if (!$user) {
|
||||
return $this->response(['error' => 'Authentication required'], self::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
switch ($method) {
|
||||
case 'GET':
|
||||
if ($id) {
|
||||
return $this->getLiveStream($id, $user);
|
||||
} else {
|
||||
return $this->getLiveStreams($data, $user);
|
||||
}
|
||||
|
||||
case 'POST':
|
||||
if ($id && $action === 'start') {
|
||||
return $this->startLiveStream($id, $user);
|
||||
} elseif ($id && $action === 'stop') {
|
||||
return $this->stopLiveStream($id, $user);
|
||||
} else {
|
||||
return $this->createLiveStream($data, $user);
|
||||
}
|
||||
|
||||
case 'PUT':
|
||||
if ($id) {
|
||||
return $this->updateLiveStream($id, $data, $user);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DELETE':
|
||||
if ($id) {
|
||||
return $this->deleteLiveStream($id, $user);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->response(['error' => 'Method not allowed'], self::METHOD_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate API request
|
||||
* @param array $headers Request headers
|
||||
* @return array|null User data or null if not authenticated
|
||||
*/
|
||||
private function authenticateRequest($headers)
|
||||
{
|
||||
$authHeader = $headers['Authorization'] ?? $headers['authorization'] ?? '';
|
||||
|
||||
if (empty($authHeader)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Support Bearer token format
|
||||
if (strpos($authHeader, 'Bearer ') === 0) {
|
||||
$token = substr($authHeader, 7);
|
||||
return $this->validateJWTToken($token);
|
||||
}
|
||||
|
||||
// Support API key format
|
||||
if (strpos($authHeader, 'ApiKey ') === 0) {
|
||||
$apiKey = substr($authHeader, 7);
|
||||
return $this->validateAPIKey($apiKey);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate JWT token
|
||||
* @param string $token JWT token
|
||||
* @return array|null User data or null if invalid
|
||||
*/
|
||||
private function validateJWTToken($token)
|
||||
{
|
||||
try {
|
||||
// Simple JWT validation (in production, use a proper JWT library)
|
||||
$parts = explode('.', $token);
|
||||
if (count($parts) !== 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$payload = json_decode(base64_decode($parts[1]), true);
|
||||
|
||||
if (!$payload || !isset($payload['user_id']) || $payload['exp'] < time()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get user from database
|
||||
$query = "SELECT usr_id, usr_user, usr_email, usr_dname FROM db_accountuser WHERE usr_id = ? AND usr_status = 'active'";
|
||||
$result = $this->db->doQuery($query, [$payload['user_id']]);
|
||||
$user = $this->db->doFetch($result);
|
||||
|
||||
if ($user) {
|
||||
return [
|
||||
'id' => $user['usr_id'],
|
||||
'username' => $user['usr_user'],
|
||||
'email' => $user['usr_email'],
|
||||
'display_name' => $user['usr_dname']
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error('JWT validation failed', ['error' => $e->getMessage()]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate API key
|
||||
* @param string $apiKey API key
|
||||
* @return array|null User data or null if invalid
|
||||
*/
|
||||
private function validateAPIKey($apiKey)
|
||||
{
|
||||
try {
|
||||
$query = "SELECT ak.user_id, ak.permissions, au.usr_user, au.usr_email, au.usr_dname
|
||||
FROM db_api_keys ak
|
||||
JOIN db_accountuser au ON ak.user_id = au.usr_id
|
||||
WHERE ak.api_key = ? AND ak.status = 'active' AND ak.expires_at > NOW()";
|
||||
$result = $this->db->doQuery($query, [$apiKey]);
|
||||
$keyData = $this->db->doFetch($result);
|
||||
|
||||
if ($keyData) {
|
||||
return [
|
||||
'id' => $keyData['user_id'],
|
||||
'username' => $keyData['usr_user'],
|
||||
'email' => $keyData['usr_email'],
|
||||
'display_name' => $keyData['usr_dname'],
|
||||
'api_permissions' => json_decode($keyData['permissions'], true) ?: []
|
||||
];
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error('API key validation failed', ['error' => $e->getMessage()]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate JWT token
|
||||
* @param array $user User data
|
||||
* @return string JWT token
|
||||
*/
|
||||
private function generateJWTToken($user)
|
||||
{
|
||||
$header = json_encode(['typ' => 'JWT', 'alg' => 'HS256']);
|
||||
$payload = json_encode([
|
||||
'user_id' => $user['id'],
|
||||
'username' => $user['username'],
|
||||
'iat' => time(),
|
||||
'exp' => time() + (24 * 60 * 60) // 24 hours
|
||||
]);
|
||||
|
||||
$headerEncoded = base64_encode($header);
|
||||
$payloadEncoded = base64_encode($payload);
|
||||
|
||||
$signature = hash_hmac('sha256', $headerEncoded . '.' . $payloadEncoded, 'your-secret-key', true);
|
||||
$signatureEncoded = base64_encode($signature);
|
||||
|
||||
return $headerEncoded . '.' . $payloadEncoded . '.' . $signatureEncoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Login user
|
||||
* @param array $data Login data
|
||||
* @return array API response
|
||||
*/
|
||||
private function login($data)
|
||||
{
|
||||
$username = $data['username'] ?? '';
|
||||
$password = $data['password'] ?? '';
|
||||
|
||||
if (empty($username) || empty($password)) {
|
||||
return $this->response(['error' => 'Username and password required'], self::BAD_REQUEST);
|
||||
}
|
||||
|
||||
// Validate credentials
|
||||
$query = "SELECT usr_id, usr_user, usr_email, usr_dname, usr_password
|
||||
FROM db_accountuser
|
||||
WHERE (usr_user = ? OR usr_email = ?) AND usr_status = 'active'";
|
||||
$result = $this->db->doQuery($query, [$username, $username]);
|
||||
$user = $this->db->doFetch($result);
|
||||
|
||||
if (!$user || !password_verify($password, $user['usr_password'])) {
|
||||
return $this->response(['error' => 'Invalid credentials'], self::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
// Generate token
|
||||
$userData = [
|
||||
'id' => $user['usr_id'],
|
||||
'username' => $user['usr_user'],
|
||||
'email' => $user['usr_email'],
|
||||
'display_name' => $user['usr_dname']
|
||||
];
|
||||
|
||||
$token = $this->generateJWTToken($userData);
|
||||
|
||||
// Update last login
|
||||
$this->db->doUpdate('db_accountuser', 'usr_id', [
|
||||
'usr_lastlogin' => date('Y-m-d H:i:s')
|
||||
], $user['usr_id']);
|
||||
|
||||
return $this->response([
|
||||
'token' => $token,
|
||||
'user' => $userData,
|
||||
'expires_in' => 86400 // 24 hours
|
||||
], self::SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get videos
|
||||
* @param array $params Query parameters
|
||||
* @param array|null $user Authenticated user
|
||||
* @return array API response
|
||||
*/
|
||||
private function getVideos($params, $user)
|
||||
{
|
||||
$page = max(1, (int)($params['page'] ?? 1));
|
||||
$limit = min(50, max(1, (int)($params['limit'] ?? 20)));
|
||||
$offset = ($page - 1) * $limit;
|
||||
|
||||
$category = $params['category'] ?? '';
|
||||
$sort = $params['sort'] ?? 'recent';
|
||||
|
||||
$where = ["vf.file_type = 'video'", "vf.privacy = 'public'"];
|
||||
$queryParams = [];
|
||||
|
||||
if ($category) {
|
||||
$where[] = "vf.file_category = ?";
|
||||
$queryParams[] = $category;
|
||||
}
|
||||
|
||||
$orderBy = match($sort) {
|
||||
'popular' => 'vf.file_views DESC',
|
||||
'recent' => 'vf.upload_date DESC',
|
||||
'rating' => 'vf.file_rating DESC',
|
||||
default => 'vf.upload_date DESC'
|
||||
};
|
||||
|
||||
$whereClause = implode(' AND ', $where);
|
||||
|
||||
$query = "SELECT vf.file_key, vf.file_title, vf.file_description, vf.file_views,
|
||||
vf.file_rating, vf.upload_date, vf.file_duration, vf.file_size,
|
||||
au.usr_user, au.usr_dname
|
||||
FROM db_videofiles vf
|
||||
JOIN db_accountuser au ON vf.usr_id = au.usr_id
|
||||
WHERE {$whereClause}
|
||||
ORDER BY {$orderBy}
|
||||
LIMIT {$limit} OFFSET {$offset}";
|
||||
|
||||
$result = $this->db->doQuery($query, $queryParams);
|
||||
|
||||
$videos = [];
|
||||
while ($row = $this->db->doFetch($result)) {
|
||||
$videos[] = [
|
||||
'id' => $row['file_key'],
|
||||
'title' => $row['file_title'],
|
||||
'description' => $row['file_description'],
|
||||
'views' => (int)$row['file_views'],
|
||||
'rating' => (float)$row['file_rating'],
|
||||
'duration' => (int)$row['file_duration'],
|
||||
'size' => (int)$row['file_size'],
|
||||
'uploaded_at' => $row['upload_date'],
|
||||
'uploader' => [
|
||||
'username' => $row['usr_user'],
|
||||
'display_name' => $row['usr_dname']
|
||||
],
|
||||
'thumbnail_url' => "/thumbnails/{$row['file_key']}_medium.jpg",
|
||||
'video_url' => "/watch/{$row['file_key']}"
|
||||
];
|
||||
}
|
||||
|
||||
return $this->response([
|
||||
'videos' => $videos,
|
||||
'pagination' => [
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'total' => $this->getVideoCount($where, $queryParams)
|
||||
]
|
||||
], self::SUCCESS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create API response
|
||||
* @param array $data Response data
|
||||
* @param int $status HTTP status code
|
||||
* @return array API response
|
||||
*/
|
||||
private function response($data, $status = self::SUCCESS)
|
||||
{
|
||||
return [
|
||||
'status' => $status,
|
||||
'data' => $data,
|
||||
'timestamp' => time(),
|
||||
'version' => $this->version
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set CORS headers
|
||||
*/
|
||||
private function setCORSHeaders()
|
||||
{
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||
header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
|
||||
header('Access-Control-Max-Age: 86400');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check rate limit
|
||||
* @param string $clientId Client identifier
|
||||
* @param string $endpoint Endpoint
|
||||
* @return bool True if within limits
|
||||
*/
|
||||
private function checkRateLimit($clientId, $endpoint)
|
||||
{
|
||||
// Different limits for different endpoints
|
||||
$limits = [
|
||||
'auth' => ['requests' => 10, 'window' => 300], // 10 requests per 5 minutes
|
||||
'upload' => ['requests' => 5, 'window' => 3600], // 5 uploads per hour
|
||||
'default' => ['requests' => 100, 'window' => 3600] // 100 requests per hour
|
||||
];
|
||||
|
||||
$endpointType = explode('/', $endpoint)[0] ?? 'default';
|
||||
$limit = $limits[$endpointType] ?? $limits['default'];
|
||||
|
||||
return $this->rateLimiter->checkRateLimit(
|
||||
"api_{$clientId}_{$endpointType}",
|
||||
$limit['requests'],
|
||||
$limit['window'],
|
||||
"api_{$endpointType}"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get client identifier
|
||||
* @param array $headers Request headers
|
||||
* @return string Client identifier
|
||||
*/
|
||||
private function getClientIdentifier($headers)
|
||||
{
|
||||
// Use API key if available, otherwise IP address
|
||||
$authHeader = $headers['Authorization'] ?? $headers['authorization'] ?? '';
|
||||
|
||||
if (strpos($authHeader, 'ApiKey ') === 0) {
|
||||
return 'key_' . substr($authHeader, 7, 10);
|
||||
}
|
||||
|
||||
return 'ip_' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown');
|
||||
}
|
||||
|
||||
/**
|
||||
* Log API request
|
||||
* @param string $method HTTP method
|
||||
* @param string $endpoint Endpoint
|
||||
* @param int|null $userId User ID
|
||||
* @param int $status Response status
|
||||
*/
|
||||
private function logAPIRequest($method, $endpoint, $userId, $status)
|
||||
{
|
||||
try {
|
||||
$logData = [
|
||||
'method' => $method,
|
||||
'endpoint' => $endpoint,
|
||||
'user_id' => $userId,
|
||||
'status' => $status,
|
||||
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
|
||||
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
|
||||
'created_at' => date('Y-m-d H:i:s')
|
||||
];
|
||||
|
||||
$this->db->doInsert('db_api_logs', $logData);
|
||||
|
||||
} catch (Exception $e) {
|
||||
$this->logger->error('Failed to log API request', [
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get video count for pagination
|
||||
* @param array $where Where conditions
|
||||
* @param array $params Query parameters
|
||||
* @return int Total count
|
||||
*/
|
||||
private function getVideoCount($where, $params)
|
||||
{
|
||||
$whereClause = implode(' AND ', $where);
|
||||
$query = "SELECT COUNT(*) as total FROM db_videofiles vf WHERE {$whereClause}";
|
||||
$result = $this->db->doQuery($query, $params);
|
||||
$row = $this->db->doFetch($result);
|
||||
|
||||
return (int)($row['total'] ?? 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user