diff --git a/.gitignore b/.gitignore index bfc9736..5350ce3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Binary files bin/ dicom-proxy +build/* # Credentials credentials/*.json @@ -18,4 +19,4 @@ vendor/ # OS specific files .DS_Store -Thumbs.db \ No newline at end of file +Thumbs.db diff --git a/README.md b/README.md index 309ff25..fef07d9 100644 --- a/README.md +++ b/README.md @@ -183,4 +183,211 @@ make test --- Untuk informasi lebih lanjut tentang OHIF: [https://ohif.org/](https://ohif.org/) -Untuk informasi lebih lanjut tentang Google Cloud Healthcare API: [https://cloud.google.com/healthcare](https://cloud.google.com/healthcare) \ No newline at end of file +Untuk informasi lebih lanjut tentang Google Cloud Healthcare API: [https://cloud.google.com/healthcare](https://cloud.google.com/healthcare) + +# GO-OHIF-Proxy Architecture + +## Project Structure +``` +go-ohif-proxy/ +├── cmd/ +│ └── server/ # Application entry point +│ └── main.go # Main function and application start +│ +├── config/ # Configuration +│ ├── config.go # Config structure definitions +│ └── config.yaml # YAML configuration file +│ +├── credentials/ # Authentication credentials +│ └── service-account.json # Google service account credentials +│ +├── internal/ # Internal packages (not importable) +│ ├── api/ # API implementation +│ │ ├── handlers/ # HTTP request handlers +│ │ │ ├── auth.go # Authentication handlers +│ │ │ ├── dicom.go # DICOM request handlers +│ │ │ └── healthcheck.go # Health check endpoint +│ │ │ +│ │ ├── middleware/ # HTTP middleware +│ │ │ ├── auth.go # Authentication middleware +│ │ │ └── logging.go # Request logging middleware +│ │ │ +│ │ ├── models/ # Data models +│ │ │ └── user.go # User and authentication models +│ │ │ +│ │ ├── repository/ # Data access layer +│ │ │ ├── interfaces.go # Repository interfaces +│ │ │ └── mysql_repository.go # MySQL implementation +│ │ │ +│ │ ├── service/ # Business logic +│ │ │ ├── auth_service.go # Authentication service +│ │ │ └── db_repository.go # Legacy repository code (to be removed) +│ │ │ +│ │ └── routes.go # API route definitions +│ │ +│ ├── auth/ # Authentication utilities +│ │ ├── google.go # Google authentication client +│ │ └── jwt.go # JWT generation and validation +│ │ +│ ├── logger/ # Logging utilities +│ │ └── logger.go # Logger configuration +│ │ +│ └── proxy/ # DICOM web proxy functionality +│ └── client.go # Google Healthcare API client +│ +├── pkg/ # Public packages (importable) +│ +├── test/ # Test files +│ └── http/ # HTTP test requests +│ ├── ohif-flow.http # OHIF workflow tests +│ └── test.http # General API tests +│ +├── docker-compose.yaml # Docker Compose configuration +├── Dockerfile # Docker build instructions +├── go.mod # Go module definition +├── go.sum # Go module checksums +├── Makefile # Build automation +└── README.md # Project documentation +``` + +## Architecture Diagram + +```mermaid +graph TD + subgraph Client + OHIF[OHIF Viewer] + end + + subgraph API_Gateway + Router[Chi Router] + Auth_MW[Auth Middleware] + Logging_MW[Logging Middleware] + CORS_MW[CORS Middleware] + PatientView_MW[Patient View Restriction] + end + + subgraph Services + AuthService[Auth Service] + DicomService[DICOM Service] + end + + subgraph Repositories + UserRepo[User Repository] + TokenRepo[Token Repository] + PatientRepo[Patient Repository] + DoctorRepo[Doctor Repository] + end + + subgraph External + GoogleHealthcare[Google Healthcare API] + MySQL[MySQL Database] + end + + subgraph Utilities + JWTManager[JWT Manager] + GoogleAuth[Google Auth Client] + Logger[Logger] + end + + OHIF -->|HTTP Requests| Router + Router --> Auth_MW + Auth_MW --> Logging_MW + Logging_MW --> CORS_MW + CORS_MW --> PatientView_MW + PatientView_MW --> DicomService + + Router -->|Auth Routes| AuthService + AuthService -->|Uses| JWTManager + AuthService -->|Uses| UserRepo + AuthService -->|Uses| TokenRepo + + DicomService -->|Uses| GoogleHealthcare + DicomService -->|Uses| PatientRepo + + UserRepo -->|Implements| MySQL + TokenRepo -->|Implements| MySQL + PatientRepo -->|Implements| MySQL + DoctorRepo -->|Implements| MySQL + + DicomService -->|Uses| GoogleAuth + GoogleAuth -->|Authenticates| GoogleHealthcare +``` + +## Authentication Flow + +```mermaid +sequenceDiagram + participant Client + participant API as API Gateway + participant AuthService + participant Repo as Repository + participant JWT as JWT Manager + participant DB as Database + + Client->>API: POST /auth/login + API->>AuthService: Login(email, password) + + alt Database Auth Enabled + AuthService->>Repo: GetUserByEmail(email) + Repo->>DB: SELECT * FROM users + DB->>Repo: User Data + Repo->>AuthService: User Object + AuthService->>JWT: Generate Tokens + JWT->>AuthService: Access + Refresh Tokens + AuthService->>Repo: StoreRefreshToken() + Repo->>DB: INSERT INTO refresh_tokens + else Hardcoded Auth + AuthService->>JWT: Generate Tokens + JWT->>AuthService: Access + Refresh Tokens + end + + AuthService->>API: Tokens + User Info + API->>Client: Auth Response + + Note over Client,API: Later - Protected Request + + Client->>API: GET /dicomWeb/* with Bearer Token + API->>AuthService: ValidateToken() + AuthService->>JWT: ParseToken() + JWT->>AuthService: Claim Data + AuthService->>API: User Context + API->>Client: Protected Resource +``` + +## DICOM Request Flow + +```mermaid +sequenceDiagram + participant Client + participant API as API Gateway + participant Auth as Auth Middleware + participant PatientMW as Patient Restriction MW + participant DicomHandler + participant GCP as Google Healthcare API + + Client->>API: GET /dicomWeb/studies/{studyUID} + + alt Whitelisted Path + API->>DicomHandler: Forward Request (Skip Auth) + else Protected Path + API->>Auth: Check Authentication + Auth->>API: User Context + API->>PatientMW: Check Access Rights + + alt Patient Role + PatientMW->>Repo: IsStudyAssignedToPatient() + Repo->>PatientMW: Access Result + alt Study Assigned + PatientMW->>DicomHandler: Forward Request + else Study Not Assigned + PatientMW->>Client: 403 Forbidden + end + else Doctor Role + PatientMW->>DicomHandler: Forward Request + end + end + + DicomHandler->>GCP: Forward Request to DICOM Store + GCP->>DicomHandler: DICOM Data + DicomHandler->>Client: DICOM Response +``` \ No newline at end of file diff --git a/internal/api/routes.go b/internal/api/routes.go index 89dfc3b..3fab51a 100644 --- a/internal/api/routes.go +++ b/internal/api/routes.go @@ -31,12 +31,16 @@ func SetupRouter(cfg *config.Config, logger *zap.Logger) http.Handler { 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"}, - ExposedHeaders: []string{"Link"}, + 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 { diff --git a/ohif-proxy b/ohif-proxy deleted file mode 100755 index 201b6ef..0000000 Binary files a/ohif-proxy and /dev/null differ diff --git a/ohif-proxy-old b/ohif-proxy-old deleted file mode 100755 index 1866000..0000000 Binary files a/ohif-proxy-old and /dev/null differ