Spaces:
Running
Running
Update
Browse files
Dockerfile
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
FROM golang:1.23-alpine AS builder
|
3 |
|
4 |
WORKDIR /home/appuser
|
5 |
-
COPY go.mod .
|
6 |
RUN go mod download
|
7 |
COPY main.go .
|
8 |
|
|
|
2 |
FROM golang:1.23-alpine AS builder
|
3 |
|
4 |
WORKDIR /home/appuser
|
5 |
+
COPY go.mod go.sum .
|
6 |
RUN go mod download
|
7 |
COPY main.go .
|
8 |
|
go.mod
CHANGED
@@ -1,3 +1,37 @@
|
|
1 |
module example.com/m/v2
|
2 |
|
3 |
go 1.23.5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
module example.com/m/v2
|
2 |
|
3 |
go 1.23.5
|
4 |
+
|
5 |
+
require (
|
6 |
+
github.com/gin-contrib/cors v1.7.5
|
7 |
+
github.com/gin-gonic/gin v1.10.0
|
8 |
+
)
|
9 |
+
|
10 |
+
require (
|
11 |
+
github.com/bytedance/sonic v1.13.2 // indirect
|
12 |
+
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
13 |
+
github.com/cloudwego/base64x v0.1.5 // indirect
|
14 |
+
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
15 |
+
github.com/gin-contrib/sse v1.0.0 // indirect
|
16 |
+
github.com/go-playground/locales v0.14.1 // indirect
|
17 |
+
github.com/go-playground/universal-translator v0.18.1 // indirect
|
18 |
+
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
19 |
+
github.com/goccy/go-json v0.10.5 // indirect
|
20 |
+
github.com/json-iterator/go v1.1.12 // indirect
|
21 |
+
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
22 |
+
github.com/kr/text v0.2.0 // indirect
|
23 |
+
github.com/leodido/go-urn v1.4.0 // indirect
|
24 |
+
github.com/mattn/go-isatty v0.0.20 // indirect
|
25 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
26 |
+
github.com/modern-go/reflect2 v1.0.2 // indirect
|
27 |
+
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
28 |
+
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
29 |
+
github.com/ugorji/go/codec v1.2.12 // indirect
|
30 |
+
golang.org/x/arch v0.15.0 // indirect
|
31 |
+
golang.org/x/crypto v0.36.0 // indirect
|
32 |
+
golang.org/x/net v0.38.0 // indirect
|
33 |
+
golang.org/x/sys v0.31.0 // indirect
|
34 |
+
golang.org/x/text v0.23.0 // indirect
|
35 |
+
google.golang.org/protobuf v1.36.6 // indirect
|
36 |
+
gopkg.in/yaml.v3 v3.0.1 // indirect
|
37 |
+
)
|
go.sum
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
2 |
+
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
3 |
+
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
4 |
+
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
5 |
+
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
6 |
+
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
7 |
+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
8 |
+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
9 |
+
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
10 |
+
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
11 |
+
github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk=
|
12 |
+
github.com/gin-contrib/cors v1.7.5/go.mod h1:4q3yi7xBEDDWKapjT2o1V7mScKDDr8k+jZ0fSquGoy0=
|
13 |
+
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
14 |
+
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
15 |
+
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
16 |
+
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
17 |
+
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
18 |
+
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
19 |
+
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
20 |
+
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
21 |
+
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
22 |
+
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
|
23 |
+
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
24 |
+
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
25 |
+
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
26 |
+
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
27 |
+
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
28 |
+
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
29 |
+
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
30 |
+
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
31 |
+
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
32 |
+
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
33 |
+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
34 |
+
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
35 |
+
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
36 |
+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
37 |
+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
38 |
+
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
39 |
+
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
40 |
+
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
41 |
+
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
42 |
+
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
43 |
+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
44 |
+
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
45 |
+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
46 |
+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
47 |
+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
48 |
+
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
49 |
+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
50 |
+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
51 |
+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
52 |
+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
53 |
+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
54 |
+
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
55 |
+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
56 |
+
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
57 |
+
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
58 |
+
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
59 |
+
golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
60 |
+
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
61 |
+
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
62 |
+
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
63 |
+
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
64 |
+
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
65 |
+
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
66 |
+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
67 |
+
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
68 |
+
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
69 |
+
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
|
70 |
+
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
71 |
+
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
72 |
+
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
73 |
+
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
74 |
+
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
75 |
+
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
76 |
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
77 |
+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
78 |
+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
79 |
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
80 |
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
81 |
+
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
82 |
+
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
main.go
CHANGED
@@ -1,10 +1,149 @@
|
|
1 |
-
// print hello world
|
2 |
package main
|
3 |
|
4 |
import (
|
5 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
)
|
7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
func main() {
|
9 |
-
|
10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
package main
|
2 |
|
3 |
import (
|
4 |
+
"io"
|
5 |
+
"log"
|
6 |
+
"net/http"
|
7 |
+
"net/url"
|
8 |
+
|
9 |
+
"strings"
|
10 |
+
"time"
|
11 |
+
|
12 |
+
"github.com/gin-contrib/cors"
|
13 |
+
"github.com/gin-gonic/gin"
|
14 |
)
|
15 |
|
16 |
+
// SecureHeaders sets security-related HTTP headers
|
17 |
+
func SecureHeaders() gin.HandlerFunc {
|
18 |
+
return func(c *gin.Context) {
|
19 |
+
c.Writer.Header().Set("X-Content-Type-Options", "nosniff")
|
20 |
+
c.Next()
|
21 |
+
}
|
22 |
+
}
|
23 |
+
|
24 |
+
// ValidReqPath checks the X-Req-Path is safe for proxying
|
25 |
+
func ValidReqPath(path string) bool {
|
26 |
+
if !strings.HasPrefix(path, "https://") {
|
27 |
+
return false
|
28 |
+
}
|
29 |
+
if strings.Contains(path, "..") {
|
30 |
+
return false
|
31 |
+
}
|
32 |
+
if strings.TrimSpace(path) == "" {
|
33 |
+
return false
|
34 |
+
}
|
35 |
+
return true
|
36 |
+
}
|
37 |
+
|
38 |
+
// Extract the root domain (e.g., "example.com" from "sub.example.com")
|
39 |
+
func extractRootDomain(urlStr string) (string, error) {
|
40 |
+
parsedURL, err := url.Parse(urlStr)
|
41 |
+
if err != nil {
|
42 |
+
return "", err
|
43 |
+
}
|
44 |
+
|
45 |
+
hostname := parsedURL.Hostname()
|
46 |
+
parts := strings.Split(hostname, ".")
|
47 |
+
|
48 |
+
// Handle special cases with fewer than 2 parts
|
49 |
+
if len(parts) < 2 {
|
50 |
+
return hostname, nil
|
51 |
+
}
|
52 |
+
|
53 |
+
// Get the last two parts as the root domain
|
54 |
+
domain := parts[len(parts)-2] + "." + parts[len(parts)-1]
|
55 |
+
return domain, nil
|
56 |
+
}
|
57 |
+
|
58 |
+
// IsValidBackendURL validates that the constructed URL belongs to the expected backend
|
59 |
+
func IsValidBackendURL(targetURL, backend string) bool {
|
60 |
+
targetApexDomain, err := extractRootDomain(targetURL)
|
61 |
+
if err != nil {
|
62 |
+
return false
|
63 |
+
}
|
64 |
+
|
65 |
+
backendApexDomain, err := extractRootDomain(targetURL)
|
66 |
+
if err != nil {
|
67 |
+
return false
|
68 |
+
}
|
69 |
+
|
70 |
+
return targetApexDomain == backendApexDomain
|
71 |
+
}
|
72 |
+
|
73 |
func main() {
|
74 |
+
r := gin.Default()
|
75 |
+
r.Use(SecureHeaders())
|
76 |
+
|
77 |
+
// CORS config for /api/req
|
78 |
+
api := r.Group("/api")
|
79 |
+
api.Use(cors.New(cors.Config{
|
80 |
+
AllowOrigins: []string{"*"},
|
81 |
+
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
82 |
+
AllowHeaders: []string{"Origin", "Content-Type", "Authorization", "X-Req-Path"},
|
83 |
+
ExposeHeaders: []string{"Content-Length"},
|
84 |
+
AllowCredentials: true,
|
85 |
+
MaxAge: 12 * time.Hour,
|
86 |
+
}))
|
87 |
+
|
88 |
+
// Secure proxy forward
|
89 |
+
api.Any("/req", func(c *gin.Context) {
|
90 |
+
const backend = "https://deeploy.ml"
|
91 |
+
|
92 |
+
reqPath := c.GetHeader("X-Req-Path")
|
93 |
+
if !ValidReqPath(reqPath) {
|
94 |
+
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid X-Req-Path"})
|
95 |
+
return
|
96 |
+
}
|
97 |
+
|
98 |
+
targetURL := reqPath
|
99 |
+
if !IsValidBackendURL(targetURL, backend) {
|
100 |
+
c.JSON(http.StatusForbidden, gin.H{"error": "Target not allowed"})
|
101 |
+
return
|
102 |
+
}
|
103 |
+
|
104 |
+
// Prepare the proxied request
|
105 |
+
req, err := http.NewRequest(c.Request.Method, targetURL, c.Request.Body)
|
106 |
+
if err != nil {
|
107 |
+
c.JSON(http.StatusInternalServerError, gin.H{"error": "Request creation failed"})
|
108 |
+
return
|
109 |
+
}
|
110 |
+
req.Header = c.Request.Header.Clone()
|
111 |
+
req.Header.Del("X-Req-Path") // Don't forward custom header
|
112 |
+
|
113 |
+
client := &http.Client{
|
114 |
+
Timeout: 60 * time.Second,
|
115 |
+
}
|
116 |
+
resp, err := client.Do(req)
|
117 |
+
if err != nil {
|
118 |
+
log.Printf("Error proxying request: Method=%s URL=%s Headers=%v Error=%v",
|
119 |
+
req.Method, req.URL, req.Header, err)
|
120 |
+
c.JSON(http.StatusBadGateway, gin.H{"error": "Backend request failed."})
|
121 |
+
return
|
122 |
+
}
|
123 |
+
|
124 |
+
defer resp.Body.Close()
|
125 |
+
|
126 |
+
// Copy headers & status from the backend
|
127 |
+
for k, v := range resp.Header {
|
128 |
+
for _, vv := range v {
|
129 |
+
c.Writer.Header().Add(k, vv)
|
130 |
+
}
|
131 |
+
}
|
132 |
+
c.Status(resp.StatusCode)
|
133 |
+
io.Copy(c.Writer, resp.Body)
|
134 |
+
})
|
135 |
+
|
136 |
+
// Serve static files from ./frontend, no directory listing
|
137 |
+
r.StaticFS("/app", gin.Dir("./frontend/", false))
|
138 |
+
|
139 |
+
// Route to serve parsed HTML template partials
|
140 |
+
r.GET("/partials", func(c *gin.Context) {
|
141 |
+
// Load HTML templates from ./partials folder
|
142 |
+
r.LoadHTMLGlob("./frontend/partials/*")
|
143 |
+
// Render the template by name
|
144 |
+
c.HTML(http.StatusOK, "index_partial.html", gin.H{})
|
145 |
+
})
|
146 |
+
|
147 |
+
log.Println("Serving on http://localhost:8080")
|
148 |
+
r.Run(":8080")
|
149 |
+
}
|