-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup.php
More file actions
executable file
·336 lines (309 loc) · 20 KB
/
setup.php
File metadata and controls
executable file
·336 lines (309 loc) · 20 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
<?php
// setup.php - Linker Monitor Interactive Installer v2.0
error_reporting(0); // Mute warnings during initial checks
// --- [新增] 环境检查 ---
$php_version_ok = version_compare(PHP_VERSION, '7.4.0', '>=');
$pdo_ok = extension_loaded('pdo');
$pdo_sqlite_ok = extension_loaded('pdo_sqlite');
$pdo_mysql_ok = extension_loaded('pdo_mysql');
$pdo_pgsql_ok = extension_loaded('pdo_pgsql');
$env_checks_passed = $php_version_ok && $pdo_ok && ($pdo_sqlite_ok || $pdo_mysql_ok || $pdo_pgsql_ok);
// --------------------
// 将配置文件路径指向项目的根目录
$configFile = __DIR__ . '/config.php';
$step = 1;
$error_message = '';
// If config exists, installation is complete.
if (file_exists($configFile)) {
$step = 'complete';
}
// Handle self-destruct action
if (isset($_POST['action']) && $_POST['action'] === 'delete_setup') {
if (unlink(__FILE__)) {
$message = "安装程序 setup.php 已成功删除。";
$step = 'deleted';
} else {
$error_message = "无法自动删除 setup.php。请检查文件权限并手动删除它。";
$step = 'complete';
}
}
// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit_setup'])) {
$db_type = $_POST['db_type'] ?? 'sqlite';
$admin_user = trim($_POST['admin_user'] ?? '');
$admin_pass = $_POST['admin_pass'] ?? '';
$site_name = trim($_POST['site_name'] ?? '灵刻监控');
// --- Data Validation ---
if (empty($admin_user) || empty($admin_pass)) {
$error_message = "管理员用户名和密码不能为空。";
} elseif (empty($site_name)) {
$error_message = "站点名称不能为空。";
} else {
// --- Test Database Connection ---
try {
$db_path = '';
$pg_host = ''; $pg_port = ''; $pg_dbname = ''; $pg_user = ''; $pg_pass = '';
$mysql_host = ''; $mysql_port = ''; $mysql_dbname = ''; $mysql_user = ''; $mysql_pass = '';
if ($db_type === 'sqlite') {
$db_path = $_POST['sqlite_path'];
if (empty($db_path)) throw new Exception("SQLite 数据库路径不能为空。");
$db_dir = dirname($db_path);
if (!is_dir($db_dir)) {
if (!mkdir($db_dir, 0755, true)) {
throw new Exception("无法创建 SQLite 数据库目录 '{$db_dir}'。");
}
}
if (!is_writable($db_dir)) throw new Exception("SQLite 数据库目录 '{$db_dir}' 不可写。");
$pdo = new PDO('sqlite:' . $db_path);
} elseif ($db_type === 'pgsql') {
$pg_host = $_POST['pg_host'];
$pg_port = $_POST['pg_port'];
$pg_dbname = $_POST['pg_dbname'];
$pg_user = $_POST['pg_user'];
$pg_pass = $_POST['pg_pass'];
if (empty($pg_host) || empty($pg_dbname) || empty($pg_user)) {
throw new Exception("PostgreSQL 主机、数据库名和用户名不能为空。");
}
$dsn = "pgsql:host={$pg_host};port={$pg_port};dbname={$pg_dbname}";
$pdo = new PDO($dsn, $pg_user, $pg_pass);
} else { // mysql
$mysql_host = $_POST['mysql_host'];
$mysql_port = $_POST['mysql_port'];
$mysql_dbname = $_POST['mysql_dbname'];
$mysql_user = $_POST['mysql_user'];
$mysql_pass = $_POST['mysql_pass'];
if (empty($mysql_host) || empty($mysql_dbname) || empty($mysql_user)) {
throw new Exception("MySQL 主机、数据库名和用户名不能为空。");
}
$dsn = "mysql:host={$mysql_host};port={$mysql_port};dbname={$mysql_dbname};charset=utf8mb4";
$pdo = new PDO($dsn, $mysql_user, $mysql_pass);
}
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// --- Connection Successful, Proceed with Setup ---
// 1. Create config.php content
$config_content = "<?php\n\n";
$config_content .= "// Auto-generated by setup.php on " . date('Y-m-d H:i:s') . "\n\n";
$config_content .= "\$db_config = [\n";
$config_content .= " 'type' => '{$db_type}',\n";
$config_content .= " 'sqlite' => [\n";
$config_content .= " 'path' => '" . addslashes($db_path) . "'\n";
$config_content .= " ],\n";
$config_content .= " 'pgsql' => [\n";
$config_content .= " 'host' => '{$pg_host}',\n";
$config_content .= " 'port' => '{$pg_port}',\n";
$config_content .= " 'dbname' => '{$pg_dbname}',\n";
$config_content .= " 'user' => '{$pg_user}',\n";
$config_content .= " 'password' => '{$pg_pass}'\n";
$config_content .= " ],\n";
$config_content .= " 'mysql' => [\n";
$config_content .= " 'host' => '{$mysql_host}',\n";
$config_content .= " 'port' => '{$mysql_port}',\n";
$config_content .= " 'dbname' => '{$mysql_dbname}',\n";
$config_content .= " 'user' => '{$mysql_user}',\n";
$config_content .= " 'password' => '{$mysql_pass}'\n";
$config_content .= " ]\n";
$config_content .= "];\n";
if (file_put_contents($configFile, $config_content) === false) {
throw new Exception("无法写入 config.php 文件。请检查目录权限。");
}
// 3. Initialize database schema
if ($db_type === 'pgsql') {
$pdo->exec("
CREATE TABLE IF NOT EXISTS servers (
id VARCHAR(255) PRIMARY KEY, name VARCHAR(255) NOT NULL, ip VARCHAR(255),
latitude REAL, longitude REAL, intro TEXT, tags TEXT, secret VARCHAR(255),
cpu_cores INT, cpu_model VARCHAR(255), mem_total BIGINT, disk_total BIGINT,
expiry_date BIGINT, price_usd_yearly REAL,
country_code VARCHAR(10), system TEXT, arch VARCHAR(255)
);
CREATE TABLE IF NOT EXISTS server_stats (
id SERIAL PRIMARY KEY, server_id VARCHAR(255) NOT NULL, timestamp BIGINT NOT NULL,
cpu_usage REAL, mem_usage_percent REAL, disk_usage_percent REAL,
net_up_speed BIGINT, net_down_speed BIGINT, total_up BIGINT, total_down BIGINT,
uptime VARCHAR(255), load_avg REAL, processes INT, connections INT
);
CREATE INDEX IF NOT EXISTS server_time_idx ON server_stats (server_id, timestamp);
CREATE TABLE IF NOT EXISTS server_status (id VARCHAR(255) PRIMARY KEY, is_online BOOLEAN NOT NULL DEFAULT false, last_checked BIGINT);
CREATE TABLE IF NOT EXISTS outages (id SERIAL PRIMARY KEY, server_id VARCHAR(255) NOT NULL, start_time BIGINT, end_time BIGINT, title VARCHAR(255), content TEXT);
CREATE TABLE IF NOT EXISTS settings (key VARCHAR(255) PRIMARY KEY, value TEXT);
CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, username VARCHAR(255) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL);
");
} else if ($db_type === 'mysql') {
$pdo->exec("
CREATE TABLE IF NOT EXISTS servers (
id VARCHAR(255) PRIMARY KEY, name VARCHAR(255) NOT NULL, ip VARCHAR(255),
latitude FLOAT, longitude FLOAT, intro TEXT, tags TEXT, secret VARCHAR(255),
cpu_cores INT, cpu_model VARCHAR(255), mem_total BIGINT, disk_total BIGINT,
expiry_date BIGINT, price_usd_yearly FLOAT,
country_code VARCHAR(10), system TEXT, arch VARCHAR(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS server_stats (
id INT AUTO_INCREMENT PRIMARY KEY, server_id VARCHAR(255) NOT NULL, timestamp BIGINT NOT NULL,
cpu_usage FLOAT, mem_usage_percent FLOAT, disk_usage_percent FLOAT,
net_up_speed BIGINT, net_down_speed BIGINT, total_up BIGINT, total_down BIGINT,
uptime VARCHAR(255), load_avg FLOAT, processes INT, connections INT,
INDEX server_time_idx (server_id, timestamp)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS server_status (id VARCHAR(255) PRIMARY KEY, is_online TINYINT(1) NOT NULL DEFAULT 0, last_checked BIGINT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS outages (id INT AUTO_INCREMENT PRIMARY KEY, server_id VARCHAR(255) NOT NULL, start_time BIGINT, end_time BIGINT, title VARCHAR(255), content TEXT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS settings (`key` VARCHAR(255) PRIMARY KEY, value TEXT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS users (id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(255) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
");
} else { // sqlite
$pdo->exec("
CREATE TABLE IF NOT EXISTS servers (
id TEXT PRIMARY KEY, name TEXT NOT NULL, ip TEXT, latitude REAL, longitude REAL, intro TEXT, tags TEXT, secret TEXT,
cpu_cores INTEGER, cpu_model TEXT, mem_total INTEGER, disk_total INTEGER,
expiry_date INTEGER, price_usd_yearly REAL,
country_code TEXT, system TEXT, arch TEXT
);
CREATE TABLE IF NOT EXISTS server_stats (
id INTEGER PRIMARY KEY AUTOINCREMENT, server_id TEXT NOT NULL, timestamp INTEGER NOT NULL,
cpu_usage REAL, mem_usage_percent REAL, disk_usage_percent REAL,
net_up_speed INTEGER, net_down_speed INTEGER, total_up INTEGER, total_down INTEGER,
uptime TEXT, load_avg REAL, processes INTEGER, connections INTEGER
);
CREATE INDEX IF NOT EXISTS server_time_idx ON server_stats (server_id, timestamp);
CREATE TABLE IF NOT EXISTS server_status (id TEXT PRIMARY KEY, is_online INTEGER DEFAULT 0, last_checked INTEGER);
CREATE TABLE IF NOT EXISTS outages (id INTEGER PRIMARY KEY AUTOINCREMENT, server_id TEXT, start_time INTEGER, end_time INTEGER, title TEXT, content TEXT);
CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT);
CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password TEXT NOT NULL);
");
}
// 4. Create admin user and save site name
$hashed_password = password_hash($admin_pass, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
$stmt->execute([$admin_user, $hashed_password]);
$stmt = $pdo->prepare("INSERT INTO settings (`key`, value) VALUES (?, ?)");
$stmt->execute(['site_name', $site_name]);
$step = 'complete';
} catch (Exception $e) {
$error_message = "设置失败: " . $e->getMessage();
if(file_exists($configFile)) {
unlink($configFile); // Clean up on failure
}
}
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>灵刻监控 安装程序</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background-color: #f0f2f5; margin: 0; padding: 2rem; color: #333; display: flex; align-items: center; justify-content: center; min-height: 100vh; }
.container { max-width: 600px; width: 100%; margin: 0 auto; background: #fff; padding: 2.5rem; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.08); }
h1 { color: #111; margin-top: 0; text-align: center; }
.message { background: #28a745; color: #fff; padding: 1rem; border-radius: 5px; margin-bottom: 1.5rem; text-align: center; }
.error-message { background: #dc3545; color: #fff; padding: 1rem; border-radius: 5px; margin-bottom: 1.5rem; }
label { font-weight: 600; display: block; margin-bottom: 0.4rem; font-size: 0.9em; }
input[type="text"], input[type="number"], input[type="password"] { width: 100%; padding: 0.6rem; border: 1px solid #ced4da; border-radius: 4px; box-sizing: border-box; margin-bottom: 1rem; }
button { width: 100%; background-color: #007bff; color: #fff; border: none; padding: 0.8rem 1.2rem; border-radius: 5px; cursor: pointer; transition: background-color 0.2s; font-weight: 600; font-size: 1em; }
button:hover { background-color: #0056b3; }
button.delete { background-color: #dc3545; margin-top: 1rem; }
button.delete:hover { background-color: #c82333; }
.section { margin-bottom: 2rem; border-bottom: 1px solid #eee; padding-bottom: 2rem; }
.db-type-selector { display: flex; gap: 1rem; margin-bottom: 1rem; flex-wrap: wrap; }
a { color: #007bff; text-decoration: none; }
a:hover { text-decoration: underline; }
</style>
</head>
<body>
<div class="container">
<h1>灵刻监控 安装</h1>
<?php if (!$env_checks_passed): ?>
<div class="error-message">
<h4>环境不满足要求</h4>
<ul>
<?php if (!$php_version_ok): ?><li>PHP 版本需要 >= 7.4 (当前: <?php echo PHP_VERSION; ?>)</li><?php endif; ?>
<?php if (!$pdo_ok): ?><li>缺少 PHP 扩展: pdo</li><?php endif; ?>
<?php if (!$pdo_sqlite_ok && !$pdo_mysql_ok && !$pdo_pgsql_ok): ?>
<li>至少需要以下 PHP 扩展中的一个: pdo_sqlite, pdo_mysql, pdo_pgsql</li>
<?php endif; ?>
</ul>
<p>请安装所需扩展后再刷新此页面。</p>
</div>
<?php endif; ?>
<?php if ($step === 'complete' || $step === 'deleted'): ?>
<div class="message">
<?php echo $step === 'deleted' ? htmlspecialchars($message) : '安装成功!'; ?>
</div>
<p>您的监控面板已准备就绪。请使用您刚刚创建的管理员账户登录。</p>
<p style="text-align:center; margin-top: 2rem;">
<a href="./admin/index.php"><button>前往登录页面</button></a>
</p>
<?php if ($step !== 'deleted'): ?>
<form method="post" onsubmit="return confirm('为了安全,您确定要删除此安装文件吗?');">
<input type="hidden" name="action" value="delete_setup">
<button type="submit" class="delete">删除安装程序 (setup.php)</button>
</form>
<?php endif; ?>
<?php elseif ($env_checks_passed): // [修改] 仅在环境检查通过时显示表单 ?>
<?php if ($error_message): ?><p class="error-message"><?php echo htmlspecialchars($error_message); ?></p><?php endif; ?>
<form method="post">
<div class="section">
<h2>1. 站点设置</h2>
<label for="site_name">站点名称</label>
<input type="text" id="site_name" name="site_name" required value="<?php echo htmlspecialchars($_POST['site_name'] ?? '灵刻监控'); ?>">
</div>
<div class="section">
<h2>2. 数据库设置</h2>
<div class="db-type-selector">
<label><input type="radio" name="db_type" value="sqlite" checked onchange="toggleDBFields()"> SQLite</label>
<label><input type="radio" name="db_type" value="pgsql" onchange="toggleDBFields()"> PostgreSQL</label>
<label><input type="radio" name="db_type" value="mysql" onchange="toggleDBFields()"> MySQL</label>
</div>
<div id="sqlite-fields">
<label for="sqlite_path">数据库文件路径</label>
<input type="text" id="sqlite_path" name="sqlite_path" value="<?php echo htmlspecialchars($_POST['sqlite_path'] ?? __DIR__ . '/linker.db'); ?>">
</div>
<div id="pgsql-fields" style="display:none;">
<label for="pg_host">主机</label>
<input type="text" id="pg_host" name="pg_host" value="<?php echo htmlspecialchars($_POST['pg_host'] ?? 'localhost'); ?>">
<label for="pg_port">端口</label>
<input type="number" id="pg_port" name="pg_port" value="<?php echo htmlspecialchars($_POST['pg_port'] ?? '5432'); ?>">
<label for="pg_dbname">数据库名称</label>
<input type="text" id="pg_dbname" name="pg_dbname" value="<?php echo htmlspecialchars($_POST['pg_dbname'] ?? ''); ?>">
<label for="pg_user">用户名</label>
<input type="text" id="pg_user" name="pg_user" value="<?php echo htmlspecialchars($_POST['pg_user'] ?? ''); ?>">
<label for="pg_pass">密码</label>
<input type="password" id="pg_pass" name="pg_pass">
</div>
<div id="mysql-fields" style="display:none;">
<label for="mysql_host">主机</label>
<input type="text" id="mysql_host" name="mysql_host" value="<?php echo htmlspecialchars($_POST['mysql_host'] ?? 'localhost'); ?>">
<label for="mysql_port">端口</label>
<input type="number" id="mysql_port" name="mysql_port" value="<?php echo htmlspecialchars($_POST['mysql_port'] ?? '3306'); ?>">
<label for="mysql_dbname">数据库名称</label>
<input type="text" id="mysql_dbname" name="mysql_dbname" value="<?php echo htmlspecialchars($_POST['mysql_dbname'] ?? ''); ?>">
<label for="mysql_user">用户名</label>
<input type="text" id="mysql_user" name="mysql_user" value="<?php echo htmlspecialchars($_POST['mysql_user'] ?? ''); ?>">
<label for="mysql_pass">密码</label>
<input type="password" id="mysql_pass" name="mysql_pass">
</div>
</div>
<div class="section">
<h2>3. 创建管理员账户</h2>
<label for="admin_user">管理员用户名</label>
<input type="text" id="admin_user" name="admin_user" required value="<?php echo htmlspecialchars($_POST['admin_user'] ?? ''); ?>">
<label for="admin_pass">管理员密码</label>
<input type="password" id="admin_pass" name="admin_pass" required>
</div>
<button type="submit" name="submit_setup">完成安装</button>
</form>
<?php endif; ?>
</div>
<script>
function toggleDBFields() {
const dbType = document.querySelector('input[name="db_type"]:checked').value;
document.getElementById('sqlite-fields').style.display = (dbType === 'sqlite') ? 'block' : 'none';
document.getElementById('pgsql-fields').style.display = (dbType === 'pgsql') ? 'block' : 'none';
document.getElementById('mysql-fields').style.display = (dbType === 'mysql') ? 'block' : 'none';
}
// Initialize visibility on page load
document.addEventListener('DOMContentLoaded', toggleDBFields);
</script>
</body>
</html>