install.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. <?php
  2. /**
  3. *ProxyPanel安装程序
  4. *
  5. * 安装完成后建议删除此文件
  6. *
  7. * @author Heron
  8. */
  9. // error_reporting(E_ERROR | E_WARNING | E_PARSE | E_NOTICE);
  10. // ini_set('display_errors', '1');
  11. define('DS', DIRECTORY_SEPARATOR); // 定义目录分隔符
  12. define('ROOT_PATH', __DIR__.DS.'..'.DS); // 定义根目录
  13. define('DB_PATH', ROOT_PATH.'sql'.DS.'db.sql');// 数据库
  14. // 判断文件或目录是否有写的权限
  15. function is_really_writable($file) {
  16. if(DIRECTORY_SEPARATOR == '/' and @ ini_get("safe_mode") == false){
  17. return is_writable($file);
  18. }
  19. if(!is_file($file) or ($fp = @fopen($file, "r+")) === false){
  20. return false;
  21. }
  22. fclose($fp);
  23. return true;
  24. }
  25. $name = "ProxyPanel";
  26. // 检测依赖组件目录是否存在
  27. $checkDirs = [
  28. 'vendor',
  29. ];
  30. // 错误信息
  31. $errInfo = '';
  32. // 数据库配置文件
  33. $ConfigFile = ROOT_PATH.'.env';
  34. // 数据库标准配置文件
  35. $exampleConfigFile = ROOT_PATH.'.env.example';
  36. // 锁定的文件
  37. $lockFile = ROOT_PATH.'.env';
  38. if(is_file($lockFile)){
  39. $errInfo = "如果需要重新安装,请备份数据库后手动移除 .env 文件";
  40. }elseif(version_compare(PHP_VERSION, '7.3.0', '<')){
  41. $errInfo = "当前PHP版本(".PHP_VERSION.")过低,请使用PHP7.3.0及以上版本";
  42. }elseif(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'){
  43. $errInfo = "当前系统环境为Windows,无法进行安装";
  44. }elseif(!is_file($exampleConfigFile)){
  45. $errInfo = "缺失标准配置文件 .env.example";
  46. }elseif(!extension_loaded("PDO")){
  47. $errInfo = "当前PHP环境未启用PDO组件,无法进行安装";
  48. }elseif(!is_really_writable(ROOT_PATH)){
  49. $open_basedir = ini_get('open_basedir');
  50. if($open_basedir){
  51. $dirArr = explode(PATH_SEPARATOR, $open_basedir);
  52. if($dirArr && in_array(__DIR__, $dirArr)){
  53. $errInfo = '当前服务器因配置了open_basedir,导致无法读取应用根目录';
  54. }
  55. }
  56. if(!$errInfo){
  57. $errInfo = '权限不足,无法写入配置文件.env';
  58. }
  59. }else{
  60. $dirArr = [];
  61. foreach($checkDirs as $k => $v){
  62. if(!is_dir(ROOT_PATH.$v)){
  63. $errInfo = '请先在'.$name."根目录下执行<b>php composer.phar install</b> 安装依赖";
  64. break;
  65. }
  66. }
  67. }
  68. // 当前是POST请求
  69. if(isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'POST'){
  70. if($errInfo){
  71. echo $errInfo;
  72. exit;
  73. }
  74. $err = '';
  75. $APP_KEY = md5(time().mt_rand(1, 1000000));
  76. $DB_HOST = isset($_POST['mysqlHost'])? trim($_POST['mysqlHost']) : '127.0.0.1';
  77. $DB_PORT = isset($_POST['mysqlPort'])? trim($_POST['mysqlPort']) : 3306;
  78. $hostArr = explode(':', $DB_HOST);
  79. if(count($hostArr) > 1){
  80. $DB_HOST = $hostArr[0];
  81. $DB_PORT = $hostArr[1];
  82. }
  83. $DB_USERNAME = isset($_POST['mysqlUsername'])? trim($_POST['mysqlUsername']) : 'proxypanel';
  84. $DB_PASSWORD = isset($_POST['mysqlPassword'])? trim($_POST['mysqlPassword']) : 'proxypanel';
  85. $DB_DATABASE = isset($_POST['mysqlDatabase'])? trim($_POST['mysqlDatabase']) : 'proxypanel';
  86. try{
  87. // 检测能否读取数据库文件
  88. $sql = @file_get_contents(DB_PATH);
  89. if(!$sql){
  90. throw new Exception("无法读取所需的".DB_PATH.",请检查是否有读权限");
  91. }
  92. $config = @file_get_contents($exampleConfigFile);
  93. if(!$config){
  94. throw new Exception("无法读取配置.env.example文件,请检查是否有读权限");
  95. }
  96. $pdo = new PDO("mysql:host={$DB_HOST};port={$DB_PORT}", $DB_USERNAME, $DB_PASSWORD, [
  97. PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  98. PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"
  99. ]);
  100. // 检测是否支持innodb存储引擎
  101. $pdoStatement = $pdo->query("SHOW VARIABLES LIKE 'innodb_version'");
  102. $result = $pdoStatement->fetch();
  103. if(!$result){
  104. throw new Exception("当前数据库不支持innodb存储引擎,请开启后再重新尝试安装");
  105. }
  106. $pdo->query("CREATE DATABASE IF NOT EXISTS `{$DB_DATABASE}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;");
  107. $pdo->query("USE `{$DB_DATABASE}`");
  108. $pdo->exec($sql);
  109. // 写入数据库配置到.env文件
  110. $callback = function($matches) use ($APP_KEY, $DB_HOST, $DB_PORT, $DB_USERNAME, $DB_PASSWORD, $DB_DATABASE) {
  111. $field = $matches[1];
  112. $replace = ${"{$field}"};
  113. return "{$matches[1]}={$replace}".PHP_EOL;
  114. };
  115. $config = preg_replace_callback("/(APP_KEY|DB_HOST|DB_DATABASE|DB_USERNAME|DB_PASSWORD|DB_PORT)=(.*)(\s+)/",
  116. $callback, $config);
  117. $result = @file_put_contents($ConfigFile, $config);
  118. if(!$result){
  119. throw new Exception("无法写入数据库信息到.env文件,请检查是否有写权限");
  120. }
  121. echo "success";
  122. }catch(PDOException $e){
  123. $err = $e->getMessage();
  124. }catch(Exception $e){
  125. $err = $e->getMessage();
  126. }
  127. echo $err;
  128. exit;
  129. }
  130. ?>
  131. <!doctype html>
  132. <html>
  133. <head>
  134. <meta charset="utf-8">
  135. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  136. <title>安装<?php
  137. echo $name; ?></title>
  138. <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1">
  139. <meta name="renderer" content="webkit">
  140. <style>
  141. body {
  142. background: #5c97bd;
  143. margin: 0;
  144. padding: 0;
  145. line-height: 1.5;
  146. }
  147. body, input, button {
  148. font-family: 'Open Sans', sans-serif;
  149. font-size: 16px;
  150. color: #fff;
  151. }
  152. .container {
  153. max-width: 515px;
  154. margin: 0 auto;
  155. padding: 20px;
  156. text-align: center;
  157. }
  158. a {
  159. color: #fff7d0;
  160. text-decoration: none;
  161. }
  162. a:hover {
  163. text-decoration: underline;
  164. }
  165. h1 {
  166. margin-top: 0;
  167. margin-bottom: 10px;
  168. }
  169. h2 {
  170. font-size: 28px;
  171. font-weight: normal;
  172. color: #fff;
  173. margin-bottom: 0;
  174. }
  175. form {
  176. margin-top: 40px;
  177. }
  178. .form-group {
  179. margin-bottom: 20px;
  180. }
  181. .form-group .form-field:first-child input {
  182. border-top-left-radius: 4px;
  183. border-top-right-radius: 4px;
  184. }
  185. .form-group .form-field:last-child input {
  186. border-bottom-left-radius: 4px;
  187. border-bottom-right-radius: 4px;
  188. }
  189. .form-field input {
  190. background: #6ba3c8;
  191. margin: 0 0 1px;
  192. border: 2px solid transparent;
  193. transition: background 0.2s, border-color 0.2s, color 0.2s;
  194. width: 100%;
  195. padding: 15px 15px 15px 180px;
  196. box-sizing: border-box;
  197. }
  198. .form-field input:focus {
  199. border-color: #e8f6ff;
  200. outline: none;
  201. }
  202. .form-field label {
  203. float: left;
  204. width: 160px;
  205. text-align: right;
  206. margin-right: -160px;
  207. position: relative;
  208. margin-top: 18px;
  209. font-size: 14px;
  210. pointer-events: none;
  211. opacity: 0.7;
  212. }
  213. button, .btn {
  214. background: #fff;
  215. color: #6ba3ca;
  216. border: 0;
  217. font-weight: bold;
  218. border-radius: 4px;
  219. cursor: pointer;
  220. padding: 15px 30px;
  221. -webkit-appearance: none;
  222. }
  223. button[disabled] {
  224. opacity: 0.5;
  225. }
  226. #error, .error, #success, .success {
  227. background: #d66c6c;
  228. color: #fff;
  229. padding: 15px 20px;
  230. border-radius: 4px;
  231. margin-bottom: 20px;
  232. }
  233. #success {
  234. background: #3C5675;
  235. }
  236. #error a, .error a {
  237. color: white;
  238. text-decoration: underline;
  239. }
  240. </style>
  241. </head>
  242. <body>
  243. <div class="container">
  244. <h2>安装 <?php
  245. echo $name; ?></h2>
  246. <div>
  247. <form method="post">
  248. <?php
  249. if($errInfo): ?>
  250. <div class="error">
  251. <?php
  252. echo $errInfo; ?>
  253. </div>
  254. <?php
  255. endif; ?>
  256. <div id="error" style="display:none"></div>
  257. <div id="success" style="display:none"></div>
  258. <div class="form-group">
  259. <div class="form-field">
  260. <label>MySQL 数据库地址</label>
  261. <input type="text" name="mysqlHost" value="127.0.0.1" required="">
  262. </div>
  263. <div class="form-field">
  264. <label>MySQL 数据库名</label>
  265. <input type="text" name="mysqlDatabase" value="proxypanel" required="">
  266. </div>
  267. <div class="form-field">
  268. <label>MySQL 用户名</label>
  269. <input type="text" name="mysqlUsername" value="proxypanel" required="">
  270. </div>
  271. <div class="form-field">
  272. <label>MySQL 密码</label>
  273. <input type="password" name="mysqlPassword">
  274. </div>
  275. <div class="form-field">
  276. <label>MySQL 端口号</label>
  277. <input type="number" name="mysqlPort" value="3306">
  278. </div>
  279. </div>
  280. <div class="form-buttons">
  281. <button type="submit" <?php
  282. echo $errInfo? 'disabled' : '' ?>>安装
  283. </button>
  284. </div>
  285. </form>
  286. <script src="//cdn.staticfile.org/jquery/2.1.4/jquery.min.js" type="text/javascript"></script>
  287. <script>
  288. $(function () {
  289. $('form').on('submit', function (e) {
  290. e.preventDefault();
  291. var $button = $(this).find('button').text('安装中...').prop('disabled', true);
  292. $.post('', $(this).serialize())
  293. .done(function (ret) {
  294. if (ret === 'success') {
  295. $('#error').hide();
  296. $("#success").text("安装成功,请使用[用户名:test@test.com、密码:123456]登录").show();
  297. $('<a class="btn" href="./admin/login">登录后台</a>').insertAfter($button);
  298. $button.remove();
  299. localStorage.setItem("fastep", "installed");
  300. } else {
  301. $('#error').show().text(ret);
  302. $button.prop('disabled', false).text('点击安装');
  303. $("html,body").animate({
  304. scrollTop: 0
  305. }, 500);
  306. }
  307. })
  308. .fail(function (data) {
  309. $('#error').show().text('发生错误:\n\n' + data.responseText);
  310. $button.prop('disabled', false).text('点击安装');
  311. $("html,body").animate({
  312. scrollTop: 0
  313. }, 500);
  314. });
  315. return false;
  316. });
  317. });
  318. </script>
  319. </div>
  320. </div>
  321. </body>
  322. </html>