feat: pagination support + fix: able to get inactive session
This commit is contained in:
@ -3,17 +3,20 @@ package admin
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"math"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"gitea.local/admin/hspguard/internal/repository"
|
||||
"gitea.local/admin/hspguard/internal/types"
|
||||
"gitea.local/admin/hspguard/internal/web"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type GetSessionsParams struct {
|
||||
Limit int32 `json:"limit"`
|
||||
Offset int32 `json:"offset"`
|
||||
PageSize int `json:"size"`
|
||||
Page int `json:"page"`
|
||||
// TODO: More filtering possibilities like onlyActive, expired, not-expired etc.
|
||||
}
|
||||
|
||||
@ -22,17 +25,22 @@ func (h *AdminHandler) GetUserSessions(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
params := GetSessionsParams{}
|
||||
|
||||
if limit, err := strconv.Atoi(q.Get("limit")); err == nil {
|
||||
params.Limit = int32(limit)
|
||||
if pageSize, err := strconv.Atoi(q.Get("size")); err == nil {
|
||||
params.PageSize = pageSize
|
||||
} else {
|
||||
params.PageSize = 15
|
||||
}
|
||||
|
||||
if offset, err := strconv.Atoi(q.Get("offset")); err == nil {
|
||||
params.Offset = int32(offset)
|
||||
if page, err := strconv.Atoi(q.Get("page")); err == nil {
|
||||
params.Page = page
|
||||
} else {
|
||||
web.Error(w, "page is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
sessions, err := h.repo.GetUserSessions(r.Context(), repository.GetUserSessionsParams{
|
||||
Limit: params.Limit,
|
||||
Offset: params.Offset,
|
||||
Limit: int32(params.PageSize),
|
||||
Offset: int32(params.Page-1) * int32(params.PageSize),
|
||||
})
|
||||
if err != nil {
|
||||
log.Println("ERR: Failed to read user sessions from db:", err)
|
||||
@ -40,35 +48,77 @@ func (h *AdminHandler) GetUserSessions(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
var mapped []*types.UserSessionDTO
|
||||
totalSessions, err := h.repo.GetUserSessionsCount(r.Context())
|
||||
if err != nil {
|
||||
log.Println("ERR: Failed to get total count of user sessions:", err)
|
||||
web.Error(w, "failed to retrieve sessions", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
mapped := make([]*types.UserSessionDTO, 0)
|
||||
|
||||
for _, session := range sessions {
|
||||
mapped = append(mapped, types.NewUserSessionDTO(&session))
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(mapped); err != nil {
|
||||
type Response struct {
|
||||
Items []*types.UserSessionDTO `json:"items"`
|
||||
Page int `json:"page"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
}
|
||||
|
||||
response := Response{
|
||||
Items: mapped,
|
||||
Page: params.Page,
|
||||
TotalPages: int(math.Ceil(float64(totalSessions) / float64(params.PageSize))),
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||
log.Println("ERR: Failed to encode sessions in response:", err)
|
||||
web.Error(w, "failed to encode sessions", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (h *AdminHandler) RevokeUserSession(w http.ResponseWriter, r *http.Request) {
|
||||
sessionId := chi.URLParam(r, "id")
|
||||
parsed, err := uuid.Parse(sessionId)
|
||||
if err != nil {
|
||||
web.Error(w, "provided service id is not valid", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.repo.RevokeUserSession(r.Context(), parsed); err != nil {
|
||||
log.Println("ERR: Failed to revoke user session:", err)
|
||||
web.Error(w, "failed to revoke user session", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("{\"success\":true}"))
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetServiceSessions(w http.ResponseWriter, r *http.Request) {
|
||||
q := r.URL.Query()
|
||||
|
||||
params := GetSessionsParams{}
|
||||
|
||||
if limit, err := strconv.Atoi(q.Get("limit")); err == nil {
|
||||
params.Limit = int32(limit)
|
||||
if pageSize, err := strconv.Atoi(q.Get("size")); err == nil {
|
||||
params.PageSize = pageSize
|
||||
} else {
|
||||
params.PageSize = 15
|
||||
}
|
||||
|
||||
if offset, err := strconv.Atoi(q.Get("offset")); err == nil {
|
||||
params.Offset = int32(offset)
|
||||
if page, err := strconv.Atoi(q.Get("page")); err == nil {
|
||||
params.Page = page
|
||||
} else {
|
||||
web.Error(w, "page is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
sessions, err := h.repo.GetServiceSessions(r.Context(), repository.GetServiceSessionsParams{
|
||||
Limit: params.Limit,
|
||||
Offset: params.Offset,
|
||||
Limit: int32(params.PageSize),
|
||||
Offset: int32(params.Page-1) * int32(params.PageSize),
|
||||
})
|
||||
if err != nil {
|
||||
log.Println("ERR: Failed to read api sessions from db:", err)
|
||||
@ -76,13 +126,32 @@ func (h *AdminHandler) GetServiceSessions(w http.ResponseWriter, r *http.Request
|
||||
return
|
||||
}
|
||||
|
||||
var mapped []*types.ServiceSessionDTO
|
||||
totalSessions, err := h.repo.GetServiceSessionsCount(r.Context())
|
||||
if err != nil {
|
||||
log.Println("ERR: Failed to get total count of service sessions:", err)
|
||||
web.Error(w, "failed to retrieve sessions", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
mapped := make([]*types.ServiceSessionDTO, 0)
|
||||
|
||||
for _, session := range sessions {
|
||||
mapped = append(mapped, types.NewServiceSessionDTO(&session))
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(sessions); err != nil {
|
||||
type Response struct {
|
||||
Items []*types.ServiceSessionDTO `json:"items"`
|
||||
Page int `json:"page"`
|
||||
TotalPages int `json:"total_pages"`
|
||||
}
|
||||
|
||||
response := Response{
|
||||
Items: mapped,
|
||||
Page: params.Page,
|
||||
TotalPages: int(math.Ceil(float64(totalSessions) / float64(params.PageSize))),
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||||
log.Println("ERR: Failed to encode sessions in response:", err)
|
||||
web.Error(w, "failed to encode sessions", http.StatusInternalServerError)
|
||||
}
|
||||
|
@ -31,8 +31,7 @@ WHERE access_token_id = $1
|
||||
|
||||
-- name: GetServiceSessionByRefreshJTI :one
|
||||
SELECT * FROM service_sessions
|
||||
WHERE refresh_token_id = $1
|
||||
AND is_active = TRUE;
|
||||
WHERE refresh_token_id = $1;
|
||||
|
||||
-- name: RevokeServiceSession :exec
|
||||
UPDATE service_sessions
|
||||
@ -59,3 +58,6 @@ JOIN api_services AS service ON service.id = session.service_id
|
||||
JOIN users AS u ON u.id = session.user_id
|
||||
ORDER BY session.issued_at DESC
|
||||
LIMIT $1 OFFSET $2;
|
||||
|
||||
-- name: GetServiceSessionsCount :one
|
||||
SELECT COUNT(*) FROM service_sessions;
|
||||
|
@ -23,8 +23,7 @@ WHERE access_token_id = $1
|
||||
|
||||
-- name: GetUserSessionByRefreshJTI :one
|
||||
SELECT * FROM user_sessions
|
||||
WHERE refresh_token_id = $1
|
||||
AND is_active = TRUE;
|
||||
WHERE refresh_token_id = $1;
|
||||
|
||||
-- name: RevokeUserSession :exec
|
||||
UPDATE user_sessions
|
||||
@ -56,3 +55,6 @@ FROM user_sessions AS session
|
||||
JOIN users AS u ON u.id = session.user_id
|
||||
ORDER BY session.issued_at DESC
|
||||
LIMIT $1 OFFSET $2;
|
||||
|
||||
-- name: GetUserSessionsCount :one
|
||||
SELECT COUNT(*) FROM user_sessions;
|
||||
|
Reference in New Issue
Block a user