PHP 前后端一体:首页咨询师列表、预约表单、管理后台、二维码准入与邮件通知;敏感配置与提交数据通过 .gitignore 排除。 Co-authored-by: Cursor <cursoragent@cursor.com>
160 lines
6.5 KiB
PHP
160 lines
6.5 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
require __DIR__ . '/api/bootstrap.php';
|
||
sendHtmlNoCacheHeaders();
|
||
$v = getAssetVersion();
|
||
?><!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||
<meta http-equiv="Pragma" content="no-cache">
|
||
<meta http-equiv="Expires" content="0">
|
||
<title>系统调试 - 心理咨询</title>
|
||
<link rel="stylesheet" href="<?= asset('css/base.css') ?>">
|
||
<link rel="stylesheet" href="<?= asset('css/admin.css') ?>">
|
||
<style>
|
||
.debug-page { padding: 16px; max-width: 640px; margin: 0 auto; }
|
||
.debug-card { background: #fff; border-radius: 12px; padding: 16px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0,0,0,.06); }
|
||
.debug-card h2 { font-size: 16px; margin-bottom: 12px; }
|
||
.debug-row { font-size: 14px; margin-bottom: 8px; line-height: 1.6; word-break: break-all; }
|
||
.debug-row.ok { color: #40916c; }
|
||
.debug-row.fail { color: #e54d4d; }
|
||
.debug-actions { display: flex; flex-direction: column; gap: 8px; }
|
||
.debug-log { background: #1e1e1e; color: #d4d4d4; padding: 12px; border-radius: 8px; font-size: 12px; white-space: pre-wrap; word-break: break-all; max-height: 320px; overflow: auto; }
|
||
.debug-note { font-size: 13px; color: #666; line-height: 1.7; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="page debug-page">
|
||
<h1 class="admin-header__title">系统调试</h1>
|
||
<p class="debug-note">访问地址须带访问令牌,例如:<br><code id="sample-url"></code></p>
|
||
|
||
<div class="debug-card">
|
||
<h2>环境检测</h2>
|
||
<div id="env-result">加载中…</div>
|
||
</div>
|
||
|
||
<div class="debug-card">
|
||
<h2>操作</h2>
|
||
<div class="debug-actions">
|
||
<button type="button" class="btn btn--primary btn--block" id="btn-refresh">刷新检测</button>
|
||
<button type="button" class="btn btn--primary btn--block" id="btn-test-submit">测试提交(写入一条记录)</button>
|
||
<button type="button" class="btn btn--ghost btn--block" id="btn-test-mail">测试邮件(发送全部历史记录)</button>
|
||
<a href="consult.php" class="btn btn--ghost btn--block" style="text-align:center;line-height:48px">打开预约页</a>
|
||
<a href="admin.php" class="btn btn--ghost btn--block" style="text-align:center;line-height:48px">管理后台</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="debug-card">
|
||
<h2>日志</h2>
|
||
<pre class="debug-log" id="debug-log">等待操作…</pre>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="<?= asset('js/api.js') ?>"></script>
|
||
<script>
|
||
function getAccessToken() {
|
||
return new URLSearchParams(window.location.search).get("access") || "";
|
||
}
|
||
|
||
function diagUrl(action) {
|
||
const token = getAccessToken();
|
||
const base = new URL("api/diagnostics.php", window.location.href);
|
||
if (action) base.searchParams.set("action", action);
|
||
if (token) base.searchParams.set("access", token);
|
||
return base.href;
|
||
}
|
||
|
||
function log(msg) {
|
||
const el = document.getElementById("debug-log");
|
||
const time = new Date().toLocaleString("zh-CN", { hour12: false });
|
||
el.textContent = `[${time}] ${msg}\n\n` + el.textContent;
|
||
}
|
||
|
||
function row(label, ok, detail) {
|
||
return `<div class="debug-row ${ok ? "ok" : "fail"}">${ok ? "✓" : "✗"} ${label}${detail ? ":" + detail : ""}</div>`;
|
||
}
|
||
|
||
async function loadDiagnostics() {
|
||
const token = getAccessToken();
|
||
const envEl = document.getElementById("env-result");
|
||
document.getElementById("sample-url").textContent =
|
||
window.location.origin + window.location.pathname.replace(/debug\.php$/, "debug.php") + "?access=你的accessToken";
|
||
|
||
if (!token) {
|
||
envEl.innerHTML = row("访问令牌", false, "URL 缺少 ?access= 参数");
|
||
return;
|
||
}
|
||
|
||
envEl.innerHTML = "检测中…";
|
||
try {
|
||
const res = await fetch(diagUrl(), { credentials: "same-origin" });
|
||
const text = await res.text();
|
||
let data;
|
||
try { data = JSON.parse(text); } catch {
|
||
envEl.innerHTML = row("接口响应", false, `非 JSON(HTTP ${res.status})`);
|
||
log("GET diagnostics 失败:\n" + text.slice(0, 800));
|
||
return;
|
||
}
|
||
if (!res.ok) throw new Error(data.message || "检测失败");
|
||
|
||
const ext = data.extensions || {};
|
||
envEl.innerHTML = [
|
||
row("PHP 版本", true, data.phpVersion),
|
||
row("资源版本", true, data.assetVersion || "-"),
|
||
row("json 扩展", !!ext.json),
|
||
row("openssl 扩展", !!ext.openssl),
|
||
row("data 目录可写", !!data.dataDir?.ok, data.dataDir?.message),
|
||
row("提交记录数", true, String(data.submissions?.count ?? 0)),
|
||
row("邮件通知", !!data.mail?.enabled, data.mail?.enabled ? `${data.mail.recipientCount} 个收件人` : "未启用"),
|
||
row("submit 接口", true, new URL("api/submit.php", window.location.href).href),
|
||
].join("");
|
||
|
||
log("环境检测成功:\n" + JSON.stringify(data, null, 2));
|
||
} catch (e) {
|
||
envEl.innerHTML = row("环境检测", false, e.message);
|
||
log("环境检测异常: " + e.message);
|
||
}
|
||
}
|
||
|
||
async function postAction(action) {
|
||
const token = getAccessToken();
|
||
if (!token) {
|
||
alert("请先在 URL 中添加 ?access=访问令牌");
|
||
return;
|
||
}
|
||
log(`开始 ${action}…`);
|
||
try {
|
||
const res = await fetch(diagUrl(action), {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: "{}",
|
||
credentials: "same-origin",
|
||
});
|
||
const text = await res.text();
|
||
let data;
|
||
try { data = JSON.parse(text); } catch {
|
||
log(`${action} 失败(HTTP ${res.status},非 JSON):\n` + text.slice(0, 800));
|
||
alert("请求失败,详见日志");
|
||
return;
|
||
}
|
||
if (!res.ok) throw new Error(data.message || `HTTP ${res.status}`);
|
||
log(`${action} 成功:\n` + JSON.stringify(data, null, 2));
|
||
alert(data.message || "操作成功");
|
||
await loadDiagnostics();
|
||
} catch (e) {
|
||
log(`${action} 异常: ` + e.message);
|
||
alert(e.message);
|
||
}
|
||
}
|
||
|
||
document.getElementById("btn-refresh").addEventListener("click", loadDiagnostics);
|
||
document.getElementById("btn-test-submit").addEventListener("click", () => postAction("submit"));
|
||
document.getElementById("btn-test-mail").addEventListener("click", () => postAction("mail"));
|
||
loadDiagnostics();
|
||
</script>
|
||
</body>
|
||
</html>
|