Install.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. <?php
  2. namespace app\admin\command;
  3. use fast\Random;
  4. use PDO;
  5. use think\Config;
  6. use think\console\Command;
  7. use think\console\Input;
  8. use think\console\input\Option;
  9. use think\console\Output;
  10. use think\Db;
  11. use think\Exception;
  12. use think\Lang;
  13. use think\Request;
  14. use think\View;
  15. class Install extends Command
  16. {
  17. protected $model = null;
  18. /**
  19. * @var \think\View 视图类实例
  20. */
  21. protected $view;
  22. /**
  23. * @var \think\Request Request 实例
  24. */
  25. protected $request;
  26. protected function configure()
  27. {
  28. $config = Config::get('database');
  29. $this
  30. ->setName('install')
  31. ->addOption('hostname', 'a', Option::VALUE_OPTIONAL, 'mysql hostname', $config['hostname'])
  32. ->addOption('hostport', 'o', Option::VALUE_OPTIONAL, 'mysql hostport', $config['hostport'])
  33. ->addOption('database', 'd', Option::VALUE_OPTIONAL, 'mysql database', $config['database'])
  34. ->addOption('prefix', 'r', Option::VALUE_OPTIONAL, 'table prefix', $config['prefix'])
  35. ->addOption('username', 'u', Option::VALUE_OPTIONAL, 'mysql username', $config['username'])
  36. ->addOption('password', 'p', Option::VALUE_OPTIONAL, 'mysql password', $config['password'])
  37. ->addOption('force', 'f', Option::VALUE_OPTIONAL, 'force override', false)
  38. ->setDescription('New installation of FastAdmin');
  39. }
  40. /**
  41. * 命令行安装
  42. */
  43. protected function execute(Input $input, Output $output)
  44. {
  45. define('INSTALL_PATH', APP_PATH . 'admin' . DS . 'command' . DS . 'Install' . DS);
  46. // 覆盖安装
  47. $force = $input->getOption('force');
  48. $hostname = $input->getOption('hostname');
  49. $hostport = $input->getOption('hostport');
  50. $database = $input->getOption('database');
  51. $prefix = $input->getOption('prefix');
  52. $username = $input->getOption('username');
  53. $password = $input->getOption('password');
  54. $installLockFile = INSTALL_PATH . "install.lock";
  55. if (is_file($installLockFile) && !$force) {
  56. throw new Exception("\nFastAdmin already installed!\nIf you need to reinstall again, use the parameter --force=true ");
  57. }
  58. $adminUsername = 'admin';
  59. $adminPassword = Random::alnum(10);
  60. $adminEmail = 'admin@admin.com';
  61. $siteName = __('My Website');
  62. $adminName = $this->installation($hostname, $hostport, $database, $username, $password, $prefix, $adminUsername, $adminPassword, $adminEmail, $siteName);
  63. if ($adminName) {
  64. $output->highlight("Admin url:http://www.yoursite.com/{$adminName}");
  65. }
  66. $output->highlight("Admin username:{$adminUsername}");
  67. $output->highlight("Admin password:{$adminPassword}");
  68. \think\Cache::rm('__menu__');
  69. $output->info("Install Successed!");
  70. }
  71. /**
  72. * PC端安装
  73. */
  74. public function index()
  75. {
  76. $this->view = View::instance(Config::get('template'), Config::get('view_replace_str'));
  77. $this->request = Request::instance();
  78. define('INSTALL_PATH', APP_PATH . 'admin' . DS . 'command' . DS . 'Install' . DS);
  79. Lang::load(INSTALL_PATH . $this->request->langset() . '.php');
  80. $installLockFile = INSTALL_PATH . "install.lock";
  81. if (is_file($installLockFile)) {
  82. echo __('The system has been installed. If you need to reinstall, please remove %s first', 'install.lock');
  83. exit;
  84. }
  85. $output = function ($code, $msg, $url = null, $data = null) {
  86. return json(['code' => $code, 'msg' => $msg, 'url' => $url, 'data' => $data]);
  87. };
  88. if ($this->request->isPost()) {
  89. $mysqlHostname = $this->request->post('mysqlHostname', '127.0.0.1');
  90. $mysqlHostport = $this->request->post('mysqlHostport', '3306');
  91. $hostArr = explode(':', $mysqlHostname);
  92. if (count($hostArr) > 1) {
  93. $mysqlHostname = $hostArr[0];
  94. $mysqlHostport = $hostArr[1];
  95. }
  96. $mysqlUsername = $this->request->post('mysqlUsername', 'root');
  97. $mysqlPassword = $this->request->post('mysqlPassword', '');
  98. $mysqlDatabase = $this->request->post('mysqlDatabase', '');
  99. $mysqlPrefix = $this->request->post('mysqlPrefix', 'fa_');
  100. $adminUsername = $this->request->post('adminUsername', 'admin');
  101. $adminPassword = $this->request->post('adminPassword', '');
  102. $adminPasswordConfirmation = $this->request->post('adminPasswordConfirmation', '');
  103. $adminEmail = $this->request->post('adminEmail', 'admin@admin.com');
  104. $siteName = $this->request->post('siteName', __('My Website'));
  105. if ($adminPassword !== $adminPasswordConfirmation) {
  106. return $output(0, __('The two passwords you entered did not match'));
  107. }
  108. $adminName = '';
  109. try {
  110. $adminName = $this->installation($mysqlHostname, $mysqlHostport, $mysqlDatabase, $mysqlUsername, $mysqlPassword, $mysqlPrefix, $adminUsername, $adminPassword, $adminEmail, $siteName);
  111. } catch (\PDOException $e) {
  112. throw new Exception($e->getMessage());
  113. } catch (\Exception $e) {
  114. return $output(0, $e->getMessage());
  115. }
  116. return $output(1, __('Install Successed'), null, ['adminName' => $adminName]);
  117. }
  118. $errInfo = '';
  119. try {
  120. $this->checkenv();
  121. } catch (\Exception $e) {
  122. $errInfo = $e->getMessage();
  123. }
  124. return $this->view->fetch(INSTALL_PATH . "install.html", ['errInfo' => $errInfo]);
  125. }
  126. /**
  127. * 执行安装
  128. */
  129. protected function installation($mysqlHostname, $mysqlHostport, $mysqlDatabase, $mysqlUsername, $mysqlPassword, $mysqlPrefix, $adminUsername, $adminPassword, $adminEmail = null, $siteName = null)
  130. {
  131. $this->checkenv();
  132. if ($mysqlDatabase == '') {
  133. throw new Exception(__('Please input correct database'));
  134. }
  135. if (!preg_match("/^\w{3,12}$/", $adminUsername)) {
  136. throw new Exception(__('Please input correct username'));
  137. }
  138. if (!preg_match("/^[\S]{6,16}$/", $adminPassword)) {
  139. throw new Exception(__('Please input correct password'));
  140. }
  141. if ($siteName == '' || preg_match("/fast" . "admin/i", $siteName)) {
  142. throw new Exception(__('Please input correct website'));
  143. }
  144. $sql = file_get_contents(INSTALL_PATH . 'fastadmin.sql');
  145. $sql = str_replace("`fa_", "`{$mysqlPrefix}", $sql);
  146. // 先尝试能否自动创建数据库
  147. $config = Config::get('database');
  148. try {
  149. $pdo = new PDO("{$config['type']}:host={$mysqlHostname}" . ($mysqlHostport ? ";port={$mysqlHostport}" : ''), $mysqlUsername, $mysqlPassword);
  150. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  151. $pdo->query("CREATE DATABASE IF NOT EXISTS `{$mysqlDatabase}` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;");
  152. // 连接install命令中指定的数据库
  153. $instance = Db::connect([
  154. 'type' => "{$config['type']}",
  155. 'hostname' => "{$mysqlHostname}",
  156. 'hostport' => "{$mysqlHostport}",
  157. 'database' => "{$mysqlDatabase}",
  158. 'username' => "{$mysqlUsername}",
  159. 'password' => "{$mysqlPassword}",
  160. 'prefix' => "{$mysqlPrefix}",
  161. ]);
  162. // 查询一次SQL,判断连接是否正常
  163. $instance->execute("SELECT 1");
  164. // 调用原生PDO对象进行批量查询
  165. $instance->getPdo()->exec($sql);
  166. } catch (\PDOException $e) {
  167. throw new Exception($e->getMessage());
  168. }
  169. // 后台入口文件
  170. $adminFile = ROOT_PATH . 'public' . DS . 'admin.php';
  171. // 数据库配置文件
  172. $dbConfigFile = APP_PATH . 'database.php';
  173. $config = @file_get_contents($dbConfigFile);
  174. $callback = function ($matches) use ($mysqlHostname, $mysqlHostport, $mysqlUsername, $mysqlPassword, $mysqlDatabase, $mysqlPrefix) {
  175. $field = "mysql" . ucfirst($matches[1]);
  176. $replace = $$field;
  177. if ($matches[1] == 'hostport' && $mysqlHostport == 3306) {
  178. $replace = '';
  179. }
  180. return "'{$matches[1]}'{$matches[2]}=>{$matches[3]}Env::get('database.{$matches[1]}', '{$replace}'),";
  181. };
  182. $config = preg_replace_callback("/'(hostname|database|username|password|hostport|prefix)'(\s+)=>(\s+)Env::get\((.*)\)\,/", $callback, $config);
  183. // 检测能否成功写入数据库配置
  184. $result = @file_put_contents($dbConfigFile, $config);
  185. if (!$result) {
  186. throw new Exception(__('The current permissions are insufficient to write the file %s', 'application/database.php'));
  187. }
  188. // 变更默认管理员密码
  189. $adminPassword = $adminPassword ? $adminPassword : Random::alnum(8);
  190. $adminEmail = $adminEmail ? $adminEmail : "admin@admin.com";
  191. $newSalt = substr(md5(uniqid(true)), 0, 6);
  192. $newPassword = md5(md5($adminPassword) . $newSalt);
  193. $data = ['username' => $adminUsername, 'email' => $adminEmail, 'password' => $newPassword, 'salt' => $newSalt];
  194. $instance->name('admin')->where('username', 'admin')->update($data);
  195. // 修改后台入口
  196. $adminName = '';
  197. if (is_file($adminFile)) {
  198. $adminName = Random::alpha(10) . '.php';
  199. rename($adminFile, ROOT_PATH . 'public' . DS . $adminName);
  200. }
  201. //修改站点名称
  202. if ($siteName != __('My Website')) {
  203. $instance->name('config')->where('name', 'name')->update(['value' => $siteName]);
  204. $configFile = APP_PATH . 'extra' . DS . 'site.php';
  205. $config = include $configFile;
  206. $configList = $instance->name("config")->select();
  207. foreach ($configList as $k => $value) {
  208. if (in_array($value['type'], ['selects', 'checkbox', 'images', 'files'])) {
  209. $value['value'] = explode(',', $value['value']);
  210. }
  211. if ($value['type'] == 'array') {
  212. $value['value'] = (array)json_decode($value['value'], true);
  213. }
  214. $config[$value['name']] = $value['value'];
  215. }
  216. $config['name'] = $siteName;
  217. file_put_contents($configFile, '<?php' . "\n\nreturn " . var_export($config, true) . ";");
  218. }
  219. $installLockFile = INSTALL_PATH . "install.lock";
  220. //检测能否成功写入lock文件
  221. $result = @file_put_contents($installLockFile, 1);
  222. if (!$result) {
  223. throw new Exception(__('The current permissions are insufficient to write the file %s', 'application/admin/command/Install/install.lock'));
  224. }
  225. return $adminName;
  226. }
  227. /**
  228. * 检测环境
  229. */
  230. protected function checkenv()
  231. {
  232. // 检测目录是否存在
  233. $checkDirs = [
  234. 'thinkphp',
  235. 'vendor',
  236. 'public' . DS . 'assets' . DS . 'libs'
  237. ];
  238. //数据库配置文件
  239. $dbConfigFile = APP_PATH . 'database.php';
  240. if (version_compare(PHP_VERSION, '5.5.0', '<')) {
  241. throw new Exception(__("The current version %s is too low, please use PHP 5.5 or higher", PHP_VERSION));
  242. }
  243. if (!extension_loaded("PDO")) {
  244. throw new Exception(__("PDO is not currently installed and cannot be installed"));
  245. }
  246. if (!is_really_writable($dbConfigFile)) {
  247. throw new Exception(__('The current permissions are insufficient to write the configuration file application/database.php'));
  248. }
  249. foreach ($checkDirs as $k => $v) {
  250. if (!is_dir(ROOT_PATH . $v)) {
  251. throw new Exception(__('Please go to the official website to download the full package or resource package and try to install'));
  252. break;
  253. }
  254. }
  255. return true;
  256. }
  257. }