install.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. <?php
  2. /**
  3. * SSRPanel安装程序
  4. *
  5. * 安装完成后建议删除此文件
  6. * @author Heron
  7. */
  8. // error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
  9. // ini_set('display_errors', '1');
  10. // 定义目录分隔符
  11. define('DS', DIRECTORY_SEPARATOR);
  12. // 定义根目录
  13. define('ROOT_PATH', __DIR__ . DS . '..' . DS);
  14. // 定义应用目录
  15. define('APP_PATH', ROOT_PATH . 'app' . DS);
  16. // 安装包目录
  17. define('INSTALL_PATH', ROOT_PATH . 'sql' . DS);
  18. // 判断文件或目录是否有写的权限
  19. function is_really_writable($file)
  20. {
  21. if (DIRECTORY_SEPARATOR == '/' and @ ini_get("safe_mode") == false) {
  22. return is_writable($file);
  23. }
  24. if (!is_file($file) or ($fp = @fopen($file, "r+")) === false) {
  25. return false;
  26. }
  27. fclose($fp);
  28. return true;
  29. }
  30. // 写配置文件
  31. function write_ini_file($assoc_arr, $path, $has_sections = FALSE)
  32. {
  33. $content = "";
  34. if ($has_sections) {
  35. foreach ($assoc_arr as $key => $elem) {
  36. $content .= "[" . $key . "]\n";
  37. foreach ($elem as $key2 => $elem2) {
  38. if (is_array($elem2)) {
  39. for ($i = 0; $i < count($elem2); $i++) {
  40. $content .= $key2 . "[] = \"" . $elem2[$i] . "\"\n";
  41. }
  42. } else if ($elem2 == "")
  43. $content .= $key2 . " = \n";
  44. else
  45. $content .= $key2 . " = \"" . $elem2 . "\"\n";
  46. }
  47. }
  48. } else {
  49. foreach ($assoc_arr as $key => $elem) {
  50. if (is_array($elem)) {
  51. for ($i = 0; $i < count($elem); $i++) {
  52. $content .= $key2 . "[] = \"" . $elem[$i] . "\"\n";
  53. }
  54. } else if ($elem == "")
  55. $content .= $key2 . " = \n";
  56. else
  57. $content .= $key2 . " = \"" . $elem . "\"\n";
  58. }
  59. }
  60. if (!$handle = fopen($path, 'w')) {
  61. return false;
  62. }
  63. if (!fwrite($handle, $content)) {
  64. return false;
  65. }
  66. fclose($handle);
  67. return true;
  68. }
  69. $sitename = "SSRPanel";
  70. $link = array(
  71. 'github' => "https://github.com/ssrpanel/SSRPanel",
  72. 'wiki' => 'https://github.com/ssrpanel/SSRPanel/wiki',
  73. 'telegram' => 'https://t.me/ssrpanel',
  74. );
  75. // 检测目录是否存在
  76. $checkDirs = [
  77. 'vendor',
  78. ];
  79. //错误信息
  80. $errInfo = '';
  81. //数据库配置文件
  82. $ConfigFile = ROOT_PATH . '.env';
  83. //数据库标准配置文件
  84. $exampleConfigFile = ROOT_PATH . '.env.example';
  85. // 锁定的文件
  86. $lockFile = ROOT_PATH . '.env';
  87. if (is_file($lockFile)) {
  88. $errInfo = "当前已经安装{$sitename},如果需要重新安装,请手动移除.env文件";
  89. } else if (version_compare(PHP_VERSION, '7.1.0', '<')) {
  90. $errInfo = "当前版本(" . PHP_VERSION . ")过低,请使用PHP7.1以上版本";
  91. } else if (!is_file($exampleConfigFile)) {
  92. $errInfo = "缺失标准配置文件.env.example";
  93. } else if (!extension_loaded("PDO")) {
  94. $errInfo = "当前未开启PDO,无法进行安装";
  95. } else if (!is_really_writable(ROOT_PATH)) {
  96. $open_basedir = ini_get('open_basedir');
  97. if ($open_basedir) {
  98. $dirArr = explode(PATH_SEPARATOR, $open_basedir);
  99. if ($dirArr && in_array(__DIR__, $dirArr)) {
  100. $errInfo = '当前服务器因配置了open_basedir,导致无法读取应用根目录';
  101. }
  102. }
  103. if (!$errInfo) {
  104. $errInfo = '权限不足,无法写入配置文件.env';
  105. }
  106. } else {
  107. $dirArr = [];
  108. foreach ($checkDirs as $k => $v) {
  109. if (!is_dir(ROOT_PATH . $v)) {
  110. $errInfo = '根目录仅包含核心代码,请在根目录执行php composer.phar install安装依赖';
  111. break;
  112. }
  113. }
  114. }
  115. // 当前是POST请求
  116. if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST') {
  117. if ($errInfo) {
  118. echo $errInfo;
  119. exit;
  120. }
  121. $err = '';
  122. $APP_KEY = md5(time() . mt_rand(1, 1000000));
  123. $DB_HOST = isset($_POST['mysqlHost']) ? $_POST['mysqlHost'] : '127.0.0.1';
  124. $DB_PORT = isset($_POST['mysqlHostport']) ? $_POST['mysqlHostport'] : 3306;
  125. $hostArr = explode(':', $DB_HOST);
  126. if (count($hostArr) > 1) {
  127. $DB_HOST = $hostArr[0];
  128. $DB_PORT = $hostArr[1];
  129. }
  130. $DB_USERNAME = isset($_POST['mysqlUsername']) ? $_POST['mysqlUsername'] : 'root';
  131. $DB_PASSWORD = isset($_POST['mysqlPassword']) ? $_POST['mysqlPassword'] : '';
  132. $DB_DATABASE = isset($_POST['mysqlDatabase']) ? $_POST['mysqlDatabase'] : 'ssrpanel';
  133. // $adminUsername = isset($_POST['adminUsername']) ? $_POST['adminUsername'] : 'admin';
  134. // $adminPassword = isset($_POST['adminPassword']) ? $_POST['adminPassword'] : 'admin';
  135. // $adminPasswordConfirmation = isset($_POST['adminPasswordConfirmation']) ? $_POST['adminPasswordConfirmation'] : 'admin';
  136. // $adminEmail = isset($_POST['adminEmail']) ? $_POST['adminEmail'] : '[email protected]';
  137. // if ($adminPassword !== $adminPasswordConfirmation) {
  138. // echo "两次输入的密码不一致";
  139. // exit;
  140. // } else if (!preg_match("/^\w+$/", $adminUsername)) {
  141. // echo "用户名只能输入字母、数字、下划线";
  142. // exit;
  143. // } else if (!preg_match("/^[\S]+$/", $adminPassword)) {
  144. // echo "密码不能包含空格";
  145. // exit;
  146. // } else if (strlen($adminUsername) < 3 || strlen($adminUsername) > 12) {
  147. // echo "用户名请输入3~12位字符";
  148. // exit;
  149. // } else if (strlen($adminPassword) < 6 || strlen($adminPassword) > 16 || stripos($adminPassword, ' ') !== false) {
  150. // echo "密码请输入6~16位字符,不能包含空格";
  151. // exit;
  152. // }
  153. try {
  154. //检测能否读取安装文件
  155. $sql = @file_get_contents(INSTALL_PATH . 'db.sql');
  156. if (!$sql) {
  157. throw new Exception("无法读取所需的sql/db.sql,请检查是否有读权限");
  158. }
  159. $pdo = new PDO("mysql:host={$DB_HOST};port={$DB_PORT}", $DB_USERNAME, $DB_PASSWORD, array(
  160. PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  161. PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
  162. ));
  163. //检测是否支持innodb存储引擎
  164. $pdoStatement = $pdo->query("SHOW VARIABLES LIKE 'innodb_version'");
  165. $result = $pdoStatement->fetch();
  166. if (!$result) {
  167. throw new Exception("当前数据库不支持innodb存储引擎,请开启后再重新尝试安装");
  168. }
  169. $pdo->query("CREATE DATABASE IF NOT EXISTS `{$DB_DATABASE}` CHARACTER SET utf8 COLLATE utf8_general_ci;");
  170. $pdo->query("USE `{$DB_DATABASE}`");
  171. $pdo->exec($sql);
  172. $config = @file_get_contents($exampleConfigFile);
  173. if (!$config) {
  174. throw new Exception("无法写入读取配置.env.example文件,请检查是否有读权限");
  175. }
  176. $callback = function ($matches) use ($APP_KEY, $DB_HOST, $DB_PORT, $DB_USERNAME, $DB_PASSWORD, $DB_DATABASE) {
  177. $field = $matches[1];
  178. $replace = ${"{$field}"};
  179. return "{$matches[1]}={$replace}" . PHP_EOL;
  180. };
  181. $config = preg_replace_callback("/(APP_KEY|DB_HOST|DB_DATABASE|DB_USERNAME|DB_PASSWORD|DB_PORT)=(.*)(\s+)/", $callback, $config);
  182. //检测能否成功写入数据库配置
  183. $result = @file_put_contents($ConfigFile, $config);
  184. if (!$result) {
  185. throw new Exception("无法写入数据库信息到.env文件,请检查是否有写权限");
  186. }
  187. echo "success";
  188. } catch (PDOException $e) {
  189. $err = $e->getMessage();
  190. } catch (Exception $e) {
  191. $err = $e->getMessage();
  192. }
  193. echo $err;
  194. exit;
  195. }
  196. ?>
  197. <!doctype html>
  198. <html>
  199. <head>
  200. <meta charset="utf-8">
  201. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  202. <title>安装<?php echo $sitename; ?></title>
  203. <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
  204. <meta name="renderer" content="webkit">
  205. <style>
  206. body {
  207. background: #5c97bd;
  208. margin: 0;
  209. padding: 0;
  210. line-height: 1.5;
  211. }
  212. body, input, button {
  213. font-family: 'Open Sans', sans-serif;
  214. font-size: 16px;
  215. color: #fff;
  216. }
  217. .container {
  218. max-width: 515px;
  219. margin: 0 auto;
  220. padding: 20px;
  221. text-align: center;
  222. }
  223. a {
  224. color: #fff7d0;
  225. text-decoration: none;
  226. }
  227. a:hover {
  228. text-decoration: underline;
  229. }
  230. h1 {
  231. margin-top: 0;
  232. margin-bottom: 10px;
  233. }
  234. h2 {
  235. font-size: 28px;
  236. font-weight: normal;
  237. color: #fff;
  238. margin-bottom: 0;
  239. }
  240. form {
  241. margin-top: 40px;
  242. }
  243. .form-group {
  244. margin-bottom: 20px;
  245. }
  246. .form-group .form-field:first-child input {
  247. border-top-left-radius: 4px;
  248. border-top-right-radius: 4px;
  249. }
  250. .form-group .form-field:last-child input {
  251. border-bottom-left-radius: 4px;
  252. border-bottom-right-radius: 4px;
  253. }
  254. .form-field input {
  255. background: #6ba3c8;
  256. margin: 0 0 1px;
  257. border: 2px solid transparent;
  258. transition: background 0.2s, border-color 0.2s, color 0.2s;
  259. width: 100%;
  260. padding: 15px 15px 15px 180px;
  261. box-sizing: border-box;
  262. }
  263. .form-field input:focus {
  264. border-color: #e8f6ff;
  265. outline: none;
  266. }
  267. .form-field label {
  268. float: left;
  269. width: 160px;
  270. text-align: right;
  271. margin-right: -160px;
  272. position: relative;
  273. margin-top: 18px;
  274. font-size: 14px;
  275. pointer-events: none;
  276. opacity: 0.7;
  277. }
  278. button, .btn {
  279. background: #fff;
  280. color: #6ba3ca;
  281. border: 0;
  282. font-weight: bold;
  283. border-radius: 4px;
  284. cursor: pointer;
  285. padding: 15px 30px;
  286. -webkit-appearance: none;
  287. }
  288. button[disabled] {
  289. opacity: 0.5;
  290. }
  291. #error, .error, #success, .success {
  292. background: #d66c6c;
  293. color: #fff;
  294. padding: 15px 20px;
  295. border-radius: 4px;
  296. margin-bottom: 20px;
  297. }
  298. #success {
  299. background: #3C5675;
  300. }
  301. #error a, .error a {
  302. color: white;
  303. text-decoration: underline;
  304. }
  305. </style>
  306. </head>
  307. <body>
  308. <div class="container">
  309. <h1>
  310. <img src="./assets/images/home_logo.png"/>
  311. </img>
  312. </h1>
  313. <h2>开始安装 <?php echo $sitename; ?></h2>
  314. <div>
  315. <p><a href="<?php echo $link['github']; ?>" target="_blank">Github</a> <a
  316. href="<?php echo $link['wiki']; ?>" target="_blank">Wiki</a> <a
  317. href="<?php echo $link['telegram']; ?>">Telegram</a></p>
  318. <form method="post">
  319. <?php if ($errInfo): ?>
  320. <div class="error">
  321. <?php echo $errInfo; ?>
  322. </div>
  323. <?php endif; ?>
  324. <div id="error" style="display:none"></div>
  325. <div id="success" style="display:none"></div>
  326. <div class="form-group">
  327. <div class="form-field">
  328. <label>MySQL 数据库地址</label>
  329. <input type="text" name="mysqlHost" value="127.0.0.1" required="">
  330. </div>
  331. <div class="form-field">
  332. <label>MySQL 数据库名</label>
  333. <input type="text" name="mysqlDatabase" value="ssrpanel" required="">
  334. </div>
  335. <div class="form-field">
  336. <label>MySQL 用户名</label>
  337. <input type="text" name="mysqlUsername" value="ssrpanel" required="">
  338. </div>
  339. <div class="form-field">
  340. <label>MySQL 密码</label>
  341. <input type="password" name="mysqlPassword">
  342. </div>
  343. <div class="form-field">
  344. <label>MySQL 端口号</label>
  345. <input type="number" name="mysqlHostport" value="3306">
  346. </div>
  347. </div>
  348. <!-- <div class="form-group">-->
  349. <!-- <div class="form-field">-->
  350. <!-- <label>管理者用户名</label>-->
  351. <!-- <input name="adminUsername" value="admin" required=""/>-->
  352. <!-- </div>-->
  353. <!---->
  354. <!-- <div class="form-field">-->
  355. <!-- <label>管理者Email</label>-->
  356. <!-- <input name="adminEmail" value="[email protected]" required="">-->
  357. <!-- </div>-->
  358. <!---->
  359. <!-- <div class="form-field">-->
  360. <!-- <label>管理者密码</label>-->
  361. <!-- <input type="password" name="adminPassword" required="">-->
  362. <!-- </div>-->
  363. <!---->
  364. <!-- <div class="form-field">-->
  365. <!-- <label>重复密码</label>-->
  366. <!-- <input type="password" name="adminPasswordConfirmation" required="">-->
  367. <!-- </div>-->
  368. <!-- </div>-->
  369. <div class="form-buttons">
  370. <button type="submit" <?php echo $errInfo ? 'disabled' : '' ?>>点击安装</button>
  371. </div>
  372. </form>
  373. <!-- jQuery -->
  374. <script src="https://cdn.staticfile.org/jquery/2.1.4/jquery.min.js"></script>
  375. <script>
  376. $(function () {
  377. $('form').on('submit', function (e) {
  378. e.preventDefault();
  379. var $button = $(this).find('button')
  380. .text('安装中...')
  381. .prop('disabled', true);
  382. $.post('', $(this).serialize())
  383. .done(function (ret) {
  384. if (ret === 'success') {
  385. $('#error').hide();
  386. $("#success").text("<?php echo $sitename; ?>安装成功,请使用默认用户名admin和密码123456登录,并尽快修改成新密码。").show();
  387. $('<a class="btn" href="./">进入SSRPanel</a>').insertAfter($button);
  388. $button.remove();
  389. localStorage.setItem("fastep", "installed");
  390. } else {
  391. $('#error').show().text(ret);
  392. $button.prop('disabled', false).text('点击安装');
  393. $("html,body").animate({
  394. scrollTop: 0
  395. }, 500);
  396. }
  397. })
  398. .fail(function (data) {
  399. $('#error').show().text('发生错误:\n\n' + data.responseText);
  400. $button.prop('disabled', false).text('点击安装');
  401. $("html,body").animate({
  402. scrollTop: 0
  403. }, 500);
  404. });
  405. return false;
  406. });
  407. });
  408. </script>
  409. </div>
  410. </div>
  411. </body>
  412. </html>