feat: implement B.1 — persist login sessions in SQLite with sliding idle timeout
Sessions move from an in-memory map (absolute 12h TTL) to a `sessions` table (migration 0002), storing only the SHA-256 of the token. Expiry is now a sliding idle window (PANEL_SESSION_IDLE_DAYS, default 7, no absolute cap), extended at most once an hour and never by the monitoring screens' background polling (GET + HX-Request), so a forgotten open tab doesn't keep a session alive indefinitely. A login now survives a container restart or redeploy. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -152,15 +152,24 @@ func (s *Server) submitLogin(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
token := s.sessions.Create(admin.Username)
|
||||
s.setSessionCookie(w, token)
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// setSessionCookie (re)issues the session cookie with a fresh Max-Age equal
|
||||
// to the sliding idle window (plan B.1), so the browser-side expiry tracks
|
||||
// whatever the database row was just set to — at login, and again whenever
|
||||
// requireAuth extends an active session.
|
||||
func (s *Server) setSessionCookie(w http.ResponseWriter, token string) {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: s.sessionCookie(),
|
||||
Value: token,
|
||||
Path: "/",
|
||||
MaxAge: s.sessions.MaxAge(),
|
||||
HttpOnly: true,
|
||||
Secure: s.cfg.CookieSecure,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
// handleLogout destroys the session and clears the cookie.
|
||||
|
||||
Reference in New Issue
Block a user