Files
dicom-iso/internal/handler/iso.go
2026-06-05 08:11:44 +07:00

134 lines
3.9 KiB
Go

package handler
import (
"encoding/json"
"fmt"
"log/slog"
"net/http"
"os"
"strings"
"mkiso-server/internal/service"
)
// writeJSON is a helper to write a JSON response.
func writeJSON(w http.ResponseWriter, status int, v interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(v)
}
// DownloadSingle handles GET /api/iso/download?accession_number=X
// Returns an ISO file as application/octet-stream.
func DownloadSingle(isoSvc *service.ISOService) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
acc := r.URL.Query().Get("accession_number")
if acc == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "missing accession_number"})
return
}
acc = strings.TrimSpace(acc)
slog.Info("handling download single", "accession", acc)
item, err := isoSvc.GenerateISO(r.Context(), acc)
if err != nil {
slog.Error("ISO generation failed", "accession", acc, "error", err)
status := http.StatusInternalServerError
msg := "ISO creation failed"
if strings.Contains(err.Error(), "no DICOM data") {
status = http.StatusNotFound
msg = "no DICOM data for accession_number"
}
writeJSON(w, status, map[string]string{
"error": msg,
"detail": err.Error(),
})
return
}
defer item.Cleanup()
// Stream ISO file
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, item.Filename))
data, err := os.ReadFile(item.Path)
if err != nil {
slog.Error("read ISO file failed", "path", item.Path, "error", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to read ISO"})
return
}
w.Write(data)
slog.Info("ISO download completed", "accession", acc, "size", len(data))
}
}
// DownloadMultiple handles GET /api/iso/download-multiple?accession_numbers=X,Y,Z
// Returns an ISO file as application/octet-stream.
func DownloadMultiple(isoSvc *service.ISOService) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
raw := r.URL.Query().Get("accession_numbers")
if raw == "" {
// Also check for "accession_number" with comma (backward compat)
raw = r.URL.Query().Get("accession_number")
}
if raw == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "missing accession_numbers"})
return
}
accs := parseAccessions(raw)
if len(accs) == 0 {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "empty accession_number list"})
return
}
slog.Info("handling download multiple", "accessions", accs)
item, err := isoSvc.GenerateISOMultiple(r.Context(), accs)
if err != nil {
slog.Error("ISO generation failed", "accessions", accs, "error", err)
status := http.StatusInternalServerError
msg := "ISO creation failed"
if strings.Contains(err.Error(), "no DICOM data") {
status = http.StatusNotFound
msg = "no DICOM data for accession_numbers"
}
writeJSON(w, status, map[string]string{
"error": msg,
"detail": err.Error(),
})
return
}
defer item.Cleanup()
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, item.Filename))
data, err := os.ReadFile(item.Path)
if err != nil {
slog.Error("read ISO file failed", "path", item.Path, "error", err)
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to read ISO"})
return
}
w.Write(data)
slog.Info("ISO download completed (multiple)", "accessions", accs, "size", len(data))
}
}
// parseAccessions parses a comma-separated list of accession numbers,
// trimming whitespace and filtering empty entries.
func parseAccessions(raw string) []string {
parts := strings.Split(raw, ",")
var result []string
for _, p := range parts {
p = strings.TrimSpace(p)
if p != "" {
result = append(result, p)
}
}
return result
}