Last active 1749263038

用于Typecho健康检查的php页面

liueic's Avatar liueic revised this gist 1749263038. Go to revision

1 file changed, 158 insertions

health_check.php(file created)

@@ -0,0 +1,158 @@
1 + <?php
2 +
3 + // 禁用错误报告,避免在健康检查中暴露敏感信息
4 + error_reporting(0);
5 + ini_set('display_errors', 0);
6 +
7 + header('Content-Type: application/json');
8 +
9 + $status = [
10 + 'overall' => 'healthy',
11 + 'checks' => []
12 + ];
13 +
14 + // --- 数据库连接检查 (修改为适应Typecho现代化配置) ---
15 + function checkDatabaseConnection() {
16 + // 假设 Typecho 的配置文件是 config.inc.php
17 + $configFilePath = __DIR__ . '/config.inc.php';
18 + if (!file_exists($configFilePath)) {
19 + return ['status' => 'critical', 'message' => 'Typecho configuration file (config.inc.php) not found.'];
20 + }
21 +
22 + // 尝试包含 config.inc.php 文件
23 + // 这将初始化 Typecho 的数据库连接对象
24 + try {
25 + require_once $configFilePath;
26 +
27 + // 检查 Typecho 的 Db 类是否已初始化并可用
28 + if (!class_exists('\Typecho\Db') || !\Typecho\Db::get()) {
29 + return ['status' => 'critical', 'message' => 'Typecho database object not initialized or failed to get instance.'];
30 + }
31 +
32 + // 获取数据库实例
33 + $db = \Typecho\Db::get();
34 +
35 + // 尝试执行一个简单的查询来验证连接和权限
36 + // Typecho 的 Db 对象没有直接的 `query` 方法用于简单SQL,
37 + // 可以通过 `fetchRow` 或 `fetchObject` 来执行 select 语句
38 + // 或者直接通过 Typecho_Db_Adapter::test() 方法 (如果存在且公开的话)
39 + // 这里我们尝试查询一个 Typecho 常用表,例如 'typecho_options'
40 +
41 + // 注意: Typecho 的 Db::query 方法返回的是结果集对象,不是直接布尔值
42 + // 我们需要确保查询没有抛出异常,并能获取到结果。
43 +
44 + // 修正:直接从 $db 对象获取前缀
45 + $optionsTableName = $db->getPrefix() . 'options';
46 +
47 + // 尝试查询一条数据,确认连接活跃且表可访问
48 + $db->fetchRow($db->select()->from($optionsTableName)->limit(1));
49 +
50 + return ['status' => 'healthy', 'message' => 'Database connection successful and query executed.'];
51 +
52 + } catch (\Typecho\Db\Exception $e) {
53 + return ['status' => 'critical', 'message' => 'Database connection failed: ' . $e->getMessage()];
54 + } catch (Exception $e) {
55 + return ['status' => 'critical', 'message' => 'An unexpected error occurred during database check: ' . $e->getMessage()];
56 + }
57 + }
58 + $status['checks']['database'] = checkDatabaseConnection();
59 + if ($status['checks']['database']['status'] === 'critical') {
60 + $status['overall'] = 'unhealthy';
61 + }
62 +
63 + // --- 文件系统权限检查 ---
64 + function checkFilePermissions() {
65 + $paths = [
66 + 'usr/uploads' => 'write', // 用户上传目录
67 + 'config.inc.php' => 'read', // 配置文件
68 + 'var/Widget/Contents/Post/Edit.php' => 'read' // 核心文件示例
69 + ];
70 +
71 + $results = [];
72 + foreach ($paths as $path => $permissionType) {
73 + $fullPath = __DIR__ . DIRECTORY_SEPARATOR . $path;
74 + if (!file_exists($fullPath)) {
75 + $results[$path] = ['status' => 'warning', 'message' => "Path not found: {$path}"];
76 + continue;
77 + }
78 +
79 + $canAccess = false;
80 + if ($permissionType === 'read') {
81 + $canAccess = is_readable($fullPath);
82 + } elseif ($permissionType === 'write') {
83 + // 对于目录,检查是否可写入
84 + if (is_dir($fullPath)) {
85 + $testFile = $fullPath . DIRECTORY_SEPARATOR . uniqid('health_check_') . '.tmp';
86 + if (file_put_contents($testFile, 'test') !== false) {
87 + unlink($testFile); // 删除测试文件
88 + $canAccess = true;
89 + }
90 + } else {
91 + $canAccess = is_writable($fullPath);
92 + }
93 + }
94 +
95 + if ($canAccess) {
96 + $results[$path] = ['status' => 'healthy', 'message' => "{$permissionType} access OK."];
97 + } else {
98 + $results[$path] = ['status' => 'critical', 'message' => "{$permissionType} access FAILED."];
99 + }
100 + }
101 + return $results;
102 + }
103 + $status['checks']['file_permissions'] = checkFilePermissions();
104 + foreach ($status['checks']['file_permissions'] as $pathStatus) {
105 + if ($pathStatus['status'] === 'critical') {
106 + $status['overall'] = 'unhealthy';
107 + break;
108 + }
109 + }
110 +
111 + // --- 核心文件可访问性检查 (HTTP请求) ---
112 + // 注意:这个检查需要服务器环境支持CURL或file_get_contents对外部URL的访问
113 + // 并且检查的是Web服务器是否能正常提供这些文件,而不是PHP脚本本身是否能访问
114 + function checkCoreFileAccess() {
115 + $baseUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://{$_SERVER['HTTP_HOST']}";
116 + // 假设这些文件是可以通过HTTP访问的Typecho核心文件
117 + // 这里需要根据你的Typecho安装路径和实际可访问的文件来调整
118 + $coreFiles = [
119 + '/index.php', // 首页
120 + '/admin/index.php' // 后台登录页
121 + ];
122 +
123 + $results = [];
124 + foreach ($coreFiles as $file) {
125 + $url = $baseUrl . $file;
126 + $ch = curl_init($url);
127 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
128 + curl_setopt($ch, CURLOPT_TIMEOUT, 5); // 5秒超时
129 + curl_setopt($ch, CURLOPT_NOBODY, true); // 只获取头部信息,不下载内容
130 + curl_exec($ch);
131 + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
132 + curl_close($ch);
133 +
134 + if ($httpCode >= 200 && $httpCode < 400) {
135 + $results[$file] = ['status' => 'healthy', 'message' => "Accessible (HTTP {$httpCode})."];
136 + } else {
137 + $results[$file] = ['status' => 'critical', 'message' => "Not accessible (HTTP {$httpCode})."];
138 + }
139 + }
140 + return $results;
141 + }
142 + // 仅在服务器支持CURL的情况下执行此检查
143 + if (function_exists('curl_init')) {
144 + $status['checks']['core_file_access'] = checkCoreFileAccess();
145 + foreach ($status['checks']['core_file_access'] as $fileStatus) {
146 + if ($fileStatus['status'] === 'critical') {
147 + $status['overall'] = 'unhealthy';
148 + break;
149 + }
150 + }
151 + } else {
152 + $status['checks']['core_file_access'] = ['status' => 'warning', 'message' => 'cURL extension is not enabled, skipping core file HTTP access check.'];
153 + }
154 +
155 + // 输出 JSON 结果
156 + echo json_encode($status, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
157 +
158 + ?>
Newer Older