diff --git a/mateclaw-server/src/main/java/vip/mate/datasource/controller/DatasourceController.java b/mateclaw-server/src/main/java/vip/mate/datasource/controller/DatasourceController.java index 6b432246..b923226a 100644 --- a/mateclaw-server/src/main/java/vip/mate/datasource/controller/DatasourceController.java +++ b/mateclaw-server/src/main/java/vip/mate/datasource/controller/DatasourceController.java @@ -10,6 +10,7 @@ import vip.mate.datasource.service.DatasourceService; import java.util.List; import java.util.Map; +import vip.mate.workspace.core.annotation.RequireWorkspaceRole; /** * 数据源管理接口 @@ -26,24 +27,28 @@ public class DatasourceController { @Operation(summary = "获取数据源列表") @GetMapping + @RequireWorkspaceRole("admin") public R> list() { return R.ok(datasourceService.listAll()); } @Operation(summary = "获取数据源详情") @GetMapping("/{id}") + @RequireWorkspaceRole("admin") public R get(@PathVariable Long id) { return R.ok(datasourceService.getByIdMasked(id)); } @Operation(summary = "创建数据源") @PostMapping + @RequireWorkspaceRole("admin") public R create(@RequestBody DatasourceEntity entity) { return R.ok(datasourceService.create(entity)); } @Operation(summary = "更新数据源") @PutMapping("/{id}") + @RequireWorkspaceRole("admin") public R update(@PathVariable Long id, @RequestBody DatasourceEntity entity) { entity.setId(id); return R.ok(datasourceService.update(entity)); @@ -51,6 +56,7 @@ public class DatasourceController { @Operation(summary = "删除数据源") @DeleteMapping("/{id}") + @RequireWorkspaceRole("admin") public R delete(@PathVariable Long id) { datasourceService.delete(id); return R.ok(); @@ -58,6 +64,7 @@ public class DatasourceController { @Operation(summary = "测试数据源连接") @PostMapping("/{id}/test") + @RequireWorkspaceRole("admin") public R> testConnection(@PathVariable Long id) { boolean ok = datasourceService.testConnection(id); return R.ok(Map.of("success", ok, "message", ok ? "连接成功" : "连接失败")); @@ -65,6 +72,7 @@ public class DatasourceController { @Operation(summary = "启用/禁用数据源") @PutMapping("/{id}/toggle") + @RequireWorkspaceRole("admin") public R toggle(@PathVariable Long id, @RequestParam boolean enabled) { return R.ok(datasourceService.toggle(id, enabled)); } diff --git a/mateclaw-server/src/main/java/vip/mate/llm/controller/ClaudeCodeOAuthController.java b/mateclaw-server/src/main/java/vip/mate/llm/controller/ClaudeCodeOAuthController.java index 31d0a897..3ab5ede3 100644 --- a/mateclaw-server/src/main/java/vip/mate/llm/controller/ClaudeCodeOAuthController.java +++ b/mateclaw-server/src/main/java/vip/mate/llm/controller/ClaudeCodeOAuthController.java @@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.RestController; import vip.mate.common.result.R; import vip.mate.llm.anthropic.oauth.ClaudeCodeOAuthService; import vip.mate.llm.anthropic.oauth.ClaudeCodeOAuthService.OAuthStatus; +import vip.mate.workspace.core.annotation.RequireWorkspaceRole; /** * RFC-062 PR-3: management-UI surface for the Claude Code OAuth provider. @@ -45,6 +46,7 @@ public class ClaudeCodeOAuthController { @Operation(summary = "Read current Claude Code OAuth credential status from local disk") @GetMapping("/status") + @RequireWorkspaceRole("admin") public R status() { return R.ok(oauthService.getStatus()); } @@ -60,6 +62,7 @@ public class ClaudeCodeOAuthController { */ @Operation(summary = "Force re-detect credentials and refresh if near expiry") @PostMapping("/reload") + @RequireWorkspaceRole("admin") public R reload() { try { oauthService.getValidToken(); diff --git a/mateclaw-server/src/main/java/vip/mate/llm/controller/ModelConfigController.java b/mateclaw-server/src/main/java/vip/mate/llm/controller/ModelConfigController.java index 960d5238..b79f1a3e 100644 --- a/mateclaw-server/src/main/java/vip/mate/llm/controller/ModelConfigController.java +++ b/mateclaw-server/src/main/java/vip/mate/llm/controller/ModelConfigController.java @@ -20,6 +20,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import java.util.HashMap; import java.util.List; import java.util.Map; +import vip.mate.workspace.core.annotation.RequireWorkspaceRole; @Tag(name = "模型配置管理") @RestController @@ -37,42 +38,49 @@ public class ModelConfigController { @Operation(summary = "获取 Provider 列表(仅 enabled)") @GetMapping + @RequireWorkspaceRole("admin") public R> list() { return R.ok(modelProviderService.listProviders()); } @Operation(summary = "RFC-074: 获取 Provider 全量目录(含未启用),供 Add Provider 抽屉使用") @GetMapping("/catalog") + @RequireWorkspaceRole("admin") public R> catalog() { return R.ok(modelProviderService.listCatalog()); } @Operation(summary = "RFC-074: 启用 Provider") @PostMapping("/{providerId}/enable") + @RequireWorkspaceRole("admin") public R enableProvider(@PathVariable String providerId) { return R.ok(modelProviderService.setEnabled(providerId, true)); } @Operation(summary = "RFC-074: 禁用 Provider(如其下模型为当前默认会自动切换)") @PostMapping("/{providerId}/disable") + @RequireWorkspaceRole("admin") public R disableProvider(@PathVariable String providerId) { return R.ok(modelProviderService.setEnabled(providerId, false)); } @Operation(summary = "获取启用模型列表") @GetMapping("/enabled") + @RequireWorkspaceRole("admin") public R> listEnabled() { return R.ok(modelConfigService.listEnabledModels()); } @Operation(summary = "获取默认模型") @GetMapping("/default") + @RequireWorkspaceRole("admin") public R getDefaultModel() { return R.ok(modelConfigService.getDefaultModel()); } @Operation(summary = "获取当前激活模型") @GetMapping("/active") + @RequireWorkspaceRole("admin") public R getActiveModel() { ModelConfigEntity model = modelConfigService.getDefaultModel(); ActiveModelsInfo info = new ActiveModelsInfo(); @@ -82,6 +90,7 @@ public class ModelConfigController { @Operation(summary = "设置当前激活模型") @PutMapping("/active") + @RequireWorkspaceRole("admin") public R setActiveModel(@RequestBody ModelSlotRequest request) { ModelConfigEntity model = modelConfigService.setDefaultModel(request.getProviderId(), request.getModel()); ActiveModelsInfo info = new ActiveModelsInfo(); @@ -91,6 +100,7 @@ public class ModelConfigController { @Operation(summary = "更新 Provider 配置") @PutMapping("/{providerId}/config") + @RequireWorkspaceRole("admin") public R updateProviderConfig(@PathVariable String providerId, @RequestBody ProviderConfigRequest request) { ProviderInfoDTO updated = modelProviderService.updateProviderConfig(providerId, request); @@ -101,12 +111,14 @@ public class ModelConfigController { @Operation(summary = "创建自定义 Provider") @PostMapping("/custom-providers") + @RequireWorkspaceRole("admin") public R createCustomProvider(@RequestBody CreateCustomProviderRequest request) { return R.ok(modelProviderService.createCustomProvider(request)); } @Operation(summary = "删除自定义 Provider") @DeleteMapping("/custom-providers/{providerId}") + @RequireWorkspaceRole("admin") public R deleteCustomProvider(@PathVariable String providerId) { modelProviderService.deleteCustomProvider(providerId); return R.ok(); @@ -122,6 +134,7 @@ public class ModelConfigController { */ @Operation(summary = "删除自定义 Provider(查询参数变体,兼容含特殊字符的旧 ID)") @DeleteMapping("/custom-providers") + @RequireWorkspaceRole("admin") public R deleteCustomProviderByQuery(@RequestParam("providerId") String providerId) { modelProviderService.deleteCustomProvider(providerId); return R.ok(); @@ -129,6 +142,7 @@ public class ModelConfigController { @Operation(summary = "向 Provider 添加模型") @PostMapping("/{providerId}/models") + @RequireWorkspaceRole("admin") public R addProviderModel(@PathVariable String providerId, @RequestBody AddProviderModelRequest request) { return R.ok(modelProviderService.addModel(providerId, request)); @@ -136,6 +150,7 @@ public class ModelConfigController { @Operation(summary = "从 Provider 删除模型") @DeleteMapping("/{providerId}/models/{modelId}") + @RequireWorkspaceRole("admin") public R removeProviderModel(@PathVariable String providerId, @PathVariable String modelId) { return R.ok(modelProviderService.removeModel(providerId, modelId)); @@ -143,18 +158,21 @@ public class ModelConfigController { @Operation(summary = "获取模型详情") @GetMapping("/{id}") + @RequireWorkspaceRole("admin") public R get(@PathVariable Long id) { return R.ok(modelConfigService.getModel(id)); } @Operation(summary = "创建模型") @PostMapping + @RequireWorkspaceRole("admin") public R create(@RequestBody ModelConfigEntity entity) { return R.ok(modelConfigService.createModel(entity)); } @Operation(summary = "更新模型") @PutMapping("/{id}") + @RequireWorkspaceRole("admin") public R update(@PathVariable Long id, @RequestBody ModelConfigEntity entity) { entity.setId(id); return R.ok(modelConfigService.updateModel(entity)); @@ -162,6 +180,7 @@ public class ModelConfigController { @Operation(summary = "删除模型") @DeleteMapping("/{id}") + @RequireWorkspaceRole("admin") public R delete(@PathVariable Long id) { modelConfigService.deleteModel(id); return R.ok(); @@ -169,6 +188,7 @@ public class ModelConfigController { @Operation(summary = "设置默认模型") @PostMapping("/{id}/default") + @RequireWorkspaceRole("admin") public R setDefault(@PathVariable Long id) { return R.ok(modelConfigService.setDefaultModel(id)); } @@ -177,12 +197,14 @@ public class ModelConfigController { @Operation(summary = "发现远端模型") @PostMapping("/{providerId}/discover") + @RequireWorkspaceRole("admin") public R discoverModels(@PathVariable String providerId) { return R.ok(modelDiscoveryService.discoverModels(providerId)); } @Operation(summary = "批量添加发现的模型") @PostMapping("/{providerId}/discover/apply") + @RequireWorkspaceRole("admin") public R> applyDiscoveredModels(@PathVariable String providerId, @RequestBody ApplyDiscoveredModelsRequest request) { int added = modelDiscoveryService.batchAddModels(providerId, request.getModelIds()); @@ -191,12 +213,14 @@ public class ModelConfigController { @Operation(summary = "测试供应商连接") @PostMapping("/{providerId}/test-connection") + @RequireWorkspaceRole("admin") public R testConnection(@PathVariable String providerId) { return R.ok(modelDiscoveryService.testConnection(providerId)); } @Operation(summary = "测试单个模型可用性") @PostMapping("/{providerId}/models/{modelId}/test") + @RequireWorkspaceRole("admin") public R testModel(@PathVariable String providerId, @PathVariable String modelId) { return R.ok(modelDiscoveryService.testModel(providerId, modelId)); @@ -206,6 +230,7 @@ public class ModelConfigController { @Operation(summary = "按类型筛选模型(chat / embedding),可选 modality 过滤") @GetMapping("/by-type") + @RequireWorkspaceRole("admin") public R> listByType( @RequestParam(defaultValue = "chat") String modelType, @RequestParam(required = false) String modality) { @@ -214,6 +239,7 @@ public class ModelConfigController { @Operation(summary = "测试 Embedding 模型连通性(嵌入一个短文本验证 API key)") @PostMapping("/embedding/{modelId}/test") + @RequireWorkspaceRole("admin") public R> testEmbedding(@PathVariable Long modelId) { Map result = new HashMap<>(); try { @@ -241,6 +267,7 @@ public class ModelConfigController { @Operation(summary = "获取系统默认 Embedding 模型 ID") @GetMapping("/embedding/default") + @RequireWorkspaceRole("admin") public R> getDefaultEmbedding() { SystemSettingEntity entity = systemSettingMapper.selectOne( new LambdaQueryWrapper() @@ -254,6 +281,7 @@ public class ModelConfigController { @Operation(summary = "设置系统默认 Embedding 模型") @PostMapping("/embedding/default") + @RequireWorkspaceRole("admin") public R setDefaultEmbedding(@RequestBody Map body) { Object v = body.get("modelId"); String value = v == null ? "" : v.toString(); diff --git a/mateclaw-server/src/main/java/vip/mate/llm/controller/OAuthController.java b/mateclaw-server/src/main/java/vip/mate/llm/controller/OAuthController.java index cde8c96c..3a8a95ab 100644 --- a/mateclaw-server/src/main/java/vip/mate/llm/controller/OAuthController.java +++ b/mateclaw-server/src/main/java/vip/mate/llm/controller/OAuthController.java @@ -12,6 +12,7 @@ import vip.mate.llm.oauth.OpenAIDeviceCodeService.DeviceCodeStartResult; import vip.mate.llm.oauth.OpenAIOAuthService; import vip.mate.llm.oauth.OpenAIOAuthService.OAuthAuthorizeResult; import vip.mate.llm.oauth.OpenAIOAuthService.OAuthStatusResult; +import vip.mate.workspace.core.annotation.RequireWorkspaceRole; @Tag(name = "OpenAI OAuth") @RestController @@ -24,6 +25,7 @@ public class OAuthController { @Operation(summary = "获取 OAuth 授权 URL(自动选 LOCAL / MANUAL_PASTE 模式)") @GetMapping("/authorize") + @RequireWorkspaceRole("admin") public R authorize(HttpServletRequest request) { // 用 Host header 判断是否远程部署 — 远程则不启 localhost server,进 MANUAL_PASTE 流。 // 优先 X-Forwarded-Host(反向代理后的实际入口),fallback 到 Host。 @@ -42,6 +44,7 @@ public class OAuthController { */ @Operation(summary = "MANUAL_PASTE 模式:用户粘贴浏览器回调 URL 完成 OAuth") @PostMapping("/callback-paste") + @RequireWorkspaceRole("admin") public R callbackPaste(@RequestBody PasteRequest request) { oauthService.completeFromPastedUrl(request.callbackUrl()); return R.ok(); @@ -52,18 +55,21 @@ public class OAuthController { @Operation(summary = "Device flow: start — request user_code") @PostMapping("/device/start") + @RequireWorkspaceRole("admin") public R deviceStart() { return R.ok(deviceCodeService.start()); } @Operation(summary = "Device flow: poll for completion") @PostMapping("/device/poll") + @RequireWorkspaceRole("admin") public R devicePoll(@RequestBody DeviceRequest request) { return R.ok(deviceCodeService.poll(request.deviceAuthId())); } @Operation(summary = "Device flow: cancel a pending session") @PostMapping("/device/cancel") + @RequireWorkspaceRole("admin") public R deviceCancel(@RequestBody DeviceRequest request) { deviceCodeService.cancel(request.deviceAuthId()); return R.ok(); @@ -74,6 +80,7 @@ public class OAuthController { @Operation(summary = "手动刷新 Token") @PostMapping("/refresh") + @RequireWorkspaceRole("admin") public R refresh() { oauthService.refreshToken(); return R.ok(); @@ -81,6 +88,7 @@ public class OAuthController { @Operation(summary = "清除 OAuth 凭证") @DeleteMapping("/revoke") + @RequireWorkspaceRole("admin") public R revoke() { oauthService.revokeToken(); return R.ok(); @@ -88,6 +96,7 @@ public class OAuthController { @Operation(summary = "获取 OAuth 连接状态") @GetMapping("/status") + @RequireWorkspaceRole("admin") public R status() { return R.ok(oauthService.getStatus()); } diff --git a/mateclaw-server/src/main/java/vip/mate/llm/controller/ProviderPoolController.java b/mateclaw-server/src/main/java/vip/mate/llm/controller/ProviderPoolController.java index 11be60d4..f51e8f72 100644 --- a/mateclaw-server/src/main/java/vip/mate/llm/controller/ProviderPoolController.java +++ b/mateclaw-server/src/main/java/vip/mate/llm/controller/ProviderPoolController.java @@ -21,6 +21,7 @@ import vip.mate.llm.service.ModelProviderService; import java.util.ArrayList; import java.util.List; import java.util.Map; +import vip.mate.workspace.core.annotation.RequireWorkspaceRole; /** * RFC-009 Phase 4 — read-only diagnostic endpoint for the provider pool. @@ -49,6 +50,7 @@ public class ProviderPoolController { @Operation(summary = "查询所有 provider 的池状态 + 冷却信息") @GetMapping + @RequireWorkspaceRole("admin") public R> snapshot() { Map poolView = providerPool.snapshot(); Map healthView = healthTracker.snapshot(); @@ -81,6 +83,7 @@ public class ProviderPoolController { @Operation(summary = "手动重新探测某个 provider,立即更新池状态") @PostMapping("/{providerId}/reprobe") + @RequireWorkspaceRole("admin") public R reprobe(@PathVariable String providerId) { ProbeResult result = initProbe.probeOne(providerId); return R.ok(new ReprobeResultDTO(