My account page

The signed-in user's profile, password and own app tokens move from the
Settings card to their own page at #/account, ready for more user
functions. The sidebar footer links to it with the user's name. Users
can now set their own note; the page header shows when and from where
the session started, which the server now records. Settings keeps the
Users table, where your own row links to My account.
This commit is contained in:
Daniel Redetzke
2026-10-04 16:07:21 +03:00
parent 4ecfddf06a
commit 19008f8a33
5 changed files with 195 additions and 93 deletions
+17 -7
View File
@@ -102,6 +102,13 @@ type principal struct {
RemoteIP string
// MustChangePassword blocks everything but changing the password.
MustChangePassword bool
Session *sessionInfo // nil for API tokens
}
// sessionInfo is when and from where a browser session started.
type sessionInfo struct {
Started time.Time `json:"started"`
IP string `json:"ip"`
}
type session struct {
@@ -110,6 +117,7 @@ type session struct {
// password ends every session started with the old one.
stamp string
expires time.Time
info sessionInfo
}
type tokenUse struct {
@@ -185,20 +193,21 @@ func (a *Auth) Login(user, pw, ip string) (string, error) {
}
delete(a.fails, ip)
a.logins[u.ID] = tokenUse{At: time.Now(), IP: ip}
return a.newSessionLocked(cfg, u), nil
return a.newSessionLocked(cfg, u, sessionInfo{Started: time.Now(), IP: ip}), nil
}
// NewSession signs a user in again, e.g. after they changed their password.
func (a *Auth) NewSession(u *User) string {
// NewSession replaces a session after the user changed their password; it
// keeps when and from where the old one started.
func (a *Auth) NewSession(u *User, info sessionInfo) string {
cfg := a.store.Get()
a.mu.Lock()
defer a.mu.Unlock()
return a.newSessionLocked(cfg, u)
return a.newSessionLocked(cfg, u, info)
}
func (a *Auth) newSessionLocked(cfg *Config, u *User) string {
func (a *Auth) newSessionLocked(cfg *Config, u *User, info sessionInfo) string {
id := randomString(32)
a.sessions[id] = &session{userID: u.ID, stamp: u.PasswordHash, expires: time.Now().Add(time.Duration(cfg.Web.SessionHours) * time.Hour)}
a.sessions[id] = &session{userID: u.ID, stamp: u.PasswordHash, expires: time.Now().Add(time.Duration(cfg.Web.SessionHours) * time.Hour), info: info}
return id
}
@@ -280,7 +289,8 @@ func (a *Auth) Authenticate(r *http.Request) (*principal, bool) {
delete(a.sessions, c.Value)
return nil, false
}
return &principal{Name: u.Username, UserID: u.ID, Scope: "rw", IsAdmin: true, RemoteIP: ip, MustChangePassword: u.MustChangePassword}, true
info := s.info
return &principal{Name: u.Username, UserID: u.ID, Scope: "rw", IsAdmin: true, RemoteIP: ip, MustChangePassword: u.MustChangePassword, Session: &info}, true
}
func (a *Auth) TokenUse(id string) *tokenUse {