Last active 1749263038

用于Typecho健康检查的php页面

health_check.php Raw
1<?php
2
3// 禁用错误报告,避免在健康检查中暴露敏感信息
4error_reporting(0);
5ini_set('display_errors', 0);
6
7header('Content-Type: application/json');
8
9$status = [
10 'overall' => 'healthy',
11 'checks' => []
12];
13
14// --- 数据库连接检查 (修改为适应Typecho现代化配置) ---
15function 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();
59if ($status['checks']['database']['status'] === 'critical') {
60 $status['overall'] = 'unhealthy';
61}
62
63// --- 文件系统权限检查 ---
64function 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();
104foreach ($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脚本本身是否能访问
114function 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的情况下执行此检查
143if (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 结果
156echo json_encode($status, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
157
158?>