feat: 添加MinIO文件系统支持并优化外部连接器功能

- 添加MinIO用户文件系统配置选项(BEAVER_MINIO_ROOT_USER等)
- 更新外部连接器配置结构,包括BASE_URL和认证令牌设置
- 改进connector provider支持更多类型(official, feishu_bot等)
- 实现Mistral模型推理模式支持reasoning_effort参数
- 增强外部连接器策略配置和运行时配置管理
- 添加connector bridge事件验证和安全保护机制
- 优化任务路由逻辑,区分simple_chat和new_task场景
- 更新初始技能工具提示配置,分离authoring admin功能
This commit is contained in:
2026-06-05 11:46:40 +08:00
parent 236ac19789
commit 2c5205b06e
120 changed files with 8321 additions and 1865 deletions

View File

@ -31,6 +31,7 @@ html,
body {
margin: 0;
min-height: 100%;
overflow-x: hidden;
}
body {
@ -56,7 +57,7 @@ select {
.portal-page {
position: relative;
min-height: 100vh;
min-height: 100dvh;
display: grid;
place-items: center;
padding: 32px;
@ -68,7 +69,7 @@ select {
.auth-page {
width: 100%;
min-height: 100vh;
min-height: 100dvh;
display: flex;
align-items: center;
justify-content: flex-end;
@ -122,11 +123,11 @@ select {
.ghost-icon-button {
position: absolute;
right: 18px;
right: 10px;
top: 50%;
z-index: 2;
width: 30px;
height: 30px;
width: 44px;
height: 44px;
padding: 0;
border: 0;
color: #a29d99;
@ -203,6 +204,9 @@ select {
.login-footer a {
justify-self: start;
min-height: 44px;
display: inline-flex;
align-items: center;
}
.portal-toolbar {
@ -234,8 +238,8 @@ select {
}
.language-switcher button {
min-width: 34px;
height: 28px;
min-width: 44px;
min-height: 44px;
border: 0;
border-radius: 999px;
color: var(--zinc-600);
@ -524,7 +528,7 @@ select {
.auth-page .auth-card.login-card {
width: 100%;
max-height: calc(100vh - clamp(48px, 10vh, 112px));
max-height: calc(100dvh - clamp(48px, 10vh, 112px));
padding: clamp(30px, 5vh, 54px) clamp(24px, 3.2vw, 44px) clamp(26px, 4vh, 40px);
display: flex;
flex-direction: column;
@ -578,6 +582,14 @@ select {
display: block;
}
.auth-page .login-field-label {
display: block;
margin-bottom: 8px;
color: var(--zinc-600);
font-size: 13px;
font-weight: 700;
}
.auth-page .login-field input,
.auth-page .login-field select {
min-height: clamp(50px, 6vh, 60px);
@ -680,7 +692,7 @@ select {
.auth-page .auth-card.login-card {
min-height: auto;
padding: 34px 22px 28px;
max-height: calc(100vh - 104px);
max-height: calc(100dvh - 104px);
}
.auth-page .login-logo {
@ -722,3 +734,134 @@ select {
padding: 24px 20px;
}
}
@media (max-width: 640px) and (max-height: 700px) {
.auth-page {
padding: 76px 16px 12px;
}
.auth-page .auth-card.login-card {
max-height: calc(100dvh - 88px);
padding: 20px 22px 18px;
}
.auth-page .login-logo {
width: 58px;
margin-bottom: 10px;
}
.auth-page .auth-card.login-card h1 {
margin-bottom: 14px;
font-size: 23px;
}
.auth-page .login-card .auth-form {
gap: 8px;
}
.auth-page .login-field-label {
margin-bottom: 4px;
font-size: 12px;
}
.auth-page .login-field input,
.auth-page .login-field select {
min-height: 46px;
padding-top: 10px;
padding-bottom: 10px;
}
.auth-page .login-card .primary-button {
min-height: 48px;
}
.auth-page .login-card .error-text {
min-height: 18px;
}
.auth-page .login-divider {
margin: 12px 0 8px;
font-size: 13px;
}
.auth-page .login-footer {
margin-top: 0;
font-size: 13px;
}
}
@media (max-height: 520px) and (orientation: landscape) {
.portal-page {
display: block;
min-height: 100dvh;
overflow-y: auto;
}
.auth-page {
min-height: 100dvh;
align-items: flex-start;
justify-content: center;
padding: 52px 16px 6px;
}
.auth-page .portal-panel {
width: min(480px, 100%);
}
.auth-page .auth-card.login-card {
max-height: calc(100dvh - 64px);
padding: 12px 22px 10px;
overflow: visible;
}
.auth-page .login-logo {
width: 34px;
margin-bottom: 2px;
}
.auth-page .auth-card.login-card h1 {
margin-bottom: 4px;
font-size: 20px;
}
.auth-page .login-card .auth-form {
gap: 4px;
}
.auth-page .login-field-label {
margin-bottom: 2px;
font-size: 11px;
}
.auth-page .login-field input,
.auth-page .login-field select {
min-height: 44px;
padding-top: 8px;
padding-bottom: 8px;
}
.auth-page .login-card .error-text {
min-height: 14px;
font-size: 12px;
}
.auth-page .login-card .primary-button {
min-height: 44px;
padding-top: 8px;
padding-bottom: 8px;
}
.auth-page .login-divider {
margin: 6px 0 4px;
font-size: 12px;
}
.auth-page .login-footer {
margin-top: 0;
font-size: 12px;
}
.portal-toolbar {
top: 8px;
}
}

View File

@ -3,7 +3,7 @@
import Image from 'next/image';
import Link from 'next/link';
import { useSearchParams } from 'next/navigation';
import { useState } from 'react';
import { useRef, useState } from 'react';
import { LanguageSwitcher } from '@/components/LanguageSwitcher';
import { buildFrontendHandoffUrl, login, withNext } from '@/lib/auth-client';
@ -20,6 +20,7 @@ export default function LoginPage() {
const [showPassword, setShowPassword] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const errorRef = useRef<HTMLDivElement>(null);
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
@ -30,7 +31,14 @@ export default function LoginPage() {
const response = await login(username, password);
window.location.replace(buildFrontendHandoffUrl(response, nextPath));
} catch (err) {
setError(err instanceof Error ? err.message : pickPortalText(locale, '登录失败,请稍后重试', 'Sign-in failed. Please try again.'));
const rawMessage = err instanceof Error ? err.message : '';
const friendlyMessage = /401|Invalid credentials|用户名或密码/.test(rawMessage)
? pickPortalText(locale, '用户名或密码错误,请检查后重试。', 'Username or password is incorrect. Please check and try again.')
: pickPortalText(locale, '登录失败,请稍后重试。', 'Sign-in failed. Please try again.');
setError(friendlyMessage);
window.requestAnimationFrame(() => {
errorRef.current?.focus();
});
} finally {
setLoading(false);
}
@ -56,7 +64,7 @@ export default function LoginPage() {
<form className="auth-form" onSubmit={handleSubmit}>
<div className="field login-field">
<label className="visually-hidden" htmlFor="username">{pickPortalText(locale, '用户名', 'Username')}</label>
<label className="login-field-label" htmlFor="username">{pickPortalText(locale, '用户名', 'Username')}</label>
<UserIcon />
<input
id="username"
@ -69,7 +77,7 @@ export default function LoginPage() {
</div>
<div className="field login-field">
<label className="visually-hidden" htmlFor="password">{pickPortalText(locale, '密码', 'Password')}</label>
<label className="login-field-label" htmlFor="password">{pickPortalText(locale, '密码', 'Password')}</label>
<LockIcon />
<input
id="password"
@ -90,9 +98,24 @@ export default function LoginPage() {
</button>
</div>
<div className="error-text">{error}</div>
<div
ref={errorRef}
className="error-text"
role={error ? 'alert' : undefined}
aria-live="polite"
tabIndex={error ? -1 : undefined}
>
{error}
</div>
<button className="primary-button" type="submit" disabled={loading}>
<button
className="primary-button"
type="submit"
disabled={loading}
aria-label={loading
? pickPortalText(locale, '登录中', 'Signing in')
: pickPortalText(locale, '登录', 'Sign in')}
>
{loading
? pickPortalText(locale, '登录中...', 'Signing in...')
: <ArrowRightIcon />}

View File

@ -0,0 +1,4 @@
{
"status": "failed",
"failedTests": []
}