169 lines
5.6 KiB
Go
169 lines
5.6 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/config"
|
|
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/handlers"
|
|
apiMiddleware "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/middleware"
|
|
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/service"
|
|
"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"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
"github.com/go-chi/cors"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// SetupRouter configures and returns the API router
|
|
func SetupRouter(cfg *config.Config, logger *zap.Logger) http.Handler {
|
|
r := chi.NewRouter()
|
|
|
|
// Base middleware
|
|
r.Use(middleware.RequestID)
|
|
r.Use(middleware.RealIP)
|
|
r.Use(middleware.Recoverer)
|
|
r.Use(apiMiddleware.Logger(logger))
|
|
|
|
// CORS configuration
|
|
r.Use(cors.Handler(cors.Options{
|
|
AllowedOrigins: []string{"*"}, // In production, restrict this to your frontend domains
|
|
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
|
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token", "X-Requested-With"},
|
|
ExposedHeaders: []string{"Link", "Content-Length", "Content-Disposition", "Content-Type"},
|
|
AllowCredentials: true,
|
|
MaxAge: 300, // Maximum value not ignored by any of major browsers
|
|
}))
|
|
|
|
r.Options("/*", func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
})
|
|
|
|
// Initialize Google auth client for proxy
|
|
googleAuth, err := auth.NewGoogleClient(cfg.Google.CredentialsPath)
|
|
if err != nil {
|
|
logger.Fatal("Failed to initialize Google auth client", zap.Error(err))
|
|
}
|
|
|
|
// Initialize Healthcare API client
|
|
healthcareClient := proxy.NewClient(googleAuth, cfg.Google)
|
|
|
|
// Initialize JWT auth service
|
|
jwtSecret := cfg.Auth.JWTSecret
|
|
if jwtSecret == "" {
|
|
jwtSecret = "vQ6PQqUyh7pBNOytClgN+Nw1XBq7F8Qo6VP3VwIqvHY=" // Default from your example, should be set in config
|
|
}
|
|
|
|
// Convert config values to time.Duration
|
|
accessExpiry := time.Duration(cfg.Auth.AccessTokenExpiry) * time.Minute
|
|
refreshExpiry := time.Duration(cfg.Auth.RefreshTokenExpiry) * time.Hour
|
|
|
|
// Create JWT manager with config values
|
|
jwtManager := auth.NewJWTManager(jwtSecret, accessExpiry, refreshExpiry)
|
|
authService := service.NewAuthService(jwtManager)
|
|
|
|
// Public routes that don't require authentication
|
|
r.Group(func(r chi.Router) {
|
|
// Health check
|
|
r.Get("/health", handlers.HealthCheck)
|
|
|
|
// Authentication endpoints
|
|
r.Route("/auth", func(r chi.Router) {
|
|
authHandler := handlers.NewAuthHandler(logger, authService)
|
|
r.Post("/login", authHandler.Login)
|
|
r.Post("/refresh", authHandler.RefreshToken)
|
|
r.Post("/logout", authHandler.Logout)
|
|
})
|
|
})
|
|
|
|
// Protected routes that require authentication
|
|
r.Group(func(r chi.Router) {
|
|
// Apply authentication middleware
|
|
r.Use(apiMiddleware.Auth(authService, logger))
|
|
|
|
// DICOM Web routes
|
|
r.Route("/dicomWeb", func(r chi.Router) {
|
|
// Add audit logging middleware to DICOM routes
|
|
r.Use(apiMiddleware.AuditLog(logger))
|
|
|
|
// Add patient view restriction for patient role
|
|
r.Use(apiMiddleware.PatientViewRestriction(logger))
|
|
|
|
// Create handler for all DICOM requests
|
|
dicomHandler := handlers.NewDicomHandler(healthcareClient, logger)
|
|
|
|
// Common routes for studies with role-specific handling
|
|
r.Route("/studies", func(r chi.Router) {
|
|
// StudyInstanceUID parameter routes - accessible by all roles
|
|
r.Get("/{studyInstanceUID}", dicomHandler.ForwardRequest) // Study details
|
|
r.Get("/{studyInstanceUID}/series", dicomHandler.ForwardRequest) // Series list for study
|
|
|
|
// Deep hierarchy routes - accessible by patients and all doctors
|
|
r.Get("/{studyInstanceUID}/series/{seriesUID}/metadata", dicomHandler.ForwardRequest)
|
|
r.Get("/{studyInstanceUID}/series/{seriesUID}/instances/{instanceUID}/frames/{frame}", dicomHandler.ForwardRequest)
|
|
|
|
// Query routes - accessible by all roles
|
|
r.Get("/", dicomHandler.ForwardRequest) // Study list with filters
|
|
})
|
|
|
|
// Expertise doctors have full access to all DICOM endpoints
|
|
r.With(apiMiddleware.RoleRequired("expertise_doctor")).HandleFunc("/*", dicomHandler.ForwardRequest)
|
|
})
|
|
})
|
|
|
|
// * DEBUG PURPOSE ONLY *
|
|
r.Get("/debug/token", func(w http.ResponseWriter, r *http.Request) {
|
|
authHeader := r.Header.Get("Authorization")
|
|
if authHeader == "" {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
json.NewEncoder(w).Encode(map[string]string{
|
|
"error": "No Authorization header provided",
|
|
})
|
|
return
|
|
}
|
|
|
|
bearerToken := strings.Split(authHeader, " ")
|
|
if len(bearerToken) != 2 || strings.ToLower(bearerToken[0]) != "bearer" {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
json.NewEncoder(w).Encode(map[string]string{
|
|
"error": "Invalid Authorization format",
|
|
})
|
|
return
|
|
}
|
|
|
|
token := bearerToken[1]
|
|
claims, err := authService.ValidateToken(token)
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusUnauthorized)
|
|
json.NewEncoder(w).Encode(map[string]string{
|
|
"error": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
json.NewEncoder(w).Encode(map[string]interface{}{
|
|
"status": "valid",
|
|
"token_type": claims.TokenType,
|
|
"user": map[string]string{
|
|
"id": claims.UserID,
|
|
"email": claims.Email,
|
|
"role": claims.Role,
|
|
"name": claims.UserName,
|
|
},
|
|
"patient_info": map[string]string{
|
|
"patient_id": claims.PatientID,
|
|
"study_iuid": claims.StudyIUID,
|
|
"accession_number": claims.AccessionNumber,
|
|
},
|
|
})
|
|
})
|
|
|
|
return r
|
|
}
|