11<?php
2+ //define('ABSPATH', dirname(__DIR__));
3+ // install.php
24require 'vendor/autoload.php ' ;
35
46use BaconQrCode \Renderer \GDLibRenderer ;
79
810if (file_exists ('install.lock ' )) {
911 echo "install.lock 系统已安装,请勿重复安装。如发生错误/需要重新安装,请删除 install.lock \n" ;
10- exit ;
12+ exit ; // 退出脚本
1113}
1214if (file_exists ('redis.lock ' )) {
1315 unlink ('redis.lock ' );
1416}
1517if (file_exists ('qrcode.png ' )) {
1618 unlink ('qrcode.png ' );
1719}
18-
20+ // 检查是否已经提交了表单
1921if ($ _SERVER ["REQUEST_METHOD " ] == "POST " ) {
22+ // 获取用户输入的数据库连接信息
2023 $ dbHost = trim (filter_var ($ _POST ['dbHost ' ] ?? '127.0.0.1 ' , FILTER_VALIDATE_IP ) ?: '127.0.0.1 ' );
2124 $ dbName = trim (htmlspecialchars ($ _POST ['dbName ' ]));
2225 $ dbUser = trim (htmlspecialchars ($ _POST ['dbUser ' ]));
2528 $ password = trim (htmlspecialchars ($ _POST ['password ' ]));
2629 $ adminEmail =
trim (
filter_var (
$ _POST [
'admin_email ' ],
FILTER_VALIDATE_EMAIL ) ?:
'[email protected] ' );
2730 $ inputPassword = trim (htmlspecialchars ($ _POST ['customPassword ' ]));
28- $ productId = '1 ' ;
31+ $ productId = '1 ' ; // 默认产品ID为1,不显示在表单中
2932 $ authMethod = $ _POST ['authMethod ' ];
30-
33+ // 处理不同认证方法
3134 switch ($ authMethod ) {
3235 case 'password ' :
3336 if (empty ($ inputPassword )) {
6164 echo "redis.lock 文件已创建。 \n" ;
6265 }
6366
67+ // 尝试连接到数据库
6468 $ mysqli = new mysqli ($ dbHost , $ dbUser , $ dbPass , $ dbName );
69+
70+ // 检查连接是否成功
6571 if ($ mysqli ->connect_error ) {
6672 die ('数据库连接失败: ' . $ mysqli ->connect_error );
6773 }
6874
75+ // 读取 SQL 文件内容
6976 $ sqlContent = file_get_contents ('install.sql ' );
77+
78+ // 执行 SQL 语句
7079 if ($ mysqli ->multi_query ($ sqlContent )) {
71- do {} while ($ mysqli ->next_result ());
72-
73- $ configFile = "<?php \nreturn [ \n 'db' => [ \n 'host' => ' {$ dbHost }', \n 'dbname' => ' {$ dbName }', \n 'user' => ' {$ dbUser }', \n 'pass' => ' {$ dbPass }' \n ], \n 'auth' => [ \n 'account' => ' {$ account }', \n 'password' => ' {$ password }', \n 'product_id' => ' {$ productId }' \n ], \n 'mail' => [ \n 'host' => '', \n 'port' => 465, \n 'username' => '', \n 'password' => '', \n 'from' => ' {$ adminEmail }', \n 'encryption' => 'ssl' \n ] \n]; " ;
80+ // 等待所有查询执行完成
81+ do {
82+ // 无需进一步处理结果
83+ } while ($ mysqli ->next_result ());
84+ echo "创建config.php文件... \n" ;
85+ // 创建 config.php 文件
86+ $ configFile = "<?php \n// config.php \nreturn [ \n 'db' => [ \n 'host' => ' {$ dbHost }', \n 'dbname' => ' {$ dbName }', \n 'user' => ' {$ dbUser }', \n 'pass' => ' {$ dbPass }' \n ], \n 'auth' => [ \n 'account' => ' {$ account }', \n 'password' => ' {$ password }', \n 'product_id' => ' {$ productId }' \n ] \n]; " ;
7487 file_put_contents ('config.php ' , $ configFile );
75-
88+ echo " config.php 文件已创建。 \n" ;
7689 $ config = require 'config.php ' ;
7790 if (file_exists ('qrcode.png ' )) {
7891 unlink ('qrcode.png ' );
7992 }
93+ // Database connection
94+ $ host = $ config ['db ' ]['host ' ];
95+ $ dbname = $ config ['db ' ]['dbname ' ];
96+ $ user = $ config ['db ' ]['user ' ];
97+ $ pass = $ config ['db ' ]['pass ' ];
8098
8199 try {
82- $ pdo = new PDO ("mysql:host= {$ config ['db ' ]['host ' ]};dbname= {$ config ['db ' ]['dbname ' ]};charset=utf8 " ,
83- $ config ['db ' ]['user ' ], $ config ['db ' ]['pass ' ]);
100+ $ pdo = new PDO ("mysql:host= $ host;dbname= $ dbname;charset=utf8 " , $ user , $ pass );
84101 $ pdo ->setAttribute (PDO ::ATTR_ERRMODE , PDO ::ERRMODE_EXCEPTION );
85102 } catch (PDOException $ e ) {
86- die ("数据库连接失败 : " . $ e ->getMessage ());
103+ die ("Could not connect to the database : " . $ e ->getMessage ());
87104 }
88105
89- if ($ useTotp ) {
106+ if ($ useTotp && ! $ usePassword ) { // 生成一个新的TOTP密钥
90107 $ totp = TOTP ::create ();
91108 $ totp ->setLabel ($ adminEmail );
92- $ totp ->setIssuer ('TuanICP ' );
109+ $ totp ->setIssuer ('TuanICP ' );// 获取密钥和二维码URL
93110 $ secret = $ totp ->getSecret ();
94- $ qrCodeUrl = $ totp ->getProvisioningUri ();
95-
96- $ renderer = new GDLibRenderer (400 );
97- $ writer = new Writer ($ renderer );
98- file_put_contents ('qrcode.png ' , $ writer ->writeString ($ qrCodeUrl ));
111+ $ qrCodeUrl = $ totp ->getProvisioningUri ();// 将TOTP密钥和用户信息存储到数据库
112+ $ query = "INSERT INTO admin (email, totp_secret ,totp_enabled) VALUES (?, ?, ?) " ;
113+ $ stmt = $ pdo ->prepare ($ query );
114+ $ stmt ->execute ([$ adminEmail , $ secret , '1 ' ]);// 创建 GDLibRenderer 实例,设置二维码大小
115+ $ renderer = new GDLibRenderer (400 );// 创建 Writer 实例
116+ $ writer = new Writer ($ renderer );// 生成并保存二维码图片
117+ $ qrcode_image = $ writer ->writeString ($ qrCodeUrl );// 将 QRCode URL 转换为图像
118+ file_put_contents ('qrcode.png ' , $ qrcode_image );// 保存为 qrcode.png 文件
99119 }
100-
101120 if ($ usePassword && !$ useTotp ) {
102- $ stmt = $ pdo ->prepare ("INSERT INTO admin (email, password, password_enabled) VALUES (?, ?, ?) " );
121+ $ query = "INSERT INTO admin (email, password, password_enabled) VALUES (?, ?, ?) " ;
122+ $ stmt = $ pdo ->prepare ($ query );
103123 $ stmt ->execute ([$ adminEmail , $ customPassword , '1 ' ]);
104- } elseif ($ useTotp && !$ usePassword ) {
105- $ stmt = $ pdo ->prepare ("INSERT INTO admin (email, totp_secret, totp_enabled) VALUES (?, ?, ?) " );
106- $ stmt ->execute ([$ adminEmail , $ secret , '1 ' ]);
107- } elseif ($ usePassword && $ useTotp ) {
108- $ stmt = $ pdo ->prepare ("INSERT INTO admin (email, password, totp_secret, totp_enabled, password_enabled) VALUES (?, ?, ?, ?, ?) " );
124+ }
125+ if ($ usePassword && $ useTotp ) {
126+ $ totp = TOTP ::create ();
127+ $ totp ->setLabel ($ adminEmail );
128+ $ totp ->setIssuer ('TuanICP ' );// 获取密钥和二维码URL
129+ $ secret = $ totp ->getSecret ();
130+ $ qrCodeUrl = $ totp ->getProvisioningUri ();// 将TOTP密钥和用户信息存储到数据库
131+ $ query = "INSERT INTO admin (email, password, totp_secret, totp_enabled, password_enabled) VALUES (?, ?, ?, ?, ?) " ;
132+ $ stmt = $ pdo ->prepare ($ query );
109133 $ stmt ->execute ([$ adminEmail , $ customPassword , $ secret , '1 ' , '1 ' ]);
134+ $ renderer = new GDLibRenderer (400 );// 创建 Writer 实例
135+ $ writer = new Writer ($ renderer );// 生成并保存二维码图片
136+ $ qrcode_image = $ writer ->writeString ($ qrCodeUrl );// 将 QRCode URL 转换为图像
137+ file_put_contents ('qrcode.png ' , $ qrcode_image );// 保存为 qrcode.png 文件
110138 }
111139
112- file_put_contents ('install.lock ' , 'installed ' );
113- echo "安装成功!请删除安装文件! \n" ;
140+ echo "安装成功,请删除安装文件!请前往phpMyAdmin进入数据库或网站后台修改网站配置信息! \n" ;
141+ if ($ useTotp === true ) {
142+ // 设置HTTP缓存控制头,确保在发送任何输出之前设置
143+ header ("Cache-Control: no-cache, no-store, must-revalidate " ); // HTTP 1.1.
144+ header ("Pragma: no-cache " ); // HTTP 1.0.
145+ header ("Expires: 0 " ); // Proxies.
114146
115- if ($ useTotp && file_exists ('qrcode.png ' )) {
116- echo '<div><img src="qrcode.png"></div> ' ;
117- echo "<h2>请使用TOTP应用扫描二维码</h2> " ;
147+ if (file_exists ('qrcode.png ' )) {
148+ echo '<div><img src="qrcode.png"></div> ' ;
149+ echo "<h2>二维码已生成,请使用TOTP应用扫描二维码绑定。如果没有TOTP应用,请自行寻找并下载安装“Free OTP”。</h2><br>
150+ <h3>此应用是免费且开源的,并且Android(安卓)和IOS(苹果)平台都支持。</h3> \n" ;
151+ } else {
152+ if (isset ($ qrCodeUrl ) && isset ($ secret )) {
153+ echo "<h2>二维码生成失败,请手动将TOTP的URL生成为二维码导入到设备,或使用TOTP密钥手动输入:</h2><br> " ;
154+ echo "<h3>TOTP的URL为:<a href=' $ qrCodeUrl'> $ qrCodeUrl</a></h3><br> " ;
155+ echo "<h3>TOTP密钥为:</h3><br> " ;
156+ echo "<textarea> $ secret</textarea> " ;
157+ } else {
158+ echo "TOTP密钥为空。环境可能未被正确设置,请检查! " ;
159+ phpinfo ();
160+ exit ;
161+ }
162+ }
118163 }
164+
165+ // 逻辑代码执行完毕后,创建 install.lock 文件
166+ file_put_contents ('install.lock ' , 'installed ' );
167+ echo "install.lock 文件已创建。 \n" ;
119168 exit ;
120169 } else {
121- die ("数据库安装失败: " . $ mysqli ->error );
170+ // 输出错误信息
171+ $ errorInfo = $ mysqli ->error ;
172+
173+ // 检查错误信息中是否包含“Access denied”来确定是否是用户名或密码错误
174+ if (strpos ($ errorInfo , "Access denied " ) !== false ) {
175+ echo "数据库安装失败。错误信息:用户名或密码错误。 " ;
176+ } else {
177+ // 如果不是用户名或密码错误,显示其他错误信息
178+ echo "数据库安装失败。错误信息: " . $ errorInfo ;
179+ }
180+
122181 }
182+
183+ // 关闭数据库连接
184+ $ mysqli ->close ();
123185}
124186?>
125187
126188<!DOCTYPE html>
127- <html lang="zh-CN ">
189+ <html lang="en ">
128190<head>
129191 <meta charset="UTF-8">
130- <title>系统安装向导</title>
131- <style>
132- body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
133- label { display: inline-block; width: 150px; margin-bottom: 10px; }
134- input, select { padding: 8px; width: 300px; }
135- #passwordDiv { margin-top: 15px; }
136- </style>
192+ <title>数据库安装</title>
193+ <link rel="stylesheet" href="css/install.css">
137194</head>
138195<body>
139- <h1>系统安装向导</h1>
140- <form method="post">
141- <label for="dbHost">数据库地址:</label>
142- <input type="text" id="dbHost" name="dbHost" value="127.0.0.1" required><br>
143-
144- <label for="dbName">数据库名:</label>
145- <input type="text" id="dbName" name="dbName" required><br>
146-
147- <label for="dbUser">数据库用户名:</label>
148- <input type="text" id="dbUser" name="dbUser" required><br>
149-
150- <label for="dbPass">数据库密码:</label>
151- <input type="password" id="dbPass" name="dbPass" required><br>
152-
153- <label for="admin_email">管理员邮箱:</label>
154- <input type="email" id="admin_email" name="admin_email" value="
[email protected] " required><br>
155-
156- <label for="authMethod">认证方式:</label>
157- <select id="authMethod" name="authMethod" required>
158- <option value="password">密码认证</option>
159- <option value="totp">TOTP认证</option>
160- <option value="both">双重认证</option>
161- </select><br>
162-
163- <div id="passwordDiv">
164- <label for="customPassword">设置密码:</label>
165- <input type="password" id="customPassword" name="customPassword">
166- </div>
167-
168- <label><input type="checkbox" name="redis" value="1"> 启用Redis</label><br><br>
169-
170- <input type="submit" value="开始安装" style="padding: 10px 20px;">
171- </form>
172-
173- <script>
174- document.getElementById('authMethod').addEventListener('change', function() {
175- document.getElementById('passwordDiv').style.display =
176- (this.value === 'totp') ? 'none' : 'block';
196+ <h1>数据库安装</h1>
197+ <form action="install.php" method="post">
198+ <label for="dbHost">数据库地址:</label>
199+ <input type="text" id="dbHost" name="dbHost" value="127.0.0.1"><br><br>
200+ <label for="dbName">数据库名:</label>
201+ <input type="text" id="dbName" name="dbName" required><br><br>
202+ <label for="dbUser">数据库用户名:</label>
203+ <input type="text" id="dbUser" name="dbUser" required><br><br>
204+ <label for="dbPass">数据库密码:</label>
205+ <input type="password" id="dbPass" name="dbPass" required><br><br>
206+ <label for="account">授权账号:</label>
207+ <input type="text" id="account" name="account" required><br><br>
208+ <label for="password">授权密码:</label>
209+ <input type="password" id="password" name="password" required><br><br>
210+ <label for="redis">是否启用Redis:</label>
211+ <input type="checkbox" id="redis" name="redis" value="1"><br><br>
212+ <label for="admin_email">管理员邮箱:</label>
213+ <input type="text" name="admin_email" value="
[email protected] " required><br><br>
214+ <label for="authMethod">密码类型:</label>
215+ <p>不懂或不清楚什么意思,请选择“传统密码”</p>
216+ <select id="authMethod" name="authMethod" required>
217+ <option value="password">传统密码</option>
218+ <option value="totp">TOTP</option>
219+ <option value="both">同时支持</option>
220+ </select><br><br>
221+ <div id="passwordDiv" style="display:block;">
222+ <label for="customPassword">自定义密码:</label>
223+ <input type="password" id="customPassword" name="customPassword">
224+ </div>
225+ <div style="display:block;">
226+ <label for="testData">测试数据:</label>
227+ <p>测试数据用于快速测试网站功能。完整数据是直接将开发环境的数据库dump下来。</p>
228+ <p>精简数据是从开发环境导出的数据,仅包含部分必要数据。</p>
229+ <p>不导入测试数据将仅创建数据库结构,不导入任何数据。</p>
230+ <p>此选项目前没用。</p>
231+ <select id="testData" name="testData" required>
232+ <option value="full">完整测试数据</option>
233+ <option value="must">精简测试数据</option>
234+ <option value="none">不导入测试数据</option>
235+ </select>
236+ </div>
237+ <input type="submit" value="安装">
238+ </form>
239+
240+ <script>
241+ document.addEventListener('DOMContentLoaded', function () {
242+ document.getElementById('authMethod').addEventListener('input', function () {
243+ var authMethod = this.value;
244+ var passwordDiv = document.getElementById('passwordDiv');
245+ if (authMethod === 'password' || authMethod === 'both') {
246+ passwordDiv.style.display = 'block';
247+ } else {
248+ passwordDiv.style.display = 'none';
249+ }
177250 });
178- </script>
251+ });
252+ </script>
179253</body>
180- </html>
254+ </html>
0 commit comments