$value) {
if (strpos($key, '_base64') !== false && !empty($value)) {
$originalKey = str_replace('_base64', '', $key);
$fileName = $_POST[$originalKey . '_name'] ?? 'uploaded_file';
$parts = explode(';', $value);
if (count($parts) == 2 && strpos($parts[1], 'base64,') === 0) {
$mimeType = str_replace('data:', '', $parts[0]);
$base64Str = substr($parts[1], 7);
$tmpFilePath = tempnam(sys_get_temp_dir(), 'b64_');
file_put_contents($tmpFilePath, base64_decode($base64Str));
$_FILES[$originalKey] = [
'name' => $fileName,
'type' => $mimeType,
'tmp_name' => $tmpFilePath,
'error' => UPLOAD_ERR_OK,
'size' => filesize($tmpFilePath)
];
}
}
}
// ==========================================
// คลาสช่วยเหลือสำหรับ 2FA (TOTP)
// ==========================================
class MiniTOTP
{
private static $base32chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
public static function createSecret($length = 16)
{
$secret = '';
for ($i = 0; $i < $length; $i++) {
$secret .= self::$base32chars[random_int(0, 31)];
}
return $secret;
}
public static function verifyCode($secret, $code, $discrepancy = 1)
{
$currentTimeSlice = floor(time() / 30);
for ($i = -$discrepancy; $i <= $discrepancy; $i++) {
$calculatedCode = self::getCode($secret, $currentTimeSlice + $i);
if (hash_equals($calculatedCode, $code)) return true;
}
return false;
}
private static function getCode($secret, $timeSlice)
{
$secretKey = self::base32Decode($secret);
$time = chr(0) . chr(0) . chr(0) . chr(0) . pack('N*', $timeSlice);
$hm = hash_hmac('SHA1', $time, $secretKey, true);
$offset = ord(substr($hm, -1)) & 0x0F;
$hashpart = substr($hm, $offset, 4);
$value = unpack('N', $hashpart);
$value = $value[1];
$value = $value & 0x7FFFFFFF;
$modulo = pow(10, 6);
return str_pad($value % $modulo, 6, '0', STR_PAD_LEFT);
}
private static function base32Decode($secret)
{
if (empty($secret)) return '';
$base32chars = self::$base32chars;
$base32charsFlipped = array_flip(str_split($base32chars));
$paddingCharCount = substr_count($secret, $base32chars[32] ?? '=');
$allowedValues = array(6, 4, 3, 1, 0);
if (!in_array($paddingCharCount, $allowedValues)) return false;
for ($i = 0; $i < 4; $i++) {
if ($paddingCharCount == $allowedValues[$i] && substr($secret, - ($allowedValues[$i])) != str_repeat($base32chars[32] ?? '=', $allowedValues[$i])) return false;
}
$secret = str_replace('=', '', $secret);
$secret = str_split($secret);
$binaryString = '';
for ($i = 0; $i < count($secret); $i = $i + 8) {
$x = '';
if (!in_array($secret[$i], str_split($base32chars))) return false;
for ($j = 0; $j < 8; $j++) {
$x .= str_pad(base_convert(@$base32charsFlipped[@$secret[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
}
$eightBits = str_split($x, 8);
for ($z = 0; $z < count($eightBits); $z++) {
$binaryString .= (($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48) ? $y : '';
}
}
return $binaryString;
}
}
// ==========================================
// ส่วน Export Excel สำหรับหน้าความคิดเห็น
// ==========================================
if (isset($_GET['export_feedback'])) {
header("Content-Type: application/vnd.ms-excel");
header("Content-Disposition: attachment; filename=feedbacks_export.xls");
header("Pragma: no-cache");
header("Expires: 0");
echo '';
echo '
';
echo '';
echo '| วันที่ | ชื่อ-นามสกุล | หัวข้อ | รายละเอียด |
';
$whereExport = "";
if (!empty($_GET['topic_filter'])) $whereExport = "WHERE topic = " . $pdo->quote($_GET['topic_filter']);
$stmtEx = $pdo->query("SELECT * FROM feedbacks $whereExport ORDER BY created_at DESC");
while ($row = $stmtEx->fetch()) {
echo "| {$row['created_at']} | " . htmlspecialchars($row['name']) . " | " . htmlspecialchars($row['topic']) . " | " . htmlspecialchars($row['message']) . " |
";
}
echo '
';
exit;
}
// ==========================================
// ตรวจสอบการเข้าสู่ระบบ & 2FA
// ==========================================
// ขั้นที่ 1: ตรวจสอบรหัสผ่าน
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['login_step1'])) {
$cid = $_POST['cid'];
$pass = $_POST['password'];
$stmt = $pdo->prepare("SELECT * FROM users WHERE cid = ?");
$stmt->execute([$cid]);
$user = $stmt->fetch();
if ($user && password_verify($pass, $user['password_hash'])) {
// หากผู้ใช้มี Secret 2FA ให้ข้ามไปขั้นตอนที่ 2
if (!empty($user['two_factor_secret'])) {
$_SESSION['pending_2fa_id'] = $user['id'];
$_SESSION['pending_2fa_name'] = $user['name'];
$_SESSION['pending_2fa_role'] = $user['role'];
$_SESSION['pending_2fa_secret'] = $user['two_factor_secret'];
} else {
// หากไม่มี 2FA ให้เข้าสู่ระบบได้เลย
$_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['name'];
$_SESSION['user_role'] = $user['role'];
logAction('LOGIN', 'auth', 'เข้าสู่ระบบสำเร็จ (ไม่มี 2FA)');
setFlash('success', 'เข้าสู่ระบบสำเร็จ ยินดีต้อนรับ');
header('Location: admin.php');
exit;
}
} else {
setFlash('error', 'เลขประจำตัวประชาชนหรือรหัสผ่านไม่ถูกต้อง');
}
}
// ขั้นที่ 2: ตรวจสอบรหัส 2FA 6 หลัก
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['login_step2'])) {
$code = $_POST['totp_code'];
$secret = $_SESSION['pending_2fa_secret'] ?? '';
if (MiniTOTP::verifyCode($secret, $code)) {
$_SESSION['user_id'] = $_SESSION['pending_2fa_id'];
$_SESSION['user_name'] = $_SESSION['pending_2fa_name'];
$_SESSION['user_role'] = $_SESSION['pending_2fa_role'];
// ล้างข้อมูล temp
unset($_SESSION['pending_2fa_id'], $_SESSION['pending_2fa_name'], $_SESSION['pending_2fa_role'], $_SESSION['pending_2fa_secret']);
logAction('LOGIN', 'auth', 'เข้าสู่ระบบสำเร็จ (ผ่าน 2FA)');
setFlash('success', 'ยืนยันตัวตนสำเร็จ ยินดีต้อนรับ');
header('Location: admin.php');
exit;
} else {
setFlash('error', 'รหัส Authenticator ไม่ถูกต้อง กรุณาลองใหม่');
}
}
// กรณียกเลิกการล็อกอินกลางคัน
if (isset($_GET['cancel_login'])) {
unset($_SESSION['pending_2fa_id'], $_SESSION['pending_2fa_name'], $_SESSION['pending_2fa_role'], $_SESSION['pending_2fa_secret']);
header('Location: admin.php');
exit;
}
// แสดงหน้า Login หรือหน้ากรอกรหัส 2FA หากยังไม่ได้เข้าสู่ระบบ
if (!isset($_SESSION['user_id'])) {
?>
เข้าสู่ระบบจัดการ - โรงพยาบาลเกาะสมุย
ยืนยันตัวตน 2 ขั้นตอน
กรุณากรอกรหัส 6 หลักจากแอปพลิเคชัน
Google Authenticator ของคุณ
เข้าสู่ระบบ (CMS)
กรุณากรอกข้อมูลยืนยันตัวตน
prepare("INSERT INTO contents (category_slug, title, details, drive_file_id, drive_file_url, created_by) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$category, $title, $detailsJson, $driveId, $driveUrl, $_SESSION['user_id']]);
logAction('CREATE', 'contents', "เพิ่มเนื้อหาใหม่: $title");
setFlash('success', 'บันทึกข้อมูลเรียบร้อยแล้ว');
header("Location: admin.php?tab=" . $_GET['tab']);
exit;
}
// 1.1 จัดการแก้ไขเนื้อหาและไฟล์
if (isset($_POST['edit_content'])) {
$id = $_POST['content_id'];
$title = $_POST['title'];
$category = $_POST['category_slug'];
$details = [];
if (!empty($_POST['extra_detail'])) $details['amount'] = $_POST['extra_detail'];
if (!empty($_POST['external_link'])) $details['gphoto_link'] = $_POST['external_link'];
$detailsJson = !empty($details) ? json_encode($details, JSON_UNESCAPED_UNICODE) : null;
if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) {
$stmt = $pdo->prepare("SELECT drive_file_id FROM contents WHERE id = ?");
$stmt->execute([$id]);
$oldFile = $stmt->fetch();
if ($oldFile && $oldFile['drive_file_id']) {
deleteFromGoogleDrive($oldFile['drive_file_id']);
}
$fileInfo = uploadToGoogleDrive($_FILES['file']['tmp_name'], $_FILES['file']['name'], $_FILES['file']['type']);
$driveId = $fileInfo['id'];
$driveUrl = $fileInfo['url'];
$stmt = $pdo->prepare("UPDATE contents SET category_slug = ?, title = ?, details = ?, drive_file_id = ?, drive_file_url = ? WHERE id = ?");
$stmt->execute([$category, $title, $detailsJson, $driveId, $driveUrl, $id]);
} else {
$stmt = $pdo->prepare("UPDATE contents SET category_slug = ?, title = ?, details = ? WHERE id = ?");
$stmt->execute([$category, $title, $detailsJson, $id]);
}
logAction('UPDATE', 'contents', "แก้ไขเนื้อหา: $title");
setFlash('success', 'อัปเดตข้อมูลเรียบร้อยแล้ว');
header("Location: admin.php?tab=" . $_GET['tab']);
exit;
}
// 2. จัดการลบเนื้อหา
if (isset($_GET['delete_id'])) {
$id = $_GET['delete_id'];
$stmt = $pdo->prepare("SELECT drive_file_id, title FROM contents WHERE id = ?");
$stmt->execute([$id]);
$item = $stmt->fetch();
if ($item) {
if ($item['drive_file_id']) deleteFromGoogleDrive($item['drive_file_id']);
$pdo->prepare("DELETE FROM contents WHERE id = ?")->execute([$id]);
logAction('DELETE', 'contents', "ลบข้อมูล: " . $item['title']);
setFlash('success', 'ลบข้อมูลและไฟล์เรียบร้อยแล้ว');
}
header("Location: admin.php?tab=" . $_GET['tab']);
exit;
}
// 3. จัดการอัปเดต Settings
if (isset($_POST['update_settings'])) {
if ($userRole !== 'admin') exit('Access Denied');
foreach ($_POST['settings'] as $key => $val) {
$pdo->prepare("UPDATE settings SET setting_value = ? WHERE setting_key = ?")->execute([$val, $key]);
}
if (isset($_FILES['director_photo']) && $_FILES['director_photo']['error'] === UPLOAD_ERR_OK) {
$stmtOld = $pdo->query("SELECT setting_value FROM settings WHERE setting_key = 'director_photo_id'");
$oldDriveId = $stmtOld->fetchColumn();
if ($oldDriveId) deleteFromGoogleDrive($oldDriveId);
$fileInfo = uploadToGoogleDrive($_FILES['director_photo']['tmp_name'], $_FILES['director_photo']['name'], $_FILES['director_photo']['type']);
$pdo->prepare("INSERT INTO settings (setting_key, setting_value, setting_label) VALUES ('director_photo_id', ?, 'ID รูปผู้อำนวยการ') ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)")->execute([$fileInfo['id']]);
$pdo->prepare("INSERT INTO settings (setting_key, setting_value, setting_label) VALUES ('director_photo_url', ?, 'URL รูปผู้อำนวยการ') ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)")->execute([$fileInfo['url']]);
}
if (isset($_FILES['hero_photo']) && $_FILES['hero_photo']['error'] === UPLOAD_ERR_OK) {
$stmtOld = $pdo->query("SELECT setting_value FROM settings WHERE setting_key = 'hero_photo_id'");
$oldDriveId = $stmtOld->fetchColumn();
if ($oldDriveId) deleteFromGoogleDrive($oldDriveId);
$fileInfo = uploadToGoogleDrive($_FILES['hero_photo']['tmp_name'], $_FILES['hero_photo']['name'], $_FILES['hero_photo']['type']);
$pdo->prepare("INSERT INTO settings (setting_key, setting_value, setting_label) VALUES ('hero_photo_id', ?, 'ID รูปหน้าปก') ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)")->execute([$fileInfo['id']]);
$pdo->prepare("INSERT INTO settings (setting_key, setting_value, setting_label) VALUES ('hero_photo_url', ?, 'URL รูปหน้าปก') ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)")->execute([$fileInfo['url']]);
}
if (isset($_FILES['donate_qrcode']) && $_FILES['donate_qrcode']['error'] === UPLOAD_ERR_OK) {
$stmtOld = $pdo->query("SELECT setting_value FROM settings WHERE setting_key = 'donate_qrcode_id'");
$oldDriveId = $stmtOld->fetchColumn();
if ($oldDriveId) deleteFromGoogleDrive($oldDriveId);
$fileInfo = uploadToGoogleDrive($_FILES['donate_qrcode']['tmp_name'], $_FILES['donate_qrcode']['name'], $_FILES['donate_qrcode']['type']);
$pdo->prepare("INSERT INTO settings (setting_key, setting_value, setting_label) VALUES ('donate_qrcode_id', ?, 'ID รูป QR Code บริจาค') ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)")->execute([$fileInfo['id']]);
$pdo->prepare("INSERT INTO settings (setting_key, setting_value, setting_label) VALUES ('donate_qrcode_url', ?, 'URL รูป QR Code บริจาค') ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)")->execute([$fileInfo['url']]);
}
logAction('UPDATE', 'settings', "อัปเดตข้อมูลโครงสร้าง/การตั้งค่า");
setFlash('success', 'อัปเดตข้อมูลเรียบร้อยแล้ว');
header("Location: admin.php?tab=" . $_GET['tab']);
exit;
}
// 4. จัดการผู้ใช้งาน (เพิ่ม/แก้ไข/ลบ/จัดการ 2FA)
if (isset($_POST['add_user'])) {
if ($userRole !== 'admin') exit('Access Denied');
$cid = trim($_POST['cid']);
$name = trim($_POST['name']);
$role = $_POST['role'];
$pass = password_hash($_POST['password'], PASSWORD_DEFAULT);
$stmt = $pdo->prepare("SELECT id FROM users WHERE cid = ?");
$stmt->execute([$cid]);
if ($stmt->fetch()) {
setFlash('error', 'เลขประจำตัวประชาชนนี้มีในระบบแล้ว');
} else {
$stmt = $pdo->prepare("INSERT INTO users (cid, password_hash, name, role) VALUES (?, ?, ?, ?)");
$stmt->execute([$cid, $pass, $name, $role]);
logAction('CREATE', 'users', "เพิ่มผู้ใช้งาน: $name");
setFlash('success', 'เพิ่มผู้ใช้งานเรียบร้อยแล้ว');
}
header("Location: admin.php?tab=users");
exit;
}
if (isset($_POST['edit_user'])) {
if ($userRole !== 'admin') exit('Access Denied');
$id = $_POST['user_id'];
$name = trim($_POST['name']);
$role = $_POST['role'];
if (!empty($_POST['password'])) {
$pass = password_hash($_POST['password'], PASSWORD_DEFAULT);
$stmt = $pdo->prepare("UPDATE users SET name = ?, role = ?, password_hash = ? WHERE id = ?");
$stmt->execute([$name, $role, $pass, $id]);
} else {
$stmt = $pdo->prepare("UPDATE users SET name = ?, role = ? WHERE id = ?");
$stmt->execute([$name, $role, $id]);
}
logAction('UPDATE', 'users', "แก้ไขข้อมูลผู้ใช้ ID: $id");
setFlash('success', 'อัปเดตข้อมูลผู้ใช้งานเรียบร้อยแล้ว');
header("Location: admin.php?tab=users");
exit;
}
if (isset($_GET['delete_user'])) {
if ($userRole !== 'admin') exit('Access Denied');
$id = $_GET['delete_user'];
if ($id == $_SESSION['user_id']) {
setFlash('error', 'ระบบไม่อนุญาตให้ลบบัญชีของตนเองได้');
} else {
$pdo->prepare("DELETE FROM users WHERE id = ?")->execute([$id]);
logAction('DELETE', 'users', "ลบผู้ใช้งาน ID: $id");
setFlash('success', 'ลบผู้ใช้งานเรียบร้อยแล้ว');
}
header("Location: admin.php?tab=users");
exit;
}
// 5. จัดการลบความคิดเห็น (เพิ่มใหม่ - Admin Only)
if (isset($_GET['delete_feedback'])) {
if ($userRole !== 'admin') exit('Access Denied');
$id = $_GET['delete_feedback'];
$pdo->prepare("DELETE FROM feedbacks WHERE id = ?")->execute([$id]);
logAction('DELETE', 'feedbacks', "ลบความคิดเห็น ID: $id");
setFlash('success', 'ลบความคิดเห็นเรียบร้อยแล้ว');
header("Location: admin.php?tab=feedback");
exit;
}
// จัดการการสร้างและลบ 2FA
if (isset($_POST['action_2fa'])) {
if ($userRole !== 'admin') exit('Access Denied');
$userId = (int)$_POST['user_id'];
if ($_POST['action_2fa'] === 'generate') {
$secret = MiniTOTP::createSecret();
$pdo->prepare("UPDATE users SET two_factor_secret = ? WHERE id = ?")->execute([$secret, $userId]);
logAction('UPDATE', 'users', "เปิดใช้งาน 2FA ให้ผู้ใช้ ID: $userId");
setFlash('success', 'สร้างรหัส 2FA เรียบร้อยแล้ว กรุณาให้ผู้ใช้งานสแกน QR Code');
// ส่งให้ UI เปิด modal QR Code อัตโนมัติหลัง reload
$_SESSION['show_qr_for_user'] = $userId;
} elseif ($_POST['action_2fa'] === 'remove') {
$pdo->prepare("UPDATE users SET two_factor_secret = NULL WHERE id = ?")->execute([$userId]);
logAction('UPDATE', 'users', "ยกเลิก 2FA ของผู้ใช้ ID: $userId");
setFlash('success', 'ยกเลิกการใช้งาน 2FA ของผู้ใช้นี้แล้ว');
}
header("Location: admin.php?tab=users");
exit;
}
// ==========================================
// ระบบ จัดการ Tab & Sidebar Logic
// ==========================================
$contentTabs = ['news', 'services', 'links', 'gallery', 'ita'];
$tabTitle = "";
$inSlugs = "''";
if (in_array($tab, $contentTabs)) {
if ($tab == 'news') {
$tabTitle = 'จัดการข่าวสารและประกาศ';
$inSlugs = "'pr_news', 'jobs', 'procurement_supplies', 'procurement_meds', 'plan', 'finance', 'academic', 'laws', 'download', 'infographic'";
} elseif ($tab == 'services') {
$tabTitle = 'จัดการบริการและแพ็คเกจ';
$inSlugs = "'clinic', 'eservices', 'packages'";
} elseif ($tab == 'links') {
$tabTitle = 'จัดการเมนูและลิงก์';
$inSlugs = "'header_menu', 'internal_systems', 'related_agencies'";
} elseif ($tab == 'gallery') {
$tabTitle = 'จัดการแกลเลอรี่ภาพกิจกรรม';
$inSlugs = "'gallery'";
} elseif ($tab == 'ita') {
$tabTitle = 'จัดการข้อมูลประเมินคุณธรรม (ITA)';
$inSlugs = "'ita'";
}
}
?>
CMS - โรงพยาบาลเกาะสมุย
เพิ่มข้อมูลในหมวด "= $tabTitle ?>"
= $tabTitle ?>
prepare("SELECT COUNT(*) FROM contents c $where");
$stmtCount->execute($params);
$totalItems = $stmtCount->fetchColumn();
$totalPages = ceil($totalItems / $limit);
$query = "SELECT c.*, cat.name as cat_name FROM contents c JOIN categories cat ON c.category_slug = cat.slug $where ORDER BY c.id DESC LIMIT $limit OFFSET $offset";
$stmt = $pdo->prepare($query);
$stmt->execute($params);
$contents = $stmt->fetchAll();
?>
| วันที่ |
หัวข้อเนื้อหา |
หมวดหมู่ |
ลิงก์ / ไฟล์แนบ |
จัดการ |
0): ?>
| = date('d/m/Y H:i', strtotime($item['created_at'])) ?> |
= htmlspecialchars($item['title']) ?>
|
= $item['cat_name'] ?>
|
|
|
|
|
0): ?>
แสดงหน้า = $page ?> จาก = max(1, $totalPages) ?> (รวมทั้งหมด = $totalItems ?> รายการ)
จัดการโครงสร้างหน้าเว็บ (หน้าปก / ผู้อำนวยการ / บริจาค)
ระบบความคิดเห็น
Export Excel
prepare("SELECT COUNT(*) FROM feedbacks $whereFb");
$stmtCountFb->execute($paramsFb);
$totalFbItems = $stmtCountFb->fetchColumn();
$totalFbPages = ceil($totalFbItems / $limit);
$stmtFb = $pdo->prepare("SELECT * FROM feedbacks $whereFb ORDER BY created_at DESC LIMIT $limit OFFSET $offset");
$stmtFb->execute($paramsFb);
$feedbacks = $stmtFb->fetchAll();
?>
| วันที่ |
ชื่อ-นามสกุล |
หัวข้อ |
รายละเอียด |
จัดการ |
0): foreach ($feedbacks as $fb): ?>
| = date('d/m/Y H:i', strtotime($fb['created_at'])) ?> |
= htmlspecialchars($fb['name'] ?: 'ไม่ระบุนาม') ?> |
= htmlspecialchars($fb['topic']) ?>
|
= htmlspecialchars($fb['message']) ?> |
|
|
|
0): ?>
แสดงหน้า = $page ?> จาก = max(1, $totalFbPages) ?> (รวมทั้งหมด = $totalFbItems ?> รายการ)
รายชื่อผู้ใช้งานในระบบ
query("SELECT COUNT(*) FROM users");
$totalUserItems = $stmtCountUser->fetchColumn();
$totalUserPages = ceil($totalUserItems / $limit);
$stmtUser = $pdo->query("SELECT * FROM users ORDER BY id DESC LIMIT $limit OFFSET $offset");
$users = $stmtUser->fetchAll();
?>
| ID |
เลขประจำตัวประชาชน |
ชื่อ-นามสกุล |
สิทธิ์การใช้งาน |
สถานะ 2FA |
จัดการ |
0): foreach ($users as $u): ?>
| = $u['id'] ?> |
= htmlspecialchars($u['cid']) ?> |
= htmlspecialchars($u['name']) ?> |
Admin
Editor
|
เปิดใช้งาน
ปิด
|
|
| ไม่มีข้อมูลผู้ใช้งาน |
0): ?>
แสดงหน้า = $page ?> จาก = max(1, $totalUserPages) ?>
จัดการระบบ 2FA
ผู้ใช้งานรายนี้ยังไม่ได้เปิดระบบยืนยันตัวตน 2 ขั้นตอน (2FA)
ผู้ใช้งานรายนี้เปิดระบบ 2FA แล้ว
prepare("SELECT name, two_factor_secret FROM users WHERE id = ?");
$stmtQR->execute([$qrUserId]);
$qrUser = $stmtQR->fetch();
if ($qrUser && $qrUser['two_factor_secret']):
$issuer = rawurlencode($settings['hospital_name'] ?? 'SamuiHospital');
$accountName = rawurlencode($qrUser['name']);
$otpauthUrl = "otpauth://totp/{$issuer}:{$accountName}?secret={$qrUser['two_factor_secret']}&issuer={$issuer}";
$qrImageUrl = "https://api.qrserver.com/v1/create-qr-code/?size=200x200&data=" . urlencode($otpauthUrl);
?>
กรุณาให้ผู้ใช้งาน (= htmlspecialchars($qrUser['name']) ?>)
ใช้แอป Google Authenticator สแกนรูปด้านล่าง
หรือกรอกรหัส (Setup Key) แทน:
= $qrUser['two_factor_secret'] ?>
ประวัติการทำงานของระบบ (Logs)
prepare("SELECT COUNT(*) FROM system_logs $whereLog");
$stmtCountLog->execute($paramsLog);
$totalLogItems = $stmtCountLog->fetchColumn();
$totalLogPages = ceil($totalLogItems / $limit);
$stmtLog = $pdo->prepare("SELECT * FROM system_logs $whereLog ORDER BY id DESC LIMIT $limit OFFSET $offset");
$stmtLog->execute($paramsLog);
$logs = $stmtLog->fetchAll();
?>
| วันเวลา |
ผู้ใช้งาน (ID) |
สถานะ |
โมดูล |
รายละเอียด |
IP Address |
0): foreach ($logs as $log): ?>
| = $log['created_at'] ?> |
= $log['user_id'] ? 'Admin (' . $log['user_id'] . ')' : 'System' ?> |
= $log['action_type'] ?>
|
= $log['module'] ?> |
= htmlspecialchars($log['description']) ?> |
= $log['ip_address'] ?> |
ไม่มีข้อมูลประวัติการทำงาน
|
0): ?>
แสดงหน้า = $page ?> จาก = max(1, $totalLogPages) ?> (รวมทั้งหมด = $totalLogItems ?> รายการ)