Add auth-managed proxy UI improvements

This commit is contained in:
2026-04-03 20:58:54 -04:00
parent 7ed709ad3d
commit f2a246ce6b
9 changed files with 1384 additions and 82 deletions

View File

@ -1,15 +1,24 @@
package noodle
import (
"errors"
"fmt"
"log"
"sync"
"time"
"github.com/google/uuid"
"go.mills.io/bitcask/v2"
)
var ErrUserNotFound = errors.New("user not found")
var ErrLastAdminRemoval = errors.New("cannot remove the last admin")
type Database struct {
connection *bitcask.Bitcask
Handle *bitcask.Collection
Users *bitcask.Collection
mu sync.Mutex
}
func NewDatabase(path string) *Database {
@ -20,6 +29,7 @@ func NewDatabase(path string) *Database {
return &Database{
connection: db,
Handle: db.Collection("noodles"),
Users: db.Collection("users"),
}
}
@ -33,6 +43,9 @@ func (db *Database) MakeID() string {
}
func (db *Database) GetAll() []Noodle {
db.mu.Lock()
defer db.mu.Unlock()
var data []Noodle
if err := db.Handle.List(&data); err != nil {
log.Print(err)
@ -42,6 +55,9 @@ func (db *Database) GetAll() []Noodle {
}
func (db *Database) GetAllGeneric() []interface{} {
db.mu.Lock()
defer db.mu.Unlock()
var data []interface{}
if err := db.Handle.List(&data); err != nil {
log.Print(err)
@ -51,6 +67,9 @@ func (db *Database) GetAllGeneric() []interface{} {
}
func (db *Database) Get(id string) Noodle {
db.mu.Lock()
defer db.mu.Unlock()
var item Noodle
log.Printf("Looking up noodle key='%s'", id)
log.Printf("key='%s' exists=%t", id, db.Handle.Has(id))
@ -62,6 +81,9 @@ func (db *Database) Get(id string) Noodle {
}
func (db *Database) Add(item Noodle) error {
db.mu.Lock()
defer db.mu.Unlock()
if err := db.Handle.Add(item.Id, item); err != nil {
log.Print(err)
return err
@ -70,6 +92,9 @@ func (db *Database) Add(item Noodle) error {
}
func (db *Database) Update(item Noodle) error {
db.mu.Lock()
defer db.mu.Unlock()
if err := db.Handle.Add(item.Id, item); err != nil {
log.Print(err)
return err
@ -78,6 +103,9 @@ func (db *Database) Update(item Noodle) error {
}
func (db *Database) Delete(id string) error {
db.mu.Lock()
defer db.mu.Unlock()
if err := db.Handle.Delete(id); err != nil {
log.Print(err)
return err
@ -88,3 +116,248 @@ func (db *Database) Delete(id string) error {
func (db *Database) Close() error {
return db.connection.Close()
}
func (db *Database) GetAllUsers() []User {
db.mu.Lock()
defer db.mu.Unlock()
var data []User
if err := db.Users.List(&data); err != nil {
log.Print(err)
return nil
}
return data
}
func (db *Database) GetUserByUsername(username string) (User, error) {
db.mu.Lock()
defer db.mu.Unlock()
var data []User
if err := db.Users.List(&data); err != nil {
log.Print(err)
return User{}, err
}
for _, user := range data {
if user.Username == username {
return user, nil
}
}
return User{}, ErrUserNotFound
}
func (db *Database) AddUser(user User) error {
db.mu.Lock()
defer db.mu.Unlock()
var data []User
if err := db.Users.List(&data); err != nil {
log.Print(err)
return err
}
for _, existing := range data {
if existing.Username == user.Username {
return fmt.Errorf("user %q already exists", user.Username)
}
}
if err := db.Users.Add(user.Id, user); err != nil {
log.Print(err)
return err
}
return nil
}
func (db *Database) UpdateUser(user User) error {
db.mu.Lock()
defer db.mu.Unlock()
if err := db.Users.Add(user.Id, user); err != nil {
log.Print(err)
return err
}
return nil
}
func (db *Database) SetUserRole(username, role string) (User, error) {
db.mu.Lock()
defer db.mu.Unlock()
var data []User
if err := db.Users.List(&data); err != nil {
log.Print(err)
return User{}, err
}
var target User
found := false
adminCount := 0
for _, user := range data {
if user.Role == UserRoleAdmin {
adminCount++
}
if user.Username == username {
target = user
found = true
}
}
if !found {
return User{}, ErrUserNotFound
}
if target.Role == UserRoleAdmin && role != UserRoleAdmin && adminCount <= 1 {
return User{}, ErrLastAdminRemoval
}
target.Role = role
if err := db.Users.Add(target.Id, target); err != nil {
log.Print(err)
return User{}, err
}
return target, nil
}
func (db *Database) SetUserPassword(username, passwordHash string) (User, error) {
db.mu.Lock()
defer db.mu.Unlock()
var data []User
if err := db.Users.List(&data); err != nil {
log.Print(err)
return User{}, err
}
for _, user := range data {
if user.Username != username {
continue
}
user.PasswordHash = passwordHash
if err := db.Users.Add(user.Id, user); err != nil {
log.Print(err)
return User{}, err
}
return user, nil
}
return User{}, ErrUserNotFound
}
func (db *Database) DeleteUser(username string) (User, error) {
db.mu.Lock()
defer db.mu.Unlock()
var data []User
if err := db.Users.List(&data); err != nil {
log.Print(err)
return User{}, err
}
var target User
found := false
adminCount := 0
for _, user := range data {
if user.Role == UserRoleAdmin {
adminCount++
}
if user.Username == username {
target = user
found = true
}
}
if !found {
return User{}, ErrUserNotFound
}
if target.Role == UserRoleAdmin && adminCount <= 1 {
return User{}, ErrLastAdminRemoval
}
if err := db.Users.Delete(target.Id); err != nil {
log.Print(err)
return User{}, err
}
return target, nil
}
func (db *Database) SetIsUp(id string, isUp bool) (Noodle, error) {
db.mu.Lock()
defer db.mu.Unlock()
var item Noodle
if err := db.Handle.Get(id, &item); err != nil {
log.Print(err)
return Noodle{}, err
}
item.IsUp = isUp
if err := db.Handle.Add(item.Id, item); err != nil {
log.Print(err)
return Noodle{}, err
}
return item, nil
}
func (db *Database) DeleteByID(id string) (Noodle, error) {
db.mu.Lock()
defer db.mu.Unlock()
var item Noodle
if err := db.Handle.Get(id, &item); err != nil {
log.Print(err)
return Noodle{}, err
}
if err := db.Handle.Delete(id); err != nil {
log.Print(err)
return Noodle{}, err
}
return item, nil
}
func (db *Database) TickExpirations() []Noodle {
db.mu.Lock()
defer db.mu.Unlock()
var noodles []Noodle
if err := db.Handle.List(&noodles); err != nil {
log.Print(err)
return nil
}
var stopped []Noodle
for _, item := range noodles {
if !item.IsUp {
continue
}
if item.Expiration <= 0 {
item.IsUp = false
stopped = append(stopped, item)
if err := db.Handle.Delete(item.Id); err != nil {
log.Print(err)
}
continue
}
item.Expiration -= time.Second
if item.Expiration <= 0 {
item.Expiration = 0
item.IsUp = false
stopped = append(stopped, item)
if err := db.Handle.Delete(item.Id); err != nil {
log.Print(err)
}
continue
}
if err := db.Handle.Add(item.Id, item); err != nil {
log.Print(err)
}
}
return stopped
}