- Added a new AGENTS.md file to document the project structure and conventions. - Updated .gitignore to include node_modules and refined cursor rules. - Introduced new backend and frontend components for improved map interactions, including context menus and controls. - Enhanced API composables for better admin and authentication functionalities. - Refactored existing components for cleaner code and improved user experience. - Updated README.md to clarify production asset serving and user setup instructions.
161 lines
3.3 KiB
Go
161 lines
3.3 KiB
Go
package app
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/andyleap/hnh-map/internal/app/response"
|
|
"github.com/andyleap/hnh-map/internal/app/store"
|
|
"go.etcd.io/bbolt"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
func (a *App) getSession(req *http.Request) *Session {
|
|
c, err := req.Cookie("session")
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
var s *Session
|
|
a.db.View(func(tx *bbolt.Tx) error {
|
|
sessions := tx.Bucket(store.BucketSessions)
|
|
if sessions == nil {
|
|
return nil
|
|
}
|
|
session := sessions.Get([]byte(c.Value))
|
|
if session == nil {
|
|
return nil
|
|
}
|
|
err := json.Unmarshal(session, &s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if s.TempAdmin {
|
|
s.Auths = Auths{AUTH_ADMIN}
|
|
return nil
|
|
}
|
|
users := tx.Bucket(store.BucketUsers)
|
|
if users == nil {
|
|
return nil
|
|
}
|
|
raw := users.Get([]byte(s.Username))
|
|
if raw == nil {
|
|
s = nil
|
|
return nil
|
|
}
|
|
u := User{}
|
|
err = json.Unmarshal(raw, &u)
|
|
if err != nil {
|
|
s = nil
|
|
return err
|
|
}
|
|
s.Auths = u.Auths
|
|
return nil
|
|
})
|
|
return s
|
|
}
|
|
|
|
func (a *App) deleteSession(s *Session) {
|
|
a.db.Update(func(tx *bbolt.Tx) error {
|
|
sessions, err := tx.CreateBucketIfNotExists(store.BucketSessions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return sessions.Delete([]byte(s.ID))
|
|
})
|
|
}
|
|
|
|
func (a *App) saveSession(s *Session) {
|
|
a.db.Update(func(tx *bbolt.Tx) error {
|
|
sessions, err := tx.CreateBucketIfNotExists(store.BucketSessions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
buf, err := json.Marshal(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return sessions.Put([]byte(s.ID), buf)
|
|
})
|
|
}
|
|
|
|
func (a *App) getPage(req *http.Request) Page {
|
|
p := Page{}
|
|
a.db.View(func(tx *bbolt.Tx) error {
|
|
c := tx.Bucket(store.BucketConfig)
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
p.Title = string(c.Get([]byte("title")))
|
|
return nil
|
|
})
|
|
return p
|
|
}
|
|
|
|
func (a *App) getUser(user, pass string) (u *User) {
|
|
a.db.View(func(tx *bbolt.Tx) error {
|
|
users := tx.Bucket(store.BucketUsers)
|
|
if users == nil {
|
|
return nil
|
|
}
|
|
raw := users.Get([]byte(user))
|
|
if raw != nil {
|
|
json.Unmarshal(raw, &u)
|
|
if u.Pass == nil {
|
|
u = nil
|
|
return nil
|
|
}
|
|
if bcrypt.CompareHashAndPassword(u.Pass, []byte(pass)) != nil {
|
|
u = nil
|
|
return nil
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
return u
|
|
}
|
|
|
|
// createSession creates a session for username, returns session ID or empty string.
|
|
func (a *App) createSession(username string, tempAdmin bool) string {
|
|
session := make([]byte, 32)
|
|
if _, err := rand.Read(session); err != nil {
|
|
return ""
|
|
}
|
|
sid := hex.EncodeToString(session)
|
|
s := &Session{
|
|
ID: sid,
|
|
Username: username,
|
|
TempAdmin: tempAdmin,
|
|
}
|
|
a.saveSession(s)
|
|
return sid
|
|
}
|
|
|
|
// setupRequired returns true if no users exist (first run).
|
|
func (a *App) setupRequired() bool {
|
|
var required bool
|
|
a.db.View(func(tx *bbolt.Tx) error {
|
|
ub := tx.Bucket(store.BucketUsers)
|
|
if ub == nil {
|
|
required = true
|
|
return nil
|
|
}
|
|
if ub.Stats().KeyN == 0 {
|
|
required = true
|
|
return nil
|
|
}
|
|
return nil
|
|
})
|
|
return required
|
|
}
|
|
|
|
func (a *App) requireAdmin(rw http.ResponseWriter, req *http.Request) *Session {
|
|
s := a.getSession(req)
|
|
if s == nil || !s.Auths.Has(AUTH_ADMIN) {
|
|
response.JSONError(rw, http.StatusUnauthorized, "Unauthorized", "UNAUTHORIZED")
|
|
return nil
|
|
}
|
|
return s
|
|
}
|