package api import ( "github.com/gin-gonic/gin" "oci-portal/internal/service" ) // NewRouter 组装全部 HTTP 路由:/auth/login 公开,业务路由要求 JWT。 func NewRouter(auth *service.AuthService, oauth *service.OAuthService, ociConfigs *service.OciConfigService, tasks *service.TaskService, console *service.ConsoleService, settings *service.SettingService, notifier *service.Notifier, systemLogs *service.SystemLogService, logEvents *service.LogEventService, proxies *service.ProxyService, aiGateway *service.AiGatewayService) *gin.Engine { r := gin.New() // 禁用 gin 内置代理信任:真实 IP 统一由 RealIPMiddleware 按安全设置解析 _ = r.SetTrustedProxies(nil) // webhook 注册在全局 Logger 之前:访问日志不落含 secret 的路径; // 不挂系统日志中间件——投递高频且已在回传日志留痕,避免刷屏 wh := &webhookHandler{events: logEvents} r.POST("/api/v1/webhooks/oci-logs/:secret", gin.Recovery(), RealIPMiddleware(settings), wh.handle) r.Use(gin.Logger(), RealIPMiddleware(settings), IPRateMiddleware(settings), gin.Recovery()) v1 := r.Group("/api/v1") ah := &authHandler{svc: auth, logs: systemLogs} v1.POST("/auth/login", ah.login) // 外部身份登录(公开):provider 列表 / 授权跳转(bind 模式 handler 内校验 JWT)/ 回调 ax := &authxHandler{auth: auth, oauth: oauth} v1.GET("/auth/oauth/providers", ax.oauthProviders) v1.GET("/auth/oauth/:provider/authorize", ax.oauthAuthorize) v1.GET("/auth/oauth/:provider/callback", ax.oauthCallback) // 控制台数据面:浏览器 WebSocket 无法带自定义头,token 经 query 在 handler 内校验 ch := &consoleHandler{svc: console, auth: auth} v1.GET("/console-sessions/:sessionId/ws", ch.ws) // AI 网关对外端点:独立密钥鉴权(Bearer / x-api-key),不挂 JWT 与系统日志 // (高频调用自有 AiCallLog);挂在全局 Use 之后,仍受 IP 限速与 Recovery 保护 aih := &aiGatewayHandler{gw: aiGateway} ai := r.Group("/ai/v1", aih.auth) ai.POST("/chat/completions", aih.chatCompletions) ai.POST("/responses", aih.responses) ai.POST("/messages", aih.messages) ai.POST("/embeddings", aih.embeddings) ai.GET("/models", aih.listModels) // 系统日志中间件挂在鉴权之后,写请求自动留痕(webconsole ws 为 GET,天然跳过) secured := v1.Group("", RequireAuth(auth), systemLogMiddleware(systemLogs)) secured.POST("/auth/logout", ah.logout) secured.GET("/regions", listRegions) secured.GET("/system-logs", (&systemLogHandler{svc: systemLogs}).list) secured.POST("/oci-configs/:id/instances/:instanceId/console-sessions", ch.create) secured.DELETE("/console-sessions/:sessionId", ch.remove) st := &settingsHandler{svc: settings, notifier: notifier} secured.GET("/about", about) secured.GET("/settings/telegram", st.getTelegram) secured.PUT("/settings/telegram", st.updateTelegram) secured.POST("/settings/telegram/test", st.testTelegram) secured.GET("/settings/notify-events", st.getNotifyEvents) secured.PUT("/settings/notify-events", st.updateNotifyEvents) secured.GET("/settings/notify-templates", st.listNotifyTemplates) secured.PUT("/settings/notify-templates/:kind", st.updateNotifyTemplate) secured.POST("/settings/notify-templates/:kind/test", st.testNotifyTemplate) secured.GET("/settings/task", st.getTaskSettings) secured.PUT("/settings/task", st.updateTaskSettings) secured.GET("/settings/security", st.getSecurity) secured.PUT("/settings/security", st.updateSecurity) // 两步验证与外部身份管理(JWT 组内) secured.GET("/auth/totp", ax.totpStatus) secured.POST("/auth/totp/setup", ax.totpSetup) secured.POST("/auth/totp/activate", ax.totpActivate) secured.POST("/auth/totp/disable", ax.totpDisable) secured.GET("/auth/credentials", ax.getCredentials) secured.PUT("/auth/credentials", ax.updateCredentials) secured.PUT("/auth/password-login", ax.updatePasswordLogin) secured.GET("/auth/identities", ax.identities) secured.DELETE("/auth/identities/:id", ax.unbindIdentity) secured.GET("/settings/oauth", func(c *gin.Context) { ax.getOAuthSettings(c, settings) }) secured.PUT("/settings/oauth", func(c *gin.Context) { ax.updateOAuthSettings(c, settings) }) px := &proxyHandler{svc: proxies} secured.GET("/proxies", px.list) secured.POST("/proxies", px.create) secured.POST("/proxies/import", px.importBatch) secured.PUT("/proxies/:id", px.update) secured.DELETE("/proxies/:id", px.remove) secured.POST("/proxies/:id/probe", px.probe) le := &logEventHandler{svc: logEvents} secured.GET("/log-events", le.list) secured.GET("/oci-configs/:id/log-webhook", le.getWebhook) secured.POST("/oci-configs/:id/log-webhook", le.ensureWebhook) secured.DELETE("/oci-configs/:id/log-webhook", le.revokeWebhook) secured.GET("/oci-configs/:id/log-relay", le.getRelay) secured.POST("/oci-configs/:id/log-relay", le.setupRelay) secured.DELETE("/oci-configs/:id/log-relay", le.teardownRelay) t := &taskHandler{svc: tasks} secured.POST("/tasks", t.create) secured.GET("/tasks", t.list) secured.GET("/tasks/:id", t.get) secured.PUT("/tasks/:id", t.update) secured.DELETE("/tasks/:id", t.remove) secured.GET("/tasks/:id/logs", t.logs) secured.POST("/tasks/:id/run", t.run) h := &ociConfigHandler{svc: ociConfigs} secured.GET("/overview", h.overview) secured.POST("/oci-configs", h.create) secured.GET("/oci-configs", h.list) secured.GET("/oci-configs/:id", h.get) secured.PUT("/oci-configs/:id", h.update) secured.POST("/oci-configs/:id/verify", h.verify) secured.DELETE("/oci-configs/:id", h.remove) secured.GET("/oci-configs/:id/compartments", h.compartments) secured.GET("/oci-configs/:id/cached-regions", h.cachedRegions) secured.GET("/oci-configs/:id/cached-compartments", h.cachedCompartments) secured.GET("/oci-configs/:id/region-subscriptions", h.regionSubscriptions) secured.POST("/oci-configs/:id/region-subscriptions", h.subscribeRegion) secured.GET("/oci-configs/:id/limits", h.limits) secured.GET("/oci-configs/:id/limits/services", h.limitServices) secured.GET("/oci-configs/:id/subscriptions", h.subscriptions) secured.GET("/oci-configs/:id/subscriptions/:subscriptionId", h.subscriptionDetail) secured.GET("/oci-configs/:id/shapes", h.shapes) secured.GET("/oci-configs/:id/images", h.images) secured.GET("/oci-configs/:id/images/:imageId", h.getImage) secured.GET("/oci-configs/:id/vcns", h.listVCNs) secured.POST("/oci-configs/:id/vcns", h.createVCN) secured.GET("/oci-configs/:id/vcns/:vcnId", h.getVCN) secured.PUT("/oci-configs/:id/vcns/:vcnId", h.updateVCN) secured.DELETE("/oci-configs/:id/vcns/:vcnId", h.deleteVCN) secured.POST("/oci-configs/:id/vcns/:vcnId/enable-ipv6", h.enableVCNIPv6) secured.GET("/oci-configs/:id/subnets", h.listSubnets) secured.POST("/oci-configs/:id/subnets", h.createSubnet) secured.GET("/oci-configs/:id/subnets/:subnetId", h.getSubnet) secured.PUT("/oci-configs/:id/subnets/:subnetId", h.updateSubnet) secured.DELETE("/oci-configs/:id/subnets/:subnetId", h.deleteSubnet) secured.GET("/oci-configs/:id/security-lists", h.listSecurityLists) secured.POST("/oci-configs/:id/security-lists", h.createSecurityList) secured.GET("/oci-configs/:id/security-lists/:securityListId", h.getSecurityList) secured.PUT("/oci-configs/:id/security-lists/:securityListId", h.updateSecurityList) secured.DELETE("/oci-configs/:id/security-lists/:securityListId", h.deleteSecurityList) secured.GET("/oci-configs/:id/availability-domains", h.availabilityDomains) secured.GET("/oci-configs/:id/instances", h.listInstances) secured.POST("/oci-configs/:id/instances", h.createInstance) secured.GET("/oci-configs/:id/instances/:instanceId", h.getInstance) secured.PUT("/oci-configs/:id/instances/:instanceId", h.updateInstance) secured.DELETE("/oci-configs/:id/instances/:instanceId", h.terminateInstance) secured.POST("/oci-configs/:id/instances/:instanceId/action", h.instanceAction) secured.GET("/oci-configs/:id/boot-volumes", h.listBootVolumes) secured.GET("/oci-configs/:id/boot-volumes/:bootVolumeId", h.getBootVolume) secured.PUT("/oci-configs/:id/boot-volumes/:bootVolumeId", h.updateBootVolume) secured.DELETE("/oci-configs/:id/boot-volumes/:bootVolumeId", h.deleteBootVolume) secured.POST("/oci-configs/:id/instances/:instanceId/change-public-ip", h.changePublicIP) secured.POST("/oci-configs/:id/instances/:instanceId/ipv6-addresses", h.addInstanceIpv6) secured.DELETE("/oci-configs/:id/instances/:instanceId/ipv6-addresses", h.deleteInstanceIpv6) secured.POST("/oci-configs/:id/instances/:instanceId/console-connections", h.createConsoleConnection) secured.GET("/oci-configs/:id/instances/:instanceId/console-connections", h.listConsoleConnections) secured.DELETE("/oci-configs/:id/console-connections/:connectionId", h.deleteConsoleConnection) secured.GET("/oci-configs/:id/instances/:instanceId/vnics", h.listInstanceVnics) secured.POST("/oci-configs/:id/instances/:instanceId/vnics", h.attachVnic) secured.DELETE("/oci-configs/:id/vnic-attachments/:attachmentId", h.detachVnic) secured.POST("/oci-configs/:id/vnics/:vnicId/ipv6-addresses", h.addVnicIpv6) secured.GET("/oci-configs/:id/instances/:instanceId/boot-volume-attachments", h.listBootVolumeAttachments) secured.POST("/oci-configs/:id/instances/:instanceId/boot-volume-attachments", h.attachBootVolume) secured.POST("/oci-configs/:id/instances/:instanceId/replace-boot-volume", h.replaceBootVolume) secured.DELETE("/oci-configs/:id/boot-volume-attachments/:attachmentId", h.detachBootVolume) secured.GET("/oci-configs/:id/volumes", h.listBlockVolumes) secured.GET("/oci-configs/:id/instances/:instanceId/volume-attachments", h.listVolumeAttachments) secured.POST("/oci-configs/:id/instances/:instanceId/volume-attachments", h.attachVolume) secured.DELETE("/oci-configs/:id/volume-attachments/:attachmentId", h.detachVolume) secured.GET("/oci-configs/:id/instances/:instanceId/traffic", h.instanceTraffic) secured.GET("/oci-configs/:id/costs", h.costs) // AI 网关面板管理:密钥 / 渠道(号池)/ 聚合模型 / 调用日志 aiadmin := &aiAdminHandler{gw: aiGateway} secured.GET("/ai-keys", aiadmin.listKeys) secured.POST("/ai-keys", aiadmin.createKey) secured.PUT("/ai-keys/:id", aiadmin.updateKey) secured.PUT("/ai-keys/:id/content-log", aiadmin.updateKeyContentLog) secured.DELETE("/ai-keys/:id", aiadmin.deleteKey) secured.GET("/ai-channels", aiadmin.listChannels) secured.POST("/ai-channels", aiadmin.createChannel) secured.PUT("/ai-channels/:id", aiadmin.updateChannel) secured.DELETE("/ai-channels/:id", aiadmin.deleteChannel) secured.POST("/ai-channels/:id/probe", aiadmin.probeChannel) secured.POST("/ai-channels/:id/sync-models", aiadmin.syncChannelModels) secured.GET("/ai-models", aiadmin.gatewayModels) secured.GET("/ai-logs", aiadmin.listLogs) secured.GET("/ai-content-logs", aiadmin.listContentLogs) secured.GET("/oci-configs/:id/audit-events", h.getAuditEvents) secured.GET("/oci-configs/:id/audit-events/detail", h.getAuditEventDetail) secured.GET("/oci-configs/:id/users", h.listTenantUsers) secured.POST("/oci-configs/:id/users", h.createTenantUser) secured.GET("/oci-configs/:id/users/:userId", h.getTenantUserDetail) secured.PUT("/oci-configs/:id/users/:userId", h.updateTenantUser) secured.DELETE("/oci-configs/:id/users/:userId", h.deleteTenantUser) secured.POST("/oci-configs/:id/users/:userId/reset-password", h.resetTenantUserPassword) secured.DELETE("/oci-configs/:id/users/:userId/mfa-devices", h.deleteTenantUserMfa) secured.DELETE("/oci-configs/:id/users/:userId/api-keys", h.deleteTenantUserApiKeys) secured.GET("/oci-configs/:id/notification-recipients", h.getNotificationRecipients) secured.PUT("/oci-configs/:id/notification-recipients", h.updateNotificationRecipients) secured.GET("/oci-configs/:id/password-policies", h.listPasswordPolicies) secured.PUT("/oci-configs/:id/password-policies/:policyId", h.updatePasswordPolicy) secured.GET("/oci-configs/:id/identity-settings", h.getIdentitySetting) secured.PUT("/oci-configs/:id/identity-settings", h.updateIdentitySetting) secured.GET("/oci-configs/:id/identity-providers", h.listIdentityProviders) secured.POST("/oci-configs/:id/identity-providers", h.createIdentityProvider) secured.POST("/oci-configs/:id/identity-providers/:idpId/activate", h.activateIdentityProvider) secured.DELETE("/oci-configs/:id/identity-providers/:idpId", h.deleteIdentityProvider) secured.GET("/oci-configs/:id/saml-metadata", h.downloadSamlMetadata) secured.GET("/oci-configs/:id/sign-on-rules", h.listSignOnRules) secured.POST("/oci-configs/:id/sign-on-exemptions", h.createMfaExemption) secured.DELETE("/oci-configs/:id/sign-on-exemptions/:ruleId", h.deleteMfaExemption) return r }