mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
fix(channels): resolve issue #19 — non-admin member errors on channel page
Three bugs surfaced when a non-admin workspace member opened the channel
admin page:
- vue-i18n "Invalid linked format" when '@' appeared in message strings
without the linked-format escape. Replaced literal '@' with vue-i18n v9
literal interpolation {'@'} in both zh-CN.ts and en-US.ts (6 strings:
QQ guide step3, accessControl requireMention/Tooltip).
- 403 from WorkspaceAccessInterceptor was being treated as 401 by the
axios interceptor and the chat SSE handler, clearing the token and
redirecting to /login. Split the two:
* 401 = authentication failure -> handleAuthFailure (logout)
* 403 = authorization failure -> keep session, surface to caller
Now a member who lacks workspace permission sees a toast instead of
being silently logged out.
- Two backend exception sites threw with the default code=500 for what
is semantically an auth/authz event, contradicting the codes returned
elsewhere for the same business event:
* AuthService.login() bad credentials 500 -> 401
* WorkspaceService.requirePermission() 500 -> 403
This aligns service-layer denials with SecurityConfig (401 for missing
JWT) and WorkspaceAccessInterceptor (403 for permission denied), so
the same business event always produces the same code.
This commit is contained in:
parent
2f93d53737
commit
03c8584910
@ -51,7 +51,7 @@ public class AuthService {
|
||||
.eq(UserEntity::getEnabled, true));
|
||||
|
||||
if (user == null || !passwordEncoder.matches(request.getPassword(), user.getPassword())) {
|
||||
throw new MateClawException("err.auth.invalid_credentials", "用户名或密码错误");
|
||||
throw new MateClawException("err.auth.invalid_credentials", 401, "用户名或密码错误");
|
||||
}
|
||||
|
||||
String token = generateToken(user);
|
||||
|
||||
@ -209,7 +209,7 @@ public class WorkspaceService {
|
||||
*/
|
||||
public void requirePermission(Long workspaceId, Long userId, String minRole) {
|
||||
if (!hasPermission(workspaceId, userId, minRole)) {
|
||||
throw new MateClawException("err.workspace.insufficient_permission", "权限不足:需要 " + minRole + " 或更高角色");
|
||||
throw new MateClawException("err.workspace.insufficient_permission", 403, "权限不足:需要 " + minRole + " 或更高角色");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -30,7 +30,9 @@ http.interceptors.response.use(
|
||||
// 后端统一响应格式 R<T>: { code: number, msg: string, data: T }
|
||||
if (data && typeof data === 'object' && 'code' in data) {
|
||||
if (data.code === 200) return data
|
||||
if (data.code === 401 || data.code === 403) {
|
||||
// 401 = authentication failure → log out
|
||||
// 403 = authorization failure (e.g. workspace permission denied) → keep session, surface error to caller
|
||||
if (data.code === 401) {
|
||||
handleAuthFailure()
|
||||
return Promise.reject(new Error(data.msg || 'Unauthorized'))
|
||||
}
|
||||
@ -39,7 +41,7 @@ http.interceptors.response.use(
|
||||
return data
|
||||
},
|
||||
(err) => {
|
||||
if (err.response?.status === 401 || err.response?.status === 403) {
|
||||
if (err.response?.status === 401) {
|
||||
handleAuthFailure()
|
||||
}
|
||||
return Promise.reject(err.response?.data?.msg || err.message)
|
||||
|
||||
@ -240,8 +240,9 @@ export function useStream(options: UseStreamOptions): UseStreamReturn {
|
||||
errorInfo.requestId = response.headers.get('X-Request-Id')
|
||||
|| errorBody?.requestId
|
||||
|| errorInfo.requestId
|
||||
// 401/403 时清除 token 并跳转登录页(与 http.ts 保持一致)
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
// 401 = authentication failure → log out (consistent with http.ts).
|
||||
// 403 = authorization failure (e.g. workspace permission denied) → keep session.
|
||||
if (response.status === 401) {
|
||||
handleAuthFailure()
|
||||
}
|
||||
throw Object.assign(new Error(errorMsg), { errorInfo })
|
||||
|
||||
@ -1561,8 +1561,8 @@ export default {
|
||||
allowFromPlaceholder: 'Comma-separated user IDs (empty = allow all)',
|
||||
denyMessage: 'Deny Message',
|
||||
denyMessagePlaceholder: 'Sorry, you do not have permission',
|
||||
requireMention: 'Require @mention',
|
||||
requireMentionTooltip: 'Whether the bot requires @mention in group chats',
|
||||
requireMention: "Require {'@'}mention",
|
||||
requireMentionTooltip: "Whether the bot requires {'@'}mention in group chats",
|
||||
},
|
||||
messageFilter: {
|
||||
title: 'Message Filter',
|
||||
@ -1639,7 +1639,7 @@ export default {
|
||||
qq: {
|
||||
step1: 'Go to <a href="https://q.qq.com/" target="_blank" rel="noopener">QQ Open Platform</a> and create a bot application',
|
||||
step2: 'Get <b>AppID</b> and <b>AppSecret</b> from the app management page, fill in below',
|
||||
step3: 'Enable required message types in "Feature Config → Message Subscription" (C2C, Group @, Channel messages, etc.)',
|
||||
step3: "Enable required message types in \"Feature Config → Message Subscription\" (C2C, Group {'@'}, Channel messages, etc.)",
|
||||
step4: 'After starting the channel, auto-receives messages via <b>WebSocket long connection</b>, <b>no public IP or callback URL required</b>',
|
||||
},
|
||||
},
|
||||
|
||||
@ -1571,8 +1571,8 @@ export default {
|
||||
allowFromPlaceholder: '逗号分隔用户 ID(留空 = 全部允许)',
|
||||
denyMessage: '拒绝提示',
|
||||
denyMessagePlaceholder: '抱歉,您没有使用权限',
|
||||
requireMention: '需要 @提及',
|
||||
requireMentionTooltip: '群聊中是否需要 @机器人才响应',
|
||||
requireMention: "需要 {'@'}提及",
|
||||
requireMentionTooltip: "群聊中是否需要 {'@'}机器人才响应",
|
||||
},
|
||||
messageFilter: {
|
||||
title: '消息过滤',
|
||||
@ -1649,7 +1649,7 @@ export default {
|
||||
qq: {
|
||||
step1: '前往 <a href="https://q.qq.com/" target="_blank" rel="noopener">QQ 开放平台</a> 创建机器人应用',
|
||||
step2: '在应用管理页面获取 <b>AppID</b> 和 <b>AppSecret</b>,填入下方配置',
|
||||
step3: '在「功能配置 → 消息订阅」中开启需要的消息类型(C2C 消息、群聊 @消息、频道消息等)',
|
||||
step3: "在「功能配置 → 消息订阅」中开启需要的消息类型(C2C 消息、群聊 {'@'}消息、频道消息等)",
|
||||
step4: '启动渠道后通过 <b>WebSocket 长连接</b>自动接收消息,<b>无需公网 IP 和回调 URL</b>',
|
||||
},
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user