132 lines
3.2 KiB
Go
132 lines
3.2 KiB
Go
package handlers
|
|
|
|
import (
|
|
"database/sql"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
"github.com/leolionad58/ticketbot/internal/db"
|
|
panelauth "github.com/leolionad58/ticketbot/internal/panel/auth"
|
|
)
|
|
|
|
// SessionsHandler serves /sessions routes (superadmin only).
|
|
type SessionsHandler struct {
|
|
SessionRepo *db.PanelSessionRepo
|
|
AdminRepo *db.PanelAdminRepo
|
|
AuditLog *db.AuditLogRepo
|
|
Renderer *Renderer
|
|
}
|
|
|
|
// sessionWithAdmin joins a PanelSession with the admin's username.
|
|
type sessionWithAdmin struct {
|
|
*db.PanelSession
|
|
DiscordUsername string
|
|
IsCurrent bool
|
|
}
|
|
|
|
type sessionsData struct {
|
|
baseData
|
|
Sessions []*sessionWithAdmin
|
|
}
|
|
|
|
// HandleList serves GET /sessions.
|
|
func (h *SessionsHandler) HandleList(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
base := h.Renderer.base(r, "sessions")
|
|
|
|
currentSess := panelauth.SessionFromContext(ctx)
|
|
currentToken := ""
|
|
if currentSess != nil {
|
|
currentToken = currentSess.Token
|
|
}
|
|
|
|
sessions, err := h.SessionRepo.List(ctx)
|
|
if err != nil {
|
|
http.Error(w, "db error: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
var enriched []*sessionWithAdmin
|
|
for _, s := range sessions {
|
|
swa := &sessionWithAdmin{
|
|
PanelSession: s,
|
|
IsCurrent: s.Token == currentToken,
|
|
}
|
|
if admin, err := h.AdminRepo.GetByID(ctx, s.AdminID); err == nil && admin != nil {
|
|
swa.DiscordUsername = admin.DiscordUsername
|
|
}
|
|
enriched = append(enriched, swa)
|
|
}
|
|
|
|
h.Renderer.Page(w, "sessions", sessionsData{
|
|
baseData: base,
|
|
Sessions: enriched,
|
|
})
|
|
}
|
|
|
|
// HandleRevoke serves POST /sessions/{id}/revoke.
|
|
func (h *SessionsHandler) HandleRevoke(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
|
|
if err != nil {
|
|
http.Error(w, "invalid id", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := h.SessionRepo.DeleteByID(ctx, id); err != nil {
|
|
http.Error(w, "db error: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// Audit log
|
|
if h.AuditLog != nil {
|
|
admin := panelauth.AdminFromContext(ctx)
|
|
if admin != nil {
|
|
_ = h.AuditLog.Insert(ctx, &db.AuditLogEntry{
|
|
AdminID: admin.ID,
|
|
Action: db.AuditRevokeSession,
|
|
EntityType: "panel_session",
|
|
EntityID: sql.NullInt64{Int64: id, Valid: true},
|
|
IPAddress: clientIP(r),
|
|
})
|
|
}
|
|
}
|
|
|
|
http.Redirect(w, r, "/sessions?flash=Session+révoquée", http.StatusSeeOther)
|
|
}
|
|
|
|
// HandleRevokeAll serves POST /sessions/revoke-all.
|
|
func (h *SessionsHandler) HandleRevokeAll(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
admin := panelauth.AdminFromContext(ctx)
|
|
if admin == nil {
|
|
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
currentSess := panelauth.SessionFromContext(ctx)
|
|
currentToken := ""
|
|
if currentSess != nil {
|
|
currentToken = currentSess.Token
|
|
}
|
|
|
|
if err := h.SessionRepo.DeleteAllExcept(ctx, admin.ID, currentToken); err != nil {
|
|
http.Error(w, "db error: "+err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
// Audit log
|
|
if h.AuditLog != nil {
|
|
_ = h.AuditLog.Insert(ctx, &db.AuditLogEntry{
|
|
AdminID: admin.ID,
|
|
Action: db.AuditRevokeAllSess,
|
|
EntityType: "panel_session",
|
|
IPAddress: clientIP(r),
|
|
})
|
|
}
|
|
|
|
http.Redirect(w, r, "/sessions?flash=Toutes+les+sessions+révoquées", http.StatusSeeOther)
|
|
}
|