diff --git a/cmd/cc-backend/main.go b/cmd/cc-backend/main.go index f9868e6..02eb389 100644 --- a/cmd/cc-backend/main.go +++ b/cmd/cc-backend/main.go @@ -274,9 +274,10 @@ func main() { rw.Header().Add("Content-Type", "text/html; charset=utf-8") rw.WriteHeader(http.StatusUnauthorized) web.RenderTemplate(rw, r, "login.tmpl", &web.Page{ - Title: "Login failed - ClusterCockpit", - Error: err.Error(), - Build: buildInfo, + Title: "Login failed - ClusterCockpit", + MsgType: "alert-warning", + Message: err.Error(), + Build: buildInfo, }) })).Methods(http.MethodPost) @@ -284,9 +285,10 @@ func main() { rw.Header().Add("Content-Type", "text/html; charset=utf-8") rw.WriteHeader(http.StatusOK) web.RenderTemplate(rw, r, "login.tmpl", &web.Page{ - Title: "Bye - ClusterCockpit", - Info: "Logout sucessful", - Build: buildInfo, + Title: "Bye - ClusterCockpit", + MsgType: "alert-info", + Message: "Logout successful", + Build: buildInfo, }) }))).Methods(http.MethodPost) @@ -299,9 +301,10 @@ func main() { func(rw http.ResponseWriter, r *http.Request, err error) { rw.WriteHeader(http.StatusUnauthorized) web.RenderTemplate(rw, r, "login.tmpl", &web.Page{ - Title: "Authentication failed - ClusterCockpit", - Error: err.Error(), - Build: buildInfo, + Title: "Authentication failed - ClusterCockpit", + MsgType: "alert-danger", + Message: err.Error(), + Build: buildInfo, }) }) }) @@ -316,11 +319,11 @@ func main() { // Send a searchId and then reply with a redirect to a user, or directly send query to job table for jobid and project. secured.HandleFunc("/search", func(rw http.ResponseWriter, r *http.Request) { - routerConfig.HandleSearchBar(rw, r, api) + routerConfig.HandleSearchBar(rw, r, buildInfo) }) // Mount all /monitoring/... and /api/... routes. - routerConfig.SetupRoutes(secured, version, commit, date) + routerConfig.SetupRoutes(secured, buildInfo) api.MountRoutes(secured) if config.Keys.EmbedStaticFiles { diff --git a/docs/searchbar.md b/docs/searchbar.md index 3a6eb2b..d7c13a7 100644 --- a/docs/searchbar.md +++ b/docs/searchbar.md @@ -20,11 +20,11 @@ * JobName: Job-Table (Allows multiple identical matches, e.g. JobNames from different clusters) * ProjectId: Job-Table * Username: Users-Table - * **Please Note**: Only users with jobs will be shown in table! I.e., Users without jobs will be missing in table. + * **Please Note**: Only users with jobs will be shown in table! I.e., Users without jobs will be missing in table. Also, a `Last 30 Days` is active by default and might filter out expected users. * Name: Users-Table - * **Please Note**: Only users with jobs will be shown in table! I.e., Users without jobs will be missing in table. + * **Please Note**: Only users with jobs will be shown in table! I.e., Users without jobs will be missing in table. Also, a `Last 30 Days` is active by default and might filter out expected users. * Best guess search always redirects to Job-Table or `/monitoring/user/$USER` (first username match) - * Unprocessable queries will redirect to `/monitoring/jobs/?` + * Unprocessable queries will display messages detailing the cause (Info, Warning, Error) * Spaces trimmed (both for searchTag and queryString) * ` job12` == `job12` * `projectID : abcd ` == `projectId:abcd` diff --git a/internal/repository/job.go b/internal/repository/job.go index 0f280cd..f79ddbc 100644 --- a/internal/repository/job.go +++ b/internal/repository/job.go @@ -524,11 +524,10 @@ var ErrForbidden = errors.New("not authorized") // If query is found to be an integer (= conversion to INT datatype succeeds), skip back to parent call // If nothing matches the search, `ErrNotFound` is returned. -func (r *JobRepository) FindUserOrProjectOrJobname(ctx context.Context, searchterm string) (username string, project string, metasnip string, err error) { +func (r *JobRepository) FindUserOrProjectOrJobname(user *auth.User, searchterm string) (username string, project string, metasnip string, err error) { if _, err := strconv.Atoi(searchterm); err == nil { // Return empty on successful conversion: parent method will redirect for integer jobId return "", "", "", nil } else { // Has to have letters and logged-in user for other guesses - user := auth.GetUser(ctx) if user != nil { // Find username in jobs (match) uresult, _ := r.FindColumnValue(user, searchterm, "job", "user", "user", false) diff --git a/internal/routerConfig/routes.go b/internal/routerConfig/routes.go index 2aa3f05..5b1cbed 100644 --- a/internal/routerConfig/routes.go +++ b/internal/routerConfig/routes.go @@ -12,7 +12,6 @@ import ( "strings" "time" - "github.com/ClusterCockpit/cc-backend/internal/api" "github.com/ClusterCockpit/cc-backend/internal/auth" "github.com/ClusterCockpit/cc-backend/internal/graph/model" "github.com/ClusterCockpit/cc-backend/internal/repository" @@ -230,7 +229,7 @@ func buildFilterPresets(query url.Values) map[string]interface{} { return filterPresets } -func SetupRoutes(router *mux.Router, version string, hash string, buildTime string) { +func SetupRoutes(router *mux.Router, buildInfo web.Build) { userCfgRepo := repository.GetUserCfgRepo() for _, route := range routes { route := route @@ -256,7 +255,7 @@ func SetupRoutes(router *mux.Router, version string, hash string, buildTime stri Title: title, User: *user, Roles: availableRoles, - Build: web.Build{Version: version, Hash: hash, Buildtime: buildTime}, + Build: buildInfo, Config: conf, Infos: infos, } @@ -270,66 +269,64 @@ func SetupRoutes(router *mux.Router, version string, hash string, buildTime stri } } -func HandleSearchBar(rw http.ResponseWriter, r *http.Request, api *api.RestApi) { +func HandleSearchBar(rw http.ResponseWriter, r *http.Request, buildInfo web.Build) { + user := auth.GetUser(r.Context()) + availableRoles, _ := auth.GetValidRolesMap(user) + if search := r.URL.Query().Get("searchId"); search != "" { - user := auth.GetUser(r.Context()) + repo := repository.GetJobRepository() splitSearch := strings.Split(search, ":") if len(splitSearch) == 2 { switch strings.Trim(splitSearch[0], " ") { case "jobId": - http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery + http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound) // All Users: Redirect to Tablequery case "jobName": - http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery + http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound) // All Users: Redirect to Tablequery case "projectId": - http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery + http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound) // All Users: Redirect to Tablequery case "username": if user.HasAnyRole([]auth.Role{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) { - http.Redirect(rw, r, "/monitoring/users/?user="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) + http.Redirect(rw, r, "/monitoring/users/?user="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusFound) } else { - http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Users: Redirect to Tablequery + web.RenderTemplate(rw, r, "message.tmpl", &web.Page{Title: "Error", MsgType: "alert-danger", Message: "Missing Access Rights", User: *user, Roles: availableRoles, Build: buildInfo}) } case "name": - usernames, _ := api.JobRepository.FindColumnValues(user, strings.Trim(splitSearch[1], " "), "user", "username", "name") + usernames, _ := repo.FindColumnValues(user, strings.Trim(splitSearch[1], " "), "user", "username", "name") if len(usernames) != 0 { joinedNames := strings.Join(usernames, "&user=") - http.Redirect(rw, r, "/monitoring/users/?user="+joinedNames, http.StatusTemporaryRedirect) + http.Redirect(rw, r, "/monitoring/users/?user="+joinedNames, http.StatusFound) } else { if user.HasAnyRole([]auth.Role{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) { - http.Redirect(rw, r, "/monitoring/users/?user=NoUserNameFound", http.StatusTemporaryRedirect) + http.Redirect(rw, r, "/monitoring/users/?user=NoUserNameFound", http.StatusPermanentRedirect) } else { - http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Users: Redirect to Tablequery + web.RenderTemplate(rw, r, "message.tmpl", &web.Page{Title: "Error", MsgType: "alert-danger", Message: "Missing Access Rights", User: *user, Roles: availableRoles, Build: buildInfo}) } } default: - log.Warnf("Searchbar type parameter '%s' unknown", strings.Trim(splitSearch[0], " ")) - http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Unknown: Redirect to Tablequery + web.RenderTemplate(rw, r, "message.tmpl", &web.Page{Title: "Warning", MsgType: "alert-warning", Message: fmt.Sprintf("Unknown search type: %s", strings.Trim(splitSearch[0], " ")), User: *user, Roles: availableRoles, Build: buildInfo}) } - } else if len(splitSearch) == 1 { - username, project, jobname, err := api.JobRepository.FindUserOrProjectOrJobname(r.Context(), strings.Trim(search, " ")) + username, project, jobname, err := repo.FindUserOrProjectOrJobname(user, strings.Trim(search, " ")) if err != nil { - log.Errorf("Error while searchbar best guess: %v", err.Error()) - http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Unknown: Redirect to Tablequery + web.RenderTemplate(rw, r, "message.tmpl", &web.Page{Title: "Info", MsgType: "alert-info", Message: "Search without result", User: *user, Roles: availableRoles, Build: buildInfo}) + return } if username != "" { - http.Redirect(rw, r, "/monitoring/user/"+username, http.StatusTemporaryRedirect) // User: Redirect to user page + http.Redirect(rw, r, "/monitoring/user/"+username, http.StatusFound) // User: Redirect to user page } else if project != "" { - http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // projectId (equal) + http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(search, " ")), http.StatusFound) // projectId (equal) } else if jobname != "" { - http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // JobName (contains) + http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(search, " ")), http.StatusFound) // JobName (contains) } else { - http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // No Result: Probably jobId + http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(strings.Trim(search, " ")), http.StatusFound) // No Result: Probably jobId } - } else { - log.Warnf("Searchbar query parameters malformed: %v", search) - http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Unknown: Redirect to Tablequery + web.RenderTemplate(rw, r, "message.tmpl", &web.Page{Title: "Error", MsgType: "alert-danger", Message: "Searchbar query parameters malformed", User: *user, Roles: availableRoles, Build: buildInfo}) } - } else { - http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) + web.RenderTemplate(rw, r, "message.tmpl", &web.Page{Title: "Warning", MsgType: "alert-warning", Message: "Empty search", User: *user, Roles: availableRoles, Build: buildInfo}) } } diff --git a/web/frontend/src/plots/MetricPlot.svelte b/web/frontend/src/plots/MetricPlot.svelte index d6f3ffe..c0440fd 100644 --- a/web/frontend/src/plots/MetricPlot.svelte +++ b/web/frontend/src/plots/MetricPlot.svelte @@ -270,6 +270,7 @@ } export function findThresholds(metricConfig, scope, subCluster) { + // console.log('NAME ' + metricConfig.name + ' / SCOPE ' + scope + ' / SUBCLUSTER ' + subCluster.name) if (!metricConfig || !scope || !subCluster) { console.warn('Argument missing for findThresholds!') return null @@ -280,8 +281,10 @@ // console.log('subClusterConfigs array empty, use metricConfig defaults') return { normal: metricConfig.normal, caution: metricConfig.caution, alert: metricConfig.alert } } else if (metricConfig.subClusters && metricConfig.subClusters.length > 0) { - // console.log('subClusterConfigs found, find and use subCluster Settings') - return metricConfig.subClusters.find(sc => sc.name == subCluster.name) + // console.log('subClusterConfigs found, use subCluster Settings if matching jobs subcluster:') + let forSubCluster = metricConfig.subClusters.find(sc => sc.name == subCluster.name) + if (forSubCluster && forSubCluster.normal && forSubCluster.caution && forSubCluster.alert) return forSubCluster + else return { normal: metricConfig.normal, caution: metricConfig.caution, alert: metricConfig.alert } } else { console.warn('metricConfig.subClusters not found!') return null diff --git a/web/templates/login.tmpl b/web/templates/login.tmpl index 1e689b8..304a96f 100644 --- a/web/templates/login.tmpl +++ b/web/templates/login.tmpl @@ -17,15 +17,9 @@
- {{if .Error}} - - {{end}} - - {{if .Info}} -