mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-12-25 21:09:05 +01:00
Add more views
This commit is contained in:
parent
b1d2403839
commit
9c5c8a05e2
11
README.md
11
README.md
@ -1,4 +1,4 @@
|
|||||||
# ClusterCockpit with a Golang backend (Only supports archived jobs)
|
# ClusterCockpit with a Golang backend
|
||||||
|
|
||||||
[![Build](https://github.com/ClusterCockpit/cc-jobarchive/actions/workflows/test.yml/badge.svg)](https://github.com/ClusterCockpit/cc-jobarchive/actions/workflows/test.yml)
|
[![Build](https://github.com/ClusterCockpit/cc-jobarchive/actions/workflows/test.yml/badge.svg)](https://github.com/ClusterCockpit/cc-jobarchive/actions/workflows/test.yml)
|
||||||
|
|
||||||
@ -11,7 +11,12 @@ git clone --recursive git@github.com:ClusterCockpit/cc-jobarchive.git
|
|||||||
# Prepare frontend
|
# Prepare frontend
|
||||||
cd ./cc-jobarchive/frontend
|
cd ./cc-jobarchive/frontend
|
||||||
yarn install
|
yarn install
|
||||||
CCFRONTEND_ROLLUP_INTRO="" yarn build
|
export CCFRONTEND_ROLLUP_INTRO='
|
||||||
|
const JOBVIEW_URL = job => `/monitoring/job/${job.jobId}`;
|
||||||
|
const USERVIEW_URL = userId => `/monitoring/user/${userId}`;
|
||||||
|
const TAG_URL = tag => `/monitoring/jobs/?tag=${tag.id}`;
|
||||||
|
'
|
||||||
|
yarn build
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
go get
|
go get
|
||||||
@ -45,4 +50,6 @@ This project uses [gqlgen](https://github.com/99designs/gqlgen) for the GraphQL
|
|||||||
|
|
||||||
- [ ] Documentation
|
- [ ] Documentation
|
||||||
- [ ] Write more TODOs
|
- [ ] Write more TODOs
|
||||||
|
- [ ] Caching
|
||||||
|
- [ ] Generate JWTs based on the provided keys
|
||||||
|
|
||||||
|
2
frontend
2
frontend
@ -1 +1 @@
|
|||||||
Subproject commit 41b8953eb14e52fe9f3c4fe69167202f8002de45
|
Subproject commit cc48461a810dbd3565000150fc99332743de92ba
|
108
server.go
108
server.go
@ -176,8 +176,27 @@ func main() {
|
|||||||
secured.HandleFunc("/config.json", config.ServeConfig).Methods(http.MethodGet)
|
secured.HandleFunc("/config.json", config.ServeConfig).Methods(http.MethodGet)
|
||||||
|
|
||||||
secured.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
|
secured.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
conf, err := config.GetUIConfig(r)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
infos := map[string]interface{}{
|
||||||
|
"clusters": config.Clusters,
|
||||||
|
"username": "",
|
||||||
|
"admin": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if user := auth.GetUser(r.Context()); user != nil {
|
||||||
|
infos["username"] = user.Username
|
||||||
|
infos["admin"] = user.IsAdmin
|
||||||
|
}
|
||||||
|
|
||||||
templates.Render(rw, r, "home", &templates.Page{
|
templates.Render(rw, r, "home", &templates.Page{
|
||||||
Title: "ClusterCockpit",
|
Title: "ClusterCockpit",
|
||||||
|
Config: conf,
|
||||||
|
Infos: infos,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -208,9 +227,34 @@ func monitoringRoutes(router *mux.Router, resolver *graph.Resolver) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filterPresets := map[string]interface{}{}
|
||||||
|
query := r.URL.Query()
|
||||||
|
if query.Get("tag") != "" {
|
||||||
|
filterPresets["tagId"] = query.Get("tag")
|
||||||
|
}
|
||||||
|
if query.Get("cluster") != "" {
|
||||||
|
filterPresets["clusterId"] = query.Get("cluster")
|
||||||
|
}
|
||||||
|
if query.Get("project") != "" {
|
||||||
|
filterPresets["projectId"] = query.Get("project")
|
||||||
|
}
|
||||||
|
if query.Get("running") == "true" {
|
||||||
|
filterPresets["isRunning"] = true
|
||||||
|
}
|
||||||
|
if query.Get("running") == "false" {
|
||||||
|
filterPresets["isRunning"] = false
|
||||||
|
}
|
||||||
|
if query.Get("from") != "" && query.Get("to") != "" {
|
||||||
|
filterPresets["startTime"] = map[string]string{
|
||||||
|
"from": query.Get("from"),
|
||||||
|
"to": query.Get("to"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
templates.Render(rw, r, "monitoring/jobs/", &templates.Page{
|
templates.Render(rw, r, "monitoring/jobs/", &templates.Page{
|
||||||
Title: "Jobs - ClusterCockpit",
|
Title: "Jobs - ClusterCockpit",
|
||||||
Config: conf,
|
Config: conf,
|
||||||
|
FilterPresets: filterPresets,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -266,8 +310,64 @@ func monitoringRoutes(router *mux.Router, resolver *graph.Resolver) {
|
|||||||
templates.Render(rw, r, "monitoring/user/", &templates.Page{
|
templates.Render(rw, r, "monitoring/user/", &templates.Page{
|
||||||
Title: fmt.Sprintf("User %s - ClusterCockpit", id),
|
Title: fmt.Sprintf("User %s - ClusterCockpit", id),
|
||||||
Config: conf,
|
Config: conf,
|
||||||
|
Infos: map[string]interface{}{"userId": id},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
router.HandleFunc("/monitoring/analysis/", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
conf, err := config.GetUIConfig(r)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
filterPresets := map[string]interface{}{}
|
||||||
|
query := r.URL.Query()
|
||||||
|
if query.Get("cluster") != "" {
|
||||||
|
filterPresets["clusterId"] = query.Get("cluster")
|
||||||
|
}
|
||||||
|
|
||||||
|
templates.Render(rw, r, "monitoring/analysis/", &templates.Page{
|
||||||
|
Title: "Analysis View - ClusterCockpit",
|
||||||
|
Config: conf,
|
||||||
|
FilterPresets: filterPresets,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
router.HandleFunc("/monitoring/systems/", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
conf, err := config.GetUIConfig(r)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
filterPresets := map[string]interface{}{}
|
||||||
|
query := r.URL.Query()
|
||||||
|
if query.Get("cluster") != "" {
|
||||||
|
filterPresets["clusterId"] = query.Get("cluster")
|
||||||
|
}
|
||||||
|
|
||||||
|
templates.Render(rw, r, "monitoring/systems/", &templates.Page{
|
||||||
|
Title: "System View - ClusterCockpit",
|
||||||
|
Config: conf,
|
||||||
|
FilterPresets: filterPresets,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
router.HandleFunc("/monitoring/node/{clusterId}/{nodeId}", func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
conf, err := config.GetUIConfig(r)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
templates.Render(rw, r, "monitoring/node/", &templates.Page{
|
||||||
|
Title: fmt.Sprintf("Node %s - ClusterCockpit", vars["nodeId"]),
|
||||||
|
Config: conf,
|
||||||
Infos: map[string]interface{}{
|
Infos: map[string]interface{}{
|
||||||
"userId": id,
|
"nodeId": vars["nodeId"],
|
||||||
|
"clusterId": vars["clusterId"],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,10 +1,57 @@
|
|||||||
{{define "content"}}
|
{{define "content"}}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<ul>
|
{{if .Infos.username}}
|
||||||
<li><a href="/monitoring/jobs/">jobs</a></li>
|
<i class="bi bi-person-circle"></i> {{ .Infos.username }}
|
||||||
<li><a href="/monitoring/users/">users</a></li>
|
{{if .Infos.admin}}
|
||||||
</ul>
|
<span class="badge bg-primary">Admin</span>
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
<div class="col" style="text-align: right;">
|
||||||
|
<form method="post" action="/logout">
|
||||||
|
<button type="submit" class="btn btn-primary">Logout</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
{{if .Infos.admin}}
|
||||||
|
<div class="col-4">
|
||||||
|
<ul>
|
||||||
|
<li><a href="/monitoring/jobs/">All jobs</a></li>
|
||||||
|
<li><a href="/monitoring/users/">All users</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="col-4">
|
||||||
|
<ul>
|
||||||
|
<li><a href="/monitoring/jobs/">My jobs</a></li>
|
||||||
|
<li><a href="/monitoring/user/{{.Infos.username}}">My user view</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
<div class="col-8">
|
||||||
|
<h2>Clusters</h2>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name/ID</th>
|
||||||
|
<th>Jobs</th>
|
||||||
|
<th>System View</th>
|
||||||
|
<th>Analysis View</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{range .Infos.clusters}}
|
||||||
|
<tr>
|
||||||
|
<td>{{.ClusterID}}</td>
|
||||||
|
<td><a href="/monitoring/jobs/?cluster={{.ClusterID}}">Jobs</a></td>
|
||||||
|
<td><a href="/monitoring/systems/?cluster={{.ClusterID}}">System View</a></td>
|
||||||
|
<td><a href="/monitoring/analysis/?cluster={{.ClusterID}}">Analysis View</a></td>
|
||||||
|
</tr>
|
||||||
|
{{end}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
18
templates/monitoring/analysis.html
Normal file
18
templates/monitoring/analysis.html
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{{define "content"}}
|
||||||
|
<div id="svelte-app"></div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "stylesheets"}}
|
||||||
|
<link rel='stylesheet' href='/build/analysis.css'>
|
||||||
|
{{end}}
|
||||||
|
{{define "javascript"}}
|
||||||
|
<script>
|
||||||
|
const filterPresets = {{ .FilterPresets }};
|
||||||
|
const clusterCockpitConfigPromise = Promise.resolve({
|
||||||
|
plot_view_plotsPerRow: {{ .Config.plot_view_plotsPerRow }},
|
||||||
|
analysis_view_histogramMetrics: {{ .Config.analysis_view_histogramMetrics }},
|
||||||
|
analysis_view_scatterPlotMetrics: {{ .Config.analysis_view_scatterPlotMetrics }}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script src='/build/analysis.js'></script>
|
||||||
|
{{end}}
|
@ -7,7 +7,7 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
{{define "javascript"}}
|
{{define "javascript"}}
|
||||||
<script>
|
<script>
|
||||||
const filterPresets = null;
|
const filterPresets = {{ .FilterPresets }};
|
||||||
const clusterCockpitConfigPromise = Promise.resolve({
|
const clusterCockpitConfigPromise = Promise.resolve({
|
||||||
plot_general_colorscheme: {{ .Config.plot_general_colorscheme }},
|
plot_general_colorscheme: {{ .Config.plot_general_colorscheme }},
|
||||||
plot_general_lineWidth: {{ .Config.plot_general_lineWidth }},
|
plot_general_lineWidth: {{ .Config.plot_general_lineWidth }},
|
||||||
|
21
templates/monitoring/node.html
Normal file
21
templates/monitoring/node.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{{define "content"}}
|
||||||
|
<div id="svelte-app"></div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "stylesheets"}}
|
||||||
|
<link rel='stylesheet' href='/build/node.css'>
|
||||||
|
{{end}}
|
||||||
|
{{define "javascript"}}
|
||||||
|
<script>
|
||||||
|
const nodeInfos = {
|
||||||
|
nodeId: "{{ .Infos.nodeId }}",
|
||||||
|
clusterId: "{{ .Infos.clusterId }}"
|
||||||
|
};
|
||||||
|
const clusterCockpitConfigPromise = Promise.resolve({
|
||||||
|
plot_general_colorscheme: {{ .Config.plot_general_colorscheme }},
|
||||||
|
plot_general_lineWidth: {{ .Config.plot_general_lineWidth }},
|
||||||
|
plot_general_colorBackground: {{ .Config.plot_general_colorBackground }},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script src='/build/node.js'></script>
|
||||||
|
{{end}}
|
19
templates/monitoring/systems.html
Normal file
19
templates/monitoring/systems.html
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{{define "content"}}
|
||||||
|
<div id="svelte-app"></div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "stylesheets"}}
|
||||||
|
<link rel='stylesheet' href='/build/systems.css'>
|
||||||
|
{{end}}
|
||||||
|
{{define "javascript"}}
|
||||||
|
<script>
|
||||||
|
const filterPresets = {{ .FilterPresets }};
|
||||||
|
const clusterCockpitConfigPromise = Promise.resolve({
|
||||||
|
plot_view_plotsPerRow: {{ .Config.plot_view_plotsPerRow }},
|
||||||
|
plot_general_colorscheme: {{ .Config.plot_general_colorscheme }},
|
||||||
|
plot_general_lineWidth: {{ .Config.plot_general_lineWidth }},
|
||||||
|
plot_general_colorBackground: {{ .Config.plot_general_colorBackground }},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<script src='/build/systems.js'></script>
|
||||||
|
{{end}}
|
@ -9,10 +9,11 @@ import (
|
|||||||
var templates map[string]*template.Template
|
var templates map[string]*template.Template
|
||||||
|
|
||||||
type Page struct {
|
type Page struct {
|
||||||
Title string
|
Title string
|
||||||
Login *LoginPage
|
Login *LoginPage
|
||||||
Infos map[string]interface{}
|
FilterPresets map[string]interface{}
|
||||||
Config map[string]interface{}
|
Infos map[string]interface{}
|
||||||
|
Config map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginPage struct {
|
type LoginPage struct {
|
||||||
@ -23,18 +24,26 @@ type LoginPage struct {
|
|||||||
func init() {
|
func init() {
|
||||||
base := template.Must(template.ParseFiles("./templates/base.html"))
|
base := template.Must(template.ParseFiles("./templates/base.html"))
|
||||||
templates = map[string]*template.Template{
|
templates = map[string]*template.Template{
|
||||||
"home": template.Must(template.Must(base.Clone()).ParseFiles("./templates/home.html")),
|
"home": template.Must(template.Must(base.Clone()).ParseFiles("./templates/home.html")),
|
||||||
"404": template.Must(template.Must(base.Clone()).ParseFiles("./templates/404.html")),
|
"404": template.Must(template.Must(base.Clone()).ParseFiles("./templates/404.html")),
|
||||||
"login": template.Must(template.Must(base.Clone()).ParseFiles("./templates/login.html")),
|
"login": template.Must(template.Must(base.Clone()).ParseFiles("./templates/login.html")),
|
||||||
"monitoring/jobs/": template.Must(template.Must(base.Clone()).ParseFiles("./templates/monitoring/jobs.html")),
|
"monitoring/jobs/": template.Must(template.Must(base.Clone()).ParseFiles("./templates/monitoring/jobs.html")),
|
||||||
"monitoring/job/": template.Must(template.Must(base.Clone()).ParseFiles("./templates/monitoring/job.html")),
|
"monitoring/job/": template.Must(template.Must(base.Clone()).ParseFiles("./templates/monitoring/job.html")),
|
||||||
"monitoring/users/": template.Must(template.Must(base.Clone()).ParseFiles("./templates/monitoring/users.html")),
|
"monitoring/users/": template.Must(template.Must(base.Clone()).ParseFiles("./templates/monitoring/users.html")),
|
||||||
"monitoring/user/": template.Must(template.Must(base.Clone()).ParseFiles("./templates/monitoring/user.html")),
|
"monitoring/user/": template.Must(template.Must(base.Clone()).ParseFiles("./templates/monitoring/user.html")),
|
||||||
|
"monitoring/analysis/": template.Must(template.Must(base.Clone()).ParseFiles("./templates/monitoring/analysis.html")),
|
||||||
|
"monitoring/systems/": template.Must(template.Must(base.Clone()).ParseFiles("./templates/monitoring/systems.html")),
|
||||||
|
"monitoring/node/": template.Must(template.Must(base.Clone()).ParseFiles("./templates/monitoring/node.html")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Render(rw http.ResponseWriter, r *http.Request, name string, page *Page) {
|
func Render(rw http.ResponseWriter, r *http.Request, name string, page *Page) {
|
||||||
if err := templates[name].Execute(rw, page); err != nil {
|
t, ok := templates[name]
|
||||||
|
if !ok {
|
||||||
|
panic("templates must be predefinied!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.Execute(rw, page); err != nil {
|
||||||
log.Printf("template error: %s\n", err.Error())
|
log.Printf("template error: %s\n", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user