mirror of
				https://github.com/ClusterCockpit/cc-backend
				synced 2025-10-31 16:05:06 +01:00 
			
		
		
		
	Add /search endpoint which redirects to user/job
This commit is contained in:
		| @@ -266,6 +266,11 @@ func (api *RestApi) stopJob(rw http.ResponseWriter, r *http.Request) { | |||||||
|  |  | ||||||
| 		job, err = api.JobRepository.FindById(id) | 		job, err = api.JobRepository.FindById(id) | ||||||
| 	} else { | 	} else { | ||||||
|  | 		if req.JobId == nil || req.Cluster == nil || req.StartTime == nil { | ||||||
|  | 			http.Error(rw, "'jobId', 'cluster' and 'startTime' are required", http.StatusBadRequest) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		job, err = api.JobRepository.Find(*req.JobId, *req.Cluster, *req.StartTime) | 		job, err = api.JobRepository.Find(*req.JobId, *req.Cluster, *req.StartTime) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								init-db.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								init-db.go
									
									
									
									
									
								
							| @@ -65,6 +65,12 @@ const JOBS_DB_SCHEMA string = ` | |||||||
| 		FOREIGN KEY (tag_id) REFERENCES tag (id) ON DELETE CASCADE); | 		FOREIGN KEY (tag_id) REFERENCES tag (id) ON DELETE CASCADE); | ||||||
| ` | ` | ||||||
|  |  | ||||||
|  | const JOBS_DB_INDEXES string = ` | ||||||
|  | 	CREATE INDEX job_by_user      ON job (user); | ||||||
|  | 	CREATE INDEX job_by_starttime ON job (start_time); | ||||||
|  | 	CREATE INDEX job_by_job_id    ON job (job_id); | ||||||
|  | ` | ||||||
|  |  | ||||||
| // Delete the tables "job", "tag" and "jobtag" from the database and | // Delete the tables "job", "tag" and "jobtag" from the database and | ||||||
| // repopulate them using the jobs found in `archive`. | // repopulate them using the jobs found in `archive`. | ||||||
| func initDB(db *sqlx.DB, archive string) error { | func initDB(db *sqlx.DB, archive string) error { | ||||||
| @@ -178,9 +184,7 @@ func initDB(db *sqlx.DB, archive string) error { | |||||||
|  |  | ||||||
| 	// Create indexes after inserts so that they do not | 	// Create indexes after inserts so that they do not | ||||||
| 	// need to be continually updated. | 	// need to be continually updated. | ||||||
| 	if _, err := db.Exec(` | 	if _, err := db.Exec(JOBS_DB_INDEXES); err != nil { | ||||||
| 		CREATE INDEX job_by_user ON job (user); |  | ||||||
| 		CREATE INDEX job_by_starttime ON job (start_time);`); err != nil { |  | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,12 @@ | |||||||
| package repository | package repository | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"database/sql" | ||||||
|  | 	"errors" | ||||||
|  | 	"strconv" | ||||||
|  |  | ||||||
|  | 	"github.com/ClusterCockpit/cc-backend/auth" | ||||||
| 	"github.com/ClusterCockpit/cc-backend/log" | 	"github.com/ClusterCockpit/cc-backend/log" | ||||||
| 	"github.com/ClusterCockpit/cc-backend/schema" | 	"github.com/ClusterCockpit/cc-backend/schema" | ||||||
| 	sq "github.com/Masterminds/squirrel" | 	sq "github.com/Masterminds/squirrel" | ||||||
| @@ -141,3 +147,38 @@ func (r *JobRepository) TagId(tagType string, tagName string) (tagId int64, exis | |||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
|  | var ErrNotFound = errors.New("no such job or user") | ||||||
|  |  | ||||||
|  | // FindJobOrUser returns a job database ID or a username if a job or user machtes the search term. | ||||||
|  | // As 0 is a valid job id, check if username is "" instead in order to check what machted. | ||||||
|  | // If nothing matches the search, `ErrNotFound` is returned. | ||||||
|  | func (r *JobRepository) FindJobOrUser(ctx context.Context, searchterm string) (job int64, username string, err error) { | ||||||
|  | 	user := auth.GetUser(ctx) | ||||||
|  | 	if id, err := strconv.Atoi(searchterm); err == nil { | ||||||
|  | 		qb := sq.Select("job.id").From("job").Where("job.job_id = ?", id) | ||||||
|  | 		if user != nil && !user.HasRole(auth.RoleAdmin) { | ||||||
|  | 			qb = qb.Where("job.user = ?", user.Username) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		err := qb.RunWith(r.DB).QueryRow().Scan(&job) | ||||||
|  | 		if err != nil && err != sql.ErrNoRows { | ||||||
|  | 			return 0, "", err | ||||||
|  | 		} else if err == nil { | ||||||
|  | 			return job, "", nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if user == nil || user.HasRole(auth.RoleAdmin) { | ||||||
|  | 		err := sq.Select("job.user").Distinct().From("job"). | ||||||
|  | 			Where("job.user = ?", searchterm). | ||||||
|  | 			RunWith(r.DB).QueryRow().Scan(&username) | ||||||
|  | 		if err != nil && err != sql.ErrNoRows { | ||||||
|  | 			return 0, "", err | ||||||
|  | 		} else if err == nil { | ||||||
|  | 			return 0, username, nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0, "", ErrNotFound | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										24
									
								
								server.go
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								server.go
									
									
									
									
									
								
							| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"io" | 	"io" | ||||||
| 	"net" | 	"net" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"net/url" | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/signal" | 	"os/signal" | ||||||
| 	"strings" | 	"strings" | ||||||
| @@ -375,6 +376,29 @@ func main() { | |||||||
| 		}) | 		}) | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
|  | 	secured.HandleFunc("/search", func(rw http.ResponseWriter, r *http.Request) { | ||||||
|  | 		if search := r.URL.Query().Get("searchId"); search != "" { | ||||||
|  | 			job, username, err := api.JobRepository.FindJobOrUser(r.Context(), search) | ||||||
|  | 			if err == repository.ErrNotFound { | ||||||
|  | 				http.Redirect(rw, r, "/monitoring/jobs/?jobId="+url.QueryEscape(search), http.StatusTemporaryRedirect) | ||||||
|  | 				return | ||||||
|  | 			} else if err != nil { | ||||||
|  | 				http.Error(rw, err.Error(), http.StatusInternalServerError) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if username != "" { | ||||||
|  | 				http.Redirect(rw, r, "/monitoring/user/"+username, http.StatusTemporaryRedirect) | ||||||
|  | 				return | ||||||
|  | 			} else { | ||||||
|  | 				http.Redirect(rw, r, fmt.Sprintf("/monitoring/job/%d", job), http.StatusTemporaryRedirect) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			http.Error(rw, "'searchId' query parameter missing", http.StatusBadRequest) | ||||||
|  | 		} | ||||||
|  | 	}) | ||||||
|  |  | ||||||
| 	setupRoutes(secured, routes) | 	setupRoutes(secured, routes) | ||||||
| 	api.MountRoutes(secured) | 	api.MountRoutes(secured) | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user