connected-to-google

This commit is contained in:
mario
2025-04-07 15:46:07 +07:00
parent f340bc5916
commit 9b8e0260f3
15 changed files with 282 additions and 286 deletions

View File

@@ -1,5 +1,5 @@
# Build stage # Build stage
FROM golang:1.18-alpine AS builder FROM golang:1.22 AS builder
# Set working directory # Set working directory
WORKDIR /app WORKDIR /app
@@ -14,10 +14,10 @@ RUN go mod download
COPY . . COPY . .
# Build the application # Build the application
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o dicom-proxy cmd/server/main.go RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o ohif-proxy cmd/server/main.go
# Final stage # Final stage
FROM alpine:3.15 FROM alpine:3.18
# Install CA certificates for HTTPS connections # Install CA certificates for HTTPS connections
RUN apk --no-cache add ca-certificates RUN apk --no-cache add ca-certificates
@@ -26,19 +26,14 @@ RUN apk --no-cache add ca-certificates
WORKDIR /app WORKDIR /app
# Copy binary from build stage # Copy binary from build stage
COPY --from=builder /app/dicom-proxy . COPY --from=builder /app/ohif-proxy .
# Copy configuration # Copy configuration and create dirs
COPY config/config.yaml ./config/ COPY config/config.yaml ./config/
# Create directory for credentials
RUN mkdir -p /app/credentials RUN mkdir -p /app/credentials
# Expose port # Expose port
EXPOSE 8080 EXPOSE 5555
# Set environment variable for config file location
ENV CONFIG_FILE=/app/config/config.yaml
# Run the application # Run the application
ENTRYPOINT ["./dicom-proxy"] CMD ["./ohif-proxy"]

View File

@@ -1,5 +1,5 @@
server: server:
port: 8080 port: 5555
read_timeout_seconds: 30 read_timeout_seconds: 30
write_timeout_seconds: 30 write_timeout_seconds: 30
idle_timeout_seconds: 60 idle_timeout_seconds: 60
@@ -7,12 +7,11 @@ server:
log_level: "info" # debug, info, warn, error log_level: "info" # debug, info, warn, error
google: google:
project_id: "your-project-id" project_id: "ohifproxy" # Replace with your GCP project ID
location: "asia-southeast2" location: "asia-southeast2" # Match your dataset region
dataset: "dicom-storage" dataset: "sas-storage" # Your dataset name
dicom_store: "ohif" dicom_store: "store-1" # Your DICOM store name
credentials_path: "./credentials/service-account.json" credentials_path: "./credentials/service-account.json"
# CORS settings - origins that are allowed to access the API
allowed_origins: allowed_origins:
- "*" # Allow all origins (you may want to restrict this in production) - "*" # For development; restrict this in production

12
docker-compose.yaml Normal file
View File

@@ -0,0 +1,12 @@
version: '3.8'
services:
ohif-proxy:
build: .
ports:
- "5555:5555"
volumes:
- ./config:/app/config
- ./credentials:/app/credentials
environment:
- LOG_LEVEL=debug

20
go.mod
View File

@@ -3,7 +3,8 @@ module devone.aplikasi.web.id/gitea/mario/go-ohif-proxy
go 1.19 go 1.19
require ( require (
github.com/gin-gonic/gin v1.9.1 github.com/go-chi/chi/v5 v5.2.1
github.com/go-chi/cors v1.2.1
github.com/spf13/viper v1.17.0 github.com/spf13/viper v1.17.0
go.uber.org/zap v1.27.0 go.uber.org/zap v1.27.0
golang.org/x/oauth2 v0.13.0 golang.org/x/oauth2 v0.13.0
@@ -13,15 +14,7 @@ require (
require ( require (
cloud.google.com/go/compute v1.23.1 // indirect cloud.google.com/go/compute v1.23.1 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/google/s2a-go v0.1.7 // indirect github.com/google/s2a-go v0.1.7 // indirect
@@ -29,14 +22,8 @@ require (
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect github.com/sagikazarmark/locafero v0.3.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect
@@ -45,11 +32,8 @@ require (
github.com/spf13/cast v1.5.1 // indirect github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
go.opencensus.io v0.24.0 // indirect go.opencensus.io v0.24.0 // indirect
go.uber.org/multierr v1.10.0 // indirect go.uber.org/multierr v1.10.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.14.0 // indirect golang.org/x/crypto v0.14.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.17.0 // indirect golang.org/x/net v0.17.0 // indirect

52
go.sum
View File

@@ -42,13 +42,7 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -68,24 +62,13 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -130,7 +113,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -163,33 +145,19 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -216,22 +184,16 @@ github.com/spf13/viper v1.17.0/go.mod h1:BmMMMLQXSbcHK6KAOiFLz0l5JHrU89OdIRHvsk0
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -249,9 +211,6 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -388,9 +347,7 @@ golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -577,6 +534,5 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@@ -1,32 +1,42 @@
package handlers package handlers
import ( import (
"github.com/gin-gonic/gin" "encoding/json"
"net/http"
"go.uber.org/zap" "go.uber.org/zap"
) )
// AuthHandler handles authentication-related requests // AuthHandler handles authentication requests
type AuthHandler struct { type AuthHandler struct {
logger *zap.Logger logger *zap.Logger
} }
// NewAuthHandler creates a new AuthHandler // NewAuthHandler creates a new auth handler
func NewAuthHandler(logger *zap.Logger) *AuthHandler { func NewAuthHandler(logger *zap.Logger) *AuthHandler {
return &AuthHandler{ return &AuthHandler{
logger: logger, logger: logger,
} }
} }
// Login handles user login // Login handles user login - placeholder for future implementation
func (h *AuthHandler) Login(c *gin.Context) { func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
// TODO: Implement login logic response := map[string]string{
h.logger.Info("Login endpoint hit") "message": "Login functionality will be implemented in a future version",
c.JSON(200, gin.H{"message": "Login not implemented"}) }
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
} }
// Logout handles user logout // Logout handles user logout - placeholder for future implementation
func (h *AuthHandler) Logout(c *gin.Context) { func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) {
// TODO: Implement logout logic response := map[string]string{
h.logger.Info("Logout endpoint hit") "message": "Logout functionality will be implemented in a future version",
c.JSON(200, gin.H{"message": "Logout not implemented"}) }
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
} }

View File

@@ -4,9 +4,10 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"strings"
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/proxy" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/proxy"
"github.com/gin-gonic/gin" "github.com/go-chi/chi/v5"
"go.uber.org/zap" "go.uber.org/zap"
) )
@@ -25,31 +26,49 @@ func NewDicomHandler(client *proxy.Client, logger *zap.Logger) *DicomHandler {
} }
// ForwardRequest forwards the request to Google Healthcare API // ForwardRequest forwards the request to Google Healthcare API
func (h *DicomHandler) ForwardRequest(c *gin.Context) { func (h *DicomHandler) ForwardRequest(w http.ResponseWriter, r *http.Request) {
path := c.Param("path") // Get the path after /dicomWeb
requestType := getRequestType(c.Request.URL.Path) urlPath := chi.URLParam(r, "*")
// If the URL parameter is empty, try to extract it from the URL path
if urlPath == "" {
// Remove /dicomWeb prefix from the URL
prefix := "/dicomWeb"
if strings.HasPrefix(r.URL.Path, prefix) {
urlPath = r.URL.Path[len(prefix):]
}
}
h.logger.Debug("Forwarding request", h.logger.Debug("Forwarding request",
zap.String("path", path), zap.String("path", urlPath),
zap.String("method", c.Request.Method), zap.String("method", r.Method),
zap.String("type", requestType), zap.String("url", r.URL.String()),
) )
// Read request body if present // Read request body if present
var bodyBytes []byte var bodyBytes []byte
if c.Request.Body != nil && c.Request.ContentLength > 0 { if r.Body != nil && r.ContentLength > 0 {
var err error var err error
bodyBytes, err = io.ReadAll(c.Request.Body) bodyBytes, err = io.ReadAll(r.Body)
if err != nil { if err != nil {
h.logger.Error("Failed to read request body", zap.Error(err)) h.logger.Error("Failed to read request body", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read request body"}) http.Error(w, "Failed to read request body", http.StatusInternalServerError)
return return
} }
} }
// Copy query parameters
queryParams := r.URL.Query().Encode()
if queryParams != "" {
if !strings.HasPrefix(urlPath, "/") {
urlPath = "/" + urlPath
}
urlPath = urlPath + "?" + queryParams
}
// Get request headers // Get request headers
headers := make(map[string]string) headers := make(map[string]string)
for k, v := range c.Request.Header { for k, v := range r.Header {
if len(v) > 0 { if len(v) > 0 {
headers[k] = v[0] headers[k] = v[0]
} }
@@ -57,45 +76,25 @@ func (h *DicomHandler) ForwardRequest(c *gin.Context) {
// Forward the request to Healthcare API // Forward the request to Healthcare API
response, err := h.client.ForwardRequest( response, err := h.client.ForwardRequest(
c.Request.Context(), r.Context(),
c.Request.Method, r.Method,
requestType, urlPath,
path,
headers, headers,
bodyBytes, bodyBytes,
) )
if err != nil { if err != nil {
h.logger.Error("Request forwarding failed", zap.Error(err)) h.logger.Error("Request forwarding failed", zap.Error(err))
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Request failed: %v", err)}) http.Error(w, fmt.Sprintf("Request failed: %v", err), http.StatusInternalServerError)
return return
} }
// Set response headers // Set response headers
for k, v := range response.Headers { for k, v := range response.Headers {
c.Header(k, v) w.Header().Set(k, v)
} }
// Set status and write response body // Set status and write response body
c.Status(response.StatusCode) w.WriteHeader(response.StatusCode)
c.Writer.Write(response.Body) w.Write(response.Body)
}
// getRequestType determines if the request is WADO, QIDO or STOW
func getRequestType(path string) string {
if len(path) < 9 {
return "unknown"
}
pathComponent := path[9:] // Remove "/dicomWeb/"
if len(pathComponent) >= 4 && pathComponent[:4] == "wado" {
return "wado"
} else if len(pathComponent) >= 4 && pathComponent[:4] == "qido" {
return "qido"
} else if len(pathComponent) >= 4 && pathComponent[:4] == "stow" {
return "stow"
}
return "unknown"
} }

View File

@@ -1,12 +1,19 @@
package handlers package handlers
import ( import (
"encoding/json"
"net/http" "net/http"
"time"
"github.com/gin-gonic/gin"
) )
// HealthCheck is a simple health check handler // HealthCheck provides a simple health check endpoint
func HealthCheck(c *gin.Context) { func HealthCheck(w http.ResponseWriter, r *http.Request) {
c.JSON(http.StatusOK, gin.H{"status": "ok"}) response := map[string]interface{}{
"status": "ok",
"timestamp": time.Now().Format(time.RFC3339),
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
} }

View File

@@ -1,94 +1,61 @@
package middleware package middleware
import ( import (
"net/http"
"time" "time"
"github.com/gin-gonic/gin" "github.com/go-chi/chi/v5/middleware"
"go.uber.org/zap" "go.uber.org/zap"
) )
// Logger middleware adds request logging // Logger middleware adds request logging
func Logger(logger *zap.Logger) gin.HandlerFunc { func Logger(logger *zap.Logger) func(http.Handler) http.Handler {
return func(c *gin.Context) { return func(next http.Handler) http.Handler {
start := time.Now() return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
path := c.Request.URL.Path start := time.Now()
query := c.Request.URL.RawQuery
// Process request // Create a wrapped response writer to capture the status code
c.Next() ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
// Calculate request time // Process request
latency := time.Since(start) next.ServeHTTP(ww, r)
// Get status // Calculate request time
status := c.Writer.Status() latency := time.Since(start)
// Log request details // Log request details
logger.Info("API Request", logger.Info("API Request",
zap.String("method", c.Request.Method), zap.String("method", r.Method),
zap.String("path", path), zap.String("path", r.URL.Path),
zap.String("query", query), zap.String("query", r.URL.RawQuery),
zap.Int("status", status), zap.Int("status", ww.Status()),
zap.Duration("latency", latency), zap.Duration("latency", latency),
zap.String("ip", c.ClientIP()), zap.String("ip", r.RemoteAddr),
zap.String("user-agent", c.Request.UserAgent()), zap.String("user-agent", r.UserAgent()),
) zap.Int("bytes", ww.BytesWritten()),
)
})
} }
} }
// AuditLog middleware records detailed information about DICOM requests // AuditLog middleware records detailed information about DICOM requests
func AuditLog(logger *zap.Logger) gin.HandlerFunc { func AuditLog(logger *zap.Logger) func(http.Handler) http.Handler {
return func(c *gin.Context) { return func(next http.Handler) http.Handler {
// We'll extract user info here when auth is implemented return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
userID := "anonymous" // Extract user info (placeholder for now)
if id, exists := c.Get("userID"); exists { userID := "TODO: userID"
userID = id.(string)
}
path := c.Request.URL.Path // Process request
method := c.Request.Method ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor)
next.ServeHTTP(ww, r)
// Process request // Audit log after request completes
c.Next() logger.Info("DICOM Access",
zap.String("userID", userID),
// Audit log after request completes zap.String("action", r.Method),
logger.Info("DICOM Access", zap.String("resource", r.URL.Path),
zap.String("userID", userID), zap.Int("status", ww.Status()),
zap.String("action", method), )
zap.String("resource", path), })
zap.Int("status", c.Writer.Status()),
)
}
}
// CORS middleware to handle cross-origin requests
func CORS(allowedOrigins []string) gin.HandlerFunc {
return func(c *gin.Context) {
origin := c.Request.Header.Get("Origin")
// Check if origin is allowed
allowed := false
for _, o := range allowedOrigins {
if o == "*" || o == origin {
allowed = true
break
}
}
// Set CORS headers if allowed
if allowed {
c.Header("Access-Control-Allow-Origin", origin)
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Content-Length, Accept-Encoding, Authorization")
c.Header("Access-Control-Allow-Credentials", "true")
}
// Handle preflight requests
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
} }
} }

View File

@@ -1,36 +1,44 @@
package api package api
import ( import (
"net/http"
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/config" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/config"
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/handlers" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/handlers"
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/api/middleware" apiMiddleware "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/auth"
"devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/proxy" "devone.aplikasi.web.id/gitea/mario/go-ohif-proxy/internal/proxy"
"github.com/gin-gonic/gin" "github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors"
"go.uber.org/zap" "go.uber.org/zap"
) )
// TODO: Refactor dari gin dengan chi
// SetupRouter configures and returns the API router // SetupRouter configures and returns the API router
func SetupRouter(cfg *config.Config, logger *zap.Logger) *gin.Engine { func SetupRouter(cfg *config.Config, logger *zap.Logger) http.Handler {
// Set Gin mode r := chi.NewRouter()
if cfg.LogLevel == "debug" {
gin.SetMode(gin.DebugMode)
} else {
gin.SetMode(gin.ReleaseMode)
}
// Initialize the router // Built-in Chi middleware
r := gin.New() r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(middleware.Recoverer)
r.Use(middleware.StripSlashes)
// Add middleware // Custom middleware
r.Use(gin.Recovery()) r.Use(apiMiddleware.Logger(logger))
r.Use(middleware.Logger(logger))
r.Use(middleware.CORS(cfg.AllowedOrigins)) // CORS middleware
r.Use(cors.Handler(cors.Options{
AllowedOrigins: cfg.AllowedOrigins,
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token"},
ExposedHeaders: []string{"Link"},
AllowCredentials: true,
MaxAge: 300,
}))
// Setup health check // Setup health check
r.GET("/health", handlers.HealthCheck) r.Get("/health", handlers.HealthCheck)
// Initialize Google auth client // Initialize Google auth client
googleAuth, err := auth.NewGoogleClient(cfg.Google.CredentialsPath) googleAuth, err := auth.NewGoogleClient(cfg.Google.CredentialsPath)
@@ -41,32 +49,24 @@ func SetupRouter(cfg *config.Config, logger *zap.Logger) *gin.Engine {
// Initialize Healthcare API client // Initialize Healthcare API client
healthcareClient := proxy.NewClient(googleAuth, cfg.Google) healthcareClient := proxy.NewClient(googleAuth, cfg.Google)
// DICOM Web routes // DICOM Web routes - simplified approach
dicomGroup := r.Group("/dicomWeb") r.Route("/dicomWeb", func(r chi.Router) {
{ // Add audit logging middleware to DICOM routes
r.Use(apiMiddleware.AuditLog(logger))
// Create single handler for all DICOM requests
dicomHandler := handlers.NewDicomHandler(healthcareClient, logger) dicomHandler := handlers.NewDicomHandler(healthcareClient, logger)
// Add audit logging middleware to DICOM routes // Catch all routes under /dicomWeb and forward them
dicomGroup.Use(middleware.AuditLog(logger)) r.HandleFunc("/*", dicomHandler.ForwardRequest)
})
// WADO routes
dicomGroup.Any("/wado/*path", dicomHandler.ForwardRequest)
// QIDO routes
dicomGroup.Any("/qido/*path", dicomHandler.ForwardRequest)
// STOW routes
dicomGroup.Any("/stow/*path", dicomHandler.ForwardRequest)
}
// Future auth routes for doctors // Future auth routes for doctors
authGroup := r.Group("/auth") r.Route("/auth", func(r chi.Router) {
{
// Auth handlers will be implemented later
authHandler := handlers.NewAuthHandler(logger) authHandler := handlers.NewAuthHandler(logger)
authGroup.POST("/login", authHandler.Login) r.Post("/login", authHandler.Login)
authGroup.POST("/logout", authHandler.Logout) r.Post("/logout", authHandler.Logout)
} })
return r return r
} }

View File

@@ -3,6 +3,7 @@ package auth
import ( import (
"context" "context"
"fmt" "fmt"
"os"
"time" "time"
"golang.org/x/oauth2" "golang.org/x/oauth2"
@@ -11,12 +12,10 @@ import (
"google.golang.org/api/option" "google.golang.org/api/option"
) )
// TODO: Ganti Auth dengan lib Goth
// GoogleClient handles authentication with Google APIs // GoogleClient handles authentication with Google APIs
type GoogleClient struct { type GoogleClient struct {
credentialsPath string credentialsPath string
tokenSource *google.Credentials tokenSource oauth2.TokenSource
} }
// NewGoogleClient creates a new Google authentication client // NewGoogleClient creates a new Google authentication client
@@ -26,24 +25,33 @@ func NewGoogleClient(credentialsPath string) (*GoogleClient, error) {
} }
// Initialize on creation to validate credentials // Initialize on creation to validate credentials
if _, err := client.getToken(); err != nil { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// Try reading the credentials file
credBytes, err := os.ReadFile(credentialsPath)
if err != nil {
return nil, fmt.Errorf("failed to read credentials file: %w", err)
}
// Create credentials from the JSON key file
creds, err := google.CredentialsFromJSON(ctx, credBytes, healthcare.CloudPlatformScope)
if err != nil {
return nil, fmt.Errorf("failed to initialize Google client: %w", err) return nil, fmt.Errorf("failed to initialize Google client: %w", err)
} }
client.tokenSource = creds.TokenSource
return client, nil return client, nil
} }
// GetAccessToken returns a valid access token for Google APIs // GetAccessToken returns a valid access token for Google APIs
func (c *GoogleClient) GetAccessToken() (string, error) { func (c *GoogleClient) GetAccessToken() (string, error) {
tokenSource, err := c.getToken() _, cancel := context.WithTimeout(context.Background(), 10*time.Second)
if err != nil { defer cancel()
return "", err
}
// Retrieve the token using the Token() method token, err := c.tokenSource.Token()
token, err := tokenSource.Token()
if err != nil { if err != nil {
return "", fmt.Errorf("failed to retrieve token: %w", err) return "", fmt.Errorf("failed to get access token: %w", err)
} }
return token.AccessToken, nil return token.AccessToken, nil
@@ -58,21 +66,3 @@ func (c *GoogleClient) GetHealthcareClient(ctx context.Context) (*healthcare.Ser
return healthcare.NewService(ctx, opts...) return healthcare.NewService(ctx, opts...)
} }
// getToken retrieves a token from the credentials file
func (c *GoogleClient) getToken() (oauth2.TokenSource, error) { // Change return type to oauth2.TokenSource
if c.tokenSource != nil {
return c.tokenSource.TokenSource, nil // Access the TokenSource field
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
credentials, err := google.FindDefaultCredentials(ctx, healthcare.CloudPlatformScope)
if err != nil {
return nil, fmt.Errorf("failed to get default credentials: %w", err)
}
c.tokenSource = credentials
return credentials.TokenSource, nil // Return the TokenSource field
}

View File

@@ -45,17 +45,25 @@ type Response struct {
} }
// ForwardRequest forwards a request to Google Healthcare API // ForwardRequest forwards a request to Google Healthcare API
func (c *Client) ForwardRequest(ctx context.Context, method, requestType, path string, headers map[string]string, body []byte) (*Response, error) { func (c *Client) ForwardRequest(ctx context.Context, method, path string, headers map[string]string, body []byte) (*Response, error) {
// Get access token
token, err := c.googleAuth.GetAccessToken() token, err := c.googleAuth.GetAccessToken()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get access token: %w", err) return nil, fmt.Errorf("failed to get access token: %w", err)
} }
// Build URL // Build URL - Simplified to exactly match OHIF's expected structure
baseURL := fmt.Sprintf("https://healthcare.googleapis.com/v1/projects/%s/locations/%s/datasets/%s/dicomStores/%s/dicomWeb", baseURL := fmt.Sprintf("https://healthcare.googleapis.com/v1/projects/%s/locations/%s/datasets/%s/dicomStores/%s/dicomWeb",
c.projectID, c.location, c.dataset, c.dicomStore) c.projectID, c.location, c.dataset, c.dicomStore)
fullURL := fmt.Sprintf("%s/%s%s", baseURL, requestType, path)
// Ensure path starts with /
if len(path) > 0 && path[0] != '/' {
path = "/" + path
}
fullURL := baseURL + path
// Log the full URL for debugging
fmt.Printf("Requesting URL: %s\n", fullURL)
// Create request // Create request
req, err := http.NewRequestWithContext(ctx, method, fullURL, bytes.NewReader(body)) req, err := http.NewRequestWithContext(ctx, method, fullURL, bytes.NewReader(body))

BIN
ohif-proxy Executable file

Binary file not shown.

69
test/http/test.http Normal file
View File

@@ -0,0 +1,69 @@
### Local OHIF Proxy Test File
@baseUrl = http://localhost:5555
### 1. Health Check
# Verifies that the proxy server is running
GET {{baseUrl}}/health
Accept: application/json
### 2. QIDO-RS: Search for Studies
# Returns all studies (should return a list of DICOM studies if any exist)
GET {{baseUrl}}/dicomWeb/studies
Accept: application/dicom+json
### 3. QIDO-RS: Search for Studies with Patient Name
# Returns studies matching patient name (replace SMITH with a name in your dataset)
GET {{baseUrl}}/dicomWeb/studies?limit=10&offset=0&fuzzymatching=true&includefield=00081030%2C00080060%2C00080090&00080090=DR.%20HERWINDO%20RIDWAN%2C%20SP.OT
Accept: application/dicom+json
### 4. QIDO-RS: Search for Studies with Date Range
# Returns studies within a date range
GET {{baseUrl}}/dicomWeb/studies?StudyDate=20200101-20230101
Accept: application/dicom+json
### 5. QIDO-RS: Search for Series in a Study
# Replace STUDY_INSTANCE_UID with an actual Study UID from your data
# (Run test #2 first and copy a StudyInstanceUID from the response)
GET {{baseUrl}}/dicomWeb/studies/STUDY_INSTANCE_UID/series
Accept: application/dicom+json
### 6. QIDO-RS: Search for Instances in a Series
# Replace STUDY_INSTANCE_UID and SERIES_INSTANCE_UID with actual values
GET {{baseUrl}}/dicomWeb/studies/STUDY_INSTANCE_UID/series/SERIES_INSTANCE_UID/instances
Accept: application/dicom+json
### 7. WADO-RS: Retrieve Study Metadata
# Replace STUDY_INSTANCE_UID with an actual Study UID
GET {{baseUrl}}/dicomWeb/studies/STUDY_INSTANCE_UID/metadata
Accept: application/dicom+json
### 8. WADO-RS: Retrieve Series Metadata
# Replace STUDY_INSTANCE_UID and SERIES_INSTANCE_UID with actual values
GET {{baseUrl}}/dicomWeb/studies/STUDY_INSTANCE_UID/series/SERIES_INSTANCE_UID/metadata
Accept: application/dicom+json
### 9. WADO-RS: Retrieve Instance Metadata
# Replace STUDY_INSTANCE_UID, SERIES_INSTANCE_UID and SOP_INSTANCE_UID with actual values
GET {{baseUrl}}/dicomWeb/studies/1.2.826.0.1.3680043.9.7307.1.202503196393.01/series/1.2.826.0.1.3680043.2.1545.1.2.1.7.20250319.100353.734.4/metadata
Accept: */*
### 10. WADO-RS: Retrieve Instance
# Replace STUDY_INSTANCE_UID, SERIES_INSTANCE_UID and SOP_INSTANCE_UID with actual values
GET {{baseUrl}}/dicomWeb/studies/STUDY_INSTANCE_UID/series/SERIES_INSTANCE_UID/instances/SOP_INSTANCE_UID
Accept: application/dicom
### 11. WADO-RS: Retrieve Frame
# Replace STUDY_INSTANCE_UID, SERIES_INSTANCE_UID, SOP_INSTANCE_UID with actual values
# This retrieves frame #1 from a multiframe image
GET {{baseUrl}}/dicomWeb/studies/1.2.826.0.1.3680043.9.7307.1.202503196393.01/series/1.2.826.0.1.3680043.2.1545.1.2.1.7.20250319.100353.734.4/instances/1.2.826.0.1.3680043.2.1545.1.2.1.7.20250319.100353.1.5/frames/1
Accept: */*
### 12. WADO-RS: Retrieve Frame as JPEG
# Replace STUDY_INSTANCE_UID, SERIES_INSTANCE_UID, SOP_INSTANCE_UID with actual values
# This retrieves frame #1 as a rendered JPEG image
GET {{baseUrl}}/dicomWeb/studies/1.2.826.0.1.3680043.9.7307.1.202503196393.01/series/1.2.826.0.1.3680043.2.1545.1.2.1.7.20250319.100353.734.4/instances/1.2.826.0.1.3680043.2.1545.1.2.1.7.20250319.100353.1.5/frames/1/rendered
Accept: image/jpeg
####
GET {{baseUrl}}/dicomWeb/studies?limit=101&offset=0&fuzzymatching=true&includefield=00081030,00080060&StudyInstanceUID=1.2.826.0.1.3680043.9.7307.1.202503196393.01