diff --git a/README.md b/README.md index f57a0f2..2c941af 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ git clone --recursive git@github.com:ClusterCockpit/cc-jobarchive.git # Prepare frontend cd ./cc-jobarchive/frontend yarn install -yarn build +CCFRONTEND_ROLLUP_INTRO="" yarn build cd .. go get @@ -33,7 +33,16 @@ touch ./var/job.db ./cc-jobarchive --help ``` +### Configuration + +A config file in the JSON format can be provided using `--config` to override the defaults. Loop at the beginning of `server.go` for the defaults and consequently the format of the configuration file. + ### Update GraphQL schema This project uses [gqlgen](https://github.com/99designs/gqlgen) for the GraphQL API. The schema can be found in `./graph/schema.graphqls`. After changing it, you need to run `go run github.com/99designs/gqlgen` which will update `graph/model`. In case new resolvers are needed, they will be inserted into `graph/schema.resolvers.go`, where you will need to implement them. +### TODO + +- [ ] Documentation +- [ ] Write more TODOs + diff --git a/auth/auth.go b/auth/auth.go index 66fd1e9..d10ac3b 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -168,7 +168,7 @@ func Login(db *sqlx.DB) http.Handler { if err != nil { log.Printf("login failed: %s\n", err.Error()) rw.WriteHeader(http.StatusUnauthorized) - templates.Render(rw, r, "login.html", &templates.Page{ + templates.Render(rw, r, "login", &templates.Page{ Title: "Login failed", Login: &templates.LoginPage{ Error: "Username or password incorrect", @@ -264,7 +264,7 @@ func Auth(next http.Handler) http.Handler { log.Printf("authentication failed: no session or jwt found\n") rw.WriteHeader(http.StatusUnauthorized) - templates.Render(rw, r, "login.html", &templates.Page{ + templates.Render(rw, r, "login", &templates.Page{ Title: "Authentication failed", Login: &templates.LoginPage{ Error: "No valid session or JWT provided", @@ -320,7 +320,7 @@ func Logout(rw http.ResponseWriter, r *http.Request) { } } - templates.Render(rw, r, "login.html", &templates.Page{ + templates.Render(rw, r, "login", &templates.Page{ Title: "Logout successful", Login: &templates.LoginPage{ Info: "Logout successful", diff --git a/server.go b/server.go index 58e3d02..5cd4790 100644 --- a/server.go +++ b/server.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "flag" + "fmt" "log" "net/http" "os" @@ -141,11 +142,12 @@ func main() { // Build routes... - graphQLEndpoint := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{DB: db}})) + resolver := &graph.Resolver{DB: db} + graphQLEndpoint := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: resolver})) graphQLPlayground := playground.Handler("GraphQL playground", "/query") handleGetLogin := func(rw http.ResponseWriter, r *http.Request) { - templates.Render(rw, r, "login.html", &templates.Page{ + templates.Render(rw, r, "login", &templates.Page{ Title: "Login", Login: &templates.LoginPage{}, }) @@ -153,7 +155,7 @@ func main() { r := mux.NewRouter() r.NotFoundHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { - templates.Render(rw, r, "404.html", &templates.Page{ + templates.Render(rw, r, "404", &templates.Page{ Title: "Not found", }) }) @@ -170,9 +172,17 @@ func main() { secured.Handle("/query", graphQLEndpoint) secured.HandleFunc("/api/jobs/start_job/", startJob).Methods(http.MethodPost) secured.HandleFunc("/api/jobs/stop_job/", stopJob).Methods(http.MethodPost, http.MethodPut) - secured.HandleFunc("/api/jobs/stop_job/{id}", stopJob).Methods(http.MethodPost, http.MethodPut) + secured.HandleFunc("/api/jobs/stop_job/{id:[0-9]+}", stopJob).Methods(http.MethodPost, http.MethodPut) secured.HandleFunc("/config.json", config.ServeConfig).Methods(http.MethodGet) + secured.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) { + templates.Render(rw, r, "home", &templates.Page{ + Title: "ClusterCockpit", + }) + }) + + monitoringRoutes(secured, resolver) + r.PathPrefix("/").Handler(http.FileServer(http.Dir(programConfig.StaticFiles))) handler := handlers.CORS( handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"}), @@ -189,3 +199,76 @@ func main() { } log.Fatal(err) } + +func monitoringRoutes(router *mux.Router, resolver *graph.Resolver) { + router.HandleFunc("/monitoring/jobs/", func(rw http.ResponseWriter, r *http.Request) { + conf, err := config.GetUIConfig(r) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + templates.Render(rw, r, "monitoring/jobs/", &templates.Page{ + Title: "Jobs - ClusterCockpit", + Config: conf, + }) + }) + + router.HandleFunc("/monitoring/job/{id:[0-9]+}", func(rw http.ResponseWriter, r *http.Request) { + conf, err := config.GetUIConfig(r) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + id := mux.Vars(r)["id"] + job, err := resolver.Query().Job(r.Context(), id) + if err != nil { + http.Error(rw, err.Error(), http.StatusNotFound) + return + } + + templates.Render(rw, r, "monitoring/job/", &templates.Page{ + Title: fmt.Sprintf("Job %s - ClusterCockpit", job.JobID), + Config: conf, + Infos: map[string]interface{}{ + "id": id, + "jobId": job.JobID, + "clusterId": job.ClusterID, + }, + }) + }) + + router.HandleFunc("/monitoring/users/", func(rw http.ResponseWriter, r *http.Request) { + conf, err := config.GetUIConfig(r) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + templates.Render(rw, r, "monitoring/users/", &templates.Page{ + Title: "Users - ClusterCockpit", + Config: conf, + }) + }) + + router.HandleFunc("/monitoring/user/{id}", func(rw http.ResponseWriter, r *http.Request) { + conf, err := config.GetUIConfig(r) + if err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + return + } + + id := mux.Vars(r)["id"] + // TODO: One could check if the user exists, but that would be unhelpfull if authentication + // is disabled or the user does not exist but has started jobs. + + templates.Render(rw, r, "monitoring/user/", &templates.Page{ + Title: fmt.Sprintf("User %s - ClusterCockpit", id), + Config: conf, + Infos: map[string]interface{}{ + "userId": id, + }, + }) + }) +} diff --git a/templates/base.html b/templates/base.html index 32d6f6b..4691f30 100644 --- a/templates/base.html +++ b/templates/base.html @@ -9,6 +9,9 @@ + + + {{block "stylesheets" .}}{{end}}