fix: scale replicas in response, K8s metrics client, quota precheck, auth tests
- Add GetMetrics method to MetricsClient interface and implement cluster metrics API - Add QuotaPrecheck service for validating resource quotas before deployment - Add auth DTO with role/permission models and auth handler tests - Add instance diagnostics: mounted NFS volumes, labels, annotations in pod diagnostics - Update workspace handler with GetWorkspace endpoint and shared-user list - Fix monitoring handler to use correct service method name - Add tail_lines fallback in instance handler for snake_case query params - Update nginx config for SSE log streaming support (no buffering) - Add comprehensive test coverage: auth_service_test, auth_handler_test, auth_dto_test, metrics_client_test, quota_precheck_test - Update error messages for quota validation and instance operations - ModifyModal: fix YAML lineWidth:0, modified keys summary, delta-only submit - InstanceCard: correctly disable scale-minus when replicas <= 0 - SidebarLayout: add hover transition for sidebar items - Update todo.md and lessons.md with latest fixes
This commit is contained in:
@ -79,6 +79,12 @@ func main() {
|
||||
passwordHasher,
|
||||
tokenGenerator,
|
||||
)
|
||||
authService.SetUserLifecycleCleanup(
|
||||
repos.InstanceRepo,
|
||||
repos.ClusterRepo,
|
||||
repos.BindingRepo,
|
||||
repos.TenantKubeClient,
|
||||
)
|
||||
|
||||
clusterService := service.NewClusterService(
|
||||
repos.ClusterRepo,
|
||||
@ -106,10 +112,13 @@ func main() {
|
||||
instanceService.SetDiagnosticsClient(repos.DiagnosticsClient)
|
||||
instanceService.SetTenantProvisioning(repos.WorkspaceRepo, repos.TenantKubeClient)
|
||||
instanceService.SetScaleClient(k8s.NewScaleClient())
|
||||
instanceService.SetUserRepository(repos.UserRepo)
|
||||
|
||||
monitoringService := service.NewMonitoringService(
|
||||
repos.ClusterRepo,
|
||||
repos.MetricsClient,
|
||||
repos.InstanceRepo,
|
||||
repos.UserRepo,
|
||||
)
|
||||
|
||||
workspaceService := service.NewWorkspaceService(
|
||||
@ -243,8 +252,8 @@ func setupRouter(
|
||||
api := router.PathPrefix("/api/v1").Subrouter()
|
||||
|
||||
// ===== 认证路由 =====
|
||||
api.HandleFunc("/auth/login", authHandler.Login)
|
||||
api.HandleFunc("/auth/refresh", authHandler.RefreshToken)
|
||||
api.HandleFunc("/auth/login", authHandler.Login).Methods(http.MethodPost)
|
||||
api.HandleFunc("/auth/refresh", authHandler.RefreshToken).Methods(http.MethodPost)
|
||||
|
||||
protected := api.PathPrefix("").Subrouter()
|
||||
protected.Use(authMiddleware(authService))
|
||||
@ -262,6 +271,8 @@ func setupRouter(
|
||||
protected.HandleFunc("/clusters/{cluster_id}", clusterHandler.UpdateCluster).Methods(http.MethodPut)
|
||||
protected.HandleFunc("/clusters/{cluster_id}", clusterHandler.DeleteCluster).Methods(http.MethodDelete)
|
||||
protected.HandleFunc("/clusters/{cluster_id}/health", clusterHandler.GetClusterHealth).Methods(http.MethodGet)
|
||||
protected.HandleFunc("/clusters/{cluster_id}/stats", monitoringHandler.GetClusterStats).Methods(http.MethodGet)
|
||||
protected.HandleFunc("/clusters/{cluster_id}/kubeconfig", workspaceHandler.IssueClusterKubeconfig).Methods(http.MethodGet)
|
||||
|
||||
// ===== Registry 路由 =====
|
||||
protected.HandleFunc("/registries", registryHandler.CreateRegistry).Methods(http.MethodPost)
|
||||
@ -273,7 +284,9 @@ func setupRouter(
|
||||
|
||||
// ===== Artifact 路由 =====
|
||||
protected.HandleFunc("/registries/{registry_id}/repositories", artifactHandler.ListRepositories).Methods(http.MethodGet)
|
||||
protected.HandleFunc("/repositories/{repository_name:.+}/tags", artifactHandler.ListRepositoryTags).Methods(http.MethodGet)
|
||||
protected.HandleFunc("/registries/{registry_id}/repositories/{repository_name:.+}/artifacts", artifactHandler.ListArtifacts).Methods(http.MethodGet)
|
||||
protected.HandleFunc("/registries/{registry_id}/repositories/{repository_name:.+}/tags", artifactHandler.ListRepositoryTags).Methods(http.MethodGet)
|
||||
protected.HandleFunc("/registries/{registry_id}/repositories/{repository_name:.+}/artifacts/{reference}", artifactHandler.GetArtifact).Methods(http.MethodGet)
|
||||
protected.HandleFunc("/registries/{registry_id}/repositories/{repository_name:.+}/artifacts/{reference}/values-schema", artifactHandler.GetArtifactValuesSchema).Methods(http.MethodGet)
|
||||
protected.HandleFunc("/registries/{registry_id}/repositories/{repository_name:.+}/artifacts/{reference}/values-yaml", artifactHandler.GetArtifactValuesYAML).Methods(http.MethodGet)
|
||||
@ -293,6 +306,7 @@ func setupRouter(
|
||||
// ===== Monitoring 路由 =====
|
||||
protected.HandleFunc("/monitoring/clusters", monitoringHandler.ListClusterMonitoring).Methods(http.MethodGet)
|
||||
protected.HandleFunc("/monitoring/clusters/{cluster_id}", monitoringHandler.GetClusterMonitoring).Methods(http.MethodGet)
|
||||
protected.HandleFunc("/monitoring/clusters/{cluster_id}/metrics", monitoringHandler.GetClusterMonitoring).Methods(http.MethodGet)
|
||||
protected.HandleFunc("/monitoring/clusters/{cluster_id}/nodes", monitoringHandler.GetNodeMetrics).Methods(http.MethodGet)
|
||||
protected.HandleFunc("/monitoring/summary", monitoringHandler.GetMonitoringSummary).Methods(http.MethodGet)
|
||||
|
||||
@ -358,15 +372,16 @@ func loggingMiddleware(next http.Handler) http.Handler {
|
||||
// corsMiddleware CORS 中间件
|
||||
func corsMiddleware(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// 设置 CORS 头
|
||||
origin := r.Header.Get("Origin")
|
||||
if origin == "" {
|
||||
origin = "*"
|
||||
if origin != "" {
|
||||
w.Header().Add("Vary", "Origin")
|
||||
if corsOriginAllowed(origin) {
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
}
|
||||
}
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With")
|
||||
w.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
w.Header().Set("Access-Control-Max-Age", "86400")
|
||||
|
||||
// 处理 OPTIONS 预检请求
|
||||
@ -378,3 +393,47 @@ func corsMiddleware(next http.Handler) http.Handler {
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func corsOriginAllowed(origin string) bool {
|
||||
origin = strings.TrimSpace(origin)
|
||||
if origin == "" {
|
||||
return false
|
||||
}
|
||||
for _, allowed := range corsAllowedOrigins() {
|
||||
if origin == allowed {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func corsAllowedOrigins() []string {
|
||||
configured := strings.TrimSpace(os.Getenv("CORS_ALLOWED_ORIGINS"))
|
||||
if configured == "" {
|
||||
configured = strings.TrimSpace(os.Getenv("ALLOWED_ORIGINS"))
|
||||
}
|
||||
if configured == "" {
|
||||
return []string{
|
||||
"http://localhost:3000",
|
||||
"http://localhost:5173",
|
||||
"http://localhost:8080",
|
||||
"http://localhost:18080",
|
||||
"http://localhost:18081",
|
||||
"http://127.0.0.1:3000",
|
||||
"http://127.0.0.1:5173",
|
||||
"http://127.0.0.1:8080",
|
||||
"http://127.0.0.1:18080",
|
||||
"http://127.0.0.1:18081",
|
||||
"http://10.6.80.114:18080",
|
||||
}
|
||||
}
|
||||
origins := make([]string, 0)
|
||||
for _, origin := range strings.Split(configured, ",") {
|
||||
origin = strings.TrimSpace(origin)
|
||||
if origin == "" || origin == "*" {
|
||||
continue
|
||||
}
|
||||
origins = append(origins, origin)
|
||||
}
|
||||
return origins
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user