setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $db->exec('PRAGMA journal_mode = WAL'); $db->exec(" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password TEXT NOT NULL, created_at INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS media ( id INTEGER PRIMARY KEY AUTOINCREMENT, owner_id INTEGER NOT NULL, owner_name TEXT NOT NULL, visibility TEXT NOT NULL CHECK(visibility IN ('public','private')), filename TEXT NOT NULL, original_name TEXT NOT NULL, mime_type TEXT NOT NULL, size INTEGER NOT NULL, created_at INTEGER NOT NULL, FOREIGN KEY (owner_id) REFERENCES users(id) ); CREATE INDEX IF NOT EXISTS idx_owner ON media(owner_id); CREATE INDEX IF NOT EXISTS idx_visibility ON media(visibility); "); // ===== Helpers ===== function json_out($data, int $code = 200): void { http_response_code($code); header('Content-Type: application/json; charset=utf-8'); echo json_encode($data); exit; } function current_user(): ?array { if (empty($_SESSION['user_id'])) return null; return ['id' => (int)$_SESSION['user_id'], 'username' => $_SESSION['username']]; } function auth_required(): array { $u = current_user(); if (!$u) json_out(['error' => 'Belum login'], 401); return $u; } function format_size(int $b): string { if ($b < 1024 * 1024) return number_format($b / 1024, 1) . ' KB'; return number_format($b / (1024 * 1024), 2) . ' MB'; } $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); $method = $_SERVER['REQUEST_METHOD']; // ===== Serve uploaded file (private check) ===== if (preg_match('#^/uploads/([a-f0-9]+\.[A-Za-z0-9]+)$#', $path, $m)) { $file = UPLOAD_DIR . '/' . $m[1]; if (!is_file($file)) { http_response_code(404); exit('Not found'); } $row = $db->prepare('SELECT * FROM media WHERE filename = ?'); $row->execute([$m[1]]); $item = $row->fetch(PDO::FETCH_ASSOC); if (!$item) { http_response_code(404); exit('Not found'); } if ($item['visibility'] === 'private') { $u = current_user(); if (!$u || $u['id'] !== (int)$item['owner_id']) { http_response_code(403); exit('Forbidden'); } } $mime = $item['mime_type'] ?: 'application/octet-stream'; header('Content-Type: ' . $mime); header('Content-Length: ' . filesize($file)); header('Accept-Ranges: bytes'); readfile($file); exit; } // ===== API Routes ===== if (str_starts_with($path, '/api/')) { // --- Auth --- if ($path === '/api/register' && $method === 'POST') { $body = json_decode(file_get_contents('php://input'), true) ?: []; $username = trim($body['username'] ?? ''); $password = $body['password'] ?? ''; if (!$username || !$password) json_out(['error' => 'Username & password wajib'], 400); if (mb_strlen($username) < 3) json_out(['error' => 'Username minimal 3 karakter'], 400); if (strlen($password) < 4) json_out(['error' => 'Password minimal 4 karakter'], 400); try { $stmt = $db->prepare('INSERT INTO users (username, password, created_at) VALUES (?, ?, ?)'); $stmt->execute([$username, password_hash($password, PASSWORD_BCRYPT), time()]); $id = (int)$db->lastInsertId(); $_SESSION['user_id'] = $id; $_SESSION['username'] = $username; json_out(['id' => $id, 'username' => $username]); } catch (PDOException $e) { if (str_contains($e->getMessage(), 'UNIQUE')) json_out(['error' => 'Username sudah dipakai'], 409); json_out(['error' => 'Gagal daftar'], 500); } } if ($path === '/api/login' && $method === 'POST') { $body = json_decode(file_get_contents('php://input'), true) ?: []; $username = trim($body['username'] ?? ''); $password = $body['password'] ?? ''; if (!$username || !$password) json_out(['error' => 'Username & password wajib'], 400); $stmt = $db->prepare('SELECT * FROM users WHERE username = ?'); $stmt->execute([$username]); $user = $stmt->fetch(PDO::FETCH_ASSOC); if (!$user || !password_verify($password, $user['password'])) json_out(['error' => 'Username atau password salah'], 401); $_SESSION['user_id'] = (int)$user['id']; $_SESSION['username'] = $user['username']; json_out(['id' => (int)$user['id'], 'username' => $user['username']]); } if ($path === '/api/logout' && $method === 'POST') { session_destroy(); json_out(['ok' => true]); } if ($path === '/api/me' && $method === 'GET') { $u = auth_required(); json_out($u); } // --- Media upload --- if ($path === '/api/upload' && $method === 'POST') { $u = auth_required(); if (empty($_FILES['files']) || !is_array($_FILES['files']['name'])) { json_out(['error' => 'Tidak ada file'], 400); } $visibility = ($_POST['visibility'] ?? 'public') === 'private' ? 'private' : 'public'; $maxBytes = MAX_SIZE_MB * 1024 * 1024; $finfo = new finfo(FILEINFO_MIME_TYPE); $uploaded = 0; $files = $_FILES['files']; for ($i = 0; $i < count($files['name']); $i++) { if ($files['error'][$i] === UPLOAD_ERR_INI_SIZE || $files['error'][$i] === UPLOAD_ERR_FORM_SIZE) { json_out(['error' => 'File terlalu besar (maks ' . MAX_SIZE_MB . ' MB)'], 413); } if ($files['error'][$i] !== UPLOAD_ERR_OK) continue; if ($files['size'][$i] > $maxBytes) { json_out(['error' => 'File terlalu besar (maks ' . MAX_SIZE_MB . ' MB)'], 413); } $tmp = $files['tmp_name'][$i]; $mime = $finfo->file($tmp) ?: 'application/octet-stream'; if (!str_starts_with($mime, 'image/') && !str_starts_with($mime, 'video/')) { json_out(['error' => 'Hanya file gambar/video yang diizinkan'], 400); } $ext = pathinfo($files['name'][$i], PATHINFO_EXTENSION); $newName = bin2hex(random_bytes(16)) . ($ext ? '.' . strtolower($ext) : ''); $dest = UPLOAD_DIR . '/' . $newName; if (!move_uploaded_file($tmp, $dest)) continue; $stmt = $db->prepare(' INSERT INTO media (owner_id, owner_name, visibility, filename, original_name, mime_type, size, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?) '); $stmt->execute([ $u['id'], $u['username'], $visibility, $newName, $files['name'][$i], $mime, $files['size'][$i], time() ]); $uploaded++; } json_out(['ok' => true, 'uploaded' => $uploaded]); } // --- My media --- if ($path === '/api/my-media' && $method === 'GET') { $u = auth_required(); $stmt = $db->prepare('SELECT id, owner_id, owner_name, visibility, filename, original_name, mime_type, size, created_at FROM media WHERE owner_id = ? ORDER BY created_at DESC'); $stmt->execute([$u['id']]); json_out($stmt->fetchAll(PDO::FETCH_ASSOC)); } // --- Public media --- if ($path === '/api/public-media' && $method === 'GET') { $stmt = $db->query("SELECT id, owner_id, owner_name, visibility, filename, original_name, mime_type, size, created_at FROM media WHERE visibility = 'public' ORDER BY created_at DESC"); json_out($stmt->fetchAll(PDO::FETCH_ASSOC)); } // --- Delete media --- if (preg_match('#^/api/media/(\d+)$#', $path, $m) && $method === 'DELETE') { $u = auth_required(); $id = (int)$m[1]; $stmt = $db->prepare('SELECT * FROM media WHERE id = ?'); $stmt->execute([$id]); $item = $stmt->fetch(PDO::FETCH_ASSOC); if (!$item) json_out(['error' => 'Tidak ditemukan'], 404); if ((int)$item['owner_id'] !== $u['id']) json_out(['error' => 'Bukan milikmu'], 403); @unlink(UPLOAD_DIR . '/' . $item['filename']); $db->prepare('DELETE FROM media WHERE id = ?')->execute([$id]); json_out(['ok' => true]); } json_out(['error' => 'Endpoint tidak ditemukan'], 404); } // ===== Frontend ===== $user = current_user(); ?> Media Hosting

🎬 MEDIA HOSTING

Login

Upload Media Baru

Visibilitas:
0%
📦 Total: 0 item 💾 Ukuran: 0 MB

Media Saya