connected-to-google
This commit is contained in:
19
Dockerfile
19
Dockerfile
@@ -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"]
|
||||||
@@ -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
12
docker-compose.yaml
Normal 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
20
go.mod
@@ -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
52
go.sum
@@ -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=
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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
BIN
ohif-proxy
Executable file
Binary file not shown.
69
test/http/test.http
Normal file
69
test/http/test.http
Normal 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
|
||||||
Reference in New Issue
Block a user