InstallController.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | saiadmin [ saiadmin快速开发框架 ]
  4. // +----------------------------------------------------------------------
  5. // | Author: sai <1430792918@qq.com>
  6. // +----------------------------------------------------------------------
  7. namespace plugin\saiadmin\app\controller;
  8. use Throwable;
  9. use support\Request;
  10. use support\Response;
  11. use plugin\saiadmin\exception\ApiException;
  12. use plugin\saiadmin\basic\OpenController;
  13. /**
  14. * 安装控制器
  15. */
  16. class InstallController extends OpenController
  17. {
  18. /**
  19. * 不需要登录的方法
  20. */
  21. protected array $noNeedLogin = ['index', 'install'];
  22. /**
  23. * 应用名称
  24. * @var string
  25. */
  26. protected string $app = 'saiadmin';
  27. protected string $version = '5.0.0';
  28. /**
  29. * 安装首页
  30. */
  31. public function index()
  32. {
  33. $data['app'] = $this->app;
  34. $data['version'] = config('plugin.saiadmin.app.version', $this->version);
  35. $env = base_path() . DIRECTORY_SEPARATOR .'.env';
  36. clearstatcache();
  37. if (is_file($env)) {
  38. $data['error'] = '程序已经安装';
  39. return view('install/error', $data);
  40. }
  41. if (!is_writable(base_path() . DIRECTORY_SEPARATOR . 'config')) {
  42. $data['error'] = '权限认证失败';
  43. return view('install/error', $data);
  44. }
  45. return view('install/index', $data);
  46. }
  47. /**
  48. * 执行安装
  49. */
  50. public function install(Request $request)
  51. {
  52. $env = base_path() . DIRECTORY_SEPARATOR .'.env';
  53. clearstatcache();
  54. if (is_file($env)) {
  55. return $this->fail('管理后台已经安装!如需重新安装,请删除根目录env配置文件并重启');
  56. }
  57. $user = $request->post('username');
  58. $password = $request->post('password');
  59. $database = $request->post('database');
  60. $host = $request->post('host');
  61. $port = (int)$request->post('port') ?: 3306;
  62. try {
  63. $db = $this->getPdo($host, $user, $password, $port);
  64. $smt = $db->query("show databases like '$database'");
  65. if (empty($smt->fetchAll())) {
  66. $db->exec("create database $database CHARSET utf8mb4 COLLATE utf8mb4_general_ci");
  67. }
  68. } catch (\Throwable $e) {
  69. $message = $e->getMessage();
  70. if (stripos($message, 'Access denied for user')) {
  71. return $this->fail('数据库用户名或密码错误');
  72. }
  73. if (stripos($message, 'Connection refused')) {
  74. return $this->fail('Connection refused. 请确认数据库IP端口是否正确,数据库已经启动');
  75. }
  76. if (stripos($message, 'timed out')) {
  77. return $this->fail('数据库连接超时,请确认数据库IP端口是否正确,安全组及防火墙已经放行端口');
  78. }
  79. throw $e;
  80. }
  81. $db->exec("use $database");
  82. $smt = $db->query("show tables like 'sa_system_menu';");
  83. $tables = $smt->fetchAll();
  84. if (count($tables) > 0) {
  85. return $this->fail('数据库已经安装,请勿重复安装');
  86. }
  87. $sql_file = base_path() . '/plugin/saiadmin/db/saiadmin-5.0.sql';
  88. if (!is_file($sql_file)) {
  89. return $this->fail('数据库SQL文件不存在');
  90. }
  91. $sql_query = file_get_contents($sql_file);
  92. $db->exec($sql_query);
  93. $this->generateConfig();
  94. $env_config = <<<EOF
  95. # 数据库配置
  96. DB_TYPE = mysql
  97. DB_HOST = $host
  98. DB_PORT = $port
  99. DB_NAME = $database
  100. DB_USER = $user
  101. DB_PASSWORD = $password
  102. DB_PREFIX =
  103. # 缓存方式
  104. CACHE_MODE = file
  105. # Redis配置
  106. REDIS_HOST = 127.0.0.1
  107. REDIS_PORT = 6379
  108. REDIS_PASSWORD = ''
  109. REDIS_DB = 0
  110. # 验证码配置
  111. CAPTCHA_MODE = cache
  112. EOF;
  113. file_put_contents(base_path() . DIRECTORY_SEPARATOR . '.env', $env_config);
  114. // 尝试reload
  115. if (function_exists('posix_kill')) {
  116. set_error_handler(function () {});
  117. posix_kill(posix_getppid(), SIGUSR1);
  118. restore_error_handler();
  119. }
  120. return $this->success('安装成功');
  121. }
  122. /**
  123. * 生成配置文件
  124. */
  125. protected function generateConfig()
  126. {
  127. // 1、think-orm配置文件
  128. $think_orm_config = <<<EOF
  129. <?php
  130. return [
  131. 'default' => 'mysql',
  132. 'connections' => [
  133. 'mysql' => [
  134. // 数据库类型
  135. 'type' => getenv('DB_TYPE'),
  136. // 服务器地址
  137. 'hostname' => getenv('DB_HOST'),
  138. // 数据库名
  139. 'database' => getenv('DB_NAME'),
  140. // 数据库用户名
  141. 'username' => getenv('DB_USER'),
  142. // 数据库密码
  143. 'password' => getenv('DB_PASSWORD'),
  144. // 数据库连接端口
  145. 'hostport' => getenv('DB_PORT'),
  146. // 数据库连接参数
  147. 'params' => [
  148. // 连接超时3秒
  149. \PDO::ATTR_TIMEOUT => 3,
  150. ],
  151. // 数据库编码默认采用utf8
  152. 'charset' => 'utf8',
  153. // 数据库表前缀
  154. 'prefix' => getenv('DB_PREFIX'),
  155. // 断线重连
  156. 'break_reconnect' => true,
  157. // 自定义分页类
  158. 'bootstrap' => '',
  159. // 连接池配置
  160. 'pool' => [
  161. 'max_connections' => 5, // 最大连接数
  162. 'min_connections' => 1, // 最小连接数
  163. 'wait_timeout' => 3, // 从连接池获取连接等待超时时间
  164. 'idle_timeout' => 60, // 连接最大空闲时间,超过该时间会被回收
  165. 'heartbeat_interval' => 50, // 心跳检测间隔,需要小于60秒
  166. ],
  167. ],
  168. ],
  169. ];
  170. EOF;
  171. file_put_contents(base_path() . '/config/think-orm.php', $think_orm_config);
  172. // 2、chache配置文件
  173. $cache_config = <<<EOF
  174. <?php
  175. return [
  176. 'default' => getenv('CACHE_MODE'),
  177. 'stores' => [
  178. 'file' => [
  179. 'driver' => 'file',
  180. 'path' => runtime_path('cache')
  181. ],
  182. 'redis' => [
  183. 'driver' => 'redis',
  184. 'connection' => 'default'
  185. ],
  186. 'array' => [
  187. 'driver' => 'array'
  188. ]
  189. ]
  190. ];
  191. EOF;
  192. file_put_contents(base_path() . '/config/cache.php', $cache_config);
  193. // 3、redis配置文件
  194. $redis_config = <<<EOF
  195. <?php
  196. return [
  197. 'default' => [
  198. 'password' => getenv('REDIS_PASSWORD'),
  199. 'host' => getenv('REDIS_HOST'),
  200. 'port' => getenv('REDIS_PORT'),
  201. 'database' => getenv('REDIS_DB'),
  202. 'pool' => [
  203. 'max_connections' => 5,
  204. 'min_connections' => 1,
  205. 'wait_timeout' => 3,
  206. 'idle_timeout' => 60,
  207. 'heartbeat_interval' => 50,
  208. ],
  209. ]
  210. ];
  211. EOF;
  212. file_put_contents(base_path() . '/config/redis.php', $redis_config);
  213. }
  214. /**
  215. * 获取pdo连接
  216. * @param $host
  217. * @param $username
  218. * @param $password
  219. * @param $port
  220. * @param $database
  221. * @return \PDO
  222. */
  223. protected function getPdo($host, $username, $password, $port, $database = null): \PDO
  224. {
  225. $dsn = "mysql:host=$host;port=$port;";
  226. if ($database) {
  227. $dsn .= "dbname=$database";
  228. }
  229. $params = [
  230. \PDO::MYSQL_ATTR_INIT_COMMAND => "set names utf8mb4",
  231. \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
  232. \PDO::ATTR_EMULATE_PREPARES => false,
  233. \PDO::ATTR_TIMEOUT => 5,
  234. \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
  235. ];
  236. return new \PDO($dsn, $username, $password, $params);
  237. }
  238. }