install.php 12 KB

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