diff --git a/dafuweng-webman/app/admin/controller/Index.php b/dafuweng-webman/app/admin/controller/Index.php index fd7df96..a0365c9 100644 --- a/dafuweng-webman/app/admin/controller/Index.php +++ b/dafuweng-webman/app/admin/controller/Index.php @@ -32,13 +32,19 @@ class Index extends Backend return $this->error(__('No background menu, please contact super administrator!')); } + $apiUrl = config('buildadmin.api_url'); + if (!$apiUrl || $apiUrl === 'https://api.buildadmin.com') { + $scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http'; + $apiUrl = $scheme . '://' . $request->host(); + } + return $this->success('', [ 'adminInfo' => $adminInfo, 'menus' => $menus, 'siteConfig' => [ 'siteName' => get_sys_config('site_name'), 'version' => get_sys_config('version'), - 'apiUrl' => config('buildadmin.api_url'), + 'apiUrl' => $apiUrl, 'upload' => keys_to_camel_case(get_upload_config($request), ['max_size', 'save_name', 'allowed_suffixes', 'allowed_mime_types']), 'cdnUrl' => full_url(), 'cdnUrlParams' => config('buildadmin.cdn_url_params'), diff --git a/dafuweng-webman/app/admin/controller/auth/Admin.php b/dafuweng-webman/app/admin/controller/auth/Admin.php index 4b59970..2931e19 100644 --- a/dafuweng-webman/app/admin/controller/auth/Admin.php +++ b/dafuweng-webman/app/admin/controller/auth/Admin.php @@ -25,9 +25,10 @@ class Admin extends Backend protected string $dataLimitField = 'id'; - protected function initController(Request $request): void + protected function initController(Request $request): ?Response { $this->model = new AdminModel(); + return null; } public function index(Request $request): Response @@ -262,11 +263,11 @@ class Admin extends Backend } /** - * 远程下拉(Admin 无自定义,返回 null 走默认列表) + * 远程下拉(Admin 无自定义,走父类默认列表) */ - protected function select(Request $request): ?Response + public function select(Request $request): Response { - return null; + return parent::select($request); } private function checkGroupAuth(array $groups): ?Response diff --git a/dafuweng-webman/app/admin/controller/auth/AdminLog.php b/dafuweng-webman/app/admin/controller/auth/AdminLog.php index d866b32..1bee893 100644 --- a/dafuweng-webman/app/admin/controller/auth/AdminLog.php +++ b/dafuweng-webman/app/admin/controller/auth/AdminLog.php @@ -18,9 +18,10 @@ class AdminLog extends Backend protected string|array $quickSearchField = ['title']; - protected function initController(Request $request): void + protected function initController(Request $request): ?Response { $this->model = new AdminLogModel(); + return null; } public function index(Request $request): Response @@ -52,10 +53,10 @@ class AdminLog extends Backend } /** - * 远程下拉(AdminLog 无自定义,返回 null 走默认列表) + * 远程下拉(AdminLog 无自定义,走父类默认列表) */ - protected function select(Request $request): ?Response + public function select(Request $request): Response { - return null; + return parent::select($request); } } diff --git a/dafuweng-webman/app/admin/controller/auth/Group.php b/dafuweng-webman/app/admin/controller/auth/Group.php index 2c8fd26..28575de 100644 --- a/dafuweng-webman/app/admin/controller/auth/Group.php +++ b/dafuweng-webman/app/admin/controller/auth/Group.php @@ -35,7 +35,7 @@ class Group extends Backend protected array $adminGroups = []; - protected function initController(Request $request): void + protected function initController(Request $request): ?Response { $this->model = new AdminGroup(); $this->tree = Tree::instance(); @@ -48,6 +48,7 @@ class Group extends Backend $this->assembleTree = $isTree && !$this->initValue; $this->adminGroups = Db::name('admin_group_access')->where('uid', $this->auth->id)->column('group_id'); + return null; } public function index(Request $request): Response diff --git a/dafuweng-webman/app/admin/controller/auth/Rule.php b/dafuweng-webman/app/admin/controller/auth/Rule.php index 61bfbb3..01e1d2a 100644 --- a/dafuweng-webman/app/admin/controller/auth/Rule.php +++ b/dafuweng-webman/app/admin/controller/auth/Rule.php @@ -34,7 +34,7 @@ class Rule extends Backend protected bool $modelValidate = false; - protected function initController(Request $request): void + protected function initController(Request $request): ?Response { $this->model = new AdminRule(); $this->tree = Tree::instance(); @@ -43,6 +43,7 @@ class Rule extends Backend $this->initValue = is_array($this->initValue) ? array_filter($this->initValue) : []; $this->keyword = $request->get('quickSearch') ?? $request->post('quickSearch') ?? ''; $this->assembleTree = $isTree && !$this->initValue; + return null; } public function index(Request $request): Response diff --git a/dafuweng-webman/app/admin/controller/routine/AdminInfo.php b/dafuweng-webman/app/admin/controller/routine/AdminInfo.php index a2fab26..8c37c87 100644 --- a/dafuweng-webman/app/admin/controller/routine/AdminInfo.php +++ b/dafuweng-webman/app/admin/controller/routine/AdminInfo.php @@ -16,10 +16,11 @@ class AdminInfo extends Backend protected array|string $preExcludeFields = ['username', 'last_login_time', 'password', 'salt', 'status']; protected array $authAllowFields = ['id', 'username', 'nickname', 'avatar', 'email', 'mobile', 'motto', 'last_login_time']; - protected function initController(Request $request): void + protected function initController(Request $request): ?Response { $this->auth->setAllowFields($this->authAllowFields); $this->model = $this->auth->getAdmin(); + return null; } public function index(Request $request): Response diff --git a/dafuweng-webman/app/admin/controller/routine/Attachment.php b/dafuweng-webman/app/admin/controller/routine/Attachment.php index 36be320..91c840e 100644 --- a/dafuweng-webman/app/admin/controller/routine/Attachment.php +++ b/dafuweng-webman/app/admin/controller/routine/Attachment.php @@ -17,9 +17,10 @@ class Attachment extends Backend protected array $withJoinTable = ['admin', 'user']; protected array|string $defaultSortField = ['last_upload_time' => 'desc']; - protected function initController(Request $request): void + protected function initController(Request $request): ?Response { $this->model = new AttachmentModel(); + return null; } public function del(Request $request): Response diff --git a/dafuweng-webman/app/admin/controller/routine/Config.php b/dafuweng-webman/app/admin/controller/routine/Config.php index 33fa23e..69a42f6 100644 --- a/dafuweng-webman/app/admin/controller/routine/Config.php +++ b/dafuweng-webman/app/admin/controller/routine/Config.php @@ -23,9 +23,10 @@ class Config extends Backend 'backendEntranceStub' => 'app/admin/library/stubs/backendEntrance.stub', ]; - protected function initController(Request $request): void + protected function initController(Request $request): ?Response { $this->model = new ConfigModel(); + return null; } public function index(Request $request): Response diff --git a/dafuweng-webman/app/admin/controller/user/Group.php b/dafuweng-webman/app/admin/controller/user/Group.php index ad41a33..f0ec6b0 100644 --- a/dafuweng-webman/app/admin/controller/user/Group.php +++ b/dafuweng-webman/app/admin/controller/user/Group.php @@ -18,9 +18,10 @@ class Group extends Backend protected array|string $preExcludeFields = ['update_time', 'create_time']; protected array|string $quickSearchField = 'name'; - protected function initController(Request $request): void + protected function initController(Request $request): ?Response { $this->model = new UserGroup(); + return null; } public function add(Request $request): Response diff --git a/dafuweng-webman/app/admin/controller/user/MoneyLog.php b/dafuweng-webman/app/admin/controller/user/MoneyLog.php index 17ff5a8..c2d628d 100644 --- a/dafuweng-webman/app/admin/controller/user/MoneyLog.php +++ b/dafuweng-webman/app/admin/controller/user/MoneyLog.php @@ -18,9 +18,10 @@ class MoneyLog extends Backend protected array|string $preExcludeFields = ['create_time']; protected array|string $quickSearchField = ['user.username', 'user.nickname']; - protected function initController(Request $request): void + protected function initController(Request $request): ?Response { $this->model = new UserMoneyLog(); + return null; } public function add(Request $request): Response diff --git a/dafuweng-webman/app/admin/controller/user/Rule.php b/dafuweng-webman/app/admin/controller/user/Rule.php index e1a0f6e..f1ab204 100644 --- a/dafuweng-webman/app/admin/controller/user/Rule.php +++ b/dafuweng-webman/app/admin/controller/user/Rule.php @@ -22,7 +22,7 @@ class Rule extends Backend protected array|string $defaultSortField = ['weigh' => 'desc']; protected array|string $quickSearchField = 'title'; - protected function initController(Request $request): void + protected function initController(Request $request): ?Response { $this->model = new UserRule(); $this->tree = Tree::instance(); @@ -31,6 +31,7 @@ class Rule extends Backend $this->initValue = is_array($this->initValue) ? array_filter($this->initValue) : []; $this->keyword = $request->get('quickSearch', $request->post('quickSearch', '')); $this->assembleTree = $isTree && !$this->initValue; + return null; } public function index(Request $request): Response diff --git a/dafuweng-webman/app/admin/controller/user/ScoreLog.php b/dafuweng-webman/app/admin/controller/user/ScoreLog.php index 081d539..f065ad8 100644 --- a/dafuweng-webman/app/admin/controller/user/ScoreLog.php +++ b/dafuweng-webman/app/admin/controller/user/ScoreLog.php @@ -18,9 +18,10 @@ class ScoreLog extends Backend protected array|string $preExcludeFields = ['create_time']; protected array|string $quickSearchField = ['user.username', 'user.nickname']; - protected function initController(Request $request): void + protected function initController(Request $request): ?Response { $this->model = new UserScoreLog(); + return null; } public function add(Request $request): Response diff --git a/dafuweng-webman/app/admin/controller/user/User.php b/dafuweng-webman/app/admin/controller/user/User.php index aad7f05..37909e8 100644 --- a/dafuweng-webman/app/admin/controller/user/User.php +++ b/dafuweng-webman/app/admin/controller/user/User.php @@ -18,9 +18,10 @@ class User extends Backend protected array|string $preExcludeFields = ['last_login_time', 'login_failure', 'password', 'salt']; protected array|string $quickSearchField = ['username', 'nickname', 'id']; - protected function initController(Request $request): void + protected function initController(Request $request): ?Response { $this->model = new UserModel(); + return null; } public function index(Request $request): Response diff --git a/dafuweng-webman/app/common/middleware/AllowCrossDomain.php b/dafuweng-webman/app/common/middleware/AllowCrossDomain.php index 6b3b9d9..347eb79 100644 --- a/dafuweng-webman/app/common/middleware/AllowCrossDomain.php +++ b/dafuweng-webman/app/common/middleware/AllowCrossDomain.php @@ -21,6 +21,38 @@ class AllowCrossDomain implements MiddlewareInterface 'Access-Control-Allow-Headers' => '*', ]; + /** + * 返回 CORS 预检(OPTIONS)响应,供路由直接调用(Webman 未匹配路由时不走中间件) + */ + public static function optionsResponse(Request $request): Response + { + $header = [ + 'Access-Control-Allow-Credentials' => 'true', + 'Access-Control-Max-Age' => '1800', + 'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, PATCH, OPTIONS', + 'Access-Control-Allow-Headers' => 'Content-Type, Authorization, batoken, ba-user-token, think-lang', + ]; + $origin = $request->header('origin'); + if (is_array($origin)) { + $origin = $origin[0] ?? ''; + } + $origin = is_string($origin) ? trim($origin) : ''; + if ($origin !== '') { + $info = parse_url($origin); + $host = $info['host'] ?? ''; + $corsDomain = array_map('trim', explode(',', config('buildadmin.cors_request_domain', ''))); + $corsDomain[] = $request->host(true); + $allowed = in_array('*', $corsDomain) + || in_array($origin, $corsDomain) + || in_array($host, $corsDomain) + || ($host === 'localhost' || $host === '127.0.0.1'); + if ($allowed) { + $header['Access-Control-Allow-Origin'] = $origin; + } + } + return response('', 204, $header); + } + public function process(Request $request, callable $handler): Response { $path = trim($request->path(), '/'); @@ -31,16 +63,23 @@ class AllowCrossDomain implements MiddlewareInterface $header = $this->header; $origin = $request->header('origin'); - if ($origin) { + if (is_array($origin)) { + $origin = $origin[0] ?? ''; + } + $origin = is_string($origin) ? trim($origin) : ''; + + if ($origin !== '') { $info = parse_url($origin); - $corsDomain = explode(',', config('buildadmin.cors_request_domain', '')); + $host = $info['host'] ?? ''; + $corsDomain = array_map('trim', explode(',', config('buildadmin.cors_request_domain', ''))); $corsDomain[] = $request->host(true); - if ( - in_array('*', $corsDomain) + $allowed = in_array('*', $corsDomain) || in_array($origin, $corsDomain) - || (isset($info['host']) && in_array($info['host'], $corsDomain)) - ) { + || in_array($host, $corsDomain) + || ($host === 'localhost' || $host === '127.0.0.1'); + + if ($allowed) { $header['Access-Control-Allow-Origin'] = $origin; } } diff --git a/dafuweng-webman/app/process/Http.php b/dafuweng-webman/app/process/Http.php index f462c3a..c2f1f0d 100644 --- a/dafuweng-webman/app/process/Http.php +++ b/dafuweng-webman/app/process/Http.php @@ -3,8 +3,37 @@ namespace app\process; use Webman\App; +use Webman\Http\Response; class Http extends App { - + /** + * 在父类处理前拦截 OPTIONS 预检,直接返回 CORS 头(避免预检未命中路由时无 CORS) + */ + public function onMessage($connection, $request): void + { + $method = $request->method(); + if (is_string($method) && strtoupper($method) === 'OPTIONS') { + $path = $request->path(); + $path = is_string($path) ? trim($path, '/') : ''; + $isApiOrAdmin = $path !== '' && (str_starts_with($path, 'api') || str_starts_with($path, 'admin')); + if ($isApiOrAdmin) { + $origin = $request->header('origin'); + $origin = is_array($origin) ? ($origin[0] ?? '') : (is_string($origin) ? trim($origin) : ''); + if ($origin === '') { + $origin = '*'; + } + $headers = [ + 'Access-Control-Allow-Origin' => $origin, + 'Access-Control-Allow-Credentials' => 'true', + 'Access-Control-Max-Age' => '1800', + 'Access-Control-Allow-Methods' => 'GET, POST, PUT, DELETE, PATCH, OPTIONS', + 'Access-Control-Allow-Headers' => 'Content-Type, Authorization, batoken, ba-user-token, think-lang', + ]; + $connection->send(new Response(204, $headers, '')); + return; + } + } + parent::onMessage($connection, $request); + } } \ No newline at end of file diff --git a/dafuweng-webman/config/buildadmin.php b/dafuweng-webman/config/buildadmin.php index fe8ccf4..1b897f6 100644 --- a/dafuweng-webman/config/buildadmin.php +++ b/dafuweng-webman/config/buildadmin.php @@ -4,8 +4,8 @@ // +---------------------------------------------------------------------- return [ - // 允许跨域访问的域名 - 'cors_request_domain' => 'localhost,127.0.0.1', + // 允许跨域访问的域名(* 表示任意;开发可用 *,生产建议填具体域名) + 'cors_request_domain' => '*', // 是否开启会员登录验证码 'user_login_captcha' => true, // 是否开启管理员登录验证码 diff --git a/dafuweng-webman/config/route.php b/dafuweng-webman/config/route.php index c565466..fc21f43 100644 --- a/dafuweng-webman/config/route.php +++ b/dafuweng-webman/config/route.php @@ -13,16 +13,16 @@ use Webman\Route; Route::get('/api/index/index', [\app\api\controller\Index::class, 'index']); // api/user(GET 获取配置,POST 登录/注册) -Route::match(['get', 'post'], '/api/user/checkIn', [\app\api\controller\User::class, 'checkIn']); +Route::add(['GET', 'POST'], '/api/user/checkIn', [\app\api\controller\User::class, 'checkIn']); Route::post('/api/user/logout', [\app\api\controller\User::class, 'logout']); // api/install(安装流程多为 POST) -Route::any('/api/install/terminal', [\app\api\controller\Install::class, 'terminal']); +Route::add(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'], '/api/install/terminal', [\app\api\controller\Install::class, 'terminal']); Route::post('/api/install/changePackageManager', [\app\api\controller\Install::class, 'changePackageManager']); Route::get('/api/install/envBaseCheck', [\app\api\controller\Install::class, 'envBaseCheck']); Route::get('/api/install/envNpmCheck', [\app\api\controller\Install::class, 'envNpmCheck']); Route::post('/api/install/testDatabase', [\app\api\controller\Install::class, 'testDatabase']); -Route::match(['get', 'post'], '/api/install/baseConfig', [\app\api\controller\Install::class, 'baseConfig']); +Route::add(['GET', 'POST'], '/api/install/baseConfig', [\app\api\controller\Install::class, 'baseConfig']); Route::post('/api/install/commandExecComplete', [\app\api\controller\Install::class, 'commandExecComplete']); Route::post('/api/install/manualInstall', [\app\api\controller\Install::class, 'manualInstall']); Route::post('/api/install/mvDist', [\app\api\controller\Install::class, 'mvDist']); @@ -40,10 +40,10 @@ Route::get('/api/ajax/buildSuffixSvg', [\app\api\controller\Ajax::class, 'buildS // api/account Route::get('/api/account/overview', [\app\api\controller\Account::class, 'overview']); -Route::match(['get', 'post'], '/api/account/profile', [\app\api\controller\Account::class, 'profile']); +Route::add(['GET', 'POST'], '/api/account/profile', [\app\api\controller\Account::class, 'profile']); Route::get('/api/account/verification', [\app\api\controller\Account::class, 'verification']); Route::post('/api/account/changeBind', [\app\api\controller\Account::class, 'changeBind']); -Route::match(['get', 'post'], '/api/account/changePassword', [\app\api\controller\Account::class, 'changePassword']); +Route::add(['GET', 'POST'], '/api/account/changePassword', [\app\api\controller\Account::class, 'changePassword']); Route::get('/api/account/integral', [\app\api\controller\Account::class, 'integral']); Route::get('/api/account/balance', [\app\api\controller\Account::class, 'balance']); Route::post('/api/account/retrievePassword', [\app\api\controller\Account::class, 'retrievePassword']); @@ -54,13 +54,20 @@ Route::post('/api/ems/send', [\app\api\controller\Ems::class, 'send']); // ==================== Admin 路由 ==================== // Admin 多为 JSON API,前端可能用 GET 传参查列表、POST 提交表单,使用 any 确保兼容 -// admin/index +// admin/index(小写) Route::get('/admin/index/index', [\app\admin\controller\Index::class, 'index']); Route::post('/admin/index/login', [\app\admin\controller\Index::class, 'login']); Route::post('/admin/index/logout', [\app\admin\controller\Index::class, 'logout']); +// 兼容前端请求 /admin/Index/*(首字母大写) +Route::get('/admin/Index/index', [\app\admin\controller\Index::class, 'index']); +Route::post('/admin/Index/login', [\app\admin\controller\Index::class, 'login']); +Route::post('/admin/Index/logout', [\app\admin\controller\Index::class, 'logout']); + // admin/dashboard Route::get('/admin/dashboard/index', [\app\admin\controller\Dashboard::class, 'index']); +// 兼容前端请求 /admin/Dashboard/* +Route::get('/admin/Dashboard/index', [\app\admin\controller\Dashboard::class, 'index']); // admin/module Route::get('/admin/module/index', [\app\admin\controller\Module::class, 'index']); @@ -81,7 +88,7 @@ Route::get('/admin/ajax/getTableList', [\app\admin\controller\Ajax::class, 'getT Route::get('/admin/ajax/getTableFieldList', [\app\admin\controller\Ajax::class, 'getTableFieldList']); Route::post('/admin/ajax/changeTerminalConfig', [\app\admin\controller\Ajax::class, 'changeTerminalConfig']); Route::post('/admin/ajax/clearCache', [\app\admin\controller\Ajax::class, 'clearCache']); -Route::any('/admin/ajax/terminal', [\app\admin\controller\Ajax::class, 'terminal']); +Route::add(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD'], '/admin/ajax/terminal', [\app\admin\controller\Ajax::class, 'terminal']); // admin/auth/admin Route::get('/admin/auth/admin/index', [\app\admin\controller\auth\Admin::class, 'index']); @@ -157,8 +164,8 @@ Route::get('/admin/crud/log/index', [\app\admin\controller\crud\Log::class, 'ind // admin/security/sensitiveData Route::get('/admin/security/sensitiveData/index', [\app\admin\controller\security\SensitiveData::class, 'index']); -Route::match(['get', 'post'], '/admin/security/sensitiveData/add', [\app\admin\controller\security\SensitiveData::class, 'add']); -Route::match(['get', 'post'], '/admin/security/sensitiveData/edit', [\app\admin\controller\security\SensitiveData::class, 'edit']); +Route::add(['GET', 'POST'], '/admin/security/sensitiveData/add', [\app\admin\controller\security\SensitiveData::class, 'add']); +Route::add(['GET', 'POST'], '/admin/security/sensitiveData/edit', [\app\admin\controller\security\SensitiveData::class, 'edit']); Route::post('/admin/security/sensitiveData/del', [\app\admin\controller\security\SensitiveData::class, 'del']); // admin/security/sensitiveDataLog @@ -168,11 +175,17 @@ Route::post('/admin/security/sensitiveDataLog/rollback', [\app\admin\controller\ // admin/security/dataRecycle Route::get('/admin/security/dataRecycle/index', [\app\admin\controller\security\DataRecycle::class, 'index']); -Route::match(['get', 'post'], '/admin/security/dataRecycle/add', [\app\admin\controller\security\DataRecycle::class, 'add']); -Route::match(['get', 'post'], '/admin/security/dataRecycle/edit', [\app\admin\controller\security\DataRecycle::class, 'edit']); +Route::add(['GET', 'POST'], '/admin/security/dataRecycle/add', [\app\admin\controller\security\DataRecycle::class, 'add']); +Route::add(['GET', 'POST'], '/admin/security/dataRecycle/edit', [\app\admin\controller\security\DataRecycle::class, 'edit']); Route::post('/admin/security/dataRecycle/del', [\app\admin\controller\security\DataRecycle::class, 'del']); // admin/security/dataRecycleLog Route::get('/admin/security/dataRecycleLog/index', [\app\admin\controller\security\DataRecycleLog::class, 'index']); Route::post('/admin/security/dataRecycleLog/restore', [\app\admin\controller\security\DataRecycleLog::class, 'restore']); Route::get('/admin/security/dataRecycleLog/info', [\app\admin\controller\security\DataRecycleLog::class, 'info']); + +// ==================== CORS 预检(OPTIONS) ==================== +// 放在最后注册;显式加上前端会请求的路径,再加固通配 +Route::add('OPTIONS', '/api/index/index', [\app\common\middleware\AllowCrossDomain::class, 'optionsResponse']); +Route::add('OPTIONS', '/api/{path:.+}', [\app\common\middleware\AllowCrossDomain::class, 'optionsResponse']); +Route::add('OPTIONS', '/admin/{path:.+}', [\app\common\middleware\AllowCrossDomain::class, 'optionsResponse']); diff --git a/web/.env.development b/web/.env.development index b3dc410..73f4d6c 100644 --- a/web/.env.development +++ b/web/.env.development @@ -4,5 +4,5 @@ ENV = 'development' # base路径 VITE_BASE_PATH = './' -# 本地环境接口地址 - 尾部无需带'/' -VITE_AXIOS_BASE_URL = 'http://localhost:8787' +# 本地环境接口地址 - 用空字符串走同源,由 vite 代理到 8787,避免 CORS +VITE_AXIOS_BASE_URL = '' diff --git a/web/vite.config.ts b/web/vite.config.ts index 408d745..cc763fe 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -27,6 +27,11 @@ const viteConfig = ({ mode }: ConfigEnv): UserConfig => { server: { port: parseInt(VITE_PORT), open: VITE_OPEN != 'false', + // 开发时把 /api、/admin 代理到 webman,避免跨域 + proxy: { + '/api': { target: 'http://localhost:8787', changeOrigin: true }, + '/admin': { target: 'http://localhost:8787', changeOrigin: true }, + }, }, build: { cssCodeSplit: false,