1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2025-01-06 10:00:26 +00:00

Working user panel

See https://mycorrhiza.wiki/hypha/idea/user_panel

It's not pretty, but it works. The next step is to make it look good.
This commit is contained in:
handlerug 2021-06-29 22:10:48 +07:00
parent c7e4281398
commit 11e98b2368
No known key found for this signature in database
GPG Key ID: 38009F0605051491
7 changed files with 255 additions and 50 deletions

View File

@ -124,6 +124,10 @@ func readTokensToUsers() {
log.Println("Found", len(tmp), "active sessions")
}
func SaveUserDatabase() error {
return dumpRegistrationCredentials()
}
func dumpRegistrationCredentials() error {
tmp := []*User{}

View File

@ -23,7 +23,7 @@ func FromRequest(rq *http.Request) *User {
if err != nil {
return EmptyUser()
}
return userByToken(cookie.Value)
return UserByToken(cookie.Value)
}
// LogoutFromRequest logs the user in `rq` out and rewrites the cookie in `w`.

View File

@ -51,6 +51,14 @@ var minimalRights = map[string]int{
"admin/shutdown": 4,
}
var groups = []string{
"anon",
"editor",
"trusted",
"moderator",
"admin",
}
// Group — Right
var groupRight = map[string]int{
"anon": 0,
@ -60,6 +68,15 @@ var groupRight = map[string]int{
"admin": 4,
}
func ValidGroup(group string) bool {
for _, grp := range groups {
if grp == group {
return true
}
}
return false
}
func EmptyUser() *User {
return &User{
Name: "anon",

View File

@ -56,18 +56,18 @@ func HasUsername(username string) bool {
}
func CredentialsOK(username, password string) bool {
return userByName(username).isCorrectPassword(password)
return UserByName(username).isCorrectPassword(password)
}
func userByToken(token string) *User {
func UserByToken(token string) *User {
if usernameUntyped, ok := tokens.Load(token); ok {
username := usernameUntyped.(string)
return userByName(username)
return UserByName(username)
}
return EmptyUser()
}
func userByName(username string) *User {
func UserByName(username string) *User {
if userUntyped, ok := users.Load(username); ok {
user := userUntyped.(*User)
return user

View File

@ -5,7 +5,7 @@
<main class="main-width">
<h1>Manage users</h1>
<form action="/admin/reindex-users" method="get">
<form action="/admin/reindex-users" method="post">
<button class="btn btn_accent" type="submit">Reindex users</button>
</form>
@ -17,14 +17,18 @@
<th>Name</th>
<th>Group</th>
<th>Registered at</th>
<th></th>
</tr>
</thead>
<tbody>
{% for _, u := range userList %}
<tr>
<td>{%s= u.Name %}</td>
<td>{%s= u.Group %}</td>
<td>{%s= u.RegisteredAt.Format("2006-01-02 15:04:05-0700") %}</td>
<td>{%s u.Name %}</td>
<td>{%s u.Group %}</td>
<td>{%s u.RegisteredAt.Format("2006-01-02 15:04:05-0700") %}</td>
<td>
<a href="/admin/users/{%u u.Name %}/edit">Edit</a>
</td>
</tr>
{% endfor %}
</tbody>
@ -32,3 +36,26 @@
</main>
</div>
{% endfunc %}
{% func AdminUsersUserHTML(u *user.User) %}
<div class="layout">
<main class="main-width">
<h1>{%s u.Name %}</h1>
<form action="" method="post">
<label for="group">Group:</label>
<select id="group" name="group">
<option{% if u.Group == "anon" %} selected{% endif %}>anon</option>
<option{% if u.Group == "editor" %} selected{% endif %}>editor</option>
<option{% if u.Group == "trusted" %} selected{% endif %}>trusted</option>
<option{% if u.Group == "moderator" %} selected{% endif %}>moderator</option>
<option{% if u.Group == "admin" %} selected{% endif %}>admin</option>
</select>
<br>
<br>
<button class="btn btn_accent" type="submit">Update</button>
</form>
</main>
</div>
{% endfunc %}

View File

@ -28,7 +28,7 @@ func StreamAdminUsersPanelHTML(qw422016 *qt422016.Writer, userList []*user.User)
<main class="main-width">
<h1>Manage users</h1>
<form action="/admin/reindex-users" method="get">
<form action="/admin/reindex-users" method="post">
<button class="btn btn_accent" type="submit">Reindex users</button>
</form>
@ -40,66 +40,172 @@ func StreamAdminUsersPanelHTML(qw422016 *qt422016.Writer, userList []*user.User)
<th>Name</th>
<th>Group</th>
<th>Registered at</th>
<th></th>
</tr>
</thead>
<tbody>
`)
//line views/admin.qtpl:23
//line views/admin.qtpl:24
for _, u := range userList {
//line views/admin.qtpl:23
//line views/admin.qtpl:24
qw422016.N().S(`
<tr>
<td>`)
//line views/admin.qtpl:25
qw422016.N().S(u.Name)
//line views/admin.qtpl:25
qw422016.N().S(`</td>
<td>`)
//line views/admin.qtpl:26
qw422016.N().S(u.Group)
qw422016.E().S(u.Name)
//line views/admin.qtpl:26
qw422016.N().S(`</td>
<td>`)
//line views/admin.qtpl:27
qw422016.N().S(u.RegisteredAt.Format("2006-01-02 15:04:05-0700"))
qw422016.E().S(u.Group)
//line views/admin.qtpl:27
qw422016.N().S(`</td>
<td>`)
//line views/admin.qtpl:28
qw422016.E().S(u.RegisteredAt.Format("2006-01-02 15:04:05-0700"))
//line views/admin.qtpl:28
qw422016.N().S(`</td>
<td>
<a href="/admin/users/`)
//line views/admin.qtpl:30
qw422016.N().U(u.Name)
//line views/admin.qtpl:30
qw422016.N().S(`/edit">Edit</a>
</td>
</tr>
`)
//line views/admin.qtpl:29
//line views/admin.qtpl:33
}
//line views/admin.qtpl:29
//line views/admin.qtpl:33
qw422016.N().S(`
</tbody>
</table>
</main>
</div>
`)
//line views/admin.qtpl:34
//line views/admin.qtpl:38
}
//line views/admin.qtpl:34
//line views/admin.qtpl:38
func WriteAdminUsersPanelHTML(qq422016 qtio422016.Writer, userList []*user.User) {
//line views/admin.qtpl:34
//line views/admin.qtpl:38
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/admin.qtpl:34
//line views/admin.qtpl:38
StreamAdminUsersPanelHTML(qw422016, userList)
//line views/admin.qtpl:34
//line views/admin.qtpl:38
qt422016.ReleaseWriter(qw422016)
//line views/admin.qtpl:34
//line views/admin.qtpl:38
}
//line views/admin.qtpl:34
//line views/admin.qtpl:38
func AdminUsersPanelHTML(userList []*user.User) string {
//line views/admin.qtpl:34
//line views/admin.qtpl:38
qb422016 := qt422016.AcquireByteBuffer()
//line views/admin.qtpl:34
//line views/admin.qtpl:38
WriteAdminUsersPanelHTML(qb422016, userList)
//line views/admin.qtpl:34
//line views/admin.qtpl:38
qs422016 := string(qb422016.B)
//line views/admin.qtpl:34
//line views/admin.qtpl:38
qt422016.ReleaseByteBuffer(qb422016)
//line views/admin.qtpl:34
//line views/admin.qtpl:38
return qs422016
//line views/admin.qtpl:34
//line views/admin.qtpl:38
}
//line views/admin.qtpl:40
func StreamAdminUsersUserHTML(qw422016 *qt422016.Writer, u *user.User) {
//line views/admin.qtpl:40
qw422016.N().S(`
<div class="layout">
<main class="main-width">
<h1>`)
//line views/admin.qtpl:43
qw422016.E().S(u.Name)
//line views/admin.qtpl:43
qw422016.N().S(`</h1>
<form action="" method="post">
<label for="group">Group:</label>
<select id="group" name="group">
<option`)
//line views/admin.qtpl:48
if u.Group == "anon" {
//line views/admin.qtpl:48
qw422016.N().S(` selected`)
//line views/admin.qtpl:48
}
//line views/admin.qtpl:48
qw422016.N().S(`>anon</option>
<option`)
//line views/admin.qtpl:49
if u.Group == "editor" {
//line views/admin.qtpl:49
qw422016.N().S(` selected`)
//line views/admin.qtpl:49
}
//line views/admin.qtpl:49
qw422016.N().S(`>editor</option>
<option`)
//line views/admin.qtpl:50
if u.Group == "trusted" {
//line views/admin.qtpl:50
qw422016.N().S(` selected`)
//line views/admin.qtpl:50
}
//line views/admin.qtpl:50
qw422016.N().S(`>trusted</option>
<option`)
//line views/admin.qtpl:51
if u.Group == "moderator" {
//line views/admin.qtpl:51
qw422016.N().S(` selected`)
//line views/admin.qtpl:51
}
//line views/admin.qtpl:51
qw422016.N().S(`>moderator</option>
<option`)
//line views/admin.qtpl:52
if u.Group == "admin" {
//line views/admin.qtpl:52
qw422016.N().S(` selected`)
//line views/admin.qtpl:52
}
//line views/admin.qtpl:52
qw422016.N().S(`>admin</option>
</select>
<br>
<br>
<button class="btn btn_accent" type="submit">Update</button>
</form>
</main>
</div>
`)
//line views/admin.qtpl:61
}
//line views/admin.qtpl:61
func WriteAdminUsersUserHTML(qq422016 qtio422016.Writer, u *user.User) {
//line views/admin.qtpl:61
qw422016 := qt422016.AcquireWriter(qq422016)
//line views/admin.qtpl:61
StreamAdminUsersUserHTML(qw422016, u)
//line views/admin.qtpl:61
qt422016.ReleaseWriter(qw422016)
//line views/admin.qtpl:61
}
//line views/admin.qtpl:61
func AdminUsersUserHTML(u *user.User) string {
//line views/admin.qtpl:61
qb422016 := qt422016.AcquireByteBuffer()
//line views/admin.qtpl:61
WriteAdminUsersUserHTML(qb422016, u)
//line views/admin.qtpl:61
qs422016 := string(qb422016.B)
//line views/admin.qtpl:61
qt422016.ReleaseByteBuffer(qb422016)
//line views/admin.qtpl:61
return qs422016
//line views/admin.qtpl:61
}

View File

@ -1,10 +1,13 @@
package web
import (
"fmt"
"mime"
"io"
"log"
"net/http"
"sort"
"strings"
"github.com/bouncepaw/mycorrhiza/cfg"
"github.com/bouncepaw/mycorrhiza/user"
@ -19,7 +22,7 @@ func initAdmin() {
http.HandleFunc("/admin/shutdown", handlerAdminShutdown)
http.HandleFunc("/admin/reindex-users", handlerAdminReindexUsers)
http.HandleFunc("/admin/users", handlerAdminUsers)
http.HandleFunc("/admin/users/", handlerAdminUsers)
}
}
@ -49,31 +52,79 @@ func handlerAdminReindexUsers(w http.ResponseWriter, rq *http.Request) {
util.PrepareRq(rq)
if user.CanProceed(rq, "admin") && rq.Method == "POST" {
user.ReadUsersFromFilesystem()
http.Redirect(w, rq, "/hypha/"+cfg.UserHypha, http.StatusSeeOther)
redirectTo := rq.Referer()
if redirectTo == "" {
redirectTo = "/hypha/"+cfg.UserHypha
}
http.Redirect(w, rq, redirectTo, http.StatusSeeOther)
}
}
func handlerAdminUsers(w http.ResponseWriter, r *http.Request) {
util.PrepareRq(r)
if user.CanProceed(r, "admin") {
// Get a sorted list of users
var userList []*user.User
for u := range user.YieldUsers() {
userList = append(userList, u)
path := strings.TrimPrefix(r.URL.Path, "/admin/users")
parts := strings.Split(path, "/")[1:]
// Users dashboard
if len(parts) == 0 {
// Get a sorted list of users
var userList []*user.User
for u := range user.YieldUsers() {
userList = append(userList, u)
}
sort.Slice(userList, func(i, j int) bool {
less := userList[i].RegisteredAt.Before(userList[j].RegisteredAt)
return less
})
html := views.AdminUsersPanelHTML(userList)
html = views.BaseHTML("Manage users", html, user.FromRequest(r))
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
if _, err := io.WriteString(w, html); err != nil {
log.Println(err)
}
return
}
sort.Slice(userList, func(i, j int) bool {
less := userList[i].RegisteredAt.Before(userList[j].RegisteredAt)
return less
})
// User edit page
if len(parts) == 2 && parts[1] == "edit" {
u := user.UserByName(parts[0])
html := views.AdminUsersPanelHTML(userList)
html = views.BaseHTML("Manage users", html, user.FromRequest(r))
if u != nil && u.Name != "anon" {
if r.Method == http.MethodGet {
html := views.AdminUsersUserHTML(u)
html = views.BaseHTML(fmt.Sprintf("User %s", u.Name), html, user.FromRequest(r))
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, err := io.WriteString(w, html)
if err != nil {
log.Println(err)
w.Header().Set("Content-Type", mime.TypeByExtension(".html"))
if _, err := io.WriteString(w, html); err != nil {
log.Println(err)
}
return
} else if r.Method == http.MethodPost {
oldGroup := u.Group
newGroup := r.PostFormValue("group")
if user.ValidGroup(newGroup) {
u.Group = newGroup
if err := user.SaveUserDatabase(); err != nil {
u.Group = oldGroup
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
io.WriteString(w, err.Error())
} else {
http.Redirect(w, r, "/admin/users/", http.StatusSeeOther)
}
} else {
w.WriteHeader(http.StatusBadRequest)
io.WriteString(w, "invalid group")
}
return
}
}
}
util.HTTP404Page(w, "404 page not found")
}
}