Files
warmbly/internal/api/handler/auth.go
T
Matthew Meszaros e0ced97ba1 feat: add organization audit trail
Move audit logs into org-scoped Postgres storage, wire audit events across backend handlers, and surface actor details in the dashboard activity log.

Add realtime audit invalidation and retention pruning so the trail stays current and bounded.
2026-06-01 04:23:00 +02:00

187 lines
4.1 KiB
Go

package handler
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/warmbly/warmbly/internal/api/middleware"
"github.com/warmbly/warmbly/internal/app/auth"
"github.com/warmbly/warmbly/internal/errx"
)
func (h *Handler) LoginStart(c *gin.Context) {
var data auth.AuthData
if err := c.ShouldBindJSON(&data); err != nil {
errx.Handle(c, errx.ErrInvalid)
return
}
resp, err := h.AuthService.LoginStart(c.Request.Context(), &data, c.ClientIP())
if err != nil {
errx.Handle(c, err)
return
}
c.JSON(http.StatusOK, resp)
}
func (h *Handler) LoginConfirm(c *gin.Context) {
var data auth.ConfirmData
if err := c.ShouldBindJSON(&data); err != nil {
errx.Handle(c, err)
return
}
resp, err := h.AuthService.LoginConfirm(c.Request.Context(), &data, data.Session, c.ClientIP(), c.Request.UserAgent())
if err != nil {
errx.Handle(c, err)
return
}
c.JSON(http.StatusOK, resp)
}
func (h *Handler) RegistrationStart(c *gin.Context) {
var data auth.AuthData
if err := c.ShouldBindJSON(&data); err != nil {
errx.Handle(c, errx.ErrInvalid)
return
}
resp, err := h.AuthService.RegistrationStart(c.Request.Context(), &data, c.ClientIP())
if err != nil {
errx.Handle(c, err)
return
}
c.JSON(http.StatusOK, resp)
}
func (h *Handler) RegistrationConfirm(c *gin.Context) {
var data auth.ConfirmData
if err := c.ShouldBindJSON(&data); err != nil {
errx.Handle(c, err)
return
}
if err := h.AuthService.RegistrationConfirm(c.Request.Context(), &data, data.Session, c.ClientIP()); err != nil {
errx.Handle(c, err)
return
}
c.Status(http.StatusNoContent)
}
func (h *Handler) RefreshToken(c *gin.Context) {
var data struct {
RefreshToken string `json:"refresh_token"`
}
if err := c.ShouldBindJSON(&data); err != nil {
errx.Handle(c, errx.ErrInvalid)
return
}
token, err := h.TokenService.RefreshToken(c.Request.Context(), data.RefreshToken)
if err != nil {
errx.Handle(c, err)
return
}
c.JSON(http.StatusOK, token)
}
func (h *Handler) Logout(c *gin.Context) {
accessToken := middleware.GetAccessToken(c)
if err := h.TokenService.RevokeSession(c.Request.Context(), accessToken); err != nil {
errx.Handle(c, err)
return
}
c.Status(http.StatusNoContent)
}
func (h *Handler) LogoutAll(c *gin.Context) {
accessToken := middleware.GetAccessToken(c)
if err := h.TokenService.RevokeAllSession(c.Request.Context(), accessToken); err != nil {
errx.Handle(c, err)
return
}
c.Status(http.StatusNoContent)
}
func (h *Handler) GetUser(c *gin.Context) {
userID := middleware.GetUserID(c)
uid, err := uuid.Parse(userID)
if err != nil {
errx.Handle(c, errx.ErrUser)
return
}
ctx := c.Request.Context()
u, xerr := h.UserService.GetUser(ctx, uid)
if xerr != nil {
errx.Handle(c, xerr)
return
}
// Populate the per-user label groups so the frontend can render
// folder/tag pickers on initial page load without three extra
// round-trips. Without this, anything the user created in a
// previous session would disappear after a refresh: the cache
// would optimistic-update from a Create response, but on reload
// the /auth/me payload had empty folders/tags/categories.
if folders, ferr := h.FolderService.List(ctx, uid); ferr == nil {
u.Folders = folders
}
if tags, terr := h.TagService.List(ctx, uid); terr == nil {
u.Tags = tags
}
if cats, cerr := h.CategoryService.List(ctx, uid); cerr == nil {
u.Categories = cats
}
c.JSON(http.StatusOK, u)
}
func (h *Handler) ResetPasswordStart(c *gin.Context) {
var data auth.ResetPasswordStart
if err := c.ShouldBindJSON(&data); err != nil {
errx.Handle(c, errx.ErrInvalid)
return
}
if err := h.AuthService.ResetPasswordStart(c.Request.Context(), &data, c.ClientIP()); err != nil {
errx.Handle(c, err)
return
}
c.Status(http.StatusOK)
}
func (h *Handler) ResetPasswordConfirm(c *gin.Context) {
var data auth.ResetPasswordConfirm
if err := c.ShouldBindJSON(&data); err != nil {
errx.Handle(c, errx.ErrInvalid)
return
}
if err := h.AuthService.ResetPasswordConfirm(c.Request.Context(), &data, data.Session, c.ClientIP()); err != nil {
errx.Handle(c, err)
return
}
c.Status(http.StatusOK)
}