Index.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. <?php
  2. namespace app\install\controller;
  3. use app\model\site\Site;
  4. use app\model\sys\SysUser;
  5. use app\service\admin\install\InstallSystemService;
  6. use app\service\admin\shop\admin\ShopCategoryService;
  7. use app\service\admin\shop\admin\ShopService;
  8. use app\service\admin\shop\admin\ShopSiteGroupService;
  9. use app\service\core\addon\CoreAddonInstallService;
  10. use app\service\core\addon\CoreAddonService;
  11. use app\service\core\schedule\CoreScheduleInstallService;
  12. use Exception;
  13. use think\facade\Cache;
  14. use think\facade\Db;
  15. use think\facade\View;
  16. use think\Response;
  17. class Index extends BaseInstall
  18. {
  19. /**
  20. *安装
  21. */
  22. public function index()
  23. {
  24. $this->checkLock();
  25. $step = input("step", 1);
  26. if ($step == 1) {
  27. return View::fetch('index/step-1');
  28. } elseif ($step == 2) {
  29. //系统变量
  30. $system_variables = [];
  31. $phpv = PHP_VERSION;
  32. $os = PHP_OS;
  33. $server = $_SERVER[ 'SERVER_SOFTWARE' ];
  34. $host = ( empty($_SERVER[ 'REMOTE_ADDR' ]) ? $_SERVER[ 'REMOTE_HOST' ] : $_SERVER[ 'REMOTE_ADDR' ] );
  35. $name = $_SERVER[ 'SERVER_NAME' ];
  36. $verison = !(version_compare(PHP_VERSION, '8.0.0') == -1);
  37. //pdo
  38. $pdo = extension_loaded('pdo') && extension_loaded('pdo_mysql');
  39. $system_variables[] = [ "name" => "pdo", "need" => "开启", "status" => $pdo ];
  40. //curl
  41. $curl = extension_loaded('curl') && function_exists('curl_init');
  42. $system_variables[] = [ "name" => "curl", "need" => "开启", "status" => $curl ];
  43. //openssl
  44. $openssl = extension_loaded('openssl');
  45. $system_variables[] = [ "name" => "openssl", "need" => "开启", "status" => $openssl ];
  46. //gd
  47. $gd = extension_loaded('gd');
  48. $system_variables[] = [ "name" => "GD库", "need" => "开启", "status" => $gd ];
  49. //fileinfo
  50. $fileinfo = extension_loaded('fileinfo');
  51. $system_variables[] = [ "name" => "fileinfo", "need" => "开启", "status" => $fileinfo ];
  52. //sodium
  53. $sodium = extension_loaded('sodium');
  54. $system_variables[] = [ "name" => "sodium", "need" => "开启", "status" => $sodium ];
  55. $root_path = str_replace("\\", DIRECTORY_SEPARATOR, dirname(__FILE__, 4));
  56. $root_path = str_replace("../", DIRECTORY_SEPARATOR, $root_path);
  57. $dirs_list = [
  58. [ "path" => $root_path . DIRECTORY_SEPARATOR, "path_name" => "niucloud/", "name" => "网站目录" ],
  59. [ "path" => $root_path . DIRECTORY_SEPARATOR . ".env", "path_name" => "niucloud/.env", "name" => "env" ],
  60. [ "path" => $root_path . DIRECTORY_SEPARATOR . ".example.env", "path_name" => "niucloud/.example_env", "name" => "env" ],
  61. [ "path" => $root_path . DIRECTORY_SEPARATOR . 'runtime'.DIRECTORY_SEPARATOR, "path_name" => "niucloud/runtime", "name" => "runtime" ],
  62. [ "path" => $root_path . DIRECTORY_SEPARATOR . 'public'.DIRECTORY_SEPARATOR.'upload'.DIRECTORY_SEPARATOR, "path_name" => "niucloud/public/upload", "name" => "upload" ],
  63. [ "path" => $root_path . DIRECTORY_SEPARATOR . 'app'.DIRECTORY_SEPARATOR.'install'.DIRECTORY_SEPARATOR, "path_name" => "niucloud/app/install", "name" => "安装目录" ]
  64. ];
  65. //目录 可读 可写检测
  66. $is_dir = true;
  67. foreach ($dirs_list as $k => $v) {
  68. @mkdir($v[ "path" ], 0755, true);
  69. $is_readable = is_readable($v[ "path" ]);
  70. $is_write = is_write($v[ "path" ]);
  71. $dirs_list[ $k ][ "is_readable" ] = $is_readable;
  72. $dirs_list[ $k ][ "is_write" ] = $is_write;
  73. if (!$is_readable || !$is_write) {
  74. $is_dir = false;
  75. }
  76. }
  77. $this->assign("root_path", $root_path);
  78. $this->assign("system_variables", $system_variables);
  79. $this->assign("phpv", $phpv);
  80. $this->assign("server", $server);
  81. $this->assign("host", $host);
  82. $this->assign("os", $os);
  83. $this->assign("name", $name);
  84. $this->assign("verison", $verison);
  85. $this->assign("dirs_list", $dirs_list);
  86. if ($verison && $pdo && $curl && $openssl && $gd && $fileinfo && $sodium && $is_dir) {
  87. $continue = true;
  88. } else {
  89. $continue = false;
  90. }
  91. $this->assign("continue", $continue);
  92. return $this->fetch('index/step-2');
  93. } elseif ($step == 3) {
  94. return $this->fetch('index/step-3');
  95. }
  96. }
  97. public function installSuccess()
  98. {
  99. Cache::delete('install_data');
  100. Cache::delete('install_status');
  101. return $this->fetch('index/step-4');
  102. }
  103. /**
  104. * 测试数据库
  105. */
  106. public function testDb()
  107. {
  108. $this->checkLock();
  109. $dbport = input("dbport", "");
  110. $dbhost = input("dbhost", "");
  111. $dbuser = input("dbuser", "");
  112. $dbpwd = input("dbpwd", "");
  113. $dbname = input("dbname", "");
  114. try {
  115. if ($dbport != "" && $dbhost != "") {
  116. $dbhost = $dbport != '3306' ? $dbhost . ':' . $dbport : $dbhost;
  117. }
  118. if ($dbhost == '' || $dbuser == '') {
  119. return fail([
  120. "status" => -1,
  121. "message" => "数据库账号或密码不能为空"
  122. ]);
  123. }
  124. if (!function_exists("mysqli_connect")) {
  125. return fail([
  126. "status" => -1,
  127. "message" => "mysqli扩展类必须开启"
  128. ]);
  129. }
  130. $conn = @mysqli_connect($dbhost, $dbuser, $dbpwd);
  131. if ($conn) {
  132. if (empty($dbname)) {
  133. $result = [
  134. "status" => 1,
  135. "message" => "数据库连接成功"
  136. ];
  137. } else {
  138. try {
  139. if (@mysqli_select_db($conn, $dbname)) {
  140. $result = [
  141. "status" => 2,
  142. "message" => "数据库存在,系统将覆盖数据库"
  143. ];
  144. } else {
  145. $result = [
  146. "status" => 1,
  147. "message" => "数据库不存在,系统将自动创建"
  148. ];
  149. }
  150. } catch ( Exception $e) {
  151. $result = [
  152. "status" => 1,
  153. "message" => "数据库不存在,系统将自动创建"
  154. ];
  155. return fail($result);
  156. }
  157. }
  158. @mysqli_close($conn);
  159. } else {
  160. $result = [
  161. "status" => -1,
  162. "message" => "数据库连接失败!"
  163. ];
  164. return fail($result);
  165. }
  166. return success($result);
  167. } catch ( Exception $e) {
  168. $result = [
  169. "status" => -1,
  170. "message" => $e->getMessage()
  171. ];
  172. return fail($result);
  173. }
  174. }
  175. /**
  176. * @param $sql_data sql文件
  177. * @return array
  178. */
  179. public function getSqlQuery($sql_data)
  180. {
  181. $this->checkLock();
  182. $sql_data = preg_replace("/TYPE=(InnoDB|MyISAM|MEMORY)( DEFAULT CHARSET=[^; ]+)?/", "ENGINE=\\1 DEFAULT CHARSET=utf8", $sql_data);
  183. $sql_data = str_replace("\r", "\n", $sql_data);
  184. $sql_query = [];
  185. $num = 0;
  186. $sql_arr = explode(";\n", trim($sql_data));
  187. unset($sql);
  188. foreach ($sql_arr as $sql) {
  189. $sql_query[ $num ] = '';
  190. $sqls = explode("\n", trim($sql));
  191. $sqls = array_filter($sqls);
  192. foreach ($sqls as $query) {
  193. $str1 = $query[0] ?? '';
  194. if ($str1 != '#' && $str1 != '-')
  195. $sql_query[ $num ] .= $query;
  196. }
  197. $num++;
  198. }
  199. return $sql_query;
  200. }
  201. public function install()
  202. {
  203. set_time_limit(300);
  204. Cache::delete('install_data');
  205. Cache::set('install_status', 0);//进行中
  206. $check = $this->testDb()->getData();
  207. if ($check[ 'code' ] != 1) {
  208. $this->setSuccessLog([ $check[ 'data' ][ 'message' ], 'error' ]);
  209. return fail($check[ 'data' ][ 'message' ]);
  210. }
  211. $admin_name = input('admin_name', "");
  212. $username = input('username', "");
  213. $password = input('password', "");
  214. $password2 = input('password2', "");
  215. $site_name = input('site_name', "");
  216. $site_username = input('site_username', "");
  217. $site_password = input('site_password', "");
  218. $site_password2 = input('site_password2', "");
  219. if ($admin_name == '' || $username == '' || $password == '') {
  220. $this->setSuccessLog([ '平台信息不能为空', 'error' ]);
  221. return fail('平台信息不能为空!');
  222. }
  223. if ($site_username == $username) {
  224. $this->setSuccessLog([ '站点管理员和平台管理员不能相同,请重新输入', 'error' ]);
  225. return fail('站点管理员和平台管理员不能相同,请重新输入');
  226. }
  227. if ($password != $password2) {
  228. $this->setSuccessLog([ '平台两次密码输入不一样,请重新输入', 'error' ]);
  229. return fail('平台两次密码输入不一样,请重新输入');
  230. }
  231. // if ($site_name == '' || $site_username == '' || $site_password == '') {
  232. // $this->setSuccessLog([ '平台信息不能为空', 'error' ]);
  233. // return fail('平台信息不能为空!');
  234. // }
  235. if($site_username == $username) {
  236. $this->setSuccessLog([ '站点账号不能跟平台账号一致', 'error' ]);
  237. return fail('站点账号不能跟平台账号一致!');
  238. }
  239. if ($site_password != $site_password2) {
  240. $this->setSuccessLog([ '站点两次密码输入不一样,请重新输入', 'error' ]);
  241. return fail('站点两次密码输入不一样,请重新输入');
  242. }
  243. try {
  244. //配置写入
  245. $res = $this->installConfig(input())->getData();
  246. if ($res[ 'code' ] != 1) {
  247. $this->setSuccessLog([ $res[ 'msg' ], 'error' ]);
  248. return fail($res[ 'msg' ]);
  249. }
  250. //数据库
  251. $res = $this->installSql(input())->getData();
  252. if ($res[ 'code' ] != 1) {
  253. $this->setSuccessLog([ $res[ 'msg' ], 'error' ]);
  254. return fail($res[ 'msg' ]);
  255. }
  256. Cache::set('install_status', 1);//成功
  257. return success();
  258. } catch ( Exception $e) {
  259. $this->setSuccessLog([ '安装失败' . $e->getMessage(), 'error' ]);
  260. return fail('安装失败' . $e->getMessage());
  261. }
  262. }
  263. public function initData()
  264. {
  265. $this->checkLock();
  266. $admin_name = input('admin_name', "");
  267. $username = input('username', "");
  268. $password = input('password', "");
  269. $password2 = input('password2', "");
  270. $site_name = input('site_name', "");
  271. $site_username = input('site_username', "");
  272. $site_password = input('site_password', "");
  273. $site_password2 = input('site_password2', "");
  274. if ($admin_name == '' || $username == '' || $password == '') {
  275. return fail('平台信息不能为空!');
  276. }
  277. if ($password != $password2) {
  278. return fail('平台两次密码输入不一样,请重新输入');
  279. }
  280. if($site_username == $username) {
  281. return fail('站点账号不能跟平台账号一致');
  282. }
  283. if ($site_password != $site_password2) {
  284. return fail('站点两次密码输入不一样,请重新输入');
  285. }
  286. try {
  287. //初始化数据
  288. $res = ( new InstallSystemService() )->install();
  289. if (!$res) {
  290. $this->setSuccessLog([ '菜单初始化失败', 'error' ]);
  291. return fail('菜单初始化失败');
  292. }
  293. //初始化计划任务
  294. $res = ( new CoreScheduleInstallService())->installSystemSchedule();
  295. if (!$res) {
  296. $this->setSuccessLog([ '计划任务初始化失败', 'error' ]);
  297. return fail('计划任务初始化失败');
  298. }
  299. $user = ( new SysUser() )->where([ [ 'uid', '=', 1 ] ])->findOrEmpty();
  300. if (!$user->isEmpty()) {
  301. $user->save([
  302. 'username' => $username,
  303. 'password' => create_password($password),
  304. ]);
  305. }
  306. ( new Site() )->where([ [ 'site_id', '=', 1 ] ])->update(['site_id' => 0]);
  307. $site = ( new Site() )->where([ [ 'site_id', '=', 0 ] ])->findOrEmpty();
  308. if (!$site->isEmpty()) {
  309. $site->save([
  310. 'site_name' => $admin_name,
  311. 'expire_time' => date('Y-m-d H:i:s',strtotime('+1 year'))
  312. ]);
  313. }
  314. //修改自增主键默认值
  315. // Db::execute("alter table ".env('database.prefix', '')."site auto_increment = 100000");
  316. // 安装插件
  317. $this->installAddon();
  318. //todo 创建店铺套餐
  319. $default_group_id = (new ShopSiteGroupService())->add(
  320. [
  321. 'group_name' => '店铺默认套餐',
  322. 'group_desc' => '',
  323. 'addon' => []
  324. ]
  325. );
  326. //todo 创建店铺分类
  327. $default_category_id = (new ShopCategoryService())->add(
  328. [
  329. 'category_name' => '店铺默认分类',
  330. ]
  331. );
  332. $shop_insert_data = [
  333. 'site_id' => 1,
  334. 'site_name' => $site_name,
  335. 'uid' => 1,
  336. 'username' => '',
  337. 'group_id' => $default_group_id,
  338. 'is_self' => 1,
  339. 'category_id' => $default_category_id,
  340. 'expire_time' => 0
  341. ];
  342. (new ShopService())->add($shop_insert_data);
  343. $fp = fopen($this->lock_file, 'wb');
  344. if (!$fp) {
  345. $this->setSuccessLog([ "写入失败,请检查目录" . dirname(__FILE__, 2) . "是否可写入!'", 'error' ]);
  346. return fail("写入失败,请检查目录" . dirname(__FILE__, 2) . "是否可写入!'");
  347. }
  348. $this->setSuccessLog([ '初始化成功', 'success' ]);
  349. fwrite($fp, '已安装');
  350. fclose($fp);
  351. Cache::set('install_status', 2);//成功
  352. // Cache::tag(MenuService::$cache_tag_name)->clear();
  353. return success();
  354. } catch ( Exception $e) {
  355. $this->setSuccessLog([ '安装失败' . $e->getMessage(), 'error' ]);
  356. return fail('安装失败' . $e->getMessage());
  357. }
  358. }
  359. /**
  360. * 安装sql
  361. * @param array $data
  362. * @return Response
  363. */
  364. public function installSql(array $data)
  365. {
  366. $this->checkLock();
  367. $dbport = $data[ 'dbport' ] ?? '';
  368. $dbhost = $data[ 'dbhost' ] ?? '';
  369. $dbuser = $data[ 'dbuser' ] ?? '';
  370. $dbpwd = $data[ 'dbpwd' ] ?? '';
  371. $dbname = $data[ 'dbname' ] ?? '';
  372. $dbprefix = $data[ 'dbprefix' ] ?? '';
  373. if ($dbhost == '' || $dbuser == '') {
  374. return fail('数据库链接配置信息丢失!');
  375. }
  376. $file_name = $this->install_root . "/source/database.sql";//数据文件
  377. //数据库连接测试
  378. $conn = @mysqli_connect($dbhost, $dbuser, $dbpwd, "", $dbport);
  379. if (!$conn) {
  380. return fail('连接数据库失败!请检查连接参数!');
  381. }
  382. //数据库可写和是否存在测试
  383. $empty_db = mysqli_select_db($conn, $dbname);
  384. if ($empty_db) {
  385. $sql = "DROP DATABASE `$dbname`";
  386. $retval = mysqli_query($conn, $sql);
  387. if (!$retval) {
  388. return fail('删除数据库失败: ' . mysqli_error($conn));
  389. }
  390. }
  391. //如果数据库不存在,我们就进行创建。
  392. $dbsql = "CREATE DATABASE `$dbname`";
  393. $db_create = mysqli_query($conn, $dbsql);
  394. if (!$db_create) {
  395. return fail('创建数据库失败,请确认是否有足够的权限!');
  396. }
  397. //链接数据库
  398. @mysqli_select_db($conn, $dbname);
  399. //导入SQL并执行。
  400. $get_sql_data = file_get_contents($file_name);
  401. $sql_query = $this->getSqlQuery($get_sql_data);
  402. @mysqli_query($conn, "SET NAMES utf8mb4");
  403. $query_count = count($sql_query);
  404. for ($i = 0; $i < $query_count; $i++) {
  405. $sql = trim($sql_query[ $i ]);
  406. $is_write = false;
  407. if (str_contains($sql, 'CREATE TABLE')) {
  408. $match_item = preg_match('/CREATE TABLE [`]?(\\w+)[`]?/is', $sql, $match_data);
  409. $is_write = true;
  410. } elseif (str_contains($sql, 'ALTER TABLE')) {
  411. $match_item = preg_match('/ALTER TABLE [`]?(\\w+)[`]?/is', $sql, $match_data);
  412. } elseif (str_contains($sql, 'INSERT INTO')) {
  413. $match_item = preg_match('/INSERT INTO [`]?(\\w+)[`]?/is', $sql, $match_data);
  414. } else {
  415. $match_item = 0;
  416. }
  417. if ($match_item > 0) {
  418. try {
  419. $table_name = $match_data[ 1 ];
  420. $new_table_name = $dbprefix . $table_name;
  421. $sql_item = $this->str_replace_first($table_name, $new_table_name, $sql);
  422. @mysqli_query($conn, $sql_item);
  423. if ($is_write) $this->setSuccessLog([ '创建表' . $table_name, 'success' ]);
  424. } catch ( Exception $e) {
  425. $this->setSuccessLog([ $e->getMessage(), 'error' ]);
  426. return fail('数据库解析失败' . $e->getMessage());
  427. }
  428. }
  429. }
  430. @mysqli_close($conn);
  431. return success();
  432. }
  433. /**
  434. * 配置设置
  435. * @param array $data
  436. * @return Response
  437. */
  438. public function installConfig(array $data)
  439. {
  440. $this->checkLock();
  441. $root_path = str_replace("\\", DIRECTORY_SEPARATOR, dirname(__FILE__, 4));
  442. $root_path = str_replace("../", DIRECTORY_SEPARATOR, $root_path);
  443. $env_dir = $root_path . DIRECTORY_SEPARATOR . ".env";
  444. $example_env = $root_path . DIRECTORY_SEPARATOR . ".example.env";
  445. $dbport = $data[ 'dbport' ] ?? '';
  446. $dbhost = $data[ 'dbhost' ] ?? '';
  447. $dbuser = $data[ 'dbuser' ] ?? '';
  448. $dbpwd = $data[ 'dbpwd' ] ?? '';
  449. $dbname = $data[ 'dbname' ] ?? '';
  450. $dbprefix = $data[ 'dbprefix' ] ?? '';
  451. $replace_key = [
  452. '{dbhost}',
  453. '{dbport}',
  454. '{dbuser}',
  455. '{dbpwd}',
  456. '{dbprefix}',
  457. '{dbname}',
  458. '{auth_key}'
  459. ];
  460. $replace_val = [
  461. $dbhost,
  462. $dbport,
  463. $dbuser,
  464. $dbpwd,
  465. $dbprefix,
  466. $dbname,
  467. unique_random(32)
  468. ];
  469. $content = str_replace($replace_key, $replace_val, file_get_contents($example_env));
  470. file_put_contents($env_dir, $content);
  471. $this->setSuccessLog([ '写入配置', 'success' ]);
  472. return success();
  473. }
  474. public function getInstallInfo()
  475. {
  476. $install_data = Cache::get('install_data') ?? [];
  477. $install_status = Cache::get('install_status') ?? 0;
  478. return success('', [
  479. 'log' => $install_data,
  480. 'status' => $install_status
  481. ]);
  482. }
  483. public function setSuccessLog($data)
  484. {
  485. if ($data[ 1 ] == 'error') {
  486. Cache::set('install_status', -1);
  487. }
  488. $time = @(int)microtime(true);
  489. $data[] = date('Y-m-d H:i:s', $time);
  490. $install_data = Cache::get('install_data') ?? [];
  491. $install_data[] = $data;
  492. Cache::set('install_data', $install_data);
  493. }
  494. /**
  495. * 安装插件
  496. * @return true
  497. */
  498. public function installAddon() {
  499. $root_path = str_replace("\\", DIRECTORY_SEPARATOR, dirname(__FILE__, 4));
  500. $root_path = str_replace("../", DIRECTORY_SEPARATOR, $root_path);
  501. $addon_path = $root_path . DIRECTORY_SEPARATOR . 'addon';
  502. $files = get_files_by_dir($addon_path);
  503. if (!empty($files)) {
  504. foreach ($files as $path) {
  505. $data = (new CoreAddonService())->getAddonConfig($path);
  506. if (isset($data['key'])) {
  507. $install_service = (new CoreAddonInstallService($data['key']));
  508. $install_service->installCheck();
  509. $install_service->install();
  510. }
  511. }
  512. }
  513. return true;
  514. }
  515. }