Файловый менеджер - Редактировать - /home/gqdcvggs/imators.systems/traffic/api.php
Назад
<?php header('Content-Type: application/json'); header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type, Authorization'); // Handle preflight OPTIONS request if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; } // Database configuration $host = 'localhost:3306'; $dbname = 'gqdcvggs_traffic'; $username = 'gqdcvggs'; $password = 'imaors_management.346980*#@-onlyforcpanel;forchange'; // Environment variables from .env file - fallback to hardcoded values if (file_exists('.env')) { $env = parse_ini_file('.env'); $mapbox_token = $env['MAPBOX_API_KEY'] ?? ''; } try { $pdo = new PDO("mysql:host=$host;dbname=$dbname", $username, $password); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { sendResponse(['success' => false, 'message' => 'Database connection failed'], 500); exit; } // Parse request $request_uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $path = trim(str_replace('/api', '', $request_uri), '/'); $method = $_SERVER['REQUEST_METHOD']; $action = $_GET['action'] ?? ''; // Extract API key if provided $api_key = $_GET['api_key'] ?? ''; // Get JWT token from Authorization header for authenticated routes $token = null; $user_id = null; if (isset($_SERVER['HTTP_AUTHORIZATION'])) { if (preg_match('/Bearer\s(\S+)/', $_SERVER['HTTP_AUTHORIZATION'], $matches)) { $token = $matches[1]; // If we have a token, verify it and extract user ID if ($token) { try { $user = verifyJWTToken($token); if ($user) { $user_id = $user['id']; } } catch (Exception $e) { // Invalid token, will continue as unauthenticated } } } } // Process the request if ($action) { // Legacy API format with action parameter processLegacyRequest($pdo, $action, $api_key, $user_id); } else { // RESTful API format with path-based routing processRESTRequest($pdo, $path, $method, $api_key, $user_id); } // Process legacy API requests (for backward compatibility) function processLegacyRequest($pdo, $action, $api_key, $user_id) { // Validate API key for certain actions $publicActions = ['getTrafficLights', 'getLightById']; if (!in_array($action, $publicActions) && !validateApiKey($pdo, $api_key) && !$user_id) { sendResponse(['success' => false, 'message' => 'Authentication required'], 401); return; } switch ($action) { case 'getTrafficLights': getAllLights($pdo); break; case 'getLightById': $id = isset($_GET['id']) ? $_GET['id'] : null; getLightById($pdo, $id); break; case 'addTrafficLight': addLight($pdo, $user_id); break; case 'updateTiming': updateLightTiming($pdo, $user_id); break; case 'getNearbylights': $lat = isset($_GET['lat']) ? $_GET['lat'] : null; $lng = isset($_GET['lng']) ? $_GET['lng'] : null; $radius = isset($_GET['radius']) ? $_GET['radius'] : 1; getNearbyLights($pdo, $lat, $lng, $radius); break; default: sendResponse(['success' => false, 'message' => 'Invalid action'], 404); break; } } // Process RESTful API requests function processRESTRequest($pdo, $path, $method, $api_key, $user_id) { // Parse path segments $segments = explode('/', $path); $base = $segments[0] ?? ''; // Route the request switch ($base) { case 'traffic-lights': handleTrafficLightsRoutes($pdo, $segments, $method, $user_id); break; case 'user': if (!$user_id) { sendResponse(['success' => false, 'message' => 'Authentication required'], 401); return; } handleUserRoutes($pdo, $segments, $method, $user_id); break; case 'login': if ($method === 'POST') { handleLogin($pdo); } else { sendResponse(['success' => false, 'message' => 'Method not allowed'], 405); } break; case 'register': if ($method === 'POST') { handleRegister($pdo); } else { sendResponse(['success' => false, 'message' => 'Method not allowed'], 405); } break; case 'mapbox-token': if ($method === 'GET') { global $mapbox_token; sendResponse(['success' => true, 'token' => $mapbox_token]); } else { sendResponse(['success' => false, 'message' => 'Method not allowed'], 405); } break; default: sendResponse(['success' => false, 'message' => 'Endpoint not found'], 404); break; } } // Traffic Lights routes handler function handleTrafficLightsRoutes($pdo, $segments, $method, $user_id) { $id = $segments[1] ?? null; $action = $segments[2] ?? null; if (!$id) { // Collection endpoints: /traffic-lights switch ($method) { case 'GET': getAllLights($pdo); break; case 'POST': addLight($pdo, $user_id); break; default: sendResponse(['success' => false, 'message' => 'Method not allowed'], 405); break; } } else { // Resource endpoints: /traffic-lights/{id} if (!$action) { switch ($method) { case 'GET': getLightById($pdo, $id); break; case 'PUT': updateLight($pdo, $id, $user_id); break; case 'DELETE': deleteLight($pdo, $id, $user_id); break; default: sendResponse(['success' => false, 'message' => 'Method not allowed'], 405); break; } } else { // Sub-resource endpoints: /traffic-lights/{id}/{action} if ($action === 'timing' && $method === 'POST') { updateLightTiming($pdo, $user_id, $id); } else { sendResponse(['success' => false, 'message' => 'Endpoint not found'], 404); } } } } // User routes handler function handleUserRoutes($pdo, $segments, $method, $user_id) { $resource = $segments[1] ?? null; if ($resource === 'traffic-lights') { if ($method === 'GET') { getUserTrafficLights($pdo, $user_id); } else { sendResponse(['success' => false, 'message' => 'Method not allowed'], 405); } } else if ($resource === 'profile') { switch ($method) { case 'GET': getUserProfile($pdo, $user_id); break; case 'PUT': updateUserProfile($pdo, $user_id); break; default: sendResponse(['success' => false, 'message' => 'Method not allowed'], 405); break; } } else { sendResponse(['success' => false, 'message' => 'Endpoint not found'], 404); } } // User login handler function handleLogin($pdo) { $json = file_get_contents('php://input'); $data = json_decode($json, true); if (!$data || !isset($data['email']) || !isset($data['password'])) { sendResponse(['success' => false, 'message' => 'Email and password are required'], 400); return; } try { $stmt = $pdo->prepare("SELECT id, name, email, password FROM users WHERE email = ?"); $stmt->execute([$data['email']]); $user = $stmt->fetch(PDO::FETCH_ASSOC); if (!$user || !password_verify($data['password'], $user['password'])) { sendResponse(['success' => false, 'message' => 'Invalid email or password'], 401); return; } // Generate JWT token $token = generateJWTToken([ 'id' => $user['id'], 'name' => $user['name'], 'email' => $user['email'] ]); sendResponse([ 'success' => true, 'message' => 'Login successful', 'token' => $token, 'user' => [ 'id' => $user['id'], 'name' => $user['name'], 'email' => $user['email'] ] ]); } catch (PDOException $e) { sendResponse(['success' => false, 'message' => 'Login failed'], 500); } } // User registration handler function handleRegister($pdo) { $json = file_get_contents('php://input'); $data = json_decode($json, true); if (!$data || !isset($data['name']) || !isset($data['email']) || !isset($data['password'])) { sendResponse(['success' => false, 'message' => 'Name, email and password are required'], 400); return; } try { // Check if email already exists $stmt = $pdo->prepare("SELECT id FROM users WHERE email = ?"); $stmt->execute([$data['email']]); if ($stmt->rowCount() > 0) { sendResponse(['success' => false, 'message' => 'Email already registered'], 409); return; } // Hash password $hashedPassword = password_hash($data['password'], PASSWORD_DEFAULT); // Insert new user $stmt = $pdo->prepare("INSERT INTO users (name, email, password, created_at) VALUES (?, ?, ?, NOW())"); $stmt->execute([$data['name'], $data['email'], $hashedPassword]); $userId = $pdo->lastInsertId(); // Generate JWT token $token = generateJWTToken([ 'id' => $userId, 'name' => $data['name'], 'email' => $data['email'] ]); sendResponse([ 'success' => true, 'message' => 'Registration successful', 'token' => $token, 'user' => [ 'id' => $userId, 'name' => $data['name'], 'email' => $data['email'] ] ]); } catch (PDOException $e) { sendResponse(['success' => false, 'message' => 'Registration failed'], 500); } } // Get user profile function getUserProfile($pdo, $user_id) { try { $stmt = $pdo->prepare("SELECT id, name, email, created_at FROM users WHERE id = ?"); $stmt->execute([$user_id]); $user = $stmt->fetch(PDO::FETCH_ASSOC); if (!$user) { sendResponse(['success' => false, 'message' => 'User not found'], 404); return; } sendResponse(['success' => true, 'user' => $user]); } catch (PDOException $e) { sendResponse(['success' => false, 'message' => 'Failed to fetch user profile'], 500); } } // Update user profile function updateUserProfile($pdo, $user_id) { $json = file_get_contents('php://input'); $data = json_decode($json, true); if (!$data) { sendResponse(['success' => false, 'message' => 'Invalid data format'], 400); return; } $allowedFields = ['name', 'email']; $updates = []; $values = []; foreach ($allowedFields as $field) { if (isset($data[$field]) && !empty($data[$field])) { $updates[] = "$field = ?"; $values[] = $data[$field]; } } if (empty($updates)) { sendResponse(['success' => false, 'message' => 'No valid fields to update'], 400); return; } $values[] = $user_id; try { $stmt = $pdo->prepare("UPDATE users SET " . implode(', ', $updates) . " WHERE id = ?"); $stmt->execute($values); if ($stmt->rowCount() === 0) { sendResponse(['success' => false, 'message' => 'No changes made or user not found'], 404); return; } $stmt = $pdo->prepare("SELECT id, name, email, created_at FROM users WHERE id = ?"); $stmt->execute([$user_id]); $user = $stmt->fetch(PDO::FETCH_ASSOC); sendResponse(['success' => true, 'message' => 'Profile updated successfully', 'user' => $user]); } catch (PDOException $e) { sendResponse(['success' => false, 'message' => 'Failed to update profile'], 500); } } // Get all traffic lights submitted by the user function getUserTrafficLights($pdo, $user_id) { try { $stmt = $pdo->prepare("SELECT * FROM traffic_lights WHERE user_id = ?"); $stmt->execute([$user_id]); $lights = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($lights as &$light) { $light['status'] = calculateLightStatus($light); } sendResponse(['success' => true, 'lights' => $lights]); } catch (PDOException $e) { sendResponse(['success' => false, 'message' => 'Failed to fetch user traffic lights'], 500); } } // Validate API key function validateApiKey($pdo, $api_key) { if (empty($api_key)) { return false; } try { $stmt = $pdo->prepare("SELECT * FROM api_keys WHERE api_key = ? AND active = 1"); $stmt->execute([$api_key]); return $stmt->rowCount() > 0; } catch (PDOException $e) { return false; } } // Get all traffic lights function getAllLights($pdo) { try { $stmt = $pdo->query("SELECT * FROM traffic_lights WHERE status = 'approved'"); $lights = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($lights as &$light) { $light['status'] = calculateLightStatus($light); } sendResponse(['success' => true, 'lights' => $lights]); } catch (PDOException $e) { sendResponse(['success' => false, 'message' => 'Failed to fetch traffic lights'], 500); } } // Get traffic light by ID function getLightById($pdo, $id) { if (!$id) { sendResponse(['success' => false, 'message' => 'ID is required'], 400); return; } try { $stmt = $pdo->prepare("SELECT * FROM traffic_lights WHERE id = ?"); $stmt->execute([$id]); $light = $stmt->fetch(PDO::FETCH_ASSOC); if ($light) { $light['status'] = calculateLightStatus($light); sendResponse(['success' => true, 'light' => $light]); } else { sendResponse(['success' => false, 'message' => 'Traffic light not found'], 404); } } catch (PDOException $e) { sendResponse(['success' => false, 'message' => 'Failed to fetch traffic light'], 500); } } // Get nearby traffic lights function getNearbyLights($pdo, $lat, $lng, $radius) { if (!$lat || !$lng) { sendResponse(['success' => false, 'message' => 'Latitude and longitude are required'], 400); return; } try { $earth_radius = 6371; // Earth's radius in km $stmt = $pdo->prepare(" SELECT *, ($earth_radius * acos(cos(radians(?)) * cos(radians(latitude)) * cos(radians(longitude) - radians(?)) + sin(radians(?)) * sin(radians(latitude)))) AS distance FROM traffic_lights WHERE status = 'approved' HAVING distance <= ? ORDER BY distance "); $stmt->execute([$lat, $lng, $lat, $radius]); $lights = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($lights as &$light) { $light['status'] = calculateLightStatus($light); } sendResponse(['success' => true, 'lights' => $lights]); } catch (PDOException $e) { sendResponse(['success' => false, 'message' => 'Failed to fetch nearby traffic lights'], 500); } } // Add new traffic light function addLight($pdo, $user_id = null) { $json = file_get_contents('php://input'); $data = json_decode($json, true); if (!$data) { sendResponse(['success' => false, 'message' => 'Invalid data format'], 400); return; } $required = ['name', 'latitude', 'longitude', 'direction', 'red_duration', 'green_duration']; foreach ($required as $field) { if (!isset($data[$field])) { sendResponse(['success' => false, 'message' => "$field is required"], 400); return; } } try { $sql = "INSERT INTO traffic_lights (name, latitude, longitude, direction, red_duration, green_duration, user_id, status, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', NOW())"; $stmt = $pdo->prepare($sql); $stmt->execute([ $data['name'], $data['latitude'], $data['longitude'], $data['direction'], $data['red_duration'], $data['green_duration'], $user_id ]); $id = $pdo->lastInsertId(); $stmt = $pdo->prepare("SELECT * FROM traffic_lights WHERE id = ?"); $stmt->execute([$id]); $light = $stmt->fetch(PDO::FETCH_ASSOC); $light['status'] = calculateLightStatus($light); sendResponse(['success' => true, 'message' => 'Traffic light added successfully', 'light' => $light]); } catch (PDOException $e) { sendResponse(['success' => false, 'message' => 'Failed to add traffic light'], 500); } } // Update traffic light function updateLight($pdo, $id, $user_id = null) { $json = file_get_contents('php://input'); $data = json_decode($json, true); if (!$data) { sendResponse(['success' => false, 'message' => 'Invalid data format'], 400); return; } // Verify ownership if user is authenticated if ($user_id) { $stmt = $pdo->prepare("SELECT user_id FROM traffic_lights WHERE id = ?"); $stmt->execute([$id]); $light = $stmt->fetch(PDO::FETCH_ASSOC); if (!$light) { sendResponse(['success' => false, 'message' => 'Traffic light not found'], 404); return; } if ($light['user_id'] != $user_id) { // Allow admins to update any light (would need a proper admin check here) $stmt = $pdo->prepare("SELECT role FROM users WHERE id = ?"); $stmt->execute([$user_id]); $user = $stmt->fetch(PDO::FETCH_ASSOC); if (!$user || $user['role'] !== 'admin') { sendResponse(['success' => false, 'message' => 'You do not have permission to update this traffic light'], 403); return; } } } $allowedFields = ['name', 'latitude', 'longitude', 'direction', 'red_duration', 'green_duration', 'status']; $fields = []; $values = []; foreach ($data as $key => $value) { if (in_array($key, $allowedFields)) { $fields[] = "$key = ?"; $values[] = $value; } } if (empty($fields)) { sendResponse(['success' => false, 'message' => 'No valid fields to update'], 400); return; } $values[] = $id; try { $stmt = $pdo->prepare("UPDATE traffic_lights SET " . implode(', ', $fields) . " WHERE id = ?"); $stmt->execute($values); if ($stmt->rowCount() === 0) { sendResponse(['success' => false, 'message' => 'Traffic light not found or no changes made'], 404); return; } $stmt = $pdo->prepare("SELECT * FROM traffic_lights WHERE id = ?"); $stmt->execute([$id]); $light = $stmt->fetch(PDO::FETCH_ASSOC); $light['status'] = calculateLightStatus($light); sendResponse(['success' => true, 'message' => 'Traffic light updated successfully', 'light' => $light]); } catch (PDOException $e) { sendResponse(['success' => false, 'message' => 'Failed to update traffic light'], 500); } } // Delete traffic light function deleteLight($pdo, $id, $user_id = null) { // Verify ownership if user is authenticated if ($user_id) { $stmt = $pdo->prepare("SELECT user_id FROM traffic_lights WHERE id = ?"); $stmt->execute([$id]); $light = $stmt->fetch(PDO::FETCH_ASSOC); if (!$light) { sendResponse(['success' => false, 'message' => 'Traffic light not found'], 404); return; } if ($light['user_id'] != $user_id) { // Allow admins to delete any light (would need a proper admin check here) $stmt = $pdo->prepare("SELECT role FROM users WHERE id = ?"); $stmt->execute([$user_id]); $user = $stmt->fetch(PDO::FETCH_ASSOC); if (!$user || $user['role'] !== 'admin') { sendResponse(['success' => false, 'message' => 'You do not have permission to delete this traffic light'], 403); return; } } } try { $stmt = $pdo->prepare("DELETE FROM traffic_lights WHERE id = ?"); $stmt->execute([$id]); if ($stmt->rowCount() === 0) { sendResponse(['success' => false, 'message' => 'Traffic light not found'], 404); return; } sendResponse(['success' => true, 'message' => 'Traffic light deleted successfully']); } catch (PDOException $e) { sendResponse(['success' => false, 'message' => 'Failed to delete traffic light'], 500); } } // Update traffic light timing function updateLightTiming($pdo, $user_id = null, $light_id = null) { $json = file_get_contents('php://input'); $data = json_decode($json, true); if (!$data) { sendResponse(['success' => false, 'message' => 'Invalid data format'], 400); return; } // Use either ID from endpoint or from request body $id = $light_id ?? $data['id'] ?? null; if (!$id || !isset($data['duration_type']) || !isset($data['duration'])) { sendResponse(['success' => false, 'message' => 'ID, duration_type and duration are required'], 400); return; } if ($data['duration_type'] !== 'red' && $data['duration_type'] !== 'green') { sendResponse(['success' => false, 'message' => 'duration_type must be either red or green'], 400); return; } try { // First get the current light data $stmt = $pdo->prepare("SELECT * FROM traffic_lights WHERE id = ?"); $stmt->execute([$id]); $light = $stmt->fetch(PDO::FETCH_ASSOC); if (!$light) { sendResponse(['success' => false, 'message' => 'Traffic light not found'], 404); return; } // Update the appropriate duration field $field = $data['duration_type'] . '_duration'; $stmt = $pdo->prepare("UPDATE traffic_lights SET $field = ? WHERE id = ?"); $stmt->execute([$data['duration'], $id]); if ($stmt->rowCount() === 0) { sendResponse(['success' => false, 'message' => 'No changes made'], 304); return; } // Record the measurement in the history table if we have a user ID if ($user_id) { $stmt = $pdo->prepare(" INSERT INTO traffic_light_measurements (traffic_light_id, user_id, duration_type, duration, measured_at) VALUES (?, ?, ?, ?, NOW()) "); $stmt->execute([$id, $user_id, $data['duration_type'], $data['duration']]); } // Get the updated light data $stmt = $pdo->prepare("SELECT * FROM traffic_lights WHERE id = ?"); $stmt->execute([$id]); $updatedLight = $stmt->fetch(PDO::FETCH_ASSOC); $updatedLight['status'] = calculateLightStatus($updatedLight); sendResponse(['success' => true, 'message' => 'Timing updated successfully', 'light' => $updatedLight]); } catch (PDOException $e) { sendResponse(['success' => false, 'message' => 'Failed to update timing'], 500); } } // Calculate the current status of a traffic light function calculateLightStatus($light) { $totalCycle = (int)$light['red_duration'] + (int)$light['green_duration']; $currentTime = time(); $timeInCycle = $currentTime % $totalCycle; if ($timeInCycle < $light['red_duration']) { return [ 'isRed' => true, 'color' => 'red', 'label' => 'RED', 'timeLeft' => $light['red_duration'] - $timeInCycle, 'nextState' => 'GREEN', 'cyclePosition' => ($timeInCycle / $totalCycle) * 100 ]; } else { return [ 'isRed' => false, 'color' => 'green', 'label' => 'GREEN', 'timeLeft' => $totalCycle - $timeInCycle, 'nextState' => 'RED', 'cyclePosition' => ($timeInCycle / $totalCycle) * 100 ]; } } // Generate JWT token function generateJWTToken($payload) { $header = json_encode(['typ' => 'JWT', 'alg' => 'HS256']); $payload['iat'] = time(); // Issued at $payload['exp'] = time() + (30 * 24 * 60 * 60); // Expires in 30 days $payload = json_encode($payload); $base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header)); $base64UrlPayload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload)); $signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, getJWTSecret(), true); $base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature)); return $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature; } // Verify JWT token function verifyJWTToken($token) { $tokenParts = explode('.', $token); if (count($tokenParts) != 3) { return null; } list($base64UrlHeader, $base64UrlPayload, $base64UrlSignature) = $tokenParts; $signature = base64_decode(str_replace(['-', '_'], ['+', '/'], $base64UrlSignature)); $rawSignature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, getJWTSecret(), true); if (!hash_equals($signature, $rawSignature)) { return null; } $payload = json_decode(base64_decode(str_replace(['-', '_'], ['+', '/'], $base64UrlPayload)), true); // Check if token is expired if (isset($payload->exp) && $payload->exp < time()) { return null; } return (array) $payload; } // Get JWT secret key function getJWTSecret() { // Ideally, this should be stored in an environment variable return 'imators_traffic_light_secret_key_346980'; } // Send JSON response with appropriate HTTP status code function sendResponse($data, $statusCode = 200) { http_response_code($statusCode); echo json_encode($data); exit; } // Create database tables if they don't exist function createTables($pdo) { try { // Traffic lights table $pdo->exec(" CREATE TABLE IF NOT EXISTS traffic_lights ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, latitude DECIMAL(10, 8) NOT NULL, longitude DECIMAL(11, 8) NOT NULL, direction ENUM('north', 'east', 'south', 'west') NOT NULL, red_duration INT NOT NULL, green_duration INT NOT NULL, user_id INT NULL, status ENUM('pending', 'approved', 'rejected') DEFAULT 'pending', created_at DATETIME NOT NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX (user_id), INDEX (status) ) "); // Users table $pdo->exec(" CREATE TABLE IF NOT EXISTS users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, role ENUM('user', 'admin') DEFAULT 'user', created_at DATETIME NOT NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX (email), INDEX (role) ) "); // API keys table $pdo->exec(" CREATE TABLE IF NOT EXISTS api_keys ( id INT AUTO_INCREMENT PRIMARY KEY, api_key VARCHAR(64) NOT NULL UNIQUE, description VARCHAR(255) NULL, active TINYINT(1) DEFAULT 1, created_at DATETIME NOT NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX (api_key), INDEX (active) ) "); // Traffic light measurements history $pdo->exec(" CREATE TABLE IF NOT EXISTS traffic_light_measurements ( id INT AUTO_INCREMENT PRIMARY KEY, traffic_light_id INT NOT NULL, user_id INT NULL, duration_type ENUM('red', 'green') NOT NULL, duration INT NOT NULL, measured_at DATETIME NOT NULL, INDEX (traffic_light_id), INDEX (user_id) ) "); return true; } catch (PDOException $e) { return false; } } // Create tables on first run createTables($pdo);
| ver. 1.6 |
Github
|
.
| PHP 8.1.33 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка