diff --git a/cmd/cc-backend/main.go b/cmd/cc-backend/main.go index c7c4b8e..e051bb8 100644 --- a/cmd/cc-backend/main.go +++ b/cmd/cc-backend/main.go @@ -47,12 +47,12 @@ import ( ) const logoString = ` - ____ _ _ ____ _ _ _ -/ ___| |_ _ ___| |_ ___ _ __ / ___|___ ___| | ___ __ (_) |_ + ____ _ _ ____ _ _ _ +/ ___| |_ _ ___| |_ ___ _ __ / ___|___ ___| | ___ __ (_) |_ | | | | | | / __| __/ _ \ '__| | / _ \ / __| |/ / '_ \| | __| -| |___| | |_| \__ \ || __/ | | |__| (_) | (__| <| |_) | | |_ +| |___| | |_| \__ \ || __/ | | |__| (_) | (__| <| |_) | | |_ \____|_|\__,_|___/\__\___|_| \____\___/ \___|_|\_\ .__/|_|\__| - |_| + |_| ` var ( @@ -226,18 +226,19 @@ func main() { } r := mux.NewRouter() + buildInfo := web.Build{Version: version, Hash: hash, Buildtime: buildTime} r.HandleFunc("/login", func(rw http.ResponseWriter, r *http.Request) { rw.Header().Add("Content-Type", "text/html; charset=utf-8") - web.RenderTemplate(rw, r, "login.tmpl", &web.Page{Title: "Login"}) + web.RenderTemplate(rw, r, "login.tmpl", &web.Page{Title: "Login", Build: buildInfo}) }).Methods(http.MethodGet) r.HandleFunc("/imprint", func(rw http.ResponseWriter, r *http.Request) { rw.Header().Add("Content-Type", "text/html; charset=utf-8") - web.RenderTemplate(rw, r, "imprint.tmpl", &web.Page{Title: "Imprint"}) + web.RenderTemplate(rw, r, "imprint.tmpl", &web.Page{Title: "Imprint", Build: buildInfo}) }) r.HandleFunc("/privacy", func(rw http.ResponseWriter, r *http.Request) { rw.Header().Add("Content-Type", "text/html; charset=utf-8") - web.RenderTemplate(rw, r, "privacy.tmpl", &web.Page{Title: "Privacy"}) + web.RenderTemplate(rw, r, "privacy.tmpl", &web.Page{Title: "Privacy", Build: buildInfo}) }) // Some routes, such as /login or /query, should only be accessible to a user that is logged in. @@ -256,6 +257,7 @@ func main() { web.RenderTemplate(rw, r, "login.tmpl", &web.Page{ Title: "Login failed - ClusterCockpit", Error: err.Error(), + Build: buildInfo, }) })).Methods(http.MethodPost) @@ -265,6 +267,7 @@ func main() { web.RenderTemplate(rw, r, "login.tmpl", &web.Page{ Title: "Bye - ClusterCockpit", Info: "Logout sucessful", + Build: buildInfo, }) }))).Methods(http.MethodPost) @@ -279,6 +282,7 @@ func main() { web.RenderTemplate(rw, r, "login.tmpl", &web.Page{ Title: "Authentication failed - ClusterCockpit", Error: err.Error(), + Build: buildInfo, }) }) }) @@ -287,7 +291,7 @@ func main() { if flagDev { r.Handle("/playground", playground.Handler("GraphQL playground", "/query")) r.PathPrefix("/swagger/").Handler(httpSwagger.Handler( - httpSwagger.URL("http://localhost:8080/swagger/doc.json"))).Methods(http.MethodGet) + httpSwagger.URL("http://clustercockpit.localhost:8082/swagger/doc.json"))).Methods(http.MethodGet) } secured.Handle("/query", graphQLEndpoint) @@ -316,7 +320,7 @@ func main() { }) // Mount all /monitoring/... and /api/... routes. - routerConfig.SetupRoutes(secured) + routerConfig.SetupRoutes(secured, version, hash, buildTime) api.MountRoutes(secured) if config.Keys.EmbedStaticFiles { diff --git a/internal/routerConfig/routes.go b/internal/routerConfig/routes.go index a5ea524..9424df7 100644 --- a/internal/routerConfig/routes.go +++ b/internal/routerConfig/routes.go @@ -253,7 +253,7 @@ func buildFilterPresets(query url.Values) map[string]interface{} { return filterPresets } -func SetupRoutes(router *mux.Router) { +func SetupRoutes(router *mux.Router, version string, hash string, buildTime string) { userCfgRepo := repository.GetUserCfgRepo() for _, route := range routes { route := route @@ -271,7 +271,7 @@ func SetupRoutes(router *mux.Router) { } username, isAdmin, isSupporter := "", true, true - + if user := auth.GetUser(r.Context()); user != nil { username = user.Username isAdmin = user.HasRole(auth.RoleAdmin) @@ -281,6 +281,7 @@ func SetupRoutes(router *mux.Router) { page := web.Page{ Title: title, User: web.User{Username: username, IsAdmin: isAdmin, IsSupporter: isSupporter}, + Build: web.Build{Version: version, Hash: hash, Buildtime: buildTime}, Config: conf, Infos: infos, } diff --git a/web/frontend/public/global.css b/web/frontend/public/global.css index 8feecf6..7e4e805 100644 --- a/web/frontend/public/global.css +++ b/web/frontend/public/global.css @@ -52,3 +52,21 @@ footer { margin: 0rem 0.8rem; white-space: nowrap; } + +.build-list { + color: gray; + font-size: 12px; + list-style-type: none; + padding-left: 0; + width: 100%; + display: flex; + flex-wrap: wrap; + justify-content: right; + margin-top: 0px; + margin-bottom: 5px; +} + +.build-list-item { + margin: 0rem 0.8rem; + white-space: nowrap; +} diff --git a/web/templates/base.tmpl b/web/templates/base.tmpl index c3a5bae..17268c8 100644 --- a/web/templates/base.tmpl +++ b/web/templates/base.tmpl @@ -40,6 +40,11 @@ + {{end}} diff --git a/web/web.go b/web/web.go index 25bbc68..61e0128 100644 --- a/web/web.go +++ b/web/web.go @@ -59,11 +59,18 @@ type User struct { IsSupporter bool } +type Build struct { + Version string + Hash string + Buildtime string +} + type Page struct { Title string // Page title Error string // For generic use (e.g. the exact error message on /login) Info string // For generic use (e.g. "Logout successfull" on /login) User User // Information about the currently logged in user + Build Build // Latest information about the application Clusters []schema.ClusterConfig // List of all clusters for use in the Header FilterPresets map[string]interface{} // For pages with the Filter component, this can be used to set initial filters. Infos map[string]interface{} // For generic use (e.g. username for /monitoring/user/, job id for /monitoring/job/)