diff --git a/internal/api/handlers/dicom.go b/internal/api/handlers/dicom.go index e3a94ab..876cd53 100644 --- a/internal/api/handlers/dicom.go +++ b/internal/api/handlers/dicom.go @@ -7,6 +7,7 @@ import ( "net/url" "strings" + "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/middleware" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/auth" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/proxy" "github.com/go-chi/chi/v5" @@ -27,13 +28,66 @@ func NewDicomHandler(client *proxy.Client, logger *zap.Logger) *DicomHandler { } } +// buildRefDoctorFilter constructs a properly encoded query string for referring doctor filtering +func (h *DicomHandler) buildRefDoctorFilter(doctorName string, queryParams url.Values) string { + // Extract basic parameters with fallbacks + limit := queryParams.Get("limit") + if limit == "" { + limit = "101" // Default limit used by OHIF + } + + offset := queryParams.Get("offset") + if offset == "" { + offset = "0" // Default offset + } + + includeField := queryParams.Get("includefield") + + // Make sure includefield includes 00080090 (ReferringPhysician) + if includeField != "" && !strings.Contains(includeField, "00080090") { + includeField = includeField + ",00080090" + } else if includeField == "" { + includeField = "00081030,00080060,00080090" + } + + // Properly encode the doctor's name + encodedName := strings.ReplaceAll(doctorName, " ", "%20") + encodedName = strings.ReplaceAll(encodedName, ",", "%2C") + encodedName = strings.ReplaceAll(encodedName, ".", "%2E") + + // Construct query string manually to avoid double-encoding + return fmt.Sprintf("limit=%s&offset=%s&fuzzymatching=false&includefield=%s&00080090=%s", + limit, offset, includeField, encodedName) +} + +// isStudyListRequest checks if a path refers to the top-level studies endpoint +func isStudyListRequest(path string) bool { + // Normalize the path first + if !strings.HasPrefix(path, "/") { + path = "/" + path + } + + // Check for exact match with "/studies" + return path == "/studies" +} + // ForwardRequest forwards the request to Google Healthcare API func (h *DicomHandler) ForwardRequest(w http.ResponseWriter, r *http.Request) { // Get claims from context if they exist var claims *auth.CustomClaims - claimsValue := r.Context().Value("claims") - if claimsValue != nil { + claimsValue := r.Context().Value(middleware.ClaimsKey) + + // Add detailed debug logging about claims + if claimsValue == nil { + h.logger.Warn("Claims not found in context", + zap.String("path", r.URL.Path), + zap.String("method", r.Method)) + } else { claims = claimsValue.(*auth.CustomClaims) + h.logger.Debug("Claims retrieved from context", + zap.String("userID", claims.UserID), + zap.String("role", claims.Role), + zap.String("userName", claims.UserName)) } // Get the path after /dicomWeb @@ -48,21 +102,26 @@ func (h *DicomHandler) ForwardRequest(w http.ResponseWriter, r *http.Request) { } } + // Normalize the path + if !strings.HasPrefix(urlPath, "/") { + urlPath = "/" + urlPath + } + h.logger.Debug("Forwarding request", zap.String("path", urlPath), zap.String("method", r.Method), - zap.String("url", r.URL.String()), - ) + zap.String("url", r.URL.String())) // Copy query parameters queryParams := r.URL.Query() + queryString := "" // Apply role-specific query modifications if claims != nil { switch claims.Role { case "patient": // For patients requesting study list, filter to only show their studies - if strings.HasPrefix(urlPath, "/studies") && !strings.Contains(urlPath, "/") { + if isStudyListRequest(urlPath) { // Check if studies are available in the claim if len(claims.StudyIUIDs) > 0 { // Remove existing StudyInstanceUID param if it exists @@ -72,8 +131,7 @@ func (h *DicomHandler) ForwardRequest(w http.ResponseWriter, r *http.Request) { queryParams.Set("StudyInstanceUID", strings.Join(claims.StudyIUIDs, ",")) h.logger.Debug("Filtering by studies", - zap.Strings("studies", claims.StudyIUIDs), - ) + zap.Strings("studies", claims.StudyIUIDs)) } } else if strings.HasPrefix(urlPath, "/studies/") { // This is a request for a specific study - check if the patient is authorized @@ -112,32 +170,36 @@ func (h *DicomHandler) ForwardRequest(w http.ResponseWriter, r *http.Request) { ) } } + + // Use standard query parameter encoding + queryString = queryParams.Encode() + case "ref_doctor": - // For ref_doctor requesting study list, apply filter from token - if strings.HasPrefix(urlPath, "/studies") && !strings.Contains(urlPath, "/") && claims.FilterURL != "" { - // Parse the filter URL and add its query params - filterURL, err := url.Parse(claims.FilterURL) - if err == nil { - filterQuery := filterURL.Query() - for key, values := range filterQuery { - for _, value := range values { - queryParams.Add(key, value) - } - } - } + // For ref_doctor requesting study list, apply filter + if isStudyListRequest(urlPath) { + // Use our helper function to build the properly encoded query + queryString = h.buildRefDoctorFilter(claims.UserName, queryParams) + + h.logger.Debug("Applied referring physician filter", + zap.String("doctorName", claims.UserName), + zap.String("queryString", queryString)) + } else { + // For other paths, use standard query parameter encoding + queryString = queryParams.Encode() } + case "expertise_doctor": // No restrictions for expertise_doctor + queryString = queryParams.Encode() } + } else { + // No claims, use standard query parameter encoding + queryString = queryParams.Encode() } - // Add the query string back to the path - encodedQuery := queryParams.Encode() - if encodedQuery != "" { - if !strings.HasPrefix(urlPath, "/") { - urlPath = "/" + urlPath - } - urlPath = urlPath + "?" + encodedQuery + // Add the query string to the path + if queryString != "" { + urlPath = urlPath + "?" + queryString } // Read request body if present @@ -166,8 +228,7 @@ func (h *DicomHandler) ForwardRequest(w http.ResponseWriter, r *http.Request) { r.Method, urlPath, headers, - bodyBytes, - ) + bodyBytes) if err != nil { h.logger.Error("Request forwarding failed", zap.Error(err))