| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- <?php
- namespace plugin\admin\app\controller;
- use Illuminate\Database\Capsule\Manager;
- use plugin\admin\app\common\Util;
- use plugin\admin\app\model\Admin;
- use support\exception\BusinessException;
- use support\Request;
- use support\Response;
- use Webman\Captcha\CaptchaBuilder;
- /**
- * 安装
- */
- class InstallController extends Base
- {
- /**
- * 不需要登录的方法
- * @var string[]
- */
- protected $noNeedLogin = ['step1', 'step2'];
- /**
- * 设置数据库
- * @param Request $request
- * @return Response
- * @throws BusinessException|\Throwable
- */
- public function step1(Request $request): Response
- {
- $database_config_file = base_path() . '/plugin/admin/config/database.php';
- clearstatcache();
- if (is_file($database_config_file)) {
- return $this->json(1, '管理后台已经安装!如需重新安装,请删除该插件数据库配置文件并重启');
- }
- if (!class_exists(CaptchaBuilder::class) || !class_exists(Manager::class)) {
- return $this->json(1, '请运行 composer require -W illuminate/database 安装illuminate/database组件并重启');
- }
- $user = $request->post('user');
- $password = $request->post('password');
- $database = $request->post('database');
- $host = $request->post('host');
- $port = (int)$request->post('port') ?: 3306;
- $overwrite = $request->post('overwrite');
- try {
- $db = $this->getPdo($host, $user, $password, $port);
- $smt = $db->query("show databases like '$database'");
- if (empty($smt->fetchAll())) {
- $db->exec("create database $database");
- }
- $db->exec("use $database");
- $smt = $db->query("show tables");
- $tables = $smt->fetchAll();
- } catch (\Throwable $e) {
- if (stripos($e, 'Access denied for user')) {
- return $this->json(1, '数据库用户名或密码错误');
- }
- if (stripos($e, 'Connection refused')) {
- return $this->json(1, 'Connection refused. 请确认数据库IP端口是否正确,数据库已经启动');
- }
- if (stripos($e, 'timed out')) {
- return $this->json(1, '数据库连接超时,请确认数据库IP端口是否正确,安全组及防火墙已经放行端口');
- }
- throw $e;
- }
- $tables_to_install = [
- 'wa_admins',
- 'wa_admin_roles',
- 'wa_roles',
- 'wa_rules',
- 'wa_options',
- 'wa_users',
- 'wa_uploads',
- ];
- $tables_exist = [];
- foreach ($tables as $table) {
- $tables_exist[] = current($table);
- }
- $tables_conflict = array_intersect($tables_to_install, $tables_exist);
- if (!$overwrite) {
- if ($tables_conflict) {
- return $this->json(1, '以下表' . implode(',', $tables_conflict) . '已经存在,如需覆盖请选择强制覆盖');
- }
- } else {
- foreach ($tables_conflict as $table) {
- $db->exec("DROP TABLE `$table`");
- }
- }
- $sql_file = base_path() . '/plugin/admin/install.sql';
- if (!is_file($sql_file)) {
- return $this->json(1, '数据库SQL文件不存在');
- }
- $sql_query = file_get_contents($sql_file);
- $sql_query = $this->removeComments($sql_query);
- $sql_query = $this->splitSqlFile($sql_query, ';');
- foreach ($sql_query as $sql) {
- $db->exec($sql);
- }
- // 导入菜单
- $menus = include base_path() . '/plugin/admin/config/menu.php';
- // 安装过程中没有数据库配置,无法使用api\Menu::import()方法
- $this->importMenu($menus, $db);
- $config_content = <<<EOF
- <?php
- return [
- 'default' => 'mysql',
- 'connections' => [
- 'mysql' => [
- 'driver' => 'mysql',
- 'host' => '$host',
- 'port' => '$port',
- 'database' => '$database',
- 'username' => '$user',
- 'password' => '$password',
- 'charset' => 'utf8mb4',
- 'collation' => 'utf8mb4_general_ci',
- 'prefix' => '',
- 'strict' => true,
- 'engine' => null,
- ],
- ],
- ];
- EOF;
- file_put_contents($database_config_file, $config_content);
- $think_orm_config = <<<EOF
- <?php
- return [
- 'default' => 'mysql',
- 'connections' => [
- 'mysql' => [
- // 数据库类型
- 'type' => 'mysql',
- // 服务器地址
- 'hostname' => '$host',
- // 数据库名
- 'database' => '$database',
- // 数据库用户名
- 'username' => '$user',
- // 数据库密码
- 'password' => '$password',
- // 数据库连接端口
- 'hostport' => $port,
- // 数据库连接参数
- 'params' => [
- // 连接超时3秒
- \PDO::ATTR_TIMEOUT => 3,
- ],
- // 数据库编码默认采用utf8
- 'charset' => 'utf8mb4',
- // 数据库表前缀
- 'prefix' => '',
- // 断线重连
- 'break_reconnect' => true,
- // 关闭SQL监听日志
- 'trigger_sql' => true,
- // 自定义分页类
- 'bootstrap' => ''
- ],
- ],
- ];
- EOF;
- file_put_contents(base_path() . '/plugin/admin/config/thinkorm.php', $think_orm_config);
- // 尝试reload
- if (function_exists('posix_kill')) {
- set_error_handler(function () {});
- posix_kill(posix_getppid(), SIGUSR1);
- restore_error_handler();
- }
- return $this->json(0);
- }
- /**
- * 设置管理员
- * @param Request $request
- * @return Response
- * @throws BusinessException
- */
- public function step2(Request $request): Response
- {
- $username = $request->post('username');
- $password = $request->post('password');
- $password_confirm = $request->post('password_confirm');
- if ($password != $password_confirm) {
- return $this->json(1, '两次密码不一致');
- }
- if (!is_file($config_file = base_path() . '/plugin/admin/config/database.php')) {
- return $this->json(1, '请先完成第一步数据库配置');
- }
- $config = include $config_file;
- $connection = $config['connections']['mysql'];
- $pdo = $this->getPdo($connection['host'], $connection['username'], $connection['password'], $connection['port'], $connection['database']);
- if ($pdo->query('select * from `wa_admins`')->fetchAll()) {
- return $this->json(1, '后台已经安装完毕,无法通过此页面创建管理员');
- }
- $smt = $pdo->prepare("insert into `wa_admins` (`username`, `password`, `nickname`, `created_at`, `updated_at`) values (:username, :password, :nickname, :created_at, :updated_at)");
- $time = date('Y-m-d H:i:s');
- $data = [
- 'username' => $username,
- 'password' => Util::passwordHash($password),
- 'nickname' => '超级管理员',
- 'created_at' => $time,
- 'updated_at' => $time
- ];
- foreach ($data as $key => $value) {
- $smt->bindValue($key, $value);
- }
- $smt->execute();
- $admin_id = $pdo->lastInsertId();
- $smt = $pdo->prepare("insert into `wa_admin_roles` (`role_id`, `admin_id`) values (:role_id, :admin_id)");
- $smt->bindValue('role_id', 1);
- $smt->bindValue('admin_id', $admin_id);
- $smt->execute();
- $request->session()->flush();
- return $this->json(0);
- }
- /**
- * 添加菜单
- * @param array $menu
- * @param \PDO $pdo
- * @return int
- */
- protected function addMenu(array $menu, \PDO $pdo): int
- {
- $allow_columns = ['title', 'key', 'icon', 'href', 'pid', 'weight', 'type'];
- $data = [];
- foreach ($allow_columns as $column) {
- if (isset($menu[$column])) {
- $data[$column] = $menu[$column];
- }
- }
- $time = date('Y-m-d H:i:s');
- $data['created_at'] = $data['updated_at'] = $time;
- $values = [];
- foreach ($data as $k => $v) {
- $values[] = ":$k";
- }
- $columns = array_keys($data);
- foreach ($columns as $k => $column) {
- $columns[$k] = "`$column`";
- }
- $sql = "insert into wa_rules (" .implode(',', $columns). ") values (" . implode(',', $values) . ")";
- $smt = $pdo->prepare($sql);
- foreach ($data as $key => $value) {
- $smt->bindValue($key, $value);
- }
- $smt->execute();
- return $pdo->lastInsertId();
- }
- /**
- * 导入菜单
- * @param array $menu_tree
- * @param \PDO $pdo
- * @return void
- */
- protected function importMenu(array $menu_tree, \PDO $pdo)
- {
- if (is_numeric(key($menu_tree)) && !isset($menu_tree['key'])) {
- foreach ($menu_tree as $item) {
- $this->importMenu($item, $pdo);
- }
- return;
- }
- $children = $menu_tree['children'] ?? [];
- unset($menu_tree['children']);
- $smt = $pdo->prepare("select * from wa_rules where `key`=:key limit 1");
- $smt->execute(['key' => $menu_tree['key']]);
- $old_menu = $smt->fetch();
- if ($old_menu) {
- $pid = $old_menu['id'];
- $params = [
- 'title' => $menu_tree['title'],
- 'icon' => $menu_tree['icon'] ?? '',
- 'key' => $menu_tree['key'],
- ];
- $sql = "update wa_rules set title=:title, icon=:icon where `key`=:key";
- $smt = $pdo->prepare($sql);
- $smt->execute($params);
- } else {
- $pid = $this->addMenu($menu_tree, $pdo);
- }
- foreach ($children as $menu) {
- $menu['pid'] = $pid;
- $this->importMenu($menu, $pdo);
- }
- }
- /**
- * 去除sql文件中的注释
- * @param $sql
- * @return string
- */
- protected function removeComments($sql): string
- {
- return preg_replace("/(\n--[^\n]*)/","", $sql);
- }
- /**
- * 分割sql文件
- * @param $sql
- * @param $delimiter
- * @return array
- */
- function splitSqlFile($sql, $delimiter): array
- {
- $tokens = explode($delimiter, $sql);
- $output = array();
- $matches = array();
- $token_count = count($tokens);
- for ($i = 0; $i < $token_count; $i++) {
- if (($i != ($token_count - 1)) || (strlen($tokens[$i] > 0))) {
- $total_quotes = preg_match_all("/'/", $tokens[$i], $matches);
- $escaped_quotes = preg_match_all("/(?<!\\\\)(\\\\\\\\)*\\\\'/", $tokens[$i], $matches);
- $unescaped_quotes = $total_quotes - $escaped_quotes;
- if (($unescaped_quotes % 2) == 0) {
- $output[] = $tokens[$i];
- $tokens[$i] = "";
- } else {
- $temp = $tokens[$i] . $delimiter;
- $tokens[$i] = "";
- $complete_stmt = false;
- for ($j = $i + 1; (!$complete_stmt && ($j < $token_count)); $j++) {
- $total_quotes = preg_match_all("/'/", $tokens[$j], $matches);
- $escaped_quotes = preg_match_all("/(?<!\\\\)(\\\\\\\\)*\\\\'/", $tokens[$j], $matches);
- $unescaped_quotes = $total_quotes - $escaped_quotes;
- if (($unescaped_quotes % 2) == 1) {
- $output[] = $temp . $tokens[$j];
- $tokens[$j] = "";
- $temp = "";
- $complete_stmt = true;
- $i = $j;
- } else {
- $temp .= $tokens[$j] . $delimiter;
- $tokens[$j] = "";
- }
- }
- }
- }
- }
- return $output;
- }
- /**
- * 获取pdo连接
- * @param $host
- * @param $username
- * @param $password
- * @param $port
- * @param $database
- * @return \PDO
- */
- protected function getPdo($host, $username, $password, $port, $database = null): \PDO
- {
- $dsn = "mysql:host=$host;port=$port;";
- if ($database) {
- $dsn .= "dbname=$database";
- }
- $params = [
- \PDO::MYSQL_ATTR_INIT_COMMAND => "set names utf8mb4",
- \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
- \PDO::ATTR_EMULATE_PREPARES => false,
- \PDO::ATTR_TIMEOUT => 5,
- \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
- ];
- return new \PDO($dsn, $username, $password, $params);
- }
- }
|