liueic revised this gist . 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