Sin descripción
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | Yzncms [ 御宅男工作室 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2018 http://yzncms.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | fastadmin: https://www.fastadmin.net/
  10. // +----------------------------------------------------------------------
  11. // +----------------------------------------------------------------------
  12. // | 安装主程序
  13. // +----------------------------------------------------------------------
  14. namespace app\admin\command;
  15. use PDO;
  16. use think\console\Command;
  17. use think\console\Input;
  18. use think\console\input\Option;
  19. use think\console\Output;
  20. use think\Db;
  21. use think\Exception;
  22. use think\facade\Config;
  23. use think\facade\Request;
  24. use think\facade\View;
  25. use util\File;
  26. use util\Random;
  27. class Install extends Command
  28. {
  29. /**
  30. * @var \think\Request Request 实例
  31. */
  32. protected $request;
  33. protected function configure()
  34. {
  35. $config = Config::get('database.');
  36. $this->setName('install')
  37. ->addOption('hostname', 'a', Option::VALUE_OPTIONAL, 'mysql hostname', $config['hostname'])
  38. ->addOption('hostport', 'o', Option::VALUE_OPTIONAL, 'mysql hostport', $config['hostport'])
  39. ->addOption('database', 'd', Option::VALUE_OPTIONAL, 'mysql database', $config['database'])
  40. ->addOption('prefix', 'r', Option::VALUE_OPTIONAL, 'table prefix', $config['prefix'])
  41. ->addOption('username', 'u', Option::VALUE_OPTIONAL, 'mysql username', $config['username'])
  42. ->addOption('password', 'p', Option::VALUE_OPTIONAL, 'mysql password', $config['password'])
  43. ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', false)
  44. ->setDescription('New installation of YznCMS');
  45. }
  46. /**
  47. * 命令行安装
  48. */
  49. protected function execute(Input $input, Output $output)
  50. {
  51. define('INSTALL_PATH', APP_PATH . 'admin' . DS . 'command' . DS . 'Install' . DS);
  52. // 覆盖安装
  53. $force = $input->getOption('force');
  54. $hostname = $input->getOption('hostname');
  55. $hostport = $input->getOption('hostport');
  56. $database = $input->getOption('database');
  57. $prefix = $input->getOption('prefix');
  58. $username = $input->getOption('username');
  59. $password = $input->getOption('password');
  60. $installLockFile = INSTALL_PATH . "install.lock";
  61. if (is_file($installLockFile) && !$force) {
  62. $output->error("\nYznCMS already installed!\nIf you need to reinstall again, use the parameter --force=true");
  63. return false;
  64. }
  65. $adminUsername = 'admin';
  66. $adminPassword = Random::alnum(10);
  67. $adminEmail = 'admin@admin.com';
  68. try {
  69. $this->installation($hostname, $hostport, $database, $username, $password, $prefix, $adminUsername, $adminPassword, $adminEmail);
  70. } catch (\Exception $e) {
  71. $output->error($e->getMessage());
  72. return false;
  73. }
  74. $output->highlight("Admin url: http://www.yoursite.com/admin");
  75. $output->highlight("Admin username: {$adminUsername}");
  76. $output->highlight("Admin password: {$adminPassword}");
  77. $output->info("Install Successed!");
  78. }
  79. /**
  80. * PC端安装
  81. */
  82. public function index()
  83. {
  84. $this->request = Request::instance();
  85. define('INSTALL_PATH', APP_PATH . 'admin' . DS . 'command' . DS . 'Install' . DS);
  86. $installLockFile = INSTALL_PATH . "install.lock";
  87. if (is_file($installLockFile)) {
  88. echo '当前已经安装成功,如果需要重新安装,请手动移除install.lock文件';
  89. exit;
  90. }
  91. $output = function ($code, $msg, $url = null, $data = null) {
  92. return json(['code' => $code, 'msg' => $msg, 'url' => $url, 'data' => $data]);
  93. };
  94. if ($this->request->isPost()) {
  95. $mysqlHostname = $this->request->post('mysqlHostname', '127.0.0.1');
  96. $mysqlHostport = $this->request->post('mysqlHostport', '3306');
  97. $hostArr = explode(':', $mysqlHostname);
  98. if (count($hostArr) > 1) {
  99. $mysqlHostname = $hostArr[0];
  100. $mysqlHostport = $hostArr[1];
  101. }
  102. $mysqlUsername = $this->request->post('mysqlUsername', 'root');
  103. $mysqlPassword = $this->request->post('mysqlPassword', '');
  104. $mysqlDatabase = $this->request->post('mysqlDatabase', '');
  105. $mysqlPrefix = $this->request->post('mysqlPrefix', 'yzn_');
  106. $adminUsername = $this->request->post('adminUsername', 'admin');
  107. $adminPassword = $this->request->post('adminPassword', '');
  108. $adminPasswordConfirmation = $this->request->post('adminPasswordConfirmation', '');
  109. $adminEmail = $this->request->post('adminEmail', 'admin@admin.com');
  110. if ($adminPassword !== $adminPasswordConfirmation) {
  111. return $output(0, '两次输入的密码不一致');
  112. }
  113. try {
  114. $this->installation($mysqlHostname, $mysqlHostport, $mysqlDatabase, $mysqlUsername, $mysqlPassword, $mysqlPrefix, $adminUsername, $adminPassword, $adminEmail);
  115. } catch (\PDOException $e) {
  116. throw new Exception($e->getMessage());
  117. } catch (\Exception $e) {
  118. return $output(0, $e->getMessage());
  119. }
  120. return $output(1, '安装成功!', null);
  121. }
  122. $errInfo = '';
  123. try {
  124. $this->checkenv();
  125. } catch (\Exception $e) {
  126. $errInfo = $e->getMessage();
  127. }
  128. return view::fetch(INSTALL_PATH . "install.html", ['errInfo' => $errInfo]);
  129. }
  130. /**
  131. * 执行安装
  132. */
  133. protected function installation($mysqlHostname, $mysqlHostport, $mysqlDatabase, $mysqlUsername, $mysqlPassword, $mysqlPrefix, $adminUsername, $adminPassword, $adminEmail = null)
  134. {
  135. $this->checkenv();
  136. if ($mysqlDatabase == '') {
  137. throw new Exception('请输入正确的数据库名');
  138. }
  139. if (!preg_match("/^\w{3,12}$/", $adminUsername)) {
  140. throw new Exception('用户名只能由3-30位数字、字母、下划线组合');
  141. }
  142. if (!preg_match("/^[\S]{6,16}$/", $adminPassword)) {
  143. throw new Exception('密码长度必须在6-30位之间,不能包含空格');
  144. }
  145. $weakPasswordArr = ['123456', '12345678', '123456789', '654321', '111111', '000000', 'password', 'qwerty', 'abc123', '1qaz2wsx'];
  146. if (in_array($adminPassword, $weakPasswordArr)) {
  147. throw new Exception('密码太简单,请重新输入');
  148. }
  149. $sql = file_get_contents(INSTALL_PATH . 'yzncms.sql');
  150. $sql = str_replace("`yzn_", "`{$mysqlPrefix}", $sql);
  151. // 先尝试能否自动创建数据库
  152. $config = Config::get('database.');
  153. try {
  154. $pdo = new PDO("{$config['type']}:host={$mysqlHostname}" . ($mysqlHostport ? ";port={$mysqlHostport}" : ''), $mysqlUsername, $mysqlPassword);
  155. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  156. $pdo->query("CREATE DATABASE IF NOT EXISTS `{$mysqlDatabase}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;");
  157. // 连接install命令中指定的数据库
  158. $instance = Db::connect([
  159. 'type' => "{$config['type']}",
  160. 'hostname' => "{$mysqlHostname}",
  161. 'hostport' => "{$mysqlHostport}",
  162. 'database' => "{$mysqlDatabase}",
  163. 'username' => "{$mysqlUsername}",
  164. 'password' => "{$mysqlPassword}",
  165. 'prefix' => "{$mysqlPrefix}",
  166. ]);
  167. // 查询一次SQL,判断连接是否正常
  168. $instance->execute("SELECT 1");
  169. $db = new PDO("{$config['type']}:dbname={$mysqlDatabase};host={$mysqlHostname}", $mysqlUsername, $mysqlPassword);
  170. $db->setAttribute(PDO::ATTR_EMULATE_PREPARES, 0);
  171. // 调用原生PDO对象进行批量查询
  172. $db->exec($sql);
  173. } catch (\PDOException $e) {
  174. throw new Exception($e->getMessage());
  175. }
  176. // 数据库配置文件
  177. $dbConfigFile = ROOT_PATH . 'config' . DS . 'database.php';
  178. $dbConfigText = @file_get_contents($dbConfigFile);
  179. $callback = function ($matches) use ($mysqlHostname, $mysqlHostport, $mysqlUsername, $mysqlPassword, $mysqlDatabase, $mysqlPrefix) {
  180. $field = "mysql" . ucfirst($matches[1]);
  181. $replace = $$field;
  182. if ($matches[1] == 'hostport' && $mysqlHostport == 3306) {
  183. $replace = '';
  184. }
  185. return "'{$matches[1]}'{$matches[2]}=>{$matches[3]}Env::get('database.{$matches[1]}', '{$replace}'),";
  186. };
  187. $dbConfigText = preg_replace_callback("/'(hostname|database|username|password|hostport|prefix)'(\s+)=>(\s+)Env::get\((.*)\)\,/", $callback, $dbConfigText);
  188. // 检测能否成功写入数据库配置
  189. $result = @file_put_contents($dbConfigFile, $dbConfigText);
  190. if (!$result) {
  191. throw new Exception('当前权限不足,无法写入文件 config/database.php');
  192. }
  193. // 设置新的Token随机密钥key
  194. $oldTokenKey = config('token.key');
  195. $newTokenKey = Random::alnum(32);
  196. $coreConfigFile = ROOT_PATH . 'config' . DS . 'token.php';
  197. $coreConfigText = @file_get_contents($coreConfigFile);
  198. $coreConfigText = preg_replace("/'key'(\s+)=>(\s+)'{$oldTokenKey}'/", "'key'\$1=>\$2'{$newTokenKey}'", $coreConfigText);
  199. $result = @file_put_contents($coreConfigFile, $coreConfigText);
  200. if (!$result) {
  201. throw new Exception('当前权限不足,无法写入文件 config/token.php');
  202. }
  203. // 变更默认管理员密码
  204. $adminPassword = $adminPassword ? $adminPassword : Random::alnum(8);
  205. $adminEmail = $adminEmail ? $adminEmail : "admin@admin.com";
  206. $newSalt = substr(md5(uniqid(true)), 0, 6);
  207. $newPassword = md5(trim($adminPassword) . $newSalt);
  208. $data = ['username' => $adminUsername, 'email' => $adminEmail, 'password' => $newPassword, 'encrypt' => $newSalt];
  209. $instance->name('admin')->where('username', 'admin')->update($data);
  210. $installLockFile = INSTALL_PATH . "install.lock";
  211. //检测能否成功写入lock文件
  212. $result = @file_put_contents($installLockFile, 1);
  213. if (!$result) {
  214. throw new Exception('当前权限不足,无法写入文件 application/admin/command/Install/install.lock');
  215. }
  216. try {
  217. //删除安装脚本
  218. @unlink(ROOT_PATH . 'public' . DS . 'install.php');
  219. } catch (\Exception $e) {
  220. }
  221. return true;
  222. }
  223. /**
  224. * 检测环境
  225. */
  226. protected function checkenv()
  227. {
  228. //数据库配置文件
  229. $dbConfigFile = ROOT_PATH . 'config' . DS . 'database.php';
  230. if (version_compare(PHP_VERSION, '7.2.5', '<')) {
  231. throw new Exception("当前版本" . PHP_VERSION . "过低,请使用PHP7.2.5以上版本");
  232. }
  233. if (!extension_loaded("PDO")) {
  234. throw new Exception("当前未开启PDO,无法进行安装");
  235. }
  236. if (!File::is_really_writable($dbConfigFile)) {
  237. throw new Exception("当前权限不足,无法写入文件 config/database.php");
  238. }
  239. return true;
  240. }
  241. }