Install.php 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | Description: 安装
  4. // +----------------------------------------------------------------------
  5. // | Author: Michael_xu | gengxiaoxu@5kcrm.com
  6. // +----------------------------------------------------------------------
  7. namespace app\admin\controller;
  8. use think\Controller;
  9. use think\Request;
  10. use think\Db;
  11. use Env;
  12. class Install extends Controller
  13. {
  14. // private $count = 100;
  15. // private $now = 0;
  16. public function _initialize()
  17. {
  18. /*防止跨域*/
  19. header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
  20. header('Access-Control-Allow-Credentials: true');
  21. header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
  22. header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, authKey, sessionId");
  23. $param = Request::instance()->param();
  24. $this->param = $param;
  25. // $request = request();
  26. // $m = strtolower($request->module());
  27. // $c = strtolower($request->controller());
  28. // $a = strtolower($request->action());
  29. //
  30. // if (!in_array($a, array('upgrade','upgradeprocess','checkversion')) && file_exists(CONF_PATH . "install.lock")) {
  31. // echo "<meta http-equiv='content-type' content='text/html; charset=UTF-8'> <script>alert('请勿重复安装!');location.href='".$_SERVER["HTTP_HOST"]."';</script>";
  32. // die();
  33. // }
  34. }
  35. private $upgrade_site = "http://message.72crm.com/";
  36. /**
  37. * [index 安装步骤]
  38. * @author Michael_xu
  39. * @param
  40. */
  41. public function index()
  42. {
  43. $protocol = strpos(strtolower($_SERVER['HTTP_REFERER']), 'https') === false ? 'http' : 'https';
  44. if (strpos(request()->url(), "index.php") === false) {
  45. $url = $protocol. "://" .$_SERVER["HTTP_HOST"] . "/index.php" . request()->url();
  46. header("Location:" . $url);
  47. }
  48. if (file_exists(CONF_PATH . "install.lock")) {
  49. echo "<meta http-equiv='content-type' content='text/html; charset=UTF-8'> <script>alert('请勿重复安装!');location.href='".$protocol."://".$_SERVER["HTTP_HOST"]."';</script>";
  50. die();
  51. }
  52. if (!file_exists(getcwd() . "/public/sql/5kcrm.sql")) {
  53. echo "<meta http-equiv='content-type' content='text/html; charset=UTF-8'> <script>alert('缺少必要的数据库文件!');location.href='".$protocol."://".$_SERVER["HTTP_HOST"]."';</script>";
  54. die();
  55. }
  56. return $this->fetch('index');
  57. }
  58. public function step1()
  59. {
  60. if (strpos(request()->url(), "index.php") === false) {
  61. $protocol = strpos(strtolower($_SERVER['HTTP_REFERER']), 'https') === false ? 'http' : 'https';
  62. $url = $protocol. "://" .$_SERVER["HTTP_HOST"] . "/index.php" . request()->url();
  63. header("Location:" . $url);
  64. }
  65. session('install_error',null);
  66. $data = [];
  67. $data['env'] = self::checkNnv();
  68. $data['dir'] = self::checkDir();
  69. $data['version'] = $this->version();
  70. $this->assign('data',$data);
  71. return $this->fetch('step1');
  72. }
  73. //版本
  74. public function version()
  75. {
  76. $res = include(CONF_PATH.'version.php');
  77. return $res ? : array('VERSION' => '11.0.0','RELEASE' => '20210219');
  78. }
  79. public function step2(){
  80. if (session('install_error')){
  81. echo "<meta http-equiv='content-type' content='text/html; charset=UTF-8'> <script>alert('环境检测未通过,不能进行下一步操作!');location.href='".$_SERVER["HTTP_REFERER"]."';</script>";
  82. die();
  83. }
  84. if (strpos(request()->url(), "index.php") === false) {
  85. $protocol = strpos(strtolower($_SERVER['HTTP_REFERER']), 'https') === false ? 'http' : 'https';
  86. $url = $protocol. "://" .$_SERVER["HTTP_HOST"] . "/index.php" . request()->url();
  87. header("Location:" . $url);
  88. }
  89. $data['os'] = PHP_OS;
  90. $data['php'] = phpversion();
  91. $data['version'] = $this->version();
  92. $this->assign('envir_data',$data);
  93. return $this->fetch();
  94. }
  95. public function step3(){
  96. return $this->fetch();
  97. }
  98. public function step4(){
  99. if (session('install_error')){
  100. return resultArray(['error' => '环境检测未通过,不能进行下一步操作!']);
  101. }
  102. if (file_exists(CONF_PATH . "install.lock")) {
  103. return resultArray(['error' => '请勿重复安装!']);
  104. }
  105. if (!file_exists(getcwd() . "/public/sql/5kcrm.sql")) {
  106. return resultArray(['error' => '缺少必要的数据库文件!']);
  107. }
  108. $temp = $this->param;
  109. $param = $temp['form'];
  110. $db_config['type'] = 'mysql';
  111. $db_config['hostname'] = $param['databaseUrl'];
  112. $db_config['hostport'] = $param['databasePort'];
  113. $db_config['database'] = $param['databaseName'];
  114. $db_config['username'] = $param['databaseUser'];
  115. $db_config['password'] = $param['databasePwd'];
  116. $db_config['prefix'] = '5kcrm_';
  117. // $db_config['prefix'] = $param['databaseTable'];
  118. $username = $param['root'];
  119. $password = $param['pwd'];
  120. $wkcode = $param['wkcode'];
  121. if (empty($db_config['hostname'])) {
  122. return resultArray(['error' => '请填写数据库主机!']);
  123. }
  124. if (empty($db_config['hostport'])) {
  125. return resultArray(['error' => '请填写数据库端口!']);
  126. }
  127. if (preg_match('/[^0-9]/', $db_config['hostport'])) {
  128. return resultArray(['error' => '数据库端口只能是数字!']);
  129. }
  130. if (empty($db_config['database'])) {
  131. return resultArray(['error' => '请填写数据库名!']);
  132. }
  133. if (empty($db_config['username'])) {
  134. return resultArray(['error' => '请填写数据库用户名!']);
  135. }
  136. if (empty($db_config['password'])) {
  137. return resultArray(['error' => '请填写数据库密码!']);
  138. }
  139. if (empty($db_config['prefix'])) {
  140. return resultArray(['error' => '请填写表前缀!']);
  141. }
  142. if (preg_match('/[^a-z0-9_]/i', $db_config['prefix'])) {
  143. return resultArray(['error' => '表前缀只能包含数字、字母和下划线!']);
  144. }
  145. if (empty($username)) {
  146. return resultArray(['error' => '请填写管理员用户名!']);
  147. }
  148. if (empty($wkcode)) {
  149. return resultArray(['error' => '请填写序列号!']);
  150. }
  151. $resCheckData = checkWkCode($wkcode);
  152. if (!$resCheckData) {
  153. return resultArray(['error' => '序列号错误!']);
  154. }
  155. $resData = object_to_array(json_decode($resCheckData));
  156. if ($resData['date'] != date('Y-m-d')) {
  157. return resultArray(['error' => '序列号已失效,请前往悟空官网个人中心获取最新数据!']);
  158. }
  159. if (empty($password)) {
  160. return resultArray(['error' => '请填写管理员密码!']);
  161. }
  162. session('install_count','');
  163. session('install_now','');
  164. $database = $db_config['database'];
  165. unset($db_config['database']);
  166. $connect = Db::connect($db_config);
  167. // 检测数据库连接
  168. try{
  169. $ret = $connect->execute('select version()');
  170. }catch(\Exception $e){
  171. return resultArray(['error' => '数据库连接失败,请检查数据库配置!']);
  172. }
  173. $check = $connect->execute("SELECT * FROM information_schema.schemata WHERE schema_name='".$database."'");
  174. if (!$check && !$connect->execute("CREATE DATABASE IF NOT EXISTS `".$database."` default collate utf8_general_ci ")) {
  175. return resultArray(['error' => '没有找到您填写的数据库名且无法创建!请检查连接账号是否有创建数据库的权限!']);
  176. }
  177. $db_config['database'] = $database;
  178. self::mkDatabase($db_config);
  179. self::mkLicense($wkcode);
  180. $C_Patch = substr($_SERVER['SCRIPT_FILENAME'],0,-10);
  181. $sql = file_get_contents( $C_Patch.'/public/sql/5kcrm.sql');
  182. $sqlList = parse_sql($sql, 0, ['5kcrm_' => $db_config['prefix']]);
  183. if ($sqlList) {
  184. $sqlList = array_filter($sqlList);
  185. $install_count = count($sqlList);
  186. session('install_count',$install_count);
  187. foreach ($sqlList as $k=>$v) {
  188. $install_now = $k+1;
  189. session('install_now',$install_now);
  190. try {
  191. $temp_sql = $v.';';
  192. Db::connect($db_config)->query($temp_sql);
  193. } catch(\Exception $e) {
  194. // return resultArray(['error' => '请启用InnoDB数据引擎,并检查数据库是否有DROP和CREATE权限']);
  195. return resultArray(['error' => '数据库sql安装出错,请操作数据库手动导入sql文件']);
  196. }
  197. }
  198. }
  199. $salt = substr(md5(time()),0,4);
  200. $password = user_md5(trim($password), $salt, $username);
  201. //插入信息
  202. Db::connect($db_config)->query("insert into ".$db_config['prefix']."admin_user (username, password, salt, img, thumb_img, realname, create_time, num, email, mobile, sex, status, structure_id, post, parent_id, type, authkey, authkey_time ) values ( '".$username."', '".$password."', '".$salt."', '', '', '管理员', ".time().", '', '', '".$username."', '', 1, 1, 'CEO', 0, 1, '', 0 )");
  203. Db::connect($db_config)->query("insert into ".$db_config['prefix']."hrm_user_det (user_id, join_time, type, status, userstatus, create_time, update_time, mobile, sex, age, job_num, idtype, idnum, birth_time, nation, internship, done_time, parroll_id, email, political, location, leave_time ) values ( 1, ".time().", 1, 1, 2, ".time().", ".time().", '".$username."', '', 0, '', 0, '', '', 0, 0, 0, 0, '', '', '', 0 )");
  204. touch(CONF_PATH . "install.lock");
  205. return resultArray(['data'=>'安装成功']);
  206. }
  207. /**
  208. * 安装成功界面
  209. *
  210. * @author fnqi
  211. * @date 2021-03-11
  212. * @return mixed
  213. */
  214. public function step5()
  215. {
  216. return $this->fetch();
  217. }
  218. /**
  219. * 安装超时界面
  220. *
  221. * @author fanqi
  222. * @date 2021-03-11
  223. * @return mixed
  224. */
  225. public function step6()
  226. {
  227. return $this->fetch();
  228. }
  229. //ajax 进度条
  230. public function progress()
  231. {
  232. $data['length'] = session('install_count');
  233. $data['now'] = session('install_now');
  234. return resultArray(['data'=>$data]);
  235. }
  236. //添加database.php文件
  237. private function mkDatabase(array $data)
  238. {
  239. $code = <<<INFO
  240. <?php
  241. return [
  242. // 数据库类型
  243. 'type' => 'mysql',
  244. // 服务器地址
  245. 'hostname' => '{$data['hostname']}',
  246. // 数据库名
  247. 'database' => '{$data['database']}',
  248. // 用户名
  249. 'username' => '{$data['username']}',
  250. // 密码
  251. 'password' => '{$data['password']}',
  252. // 端口
  253. 'hostport' => '{$data['hostport']}',
  254. // 连接dsn
  255. 'dsn' => '',
  256. // 数据库连接参数
  257. 'params' => [],
  258. // 数据库编码默认采用utf8
  259. 'charset' => 'utf8',
  260. // 数据库表前缀
  261. 'prefix' => '{$data['prefix']}',
  262. // 数据库调试模式
  263. 'debug' => true,
  264. // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
  265. 'deploy' => 0,
  266. // 数据库读写是否分离 主从式有效
  267. 'rw_separate' => false,
  268. // 读写分离后 主服务器数量
  269. 'master_num' => 1,
  270. // 指定从服务器序号
  271. 'slave_no' => '',
  272. // 自动读取主库数据
  273. 'read_master' => false,
  274. // 是否严格检查字段是否存在
  275. 'fields_strict' => true,
  276. // 数据集返回类型
  277. 'resultset_type' => 'array',
  278. ];
  279. INFO;
  280. file_put_contents( CONF_PATH.'database.php', $code);
  281. // 判断写入是否成功
  282. $config = include CONF_PATH.'database.php';
  283. if (empty($config['database']) || $config['database'] != $data['database']) {
  284. return resultArray(['error' => '[config/database.php]数据库配置写入失败!']);
  285. }
  286. return true;
  287. }
  288. //检查目录权限
  289. public function check_dir_iswritable($dir_path){
  290. $dir_path=str_replace( '\\','/',$dir_path);
  291. $is_writale=1;
  292. if (!is_dir($dir_path)) {
  293. $is_writale=0;
  294. return $is_writale;
  295. } else {
  296. $file_hd=@fopen($dir_path.'/test.txt','w');
  297. if (!$file_hd) {
  298. @fclose($file_hd);
  299. @unlink($dir_path.'/test.txt');
  300. $is_writale=0;
  301. return $is_writale;
  302. }
  303. $dir_hd = opendir($dir_path);
  304. while (false !== ($file=readdir($dir_hd))) {
  305. if ($file != "." && $file != "..") {
  306. if (is_file($dir_path.'/'.$file)) {
  307. //文件不可写,直接返回
  308. if (!is_writable($dir_path.'/'.$file)) {
  309. return 0;
  310. }
  311. } else {
  312. $file_hd2=@fopen($dir_path.'/'.$file.'/test.txt','w');
  313. if (!$file_hd2) {
  314. @fclose($file_hd2);
  315. @unlink($dir_path.'/'.$file.'/test.txt');
  316. $is_writale=0;
  317. return $is_writale;
  318. }
  319. //递归
  320. $is_writale=check_dir_iswritable($dir_path.'/'.$file);
  321. }
  322. }
  323. }
  324. }
  325. return $is_writale;
  326. }
  327. /**
  328. * [checkVersion 检查升级]
  329. * @author Michael_xu
  330. * @param
  331. */
  332. public function checkVersion(){
  333. $version = Config::load('version');
  334. $info = sendRequest($this->upgrade_site.'index.php?m=version&a=checkVersion', $version['VERSION']);
  335. if ($info){
  336. return resultArray(['data' => $info]);
  337. } else {
  338. return resultArray(['error' => '检查新版本出错!']);
  339. }
  340. }
  341. /**
  342. * 环境检测
  343. * @return array
  344. */
  345. private function checkNnv()
  346. {
  347. $items = [
  348. 'os' => ['操作系统', PHP_OS, '类Unix', 'ok'],
  349. 'php' => ['PHP版本', PHP_VERSION, '7.3 ( <em style="color: #888; font-size: 12px;">>= 7.0</em> )', 'ok','性能更佳'],
  350. 'gd' => ['gd', '开启', '开启', 'ok'],
  351. 'openssl' => ['openssl', '开启', '开启', 'ok'],
  352. 'pdo' => ['pdo', '开启', '开启', 'ok'],
  353. ];
  354. // linux系统下需要posix扩展
  355. if (in_array(PHP_OS, ['Linux', 'centos', 'Centos', 'ubuntu', 'Ubuntu'])) {
  356. $items['posix'] = ['posix', '开启', '开启', 'ok'];
  357. }
  358. session('install_error','');
  359. if (substr($items['php'][1],0,3) < '7.0') {
  360. $items['php'][3] = 'error';
  361. session('install_error', true);
  362. }
  363. if (!extension_loaded('gd')) {
  364. $items['gd'][1] = '未开启';
  365. $items['gd'][3] = 'error';
  366. session('install_error', true);
  367. }
  368. if (!extension_loaded('openssl')) {
  369. $items['openssl'][1] = '未开启';
  370. $items['openssl'][3] = 'error';
  371. session('install_error', true);
  372. }
  373. if (!extension_loaded('pdo')) {
  374. $items['pdo'][1] = '未开启';
  375. $items['pdo'][3] = 'error';
  376. session('install_error', true);
  377. }
  378. // linux系统下检查是否加载了posix扩展
  379. if (in_array(PHP_OS, ['Linux', 'centos', 'Centos', 'ubuntu', 'Ubuntu']) && !extension_loaded('posix')) {
  380. $items['posix'][1] = '未开启';
  381. $items['posix'][3] = 'error';
  382. session('install_error', true);
  383. }
  384. return $items;
  385. }
  386. /**
  387. * 目录权限检查
  388. * @return array
  389. */
  390. private function checkDir()
  391. {
  392. $items = [
  393. ['dir', $this->root_path.'application', 'application', '读写', '读写', 'ok'],
  394. ['dir', $this->root_path.'extend', 'extend', '读写', '读写', 'ok'],
  395. ['dir', $this->root_path.'runtime', './temp', '读写', '读写', 'ok'],
  396. ['dir', $this->root_path.'public', './upload', '读写', '读写', 'ok'],
  397. ['file', $this->root_path.'config', 'config', '读写', '读写', 'ok'],
  398. ];
  399. foreach ($items as &$v) {
  400. if ($v[0] == 'dir') {// 文件夹
  401. if (!is_writable($v[1])) {
  402. if (is_dir($v[1])) {
  403. $v[4] = '不可写';
  404. $v[5] = 'no';
  405. } else {
  406. $v[4] = '不存在';
  407. $v[5] = 'no';
  408. }
  409. session('install_error', true);
  410. }
  411. } else {// 文件
  412. if (!is_writable($v[1])) {
  413. $v[4] = '不可写';
  414. $v[5] = 'no';
  415. session('install_error', true);
  416. }
  417. }
  418. }
  419. return $items;
  420. }
  421. /**
  422. * 验证序列号
  423. * @param
  424. * @return
  425. */
  426. public function checkCodeOld($username) {
  427. $encryption = md5($username);
  428. $substr = substr($username, strlen($username)-6);
  429. $subArr = str_split($substr, 1);
  430. $code = '';
  431. for ($i = 0; $i <= 5; $i++) {
  432. $code .= $encryption[$subArr[$i]];
  433. }
  434. return $code;
  435. }
  436. //写入license文件
  437. private function mkLicense($wkcode)
  438. {
  439. file_put_contents( CONF_PATH.'license.dat', $wkcode);
  440. // 判断写入是否成功
  441. // $config = include CONF_PATH.'license.dat';
  442. // if (empty($config)) {
  443. // return resultArray(['error' => 'license配置写入失败!']);
  444. // }
  445. return true;
  446. }
  447. }