-
Notifications
You must be signed in to change notification settings - Fork 0
Add full AVSTube PHP/MySQL video sharing site with admin, popup ads, and SQL schema #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,28 +1,62 @@ | ||
| # Decrypt | ||
| ### [+] Created By HTR-TECH (@***tahmid.rayat***) | ||
| ### [+] Disclaimer : | ||
| ***Decrypter is a tool to decrypt Encrypted Bash Scripts into a Readable Format.This Tool is created for Educational Purpose only.I am not responsible for any misuse of this tool.*** | ||
|
|
||
| <img src="https://raw.githubusercontent.com/htr-tech/release-download/master/images/decrypter.png" alt="" border="0" /> | ||
|
|
||
| ### [+] Installation | ||
| ```apt update``` | ||
|
|
||
| ```apt install git python2 -y``` | ||
|
|
||
| ```git clone https://github.com/hax0rtahm1d/decrypt``` | ||
|
|
||
| ```cd decrypt``` | ||
|
|
||
| ```python2 dec.py``` | ||
|
|
||
| ### Or, Use Single Command | ||
|
|
||
| ``` | ||
| apt update && apt install git python2 -y && git clone https://github.com/hax0rtahm1d/decrypt && cd decrypt && python2 dec.py | ||
| # AVSTube | ||
|
|
||
| Website chia sẻ video dùng **PHP 8 + MySQL + Bootstrap 5**. | ||
|
|
||
| ## 1) Cấu trúc thư mục | ||
|
|
||
| ```text | ||
| /project (repo root) | ||
| ├── admin/ | ||
| │ ├── index.php | ||
| │ ├── login.php | ||
| │ ├── logout.php | ||
| │ ├── dashboard.php | ||
| │ ├── videos.php | ||
| │ ├── ads.php | ||
| │ ├── popup.php | ||
| │ ├── announcements.php | ||
| │ ├── settings.php | ||
| │ ├── auth.php | ||
| │ └── partials.php | ||
| ├── assets/ | ||
| │ ├── css/style.css | ||
| │ └── js/main.js | ||
| ├── uploads/ | ||
| │ ├── thumbnails/ | ||
| │ └── videos/ | ||
| ├── sql/avstube.sql | ||
| ├── config.php | ||
| ├── index.php | ||
| ├── video.php | ||
| ├── search.php | ||
| ├── comment.php | ||
| └── README.md | ||
| ``` | ||
|
|
||
| ## [+] Find Me on : | ||
| [](https://github.com/htr-tech) | ||
| [](https://www.instagram.com/tahmid.rayat) | ||
| [](https://m.me/tahmid.rayat.official) | ||
| ## 2) Cài đặt | ||
|
|
||
| 1. Tạo virtual host trỏ vào thư mục project. | ||
| 2. Tạo database và import file SQL: | ||
| ```bash | ||
| mysql -u root -p < sql/avstube.sql | ||
| ``` | ||
| 3. Cập nhật DB trong `config.php` nếu khác mặc định. | ||
| 4. Cấp quyền ghi cho `uploads/`. | ||
| 5. Truy cập website tại `/index.php`. | ||
|
|
||
| ## 3) Tài khoản admin mặc định | ||
|
|
||
| - Username: `admin` | ||
| - Password: `admin2006` | ||
| - URL: `/admin/login.php` | ||
|
|
||
| ## 4) Tính năng chính | ||
|
|
||
| - Giao diện dark theo mẫu HTML yêu cầu (Bootstrap + Font Awesome). | ||
| - Trang chủ grid video, hover scale, responsive. | ||
| - Xem video hỗ trợ `embed` và `mp4`. | ||
| - Bình luận theo video. | ||
| - Tìm kiếm video theo tiêu đề. | ||
| - Quản trị đầy đủ: dashboard/chart, video CRUD, ads, popup ads, announcements, settings. | ||
| - Popup quảng cáo full màn hình, redirect khi click/X, ẩn vĩnh viễn bằng `localStorage`. | ||
| - Prepared statements + validate input cơ bản. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,37 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <?php | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| require_once __DIR__ . '/auth.php'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| require_once __DIR__ . '/partials.php'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isset($_GET['toggle'])) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $id = (int) $_GET['toggle']; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $pdo->prepare('UPDATE ads SET status = IF(status=1,0,1) WHERE id=:id')->execute(['id' => $id]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| header('Location: ads.php'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (isset($_GET['delete'])) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $pdo->prepare('DELETE FROM ads WHERE id=:id')->execute(['id' => (int) $_GET['delete']]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| header('Location: ads.php'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ($_SERVER['REQUEST_METHOD'] === 'POST') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $type = in_array($_POST['type'] ?? '', ['banner', 'google'], true) ? $_POST['type'] : 'banner'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $image = trim($_POST['image'] ?? ''); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $link = trim($_POST['link'] ?? ''); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $pdo->prepare('INSERT INTO ads (type,image,link,status) VALUES (:type,:image,:link,1)')->execute(compact('type', 'image', 'link')); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| header('Location: ads.php'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| exit; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+16
to
+23
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing CSRF protection and URL validation on ad creation.
🔒 Proposed fix if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== ($_SESSION['csrf_token'] ?? '')) {
+ http_response_code(403);
+ exit('Invalid CSRF token');
+ }
$type = in_array($_POST['type'] ?? '', ['banner', 'google'], true) ? $_POST['type'] : 'banner';
$image = trim($_POST['image'] ?? '');
$link = trim($_POST['link'] ?? '');
+
+ // Validate URLs for banner type
+ if ($type === 'banner') {
+ if (!filter_var($image, FILTER_VALIDATE_URL) || !preg_match('/^https?:\/\//', $image)) {
+ $image = '';
+ }
+ if ($link && (!filter_var($link, FILTER_VALIDATE_URL) || !preg_match('/^https?:\/\//', $link))) {
+ $link = '';
+ }
+ }
+
$pdo->prepare('INSERT INTO ads (type,image,link,status) VALUES (:type,:image,:link,1)')->execute(compact('type', 'image', 'link'));📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| $ads = $pdo->query("SELECT * FROM ads WHERE type IN ('banner','google') ORDER BY id DESC")->fetchAll(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| adminHeader('Ads'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ?> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <h2>Ads</h2> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div class="card card-dark p-3 mb-4"><form method="post" class="row g-2"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div class="col-md-3"><select name="type" class="form-select"><option value="banner">Banner</option><option value="google">Google Code</option></select></div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div class="col-md-4"><input name="image" class="form-control" placeholder="Image URL hoặc mã Google Ads"></div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div class="col-md-4"><input name="link" class="form-control" placeholder="Link redirect (banner)"></div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div class="col-md-1"><button class="btn btn-danger w-100">Add</button></div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </form></div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <table class="table table-dark table-striped"><thead><tr><th>ID</th><th>Type</th><th>Status</th><th>Action</th></tr></thead><tbody> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <?php foreach ($ads as $ad): ?><tr><td><?= (int) $ad['id'] ?></td><td><?= e($ad['type']) ?></td><td><?= $ad['status'] ? 'ON' : 'OFF' ?></td><td><a href="ads.php?toggle=<?= (int) $ad['id'] ?>" class="btn btn-sm btn-warning">Toggle</a> <a href="ads.php?delete=<?= (int) $ad['id'] ?>" class="btn btn-sm btn-danger">Delete</a></td></tr><?php endforeach; ?> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </tbody></table> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <?php adminFooter(); ?> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,25 @@ | ||||||||||||||||||||||||||||||
| <?php | ||||||||||||||||||||||||||||||
| require_once __DIR__ . '/auth.php'; | ||||||||||||||||||||||||||||||
| require_once __DIR__ . '/partials.php'; | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| if (isset($_GET['delete'])) { | ||||||||||||||||||||||||||||||
| $pdo->prepare('DELETE FROM announcements WHERE id=:id')->execute(['id' => (int) $_GET['delete']]); | ||||||||||||||||||||||||||||||
| header('Location: announcements.php'); | ||||||||||||||||||||||||||||||
| exit; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
Comment on lines
+5
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CSRF vulnerability: state-changing DELETE via GET request. The delete action is triggered via a GET request, which is vulnerable to CSRF attacks (e.g., an attacker can embed 🔒 Proposed fix: use POST with CSRF token-if (isset($_GET['delete'])) {
- $pdo->prepare('DELETE FROM announcements WHERE id=:id')->execute(['id' => (int) $_GET['delete']]);
- header('Location: announcements.php');
- exit;
-}
+if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete'])) {
+ if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== ($_SESSION['csrf_token'] ?? '')) {
+ http_response_code(403);
+ exit('Invalid CSRF token');
+ }
+ $pdo->prepare('DELETE FROM announcements WHERE id=:id')->execute(['id' => (int) $_POST['delete']]);
+ header('Location: announcements.php');
+ exit;
+}Then update the delete button in the table to use a form: <form method="post" style="display:inline">
<input type="hidden" name="csrf_token" value="<?= e($_SESSION['csrf_token'] ?? '') ?>">
<input type="hidden" name="delete" value="<?= (int)$r['id'] ?>">
<button class="btn btn-sm btn-danger">Delete</button>
</form>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| if ($_SERVER['REQUEST_METHOD'] === 'POST') { | ||||||||||||||||||||||||||||||
| $title = trim($_POST['title'] ?? ''); | ||||||||||||||||||||||||||||||
| $content = trim($_POST['content'] ?? ''); | ||||||||||||||||||||||||||||||
| if ($title !== '' && $content !== '') { | ||||||||||||||||||||||||||||||
| $pdo->prepare('INSERT INTO announcements (title, content) VALUES (:title, :content)')->execute(compact('title', 'content')); | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
| header('Location: announcements.php'); | ||||||||||||||||||||||||||||||
| exit; | ||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||
|
Comment on lines
+10
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing CSRF protection on POST form submission. The announcement creation form lacks CSRF token validation, making it vulnerable to cross-site request forgery attacks. 🔒 Proposed fix: add CSRF token validation if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== ($_SESSION['csrf_token'] ?? '')) {
+ http_response_code(403);
+ exit('Invalid CSRF token');
+ }
$title = trim($_POST['title'] ?? '');
$content = trim($_POST['content'] ?? '');And add the token field to the form in Line 23: <input type="hidden" name="csrf_token" value="<?= e($_SESSION['csrf_token'] ?? '') ?>">🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| $rows = $pdo->query('SELECT * FROM announcements ORDER BY id DESC')->fetchAll(); | ||||||||||||||||||||||||||||||
| adminHeader('Announcements'); | ||||||||||||||||||||||||||||||
| ?> | ||||||||||||||||||||||||||||||
| <h2>Announcements</h2> | ||||||||||||||||||||||||||||||
| <div class="card card-dark p-3 mb-3"><form method="post" class="row g-2"><div class="col-md-4"><input class="form-control" name="title" required placeholder="Title"></div><div class="col-md-6"><input class="form-control" name="content" required placeholder="Content"></div><div class="col-md-2"><button class="btn btn-danger w-100">Add</button></div></form></div> | ||||||||||||||||||||||||||||||
| <table class="table table-dark"><thead><tr><th>ID</th><th>Title</th><th>Content</th><th></th></tr></thead><tbody><?php foreach($rows as $r): ?><tr><td><?= (int)$r['id'] ?></td><td><?= e($r['title']) ?></td><td><?= e($r['content']) ?></td><td><a class="btn btn-sm btn-danger" href="announcements.php?delete=<?= (int)$r['id'] ?>">Delete</a></td></tr><?php endforeach; ?></tbody></table> | ||||||||||||||||||||||||||||||
| <?php adminFooter(); ?> | ||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| <?php | ||
| require_once __DIR__ . '/../config.php'; | ||
| adminOnly(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| <?php | ||
| require_once __DIR__ . '/auth.php'; | ||
| require_once __DIR__ . '/partials.php'; | ||
|
|
||
| $totalVideos = (int) $pdo->query('SELECT COUNT(*) FROM videos')->fetchColumn(); | ||
| $totalViews = (int) $pdo->query('SELECT COALESCE(SUM(views),0) FROM videos')->fetchColumn(); | ||
| $todayViews = (int) $pdo->query('SELECT COALESCE(SUM(daily_views),0) FROM video_stats WHERE view_date = CURDATE()')->fetchColumn(); | ||
| $topVideo = $pdo->query('SELECT title, views FROM videos ORDER BY views DESC LIMIT 1')->fetch(); | ||
|
|
||
| $chartRows = $pdo->query('SELECT view_date, COALESCE(SUM(daily_views),0) AS views FROM video_stats GROUP BY view_date ORDER BY view_date DESC LIMIT 7')->fetchAll(); | ||
| $labels = []; | ||
| $values = []; | ||
| foreach (array_reverse($chartRows) as $row) { | ||
| $labels[] = $row['view_date']; | ||
| $values[] = (int) $row['views']; | ||
| } | ||
|
|
||
| adminHeader('Dashboard'); | ||
| ?> | ||
| <h2>Dashboard</h2> | ||
| <div class="row g-3 mb-4"> | ||
| <div class="col-md-3"><div class="card card-dark p-3"><small>Total Video</small><h3><?= $totalVideos ?></h3></div></div> | ||
| <div class="col-md-3"><div class="card card-dark p-3"><small>Total Views</small><h3><?= number_format($totalViews) ?></h3></div></div> | ||
| <div class="col-md-3"><div class="card card-dark p-3"><small>Views Today</small><h3><?= number_format($todayViews) ?></h3></div></div> | ||
| <div class="col-md-3"><div class="card card-dark p-3"><small>Top Video</small><h6 class="mb-0"><?= e($topVideo['title'] ?? 'N/A') ?></h6></div></div> | ||
| </div> | ||
| <div class="card card-dark p-3"> | ||
| <h5>Thống kê theo ngày</h5> | ||
| <canvas id="viewsChart" height="100"></canvas> | ||
| </div> | ||
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | ||
| <script> | ||
| new Chart(document.getElementById('viewsChart'), { | ||
| type: 'line', | ||
| data: { labels: <?= json_encode($labels) ?>, datasets: [{label: 'Views', data: <?= json_encode($values) ?>, borderColor: '#c00', tension: 0.4}]}, | ||
| options: { plugins: {legend: {labels:{color:'#ddd'}}}, scales: {x:{ticks:{color:'#aaa'}}, y:{ticks:{color:'#aaa'}}}} | ||
| }); | ||
| </script> | ||
| <?php adminFooter(); ?> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| <?php | ||
| require_once __DIR__ . '/../config.php'; | ||
| header('Location: ' . (isAdmin() ? 'dashboard.php' : 'login.php')); | ||
| exit; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| <?php | ||
| require_once __DIR__ . '/../config.php'; | ||
|
|
||
| if (isAdmin()) { | ||
| header('Location: dashboard.php'); | ||
| exit; | ||
| } | ||
|
|
||
| $error = ''; | ||
| if ($_SERVER['REQUEST_METHOD'] === 'POST') { | ||
| $username = trim($_POST['username'] ?? ''); | ||
| $password = trim($_POST['password'] ?? ''); | ||
|
|
||
| $stmt = $pdo->prepare('SELECT * FROM users WHERE username = :username LIMIT 1'); | ||
| $stmt->execute(['username' => $username]); | ||
| $user = $stmt->fetch(); | ||
|
|
||
| if ($user && password_verify($password, $user['password']) && $user['role'] === 'admin') { | ||
| $_SESSION['admin_id'] = $user['id']; | ||
| $_SESSION['admin_username'] = $user['username']; | ||
| header('Location: dashboard.php'); | ||
| exit; | ||
| } | ||
| $error = 'Sai thông tin đăng nhập'; | ||
| } | ||
| ?> | ||
| <!DOCTYPE html> | ||
| <html lang="vi"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Admin Login</title> | ||
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> | ||
| <style>body{background:#000;color:#ddd;display:flex;align-items:center;justify-content:center;min-height:100vh}.box{width:100%;max-width:420px;background:#111;border:1px solid #222;border-radius:12px;padding:24px}</style> | ||
| </head><body> | ||
| <div class="box"><h3 class="text-center text-danger mb-3">AVSTube Admin</h3> | ||
| <?php if ($error): ?><div class="alert alert-danger"><?= e($error) ?></div><?php endif; ?> | ||
| <form method="post"><input class="form-control bg-dark text-light border-secondary mb-2" name="username" required placeholder="Username"> | ||
| <input type="password" class="form-control bg-dark text-light border-secondary mb-3" name="password" required placeholder="Password"> | ||
| <button class="btn btn-danger w-100">Đăng nhập</button></form></div> | ||
| </body></html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| <?php | ||
| require_once __DIR__ . '/../config.php'; | ||
| session_destroy(); | ||
| header('Location: login.php'); | ||
| exit; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| <?php | ||
| function adminHeader(string $title = 'Admin'): void | ||
| { | ||
| echo '<!DOCTYPE html><html lang="vi"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">'; | ||
| echo '<title>' . e($title) . ' - AVSTube Admin</title>'; | ||
| echo '<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">'; | ||
| echo '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">'; | ||
| echo '<style>body{background:#0b0b0b;color:#ddd} .sidebar{background:#111;min-height:100vh;padding:20px} .sidebar a{display:block;color:#bbb;padding:10px;border-radius:8px;margin-bottom:6px} .sidebar a:hover,.sidebar a.active{background:#c00;color:#fff} .card-dark{background:#111;border:1px solid #222} .table{color:#ddd} .form-control,.form-select,textarea{background:#1a1a1a!important;color:#ddd!important;border-color:#333!important}</style>'; | ||
| echo '</head><body><div class="container-fluid"><div class="row">'; | ||
| echo '<aside class="col-md-3 col-lg-2 sidebar">'; | ||
| echo '<h3 class="text-danger">AVSTube</h3>'; | ||
| echo '<a href="dashboard.php"><i class="fa fa-chart-line"></i> Dashboard</a>'; | ||
| echo '<a href="videos.php"><i class="fa fa-video"></i> Videos</a>'; | ||
| echo '<a href="ads.php"><i class="fa fa-bullhorn"></i> Ads</a>'; | ||
| echo '<a href="popup.php"><i class="fa fa-up-right-and-down-left-from-center"></i> Popup Ads</a>'; | ||
| echo '<a href="announcements.php"><i class="fa fa-bell"></i> Announcements</a>'; | ||
| echo '<a href="settings.php"><i class="fa fa-gear"></i> Settings</a>'; | ||
| echo '<a href="logout.php"><i class="fa fa-right-from-bracket"></i> Logout</a>'; | ||
| echo '</aside><main class="col-md-9 col-lg-10 p-4">'; | ||
| } | ||
|
|
||
| function adminFooter(): void | ||
| { | ||
| echo '</main></div></div><script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script></body></html>'; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| <?php | ||
| require_once __DIR__ . '/auth.php'; | ||
| require_once __DIR__ . '/partials.php'; | ||
|
|
||
| if ($_SERVER['REQUEST_METHOD'] === 'POST') { | ||
| $image = trim($_POST['image'] ?? ''); | ||
| $link1 = trim($_POST['link1'] ?? ''); | ||
| $link2 = trim($_POST['link2'] ?? ''); | ||
| $links = implode(',', array_filter([$link1, $link2])); | ||
|
|
||
| $pdo->exec("DELETE FROM ads WHERE type='popup'"); | ||
| $stmt = $pdo->prepare("INSERT INTO ads (type,image,link,status) VALUES ('popup',:image,:link,1)"); | ||
| $stmt->execute(['image' => $image, 'link' => $links]); | ||
| header('Location: popup.php?saved=1'); | ||
| exit; | ||
| } | ||
|
|
||
| $popup = $pdo->query("SELECT * FROM ads WHERE type='popup' ORDER BY id DESC LIMIT 1")->fetch(); | ||
| $links = array_values(array_filter(array_map('trim', explode(',', (string) ($popup['link'] ?? ''))))); | ||
| adminHeader('Popup Ads'); | ||
| ?> | ||
| <h2>Popup Ads</h2> | ||
| <?php if (isset($_GET['saved'])): ?><div class="alert alert-success">Saved</div><?php endif; ?> | ||
| <div class="card card-dark p-3"> | ||
| <form method="post" class="row g-3"> | ||
| <div class="col-md-12"><label>Popup image URL</label><input name="image" class="form-control" required value="<?= e($popup['image'] ?? '') ?>"></div> | ||
| <div class="col-md-6"><label>Link quảng cáo 1</label><input name="link1" class="form-control" required value="<?= e($links[0] ?? '') ?>"></div> | ||
| <div class="col-md-6"><label>Link quảng cáo 2 (optional)</label><input name="link2" class="form-control" value="<?= e($links[1] ?? '') ?>"></div> | ||
| <div class="col-12"><button class="btn btn-danger">Lưu popup</button></div> | ||
| </form> | ||
| </div> | ||
| <?php adminFooter(); ?> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| <?php | ||
| require_once __DIR__ . '/auth.php'; | ||
| require_once __DIR__ . '/partials.php'; | ||
|
|
||
| if ($_SERVER['REQUEST_METHOD'] === 'POST') { | ||
| $logo = trim($_POST['site_logo'] ?? ''); | ||
| $banner = trim($_POST['site_banner'] ?? ''); | ||
|
|
||
| foreach (['site_logo' => $logo, 'site_banner' => $banner] as $key => $value) { | ||
| $stmt = $pdo->prepare('INSERT INTO settings (setting_key, setting_value) VALUES (:k, :v) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)'); | ||
| $stmt->execute(['k' => $key, 'v' => $value]); | ||
| } | ||
| header('Location: settings.php?saved=1'); | ||
| exit; | ||
| } | ||
|
Comment on lines
+5
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing CSRF protection on settings form. Admin forms should include CSRF token validation to prevent cross-site request forgery attacks, especially for state-changing operations. 🤖 Prompt for AI Agents |
||
| adminHeader('Settings'); | ||
| ?> | ||
| <h2>Settings</h2> | ||
| <?php if (isset($_GET['saved'])): ?><div class="alert alert-success">Đã lưu cài đặt</div><?php endif; ?> | ||
| <div class="card card-dark p-3"> | ||
| <form method="post" class="row g-3"> | ||
| <div class="col-md-12"><label>Logo URL</label><input class="form-control" name="site_logo" value="<?= e(setting($pdo, 'site_logo')) ?>"></div> | ||
| <div class="col-md-12"><label>Banner URL</label><input class="form-control" name="site_banner" value="<?= e(setting($pdo, 'site_banner')) ?>"></div> | ||
| <div class="col-md-12"><button class="btn btn-danger">Lưu</button></div> | ||
| </form></div> | ||
| <?php adminFooter(); ?> | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CSRF vulnerability: state-changing operations via GET requests.
Both toggle and delete actions use GET requests without CSRF protection. These can be exploited via image tags, links, or other cross-site vectors to modify ad state without authorization.
Convert these to POST requests with CSRF token validation, similar to the fix suggested for
admin/announcements.php.🤖 Prompt for AI Agents