Spaces:
Sleeping
Sleeping
tebakaja
commited on
Commit
•
542878f
0
Parent(s):
feat: Crafting Load Balancer Proxy
Browse files- Dockerfile +0 -0
- Makefile +28 -0
- go.mod +20 -0
- go.sum +62 -0
- haproxy/Dockerfile +0 -0
- haproxy/haproxy.cfg +61 -0
- main.go +25 -0
- nginx/Dockerfile +0 -0
- nginx/nginx.conf +0 -0
- proxy/crypto/constants.go +3 -0
- proxy/crypto/interfaces.go +10 -0
- proxy/crypto/list_handler.go +60 -0
- proxy/crypto/list_service.go +49 -0
- proxy/crypto/prediction_handler.go +73 -0
- proxy/crypto/prediction_service.go +63 -0
- proxy/crypto/structs.go +31 -0
- proxy/loadbalancers.go +1 -0
- proxy/middlewares.go +34 -0
- proxy/national_currency/constants.go +3 -0
- proxy/national_currency/interfaces.go +10 -0
- proxy/national_currency/list_handler.go +60 -0
- proxy/national_currency/list_service.go +49 -0
- proxy/national_currency/prediction_handler.go +73 -0
- proxy/national_currency/prediction_service.go +63 -0
- proxy/national_currency/structs.go +31 -0
- proxy/stock/constants.go +3 -0
- proxy/stock/interfaces.go +10 -0
- proxy/stock/list_handler.go +60 -0
- proxy/stock/list_service.go +49 -0
- proxy/stock/prediction_handler.go +73 -0
- proxy/stock/prediction_service.go +63 -0
- proxy/stock/structs.go +31 -0
- traefik/Dockerfile +0 -0
- traefik/dynamic.yaml +42 -0
- traefik/traefik.yaml +14 -0
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
|