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 = << '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 = << '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("/(? "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); } }