setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $db->exec("CREATE TABLE IF NOT EXISTS videos (id TEXT PRIMARY KEY, title TEXT, url TEXT, cover TEXT, tags TEXT, author TEXT, likes INTEGER DEFAULT 0, created_at INTEGER)"); $db->exec("CREATE TABLE IF NOT EXISTS comments (id TEXT PRIMARY KEY, video_id TEXT, text TEXT, author TEXT, is_featured INTEGER DEFAULT 0, created_at INTEGER)"); $db->exec("CREATE TABLE IF NOT EXISTS announcements (id TEXT PRIMARY KEY, text TEXT, active INTEGER DEFAULT 1, updated_at INTEGER)"); $db->exec("CREATE TABLE IF NOT EXISTS users (username TEXT PRIMARY KEY, avatar TEXT)"); } catch (Exception $e) { die(json_encode(['success' => false, 'error' => "數據庫連接失敗: " . $e->getMessage()])); } $admins = [ 'NekoBenson' => 'ddm20110116', 'Pony9163' => '12345678' ]; function getUploadErrorMessage($code) { switch ($code) { case UPLOAD_ERR_INI_SIZE: return "文件太大!超過了伺服器 php.ini 中 upload_max_filesize (" . ini_get('upload_max_filesize') . ") 的限制。請修改 php.ini。"; case UPLOAD_ERR_FORM_SIZE: return "文件超過了 HTML 表單定義的限制。"; case UPLOAD_ERR_PARTIAL: return "網路中斷,文件只有部分被上傳。"; case UPLOAD_ERR_NO_FILE: return "沒有接收到任何文件數據。"; case UPLOAD_ERR_NO_TMP_DIR: return "伺服器錯誤:找不到臨時文件夾 (tmp_dir)。"; case UPLOAD_ERR_CANT_WRITE: return "伺服器錯誤:磁碟寫入失敗,請檢查 uploads 文件夾及其父目錄的 0777 權限。"; case UPLOAD_ERR_EXTENSION: return "伺服器錯誤:文件上傳被某個 PHP 擴展攔截。"; default: return "未知上傳錯誤 (錯誤碼: $code)。"; } } function getAllData($db) { $videos = $db->query("SELECT * FROM videos ORDER BY created_at DESC")->fetchAll(PDO::FETCH_ASSOC); foreach($videos as &$v) { $v['tags'] = json_decode($v['tags'] ?: '[]'); } $comments = $db->query("SELECT * FROM comments ORDER BY created_at DESC")->fetchAll(PDO::FETCH_ASSOC); foreach($comments as &$c) { $c['isFeatured'] = (bool)$c['is_featured']; $c['videoId'] = $c['video_id']; } $announcements = $db->query("SELECT * FROM announcements")->fetchAll(PDO::FETCH_ASSOC); foreach($announcements as &$a) { $a['active'] = (bool)$a['active']; } $users = $db->query("SELECT username, avatar FROM users")->fetchAll(PDO::FETCH_ASSOC); $userMap = []; foreach($users as $u) { $userMap[$u['username']] = $u['avatar']; } return [ 'videos' => $videos, 'comments' => $comments, 'announcements' => $announcements, 'userMap' => $userMap, 'server_info' => [ 'upload_max' => ini_get('upload_max_filesize'), 'post_max' => ini_get('post_max_size'), 'php_version' => phpversion() ] ]; } if (isset($_GET['api'])) { header('Content-Type: application/json'); $action = $_GET['api']; // 【極限診斷】檢查是否因為影片體積過大導致 PHP 丟棄了所有 POST 數據 if ($_SERVER['REQUEST_METHOD'] === 'POST' && empty($_POST) && empty($_FILES) && $_SERVER['CONTENT_LENGTH'] > 0) { echo json_encode(['success' => false, 'error' => '您上傳的影片體積超出了伺服器極限!PHP 丟棄了所有數據。請修改 php.ini 中的 post_max_size (當前為 ' . ini_get('post_max_size') . ')']); exit; } $input = json_decode(file_get_contents('php://input'), true) ?: $_POST; $response = ['success' => false]; $currentUser = $_SESSION['user'] ?? null; try { if ($action === 'init') { $response = ['success' => true, 'is_php_running' => true, 'user' => $currentUser] + getAllData($db); } elseif ($action === 'login') { $username = $input['username'] ?? ''; $password = $input['password'] ?? ''; $isAdmin = (isset($admins[$username]) && $admins[$username] === $password); if (isset($admins[$username]) && $admins[$username] !== $password) throw new Exception("管理員密碼錯誤"); $stmt = $db->prepare("SELECT avatar FROM users WHERE username = ?"); $stmt->execute([$username]); $userRow = $stmt->fetch(PDO::FETCH_ASSOC); $avatar = $userRow ? $userRow['avatar'] : null; $_SESSION['user'] = ['username' => $username, 'isAdmin' => $isAdmin, 'avatar' => $avatar]; $response = ['success' => true, 'user' => $_SESSION['user']] + getAllData($db); } elseif ($action === 'logout') { session_destroy(); $response = ['success' => true]; } elseif ($action === 'update_avatar' && $currentUser) { if (isset($_FILES['avatarFile'])) { if ($_FILES['avatarFile']['error'] !== UPLOAD_ERR_OK) { throw new Exception("頭像上傳失敗: " . getUploadErrorMessage($_FILES['avatarFile']['error'])); } $ext = pathinfo($_FILES['avatarFile']['name'], PATHINFO_EXTENSION); $filename = 'usr_' . time() . '_' . rand(1000, 9999) . '.' . $ext; $target = $upload_dir . '/avatars/' . $filename; if (move_uploaded_file($_FILES['avatarFile']['tmp_name'], $target)) { $avatarUrl = 'uploads/avatars/' . $filename; $db->prepare("INSERT OR REPLACE INTO users (username, avatar) VALUES (?, ?)")->execute([$currentUser['username'], $avatarUrl]); $_SESSION['user']['avatar'] = $avatarUrl; $response = ['success' => true, 'user' => $_SESSION['user']] + getAllData($db); } else { throw new Exception("伺服器無法保存頭像,權限不足 (move_uploaded_file 失敗)。"); } } else { throw new Exception("未收到頭像數據。"); } } elseif ($action === 'add_video' && $currentUser) { $mediaUrl = ''; $coverUrl = ''; // 檢查伺服器是否開啟了文件上傳功能 if (!ini_get('file_uploads')) { throw new Exception("嚴重錯誤:伺服器 php.ini 中關閉了 file_uploads,完全禁止上傳文件!請聯絡伺服器管理員。"); } // 檢查 $_FILES 陣列是否包含 mediaFile if (!isset($_FILES['mediaFile'])) { $diag = "後端完全沒有收到 mediaFile 數據!\n"; $diag .= "可能是由於:\n1. 文件過大被伺服器直接丟棄 (檢查 php.ini 的 upload_max_filesize)\n"; $diag .= "2. 網路中斷\n"; $diag .= "當前 PHP 限制: post_max_size = " . ini_get('post_max_size') . ", upload_max_filesize = " . ini_get('upload_max_filesize'); throw new Exception($diag); } // 檢查 PHP 上傳錯誤碼 if ($_FILES['mediaFile']['error'] !== UPLOAD_ERR_OK) { throw new Exception("上傳被 PHP 引擎中斷: \n" . getUploadErrorMessage($_FILES['mediaFile']['error'])); } $ext = strtolower(pathinfo($_FILES['mediaFile']['name'], PATHINFO_EXTENSION)); $filename = 'med_' . time() . '_' . rand(1000, 9999) . '.' . $ext; $dest = $upload_dir . '/media/' . $filename; // 執行文件移動,如果失敗給出詳細環境資訊 if (move_uploaded_file($_FILES['mediaFile']['tmp_name'], $dest)) { $mediaUrl = 'uploads/media/' . $filename; } else { $err = "核心錯誤:move_uploaded_file() 失敗,文件無法從臨時區移動到正式目錄!\n"; $err .= "1. 檢查目錄: " . $upload_dir . "/media\n"; $err .= "2. 目錄存在嗎?" . (is_dir($upload_dir . '/media') ? "是" : "否") . "\n"; $err .= "3. 目錄可寫嗎?" . (is_writable($upload_dir . '/media') ? "是" : "否 (請設置 chmod 0777)") . "\n"; throw new Exception($err); } if (isset($_FILES['coverFile']) && $_FILES['coverFile']['error'] === UPLOAD_ERR_OK) { $ext = strtolower(pathinfo($_FILES['coverFile']['name'], PATHINFO_EXTENSION)); $filename = 'cov_' . time() . '_' . rand(1000, 9999) . '.' . $ext; if (move_uploaded_file($_FILES['coverFile']['tmp_name'], $upload_dir . '/covers/' . $filename)) { $coverUrl = 'uploads/covers/' . $filename; } } $tags = isset($input['tags']) ? json_decode($input['tags'], true) : []; $stmt = $db->prepare("INSERT INTO videos (id, title, url, cover, tags, author, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)"); $id = 'vid_' . time() . rand(100,999); $stmt->execute([$id, $input['title'] ?: '無題', $mediaUrl, $coverUrl, json_encode($tags), $currentUser['username'], time() * 1000]); $response = ['success' => true] + getAllData($db); } elseif ($action === 'delete_video' && $currentUser) { $stmt = $db->prepare("SELECT author, url, cover FROM videos WHERE id = ?"); $stmt->execute([$input['id']]); $video = $stmt->fetch(PDO::FETCH_ASSOC); if (!$video) { throw new Exception("找不到該內容或已被刪除。"); } if ($currentUser['isAdmin'] || $video['author'] === $currentUser['username']) { $db->prepare("DELETE FROM videos WHERE id = ?")->execute([$input['id']]); // 同時清理評論 $db->prepare("DELETE FROM comments WHERE video_id = ?")->execute([$input['id']]); // 物理刪除 if ($video['url'] && strpos($video['url'], 'uploads/') === 0) @unlink(__DIR__ . '/' . $video['url']); if ($video['cover'] && strpos($video['cover'], 'uploads/') === 0) @unlink(__DIR__ . '/' . $video['cover']); $response = ['success' => true] + getAllData($db); } else { throw new Exception("權限不足,您只能刪除自己的作品!"); } } elseif ($action === 'like_video') { $db->prepare("UPDATE videos SET likes = likes + 1 WHERE id = ?")->execute([$input['id']]); $response = ['success' => true] + getAllData($db); } elseif ($action === 'add_comment' && $currentUser) { $stmt = $db->prepare("INSERT INTO comments (id, video_id, text, author, created_at) VALUES (?, ?, ?, ?, ?)"); $id = 'cmt_' . time() . rand(100,999); $stmt->execute([$id, $input['videoId'], $input['text'], $currentUser['username'], time() * 1000]); $response = ['success' => true] + getAllData($db); } } catch (Exception $e) { $response['error'] = $e->getMessage(); } echo json_encode($response); exit; } ?> PonyHub - 圖文影音分享平台