-
Notifications
You must be signed in to change notification settings - Fork 0
Create simple PHP news site for shared hosting #41
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
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| Options -Indexes | ||
| RewriteEngine On | ||
|
|
||
| RewriteCond %{REQUEST_URI} !^/admin | ||
| RewriteCond %{REQUEST_URI} !^/assets | ||
| RewriteCond %{REQUEST_URI} !^/api | ||
| RewriteCond %{REQUEST_URI} !^/sql | ||
| RewriteCond %{REQUEST_URI} !^/logs | ||
| RewriteCond %{REQUEST_FILENAME} !-f | ||
| RewriteCond %{REQUEST_FILENAME} !-d | ||
| RewriteRule ^([A-Za-z0-9\-]+)/?$ post.php?slug=$1 [L,QSA] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| # Website tin tức đơn giản | ||
|
|
||
| Upload toàn bộ thư mục `public_html` lên hosting (DataOnline.vn), import `sql/database.sql`, cập nhật thông tin DB trong `config.php`. Sau khi import có sẵn tài khoản admin/admin567. | ||
|
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. Default credentials ( The README + Recommendations:
🤖 Prompt for AI Agents |
||
|
|
||
| ## Đường dẫn | ||
| - Trang chủ: `/` | ||
| - Trang bài: `/slug` | ||
| - Đăng nhập: `/admin/login.php` | ||
| - Trang quản trị một trang: `/admin/panel.php` | ||
|
|
||
| ## Lưu ý | ||
| - Bật/tắt quảng cáo, link Shopee, link liên hệ nằm trong tab Cài đặt của panel. | ||
| - Khi quảng cáo bật và người dùng mở trực tiếp `/slug`, overlay sẽ xuất hiện ngay; đóng overlay sẽ mở tab mới `/slug?ad=0` và tab hiện tại chuyển sang link Shopee. | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,40 @@ | ||||||||||||||||||||||||||||||||||||
| <?php | ||||||||||||||||||||||||||||||||||||
| require_once __DIR__ . '/../lib/db.php'; | ||||||||||||||||||||||||||||||||||||
| require_once __DIR__ . '/../lib/auth.php'; | ||||||||||||||||||||||||||||||||||||
| require_once __DIR__ . '/../lib/error_handler.php'; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ensure_session(); | ||||||||||||||||||||||||||||||||||||
| $error = ''; | ||||||||||||||||||||||||||||||||||||
| if ($_SERVER['REQUEST_METHOD'] === 'POST') { | ||||||||||||||||||||||||||||||||||||
| $username = trim($_POST['username'] ?? ''); | ||||||||||||||||||||||||||||||||||||
| $password = $_POST['password'] ?? ''; | ||||||||||||||||||||||||||||||||||||
| if (admin_login($username, $password)) { | ||||||||||||||||||||||||||||||||||||
| header('Location: /admin/panel.php'); | ||||||||||||||||||||||||||||||||||||
| exit; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| $error = 'Sai tài khoản hoặc mật khẩu'; | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+8
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. Regenerate session ID after login; add CSRF to the login form. Successful authentication should call 🔒 Minimal fix for session fixation if (admin_login($username, $password)) {
+ session_regenerate_id(true);
header('Location: /admin/panel.php');
exit;
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| ?><!DOCTYPE html> | ||||||||||||||||||||||||||||||||||||
| <html lang="vi"> | ||||||||||||||||||||||||||||||||||||
| <head> | ||||||||||||||||||||||||||||||||||||
| <meta charset="UTF-8"> | ||||||||||||||||||||||||||||||||||||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||||||||||||||||||||||||||||||||
| <title>Đăng nhập</title> | ||||||||||||||||||||||||||||||||||||
| <link rel="stylesheet" href="/assets/css/admin.css"> | ||||||||||||||||||||||||||||||||||||
| </head> | ||||||||||||||||||||||||||||||||||||
| <body> | ||||||||||||||||||||||||||||||||||||
| <div class="container"> | ||||||||||||||||||||||||||||||||||||
| <div class="card"> | ||||||||||||||||||||||||||||||||||||
| <h2>Đăng nhập admin</h2> | ||||||||||||||||||||||||||||||||||||
| <?php if ($error): ?><p class="alert"><?php echo htmlspecialchars($error, ENT_QUOTES, 'UTF-8'); ?></p><?php endif; ?> | ||||||||||||||||||||||||||||||||||||
| <form method="post" autocomplete="off"> | ||||||||||||||||||||||||||||||||||||
| <label>Tên đăng nhập</label> | ||||||||||||||||||||||||||||||||||||
| <input name="username" required> | ||||||||||||||||||||||||||||||||||||
| <label>Mật khẩu</label> | ||||||||||||||||||||||||||||||||||||
| <input type="password" name="password" required> | ||||||||||||||||||||||||||||||||||||
| <button type="submit">Đăng nhập</button> | ||||||||||||||||||||||||||||||||||||
| </form> | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||
| </body> | ||||||||||||||||||||||||||||||||||||
| </html> | ||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| <?php | ||
| require_once __DIR__ . '/../lib/auth.php'; | ||
| require_once __DIR__ . '/../lib/error_handler.php'; | ||
|
|
||
| admin_logout(); | ||
| header('Location: /admin/login.php'); | ||
|
Comment on lines
+5
to
+6
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. Logout endpoint is vulnerable to CSRF. Any page an admin visits can silently trigger a 🛡️ Proposed fix <?php
require_once __DIR__ . '/../lib/auth.php';
+require_once __DIR__ . '/../lib/csrf.php';
require_once __DIR__ . '/../lib/error_handler.php';
+admin_require();
+csrf_verify(); // verify POST token, or gate on POST method
admin_logout();
header('Location: /admin/login.php');
exit;🤖 Prompt for AI Agents |
||
| exit; | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,181 @@ | ||||||||||||||||||||||||||||||||||||||||||
| <?php | ||||||||||||||||||||||||||||||||||||||||||
| require_once __DIR__ . '/../lib/db.php'; | ||||||||||||||||||||||||||||||||||||||||||
| require_once __DIR__ . '/../lib/auth.php'; | ||||||||||||||||||||||||||||||||||||||||||
| require_once __DIR__ . '/../lib/csrf.php'; | ||||||||||||||||||||||||||||||||||||||||||
| require_once __DIR__ . '/../lib/error_handler.php'; | ||||||||||||||||||||||||||||||||||||||||||
| require_once __DIR__ . '/../lib/slugify.php'; | ||||||||||||||||||||||||||||||||||||||||||
| require_once __DIR__ . '/../lib/track.php'; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| admin_require(); | ||||||||||||||||||||||||||||||||||||||||||
| $pdo = db(); | ||||||||||||||||||||||||||||||||||||||||||
| $message = ''; | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| if ($_SERVER['REQUEST_METHOD'] === 'POST') { | ||||||||||||||||||||||||||||||||||||||||||
| if (!csrf_check($_POST['csrf'] ?? '')) { | ||||||||||||||||||||||||||||||||||||||||||
| die('CSRF không hợp lệ'); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| $action = $_POST['action'] ?? ''; | ||||||||||||||||||||||||||||||||||||||||||
| if ($action === 'create_post') { | ||||||||||||||||||||||||||||||||||||||||||
| $title = trim($_POST['title'] ?? ''); | ||||||||||||||||||||||||||||||||||||||||||
| $slug = trim($_POST['slug'] ?? ''); | ||||||||||||||||||||||||||||||||||||||||||
| $slug = $slug !== '' ? $slug : slugify($title); | ||||||||||||||||||||||||||||||||||||||||||
| $content = trim($_POST['content'] ?? ''); | ||||||||||||||||||||||||||||||||||||||||||
| $images = array_filter(array_map('trim', explode("\n", $_POST['images'] ?? ''))); | ||||||||||||||||||||||||||||||||||||||||||
| $videos = array_filter(array_map('trim', explode("\n", $_POST['videos'] ?? ''))); | ||||||||||||||||||||||||||||||||||||||||||
| $stmt = $pdo->prepare('INSERT INTO posts (title, slug, content, images_json, videos_json, created_at) VALUES (?, ?, ?, ?, ?, NOW())'); | ||||||||||||||||||||||||||||||||||||||||||
| $stmt->execute([$title, $slug, $content, json_encode(array_values($images)), json_encode(array_values($videos))]); | ||||||||||||||||||||||||||||||||||||||||||
| $message = 'Đã thêm bài'; | ||||||||||||||||||||||||||||||||||||||||||
| } elseif ($action === 'update_post') { | ||||||||||||||||||||||||||||||||||||||||||
| $postId = (int)($_POST['post_id'] ?? 0); | ||||||||||||||||||||||||||||||||||||||||||
| $title = trim($_POST['title'] ?? ''); | ||||||||||||||||||||||||||||||||||||||||||
| $slug = trim($_POST['slug'] ?? ''); | ||||||||||||||||||||||||||||||||||||||||||
| $slug = $slug !== '' ? $slug : slugify($title); | ||||||||||||||||||||||||||||||||||||||||||
| $content = trim($_POST['content'] ?? ''); | ||||||||||||||||||||||||||||||||||||||||||
| $images = array_filter(array_map('trim', explode("\n", $_POST['images'] ?? ''))); | ||||||||||||||||||||||||||||||||||||||||||
| $videos = array_filter(array_map('trim', explode("\n", $_POST['videos'] ?? ''))); | ||||||||||||||||||||||||||||||||||||||||||
| $stmt = $pdo->prepare('UPDATE posts SET title=?, slug=?, content=?, images_json=?, videos_json=? WHERE id=?'); | ||||||||||||||||||||||||||||||||||||||||||
| $stmt->execute([$title, $slug, $content, json_encode(array_values($images)), json_encode(array_values($videos)), $postId]); | ||||||||||||||||||||||||||||||||||||||||||
| $message = 'Đã cập nhật'; | ||||||||||||||||||||||||||||||||||||||||||
| } elseif ($action === 'delete_post') { | ||||||||||||||||||||||||||||||||||||||||||
| $postId = (int)($_POST['post_id'] ?? 0); | ||||||||||||||||||||||||||||||||||||||||||
| $stmt = $pdo->prepare('DELETE FROM posts WHERE id = ?'); | ||||||||||||||||||||||||||||||||||||||||||
| $stmt->execute([$postId]); | ||||||||||||||||||||||||||||||||||||||||||
| $message = 'Đã xóa'; | ||||||||||||||||||||||||||||||||||||||||||
| } elseif ($action === 'save_settings') { | ||||||||||||||||||||||||||||||||||||||||||
| $adsEnabled = isset($_POST['ads_enabled']) ? 1 : 0; | ||||||||||||||||||||||||||||||||||||||||||
| $adLink = trim($_POST['ad_link'] ?? ''); | ||||||||||||||||||||||||||||||||||||||||||
| $adTitle = trim($_POST['ad_title'] ?? ''); | ||||||||||||||||||||||||||||||||||||||||||
| $adBody = trim($_POST['ad_body'] ?? ''); | ||||||||||||||||||||||||||||||||||||||||||
| $contactLink = trim($_POST['contact_link'] ?? ''); | ||||||||||||||||||||||||||||||||||||||||||
| $stmt = $pdo->prepare('UPDATE settings SET ads_enabled=?, ad_link=?, ad_title=?, ad_body=?, contact_link=? WHERE id = 1'); | ||||||||||||||||||||||||||||||||||||||||||
| $stmt->execute([$adsEnabled, $adLink, $adTitle, $adBody, $contactLink]); | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+46
to
+52
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. Validate Unvalidated URLs allow ✅ Example validation guard } elseif ($action === 'save_settings') {
$adsEnabled = isset($_POST['ads_enabled']) ? 1 : 0;
$adLink = trim($_POST['ad_link'] ?? '');
$adTitle = trim($_POST['ad_title'] ?? '');
$adBody = trim($_POST['ad_body'] ?? '');
$contactLink = trim($_POST['contact_link'] ?? '');
+ foreach (['adLink' => $adLink, 'contactLink' => $contactLink] as $label => $link) {
+ if ($link !== '' && !preg_match('~^https?://~i', $link)) {
+ $message = 'Link không hợp lệ';
+ goto render_after_post;
+ }
+ }
$stmt = $pdo->prepare('UPDATE settings SET ads_enabled=?, ad_link=?, ad_title=?, ad_body=?, contact_link=? WHERE id = 1');
$stmt->execute([$adsEnabled, $adLink, $adTitle, $adBody, $contactLink]);
$message = 'Đã lưu cài đặt';
}
+render_after_post:📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
| $message = 'Đã lưu cài đặt'; | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| $settings = load_settings($pdo); | ||||||||||||||||||||||||||||||||||||||||||
| $posts = $pdo->query('SELECT * FROM posts ORDER BY created_at DESC')->fetchAll(); | ||||||||||||||||||||||||||||||||||||||||||
| $totalPosts = count($posts); | ||||||||||||||||||||||||||||||||||||||||||
| $totalViews = stat_total($pdo, 'view'); | ||||||||||||||||||||||||||||||||||||||||||
| $totalClicks = stat_total($pdo, 'ad_click'); | ||||||||||||||||||||||||||||||||||||||||||
| $csrf = csrf_token(); | ||||||||||||||||||||||||||||||||||||||||||
| $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https://' : 'http://'; | ||||||||||||||||||||||||||||||||||||||||||
| $baseUrl = $scheme . ($_SERVER['HTTP_HOST'] ?? 'domain'); | ||||||||||||||||||||||||||||||||||||||||||
| ?><!DOCTYPE html> | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+63
to
+65
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. Don’t trust
🔧 Example (config-based)-$scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https://' : 'http://';
-$baseUrl = $scheme . ($_SERVER['HTTP_HOST'] ?? 'domain');
+$baseUrl = rtrim(BASE_URL, '/');🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
| <html lang="vi"> | ||||||||||||||||||||||||||||||||||||||||||
| <head> | ||||||||||||||||||||||||||||||||||||||||||
| <meta charset="UTF-8"> | ||||||||||||||||||||||||||||||||||||||||||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||||||||||||||||||||||||||||||||||||||
| <title>Quản trị</title> | ||||||||||||||||||||||||||||||||||||||||||
| <link rel="stylesheet" href="/assets/css/admin.css"> | ||||||||||||||||||||||||||||||||||||||||||
| <script> | ||||||||||||||||||||||||||||||||||||||||||
| function switchTab(id){ | ||||||||||||||||||||||||||||||||||||||||||
| document.querySelectorAll('.tab').forEach(el=>el.classList.add('hidden')); | ||||||||||||||||||||||||||||||||||||||||||
| document.getElementById(id).classList.remove('hidden'); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| function copyLink(link){ | ||||||||||||||||||||||||||||||||||||||||||
| navigator.clipboard.writeText(link).then(()=>{ | ||||||||||||||||||||||||||||||||||||||||||
| alert('Đã copy link'); | ||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||
| </script> | ||||||||||||||||||||||||||||||||||||||||||
| </head> | ||||||||||||||||||||||||||||||||||||||||||
| <body> | ||||||||||||||||||||||||||||||||||||||||||
| <div class="container"> | ||||||||||||||||||||||||||||||||||||||||||
| <div class="card"> | ||||||||||||||||||||||||||||||||||||||||||
| <div class="top-row"> | ||||||||||||||||||||||||||||||||||||||||||
| <h2>Xin chào, <?php echo htmlspecialchars(admin_name(), ENT_QUOTES, 'UTF-8'); ?></h2> | ||||||||||||||||||||||||||||||||||||||||||
| <a class="logout" href="/admin/logout.php">Đăng xuất</a> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
| <div class="nav"> | ||||||||||||||||||||||||||||||||||||||||||
| <button onclick="switchTab('tab-post')">Bài viết</button> | ||||||||||||||||||||||||||||||||||||||||||
| <button onclick="switchTab('tab-settings')">Cài đặt</button> | ||||||||||||||||||||||||||||||||||||||||||
| <button onclick="switchTab('tab-stats')">Thống kê</button> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
| <?php if ($message): ?><p class="badge"><?php echo htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); ?></p><?php endif; ?> | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| <div id="tab-post" class="tab"> | ||||||||||||||||||||||||||||||||||||||||||
| <h3>Thêm bài</h3> | ||||||||||||||||||||||||||||||||||||||||||
| <form method="post"> | ||||||||||||||||||||||||||||||||||||||||||
| <input type="hidden" name="csrf" value="<?php echo htmlspecialchars($csrf, ENT_QUOTES, 'UTF-8'); ?>"> | ||||||||||||||||||||||||||||||||||||||||||
| <input type="hidden" name="action" value="create_post"> | ||||||||||||||||||||||||||||||||||||||||||
| <label>Tiêu đề</label> | ||||||||||||||||||||||||||||||||||||||||||
| <input name="title" required> | ||||||||||||||||||||||||||||||||||||||||||
| <label>Slug (để trống tự tạo)</label> | ||||||||||||||||||||||||||||||||||||||||||
| <input name="slug"> | ||||||||||||||||||||||||||||||||||||||||||
| <label>Nội dung</label> | ||||||||||||||||||||||||||||||||||||||||||
| <textarea name="content" rows="4"></textarea> | ||||||||||||||||||||||||||||||||||||||||||
| <label>Link ảnh (mỗi dòng một link Telegram)</label> | ||||||||||||||||||||||||||||||||||||||||||
| <textarea name="images" rows="4"></textarea> | ||||||||||||||||||||||||||||||||||||||||||
| <label>Link video (mỗi dòng một link Telegram)</label> | ||||||||||||||||||||||||||||||||||||||||||
| <textarea name="videos" rows="4"></textarea> | ||||||||||||||||||||||||||||||||||||||||||
| <button type="submit">Thêm bài</button> | ||||||||||||||||||||||||||||||||||||||||||
| </form> | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| <h3>Danh sách bài</h3> | ||||||||||||||||||||||||||||||||||||||||||
| <?php foreach ($posts as $item): $itemId = (int)$item['id']; $slugVal = $item['slug']; $full = $baseUrl . '/' . $slugVal; ?> | ||||||||||||||||||||||||||||||||||||||||||
| <div class="card inner"> | ||||||||||||||||||||||||||||||||||||||||||
| <div class="row"> | ||||||||||||||||||||||||||||||||||||||||||
| <strong><?php echo htmlspecialchars($item['title'], ENT_QUOTES, 'UTF-8'); ?></strong> | ||||||||||||||||||||||||||||||||||||||||||
| <span class="badge">ID <?php echo $itemId; ?></span> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
| <p class="link-copy">Link: <?php echo htmlspecialchars($full, ENT_QUOTES, 'UTF-8'); ?></p> | ||||||||||||||||||||||||||||||||||||||||||
| <button type="button" onclick="copyLink('<?php echo htmlspecialchars($full, ENT_QUOTES, 'UTF-8'); ?>')">Copy link</button> | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+123
to
+124
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. Escape for JavaScript context, not just HTML.
🔐 Safe JS string encoding-<button type="button" onclick="copyLink('<?php echo htmlspecialchars($full, ENT_QUOTES, 'UTF-8'); ?>')">Copy link</button>
+<button type="button" onclick="copyLink(<?php echo json_encode($full, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT); ?>)">Copy link</button>📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
| <details> | ||||||||||||||||||||||||||||||||||||||||||
| <summary>Sửa / Xóa</summary> | ||||||||||||||||||||||||||||||||||||||||||
| <form method="post"> | ||||||||||||||||||||||||||||||||||||||||||
| <input type="hidden" name="csrf" value="<?php echo htmlspecialchars($csrf, ENT_QUOTES, 'UTF-8'); ?>"> | ||||||||||||||||||||||||||||||||||||||||||
| <input type="hidden" name="action" value="update_post"> | ||||||||||||||||||||||||||||||||||||||||||
| <input type="hidden" name="post_id" value="<?php echo $itemId; ?>"> | ||||||||||||||||||||||||||||||||||||||||||
| <label>Tiêu đề</label> | ||||||||||||||||||||||||||||||||||||||||||
| <input name="title" value="<?php echo htmlspecialchars($item['title'], ENT_QUOTES, 'UTF-8'); ?>" required> | ||||||||||||||||||||||||||||||||||||||||||
| <label>Slug</label> | ||||||||||||||||||||||||||||||||||||||||||
| <input name="slug" value="<?php echo htmlspecialchars($item['slug'], ENT_QUOTES, 'UTF-8'); ?>" required> | ||||||||||||||||||||||||||||||||||||||||||
| <label>Nội dung</label> | ||||||||||||||||||||||||||||||||||||||||||
| <textarea name="content" rows="4"><?php echo htmlspecialchars($item['content'], ENT_QUOTES, 'UTF-8'); ?></textarea> | ||||||||||||||||||||||||||||||||||||||||||
| <label>Link ảnh</label> | ||||||||||||||||||||||||||||||||||||||||||
| <textarea name="images" rows="3"><?php echo htmlspecialchars(implode("\n", json_decode($item['images_json'], true) ?: []), ENT_QUOTES, 'UTF-8'); ?></textarea> | ||||||||||||||||||||||||||||||||||||||||||
| <label>Link video</label> | ||||||||||||||||||||||||||||||||||||||||||
| <textarea name="videos" rows="3"><?php echo htmlspecialchars(implode("\n", json_decode($item['videos_json'], true) ?: []), ENT_QUOTES, 'UTF-8'); ?></textarea> | ||||||||||||||||||||||||||||||||||||||||||
| <button type="submit">Lưu</button> | ||||||||||||||||||||||||||||||||||||||||||
| </form> | ||||||||||||||||||||||||||||||||||||||||||
| <form method="post" onsubmit="return confirm('Xóa bài này?');"> | ||||||||||||||||||||||||||||||||||||||||||
| <input type="hidden" name="csrf" value="<?php echo htmlspecialchars($csrf, ENT_QUOTES, 'UTF-8'); ?>"> | ||||||||||||||||||||||||||||||||||||||||||
| <input type="hidden" name="action" value="delete_post"> | ||||||||||||||||||||||||||||||||||||||||||
| <input type="hidden" name="post_id" value="<?php echo $itemId; ?>"> | ||||||||||||||||||||||||||||||||||||||||||
| <button type="submit" class="danger">Xóa</button> | ||||||||||||||||||||||||||||||||||||||||||
| </form> | ||||||||||||||||||||||||||||||||||||||||||
| </details> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
| <?php endforeach; ?> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| <div id="tab-settings" class="tab hidden"> | ||||||||||||||||||||||||||||||||||||||||||
| <h3>Cài đặt</h3> | ||||||||||||||||||||||||||||||||||||||||||
| <form method="post"> | ||||||||||||||||||||||||||||||||||||||||||
| <input type="hidden" name="csrf" value="<?php echo htmlspecialchars($csrf, ENT_QUOTES, 'UTF-8'); ?>"> | ||||||||||||||||||||||||||||||||||||||||||
| <input type="hidden" name="action" value="save_settings"> | ||||||||||||||||||||||||||||||||||||||||||
| <label><input type="checkbox" name="ads_enabled" <?php echo !empty($settings['ads_enabled']) ? 'checked' : ''; ?>> Bật quảng cáo</label> | ||||||||||||||||||||||||||||||||||||||||||
| <label>Link Shopee</label> | ||||||||||||||||||||||||||||||||||||||||||
| <input name="ad_link" value="<?php echo htmlspecialchars($settings['ad_link'], ENT_QUOTES, 'UTF-8'); ?>" placeholder="https://s.shopee.vn/xxxx"> | ||||||||||||||||||||||||||||||||||||||||||
| <label>Tiêu đề quảng cáo</label> | ||||||||||||||||||||||||||||||||||||||||||
| <input name="ad_title" value="<?php echo htmlspecialchars($settings['ad_title'], ENT_QUOTES, 'UTF-8'); ?>"> | ||||||||||||||||||||||||||||||||||||||||||
| <label>Nội dung quảng cáo</label> | ||||||||||||||||||||||||||||||||||||||||||
| <textarea name="ad_body" rows="3"><?php echo htmlspecialchars($settings['ad_body'], ENT_QUOTES, 'UTF-8'); ?></textarea> | ||||||||||||||||||||||||||||||||||||||||||
| <label>Link liên hệ admin</label> | ||||||||||||||||||||||||||||||||||||||||||
| <input name="contact_link" value="<?php echo htmlspecialchars($settings['contact_link'], ENT_QUOTES, 'UTF-8'); ?>" placeholder="Facebook hoặc Telegram"> | ||||||||||||||||||||||||||||||||||||||||||
| <button type="submit">Lưu</button> | ||||||||||||||||||||||||||||||||||||||||||
| </form> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| <div id="tab-stats" class="tab hidden"> | ||||||||||||||||||||||||||||||||||||||||||
| <h3>Thống kê</h3> | ||||||||||||||||||||||||||||||||||||||||||
| <p>Tổng số bài báo: <strong><?php echo $totalPosts; ?></strong></p> | ||||||||||||||||||||||||||||||||||||||||||
| <p>Tổng lượt xem bài: <strong><?php echo $totalViews; ?></strong></p> | ||||||||||||||||||||||||||||||||||||||||||
| <p>Tổng lượt click quảng cáo: <strong><?php echo $totalClicks; ?></strong></p> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||
| </body> | ||||||||||||||||||||||||||||||||||||||||||
| </html> | ||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| <?php | ||
| require_once __DIR__ . '/../lib/db.php'; | ||
| require_once __DIR__ . '/../lib/error_handler.php'; | ||
| require_once __DIR__ . '/../lib/track.php'; | ||
|
|
||
| $pdo = db(); | ||
| $type = $_POST['type'] ?? 'view'; | ||
| $slug = $_POST['slug'] ?? ''; | ||
| $postId = null; | ||
|
|
||
| if ($slug !== '') { | ||
| $stmt = $pdo->prepare('SELECT id FROM posts WHERE slug = ? LIMIT 1'); | ||
| $stmt->execute([$slug]); | ||
| $row = $stmt->fetch(); | ||
| if ($row) { | ||
| $postId = (int)$row['id']; | ||
| } | ||
| } | ||
|
|
||
| record_stat($pdo, $type === 'ad_click' ? 'ad_click' : 'view', $postId, 60); | ||
|
|
||
| echo json_encode(['ok' => true]); | ||
|
Comment on lines
+1
to
+22
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 Two minor issues:
🔧 Proposed fix <?php
+if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
+ http_response_code(405);
+ exit;
+}
require_once __DIR__ . '/../lib/db.php';
require_once __DIR__ . '/../lib/error_handler.php';
require_once __DIR__ . '/../lib/track.php';
// ...
+header('Content-Type: application/json');
echo json_encode(['ok' => true]);🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| body { | ||
| margin: 0; | ||
| font-family: 'Segoe UI', 'Helvetica Neue', Arial, sans-serif; | ||
| background: #0b1222; | ||
| color: #e2e8f0; | ||
| } | ||
| .container { max-width: 880px; margin: 0 auto; padding: 18px 14px 60px; } | ||
| .card { background: #111827; border-radius: 12px; padding: 16px; box-shadow: 0 12px 32px rgba(0,0,0,0.35); } | ||
| .card + .card { margin-top: 12px; } | ||
| input, textarea, button { width: 100%; padding: 12px; margin: 6px 0 12px; border-radius: 8px; border: 1px solid #1f2937; background: #0d152b; color: #e2e8f0; } | ||
| label { font-weight: 600; display: block; margin-top: 4px; } | ||
| button { cursor: pointer; background: #2563eb; border: none; font-weight: 600; } | ||
| button.danger { background: #ef4444; } | ||
| .nav { display: flex; gap: 8px; margin: 12px 0; } | ||
| .nav button { flex: 1; } | ||
| .badge { display: inline-block; padding: 6px 10px; background: #1d4ed8; border-radius: 8px; font-size: 12px; } | ||
| .alert { color: #fca5a5; } | ||
| .hidden { display: none; } | ||
| .link-copy { font-size: 13px; color: #93c5fd; word-break: break-all; } | ||
| .row { display: flex; justify-content: space-between; align-items: center; } | ||
| .top-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; } | ||
| .logout { color: #93c5fd; text-decoration: none; } | ||
| .card.inner { margin-top: 10px; } | ||
| @media (max-width: 600px) { .nav { flex-direction: column; } .top-row { flex-direction: column; align-items: flex-start; } } |
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.
Critical:
/sqland/logsdirectories are publicly accessible.Options -Indexesonly suppresses directory listing. It does not block direct file access —http://domain/sql/database.sqlis fully readable by anyone, exposing the entire schema and the seeded admin bcrypt hash. If log files land in/logs, they are similarly exposed (stack traces, DB errors, IP addresses).The safest fix is to move both directories outside
public_html. If that isn't feasible, add explicit deny rules:🔒 Proposed .htaccess fix (deny access to sensitive directories)
Alternatively, place a
deny.htaccessinside each directory:🤖 Prompt for AI Agents