package auth import ( "encoding/json" "net/http" "github.com/go-chi/chi/v5" "github.com/example/remote-access-platform/backend/internal/platform/httpx" "github.com/example/remote-access-platform/backend/internal/platform/module" ) type Module struct { service *Service } func NewModule(deps module.Dependencies, service *Service) *Module { return &Module{service: service} } func (m *Module) Name() string { return "auth" } func (m *Module) RegisterRoutes(router chi.Router) { router.Route("/installation", func(r chi.Router) { r.Get("/status", m.handleInstallationStatus) r.Post("/bootstrap-owner", m.handleBootstrapOwner) }) router.Route("/auth", func(r chi.Router) { r.Post("/login", m.handleLogin) r.Post("/refresh", m.handleRefresh) r.Post("/sessions/revoke", m.handleRevokeAuthSession) r.Get("/devices", m.handleTrustedDevices) r.Post("/devices/{deviceID}/revoke", m.handleRevokeTrustedDevice) }) router.Route("/users", func(r chi.Router) { r.Get("/", m.handleListUsers) r.Post("/", m.handleCreateUser) }) } func (m *Module) handleInstallationStatus(w http.ResponseWriter, r *http.Request) { status, err := m.service.InstallationStatus(r.Context()) if err != nil { statusCode, message := m.service.MapError(err) httpx.WriteError(w, statusCode, message) return } httpx.WriteJSON(w, http.StatusOK, map[string]any{"installation": status}) } func (m *Module) handleBootstrapOwner(w http.ResponseWriter, r *http.Request) { var cmd BootstrapOwnerCommand if err := json.NewDecoder(r.Body).Decode(&cmd); err != nil { httpx.WriteError(w, http.StatusBadRequest, "invalid installation bootstrap payload") return } result, err := m.service.BootstrapOwner(r.Context(), cmd) if err != nil { status, message := m.service.MapError(err) httpx.WriteError(w, status, message) return } httpx.WriteJSON(w, http.StatusCreated, result) } func (m *Module) handleLogin(w http.ResponseWriter, r *http.Request) { var cmd LoginCommand if err := json.NewDecoder(r.Body).Decode(&cmd); err != nil { httpx.WriteError(w, http.StatusBadRequest, "invalid login payload") return } result, err := m.service.Login(r.Context(), cmd) if err != nil { status, message := m.service.MapError(err) httpx.WriteError(w, status, message) return } httpx.WriteJSON(w, http.StatusOK, result) } func (m *Module) handleListUsers(w http.ResponseWriter, r *http.Request) { actorUserID := r.URL.Query().Get("actor_user_id") users, err := m.service.ListUsers(r.Context(), actorUserID) if err != nil { status, message := m.service.MapError(err) httpx.WriteError(w, status, message) return } httpx.WriteJSON(w, http.StatusOK, map[string]any{"users": users}) } func (m *Module) handleCreateUser(w http.ResponseWriter, r *http.Request) { var cmd CreateUserCommand if err := json.NewDecoder(r.Body).Decode(&cmd); err != nil { httpx.WriteError(w, http.StatusBadRequest, "invalid user payload") return } user, err := m.service.CreateUser(r.Context(), cmd) if err != nil { status, message := m.service.MapError(err) httpx.WriteError(w, status, message) return } httpx.WriteJSON(w, http.StatusCreated, map[string]any{"user": user}) } func (m *Module) handleRefresh(w http.ResponseWriter, r *http.Request) { var cmd RefreshCommand if err := json.NewDecoder(r.Body).Decode(&cmd); err != nil { httpx.WriteError(w, http.StatusBadRequest, "invalid refresh payload") return } result, err := m.service.Refresh(r.Context(), cmd) if err != nil { status, message := m.service.MapError(err) httpx.WriteError(w, status, message) return } httpx.WriteJSON(w, http.StatusOK, result) } func (m *Module) handleRevokeAuthSession(w http.ResponseWriter, r *http.Request) { var cmd RevokeAuthSessionCommand if err := json.NewDecoder(r.Body).Decode(&cmd); err != nil { httpx.WriteError(w, http.StatusBadRequest, "invalid auth session revoke payload") return } if err := m.service.RevokeAuthSession(r.Context(), cmd); err != nil { status, message := m.service.MapError(err) httpx.WriteError(w, status, message) return } httpx.WriteJSON(w, http.StatusAccepted, map[string]any{ "status": "revoked", "message": httpx.NewMessage( "auth.session.revoked", "status.auth.session.revoked", "Auth session revoked.", nil, "", ), }) } func (m *Module) handleTrustedDevices(w http.ResponseWriter, r *http.Request) { userID := r.URL.Query().Get("user_id") if userID == "" { httpx.WriteError(w, http.StatusBadRequest, "user_id is required") return } devices, err := m.service.ListTrustedDevices(r.Context(), userID) if err != nil { status, message := m.service.MapError(err) httpx.WriteError(w, status, message) return } httpx.WriteJSON(w, http.StatusOK, map[string]any{ "devices": devices, }) } func (m *Module) handleRevokeTrustedDevice(w http.ResponseWriter, r *http.Request) { var payload struct { UserID string `json:"user_id"` Reason string `json:"reason"` } if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { httpx.WriteError(w, http.StatusBadRequest, "invalid device revoke payload") return } err := m.service.RevokeTrustedDevice(r.Context(), RevokeDeviceCommand{ UserID: payload.UserID, DeviceID: chi.URLParam(r, "deviceID"), Reason: payload.Reason, }) if err != nil { status, message := m.service.MapError(err) httpx.WriteError(w, status, message) return } httpx.WriteJSON(w, http.StatusAccepted, map[string]any{ "status": "revoked", "message": httpx.NewMessage( "auth.device.revoked", "status.auth.device.revoked", "Trusted device revoked.", nil, "", ), }) }