Begin rework of the instances page
This commit is contained in:
parent
207e23778b
commit
a2ad685b61
6 changed files with 160 additions and 131 deletions
|
|
@ -124,13 +124,13 @@ func (admin *Admin) HandleRoute(ctx context.Context, route string) (handler http
|
|||
|
||||
{
|
||||
rebuild := admin.instanceRebuild(ctx)
|
||||
router.Handler(http.MethodGet, route+"rebuild/:slug", rebuild)
|
||||
router.Handler(http.MethodGet, route+"instance/:slug/rebuild", rebuild)
|
||||
}
|
||||
|
||||
{
|
||||
grants := admin.grants(ctx)
|
||||
router.Handler(http.MethodGet, route+"grants/:slug", grants)
|
||||
router.Handler(http.MethodPost, route+"grants/", grants) // NOTE(twiesing): This path is intentionally different!
|
||||
iUsers := admin.instanceUsers(ctx)
|
||||
router.Handler(http.MethodGet, route+"instance/:slug/users", iUsers)
|
||||
router.Handler(http.MethodPost, route+"grants/", iUsers) // NOTE(twiesing): This path is intentionally different!
|
||||
}
|
||||
|
||||
// add a router for the login page
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
<div class="pure-button-group" role="group" aria-label="Table Of Contents">
|
||||
<a class="pure-button pure-button-small" href="#overview">Info & Status</a>
|
||||
<a class="pure-button pure-button-small" href="#requirements">Drupal Status Report</a>
|
||||
<a class="pure-button pure-button-small" href="#users">Users (Drupal)</a>
|
||||
<a class="pure-button pure-button-small" href="#wisski">WissKI Data</a>
|
||||
<a class="pure-button pure-button-small" href="#stats">Statistics</a>
|
||||
<a class="pure-button pure-button-small" href="#ssh">SSH Keys</a>
|
||||
|
|
@ -346,93 +345,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-u-1-1">
|
||||
<h2 id="users">Users (Drupal)</h2>
|
||||
<a class="pure-button" href="/admin/grants/{{ .Info.Slug }}">Manage Grants</a>
|
||||
</div>
|
||||
|
||||
<div class="pure-u-1">
|
||||
<div class="padding">
|
||||
<div class="overflow">
|
||||
<table class="pure-table pure-table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
ID
|
||||
</th>
|
||||
<th>
|
||||
Active
|
||||
</th>
|
||||
<th>
|
||||
Name
|
||||
</th>
|
||||
|
||||
<th>
|
||||
Email
|
||||
</th>
|
||||
<th>
|
||||
Roles
|
||||
</th>
|
||||
<th>
|
||||
Created
|
||||
</th>
|
||||
<th>
|
||||
Last Login
|
||||
</th>
|
||||
<th>
|
||||
Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ $slug := .Instance.Slug }}
|
||||
{{ $csrf := .CSRF }}
|
||||
{{ range $index, $user := .Info.Users }}
|
||||
<tr {{ if not $user.Status }}class="disabled" aria-disabled="true"{{ end }}>
|
||||
<td>
|
||||
<code>{{ $user.UID }}</code>
|
||||
</td>
|
||||
<td>
|
||||
<code>{{ $user.Status }}</code>
|
||||
</td>
|
||||
<td>
|
||||
<code>{{ $user.Name }}</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{{ if $user.Mail }}
|
||||
<a href="mailto:{{ $user.Mail }}">{{ $user.Mail }}</a>
|
||||
{{ end }}
|
||||
</td>
|
||||
<td>
|
||||
{{ range $role, $unuused := $user.Roles }}
|
||||
<code>
|
||||
{{ $role }}
|
||||
</code>
|
||||
{{ end }}
|
||||
</td>
|
||||
<td>
|
||||
<code class="date">{{ $user.Created.Time.Format "2006-01-02T15:04:05Z07:00" }}</code>
|
||||
</td>
|
||||
<td>
|
||||
<code class="date">{{ $user.Login.Time.Format "2006-01-02T15:04:05Z07:00" }}</code>
|
||||
</td>
|
||||
<td>
|
||||
<form action="/admin/login" method="POST" target="_blank">
|
||||
<input type="hidden" name="slug" value="{{ $slug }}">
|
||||
<input type="hidden" name="user" value="{{ $user.Name }}">
|
||||
<input type="submit" class="pure-button pure-button-action" value="Login in new window">
|
||||
{{ $csrf }}
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-u-1-1">
|
||||
<h2 id="wisski">WissKI Data</h2>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,102 @@
|
|||
{{ $csrf := .CSRF }}
|
||||
{{ $slug := .Instance.Slug }}
|
||||
|
||||
|
||||
<div class="pure-u-1-1">
|
||||
<h2 id="users">Users</h2>
|
||||
|
||||
<p>
|
||||
This page provides a list of users found in this Drupal instance.
|
||||
You can click the login button to authenticate as that user.
|
||||
<br />
|
||||
Please be aware that these never end an already existing session.
|
||||
When already logged into drupal, an error message <em>Access denied</em> may appear.
|
||||
To prevent this, log out of the drupal instance before clicking the button.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="pure-u-1">
|
||||
<div class="padding">
|
||||
<div class="overflow">
|
||||
<table class="pure-table pure-table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
ID
|
||||
</th>
|
||||
<th>
|
||||
Active
|
||||
</th>
|
||||
<th>
|
||||
Name
|
||||
</th>
|
||||
|
||||
<th>
|
||||
Email
|
||||
</th>
|
||||
<th>
|
||||
Roles
|
||||
</th>
|
||||
<th>
|
||||
Created
|
||||
</th>
|
||||
<th>
|
||||
Last Login
|
||||
</th>
|
||||
<th>
|
||||
Action
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ $slug := .Instance.Slug }}
|
||||
{{ $csrf := .CSRF }}
|
||||
{{ range $index, $user := .Users }}
|
||||
<tr {{ if not $user.Status }}class="disabled" aria-disabled="true"{{ end }}>
|
||||
<td>
|
||||
<code>{{ $user.UID }}</code>
|
||||
</td>
|
||||
<td>
|
||||
<code>{{ $user.Status }}</code>
|
||||
</td>
|
||||
<td>
|
||||
<code>{{ $user.Name }}</code>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
{{ if $user.Mail }}
|
||||
<a href="mailto:{{ $user.Mail }}">{{ $user.Mail }}</a>
|
||||
{{ end }}
|
||||
</td>
|
||||
<td>
|
||||
{{ range $role, $unuused := $user.Roles }}
|
||||
<code>
|
||||
{{ $role }}
|
||||
</code>
|
||||
{{ end }}
|
||||
</td>
|
||||
<td>
|
||||
<code class="date">{{ $user.Created.Time.Format "2006-01-02T15:04:05Z07:00" }}</code>
|
||||
</td>
|
||||
<td>
|
||||
<code class="date">{{ $user.Login.Time.Format "2006-01-02T15:04:05Z07:00" }}</code>
|
||||
</td>
|
||||
<td>
|
||||
<form action="/admin/login" method="POST" target="_blank">
|
||||
<input type="hidden" name="slug" value="{{ $slug }}">
|
||||
<input type="hidden" name="user" value="{{ $user.Name }}">
|
||||
<input type="submit" class="pure-button pure-button-action" value="Login in new window">
|
||||
{{ $csrf }}
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pure-u-1-1">
|
||||
<h2 id="overview">Grants</h2>
|
||||
|
||||
|
|
@ -116,7 +213,7 @@
|
|||
{{ end }}
|
||||
</datalist>
|
||||
<datalist id="drupal-users">
|
||||
{{ range $unused, $drupal := .Drupals }}
|
||||
<option value="{{ $drupal }}">
|
||||
{{ range $unused, $user := .Users }}
|
||||
<option value="{{ $user.Name }}">
|
||||
{{ end }}
|
||||
</datalist>
|
||||
|
|
@ -40,10 +40,6 @@ func (admin *Admin) instance(ctx context.Context) http.Handler {
|
|||
menuInstances,
|
||||
menuInstance,
|
||||
),
|
||||
templating.Actions(
|
||||
menuRebuild,
|
||||
menuGrants,
|
||||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (ic instanceContext, funcs []templating.FlagFunc, err error) {
|
||||
|
|
@ -67,12 +63,36 @@ func (admin *Admin) instance(ctx context.Context) http.Handler {
|
|||
|
||||
funcs = []templating.FlagFunc{
|
||||
templating.ReplaceCrumb(menuInstance, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + slug)}),
|
||||
templating.ReplaceAction(menuRebuild, component.MenuItem{Title: "Rebuild", Path: template.URL("/admin/rebuild/" + slug)}),
|
||||
templating.ReplaceAction(menuGrants, component.MenuItem{Title: "Grants", Path: template.URL("/admin/grants/" + slug)}),
|
||||
|
||||
templating.Title(instance.Slug),
|
||||
|
||||
admin.instanceTabs(slug, "overview"),
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
// instanceTabs
|
||||
func (admin *Admin) instanceTabs(slug string, active string) templating.FlagFunc {
|
||||
return func(flags templating.Flags, r *http.Request) templating.Flags {
|
||||
flags.Tabs = []component.MenuItem{
|
||||
{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"},
|
||||
|
||||
// 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"},
|
||||
{Title: "Snapshots", Path: template.URL("/instance/" + slug + "/snapshots"), Active: active == "snapshots"},
|
||||
{Title: "Danger Zone", Path: template.URL("/instance/" + slug + "/danger"), Active: active == "danger"},
|
||||
*/
|
||||
}
|
||||
return flags
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,8 +83,9 @@ func (admin *Admin) instanceRebuild(ctx context.Context) http.Handler {
|
|||
// replace the menu item
|
||||
funcs = []templating.FlagFunc{
|
||||
templating.ReplaceCrumb(menuInstance, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + instance.Slug)}),
|
||||
templating.ReplaceCrumb(menuRebuild, component.MenuItem{Title: "Rebuild", Path: template.URL("/admin/rebuild/" + instance.Slug)}),
|
||||
templating.ReplaceCrumb(menuRebuild, component.MenuItem{Title: "Rebuild", Path: template.URL("/admin/instance/" + instance.Slug + "/rebuild")}),
|
||||
templating.Title(instance.Slug + " - Rebuild"),
|
||||
admin.instanceTabs(slug, "rebuild"),
|
||||
}
|
||||
|
||||
isc.prepare(true)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"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/models"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/status"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
|
||||
"github.com/tkw1536/pkglib/httpx"
|
||||
"github.com/tkw1536/pkglib/httpx/field"
|
||||
|
|
@ -21,15 +22,15 @@ import (
|
|||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
//go:embed "html/grants.html"
|
||||
var grantsHTML []byte
|
||||
var grantsTemplate = templating.Parse[grantsContext](
|
||||
"grants.html", grantsHTML, nil,
|
||||
//go:embed "html/instance_users.html"
|
||||
var instanceUsersHTML []byte
|
||||
var instanceUsersTemplate = templating.Parse[instanceUsersContext](
|
||||
"instance_users.html", instanceUsersHTML, nil,
|
||||
|
||||
templating.Assets(assets.AssetsAdmin),
|
||||
)
|
||||
|
||||
type grantsContext struct {
|
||||
type instanceUsersContext struct {
|
||||
templating.RuntimeFlags
|
||||
|
||||
Error string
|
||||
|
|
@ -37,13 +38,14 @@ type grantsContext struct {
|
|||
instance *wisski.WissKI
|
||||
Instance models.Instance // current instance
|
||||
|
||||
Grants []models.Grant // grants that exist for the user
|
||||
Users []status.DrupalUser // drupal users
|
||||
|
||||
Usernames []string // unuused distillery usernames
|
||||
Drupals []string // unusued drupal usernames
|
||||
Grants []models.Grant // grants that exist for the user
|
||||
}
|
||||
|
||||
func (admin *Admin) grants(ctx context.Context) http.Handler {
|
||||
tpl := grantsTemplate.Prepare(
|
||||
func (admin *Admin) instanceUsers(ctx context.Context) http.Handler {
|
||||
tpl := instanceUsersTemplate.Prepare(
|
||||
admin.dependencies.Templating,
|
||||
templating.Crumbs(
|
||||
menuAdmin,
|
||||
|
|
@ -53,16 +55,16 @@ func (admin *Admin) grants(ctx context.Context) http.Handler {
|
|||
),
|
||||
)
|
||||
|
||||
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (grantsContext, []templating.FlagFunc, error) {
|
||||
return tpl.HTMLHandlerWithFlags(func(r *http.Request) (instanceUsersContext, []templating.FlagFunc, error) {
|
||||
if r.Method == http.MethodGet {
|
||||
return admin.getGrants(r)
|
||||
return admin.getGrantsUsers(r)
|
||||
} else {
|
||||
return admin.postGrants(r)
|
||||
return admin.postInstanceUsers(r)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (admin *Admin) getGrants(r *http.Request) (gc grantsContext, funcs []templating.FlagFunc, err error) {
|
||||
func (admin *Admin) getGrantsUsers(r *http.Request) (gc instanceUsersContext, funcs []templating.FlagFunc, err error) {
|
||||
slug := httprouter.ParamsFromContext(r.Context()).ByName("slug")
|
||||
|
||||
funcs, err = gc.use(r, slug, admin)
|
||||
|
|
@ -70,14 +72,14 @@ func (admin *Admin) getGrants(r *http.Request) (gc grantsContext, funcs []templa
|
|||
return gc, nil, err
|
||||
}
|
||||
|
||||
if err := gc.useGrants(r, admin); err != nil {
|
||||
if err := gc.useUsers(r, admin); err != nil {
|
||||
return gc, nil, err
|
||||
}
|
||||
|
||||
return gc, funcs, nil
|
||||
}
|
||||
|
||||
func (admin *Admin) postGrants(r *http.Request) (gc grantsContext, funcs []templating.FlagFunc, err error) {
|
||||
func (admin *Admin) postInstanceUsers(r *http.Request) (gc instanceUsersContext, funcs []templating.FlagFunc, err error) {
|
||||
// parse the form
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return gc, nil, err
|
||||
|
|
@ -119,13 +121,13 @@ func (admin *Admin) postGrants(r *http.Request) (gc grantsContext, funcs []templ
|
|||
}
|
||||
|
||||
// fetch the grants for the instance
|
||||
if err := gc.useGrants(r, admin); err != nil {
|
||||
if err := gc.useUsers(r, admin); err != nil {
|
||||
return gc, nil, err
|
||||
}
|
||||
return gc, funcs, nil
|
||||
}
|
||||
|
||||
func (gc *grantsContext) use(r *http.Request, slug string, admin *Admin) (funcs []templating.FlagFunc, err error) {
|
||||
func (gc *instanceUsersContext) use(r *http.Request, slug string, admin *Admin) (funcs []templating.FlagFunc, err error) {
|
||||
// find the instance itself
|
||||
gc.instance, err = admin.dependencies.Instances.WissKI(r.Context(), slug)
|
||||
if err == instances.ErrWissKINotFound {
|
||||
|
|
@ -139,13 +141,14 @@ func (gc *grantsContext) use(r *http.Request, slug string, admin *Admin) (funcs
|
|||
// replace the functions
|
||||
funcs = []templating.FlagFunc{
|
||||
templating.ReplaceCrumb(menuInstance, component.MenuItem{Title: "Instance", Path: template.URL("/admin/instance/" + slug)}),
|
||||
templating.ReplaceCrumb(menuGrants, component.MenuItem{Title: "Grants", Path: template.URL("/admin/grants/" + slug)}),
|
||||
templating.Title(gc.Instance.Slug + " - Grants"),
|
||||
templating.ReplaceCrumb(menuGrants, component.MenuItem{Title: "Users & Grants", Path: template.URL("/admin/instance/" + slug + "/users")}),
|
||||
templating.Title(gc.Instance.Slug + " - Users & Grants"),
|
||||
admin.instanceTabs(slug, "users"),
|
||||
}
|
||||
return funcs, nil
|
||||
}
|
||||
|
||||
func (gc *grantsContext) useGrants(r *http.Request, admin *Admin) (err error) {
|
||||
func (gc *instanceUsersContext) useUsers(r *http.Request, admin *Admin) (err error) {
|
||||
gc.Grants, err = admin.dependencies.Policy.Instance(r.Context(), gc.Instance.Slug)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -169,18 +172,14 @@ func (gc *grantsContext) useGrants(r *http.Request, admin *Admin) (err error) {
|
|||
gc.Usernames = maps.Keys(userNameMap)
|
||||
slices.Sort(gc.Usernames)
|
||||
|
||||
// get the drupal usernames
|
||||
drupals, err := gc.instance.Users().All(r.Context(), nil)
|
||||
// get the drupal user data
|
||||
gc.Users, err = gc.instance.Users().All(r.Context(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// and convert them to strings only
|
||||
gc.Drupals = make([]string, len(drupals))
|
||||
for i, drupal := range drupals {
|
||||
gc.Drupals[i] = string(drupal.Name)
|
||||
}
|
||||
slices.Sort(gc.Drupals)
|
||||
slices.SortFunc(gc.Users, func(a, b status.DrupalUser) int {
|
||||
return int(b.UID) - int(a.UID)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue