84 lines
3.6 KiB
PHP
84 lines
3.6 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace app\mcp\controller;
|
|
|
|
use app\BaseController;
|
|
use app\mcp\service\AuditLogger;
|
|
use app\mcp\service\ConsoleService;
|
|
use app\mcp\service\GrantService;
|
|
use app\mcp\service\Guard;
|
|
use app\mcp\service\McpConfig;
|
|
use app\mcp\service\McpException;
|
|
use think\Response;
|
|
|
|
/**
|
|
* AI 后台浏览器(供行知服务器上的内置浏览器调用,都要求 Bearer AI 授权令牌):
|
|
* POST /mcp/console/open 换取“AI 浏览器”专用终端的后台登录(令牌只给行知服务器,写进浏览器,不给模型)
|
|
* POST /mcp/console/close 作废该会话(行知关闭浏览器或空闲超时时调用;授权已失效时也照样注销)
|
|
* 失败时 data.reason 为 feature_disabled / console_disabled / no_console_permission / ip_not_allowed 等。
|
|
*/
|
|
class ConsoleController extends BaseController
|
|
{
|
|
public function open(): Response
|
|
{
|
|
$blocked = $this->blocked();
|
|
if ($blocked) {
|
|
return $blocked;
|
|
}
|
|
try {
|
|
$identity = GrantService::authenticate($this->request);
|
|
} catch (McpException $e) {
|
|
return Guard::envelope(-1, $e->getMessage(), ['reason' => $e->reason], 401);
|
|
}
|
|
$entry = ['grant_id' => $identity->grant['id'], 'admin_id' => $identity->adminId, 'tool' => 'console.open', 'resource' => 'console',
|
|
'client_task_id' => (string) $this->request->header('x-xingzhi-task-id', ''), 'ip' => $this->request->ip()];
|
|
try {
|
|
$data = ConsoleService::open($identity);
|
|
AuditLogger::log($entry + ['status' => 'ok']);
|
|
return Guard::envelope(1, '', $data);
|
|
} catch (McpException $e) {
|
|
AuditLogger::log($entry + ['status' => 'denied', 'message' => $e->reason]);
|
|
return Guard::envelope(0, $e->getMessage(), ['reason' => $e->reason], 200, 1);
|
|
}
|
|
}
|
|
|
|
public function close(): Response
|
|
{
|
|
$blocked = $this->blocked();
|
|
if ($blocked) {
|
|
return $blocked;
|
|
}
|
|
try {
|
|
$identity = GrantService::authenticate($this->request);
|
|
[$grantId, $adminId, $note] = [(int) $identity->grant['id'], $identity->adminId, ''];
|
|
} catch (McpException $e) {
|
|
// 授权已撤销、过期或账号失去权限时也照样注销它换来的后台会话:收回登录不需要授权仍然有效
|
|
$grant = GrantService::findByToken($this->request);
|
|
if (!$grant) {
|
|
return Guard::envelope(-1, $e->getMessage(), ['reason' => $e->reason], 401);
|
|
}
|
|
[$grantId, $adminId, $note] = [(int) $grant['id'], (int) $grant['admin_id'], ' (grant ' . $e->reason . ')'];
|
|
}
|
|
$closed = ConsoleService::closeForAdmin($adminId);
|
|
AuditLogger::log(['grant_id' => $grantId, 'admin_id' => $adminId, 'tool' => 'console.close', 'resource' => 'console',
|
|
'status' => 'ok', 'message' => ($closed ? 'closed' : 'none') . $note, 'ip' => $this->request->ip()]);
|
|
return Guard::envelope(1, $closed ? '已关闭' : '没有需要关闭的会话', ['closed' => $closed]);
|
|
}
|
|
|
|
private function blocked(): ?Response
|
|
{
|
|
if (!McpConfig::enabled()) {
|
|
return Guard::envelope(0, 'AI 助手接口未启用', ['reason' => 'feature_disabled'], 503, 1);
|
|
}
|
|
if ($this->request->method(true) !== 'POST') {
|
|
return response('', 405)->header(['Allow' => 'POST']);
|
|
}
|
|
$guard = Guard::check($this->request);
|
|
if ($guard !== null) {
|
|
return Guard::envelope(0, $guard[1], ['reason' => $guard[2]], 200, 1);
|
|
}
|
|
return null;
|
|
}
|
|
}
|