mirror of
				https://github.com/ClusterCockpit/cc-backend
				synced 2025-10-31 07:55:06 +01:00 
			
		
		
		
	Rework searchbar functions, add user jobtable
This commit is contained in:
		| @@ -491,195 +491,94 @@ 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) FindJobnameOrUserOrProject(ctx context.Context, searchterm string) (metasnip string, username string, project string, err error) { | ||||
| 	user := auth.GetUser(ctx) | ||||
| func (r *JobRepository) FindUserOrProjectOrJobname(ctx context.Context, 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 | ||||
|  | ||||
| 		if user != nil && user.HasNotRoles([]string{auth.RoleAdmin, auth.RoleSupport}) { | ||||
| 			err := sq.Select("job.user").Distinct().From("job"). | ||||
| 				Where("job.user = ?", searchterm). | ||||
| 				RunWith(r.stmtCache).QueryRow().Scan(&username) | ||||
| 	} 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) | ||||
| 			if uresult != "" { | ||||
| 				return uresult, "", "", nil | ||||
| 			} | ||||
| 			// Find username by name (like) | ||||
| 			nresult, _ := r.FindColumnValue(user, searchterm, "user", "username", "name", true) | ||||
| 			if nresult != "" { | ||||
| 				return nresult, "", "", nil | ||||
| 			} | ||||
| 			// Find projectId in jobs (match) | ||||
| 			presult, _ := r.FindColumnValue(user, searchterm, "job", "project", "project", false) | ||||
| 			if presult != "" { | ||||
| 				return "", presult, "", nil | ||||
| 			} | ||||
| 			// Still no return (or not authorized for above): Try JobName | ||||
| 			// Match Metadata, on hit, parent method redirects to jobName GQL query | ||||
| 			err := sq.Select("job.cluster").Distinct().From("job"). | ||||
| 				Where("job.meta_data LIKE ?", "%"+searchterm+"%"). | ||||
| 				RunWith(r.stmtCache).QueryRow().Scan(&metasnip) | ||||
| 			if err != nil && err != sql.ErrNoRows { | ||||
| 				return "", "", "", err | ||||
| 			} else if err == nil { | ||||
| 				return "", username, "", nil | ||||
| 			} | ||||
|  | ||||
| 			if username == "" { // Try with Name2Username query | ||||
| 				errtwo := sq.Select("user.username").Distinct().From("user"). | ||||
| 					Where("user.name LIKE ?", fmt.Sprint("%"+searchterm+"%")). | ||||
| 					RunWith(r.stmtCache).QueryRow().Scan(&username) | ||||
| 				if errtwo != nil && errtwo != sql.ErrNoRows { | ||||
| 					return "", "", "", errtwo | ||||
| 				} else if errtwo == nil { | ||||
| 					return "", username, "", nil | ||||
| 				} | ||||
| 				return "", "", metasnip[0:1], nil | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if user == nil || user.HasRole(auth.RoleAdmin) || user.HasRole(auth.RoleSupport) { | ||||
| 			err := sq.Select("job.project").Distinct().From("job"). | ||||
| 				Where("job.project = ?", searchterm). | ||||
| 				RunWith(r.stmtCache).QueryRow().Scan(&project) | ||||
| 			if err != nil && err != sql.ErrNoRows { | ||||
| 				return "", "", "", err | ||||
| 			} else if err == nil { | ||||
| 				return "", "", project, nil | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// All Authorizations: If unlabeled query not username or projectId, try for jobname: Match Metadata, on hit, parent method redirects to jobName GQL query | ||||
| 		err := sq.Select("job.cluster").Distinct().From("job"). | ||||
| 			Where("job.meta_data LIKE ?", "%"+searchterm+"%"). | ||||
| 			RunWith(r.stmtCache).QueryRow().Scan(&metasnip) | ||||
| 		if err != nil && err != sql.ErrNoRows { | ||||
| 			return "", "", "", err | ||||
| 		} else if err == nil { | ||||
| 			return metasnip[0:1], "", "", nil | ||||
| 		} | ||||
|  | ||||
| 		return "", "", "", ErrNotFound | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *JobRepository) FindUser(ctx context.Context, searchterm string) (username string, err error) { | ||||
| 	user := auth.GetUser(ctx) | ||||
| 	if user == nil || user.HasAnyRole([]string{auth.RoleAdmin, auth.RoleSupport}) { | ||||
| 		err := sq.Select("job.user").Distinct().From("job"). | ||||
| 			Where("job.user = ?", searchterm). | ||||
| 			RunWith(r.stmtCache).QueryRow().Scan(&username) | ||||
| func (r *JobRepository) FindColumnValue(user *auth.User, searchterm string, table string, selectColumn string, whereColumn string, isLike bool) (result string, err error) { | ||||
| 	compareStr := " = ?" | ||||
| 	query := searchterm | ||||
| 	if isLike == true { | ||||
| 		compareStr = " LIKE ?" | ||||
| 		query = "%" + searchterm + "%" | ||||
| 	} | ||||
| 	if user.HasAnyRole([]string{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) { | ||||
| 		err := sq.Select(table+"."+selectColumn).Distinct().From(table). | ||||
| 			Where(table+"."+whereColumn+compareStr, query). | ||||
| 			RunWith(r.stmtCache).QueryRow().Scan(&result) | ||||
| 		if err != nil && err != sql.ErrNoRows { | ||||
| 			return "", err | ||||
| 		} else if err == nil { | ||||
| 			return username, nil | ||||
| 			return result, nil | ||||
| 		} | ||||
| 		return "", ErrNotFound | ||||
|  | ||||
| 	} else { | ||||
| 		log.Infof("Non-Admin User %s : Requested Query Username -> %s: Forbidden", user.Name, searchterm) | ||||
| 		log.Infof("Non-Admin User %s : Requested Query '%s' on table '%s' : Forbidden", user.Name, query, table) | ||||
| 		return "", ErrForbidden | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *JobRepository) FindUserByName(ctx context.Context, searchterm string) (username string, err error) { | ||||
| 	user := auth.GetUser(ctx) | ||||
| 	if user == nil || user.HasRole(auth.RoleAdmin) || user.HasRole(auth.RoleSupport) { | ||||
| 		err := sq.Select("user.username").Distinct().From("user"). | ||||
| 			Where("user.name = ?", searchterm). | ||||
| 			RunWith(r.stmtCache).QueryRow().Scan(&username) | ||||
| 		if err != nil && err != sql.ErrNoRows { | ||||
| 			return "", err | ||||
| 		} else if err == nil { | ||||
| 			return username, nil | ||||
| 		} | ||||
| 		return "", ErrNotFound | ||||
|  | ||||
| 	} else { | ||||
| 		log.Infof("Non-Admin User %s : Requested Query Name -> %s: Forbidden", user.Name, searchterm) | ||||
| 		return "", ErrForbidden | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *JobRepository) FindUsers(ctx context.Context, searchterm string) (usernames []string, err error) { | ||||
| 	user := auth.GetUser(ctx) | ||||
| func (r *JobRepository) FindColumnValues(user *auth.User, query string, table string, selectColumn string, whereColumn string) (results []string, err error) { | ||||
| 	emptyResult := make([]string, 0) | ||||
| 	if user == nil || user.HasRole(auth.RoleAdmin) || user.HasRole(auth.RoleSupport) { | ||||
| 		rows, err := sq.Select("job.user").Distinct().From("job"). | ||||
| 			Where("job.user LIKE ?", fmt.Sprint("%", searchterm, "%")). | ||||
| 	if user.HasAnyRole([]string{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) { | ||||
| 		rows, err := sq.Select(table+"."+selectColumn).Distinct().From(table). | ||||
| 			Where(table+"."+whereColumn+" LIKE ?", fmt.Sprint("%", query, "%")). | ||||
| 			RunWith(r.stmtCache).Query() | ||||
| 		if err != nil && err != sql.ErrNoRows { | ||||
| 			return emptyResult, err | ||||
| 		} else if err == nil { | ||||
| 			for rows.Next() { | ||||
| 				var name string | ||||
| 				err := rows.Scan(&name) | ||||
| 				var result string | ||||
| 				err := rows.Scan(&result) | ||||
| 				if err != nil { | ||||
| 					rows.Close() | ||||
| 					log.Warnf("Error while scanning rows: %v", err) | ||||
| 					return emptyResult, err | ||||
| 				} | ||||
| 				usernames = append(usernames, name) | ||||
| 				results = append(results, result) | ||||
| 			} | ||||
| 			return usernames, nil | ||||
| 			return results, nil | ||||
| 		} | ||||
| 		return emptyResult, ErrNotFound | ||||
|  | ||||
| 	} else { | ||||
| 		log.Infof("Non-Admin User %s : Requested Query Usernames -> %s: Forbidden", user.Name, searchterm) | ||||
| 		log.Infof("Non-Admin User %s : Requested Query '%s' on table '%s' : Forbidden", user.Name, query, table) | ||||
| 		return emptyResult, ErrForbidden | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *JobRepository) FindUsersByName(ctx context.Context, searchterm string) (usernames []string, err error) { | ||||
| 	user := auth.GetUser(ctx) | ||||
| 	emptyResult := make([]string, 0) | ||||
| 	if user == nil || user.HasRole(auth.RoleAdmin) || user.HasRole(auth.RoleSupport) { | ||||
| 		rows, err := sq.Select("user.username").Distinct().From("user"). | ||||
| 			Where("user.name LIKE ?", fmt.Sprint("%", searchterm, "%")). | ||||
| 			RunWith(r.stmtCache).Query() | ||||
| 		if err != nil && err != sql.ErrNoRows { | ||||
| 			return emptyResult, err | ||||
| 		} else if err == nil { | ||||
| 			for rows.Next() { | ||||
| 				var username string | ||||
| 				err := rows.Scan(&username) | ||||
| 				if err != nil { | ||||
| 					rows.Close() | ||||
| 					log.Warnf("Error while scanning rows: %v", err) | ||||
| 					return emptyResult, err | ||||
| 				} | ||||
| 				usernames = append(usernames, username) | ||||
| 			} | ||||
| 			return usernames, nil | ||||
| 		} | ||||
| 		return emptyResult, ErrNotFound | ||||
|  | ||||
| 	} else { | ||||
| 		log.Infof("Non-Admin User %s : Requested Query name -> %s: Forbidden", user.Name, searchterm) | ||||
| 		return emptyResult, ErrForbidden | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *JobRepository) FindNameByUser(ctx context.Context, searchterm string) (name string, err error) { | ||||
| 	user := auth.GetUser(ctx) | ||||
| 	if user == nil || user.HasRole(auth.RoleAdmin) || user.HasRole(auth.RoleSupport) { | ||||
| 		err := sq.Select("user.name").Distinct().From("user"). | ||||
| 			Where("user.username = ?", searchterm). | ||||
| 			RunWith(r.stmtCache).QueryRow().Scan(&name) | ||||
| 		if err != nil && err != sql.ErrNoRows { | ||||
| 			return "", err | ||||
| 		} else if err == nil { | ||||
| 			return name, nil | ||||
| 		} | ||||
| 		return "", ErrNotFound | ||||
|  | ||||
| 	} else { | ||||
| 		log.Infof("Non-Admin User %s : Requested Query Name -> %s: Forbidden", user.Name, searchterm) | ||||
| 		return "", ErrForbidden | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *JobRepository) FindProject(ctx context.Context, searchterm string) (project string, err error) { | ||||
|  | ||||
| 	user := auth.GetUser(ctx) | ||||
| 	if user == nil || user.HasRole(auth.RoleAdmin) || user.HasRole(auth.RoleSupport) { | ||||
| 		err := sq.Select("job.project").Distinct().From("job"). | ||||
| 			Where("job.project = ?", searchterm). | ||||
| 			RunWith(r.stmtCache).QueryRow().Scan(&project) | ||||
| 		if err != nil && err != sql.ErrNoRows { | ||||
| 			return "", err | ||||
| 		} else if err == nil { | ||||
| 			return project, nil | ||||
| 		} | ||||
| 		return "", ErrNotFound | ||||
| 	} else { | ||||
| 		log.Infof("Non-Admin User %s : Requested Query Project -> %s: Forbidden", user.Name, project) | ||||
| 		return "", ErrForbidden | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (r *JobRepository) Partitions(cluster string) ([]string, error) { | ||||
| 	var err error | ||||
| 	start := time.Now() | ||||
| @@ -917,7 +816,8 @@ func (r *JobRepository) JobsStatistics(ctx context.Context, | ||||
| 		if col == "job.user" { | ||||
| 			for id := range stats { | ||||
| 				emptyDash := "-" | ||||
| 				name, _ := r.FindNameByUser(ctx, id) | ||||
| 				user := auth.GetUser(ctx) | ||||
| 				name, _ := r.FindColumnValue(user, id, "user", "name", "username", false) | ||||
| 				if name != "" { | ||||
| 					stats[id].Name = &name | ||||
| 				} else { | ||||
|   | ||||
| @@ -312,75 +312,61 @@ func SetupRoutes(router *mux.Router, version string, hash string, buildTime stri | ||||
|  | ||||
| func HandleSearchBar(rw http.ResponseWriter, r *http.Request, api *api.RestApi) { | ||||
| 	if search := r.URL.Query().Get("searchId"); search != "" { | ||||
| 		user := auth.GetUser(r.Context()) | ||||
| 		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 | ||||
| 				return | ||||
| 			case "jobName": | ||||
| 				http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery | ||||
| 				return | ||||
| 			case "projectId": | ||||
| 				project, _ := api.JobRepository.FindProject(r.Context(), strings.Trim(splitSearch[1], " ")) // Restricted: projectId | ||||
| 				if project != "" { | ||||
| 					http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(project), http.StatusTemporaryRedirect) | ||||
| 					return | ||||
| 				} else { | ||||
| 					http.Redirect(rw, r, "/monitoring/jobs/?jobId=NotFound", http.StatusTemporaryRedirect) // Workaround to display correctly empty table | ||||
| 				} | ||||
| 				http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // All Users: Redirect to Tablequery | ||||
| 			case "username": | ||||
| 				usernames, _ := api.JobRepository.FindUsers(r.Context(), strings.Trim(splitSearch[1], " ")) // Restricted: usernames | ||||
| 				if len(usernames) == 1 { | ||||
| 					http.Redirect(rw, r, "/monitoring/user/"+usernames[0], http.StatusTemporaryRedirect) // One Match: Redirect to User View | ||||
| 					return | ||||
| 				} else if len(usernames) > 1 { | ||||
| 					http.Redirect(rw, r, "/monitoring/users/?user="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) // > 1 Matches: Redirect to user table | ||||
| 					return | ||||
| 				if user.HasAnyRole([]string{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) { | ||||
| 					http.Redirect(rw, r, "/monitoring/users/?user="+url.QueryEscape(strings.Trim(splitSearch[1], " ")), http.StatusTemporaryRedirect) | ||||
| 				} else { | ||||
| 					http.Redirect(rw, r, "/monitoring/users/?user=NotFound", http.StatusTemporaryRedirect) // Workaround to display correctly empty table | ||||
| 					http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Users: Redirect to Tablequery | ||||
| 				} | ||||
| 			case "name": | ||||
| 				usernames, _ := api.JobRepository.FindUsersByName(r.Context(), strings.Trim(splitSearch[1], " ")) // Restricted: usernames queried by name | ||||
| 				if len(usernames) == 1 { | ||||
| 					http.Redirect(rw, r, "/monitoring/user/"+usernames[0], http.StatusTemporaryRedirect) | ||||
| 					return | ||||
| 				} else if len(usernames) > 1 { | ||||
| 				usernames, _ := api.JobRepository.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) // > 1 Matches: Redirect to user table | ||||
| 					return | ||||
| 					http.Redirect(rw, r, "/monitoring/users/?user="+joinedNames, http.StatusTemporaryRedirect) | ||||
| 				} else { | ||||
| 					http.Redirect(rw, r, "/monitoring/users/?user=NotFound", http.StatusTemporaryRedirect) // Workaround to display correctly empty table | ||||
| 					if user.HasAnyRole([]string{auth.RoleAdmin, auth.RoleSupport, auth.RoleManager}) { | ||||
| 						http.Redirect(rw, r, "/monitoring/users/?user=NoUserNameFound", http.StatusTemporaryRedirect) | ||||
| 					} else { | ||||
| 						http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Users: Redirect to Tablequery | ||||
| 					} | ||||
| 				} | ||||
| 			default: | ||||
| 				http.Error(rw, "'searchId' type parameter unknown", http.StatusBadRequest) | ||||
| 				log.Warnf("Searchbar type parameter '%s' unknown", strings.Trim(splitSearch[0], " ")) | ||||
| 				http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Unknown: Redirect to Tablequery | ||||
| 			} | ||||
|  | ||||
| 		} else if len(splitSearch) == 1 { | ||||
| 			jobname, username, project, err := api.JobRepository.FindJobnameOrUserOrProject(r.Context(), strings.Trim(search, " ")) // Determine Access within | ||||
| 			username, project, jobname, err := api.JobRepository.FindUserOrProjectOrJobname(r.Context(), strings.Trim(search, " ")) | ||||
|  | ||||
| 			if err != nil { | ||||
| 				http.Error(rw, err.Error(), http.StatusInternalServerError) | ||||
| 				return | ||||
| 				log.Errorf("Error while searchbar best guess: %v", err.Error()) | ||||
| 				http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Unknown: Redirect to Tablequery | ||||
| 			} | ||||
|  | ||||
| 			if username != "" { | ||||
| 				http.Redirect(rw, r, "/monitoring/user/"+username, http.StatusTemporaryRedirect) // User: Redirect to user page | ||||
| 				return | ||||
| 			} else if project != "" { | ||||
| 				http.Redirect(rw, r, "/monitoring/jobs/?projectMatch=eq&project="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // projectId (equal) | ||||
| 				return | ||||
| 			} else if jobname != "" { | ||||
| 				http.Redirect(rw, r, "/monitoring/jobs/?jobName="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // JobName (contains) | ||||
| 				return | ||||
| 			} else { | ||||
| 				http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(strings.Trim(search, " ")), http.StatusTemporaryRedirect) // No Result: Probably jobId | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 		} else { | ||||
| 			http.Error(rw, "'searchId' query parameter malformed", http.StatusBadRequest) | ||||
| 			log.Warnf("Searchbar query parameters malformed: %v", search) | ||||
| 			http.Redirect(rw, r, "/monitoring/jobs/?", http.StatusTemporaryRedirect) // Unknown: Redirect to Tablequery | ||||
| 		} | ||||
|  | ||||
| 	} else { | ||||
|   | ||||
| @@ -10,8 +10,9 @@ | ||||
|     let isOpen = false | ||||
|  | ||||
|     const userviews = [ | ||||
|         { title: 'My Jobs',  href: `/monitoring/user/${username}`, icon: 'bar-chart-line-fill' }, | ||||
|         { title: 'Tags',     href: '/monitoring/tags/',            icon: 'tags' } | ||||
|         { title: 'My Jobs',     href: `/monitoring/user/${username}`, icon: 'bar-chart-line-fill' }, | ||||
|         { title: `Job Search`,  href: '/monitoring/jobs/',            icon: 'card-list' }, | ||||
|         { title: 'Tags',        href: '/monitoring/tags/',            icon: 'tags' } | ||||
|     ] | ||||
|  | ||||
|     const managerviews = [ | ||||
| @@ -91,9 +92,9 @@ | ||||
|     <div class="d-flex"> | ||||
|         <form method="GET" action="/search"> | ||||
|             <InputGroup> | ||||
|                 <Input type="text" placeholder={(authlevel >= 4) ? "Search jobId / username" : "Search jobId"} name="searchId"/> | ||||
|                 <Input type="text" placeholder={"Search 'type:<query>' ..."} name="searchId"/> | ||||
|                 <Button outline type="submit"><Icon name="search"/></Button> | ||||
|                 <InputGroupText style="cursor:help;" title={(authlevel >= 4) ? "Example: 'projectId:a100cd', Types are: jobId | jobName | projectId | username" | "name" : "Example: 'jobName:myjob', Types are jobId | jobName"}><Icon name="info-circle"/></InputGroupText> | ||||
|                 <InputGroupText style="cursor:help;" title={(authlevel >= 4) ? "Example: 'projectId:a100cd', Types are: jobId | jobName | projectId | username | name" : "Example: 'jobName:myjob', Types are jobId | jobName | projectId"}><Icon name="info-circle"/></InputGroupText> | ||||
|             </InputGroup> | ||||
|         </form> | ||||
|         {#if username} | ||||
|   | ||||
| @@ -14,6 +14,7 @@ | ||||
|     const ccconfig = getContext('cc-config') | ||||
|  | ||||
|     export let filterPresets = {} | ||||
|     export let authLevel | ||||
|  | ||||
|     let filters, jobList, matchedJobs = null | ||||
|     let sorting = { field: 'startTime', order: 'DESC' }, isSortingOpen = false, isMetricsSelectionOpen = false | ||||
| @@ -60,7 +61,7 @@ | ||||
|     </Col> | ||||
|  | ||||
|     <Col xs="3" style="margin-left: auto;"> | ||||
|         <UserOrProject on:update={({ detail }) => filters.update(detail)}/> | ||||
|         <UserOrProject bind:authLevel={authLevel} on:update={({ detail }) => filters.update(detail)}/> | ||||
|     </Col> | ||||
|     <Col xs="2"> | ||||
|         <Refresher on:reload={() => jobList.update()} /> | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
|  | ||||
|     export let user = '' | ||||
|     export let project = '' | ||||
|     export let authLevel | ||||
|     let mode = 'user', term = '' | ||||
|     const throttle = 500 | ||||
|  | ||||
| @@ -22,30 +23,53 @@ | ||||
|  | ||||
|     let timeoutId = null | ||||
|     function termChanged(sleep = throttle) { | ||||
|         if (mode == 'user') | ||||
|             user = term | ||||
|         else | ||||
|         if (authLevel == 2) { | ||||
|             project = term | ||||
|  | ||||
|         if (timeoutId != null) | ||||
|             clearTimeout(timeoutId) | ||||
|             if (timeoutId != null) | ||||
|                 clearTimeout(timeoutId) | ||||
|  | ||||
|         timeoutId = setTimeout(() => { | ||||
|             dispatch('update', { | ||||
|                 user, | ||||
|                 project | ||||
|             }) | ||||
|         }, sleep) | ||||
|             timeoutId = setTimeout(() => { | ||||
|                 dispatch('update', { | ||||
|                     project | ||||
|                 }) | ||||
|             }, sleep) | ||||
|         } else if (authLevel >= 3) { | ||||
|             if (mode == 'user') | ||||
|                 user = term | ||||
|             else | ||||
|                 project = term | ||||
|  | ||||
|             if (timeoutId != null) | ||||
|                 clearTimeout(timeoutId) | ||||
|  | ||||
|             timeoutId = setTimeout(() => { | ||||
|                 dispatch('update', { | ||||
|                     user, | ||||
|                     project | ||||
|                 }) | ||||
|             }, sleep) | ||||
|         } | ||||
|     } | ||||
| </script> | ||||
|  | ||||
| <InputGroup> | ||||
|     <select style="max-width: 175px;" class="form-select" | ||||
|         bind:value={mode} on:change={modeChanged}> | ||||
|         <option value={'user'}>Search User</option> | ||||
|         <option value={'project'}>Search Project</option> | ||||
|     </select> | ||||
|     <Input | ||||
|         type="text" bind:value={term} on:change={() => termChanged()} on:keyup={(event) => termChanged(event.key == 'Enter' ? 0 : throttle)} | ||||
|         placeholder={mode == 'user' ? 'filter username...' : 'filter project...'} /> | ||||
| </InputGroup> | ||||
| {#if authLevel == 2} | ||||
|     <InputGroup> | ||||
|         <Input | ||||
|             type="text" bind:value={term} on:change={() => termChanged()} on:keyup={(event) => termChanged(event.key == 'Enter' ? 0 : throttle)} placeholder='filter project...' | ||||
|         /> | ||||
|     </InputGroup> | ||||
| {:else if authLevel >= 3} | ||||
|     <InputGroup> | ||||
|         <select style="max-width: 175px;" class="form-select" | ||||
|             bind:value={mode} on:change={modeChanged}> | ||||
|             <option value={'user'}>Search User</option> | ||||
|             <option value={'project'}>Search Project</option> | ||||
|         </select> | ||||
|         <Input | ||||
|             type="text" bind:value={term} on:change={() => termChanged()} on:keyup={(event) => termChanged(event.key == 'Enter' ? 0 : throttle)} | ||||
|             placeholder={mode == 'user' ? 'filter username...' : 'filter project...'} /> | ||||
|     </InputGroup> | ||||
| {:else} | ||||
|     Unauthorized | ||||
| {/if} | ||||
| @@ -5,6 +5,7 @@ new Jobs({ | ||||
|     target: document.getElementById('svelte-app'), | ||||
|     props: { | ||||
|         filterPresets: filterPresets, | ||||
|         authLevel: authLevel | ||||
|     }, | ||||
|     context: new Map([ | ||||
|             ['cc-config', clusterCockpitConfig] | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
|     <script> | ||||
|         const filterPresets = {{ .FilterPresets }}; | ||||
|         const clusterCockpitConfig = {{ .Config }}; | ||||
|         const authLevel = {{ .User.AuthLevel }}; | ||||
|     </script> | ||||
|     <script src='/build/jobs.js'></script> | ||||
| {{end}} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user