From 419902c59bdb530cd3554d3eb775e4ad3838d3f5 Mon Sep 17 00:00:00 2001 From: Tom Wiesing Date: Fri, 10 Nov 2023 19:30:05 +0100 Subject: [PATCH] panel/admin: Continue moving out information This page further splits up the admin page into several parts. --- internal/dis/component/server/admin/admin.go | 24 ++ .../component/server/admin/html/instance.html | 260 ------------------ .../server/admin/html/instance_data.html | 61 ++++ .../server/admin/html/instance_drupal.html | 94 +++++++ .../server/admin/html/instance_ssh.html | 38 +++ .../server/admin/html/instance_stats.html | 98 +++++++ .../dis/component/server/admin/instance.go | 15 +- .../component/server/admin/instance_data.go | 77 ++++++ .../component/server/admin/instance_drupal.go | 86 ++++++ .../component/server/admin/instance_ssh.go | 80 ++++++ .../component/server/admin/instance_stats.go | 67 +++++ internal/wisski/wisski.go | 16 ++ 12 files changed, 645 insertions(+), 271 deletions(-) create mode 100644 internal/dis/component/server/admin/html/instance_data.html create mode 100644 internal/dis/component/server/admin/html/instance_drupal.html create mode 100644 internal/dis/component/server/admin/html/instance_ssh.html create mode 100644 internal/dis/component/server/admin/html/instance_stats.html create mode 100644 internal/dis/component/server/admin/instance_data.go create mode 100644 internal/dis/component/server/admin/instance_drupal.go create mode 100644 internal/dis/component/server/admin/instance_ssh.go create mode 100644 internal/dis/component/server/admin/instance_stats.go diff --git a/internal/dis/component/server/admin/admin.go b/internal/dis/component/server/admin/admin.go index 128db04..4f18cf7 100644 --- a/internal/dis/component/server/admin/admin.go +++ b/internal/dis/component/server/admin/admin.go @@ -75,6 +75,10 @@ var ( menuGrants = component.DummyMenuItem() menuPurge = component.DummyMenuItem() menuSnapshots = component.DummyMenuItem() + menuSSH = component.DummyMenuItem() + menuStats = component.DummyMenuItem() + menuData = component.DummyMenuItem() + menuDrupal = component.DummyMenuItem() ) func (admin *Admin) HandleRoute(ctx context.Context, route string) (handler http.Handler, err error) { @@ -145,6 +149,26 @@ func (admin *Admin) HandleRoute(ctx context.Context, route string) (handler http router.Handler(http.MethodGet, route+"instance/:slug/snapshots", snapshots) } + { + ssh := admin.instanceSSH(ctx) + router.Handler(http.MethodGet, route+"instance/:slug/ssh", ssh) + } + + { + stats := admin.instanceStats(ctx) + router.Handler(http.MethodGet, route+"instance/:slug/stats", stats) + } + + { + data := admin.instanceData(ctx) + router.Handler(http.MethodGet, route+"instance/:slug/data", data) + } + + { + drupal := admin.instanceDrupal(ctx) + router.Handler(http.MethodGet, route+"instance/:slug/drupal", drupal) + } + // add a router for the login page router.Handler(http.MethodPost, route+"login", admin.loginHandler(ctx)) diff --git a/internal/dis/component/server/admin/html/instance.html b/internal/dis/component/server/admin/html/instance.html index 1efa196..8251d1f 100644 --- a/internal/dis/component/server/admin/html/instance.html +++ b/internal/dis/component/server/admin/html/instance.html @@ -7,8 +7,6 @@ Info & Status Drupal Status Report WissKI Data - Statistics - SSH Keys @@ -126,39 +124,6 @@ -
-
-
- - - - - - - - - - - - - - - - -
- Drupal Info -
- Drupal Version - - {{ .Info.DrupalVersion }} -
- Default Theme - - {{ .Info.Theme }} -
-
-
-
@@ -288,228 +253,3 @@
- -
-

Drupal Status Report

-
- -
-
-
- - - - - - - - - - - - - {{ range $name, $req := .Info.Requirements }} - - - - - - - - {{ end }} - -
- ID - - Severity - - Title - - Value - - Description -
- {{ $req.ID }} - - {{ $req.Level }} - - {{ $req.Title }} - - {{ $req.Value }} - - {{ $req.Description }} -
-
-
-
- -
-

WissKI Data

-
- -
-
-
- - - - - - - - - {{ range $name, $xml := .Info.Pathbuilders }} - - - - - {{ end }} - -
- Pathbuilders -
- {{ $name }} - - {{ $xml }} -
-
-
-
-
-
-
- - - - - - - - {{ range $index, $prefix := .Info.Prefixes }} - - - - {{ end }} - -
- URI Prefixes - - {{ if .Info.NoPrefixes }} - (excluded from resolver) - {{ end }} -
- {{ $prefix }} -
-
-
-
- -
-

Statistics

-
- -
-
-
- - - - - - - - - - - - - - - {{ range .Info.Statistics.Bundles.Bundles }} - - - - - - - - {{ end }} - -
- Bundles -
- Label - - Machine Name - - Count - - LastEdit - - MainBundle -
- {{ .Label }} - - {{ .MachineName }} - - {{ .Count }} - - {{ .LastEdit.Time.Format "2006-01-02T15:04:05Z07:00" }} - - {{ .MainBundle }} -
-
-
-
- - -
-
-
- - - - - - - - - - - - {{ range .Info.Statistics.Triplestore.Graphs }} - - - - - {{ end }} - -
- Triplestore -
- URI - - Count -
- {{ .URI }} - - {{ .Count }} -
-
-
-
- -
-

SSH Keys

- - - {{ range .Info.SSHKeys }} - - - - {{ end }} - -
- {{ . }} -
-
diff --git a/internal/dis/component/server/admin/html/instance_data.html b/internal/dis/component/server/admin/html/instance_data.html new file mode 100644 index 0000000..28b8bf9 --- /dev/null +++ b/internal/dis/component/server/admin/html/instance_data.html @@ -0,0 +1,61 @@ + +
+

WissKI Data

+
+ +
+
+
+ + + + + + + + + {{ range $name, $xml := .Pathbuilders }} + + + + + {{ end }} + +
+ Pathbuilders +
+ {{ $name }} + + {{ $xml }} +
+
+
+
+
+
+
+ + + + + + + + {{ range $index, $prefix := .Prefixes }} + + + + {{ end }} + +
+ URI Prefixes + + {{ if .NoPrefixes }} + (excluded from resolver) + {{ end }} +
+ {{ $prefix }} +
+
+
+
diff --git a/internal/dis/component/server/admin/html/instance_drupal.html b/internal/dis/component/server/admin/html/instance_drupal.html new file mode 100644 index 0000000..1f255aa --- /dev/null +++ b/internal/dis/component/server/admin/html/instance_drupal.html @@ -0,0 +1,94 @@ +
+

Drupal Information

+
+ +
+
+
+ + + + + + + + + + + + + + + + +
+ Drupal Info +
+ Drupal Version + + {{ .DrupalVersion }} +
+ Default Theme + + {{ .DefaultTheme }} +
+
+
+
+ +
+

Status Report

+

+ This mirrors the Status Report page found inside drupal. +

+
+ +
+
+
+ + + + + + + + + + + + + {{ range $name, $req := .Requirements }} + + + + + + + + {{ end }} + +
+ ID + + Severity + + Title + + Value + + Description +
+ {{ $req.ID }} + + {{ $req.Level }} + + {{ $req.Title }} + + {{ $req.Value }} + + {{ $req.Description }} +
+
+
+
diff --git a/internal/dis/component/server/admin/html/instance_ssh.html b/internal/dis/component/server/admin/html/instance_ssh.html new file mode 100644 index 0000000..6e466a2 --- /dev/null +++ b/internal/dis/component/server/admin/html/instance_ssh.html @@ -0,0 +1,38 @@ +
+

SSH Access

+

+ Every WissKI Instance comes with access via SSH. + Every distillery user that has administrative access to this instance can access it. + Furthermore, every distillery administrator has access. +

+

+ To access the ssh server of this instance, use: +

+ +
ssh -J {{ .PanelDomain }}:{{ .Port }} www-data@{{ .Hostname }}
+
+

+ For this to work, configure an ssh key on your Personal SSH Page. +

+
+ +
+

SSH Keys

+

+ This page lists all SSH Keys that have access to this system. +

+
+ +
+ + + {{ range .SSHKeys }} + + + + {{ end }} + +
+ {{ . }} +
+
\ No newline at end of file diff --git a/internal/dis/component/server/admin/html/instance_stats.html b/internal/dis/component/server/admin/html/instance_stats.html new file mode 100644 index 0000000..a0a966a --- /dev/null +++ b/internal/dis/component/server/admin/html/instance_stats.html @@ -0,0 +1,98 @@ +
+

+ This page contains statistics generated by the WissKI Statistics module. + If the module is not available, this page may be empty. +

+
+ + +
+
+
+ + + + + + + + + + + + + + + {{ range .Statistics.Bundles.Bundles }} + + + + + + + + {{ end }} + +
+ Bundles +
+ Label + + Machine Name + + Count + + LastEdit + + MainBundle +
+ {{ .Label }} + + {{ .MachineName }} + + {{ .Count }} + + {{ .LastEdit.Time.Format "2006-01-02T15:04:05Z07:00" }} + + {{ .MainBundle }} +
+
+
+
+ + +
+
+
+ + + + + + + + + + + + {{ range .Statistics.Triplestore.Graphs }} + + + + + {{ end }} + +
+ Triplestore +
+ URI + + Count +
+ {{ .URI }} + + {{ .Count }} +
+
+
+
diff --git a/internal/dis/component/server/admin/instance.go b/internal/dis/component/server/admin/instance.go index aaea17c..a739a33 100644 --- a/internal/dis/component/server/admin/instance.go +++ b/internal/dis/component/server/admin/instance.go @@ -79,19 +79,12 @@ func (admin *Admin) instanceTabs(slug string, active string) templating.FlagFunc {Title: "Overview", Path: template.URL("/admin/instance/" + slug), Active: active == "overview"}, {Title: "Rebuild", Path: template.URL("/admin/instance/" + slug + "/rebuild"), Active: active == "rebuild"}, {Title: "Users & Grants", Path: template.URL("/admin/instance/" + slug + "/users"), Active: active == "users"}, + {Title: "Drupal Status", Path: template.URL("/admin/instance/" + slug + "/drupal"), Active: active == "drupal"}, + {Title: "WissKI Data", Path: template.URL("/admin/instance/" + slug + "/data"), Active: active == "data"}, + {Title: "WissKI Stats", Path: template.URL("/admin/instance/" + slug + "/stats"), Active: active == "stats"}, + {Title: "SSH", Path: template.URL("/admin/instance/" + slug + "/ssh"), Active: active == "ssh"}, {Title: "Snapshots", Path: template.URL("/admin/instance/" + slug + "/snapshots"), Active: active == "snapshots"}, {Title: "Purge", Path: template.URL("/admin/instance/" + slug + "/purge"), Active: active == "purge"}, - - // TODO: These still need to be migrated to their own tabs - // Then we also need to redo the main page - /* - {Title: "Status", Path: template.URL("/instance/" + slug + "/status"), Active: active == "status"}, - {Title: "Database", Path: template.URL("/instance/" + slug + "/database"), Active: active == "database"}, - {Title: "Drupal", Path: template.URL("/instance/" + slug + "/drupal"), Active: active == "drupal"}, - {Title: "Users & Grants", Path: template.URL("/instance/" + slug + "/users"), Active: active == "users"}, - {Title: "Stats", Path: template.URL("/instance/" + slug + "/stats"), Active: active == "stats"}, - {Title: "SSH", Path: template.URL("/instance/" + slug + "/ssh"), Active: active == "ssh"}, - */ } return flags } diff --git a/internal/dis/component/server/admin/instance_data.go b/internal/dis/component/server/admin/instance_data.go new file mode 100644 index 0000000..adf83f0 --- /dev/null +++ b/internal/dis/component/server/admin/instance_data.go @@ -0,0 +1,77 @@ +package admin + +import ( + "context" + _ "embed" + "html/template" + "net/http" + + "github.com/FAU-CDI/wisski-distillery/internal/dis/component" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" + "github.com/FAU-CDI/wisski-distillery/internal/wisski" + "github.com/tkw1536/pkglib/httpx" + + "github.com/julienschmidt/httprouter" +) + +//go:embed "html/instance_data.html" +var instanceDataHTML []byte +var instanceDataTemplate = templating.Parse[instanceDataContext]( + "instance_data.html", instanceDataHTML, nil, + + templating.Assets(assets.AssetsAdmin), +) + +type instanceDataContext struct { + templating.RuntimeFlags + + Instance *wisski.WissKI + Pathbuilders map[string]string + NoPrefixes bool + Prefixes []string +} + +func (admin *Admin) instanceData(ctx context.Context) http.Handler { + tpl := instanceDataTemplate.Prepare( + admin.dependencies.Templating, + templating.Crumbs( + menuAdmin, + menuInstances, + menuInstance, + menuData, + ), + ) + + return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ctx instanceDataContext, funcs []templating.FlagFunc, err error) { + slug := httprouter.ParamsFromContext(r.Context()).ByName("slug") + + // setup the context with just the instance + ctx.Instance, err = admin.dependencies.Instances.WissKI(r.Context(), slug) + if err != nil { + return ctx, nil, httpx.ErrNotFound + } + + server := ctx.Instance.PHP().NewServer() + defer server.Close() + + ctx.Pathbuilders, err = ctx.Instance.Pathbuilder().GetAll(r.Context(), server) + if err != nil { + return ctx, nil, httpx.ErrInternalServerError + } + + prefixes := ctx.Instance.Prefixes() + ctx.NoPrefixes = prefixes.NoPrefix() + ctx.Prefixes, err = prefixes.All(r.Context(), server) + if err != nil { + return ctx, nil, httpx.ErrInternalServerError + } + + return ctx, []templating.FlagFunc{ + templating.ReplaceCrumb(menuInstance, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + ctx.Instance.Slug)}), + templating.ReplaceCrumb(menuData, component.MenuItem{Title: "SSH", Path: template.URL("/admin/instance/" + ctx.Instance.Slug + "/data")}), + templating.Title(ctx.Instance.Slug + " - Data"), + admin.instanceTabs(slug, "data"), + }, nil + }) +} diff --git a/internal/dis/component/server/admin/instance_drupal.go b/internal/dis/component/server/admin/instance_drupal.go new file mode 100644 index 0000000..41d7904 --- /dev/null +++ b/internal/dis/component/server/admin/instance_drupal.go @@ -0,0 +1,86 @@ +package admin + +import ( + "context" + _ "embed" + "html/template" + "net/http" + + "github.com/FAU-CDI/wisski-distillery/internal/dis/component" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" + "github.com/FAU-CDI/wisski-distillery/internal/status" + "github.com/FAU-CDI/wisski-distillery/internal/wisski" + "github.com/tkw1536/pkglib/httpx" + + "github.com/julienschmidt/httprouter" +) + +//go:embed "html/instance_drupal.html" +var instanceDrupalHTML []byte +var instanceDrupalTemplate = templating.Parse[instanceDrupalContext]( + "instance_drupal.html", instanceDrupalHTML, nil, + + templating.Assets(assets.AssetsAdmin), +) + +type instanceDrupalContext struct { + templating.RuntimeFlags + + Instance *wisski.WissKI + + DrupalVersion string + DefaultTheme string + + Requirements []status.Requirement +} + +func (admin *Admin) instanceDrupal(ctx context.Context) http.Handler { + tpl := instanceDrupalTemplate.Prepare( + admin.dependencies.Templating, + templating.Crumbs( + menuAdmin, + menuInstances, + menuInstance, + menuDrupal, + ), + ) + + return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ctx instanceDrupalContext, funcs []templating.FlagFunc, err error) { + slug := httprouter.ParamsFromContext(r.Context()).ByName("slug") + + // setup the context with just the instance + ctx.Instance, err = admin.dependencies.Instances.WissKI(r.Context(), slug) + if err != nil { + return ctx, nil, httpx.ErrNotFound + } + + server := ctx.Instance.PHP().NewServer() + defer server.Close() + + // get the requirements + ctx.Requirements, err = ctx.Instance.Requirements().Get(r.Context(), server) + if err != nil { + return ctx, nil, httpx.ErrInternalServerError + } + + // get the drupal version + ctx.DrupalVersion, err = ctx.Instance.Version().Get(r.Context(), server) + if err != nil { + return ctx, nil, httpx.ErrInternalServerError + } + + // get the default theme + ctx.DefaultTheme, err = ctx.Instance.Theme().Get(r.Context(), server) + if err != nil { + return ctx, nil, httpx.ErrInternalServerError + } + + return ctx, []templating.FlagFunc{ + templating.ReplaceCrumb(menuInstance, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + ctx.Instance.Slug)}), + templating.ReplaceCrumb(menuDrupal, component.MenuItem{Title: "Drupal Status", Path: template.URL("/admin/instance/" + ctx.Instance.Slug + "/drupal")}), + templating.Title(ctx.Instance.Slug + " - Drupal Status"), + admin.instanceTabs(slug, "drupal"), + }, nil + }) +} diff --git a/internal/dis/component/server/admin/instance_ssh.go b/internal/dis/component/server/admin/instance_ssh.go new file mode 100644 index 0000000..2fb691d --- /dev/null +++ b/internal/dis/component/server/admin/instance_ssh.go @@ -0,0 +1,80 @@ +package admin + +import ( + "context" + _ "embed" + "html/template" + "net/http" + + "github.com/FAU-CDI/wisski-distillery/internal/dis/component" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" + "github.com/FAU-CDI/wisski-distillery/internal/wisski" + "github.com/tkw1536/pkglib/httpx" + + "github.com/julienschmidt/httprouter" + + gossh "golang.org/x/crypto/ssh" +) + +//go:embed "html/instance_ssh.html" +var instanceSSHHTML []byte +var instanceSSHTemplate = templating.Parse[instanceSSHContext]( + "instance_ssh.html", instanceSSHHTML, nil, + + templating.Assets(assets.AssetsAdmin), +) + +type instanceSSHContext struct { + templating.RuntimeFlags + + Instance *wisski.WissKI + SSHKeys []string + + Hostname string + PanelDomain string + Port uint16 +} + +func (admin *Admin) instanceSSH(ctx context.Context) http.Handler { + tpl := instanceSSHTemplate.Prepare( + admin.dependencies.Templating, + templating.Crumbs( + menuAdmin, + menuInstances, + menuInstance, + menuSSH, + ), + ) + + return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ctx instanceSSHContext, funcs []templating.FlagFunc, err error) { + slug := httprouter.ParamsFromContext(r.Context()).ByName("slug") + + // setup the context with just the instance + ctx.Instance, err = admin.dependencies.Instances.WissKI(r.Context(), slug) + if err != nil { + return ctx, nil, httpx.ErrNotFound + } + + ctx.Hostname = ctx.Instance.Domain() + ctx.PanelDomain = admin.Config.HTTP.PanelDomain() + ctx.Port = admin.Config.Listen.SSHPort + + keys, err := ctx.Instance.SSH().Keys(r.Context()) + if err != nil { + return ctx, nil, httpx.ErrInternalServerError + } + + ctx.SSHKeys = make([]string, len(keys)) + for i, key := range keys { + ctx.SSHKeys[i] = string(gossh.MarshalAuthorizedKey(key)) + } + + return ctx, []templating.FlagFunc{ + templating.ReplaceCrumb(menuInstance, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + ctx.Instance.Slug)}), + templating.ReplaceCrumb(menuSSH, component.MenuItem{Title: "SSH", Path: template.URL("/admin/instance/" + ctx.Instance.Slug + "/ssh")}), + templating.Title(ctx.Instance.Slug + " - SSH"), + admin.instanceTabs(slug, "ssh"), + }, nil + }) +} diff --git a/internal/dis/component/server/admin/instance_stats.go b/internal/dis/component/server/admin/instance_stats.go new file mode 100644 index 0000000..3f75fd4 --- /dev/null +++ b/internal/dis/component/server/admin/instance_stats.go @@ -0,0 +1,67 @@ +package admin + +import ( + "context" + _ "embed" + "html/template" + "net/http" + + "github.com/FAU-CDI/wisski-distillery/internal/dis/component" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" + "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" + "github.com/FAU-CDI/wisski-distillery/internal/status" + "github.com/FAU-CDI/wisski-distillery/internal/wisski" + "github.com/tkw1536/pkglib/httpx" + + "github.com/julienschmidt/httprouter" +) + +//go:embed "html/instance_stats.html" +var instanceStatsHTML []byte +var instanceStatsTemplate = templating.Parse[instanceStatsContext]( + "instance_stats.html", instanceStatsHTML, nil, + + templating.Assets(assets.AssetsAdmin), +) + +type instanceStatsContext struct { + templating.RuntimeFlags + + Instance *wisski.WissKI + Statistics status.Statistics +} + +func (admin *Admin) instanceStats(ctx context.Context) http.Handler { + tpl := instanceStatsTemplate.Prepare( + admin.dependencies.Templating, + templating.Crumbs( + menuAdmin, + menuInstances, + menuInstance, + menuStats, + ), + ) + + return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ctx instanceStatsContext, funcs []templating.FlagFunc, err error) { + slug := httprouter.ParamsFromContext(r.Context()).ByName("slug") + + // setup the context with just the instance + ctx.Instance, err = admin.dependencies.Instances.WissKI(r.Context(), slug) + if err != nil { + return ctx, nil, httpx.ErrNotFound + } + + // read statistics + ctx.Statistics, err = ctx.Instance.Stats().Get(r.Context(), nil) + if err != nil { + return ctx, nil, httpx.ErrInternalServerError + } + + return ctx, []templating.FlagFunc{ + templating.ReplaceCrumb(menuInstance, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + ctx.Instance.Slug)}), + templating.ReplaceCrumb(menuStats, component.MenuItem{Title: "SSH", Path: template.URL("/admin/instance/" + ctx.Instance.Slug + "/stats")}), + templating.Title(ctx.Instance.Slug + " - Stats"), + admin.instanceTabs(slug, "stats"), + }, nil + }) +} diff --git a/internal/wisski/wisski.go b/internal/wisski/wisski.go index c40d77a..f36ac12 100644 --- a/internal/wisski/wisski.go +++ b/internal/wisski/wisski.go @@ -122,6 +122,22 @@ func (wisski *WissKI) Blocks() *extras.Blocks { return export[*extras.Blocks](wisski) } +func (wisski *WissKI) Stats() *extras.Stats { + return export[*extras.Stats](wisski) +} + +func (wisski *WissKI) Requirements() *extras.Requirements { + return export[*extras.Requirements](wisski) +} + +func (wisski *WissKI) Version() *extras.Version { + return export[*extras.Version](wisski) +} + +func (wisski *WissKI) Theme() *extras.Theme { + return export[*extras.Theme](wisski) +} + // // All components // THESE SHOULD NEVER BE CALLED DIRECTLY