package handlers import ( "encoding/json" "net/http" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/middleware" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/models" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/service" "go.uber.org/zap" ) // ShortLinkHandler handles shortlink operations type ShortLinkHandler struct { logger *zap.Logger shortLinkService *service.ShortLinkService } // NewShortLinkHandler creates a new shortlink handler func NewShortLinkHandler(logger *zap.Logger, shortLinkService *service.ShortLinkService) *ShortLinkHandler { return &ShortLinkHandler{ logger: logger, shortLinkService: shortLinkService, } } // GenerateShortLink handles shortlink generation requests func (h *ShortLinkHandler) GenerateShortLink(w http.ResponseWriter, r *http.Request) { // Only allow admin or expertise_doctor roles to generate shortlinks userRole, ok := r.Context().Value(middleware.UserRoleKey).(string) if !ok || (userRole != "admin" && userRole != "expertise_doctor") { h.logger.Warn("Unauthorized attempt to generate shortlink", zap.String("role", userRole)) http.Error(w, "Only admin or expertise doctor can generate short links", http.StatusForbidden) return } // Parse request body var req models.GenerateShortLinkRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { h.logger.Error("Failed to parse shortlink generation request", zap.Error(err)) http.Error(w, "Invalid request body", http.StatusBadRequest) return } // Get user ID from context userID, ok := r.Context().Value(middleware.UserIDKey).(string) if !ok { h.logger.Error("User ID not found in context") http.Error(w, "User context not found", http.StatusInternalServerError) return } // Generate shortlink using configured baseURL from service response, err := h.shortLinkService.GenerateShortLink(&req, userID) if err != nil { h.logger.Error("Failed to generate shortlink", zap.Error(err), zap.String("patientID", req.PatientID), zap.String("studyUID", req.StudyUID)) statusCode := http.StatusInternalServerError message := "Failed to generate shortlink" if err == service.ErrInvalidStudyUID { statusCode = http.StatusBadRequest message = "Invalid StudyInstanceUID" } http.Error(w, message, statusCode) return } // Log successful shortlink generation h.logger.Info("Shortlink generated successfully", zap.String("token", response.ShortToken), zap.String("patientID", req.PatientID), zap.String("studyUID", req.StudyUID), zap.String("createdBy", userID)) // Return response w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(response) } // ShortLinkAuth handles authentication requests using shortlinks func (h *ShortLinkHandler) ShortLinkAuth(w http.ResponseWriter, r *http.Request) { // Parse request body var req models.ShortLinkAuthRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { h.logger.Error("Failed to parse shortlink auth request", zap.Error(err)) http.Error(w, "Invalid request body", http.StatusBadRequest) return } // Validate and authenticate response, err := h.shortLinkService.AuthenticateWithShortLink(&req) if err != nil { h.logger.Warn("Shortlink authentication failed", zap.Error(err), zap.String("token", req.ShortToken)) statusCode := http.StatusUnauthorized message := "Authentication failed" switch err { case service.ErrShortLinkNotFound, service.ErrShortLinkExpired: message = "Short link not found or expired" case service.ErrInvalidDOB: message = "Invalid date of birth" case service.ErrTooManyAttempts: message = "Too many failed attempts" } http.Error(w, message, statusCode) return } // Log successful authentication h.logger.Info("Shortlink authentication successful", zap.String("token", req.ShortToken)) // Return response w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(response) }