tebakaja commited on
Commit
542878f
0 Parent(s):

feat: Crafting Load Balancer Proxy

Browse files
Dockerfile ADDED
File without changes
Makefile ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ENDPOINT=http://192.168.137.1:7860
2
+
3
+ start: go run main.go
4
+
5
+ haproxy-test:
6
+ haproxy -f ./haproxy/haproxy.cfg
7
+
8
+ nginx-test:
9
+ nginx -c nginx.conf
10
+
11
+ traefik-test:
12
+ traefik \
13
+ --configFile=./traefik/traefik.yaml \
14
+ --entryPoints.web.address=":7860" \
15
+ --entryPoints.websecure.address=":443" \
16
+ --entryPoints.web.http.redirections.entryPoint.to=websecure \
17
+ --entryPoints.web.http.redirections.entryPoint.scheme=https \
18
+ --api.dashboard=true \
19
+ --api.insecure=false
20
+
21
+ crypto-list-test:
22
+ curl -X GET $(ENDPOINT)/crypto/lists
23
+
24
+ crypto-prediction-test:
25
+ curl -X POST $(ENDPOINT)/crypto/prediction \
26
+ -H "Content-Type: application/json" \
27
+ -d "{\"days\": 2, \"currency\": \"BTC-USD\"}"
28
+
go.mod ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module tebakaja_lb_proxy
2
+
3
+ go 1.20
4
+
5
+ require (
6
+ github.com/andybalholm/brotli v1.1.0 // indirect
7
+ github.com/gofiber/fiber/v2 v2.52.5 // indirect
8
+ github.com/google/uuid v1.6.0 // indirect
9
+ github.com/klauspost/compress v1.17.9 // indirect
10
+ github.com/mattn/go-colorable v0.1.13 // indirect
11
+ github.com/mattn/go-isatty v0.0.20 // indirect
12
+ github.com/mattn/go-runewidth v0.0.15 // indirect
13
+ github.com/philhofer/fwd v1.1.2 // indirect
14
+ github.com/rivo/uniseg v0.4.7 // indirect
15
+ github.com/tinylib/msgp v1.1.8 // indirect
16
+ github.com/valyala/bytebufferpool v1.0.0 // indirect
17
+ github.com/valyala/fasthttp v1.55.0 // indirect
18
+ github.com/valyala/tcplisten v1.0.0 // indirect
19
+ golang.org/x/sys v0.22.0 // indirect
20
+ )
go.sum ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
2
+ github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
3
+ github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
4
+ github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
5
+ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
6
+ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
7
+ github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
8
+ github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
9
+ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
10
+ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
11
+ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
12
+ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
13
+ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
14
+ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
15
+ github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
16
+ github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
17
+ github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
18
+ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
19
+ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
20
+ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
21
+ github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
22
+ github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
23
+ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
24
+ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
25
+ github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8=
26
+ github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM=
27
+ github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
28
+ github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
29
+ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
30
+ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
31
+ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
32
+ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
33
+ golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
34
+ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
35
+ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
36
+ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
37
+ golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
38
+ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
39
+ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
40
+ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
41
+ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
42
+ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
43
+ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
44
+ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
45
+ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
46
+ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
47
+ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
48
+ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
49
+ golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
50
+ golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
51
+ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
52
+ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
53
+ golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
54
+ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
55
+ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
56
+ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
57
+ golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
58
+ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
59
+ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
60
+ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
61
+ golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
62
+ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
haproxy/Dockerfile ADDED
File without changes
haproxy/haproxy.cfg ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ global
2
+ daemon
3
+ maxconn 1024
4
+ log /dev/log local0
5
+ log /dev/log local1 notice
6
+ stats timeout 30s
7
+
8
+ ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
9
+
10
+ defaults
11
+ log global
12
+ mode http
13
+ option httplog
14
+ option dontlognull
15
+ retries 3
16
+ option redispatch
17
+ timeout http-request 10s
18
+ timeout queue 10s
19
+ timeout connect 10s
20
+ timeout client 10s
21
+ timeout server 10s
22
+ timeout check 10s
23
+
24
+ frontend http_front
25
+ bind *:80
26
+ bind :::80
27
+ mode http
28
+
29
+ stick-table type ip size 100k expire 30s store http_req_rate(10s)
30
+ http-request track-sc0 src
31
+ http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }
32
+
33
+ acl is_websocket hdr(Upgrade) -i websocket
34
+ acl is_websocket hdr_beg(Host) -i ws
35
+
36
+ use_backend websocket_chatting_back if is_websocket
37
+ default_backend websocket_chatting_back
38
+
39
+ frontend https_front
40
+ bind *:443 ssl crt F:\command\haproxy\certificates.pem
41
+ bind :::443 ssl crt F:\command\haproxy\certificates.pem
42
+ mode http
43
+
44
+ acl is_websocket hdr(Upgrade) -i websocket
45
+ acl is_websocket hdr_beg(Host) -i ws
46
+
47
+ stick-table type ip size 100k expire 30s store http_req_rate(10s)
48
+ http-request track-sc0 src
49
+ http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }
50
+
51
+ use_backend websocket_chatting_back if is_websocket
52
+ default_backend websocket_chatting_back
53
+
54
+ backend websocket_chatting_back
55
+ mode http
56
+ timeout queue 10s
57
+ balance roundrobin
58
+ option forwardfor
59
+ http-request set-header X-Forwarded-Port %[dst_port]
60
+ http-request add-header X-Forwarded-Proto https if { ssl_fc }
61
+ server websocket_svc_1 qywok-cryptocurrency-prediction.hf.space:443/crypto/lists ssl verify required check
main.go ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package main
2
+
3
+ import (
4
+ "fmt"
5
+ "log"
6
+
7
+ "github.com/gofiber/fiber/v2"
8
+
9
+ proxy "tebakaja_lb_proxy/proxy"
10
+ crypto_proxy "tebakaja_lb_proxy/proxy/crypto"
11
+ )
12
+
13
+ func main() {
14
+ app := fiber.New()
15
+
16
+ app.Use(proxy.LoggingMiddleware)
17
+ app.Use(proxy.RateLimiter())
18
+
19
+ cryptoGroup := app.Group("/crypto")
20
+ cryptoGroup.Get("/lists", crypto_proxy.CryptoListsHandler(&crypto_proxy.CryptoServiceImpl{}))
21
+ cryptoGroup.Post("/prediction", crypto_proxy.CryptoPredictionHandler(&crypto_proxy.CryptoServiceImpl{}))
22
+
23
+ port := 7860
24
+ log.Fatal(app.Listen(fmt.Sprintf("0.0.0.0:%d", port)))
25
+ }
nginx/Dockerfile ADDED
File without changes
nginx/nginx.conf ADDED
File without changes
proxy/crypto/constants.go ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ package crypto
2
+
3
+ const CRYPTO_ENDPOINT = "https://qywok-cryptocurrency-prediction.hf.space/crypto"
proxy/crypto/interfaces.go ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ package crypto
2
+
3
+ import "context"
4
+
5
+ type CryptoService interface {
6
+ CryptoListsService(ctx context.Context) (ApiResponse, error)
7
+ CryptoPredictionService(ctx context.Context, req PredictionRequest) (ApiResponse, error)
8
+ }
9
+
10
+ type CryptoServiceImpl struct{}
proxy/crypto/list_handler.go ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package crypto
2
+
3
+ import (
4
+ "log"
5
+ "sync"
6
+ "time"
7
+ "context"
8
+ "net/http"
9
+
10
+ "github.com/gofiber/fiber/v2"
11
+ )
12
+
13
+
14
+ /*
15
+ * --- Cryptocurrency Prediction Model Lists Handler ---
16
+ */
17
+ func CryptoListsHandler(service CryptoService) fiber.Handler {
18
+ return func(c *fiber.Ctx) error {
19
+ ctx, cancel := context.WithTimeout(c.Context(), 120*time.Second)
20
+ defer cancel()
21
+
22
+ ch := make(chan ApiResponse, 1)
23
+ var wg sync.WaitGroup
24
+ wg.Add(1)
25
+
26
+ go func() {
27
+ defer wg.Done()
28
+
29
+ apiResponse, err := service.CryptoListsService(ctx)
30
+ if err != nil {
31
+ log.Printf("[%s] %v", time.Now().Format("2006-01-02 15:04:05"), err)
32
+ ch <- apiResponse
33
+ return
34
+ }
35
+
36
+ ch <- apiResponse
37
+ }()
38
+
39
+ go func() {
40
+ wg.Wait()
41
+ close(ch)
42
+ }()
43
+
44
+ select {
45
+ case apiResponse, ok := <-ch:
46
+ if !ok {
47
+ return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
48
+ "error": "Failed to get a response from the server",
49
+ })
50
+ }
51
+ return c.Status(apiResponse.StatusCode).JSON(apiResponse)
52
+
53
+ case <-ctx.Done():
54
+ log.Printf("[%s] Timeout: %v", time.Now().Format("2006-01-02 15:04:05"), ctx.Err())
55
+ return c.Status(http.StatusRequestTimeout).JSON(fiber.Map{
56
+ "error": "Request timeout",
57
+ })
58
+ }
59
+ }
60
+ }
proxy/crypto/list_service.go ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package crypto
2
+
3
+ import (
4
+ "fmt"
5
+ "context"
6
+ "net/http"
7
+ "encoding/json"
8
+ )
9
+
10
+
11
+ /*
12
+ * --- Cryptocurrency Prediction Model Lists Service ---
13
+ */
14
+ func (s *CryptoServiceImpl) CryptoListsService(ctx context.Context) (ApiResponse, error) {
15
+ endpoint := fmt.Sprintf("%s/lists", CRYPTO_ENDPOINT)
16
+ req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil)
17
+ if err != nil {
18
+ return ApiResponse{
19
+ Message: fmt.Sprintf("Failed to make request: %v", err),
20
+ StatusCode: http.StatusInternalServerError,
21
+ }, err
22
+ }
23
+
24
+ resp, err := http.DefaultClient.Do(req)
25
+ if err != nil {
26
+ return ApiResponse{
27
+ Message: fmt.Sprintf("Failed to make request: %v", err),
28
+ StatusCode: http.StatusInternalServerError,
29
+ }, err
30
+ }
31
+ defer resp.Body.Close()
32
+
33
+ if resp.StatusCode != http.StatusOK {
34
+ return ApiResponse{
35
+ Message: fmt.Sprintf("Request failed: %d", resp.StatusCode),
36
+ StatusCode: resp.StatusCode,
37
+ }, fmt.Errorf("request failed: %d", resp.StatusCode)
38
+ }
39
+
40
+ var apiResponse ApiResponse
41
+ if err := json.NewDecoder(resp.Body).Decode(&apiResponse); err != nil {
42
+ return ApiResponse{
43
+ Message: fmt.Sprintf("Failed to parse JSON response: %v", err),
44
+ StatusCode: http.StatusInternalServerError,
45
+ }, err
46
+ }
47
+
48
+ return apiResponse, nil
49
+ }
proxy/crypto/prediction_handler.go ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package crypto
2
+
3
+ import (
4
+ "fmt"
5
+ "log"
6
+ "sync"
7
+ "time"
8
+ "context"
9
+ "net/http"
10
+
11
+ "github.com/gofiber/fiber/v2"
12
+ )
13
+
14
+
15
+ /*
16
+ * --- Cryptocurrency Prediction Handler ---
17
+ */
18
+ func CryptoPredictionHandler(service CryptoService) fiber.Handler {
19
+ return func(c *fiber.Ctx) error {
20
+ ctx, cancel := context.WithTimeout(c.Context(), 120*time.Second)
21
+ defer cancel()
22
+
23
+ ch := make(chan ApiResponse, 1)
24
+ var wg sync.WaitGroup
25
+ wg.Add(1)
26
+
27
+ go func() {
28
+ defer wg.Done()
29
+
30
+ var predictionReq PredictionRequest
31
+ if err := c.BodyParser(&predictionReq); err != nil {
32
+ log.Printf("[%s] Failed to parse request body: %v",
33
+ time.Now().Format("2006-01-02 15:04:05"), err)
34
+
35
+ ch <- ApiResponse{
36
+ Message: fmt.Sprintf("Failed to parse request body: %v", err),
37
+ StatusCode: http.StatusBadRequest,
38
+ }
39
+ return
40
+ }
41
+
42
+ apiResponse, err := service.CryptoPredictionService(ctx, predictionReq)
43
+ if err != nil {
44
+ log.Printf("[%s] %v", time.Now().Format("2006-01-02 15:04:05"), err)
45
+ ch <- apiResponse
46
+ return
47
+ }
48
+
49
+ ch <- apiResponse
50
+ }()
51
+
52
+ go func() {
53
+ wg.Wait()
54
+ close(ch)
55
+ }()
56
+
57
+ select {
58
+ case apiResponse, ok := <-ch:
59
+ if !ok {
60
+ return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
61
+ "error": "Failed to get a response from the server",
62
+ })
63
+ }
64
+ return c.Status(apiResponse.StatusCode).JSON(apiResponse)
65
+
66
+ case <-ctx.Done():
67
+ log.Printf("[%s] Timeout: %v", time.Now().Format("2006-01-02 15:04:05"), ctx.Err())
68
+ return c.Status(http.StatusRequestTimeout).JSON(fiber.Map{
69
+ "error": "Request timeout",
70
+ })
71
+ }
72
+ }
73
+ }
proxy/crypto/prediction_service.go ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package crypto
2
+
3
+ import (
4
+ "fmt"
5
+ "bytes"
6
+ "context"
7
+ "net/http"
8
+ "encoding/json"
9
+ )
10
+
11
+
12
+ /*
13
+ * --- Cryptocurrency Prediction Service ---
14
+ */
15
+ func (s *CryptoServiceImpl) CryptoPredictionService(ctx context.Context, req PredictionRequest) (ApiResponse, error) {
16
+ reqBody, err := json.Marshal(req)
17
+ if err != nil {
18
+ return ApiResponse{
19
+ Message: fmt.Sprintf("Failed to marshal request body: %v", err),
20
+ StatusCode: http.StatusInternalServerError,
21
+ }, err
22
+ }
23
+
24
+ endpoint := fmt.Sprintf("%s/prediction", CRYPTO_ENDPOINT)
25
+ httpReq, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewBuffer(reqBody))
26
+ if err != nil {
27
+ return ApiResponse{
28
+ Message: fmt.Sprintf("Failed to make request: %v", err),
29
+ StatusCode: http.StatusInternalServerError,
30
+ }, err
31
+ }
32
+ httpReq.Header.Set("Content-Type", "application/json")
33
+
34
+ resp, err := http.DefaultClient.Do(httpReq)
35
+ if err != nil {
36
+ return ApiResponse{
37
+ Message: fmt.Sprintf("Failed to make request: %v", err),
38
+ StatusCode: http.StatusInternalServerError,
39
+ }, err
40
+ }
41
+ defer resp.Body.Close()
42
+
43
+ if resp.StatusCode != http.StatusOK {
44
+ return ApiResponse{
45
+ Message: fmt.Sprintf("Request failed: %d", resp.StatusCode),
46
+ StatusCode: resp.StatusCode,
47
+ }, fmt.Errorf("request failed: %d", resp.StatusCode)
48
+ }
49
+
50
+ var apiResponse PredictionResponse
51
+ if err := json.NewDecoder(resp.Body).Decode(&apiResponse); err != nil {
52
+ return ApiResponse{
53
+ Message: fmt.Sprintf("Failed to parse JSON response: %v", err),
54
+ StatusCode: http.StatusInternalServerError,
55
+ }, err
56
+ }
57
+
58
+ return ApiResponse{
59
+ Message: apiResponse.Message,
60
+ StatusCode: apiResponse.StatusCode,
61
+ Data: apiResponse.Data,
62
+ }, nil
63
+ }
proxy/crypto/structs.go ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package crypto
2
+
3
+ type ApiResponse struct {
4
+ Message string `json:"message"`
5
+ StatusCode int `json:"status_code"`
6
+ Data interface{} `json:"data"`
7
+ }
8
+
9
+ type PredictionRequest struct {
10
+ Days int `json:"days"`
11
+ Currency string `json:"currency"`
12
+ }
13
+
14
+ type PredictionResponse struct {
15
+ Message string `json:"message"`
16
+ StatusCode int `json:"status_code"`
17
+
18
+ Data struct {
19
+ Currency string `json:"currency"`
20
+ Predictions struct {
21
+ Actuals []struct {
22
+ Date string `json:"date"`
23
+ Price float64 `json:"price"`
24
+ } `json:"actuals"`
25
+ Predictions []struct {
26
+ Date string `json:"date"`
27
+ Price float64 `json:"price"`
28
+ } `json:"predictions"`
29
+ } `json:"predictions"`
30
+ } `json:"data"`
31
+ }
proxy/loadbalancers.go ADDED
@@ -0,0 +1 @@
 
 
1
+ package proxy
proxy/middlewares.go ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package proxy
2
+
3
+ import (
4
+ "log"
5
+ "net/http"
6
+ "time"
7
+
8
+ "github.com/gofiber/fiber/v2"
9
+ "github.com/gofiber/fiber/v2/middleware/limiter"
10
+ )
11
+
12
+ // Middleware untuk logging setiap request
13
+ func LoggingMiddleware(c *fiber.Ctx) error {
14
+ start := time.Now()
15
+ err := c.Next()
16
+ log.Printf("[%s] %s %s - %d %s in %v", time.Now().Format("2006-01-02 15:04:05"), c.Method(), c.Path(), c.Response().StatusCode(), http.StatusText(c.Response().StatusCode()), time.Since(start))
17
+ return err
18
+ }
19
+
20
+ // Middleware rate limiter: 200 requests per menit per IP
21
+ func RateLimiter() func(*fiber.Ctx) error {
22
+ return limiter.New(limiter.Config{
23
+ Max: 200,
24
+ Expiration: 1 * time.Minute,
25
+ KeyGenerator: func(c *fiber.Ctx) string {
26
+ return c.IP()
27
+ },
28
+ LimitReached: func(c *fiber.Ctx) error {
29
+ return c.Status(http.StatusTooManyRequests).JSON(fiber.Map{
30
+ "error": "Rate limit exceeded",
31
+ })
32
+ },
33
+ })
34
+ }
proxy/national_currency/constants.go ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ package national_currency
2
+
3
+ const CRYPTO_ENDPOINT = "https://qywok-cryptocurrency-prediction.hf.space/crypto"
proxy/national_currency/interfaces.go ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ package national_currency
2
+
3
+ import "context"
4
+
5
+ type CryptoService interface {
6
+ CryptoListsService(ctx context.Context) (ApiResponse, error)
7
+ CryptoPredictionService(ctx context.Context, req PredictionRequest) (ApiResponse, error)
8
+ }
9
+
10
+ type CryptoServiceImpl struct{}
proxy/national_currency/list_handler.go ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package national_currency
2
+
3
+ import (
4
+ "log"
5
+ "sync"
6
+ "time"
7
+ "context"
8
+ "net/http"
9
+
10
+ "github.com/gofiber/fiber/v2"
11
+ )
12
+
13
+
14
+ /*
15
+ * --- Cryptocurrency Prediction Model Lists Handler ---
16
+ */
17
+ func CryptoListsHandler(service CryptoService) fiber.Handler {
18
+ return func(c *fiber.Ctx) error {
19
+ ctx, cancel := context.WithTimeout(c.Context(), 120*time.Second)
20
+ defer cancel()
21
+
22
+ ch := make(chan ApiResponse, 1)
23
+ var wg sync.WaitGroup
24
+ wg.Add(1)
25
+
26
+ go func() {
27
+ defer wg.Done()
28
+
29
+ apiResponse, err := service.CryptoListsService(ctx)
30
+ if err != nil {
31
+ log.Printf("[%s] %v", time.Now().Format("2006-01-02 15:04:05"), err)
32
+ ch <- apiResponse
33
+ return
34
+ }
35
+
36
+ ch <- apiResponse
37
+ }()
38
+
39
+ go func() {
40
+ wg.Wait()
41
+ close(ch)
42
+ }()
43
+
44
+ select {
45
+ case apiResponse, ok := <-ch:
46
+ if !ok {
47
+ return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
48
+ "error": "Failed to get a response from the server",
49
+ })
50
+ }
51
+ return c.Status(apiResponse.StatusCode).JSON(apiResponse)
52
+
53
+ case <-ctx.Done():
54
+ log.Printf("[%s] Timeout: %v", time.Now().Format("2006-01-02 15:04:05"), ctx.Err())
55
+ return c.Status(http.StatusRequestTimeout).JSON(fiber.Map{
56
+ "error": "Request timeout",
57
+ })
58
+ }
59
+ }
60
+ }
proxy/national_currency/list_service.go ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package national_currency
2
+
3
+ import (
4
+ "fmt"
5
+ "context"
6
+ "net/http"
7
+ "encoding/json"
8
+ )
9
+
10
+
11
+ /*
12
+ * --- Cryptocurrency Prediction Model Lists Service ---
13
+ */
14
+ func (s *CryptoServiceImpl) CryptoListsService(ctx context.Context) (ApiResponse, error) {
15
+ endpoint := fmt.Sprintf("%s/lists", CRYPTO_ENDPOINT)
16
+ req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil)
17
+ if err != nil {
18
+ return ApiResponse{
19
+ Message: fmt.Sprintf("Failed to make request: %v", err),
20
+ StatusCode: http.StatusInternalServerError,
21
+ }, err
22
+ }
23
+
24
+ resp, err := http.DefaultClient.Do(req)
25
+ if err != nil {
26
+ return ApiResponse{
27
+ Message: fmt.Sprintf("Failed to make request: %v", err),
28
+ StatusCode: http.StatusInternalServerError,
29
+ }, err
30
+ }
31
+ defer resp.Body.Close()
32
+
33
+ if resp.StatusCode != http.StatusOK {
34
+ return ApiResponse{
35
+ Message: fmt.Sprintf("Request failed: %d", resp.StatusCode),
36
+ StatusCode: resp.StatusCode,
37
+ }, fmt.Errorf("request failed: %d", resp.StatusCode)
38
+ }
39
+
40
+ var apiResponse ApiResponse
41
+ if err := json.NewDecoder(resp.Body).Decode(&apiResponse); err != nil {
42
+ return ApiResponse{
43
+ Message: fmt.Sprintf("Failed to parse JSON response: %v", err),
44
+ StatusCode: http.StatusInternalServerError,
45
+ }, err
46
+ }
47
+
48
+ return apiResponse, nil
49
+ }
proxy/national_currency/prediction_handler.go ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package national_currency
2
+
3
+ import (
4
+ "fmt"
5
+ "log"
6
+ "sync"
7
+ "time"
8
+ "context"
9
+ "net/http"
10
+
11
+ "github.com/gofiber/fiber/v2"
12
+ )
13
+
14
+
15
+ /*
16
+ * --- Cryptocurrency Prediction Handler ---
17
+ */
18
+ func CryptoPredictionHandler(service CryptoService) fiber.Handler {
19
+ return func(c *fiber.Ctx) error {
20
+ ctx, cancel := context.WithTimeout(c.Context(), 120*time.Second)
21
+ defer cancel()
22
+
23
+ ch := make(chan ApiResponse, 1)
24
+ var wg sync.WaitGroup
25
+ wg.Add(1)
26
+
27
+ go func() {
28
+ defer wg.Done()
29
+
30
+ var predictionReq PredictionRequest
31
+ if err := c.BodyParser(&predictionReq); err != nil {
32
+ log.Printf("[%s] Failed to parse request body: %v",
33
+ time.Now().Format("2006-01-02 15:04:05"), err)
34
+
35
+ ch <- ApiResponse{
36
+ Message: fmt.Sprintf("Failed to parse request body: %v", err),
37
+ StatusCode: http.StatusBadRequest,
38
+ }
39
+ return
40
+ }
41
+
42
+ apiResponse, err := service.CryptoPredictionService(ctx, predictionReq)
43
+ if err != nil {
44
+ log.Printf("[%s] %v", time.Now().Format("2006-01-02 15:04:05"), err)
45
+ ch <- apiResponse
46
+ return
47
+ }
48
+
49
+ ch <- apiResponse
50
+ }()
51
+
52
+ go func() {
53
+ wg.Wait()
54
+ close(ch)
55
+ }()
56
+
57
+ select {
58
+ case apiResponse, ok := <-ch:
59
+ if !ok {
60
+ return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
61
+ "error": "Failed to get a response from the server",
62
+ })
63
+ }
64
+ return c.Status(apiResponse.StatusCode).JSON(apiResponse)
65
+
66
+ case <-ctx.Done():
67
+ log.Printf("[%s] Timeout: %v", time.Now().Format("2006-01-02 15:04:05"), ctx.Err())
68
+ return c.Status(http.StatusRequestTimeout).JSON(fiber.Map{
69
+ "error": "Request timeout",
70
+ })
71
+ }
72
+ }
73
+ }
proxy/national_currency/prediction_service.go ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package national_currency
2
+
3
+ import (
4
+ "fmt"
5
+ "bytes"
6
+ "context"
7
+ "net/http"
8
+ "encoding/json"
9
+ )
10
+
11
+
12
+ /*
13
+ * --- Cryptocurrency Prediction Service ---
14
+ */
15
+ func (s *CryptoServiceImpl) CryptoPredictionService(ctx context.Context, req PredictionRequest) (ApiResponse, error) {
16
+ reqBody, err := json.Marshal(req)
17
+ if err != nil {
18
+ return ApiResponse{
19
+ Message: fmt.Sprintf("Failed to marshal request body: %v", err),
20
+ StatusCode: http.StatusInternalServerError,
21
+ }, err
22
+ }
23
+
24
+ endpoint := fmt.Sprintf("%s/prediction", CRYPTO_ENDPOINT)
25
+ httpReq, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewBuffer(reqBody))
26
+ if err != nil {
27
+ return ApiResponse{
28
+ Message: fmt.Sprintf("Failed to make request: %v", err),
29
+ StatusCode: http.StatusInternalServerError,
30
+ }, err
31
+ }
32
+ httpReq.Header.Set("Content-Type", "application/json")
33
+
34
+ resp, err := http.DefaultClient.Do(httpReq)
35
+ if err != nil {
36
+ return ApiResponse{
37
+ Message: fmt.Sprintf("Failed to make request: %v", err),
38
+ StatusCode: http.StatusInternalServerError,
39
+ }, err
40
+ }
41
+ defer resp.Body.Close()
42
+
43
+ if resp.StatusCode != http.StatusOK {
44
+ return ApiResponse{
45
+ Message: fmt.Sprintf("Request failed: %d", resp.StatusCode),
46
+ StatusCode: resp.StatusCode,
47
+ }, fmt.Errorf("request failed: %d", resp.StatusCode)
48
+ }
49
+
50
+ var apiResponse PredictionResponse
51
+ if err := json.NewDecoder(resp.Body).Decode(&apiResponse); err != nil {
52
+ return ApiResponse{
53
+ Message: fmt.Sprintf("Failed to parse JSON response: %v", err),
54
+ StatusCode: http.StatusInternalServerError,
55
+ }, err
56
+ }
57
+
58
+ return ApiResponse{
59
+ Message: apiResponse.Message,
60
+ StatusCode: apiResponse.StatusCode,
61
+ Data: apiResponse.Data,
62
+ }, nil
63
+ }
proxy/national_currency/structs.go ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package national_currency
2
+
3
+ type ApiResponse struct {
4
+ Message string `json:"message"`
5
+ StatusCode int `json:"status_code"`
6
+ Data interface{} `json:"data"`
7
+ }
8
+
9
+ type PredictionRequest struct {
10
+ Days int `json:"days"`
11
+ Currency string `json:"currency"`
12
+ }
13
+
14
+ type PredictionResponse struct {
15
+ Message string `json:"message"`
16
+ StatusCode int `json:"status_code"`
17
+
18
+ Data struct {
19
+ Currency string `json:"currency"`
20
+ Predictions struct {
21
+ Actuals []struct {
22
+ Date string `json:"date"`
23
+ Price float64 `json:"price"`
24
+ } `json:"actuals"`
25
+ Predictions []struct {
26
+ Date string `json:"date"`
27
+ Price float64 `json:"price"`
28
+ } `json:"predictions"`
29
+ } `json:"predictions"`
30
+ } `json:"data"`
31
+ }
proxy/stock/constants.go ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ package stock
2
+
3
+ const CRYPTO_ENDPOINT = "https://qywok-cryptocurrency-prediction.hf.space/crypto"
proxy/stock/interfaces.go ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ package stock
2
+
3
+ import "context"
4
+
5
+ type CryptoService interface {
6
+ CryptoListsService(ctx context.Context) (ApiResponse, error)
7
+ CryptoPredictionService(ctx context.Context, req PredictionRequest) (ApiResponse, error)
8
+ }
9
+
10
+ type CryptoServiceImpl struct{}
proxy/stock/list_handler.go ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package stock
2
+
3
+ import (
4
+ "log"
5
+ "sync"
6
+ "time"
7
+ "context"
8
+ "net/http"
9
+
10
+ "github.com/gofiber/fiber/v2"
11
+ )
12
+
13
+
14
+ /*
15
+ * --- Cryptocurrency Prediction Model Lists Handler ---
16
+ */
17
+ func CryptoListsHandler(service CryptoService) fiber.Handler {
18
+ return func(c *fiber.Ctx) error {
19
+ ctx, cancel := context.WithTimeout(c.Context(), 120*time.Second)
20
+ defer cancel()
21
+
22
+ ch := make(chan ApiResponse, 1)
23
+ var wg sync.WaitGroup
24
+ wg.Add(1)
25
+
26
+ go func() {
27
+ defer wg.Done()
28
+
29
+ apiResponse, err := service.CryptoListsService(ctx)
30
+ if err != nil {
31
+ log.Printf("[%s] %v", time.Now().Format("2006-01-02 15:04:05"), err)
32
+ ch <- apiResponse
33
+ return
34
+ }
35
+
36
+ ch <- apiResponse
37
+ }()
38
+
39
+ go func() {
40
+ wg.Wait()
41
+ close(ch)
42
+ }()
43
+
44
+ select {
45
+ case apiResponse, ok := <-ch:
46
+ if !ok {
47
+ return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
48
+ "error": "Failed to get a response from the server",
49
+ })
50
+ }
51
+ return c.Status(apiResponse.StatusCode).JSON(apiResponse)
52
+
53
+ case <-ctx.Done():
54
+ log.Printf("[%s] Timeout: %v", time.Now().Format("2006-01-02 15:04:05"), ctx.Err())
55
+ return c.Status(http.StatusRequestTimeout).JSON(fiber.Map{
56
+ "error": "Request timeout",
57
+ })
58
+ }
59
+ }
60
+ }
proxy/stock/list_service.go ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package stock
2
+
3
+ import (
4
+ "fmt"
5
+ "context"
6
+ "net/http"
7
+ "encoding/json"
8
+ )
9
+
10
+
11
+ /*
12
+ * --- Cryptocurrency Prediction Model Lists Service ---
13
+ */
14
+ func (s *CryptoServiceImpl) CryptoListsService(ctx context.Context) (ApiResponse, error) {
15
+ endpoint := fmt.Sprintf("%s/lists", CRYPTO_ENDPOINT)
16
+ req, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil)
17
+ if err != nil {
18
+ return ApiResponse{
19
+ Message: fmt.Sprintf("Failed to make request: %v", err),
20
+ StatusCode: http.StatusInternalServerError,
21
+ }, err
22
+ }
23
+
24
+ resp, err := http.DefaultClient.Do(req)
25
+ if err != nil {
26
+ return ApiResponse{
27
+ Message: fmt.Sprintf("Failed to make request: %v", err),
28
+ StatusCode: http.StatusInternalServerError,
29
+ }, err
30
+ }
31
+ defer resp.Body.Close()
32
+
33
+ if resp.StatusCode != http.StatusOK {
34
+ return ApiResponse{
35
+ Message: fmt.Sprintf("Request failed: %d", resp.StatusCode),
36
+ StatusCode: resp.StatusCode,
37
+ }, fmt.Errorf("request failed: %d", resp.StatusCode)
38
+ }
39
+
40
+ var apiResponse ApiResponse
41
+ if err := json.NewDecoder(resp.Body).Decode(&apiResponse); err != nil {
42
+ return ApiResponse{
43
+ Message: fmt.Sprintf("Failed to parse JSON response: %v", err),
44
+ StatusCode: http.StatusInternalServerError,
45
+ }, err
46
+ }
47
+
48
+ return apiResponse, nil
49
+ }
proxy/stock/prediction_handler.go ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package stock
2
+
3
+ import (
4
+ "fmt"
5
+ "log"
6
+ "sync"
7
+ "time"
8
+ "context"
9
+ "net/http"
10
+
11
+ "github.com/gofiber/fiber/v2"
12
+ )
13
+
14
+
15
+ /*
16
+ * --- Cryptocurrency Prediction Handler ---
17
+ */
18
+ func CryptoPredictionHandler(service CryptoService) fiber.Handler {
19
+ return func(c *fiber.Ctx) error {
20
+ ctx, cancel := context.WithTimeout(c.Context(), 120*time.Second)
21
+ defer cancel()
22
+
23
+ ch := make(chan ApiResponse, 1)
24
+ var wg sync.WaitGroup
25
+ wg.Add(1)
26
+
27
+ go func() {
28
+ defer wg.Done()
29
+
30
+ var predictionReq PredictionRequest
31
+ if err := c.BodyParser(&predictionReq); err != nil {
32
+ log.Printf("[%s] Failed to parse request body: %v",
33
+ time.Now().Format("2006-01-02 15:04:05"), err)
34
+
35
+ ch <- ApiResponse{
36
+ Message: fmt.Sprintf("Failed to parse request body: %v", err),
37
+ StatusCode: http.StatusBadRequest,
38
+ }
39
+ return
40
+ }
41
+
42
+ apiResponse, err := service.CryptoPredictionService(ctx, predictionReq)
43
+ if err != nil {
44
+ log.Printf("[%s] %v", time.Now().Format("2006-01-02 15:04:05"), err)
45
+ ch <- apiResponse
46
+ return
47
+ }
48
+
49
+ ch <- apiResponse
50
+ }()
51
+
52
+ go func() {
53
+ wg.Wait()
54
+ close(ch)
55
+ }()
56
+
57
+ select {
58
+ case apiResponse, ok := <-ch:
59
+ if !ok {
60
+ return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
61
+ "error": "Failed to get a response from the server",
62
+ })
63
+ }
64
+ return c.Status(apiResponse.StatusCode).JSON(apiResponse)
65
+
66
+ case <-ctx.Done():
67
+ log.Printf("[%s] Timeout: %v", time.Now().Format("2006-01-02 15:04:05"), ctx.Err())
68
+ return c.Status(http.StatusRequestTimeout).JSON(fiber.Map{
69
+ "error": "Request timeout",
70
+ })
71
+ }
72
+ }
73
+ }
proxy/stock/prediction_service.go ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package stock
2
+
3
+ import (
4
+ "fmt"
5
+ "bytes"
6
+ "context"
7
+ "net/http"
8
+ "encoding/json"
9
+ )
10
+
11
+
12
+ /*
13
+ * --- Cryptocurrency Prediction Service ---
14
+ */
15
+ func (s *CryptoServiceImpl) CryptoPredictionService(ctx context.Context, req PredictionRequest) (ApiResponse, error) {
16
+ reqBody, err := json.Marshal(req)
17
+ if err != nil {
18
+ return ApiResponse{
19
+ Message: fmt.Sprintf("Failed to marshal request body: %v", err),
20
+ StatusCode: http.StatusInternalServerError,
21
+ }, err
22
+ }
23
+
24
+ endpoint := fmt.Sprintf("%s/prediction", CRYPTO_ENDPOINT)
25
+ httpReq, err := http.NewRequestWithContext(ctx, "POST", endpoint, bytes.NewBuffer(reqBody))
26
+ if err != nil {
27
+ return ApiResponse{
28
+ Message: fmt.Sprintf("Failed to make request: %v", err),
29
+ StatusCode: http.StatusInternalServerError,
30
+ }, err
31
+ }
32
+ httpReq.Header.Set("Content-Type", "application/json")
33
+
34
+ resp, err := http.DefaultClient.Do(httpReq)
35
+ if err != nil {
36
+ return ApiResponse{
37
+ Message: fmt.Sprintf("Failed to make request: %v", err),
38
+ StatusCode: http.StatusInternalServerError,
39
+ }, err
40
+ }
41
+ defer resp.Body.Close()
42
+
43
+ if resp.StatusCode != http.StatusOK {
44
+ return ApiResponse{
45
+ Message: fmt.Sprintf("Request failed: %d", resp.StatusCode),
46
+ StatusCode: resp.StatusCode,
47
+ }, fmt.Errorf("request failed: %d", resp.StatusCode)
48
+ }
49
+
50
+ var apiResponse PredictionResponse
51
+ if err := json.NewDecoder(resp.Body).Decode(&apiResponse); err != nil {
52
+ return ApiResponse{
53
+ Message: fmt.Sprintf("Failed to parse JSON response: %v", err),
54
+ StatusCode: http.StatusInternalServerError,
55
+ }, err
56
+ }
57
+
58
+ return ApiResponse{
59
+ Message: apiResponse.Message,
60
+ StatusCode: apiResponse.StatusCode,
61
+ Data: apiResponse.Data,
62
+ }, nil
63
+ }
proxy/stock/structs.go ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package stock
2
+
3
+ type ApiResponse struct {
4
+ Message string `json:"message"`
5
+ StatusCode int `json:"status_code"`
6
+ Data interface{} `json:"data"`
7
+ }
8
+
9
+ type PredictionRequest struct {
10
+ Days int `json:"days"`
11
+ Currency string `json:"currency"`
12
+ }
13
+
14
+ type PredictionResponse struct {
15
+ Message string `json:"message"`
16
+ StatusCode int `json:"status_code"`
17
+
18
+ Data struct {
19
+ Currency string `json:"currency"`
20
+ Predictions struct {
21
+ Actuals []struct {
22
+ Date string `json:"date"`
23
+ Price float64 `json:"price"`
24
+ } `json:"actuals"`
25
+ Predictions []struct {
26
+ Date string `json:"date"`
27
+ Price float64 `json:"price"`
28
+ } `json:"predictions"`
29
+ } `json:"predictions"`
30
+ } `json:"data"`
31
+ }
traefik/Dockerfile ADDED
File without changes
traefik/dynamic.yaml ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ http:
2
+ middlewares:
3
+ dashboard-auth:
4
+ basicAuth:
5
+ users:
6
+ # - "admin:$apr1$vO3/IDvg$JrbhU9NSQub83/mQu9/fP1"
7
+
8
+ rate-limit:
9
+ rateLimit:
10
+ average: 100
11
+ burst: 50
12
+
13
+ routers:
14
+ api:
15
+ rule: "PathPrefix(`/api`)"
16
+ entryPoints:
17
+ - "web"
18
+ middlewares:
19
+ - "dashboard-auth"
20
+ - "rate-limit"
21
+ service: "api@internal"
22
+
23
+ dashboard:
24
+ rule: "PathPrefix(`/dashboard`)"
25
+ entryPoints:
26
+ - "web"
27
+ middlewares:
28
+ - "dashboard-auth"
29
+ - "rate-limit"
30
+ service: "api@internal"
31
+
32
+ crypto-lists:
33
+ rule: "PathPrefix(`/crypto/lists`)"
34
+ entryPoints:
35
+ - "websecure"
36
+ service: "crypto-lists-service"
37
+
38
+ services:
39
+ crypto-lists-service:
40
+ loadBalancer:
41
+ servers:
42
+ - url: "https://qywok-cryptocurrency-prediction.hf.space/crypto/lists"
traefik/traefik.yaml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ providers:
2
+ file:
3
+ filename: ./traefik/dynamic.yaml
4
+ watch: true
5
+
6
+ entryPoints:
7
+ web:
8
+ address: ":7860"
9
+ websecure:
10
+ address: ":443"
11
+
12
+ api:
13
+ dashboard: true
14
+ insecure: false