176 lines
5.1 KiB
Go
176 lines
5.1 KiB
Go
// SPDX-License-Identifier: MIT
|
|
|
|
package admin
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/vcraescu/go-paginator/v2"
|
|
"github.com/vcraescu/go-paginator/v2/adapter"
|
|
"github.com/vcraescu/go-paginator/v2/view"
|
|
"go.mindeco.de/http/render"
|
|
|
|
"github.com/ssb-ngi-pointer/go-ssb-room/admindb"
|
|
"github.com/ssb-ngi-pointer/go-ssb-room/roomstate"
|
|
)
|
|
|
|
var HTMLTemplates = []string{
|
|
"admin/dashboard.tmpl",
|
|
"admin/menu.tmpl",
|
|
|
|
"admin/allow-list.tmpl",
|
|
"admin/allow-list-remove-confirm.tmpl",
|
|
|
|
"admin/invite-list.tmpl",
|
|
"admin/invite-revoke-confirm.tmpl",
|
|
"admin/invite-created.tmpl",
|
|
|
|
"admin/notice-edit.tmpl",
|
|
}
|
|
|
|
// Databases is an option struct that encapsualtes the required database services
|
|
type Databases struct {
|
|
AllowList admindb.AllowListService
|
|
Invites admindb.InviteService
|
|
Notices admindb.NoticesService
|
|
PinnedNotices admindb.PinnedNoticesService
|
|
}
|
|
|
|
// Handler supplies the elevated access pages to known users.
|
|
// It is not registering on the mux router like other pages to clean up the authorize flow.
|
|
func Handler(
|
|
domainName string,
|
|
r *render.Renderer,
|
|
roomState *roomstate.Manager,
|
|
dbs Databases,
|
|
) http.Handler {
|
|
mux := &http.ServeMux{}
|
|
// TODO: configure 404 handler
|
|
|
|
mux.HandleFunc("/dashboard", r.HTML("admin/dashboard.tmpl", func(rw http.ResponseWriter, req *http.Request) (interface{}, error) {
|
|
lst := roomState.List()
|
|
return struct {
|
|
Clients []string
|
|
Count int
|
|
}{lst, len(lst)}, nil
|
|
}))
|
|
mux.HandleFunc("/menu", r.HTML("admin/menu.tmpl", func(w http.ResponseWriter, req *http.Request) (interface{}, error) {
|
|
return map[string]interface{}{}, nil
|
|
}))
|
|
|
|
var ah = allowListHandler{
|
|
r: r,
|
|
al: dbs.AllowList,
|
|
}
|
|
mux.HandleFunc("/members", r.HTML("admin/allow-list.tmpl", ah.overview))
|
|
mux.HandleFunc("/members/add", ah.add)
|
|
mux.HandleFunc("/members/remove/confirm", r.HTML("admin/allow-list-remove-confirm.tmpl", ah.removeConfirm))
|
|
mux.HandleFunc("/members/remove", ah.remove)
|
|
|
|
var ih = invitesHandler{
|
|
r: r,
|
|
db: dbs.Invites,
|
|
|
|
domainName: domainName,
|
|
}
|
|
mux.HandleFunc("/invites", r.HTML("admin/invite-list.tmpl", ih.overview))
|
|
mux.HandleFunc("/invites/create", r.HTML("admin/invite-created.tmpl", ih.create))
|
|
mux.HandleFunc("/invites/revoke/confirm", r.HTML("admin/invite-revoke-confirm.tmpl", ih.revokeConfirm))
|
|
mux.HandleFunc("/invites/revoke", ih.revoke)
|
|
|
|
var nh = noticeHandler{
|
|
r: r,
|
|
noticeDB: dbs.Notices,
|
|
pinnedDB: dbs.PinnedNotices,
|
|
}
|
|
mux.HandleFunc("/notice/edit", r.HTML("admin/notice-edit.tmpl", nh.edit))
|
|
mux.HandleFunc("/notice/translation/draft", r.HTML("admin/notice-edit.tmpl", nh.draftTranslation))
|
|
mux.HandleFunc("/notice/translation/add", nh.addTranslation)
|
|
mux.HandleFunc("/notice/save", nh.save)
|
|
|
|
return customStripPrefix("/admin", mux)
|
|
}
|
|
|
|
// how many elements does a paginated page have by default
|
|
const defaultPageSize = 20
|
|
|
|
// paginate receives the total slice and it's length/count, a URL query for the 'limit' and which 'page'.
|
|
//
|
|
// The members of the map are:
|
|
// Entries: the paginated slice
|
|
// Count: the total number of the whole, unpaginated list
|
|
// FirstInView: a bool thats true if you render the first page
|
|
// LastInView: a bool thats true if you render the last page
|
|
// Paginator and View: helpers for rendering the page accessor (see github.com/vcraescu/go-paginator)
|
|
//
|
|
// TODO: we could return a struct instead but then need to re-think how we embedd it into all the pages where we need it.
|
|
// Maybe renderData["Pages"] = paginatedData
|
|
func paginate(total interface{}, count int, qry url.Values) (map[string]interface{}, error) {
|
|
pageSize, err := strconv.Atoi(qry.Get("limit"))
|
|
if err != nil {
|
|
pageSize = defaultPageSize
|
|
}
|
|
|
|
page, err := strconv.Atoi(qry.Get("page"))
|
|
if err != nil || page < 1 {
|
|
page = 1
|
|
}
|
|
|
|
paginator := paginator.New(adapter.NewSliceAdapter(total), pageSize)
|
|
paginator.SetPage(page)
|
|
|
|
var entries []interface{}
|
|
if err = paginator.Results(&entries); err != nil {
|
|
return nil, fmt.Errorf("paginator failed with %w", err)
|
|
}
|
|
|
|
view := view.New(paginator)
|
|
pagesSlice, err := view.Pages()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("paginator view.Pages failed with %w", err)
|
|
}
|
|
if len(pagesSlice) == 0 {
|
|
pagesSlice = []int{1}
|
|
}
|
|
|
|
last, err := view.Last()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("paginator view.Last failed with %w", err)
|
|
}
|
|
|
|
return map[string]interface{}{
|
|
"Entries": entries,
|
|
"Count": count,
|
|
"Paginator": paginator,
|
|
"View": view,
|
|
"FirstInView": pagesSlice[0] == 1,
|
|
"LastInView": pagesSlice[len(pagesSlice)-1] == last,
|
|
}, nil
|
|
}
|
|
|
|
// trim prefix if exists (workaround for named router problem)
|
|
func customStripPrefix(prefix string, h http.Handler) http.Handler {
|
|
if prefix == "" {
|
|
return h
|
|
}
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
p := strings.TrimPrefix(r.URL.Path, prefix)
|
|
rp := strings.TrimPrefix(r.URL.RawPath, prefix)
|
|
if len(p) < len(r.URL.Path) && (r.URL.RawPath == "" || len(rp) < len(r.URL.RawPath)) {
|
|
r2 := new(http.Request)
|
|
*r2 = *r
|
|
r2.URL = new(url.URL)
|
|
*r2.URL = *r.URL
|
|
r2.URL.Path = p
|
|
r2.URL.RawPath = rp
|
|
h.ServeHTTP(w, r2)
|
|
} else {
|
|
h.ServeHTTP(w, r)
|
|
}
|
|
})
|
|
}
|