diff options
-rw-r--r-- | README.md | 191 | ||||
-rw-r--r-- | docker-compose-test.yaml | 95 | ||||
-rw-r--r-- | docker-compose.yaml | 8 | ||||
-rw-r--r-- | hived/Dockerfile | 2 | ||||
-rw-r--r-- | hived/Dockerfile_distroless | 2 | ||||
-rw-r--r-- | hived/Dockerfile_distroless_vendored | 4 | ||||
-rw-r--r-- | hived/alertHandlers.go | 5 | ||||
-rw-r--r-- | hived/go.mod | 9 | ||||
-rw-r--r-- | hived/go.sum | 37 | ||||
-rw-r--r-- | hived/hived.go | 131 | ||||
-rw-r--r-- | hived/hived.toml | 3 | ||||
-rw-r--r-- | hived/tickerHandlers.go | 8 |
12 files changed, 307 insertions, 188 deletions
@@ -4,3 +4,194 @@ # hived `hived` is a cryptocurrency api server:<br/> + +endpoints: + +- `/api/crypto/v1/price`: get the price of a cryptocurrency +- `/api/crypto/v1/pair`: +- `/_/`: the pocketbase admin panel +- `/api/crypto/v1/alert`: set a condition to be alerted on when true + +You can use arithmatical expressions in the alert endpoint. Hived is using [govaluate](https://github.com/Knetic/govaluate). +Here are some examples: + +```txt +bitcoin>ethereum +``` + +```txt +ethereum*10>(bitcoin+dodge) +``` + +- `/api/crypto/v1/ticker`: get the latest price of a cryptocurrency + +## Options + +Hived extends pocketbase so all the pocketbase options are available: + +```txt +$ hived -help +PocketBase CLI + +Usage: + hived [command] + +Available Commands: + admin Manages admin accounts + migrate Executes app DB migration scripts + serve Starts the web server (default to 127.0.0.1:8090 if no domain is specified) + update Automatically updates the current app executable with the latest available version + +Flags: + --automigrate enable/disable auto migrations (default true) + --dev enable dev mode, aka. printing logs and sql statements to the console + --dir string the PocketBase data directory (default "hived/pb_data") + --encryptionEnv string the env variable whose value of 32 characters will be used + as encryption key for the app settings (default none) + -h, --help help for hived + --hooksDir string the directory with the JS app hooks + --hooksPool int the total prewarm goja.Runtime instances for the JS app hooks execution (default 25) + --hooksWatch auto restart the app on pb_hooks file change (default true) + --indexFallback fallback the request to index.html on missing static path (eg. when pretty urls are used with SPA) (default true) + --migrationsDir string the directory with the user defined migrations + --publicDir string the directory to serve static files (default "hived/pb_public") + --queryTimeout int the default SELECT queries timeout in seconds (default 30) + -v, --version version for hived + +Use "hived [command] --help" for more information about a command. +``` + +## Supported Sources + +## Config File + +```toml +keydbAddress = "keydb:6379" +keydbPassword = "" +keydbDB = 0 +alertsCheckInterval = 600 +tickerCheckInterval = 600 +cacheDuration = 600 +telegramChannelID = 1234567 +telegramBotToken = "1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZ" +``` + +## Admin Panel + +Hived is using [pocketbase](https://github.com/pocketbase/pocketbase). The admin panel provided is available at `/_/`. + +## Curl Examples + +```bash +# price +curl -u "user:password" -X GET https://hived.mydomain.com/api/crypto/v1/price?name=PEPE&unit=USD + +# pair +curl -u "user:password" -X GET https://hived.mydomain.com/api/crypto/v1/pair?one=ETH&two=CAKE&multiplier=4.0 + +# alert +curl -u "user:password" -X POST -H "Content-Type: application/json" -d '{"name":"alert1", "expr":"ETH>CAKE"}' https://hived.mydomain.com/api/crypto/v1/alert +curl -u "user:password" -X GET -H "Content-Type: application/json" https://hived.mydomain.com/api/crypto/v1/alert?key=alert1 +curl -u "user:password" -X DELETE -H "Content-Type: application/json" https://hived.mydomain.com/api/crypto/v1/alert?key=alert1 + +# ticker +curl -u "user:password" -X POST -H "Content-Type: application/json" -d '{"name":"ethereum"}' https://hived.mydomain.com/api/crypto/v1/ticker +curl -u "user:password" -X GET -H "Content-Type: application/json" https://hived.mydomain.com/api/crypto/v1/ticker?key=ethereum +curl -u "user:password" -X DELETE -H "Content-Type: application/json" https://hived.mydomain.com/api/crypto/v1/ticker?key=ethereum +``` + +## Deployment + +There are a couple of Dockerfiles provided by default in the repo:<br/> + +```yaml +services: + nginx: + image: nginx:stable + deploy: + resources: + limits: + memory: 128M + logging: + driver: "json-file" + options: + max-size: "100m" + ports: + - "443:443/tcp" + networks: + - apinet + restart: unless-stopped + cap_drop: + - ALL + cap_add: + - CHOWN + - DAC_OVERRIDE + - SETGID + - SETUID + - NET_BIND_SERVICE + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf:ro + - /etc/letsencrypt/live/hived.mydomain.com/fullchain.pem:/etc/letsencrypt/live/hived.mydomain.com/fullchain.pem:ro + - /etc/letsencrypt/live/hived.mydomain.com/privkey.pem:/etc/letsencrypt/live/hived.mydomain.com/privkey.pem:ro + - pb-vault:/pb/pd-data/ + depends_on: + - hived + hived: + image: hived + build: + context: ./hived + dockerfile: ./Dockerfile_distroless_vendored + deploy: + resources: + limits: + memory: 256M + logging: + driver: "json-file" + options: + max-size: "100m" + networks: + - apinet + - dbnet + ports: + - "127.0.0.1:10009:8090" + entrypoint: ["hived"] + command: ["serve", "--http=0.0.0.0:8090"] + depends_on: + - keydb + cap_drop: + - ALL + environment: + - SERVER_DEPLOYMENT_TYPE=test + - HIVED_PRICE_SOURCE=cmc + - CMC_API_KEY= + - POLYGON_API_KEY= + - CRYPTOCOMPARE_API_KEY= + volumes: + - ./hived/hived.toml:/hived/hived.toml + dns: + - 1.1.1.1 + keydb: + image: eqalpha/keydb:alpine_x86_64_v6.3.4 + deploy: + resources: + limits: + memory: 256M + logging: + driver: "json-file" + options: + max-size: "100m" + networks: + - dbnet + ports: + - "127.0.0.1:6380:6379" + environment: + - ALLOW_EMPTY_PASSWORD=yes + volumes: + - keydb-data:/data/ +networks: + dbnet: + apinet: +volumes: + keydb-data: + pb-vault: +``` diff --git a/docker-compose-test.yaml b/docker-compose-test.yaml deleted file mode 100644 index 49ceab9..0000000 --- a/docker-compose-test.yaml +++ /dev/null @@ -1,95 +0,0 @@ -services: - nginx: - image: nginx:stable - deploy: - resources: - limits: - memory: 128M - logging: - driver: "json-file" - options: - max-size: "100m" - ports: - - "127.0.0.1:10008:443" - networks: - - apinet - restart: unless-stopped - cap_drop: - - ALL - cap_add: - - CHOWN - - DAC_OVERRIDE - - SETGID - - SETUID - - NET_BIND_SERVICE - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf:ro - - ./ss_certs/server.cert:/etc/letsencrypt/live/api.terminaldweller.com/fullchain.pem:ro - - ./ss_certs/server.key:/etc/letsencrypt/live/api.terminaldweller.com/privkey.pem:ro - depends_on: - - hived - hived: - image: hived - build: - context: ./hived - deploy: - resources: - limits: - memory: 256M - logging: - driver: "json-file" - options: - max-size: "100m" - secrets: - - tg_bot_token - networks: - - apinet - - dbnet - ports: - - "127.0.0.1:10009:8090" - entrypoint: ["/hived/hived"] - command: ["serve", "--http=0.0.0.0:8090"] - depends_on: - - keydb - cap_drop: - - ALL - environment: - - SERVER_DEPLOYMENT_TYPE=test - - HIVED_PRICE_SOURCE=cryptocompare - - CMC_API_KEY= - - POLYGON_API_KEY= - - CRYPTOCOMPARE_API_KEY= - - TELEGRAM_BOT_TOKEN= - volumes: - - ./hived/hived.toml:/hived/hived.toml - keydb: - image: eqalpha/keydb:alpine_x86_64_v6.3.4 - deploy: - resources: - limits: - memory: 256M - logging: - driver: "json-file" - options: - max-size: "100m" - networks: - - dbnet - ports: - - "127.0.0.1:6380:6379" - environment: - - ALLOW_EMPTY_PASSWORD=yes - volumes: - - keydb-data:/data/ -networks: - dbnet: - apinet: -secrets: - tg_bot_token: - file: ./tgtoken - polygon_api_key: - file: ./polygon_api_key - cmc_api_key: - file: ./cmc_api_key -volumes: - keydb-data: - pb-vault: diff --git a/docker-compose.yaml b/docker-compose.yaml index 65d853a..7b7f754 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -10,7 +10,7 @@ services: options: max-size: "100m" ports: - - "127.0.0.1:10008:443" + - "8007:443" networks: - apinet restart: unless-stopped @@ -29,9 +29,7 @@ services: depends_on: - hived hived: - image: hived - build: - context: ./hived + image: terminaldweller/hived:main deploy: resources: limits: @@ -42,6 +40,7 @@ services: max-size: "100m" secrets: - tg_bot_token + restart: unless-stopped networks: - apinet - dbnet @@ -92,4 +91,3 @@ secrets: file: ./cmc_api_key volumes: keydb-data: - p diff --git a/hived/Dockerfile b/hived/Dockerfile index 407c8ac..bfeb2e8 100644 --- a/hived/Dockerfile +++ b/hived/Dockerfile @@ -2,7 +2,7 @@ FROM golang:1.22-alpine3.20 as builder COPY go.* /hived/ RUN cd /hived && go mod download COPY *.go /hived/ -RUN cd /hived && go build +RUN cd /hived && CGO_ENABLED=0 go build FROM alpine:3.20 COPY --from=builder /hived/hived /hived/ diff --git a/hived/Dockerfile_distroless b/hived/Dockerfile_distroless index c21dd2d..77c8731 100644 --- a/hived/Dockerfile_distroless +++ b/hived/Dockerfile_distroless @@ -2,7 +2,7 @@ FROM golang:1.22-alpine3.20 as builder COPY go.* /hived/ RUN cd /hived && go mod download COPY *.go /hived/ -RUN cd /hived && go build +RUN cd /hived && CGO_ENABLED=0 go build FROM gcr.io/distroless/static-debian12 COPY --from=builder /hived/hived "/usr/bin/hived" diff --git a/hived/Dockerfile_distroless_vendored b/hived/Dockerfile_distroless_vendored index 125c0ae..11c3066 100644 --- a/hived/Dockerfile_distroless_vendored +++ b/hived/Dockerfile_distroless_vendored @@ -1,9 +1,9 @@ -FROM golang:1.22-alpine3.20 as builder +FROM golang:1.22-alpine3.20 AS builder WORKDIR /hived COPY go.sum go.mod /hived/ COPY vendor /hived/vendor COPY *.go /hived/ -RUN go build +RUN CGO_ENABLED=0 go build FROM gcr.io/distroless/static-debian12 COPY --from=builder /hived/hived "/usr/bin/hived" diff --git a/hived/alertHandlers.go b/hived/alertHandlers.go index 6fb7614..54fb042 100644 --- a/hived/alertHandlers.go +++ b/hived/alertHandlers.go @@ -146,10 +146,13 @@ func (alertHandler Handler) HandleAlertGet(writer http.ResponseWriter, request * } var ErrorString string + var IsSuccessful bool if err == nil { ErrorString = "" + IsSuccessful = true } else { ErrorString = err.Error() + IsSuccessful = false } writer.Header().Add("Content-Type", "application/json") @@ -159,7 +162,7 @@ func (alertHandler Handler) HandleAlertGet(writer http.ResponseWriter, request * Error string `json:"error"` Key string `json:"key"` Expr string `json:"expr"` - }{IsSuccessful: true, Error: ErrorString, Key: identifier, Expr: redisResultString}) + }{IsSuccessful: IsSuccessful, Error: ErrorString, Key: identifier, Expr: redisResultString}) if err != nil { log.Error().Err(err) diff --git a/hived/go.mod b/hived/go.mod index d9b8a4e..80af911 100644 --- a/hived/go.mod +++ b/hived/go.mod @@ -7,12 +7,11 @@ require ( github.com/Knetic/govaluate v3.0.0+incompatible github.com/go-redis/redis/v8 v8.11.5 github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible + github.com/google/uuid v1.6.0 github.com/labstack/echo/v5 v5.0.0-20230722203903-ec5b858dab61 github.com/pocketbase/pocketbase v0.22.13 github.com/rs/zerolog v1.31.0 - github.com/terminaldweller/grpc v1.0.3 - google.golang.org/grpc v1.64.0 - google.golang.org/protobuf v1.34.1 + golang.org/x/crypto v0.23.0 ) require ( @@ -55,7 +54,6 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect - github.com/google/uuid v1.6.0 // indirect github.com/googleapis/gax-go/v2 v2.12.4 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -77,7 +75,6 @@ require ( github.com/valyala/fasttemplate v1.2.2 // indirect go.opencensus.io v0.24.0 // indirect gocloud.dev v0.37.0 // indirect - golang.org/x/crypto v0.23.0 // indirect golang.org/x/image v0.16.0 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect @@ -89,6 +86,8 @@ require ( golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/api v0.182.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/grpc v1.64.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect modernc.org/gc/v3 v3.0.0-20240304020402-f0dba7c97c2b // indirect modernc.org/libc v1.51.0 // indirect modernc.org/mathutil v1.6.0 // indirect diff --git a/hived/go.sum b/hived/go.sum index 0bc72a3..803d6e8 100644 --- a/hived/go.sum +++ b/hived/go.sum @@ -1,5 +1,4 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.114.0 h1:OIPFAdfrFDFO2ve2U7r/H5SwSbBzEdrBdE7xkgwc+kY= cloud.google.com/go v0.114.0/go.mod h1:ZV9La5YYxctro1HTPug5lXH/GefROyW8PPD4T8n9J8E= cloud.google.com/go/auth v0.4.2 h1:sb0eyLkhRtpq5jA+a8KWw0W70YcdVca7KJ8TM0AFYDg= @@ -23,7 +22,6 @@ github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= @@ -68,7 +66,6 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.28.10/go.mod h1:0Aqn1MnEuitqfsCNyKsd github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q= github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= @@ -76,11 +73,6 @@ github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86c github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -111,8 +103,6 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= @@ -126,7 +116,6 @@ github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= github.com/ganigeorgiev/fexpr v0.4.1 h1:hpUgbUEEWIZhSDBtf4M9aUNfQQ0BZkGRaMePy7Gcx5k= github.com/ganigeorgiev/fexpr v0.4.1/go.mod h1:RyGiGqmeXhEQ6+mlGdnUleLHgtzzu/VGO2WtJkF5drE= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -155,17 +144,13 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -174,7 +159,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= @@ -191,7 +175,6 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfF github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg= github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= @@ -247,7 +230,6 @@ github.com/pocketbase/pocketbase v0.22.13/go.mod h1:QrZElN3ifiRf9aCHv9ks6JGWYdY3 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -265,9 +247,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -275,8 +255,6 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= -github.com/terminaldweller/grpc v1.0.3 h1:yCRm0HKRD4M87CBmmO5KjkSFRq4X2lzHLJRH1ApzeiE= -github.com/terminaldweller/grpc v1.0.3/go.mod h1:pYpuXZw8rHZwTABVEVZEfErFr+PyEhAaSrFm7y1yvTo= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= @@ -294,7 +272,6 @@ go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGX go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= gocloud.dev v0.37.0 h1:XF1rN6R0qZI/9DYjN16Uy0durAmSlf58DHOcb28GPro= gocloud.dev v0.37.0/go.mod h1:7/O4kqdInCNsc6LqgmuFnS0GRew4XNNYWpA44yQnwco= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -314,25 +291,21 @@ golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= @@ -341,7 +314,6 @@ golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -379,7 +351,6 @@ golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.182.0 h1:if5fPvudRQ78GeRx3RayIoiuV7modtErPIZC/T2bIvE= @@ -389,7 +360,6 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 h1:ImUcDPHjTrAqNhlOkSocDLfG9rrNHH7w7uoKWPaWZ8s= google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7/go.mod h1:/3XmxOjePkvmKrHuBy4zNFw7IzxJXtAgdpXi8Ll990U= @@ -401,10 +371,7 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= @@ -416,9 +383,6 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -428,7 +392,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/hived/hived.go b/hived/hived.go index c714cfd..b91babe 100644 --- a/hived/hived.go +++ b/hived/hived.go @@ -29,6 +29,7 @@ import ( "github.com/pocketbase/pocketbase/plugins/migratecmd" "github.com/rs/zerolog" "github.com/rs/zerolog/log" + "golang.org/x/crypto/bcrypt" ) const ( @@ -75,6 +76,7 @@ type HivedConfig struct { TickerCheckInterval int64 `toml:"tickerCheckInterval"` CacheDuration int64 `toml:"cacheDuration"` TelegramChannelID int64 `toml:"telegramChannelID"` + TelegramBotToken string `toml:"telegramBotToken"` } type appWrapper struct { @@ -128,11 +130,8 @@ type HTTPHandler struct { function HTTPHandlerFunc } -func getTGBot() *tgbotapi.BotAPI { - token := os.Getenv("TELEGRAM_BOT_TOKEN") - fmt.Println("YYY:", token) - - bot, err := tgbotapi.NewBotAPI(token) +func getTGBot(tgtoken string) *tgbotapi.BotAPI { + bot, err := tgbotapi.NewBotAPI(tgtoken) if err != nil { log.Fatal().Err(err).Send() } @@ -172,10 +171,10 @@ func addSecureHeaders(writer *http.ResponseWriter) { (*writer).Header().Set("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS") } -func sendToTg(msg string, channelID int64) { - tgbotapi := getTGBot() +func (cw HivedConfig) sendToTg(msg string) { + tgbotapi := getTGBot(cw.TelegramBotToken) - err := sendMessage(tgbotapi, msg, channelID) + err := sendMessage(tgbotapi, msg, cw.TelegramChannelID) if err != nil { log.Info().Err(err) } @@ -798,7 +797,7 @@ func getTickers() tickersType { return tickers } -func alertManagerWorker(alert alertType, tgChannelID int64) { +func alertManagerWorker(alert alertType, config HivedConfig) { expression, err := govaluate.NewEvaluableExpression(alert.Expr) if err != nil { log.Error().Err(err) @@ -874,38 +873,38 @@ func alertManagerWorker(alert alertType, tgChannelID int64) { log.Error().Err(err) } - sendToTg(msgText, tgChannelID) + config.sendToTg(msgText) } -func alertManager(alertsCheckInterval int64, tgChannelID int64) { +func alertManager(config HivedConfig) { for { alerts := getAlerts() log.Info().Msg(fmt.Sprintf("%v", alerts)) for alertIndex := range alerts.Alerts { - go alertManagerWorker(alerts.Alerts[alertIndex], tgChannelID) + go alertManagerWorker(alerts.Alerts[alertIndex], config) } - time.Sleep(time.Second * time.Duration(alertsCheckInterval)) + time.Sleep(time.Second * time.Duration(config.AlertsCheckInterval)) } } -func tickerManager(tickerCheckInterval int64, tgChannelID int64) { +func tickerManager(config HivedConfig) { for { tickers := getTickers() log.Info().Msg(fmt.Sprintf("%v", tickers)) for tickerIndex := range tickers.Tickers { - go tickerManagerWorker(tickers.Tickers[tickerIndex], tgChannelID) + go tickerManagerWorker(tickers.Tickers[tickerIndex], config) } - time.Sleep(time.Second * time.Duration(tickerCheckInterval)) + time.Sleep(time.Second * time.Duration(config.TickerCheckInterval)) } } -func tickerManagerWorker(ticker tickerType, tgChannelID int64) { +func tickerManagerWorker(ticker tickerType, config HivedConfig) { var waitGroup sync.WaitGroup priceChan := make(chan priceChanStruct, 1) @@ -943,7 +942,7 @@ func tickerManagerWorker(ticker tickerType, tgChannelID int64) { log.Print(msgText) - sendToTg(msgText, tgChannelID) + config.sendToTg(msgText) } type addAlertJSONType struct { @@ -1069,6 +1068,50 @@ func defaultPublicDir() string { return filepath.Join(os.Args[0], "../pb_public") } +func (aw appWrapper) apikeyAuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + apikey := c.Request().Header["Apikey"][0] + user := c.Request().Header["User"][0] + + userRecord, err := aw.app.Dao().FindAuthRecordByUsername("users", user) + if err != nil { + return apis.NewBadRequestError("unauthorized", nil) + } + + hashedAPIKey := userRecord.Get("apikey") + + hashedAPIKeyStr, ok := hashedAPIKey.(string) + if !ok { + return apis.NewBadRequestError("unauthorized", nil) + } + + err = bcrypt.CompareHashAndPassword([]byte(hashedAPIKeyStr), []byte(apikey)) + + return next(c) + } +} + +func (aw appWrapper) authMiddleware(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + user, pass, ok := c.Request().BasicAuth() + if !ok { + return apis.NewBadRequestError("unauthorized", nil) + } + + userRecord, err := aw.app.Dao().FindAuthRecordByUsername("users", user) + if err != nil { + log.Print(err) + return apis.NewBadRequestError("unauthorized", nil) + } + + if !userRecord.ValidatePassword(pass) { + return apis.NewBadRequestError("unauthorized", nil) + } + + return next(c) + } +} + func setRootCmds(app *pocketbase.PocketBase) RootCmds { var rootCmds RootCmds @@ -1137,23 +1180,33 @@ func startPocketbaseApp() { aw := appWrapper{app: app} app.OnBeforeServe().Add(func(e *core.ServeEvent) error { - e.Router.POST("/", aw.postHandler) - e.Router.GET("/health", aw.healthHandler) - e.Router.GET("/api/crypto/v1/price", aw.PriceHandler) - e.Router.GET("/api/crypto/v1/pair", aw.PairHandler) - - e.Router.GET("/api/crypto/v1/alert", aw.alertHandler) - e.Router.PUT("/api/crypto/v1/alert", aw.alertHandler) - e.Router.POST("/api/crypto/v1/alert", aw.alertHandler) - e.Router.PATCH("/api/crypto/v1/alert", aw.alertHandler) - e.Router.DELETE("/api/crypto/v1/alert", aw.alertHandler) - - e.Router.GET("/api/crypto/v1/ticker", aw.tickerHandler) - e.Router.PUT("/api/crypto/v1/ticker", aw.tickerHandler) - e.Router.POST("/api/crypto/v1/ticker", aw.tickerHandler) - e.Router.PATCH("/api/crypto/v1/ticker", aw.tickerHandler) - e.Router.DELETE("/api/crypto/v1/ticker", aw.tickerHandler) + e.Router.POST("/", aw.postHandler, aw.authMiddleware) + e.Router.GET("/health", aw.healthHandler, aw.authMiddleware) + e.Router.GET("/api/crypto/v1/price", aw.PriceHandler, aw.authMiddleware) + e.Router.GET("/api/crypto/v1/pair", aw.PairHandler, aw.authMiddleware) + + e.Router.GET("/api/crypto/v1/alert", aw.alertHandler, aw.authMiddleware) + e.Router.PUT("/api/crypto/v1/alert", aw.alertHandler, aw.authMiddleware) + e.Router.POST("/api/crypto/v1/alert", aw.alertHandler, aw.authMiddleware) + e.Router.PATCH("/api/crypto/v1/alert", aw.alertHandler, aw.authMiddleware) + e.Router.DELETE("/api/crypto/v1/alert", aw.alertHandler, aw.authMiddleware) + + e.Router.GET("/api/crypto/v1/ticker", aw.tickerHandler, aw.authMiddleware) + e.Router.PUT("/api/crypto/v1/ticker", aw.tickerHandler, aw.authMiddleware) + e.Router.POST("/api/crypto/v1/ticker", aw.tickerHandler, aw.authMiddleware) + e.Router.PATCH("/api/crypto/v1/ticker", aw.tickerHandler, aw.authMiddleware) + e.Router.DELETE("/api/crypto/v1/ticker", aw.tickerHandler, aw.authMiddleware) + + return nil + }) + + app.OnRecordAfterCreateRequest("users").Add(func(e *core.RecordCreateEvent) error { + apikeyHash, err := GenAPIKey() + if err != nil { + return err + } + e.Record.Set("apikey", apikeyHash) return nil }) @@ -1197,7 +1250,9 @@ func startPocketbaseApp() { } func main() { - data, err := os.ReadFile("/hived/hived.toml") + configPathFlag := flag.String("config", "/hived/hived.toml", "path to the hived config file") + flag.Parse() + data, err := os.ReadFile(*configPathFlag) if err != nil { log.Fatal().Err(err) } @@ -1209,7 +1264,7 @@ func main() { log.Fatal().Err(err) } - fmt.Println("XXX:", config) + fmt.Println("config:", config) rdb = redis.NewClient(&redis.Options{ Addr: config.KeydbAddress, @@ -1220,9 +1275,9 @@ func main() { setupLogging() - go alertManager(config.AlertsCheckInterval, config.TelegramChannelID) + go alertManager(config) - go tickerManager(config.TickerCheckInterval, config.TelegramChannelID) + go tickerManager(config) startPocketbaseApp() } diff --git a/hived/hived.toml b/hived/hived.toml index abddb19..f8e87b7 100644 --- a/hived/hived.toml +++ b/hived/hived.toml @@ -4,4 +4,5 @@ keydbDB = 0 alertsCheckInterval = 600 tickerCheckInterval = 600 cacheDuration = 600 -telegramChannelID = +telegramChannelID = 146328407 +telegramBotToken = "556550001:AAFWaKwhezZNBqouGOkulbgghBJ78I1Wzu0" diff --git a/hived/tickerHandlers.go b/hived/tickerHandlers.go index d857eaf..e5dff22 100644 --- a/hived/tickerHandlers.go +++ b/hived/tickerHandlers.go @@ -49,10 +49,14 @@ func (tickerHandler Handler) HandleTickerGet(writer http.ResponseWriter, request } var ErrorString string + var IsSuccessful bool + if err == nil { ErrorString = "" + IsSuccessful = true } else { ErrorString = err.Error() + IsSuccessful = false } writer.Header().Add("Content-Type", "application/json") @@ -62,7 +66,7 @@ func (tickerHandler Handler) HandleTickerGet(writer http.ResponseWriter, request Error string `json:"error"` Key string `json:"key"` Expr string `json:"expr"` - }{IsSuccessful: true, Error: ErrorString, Key: identifier, Expr: redisResultString}) + }{IsSuccessful: IsSuccessful, Error: ErrorString, Key: identifier, Expr: redisResultString}) if err != nil { log.Error().Err(err) @@ -103,7 +107,7 @@ func (tickerHandler Handler) HandleTickerDelete(writer http.ResponseWriter, requ defer cancel() tickerHandler.rdb.Del(ctx, identifier) - setKey := "alert:" + identifier + setKey := "ticker:" + identifier tickerHandler.rdb.SRem(ctx, "tickerkeys", setKey) log.Printf(setKey) |