From acdda47cafc315fc975cdb4e812cc8fa4bbdcf4a Mon Sep 17 00:00:00 2001 From: Keyur Shah <51239095+keyurboss@users.noreply.github.com> Date: Sun, 8 Dec 2024 17:50:21 +0530 Subject: [PATCH] feat(data): Order Entity And Crud Api (#37) * Enum Validator Added * Order Entity Completed * Order Events Added * Updated Scripts * Order Repo WIP * Order Repo Completed * Trade User Caching Data Completed * WIP * Order Base Validator Added * Docker Added * Place Order WIP * Last Rate Reader Added From Redis * Spot Price Calculation Added And CRUD Added * Basic Testing Added * Some of the info is updated For Bullion Site Details * Deps Updated * Deps Added * CI Version Changes * SOME MORE PROGRESS * THIS IS ADDED * Go updating All Modules --- .dockerignore | 32 +++ .github/workflows/builder.yml | 2 +- .vscode/settings.json | 1 + Dockerfile | 78 ++++++ README.Docker.md | 22 ++ compose.yaml | 77 ++++++ go.mod | 110 ++++---- go.sum | 237 +++++++++------- main.go | 25 +- src/apis/apis.go | 2 + src/apis/auth/get-bullion-details.go | 2 +- src/apis/data/index.go | 1 + .../data/product/add-update-bank-calc.api.go | 35 +++ src/apis/data/product/get-bank-calc.api.go | 31 +++ src/apis/data/product/get-live.rate.api.go | 10 + src/apis/data/product/index.go | 6 + src/apis/data/trade-user-group/index.go | 4 +- src/apis/data/trade-user/index.go | 4 +- src/apis/order/admin-order/index.go | 9 + src/apis/order/index.go | 14 + src/apis/order/user/index.go | 8 + src/env/index.go | 15 +- src/env/keys.go | 3 +- src/events/bank-rate-calc.event.go | 29 ++ src/events/order.events.go | 53 ++++ src/interfaces/bank-rate.calc.entity.go | 35 +++ src/interfaces/base-entity.go | 8 +- .../base-enum-validator.interface.go | 10 + src/interfaces/base-symbol.enum.go | 101 +++++-- src/interfaces/bullion-site-info.interface.go | 42 ++- src/interfaces/calculate-on-price.enum.go | 58 +++- src/interfaces/calculate-symbol.enum.go | 19 +- src/interfaces/error-code.enum.go | 9 + src/interfaces/general-type.go | 3 + .../general-user-req-auth-status.enum.go | 18 +- src/interfaces/general-user-req.entity.go | 2 +- src/interfaces/general-users.entity.go | 2 +- src/interfaces/order-entity.interface.go | 129 +++++++++ src/interfaces/order.enum.go | 139 ++++++++++ src/interfaces/price-key.enum.go | 83 ++++++ src/interfaces/product-entity.interface.go | 23 +- .../product-entity.interface_test.go | 39 +++ src/interfaces/trade-user-group.entity.go | 11 + src/interfaces/trade-user.entity.go | 38 ++- src/interfaces/user-roles.enum.go | 21 +- src/mongodb/repos/bank-rate-calc.repo.go | 103 +++++++ src/mongodb/repos/order.repo.go | 203 ++++++++++++++ src/mongodb/repos/trade-user.repo.go | 22 +- src/redis/index.go | 16 +- src/services/bank-rate-cacl.service.go | 57 ++++ src/services/bullion-details.service.go | 4 +- src/services/general-user.service.go | 5 +- src/services/live-rate.service.go | 90 +++++++ src/services/order-general.service.go | 255 ++++++++++++++++++ src/services/trade-user-group.service.go | 3 - src/services/trade-user.service.go | 41 ++- src/utility/firebase/firebase-app.go | 1 + src/utility/jwt/jwt.go | 4 +- 58 files changed, 2114 insertions(+), 290 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 README.Docker.md create mode 100644 compose.yaml create mode 100644 src/apis/data/product/add-update-bank-calc.api.go create mode 100644 src/apis/data/product/get-bank-calc.api.go create mode 100644 src/apis/data/product/get-live.rate.api.go create mode 100644 src/apis/order/admin-order/index.go create mode 100644 src/apis/order/index.go create mode 100644 src/apis/order/user/index.go create mode 100644 src/events/bank-rate-calc.event.go create mode 100644 src/events/order.events.go create mode 100644 src/interfaces/bank-rate.calc.entity.go create mode 100644 src/interfaces/base-enum-validator.interface.go create mode 100644 src/interfaces/general-type.go create mode 100644 src/interfaces/order-entity.interface.go create mode 100644 src/interfaces/order.enum.go create mode 100644 src/interfaces/price-key.enum.go create mode 100644 src/interfaces/product-entity.interface_test.go create mode 100644 src/mongodb/repos/bank-rate-calc.repo.go create mode 100644 src/mongodb/repos/order.repo.go create mode 100644 src/services/bank-rate-cacl.service.go create mode 100644 src/services/live-rate.service.go create mode 100644 src/services/order-general.service.go diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e5c60ab --- /dev/null +++ b/.dockerignore @@ -0,0 +1,32 @@ +# Include any files or directories that you don't want to be copied to your +# container here (e.g., local build artifacts, temporary files, etc.). +# +# For more help, visit the .dockerignore file reference guide at +# https://docs.docker.com/go/build-context-dockerignore/ + +**/.DS_Store +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/bin +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md diff --git a/.github/workflows/builder.yml b/.github/workflows/builder.yml index c262919..965e20e 100644 --- a/.github/workflows/builder.yml +++ b/.github/workflows/builder.yml @@ -12,7 +12,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: '1.21.1' + go-version: '1.22.2' - name: Build run: go build -v ./... diff --git a/.vscode/settings.json b/.vscode/settings.json index bdad887..999bef1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "cSpell.words": [ + "adminorder", "Akshat", "bankdetails", "bson", diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..aaaf910 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,78 @@ +# syntax=docker/dockerfile:1 + +# Comments are provided throughout this file to help you get started. +# If you need more help, visit the Dockerfile reference guide at +# https://docs.docker.com/go/dockerfile-reference/ + +# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7 + +################################################################################ +# Create a stage for building the application. +ARG GO_VERSION=1.22.1 +FROM --platform=$BUILDPLATFORM golang:${GO_VERSION} AS build +WORKDIR /src + +# Download dependencies as a separate step to take advantage of Docker's caching. +# Leverage a cache mount to /go/pkg/mod/ to speed up subsequent builds. +# Leverage bind mounts to go.sum and go.mod to avoid having to copy them into +# the container. +RUN --mount=type=cache,target=/go/pkg/mod/ \ + --mount=type=bind,source=go.sum,target=go.sum \ + --mount=type=bind,source=go.mod,target=go.mod \ + go mod download -x + +# This is the architecture you’re building for, which is passed in by the builder. +# Placing it here allows the previous steps to be cached across architectures. +ARG TARGETARCH + +# Build the application. +# Leverage a cache mount to /go/pkg/mod/ to speed up subsequent builds. +# Leverage a bind mount to the current directory to avoid having to copy the +# source code into the container. +RUN --mount=type=cache,target=/go/pkg/mod/ \ + --mount=type=bind,target=. \ + CGO_ENABLED=0 GOARCH=$TARGETARCH go build -o /bin/server . + +################################################################################ +# Create a new stage for running the application that contains the minimal +# runtime dependencies for the application. This often uses a different base +# image from the build stage where the necessary files are copied from the build +# stage. +# +# The example below uses the alpine image as the foundation for running the app. +# By specifying the "latest" tag, it will also use whatever happens to be the +# most recent version of that image when you build your Dockerfile. If +# reproducability is important, consider using a versioned tag +# (e.g., alpine:3.17.2) or SHA (e.g., alpine@sha256:c41ab5c992deb4fe7e5da09f67a8804a46bd0592bfdf0b1847dde0e0889d2bff). +FROM alpine:latest AS final + +# Install any runtime dependencies that are needed to run your application. +# Leverage a cache mount to /var/cache/apk/ to speed up subsequent builds. +RUN --mount=type=cache,target=/var/cache/apk \ + apk --update add \ + ca-certificates \ + tzdata \ + && \ + update-ca-certificates + +# Create a non-privileged user that the app will run under. +# See https://docs.docker.com/go/dockerfile-user-best-practices/ +ARG UID=10001 +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + appuser +USER appuser + +# Copy the executable from the "build" stage. +COPY --from=build /bin/server /bin/ + +# Expose the port that the application listens on. +EXPOSE 5000 + +# What the container should run when it is started. +ENTRYPOINT [ "/bin/server" ] diff --git a/README.Docker.md b/README.Docker.md new file mode 100644 index 0000000..7e31be9 --- /dev/null +++ b/README.Docker.md @@ -0,0 +1,22 @@ +### Building and running your application + +When you're ready, start your application by running: +`docker compose up --build`. + +Your application will be available at http://localhost:5000. + +### Deploying your application to the cloud + +First, build your image, e.g.: `docker build -t myapp .`. +If your cloud uses a different CPU architecture than your development +machine (e.g., you are on a Mac M1 and your cloud provider is amd64), +you'll want to build the image for that platform, e.g.: +`docker build --platform=linux/amd64 -t myapp .`. + +Then, push it to your registry, e.g. `docker push myregistry.com/myapp`. + +Consult Docker's [getting started](https://docs.docker.com/go/get-started-sharing/) +docs for more detail on building and pushing. + +### References +* [Docker's Go guide](https://docs.docker.com/language/golang/) \ No newline at end of file diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..424e5dc --- /dev/null +++ b/compose.yaml @@ -0,0 +1,77 @@ +# Comments are provided throughout this file to help you get started. +# If you need more help, visit the Docker Compose reference guide at +# https://docs.docker.com/go/compose-spec-reference/ + +# Here the instructions define your application as a service called "server". +# This service is built from the Dockerfile in the current directory. +# You can add other services your application may depend on here, such as a +# database or a cache. For examples, see the Awesome Compose repository: +# https://github.com/docker/awesome-compose + +services: + server: + build: + context: . + target: final + env_file: + - path: ./.env + required: true # default + - path: ./override.env + required: false # optional + environment: + - APP_ENV=${APP_ENV} + - PORT=${PORT} + - DB_URL=${DB_URL} + - DB_NAME=${DB_NAME} + - REDIS_DB_HOST=redis + - REDIS_DB_PORT=${REDIS_DB_PORT} + - REDIS_DB_PASSWORD=${REDIS_DB_PASSWORD} + - REDIS_DB_DATABASE=${REDIS_DB_DATABASE} + - ACCESS_TOKEN_KEY=${ACCESS_TOKEN_KEY} + - REFRESH_TOKEN_KEY=${REFRESH_TOKEN_KEY} + - FIREBASE_JSON_STRING=${FIREBASE_JSON_STRING} + - FIREBASE_DATABASE_URL=${FIREBASE_DATABASE_URL} + ports: + - 5000:${PORT} + + redis: + image: redis:alpine + command: + - '--port ${REDIS_DB_PORT}' + - '--requirepass ${REDIS_DB_PASSWORD}' + ports: + - '${REDIS_DB_PORT}:${REDIS_DB_PORT}' + +# The commented out section below is an example of how to define a PostgreSQL +# database that your application can use. `depends_on` tells Docker Compose to +# start the database before your application. The `db-data` volume persists the +# database data between container restarts. The `db-password` secret is used +# to set the database password. You must create `db/password.txt` and add +# a password of your choosing to it before running `docker compose up`. +# depends_on: +# db: +# condition: service_healthy +# db: +# image: postgres +# restart: always +# user: postgres +# secrets: +# - db-password +# volumes: +# - db-data:/var/lib/postgresql/data +# environment: +# - POSTGRES_DB=example +# - POSTGRES_PASSWORD_FILE=/run/secrets/db-password +# expose: +# - 5432 +# healthcheck: +# test: [ "CMD", "pg_isready" ] +# interval: 10s +# timeout: 5s +# retries: 5 +# volumes: +# db-data: +# secrets: +# db-password: +# file: db/password.txt + diff --git a/go.mod b/go.mod index 94d87b6..b672596 100644 --- a/go.mod +++ b/go.mod @@ -1,78 +1,94 @@ module github.com/rpsoftech/bullion-server -go 1.21.6 +go 1.22.7 + +toolchain go1.23.3 require ( - cloud.google.com/go/firestore v1.15.0 - firebase.google.com/go/v4 v4.13.0 - github.com/go-faker/faker/v4 v4.3.0 - github.com/go-playground/validator/v10 v10.19.0 - github.com/gofiber/fiber/v2 v2.52.2 + cloud.google.com/go/firestore v1.17.0 + firebase.google.com/go/v4 v4.15.1 + github.com/go-faker/faker/v4 v4.5.0 + github.com/go-playground/validator/v10 v10.23.0 + github.com/gofiber/fiber/v2 v2.52.5 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/google/uuid v1.6.0 github.com/joho/godotenv v1.5.1 github.com/mitchellh/mapstructure v1.5.0 - github.com/redis/go-redis/v9 v9.5.1 - go.mongodb.org/mongo-driver v1.14.0 - golang.org/x/crypto v0.21.0 - google.golang.org/api v0.169.0 + github.com/redis/go-redis/v9 v9.7.0 + go.mongodb.org/mongo-driver v1.17.1 + golang.org/x/crypto v0.30.0 + google.golang.org/api v0.210.0 ) require ( - cloud.google.com/go v0.112.1 // indirect - cloud.google.com/go/compute v1.25.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v1.1.6 // indirect - cloud.google.com/go/longrunning v0.5.5 // indirect - cloud.google.com/go/storage v1.39.1 // indirect + cel.dev/expr v0.19.1 // indirect + cloud.google.com/go v0.116.0 // indirect + cloud.google.com/go/auth v0.12.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect + cloud.google.com/go/compute/metadata v0.5.2 // indirect + cloud.google.com/go/iam v1.3.0 // indirect + cloud.google.com/go/longrunning v0.6.3 // indirect + cloud.google.com/go/monitoring v1.22.0 // indirect + cloud.google.com/go/storage v1.48.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect github.com/MicahParks/keyfunc v1.9.0 // indirect - github.com/andybalholm/brotli v1.1.0 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/andybalholm/brotli v1.1.1 // indirect + github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/envoyproxy/go-control-plane v0.13.1 // indirect + github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/go-logr/logr v1.4.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.7 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang-jwt/jwt/v4 v4.5.1 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect - github.com/google/s2a-go v0.1.7 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.12.2 // indirect - github.com/klauspost/compress v1.17.7 // indirect + github.com/google/s2a-go v0.1.8 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/gax-go/v2 v2.14.0 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect github.com/montanaflynn/stats v0.7.1 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.52.0 // indirect + github.com/valyala/fasthttp v1.57.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect - github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect + github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - go.opentelemetry.io/otel v1.24.0 // indirect - go.opentelemetry.io/otel/metric v1.24.0 // indirect - go.opentelemetry.io/otel/trace v1.24.0 // indirect - golang.org/x/net v0.22.0 // indirect - golang.org/x/oauth2 v0.18.0 // indirect - golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.5.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/appengine/v2 v2.0.5 // indirect - google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 // indirect - google.golang.org/grpc v1.62.1 // indirect - google.golang.org/protobuf v1.33.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.32.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/sdk v1.32.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect + golang.org/x/net v0.32.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/time v0.8.0 // indirect + google.golang.org/appengine/v2 v2.0.6 // indirect + google.golang.org/genproto v0.0.0-20241206012308-a4fef0638583 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241206012308-a4fef0638583 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583 // indirect + google.golang.org/grpc v1.68.1 // indirect + google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3 // indirect + google.golang.org/protobuf v1.35.2 // indirect ) diff --git a/go.sum b/go.sum index c0e572a..ae9c0f5 100644 --- a/go.sum +++ b/go.sum @@ -1,34 +1,56 @@ +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= -cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= -cloud.google.com/go/compute v1.25.0 h1:H1/4SqSUhjPFE7L5ddzHOfY2bCAvjwNRZPNl6Ni5oYU= -cloud.google.com/go/compute v1.25.0/go.mod h1:GR7F0ZPZH8EhChlMo9FkLd7eUTwEymjqQagxzilIxIE= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/firestore v1.15.0 h1:/k8ppuWOtNuDHt2tsRV42yI21uaGnKDEQnRFeBpbFF8= -cloud.google.com/go/firestore v1.15.0/go.mod h1:GWOxFXcv8GZUtYpWHw/w6IuYNux/BtmeVTMmjrm4yhk= -cloud.google.com/go/iam v1.1.6 h1:bEa06k05IO4f4uJonbB5iAgKTPpABy1ayxaIZV/GHVc= -cloud.google.com/go/iam v1.1.6/go.mod h1:O0zxdPeGBoFdWW3HWmBxJsk0pfvNM/p/qa82rWOGTwI= -cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMILWnOg= -cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= -cloud.google.com/go/storage v1.39.1 h1:MvraqHKhogCOTXTlct/9C3K3+Uy2jBmFYb3/Sp6dVtY= -cloud.google.com/go/storage v1.39.1/go.mod h1:xK6xZmxZmo+fyP7+DEF6FhNc24/JAe95OLyOHCXFH1o= -firebase.google.com/go/v4 v4.13.0 h1:meFz9nvDNh/FDyrEykoAzSfComcQbmnQSjoHrePRqeI= -firebase.google.com/go/v4 v4.13.0/go.mod h1:e1/gaR6EnbQfsmTnAMx1hnz+ninJIrrr/RAh59Tpfn8= +cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= +cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= +cloud.google.com/go/auth v0.12.0 h1:ARAD8r0lkiHw2go7kEnmviF6TOYhzLM+yDGcDt9mP68= +cloud.google.com/go/auth v0.12.0/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= +cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= +cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= +cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo= +cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= +cloud.google.com/go/firestore v1.17.0 h1:iEd1LBbkDZTFsLw3sTH50eyg4qe8eoG6CjocmEXO9aQ= +cloud.google.com/go/firestore v1.17.0/go.mod h1:69uPx1papBsY8ZETooc71fOhoKkD70Q1DwMrtKuOT/Y= +cloud.google.com/go/iam v1.3.0 h1:4Wo2qTaGKFtajbLpF6I4mywg900u3TLlHDb6mriLDPU= +cloud.google.com/go/iam v1.3.0/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY= +cloud.google.com/go/logging v1.12.0 h1:ex1igYcGFd4S/RZWOCU51StlIEuey5bjqwH9ZYjHibk= +cloud.google.com/go/logging v1.12.0/go.mod h1:wwYBt5HlYP1InnrtYI0wtwttpVU1rifnMT7RejksUAM= +cloud.google.com/go/longrunning v0.6.3 h1:A2q2vuyXysRcwzqDpMMLSI6mb6o39miS52UEG/Rd2ng= +cloud.google.com/go/longrunning v0.6.3/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI= +cloud.google.com/go/monitoring v1.22.0 h1:mQ0040B7dpuRq1+4YiQD43M2vW9HgoVxY98xhqGT+YI= +cloud.google.com/go/monitoring v1.22.0/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU= +cloud.google.com/go/storage v1.48.0 h1:FhBDHACbVtdPx7S/AbcKujPWiHvfO6F8OXGgCEbB2+o= +cloud.google.com/go/storage v1.48.0/go.mod h1:aFoDYNMAjv67lp+xcuZqjUKv/ctmplzQ3wJgodA7b+M= +cloud.google.com/go/trace v1.11.2 h1:4ZmaBdL8Ng/ajrgKqY5jfvzqMXbrDcBsUGXOT9aqTtI= +cloud.google.com/go/trace v1.11.2/go.mod h1:bn7OwXd4pd5rFuAnTrzBuoZ4ax2XQeG3qNgYmfCy0Io= +firebase.google.com/go/v4 v4.15.1 h1:tR2dzKw1MIfCfG2bhAyxa5KQ57zcE7iFKmeYClET6ZM= +firebase.google.com/go/v4 v4.15.1/go.mod h1:eunxbsh4UXI2rA8po3sOiebvWYuW0DVxAdZFO0I6wdY= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 h1:o90wcURuxekmXrtxmYWTyNla0+ZEHhud6DI1ZTxd1vI= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0/go.mod h1:6fTWu4m3jocfUZLYF5KsZC1TUfRvEjs7lM4crme/irw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0 h1:jJKWl98inONJAr/IZrdFQUWcwUO95DLY1XMD1ZIut+g= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.49.0/go.mod h1:l2fIqmwB+FKSfvn3bAD/0i+AXAxhIZjTK2svT/mgUXs= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 h1:GYUJLfvd++4DMuMhCFLgLXvFwofIxh/qOwoGuS/LTew= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0/go.mod h1:wRbFgBQUVm1YXrvWKofAEmq9HNJTDphbAaJSSX01KUI= github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID3+o= github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw= -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -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/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -37,16 +59,20 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu 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.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE= +github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/go-faker/faker/v4 v4.3.0 h1:UXOW7kn/Mwd0u6MR30JjUKVzguT20EB/hBOddAAO+DY= -github.com/go-faker/faker/v4 v4.3.0/go.mod h1:F/bBy8GH9NxOxMInug5Gx4WYeG6fHJZ8Ol/dhcpRub4= +github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA= +github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU= +github.com/go-faker/faker/v4 v4.5.0 h1:ARzAY2XoOL9tOUK+KSecUQzyXQsUaZHefjyF8x6YFHc= +github.com/go-faker/faker/v4 v4.5.0/go.mod h1:p3oq1GRjG2PZ7yqeFFfQI20Xm61DoBDlCA8RiSyZ48M= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -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/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -55,19 +81,19 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= -github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/gofiber/fiber/v2 v2.52.2 h1:b0rYH6b06Df+4NyrbdptQL8ifuxw/Tf2DgfkZkDaxEo= -github.com/gofiber/fiber/v2 v2.52.2/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= +github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= +github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo= +github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -79,7 +105,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 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/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= @@ -93,21 +118,21 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ 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/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= -github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= -github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= +github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= +github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go/v2 v2.12.2 h1:mhN09QQW1jEWeMF74zGR81R30z4VJzjZsfkUhuHF+DA= -github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= +github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o= +github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= -github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -115,17 +140,19 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= -github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -135,12 +162,12 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE 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= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0= -github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ= +github.com/valyala/fasthttp v1.57.0 h1:Xw8SjWGEP/+wAAgyy5XTvgrWlOD1+TxbbvNADYCm1Tg= +github.com/valyala/fasthttp v1.57.0/go.mod h1:h6ZBaPRlzpZ6O3H5t2gEk1Qi33+TmLvfwgLLp0t9CpE= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= @@ -149,31 +176,38 @@ github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= -github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= -go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM= +go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= -go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= -go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= -go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= -go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= -go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= -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/contrib/detectors/gcp v1.32.0 h1:P78qWqkLSShicHmAzfECaTgvslqHxblNE9j62Ws1NK8= +go.opentelemetry.io/contrib/detectors/gcp v1.32.0/go.mod h1:TVqo0Sda4Cv8gCIixd7LuLwW4EylumVWfhjZJjDD4DU= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0 h1:qtFISDHKolvIxzSs0gIaiPUPR0Cucb0F2coHC7ZLdps= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.57.0/go.mod h1:Y+Pop1Q6hCOnETWTW4NROK/q1hv50hM7yDaUTjG8lp8= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT6POBP+gh8RUH19EOTnQIor5QE0uSRPtzCpSw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -188,17 +222,17 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL 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.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.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-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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -209,18 +243,18 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -230,32 +264,30 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 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-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.169.0 h1:QwWPy71FgMWqJN/l6jVlFHUa29a7dcUy02I8o799nPY= -google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg= +google.golang.org/api v0.210.0 h1:HMNffZ57OoZCRYSbdWVRoqOa8V8NIHLL0CzdBPLztWk= +google.golang.org/api v0.210.0/go.mod h1:B9XDZGnx2NtyjzVkOVTGrFSAVZgPcbedzKg/gTLwqBs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/appengine/v2 v2.0.5 h1:4C+F3Cd3L2nWEfSmFEZDPjQvDwL8T0YCeZBysZifP3k= -google.golang.org/appengine/v2 v2.0.5/go.mod h1:WoEXGoXNfa0mLvaH5sV3ZSGXwVmy8yf7Z1JKf3J3wLI= +google.golang.org/appengine/v2 v2.0.6 h1:LvPZLGuchSBslPBp+LAhihBeGSiRh1myRoYK4NtuBIw= +google.golang.org/appengine/v2 v2.0.6/go.mod h1:WoEXGoXNfa0mLvaH5sV3ZSGXwVmy8yf7Z1JKf3J3wLI= 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-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= -google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7 h1:oqta3O3AnlWbmIE3bFnWbu4bRxZjfbWCp0cKSuZh01E= -google.golang.org/genproto/googleapis/api v0.0.0-20240311173647-c811ad7063a7/go.mod h1:VQW3tUculP/D4B+xVCo+VgSq8As6wA9ZjHl//pmk+6s= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 h1:8EeVk1VKMD+GD/neyEHGmz7pFblqPjHoi+PGQIlLx2s= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/genproto v0.0.0-20241206012308-a4fef0638583 h1:pjPnE7Rv3PAwHISLRJhA3HQTnM2uu5qcnroxTkRb5G8= +google.golang.org/genproto v0.0.0-20241206012308-a4fef0638583/go.mod h1:dW27OyXi0Ph+N43jeCWMFC86aTT5VgdeQtOSf0Hehdw= +google.golang.org/genproto/googleapis/api v0.0.0-20241206012308-a4fef0638583 h1:v+j+5gpj0FopU0KKLDGfDo9ZRRpKdi5UBrCP0f76kuY= +google.golang.org/genproto/googleapis/api v0.0.0-20241206012308-a4fef0638583/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583 h1:IfdSdTcLFy4lqUQrQJLkLt1PB+AsqVz6lwkWPzWEz10= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 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.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= -google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3 h1:hUfOButuEtpc0UvYiaYRbNwxVYr0mQQOWq6X8beJ9Gc= +google.golang.org/grpc/stats/opentelemetry v0.0.0-20241028142157-ada6787961b3/go.mod h1:jzYlkSMbKypzuu6xoAEijsNVo9ZeDF1u/zCfFgsx7jg= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -266,10 +298,9 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 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.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/main.go b/main.go index d669fd7..d12eb2c 100644 --- a/main.go +++ b/main.go @@ -28,6 +28,7 @@ func main() { defer deferMainFunc() app := fiber.New(fiber.Config{ + ServerHeader: "Bullion Server V1.0.0", ErrorHandler: func(c *fiber.Ctx, err error) error { mappedError, ok := err.(*interfaces.RequestError) if !ok { @@ -41,19 +42,22 @@ func main() { return c.Status(mappedError.StatusCode).JSON(mappedError) }, }) - + // TODO Add middleware to recover from panics https://docs.gofiber.io/api/middleware/recover app.Use(logger.New()) app.Use(middleware.TokenDecrypter) - app.Get("/token", func(c *fiber.Ctx) error { - a, _ := services.AccessTokenService.GenerateToken(jwt.GeneralUserAccessRefreshToken{ - Role: interfaces.ROLE_ADMIN, - RegisteredClaims: &j.RegisteredClaims{ - IssuedAt: j.NewNumericDate(time.Now()), - }, - }) - return c.SendString(a) - }) + if env.Env.APP_ENV != env.APP_ENV_PRODUCTION { + + app.Get("/token", func(c *fiber.Ctx) error { + a, _ := services.AccessTokenService.GenerateToken(jwt.GeneralUserAccessRefreshToken{ + Role: interfaces.ROLE_ADMIN, + RegisteredClaims: &j.RegisteredClaims{ + IssuedAt: j.NewNumericDate(time.Now()), + }, + }) + return c.SendString(a) + }).Name("Temp Admin Access Token") + } // repos.BullionSiteInfoRepo.Save(interfaces.CreateNewBullionSiteInfo("Akshat Bullion", "https://akshatbullion.com").AddGeneralUserInfo(true, true)) // app.Get("/", func(c *fiber.Ctx) error { // bull := repos.BullionSiteInfoRepo.FindOne("ad3cee16-e8d7-4a27-a060-46d99c133273") @@ -71,6 +75,7 @@ func main() { if env.Env.APP_ENV == env.APP_ENV_LOCAL || env.Env.APP_ENV == env.APP_ENV_DEVELOPE { hostAndPort = "127.0.0.1" } + hostAndPort = hostAndPort + ":" + strconv.Itoa(env.Env.PORT) app.Listen(hostAndPort) // log.Fatal(app.Listen(":" + strconv.Itoa(env.Env.PORT))) diff --git a/src/apis/apis.go b/src/apis/apis.go index d57dda2..20040c6 100644 --- a/src/apis/apis.go +++ b/src/apis/apis.go @@ -4,9 +4,11 @@ import ( "github.com/gofiber/fiber/v2" "github.com/rpsoftech/bullion-server/src/apis/auth" "github.com/rpsoftech/bullion-server/src/apis/data" + "github.com/rpsoftech/bullion-server/src/apis/order" ) func AddApis(app fiber.Router) { auth.AddAuthPackages(app.Group("/auth")) data.AddDataPackage(app.Group("/data")) + order.AddOrderPackage(app.Group("/order")) } diff --git a/src/apis/auth/get-bullion-details.go b/src/apis/auth/get-bullion-details.go index 60daafe..d3b1e23 100644 --- a/src/apis/auth/get-bullion-details.go +++ b/src/apis/auth/get-bullion-details.go @@ -23,7 +23,7 @@ func apiGetBullionDetailsByShortName(c *fiber.Ctx) error { if entity, err := services.BullionDetailsService.GetBullionDetailsByShortName(shortName); err != nil { return err } else { - return c.JSON(entity) + return c.JSON(entity.BullionPublicInfo) } } diff --git a/src/apis/data/index.go b/src/apis/data/index.go index c29e8c9..ebd2efe 100644 --- a/src/apis/data/index.go +++ b/src/apis/data/index.go @@ -14,6 +14,7 @@ import ( func AddDataPackage(router fiber.Router) { router.Use(middleware.AllowOnlyValidTokenMiddleWare) router.Use(middleware.AllowAllUsers.Validate) + product.AddRateApi(router.Group("/rates")) { productGroup := router.Group("/product") product.AddProduct(productGroup) diff --git a/src/apis/data/product/add-update-bank-calc.api.go b/src/apis/data/product/add-update-bank-calc.api.go new file mode 100644 index 0000000..f1539c8 --- /dev/null +++ b/src/apis/data/product/add-update-bank-calc.api.go @@ -0,0 +1,35 @@ +package product + +import ( + "github.com/gofiber/fiber/v2" + "github.com/rpsoftech/bullion-server/src/interfaces" + "github.com/rpsoftech/bullion-server/src/services" + "github.com/rpsoftech/bullion-server/src/utility" +) + +type apiAddUpdateBankCalcBody struct { + GOLD_SPOT *interfaces.BankRateCalcBase `bson:"goldSpot" json:"goldSpot" validate:"required"` + SILVER_SPOT *interfaces.BankRateCalcBase `bson:"silverSpot" json:"silverSpot" validate:"required"` +} + +func apiAddUpdateBankCalc(c *fiber.Ctx) error { + body := new(apiAddUpdateBankCalcBody) + c.BodyParser(body) + if err := utility.ValidateReqInput(body); err != nil { + return err + } + userId, err := interfaces.ExtractTokenUserIdFromCtx(c) + if err != nil { + return err + } + bullionId, err := interfaces.ExtractBullionIdFromCtx(c) + if err != nil { + return err + } + entity, err := services.BankRateCalcService.SaveBankRateCalc(body.GOLD_SPOT, body.SILVER_SPOT, bullionId, userId) + if err != nil { + return err + } else { + return c.JSON(entity) + } +} diff --git a/src/apis/data/product/get-bank-calc.api.go b/src/apis/data/product/get-bank-calc.api.go new file mode 100644 index 0000000..94d6e01 --- /dev/null +++ b/src/apis/data/product/get-bank-calc.api.go @@ -0,0 +1,31 @@ +package product + +import ( + "net/http" + + "github.com/gofiber/fiber/v2" + "github.com/rpsoftech/bullion-server/src/interfaces" + "github.com/rpsoftech/bullion-server/src/services" +) + +func apiGetBankCalc(c *fiber.Ctx) error { + + id := c.Query("bullionId") + if id == "" { + return &interfaces.RequestError{ + StatusCode: http.StatusBadRequest, + Code: interfaces.ERROR_INVALID_INPUT, + Message: "Please Pass Valid Bullion Id", + Name: "INVALID_INPUT", + } + } + if err := interfaces.ValidateBullionIdMatchingInToken(c, id); err != nil { + return err + } + entity, err := services.BankRateCalcService.GetBankRateCalcByBullionId(id) + if err != nil { + return err + } else { + return c.JSON(entity) + } +} diff --git a/src/apis/data/product/get-live.rate.api.go b/src/apis/data/product/get-live.rate.api.go new file mode 100644 index 0000000..33e5480 --- /dev/null +++ b/src/apis/data/product/get-live.rate.api.go @@ -0,0 +1,10 @@ +package product + +import ( + "github.com/gofiber/fiber/v2" + "github.com/rpsoftech/bullion-server/src/services" +) + +func apiGetLiveRate(c *fiber.Ctx) error { + return c.JSON(services.LiveRateService.LastRateMap) +} diff --git a/src/apis/data/product/index.go b/src/apis/data/product/index.go index 651f306..d4f6491 100644 --- a/src/apis/data/product/index.go +++ b/src/apis/data/product/index.go @@ -8,9 +8,15 @@ import ( func AddProduct(router fiber.Router) { router.Get("/getAll", apiGetProducts) router.Get("/getProduct", apiGetProducts) + router.Get("/getBankCalc", apiGetBankCalc) adminGroup := router.Use(middleware.AllowAllAdmins.Validate) adminGroup.Put("/add", apiAddNewProduct) adminGroup.Patch("/update", apiUpdateProducts) adminGroup.Patch("/updateCalcSnapShot", apiUpdateProductCalcSnapshot) adminGroup.Patch("/updateSequence", apiUpdateProductSequence) + adminGroup.Patch("/updateBankCalc", apiAddUpdateBankCalc) +} + +func AddRateApi(router fiber.Router) { + router.Get("/", apiGetLiveRate) } diff --git a/src/apis/data/trade-user-group/index.go b/src/apis/data/trade-user-group/index.go index 41c3730..885533b 100644 --- a/src/apis/data/trade-user-group/index.go +++ b/src/apis/data/trade-user-group/index.go @@ -11,8 +11,8 @@ func AddTradeUserGroupAPIs(router fiber.Router) { { adminGroup := router.Group("/admin", middleware.AllowOnlyBigAdmins.Validate) adminGroup.Put("/createNewTradeGroup", apiCreateNewTradeGroup) - adminGroup.Post("/updateTradeGroup", apiUpdateTradeUserGroup) - adminGroup.Post("/updateTradeGroupProductMap", apiUpdateTradeUserGroupMap) + adminGroup.Patch("/updateTradeGroup", apiUpdateTradeUserGroup) + adminGroup.Patch("/updateTradeGroupProductMap", apiUpdateTradeUserGroupMap) } adminAndTradeGroup := router.Use(middleware.AllowAllAdminsAndTradeUsers.Validate) adminAndTradeGroup.Get("/getTradeGroupDetailsByID", apiGetTradeGroupDetailsById) diff --git a/src/apis/data/trade-user/index.go b/src/apis/data/trade-user/index.go index 0b0ea37..238af0d 100644 --- a/src/apis/data/trade-user/index.go +++ b/src/apis/data/trade-user/index.go @@ -8,6 +8,6 @@ import ( func AddTradeUserAPIs(router fiber.Router) { adminGroup := router.Use(middleware.AllowOnlyBigAdmins.Validate) adminGroup.Get("/getTradeUser", apiGetTradeUserDetails) - adminGroup.Post("/updateTradeUserDetails", apiUpdateTradeUserDetails) - adminGroup.Post("/updateTradeUserStatus", apiChangeTradeUserStatus) + adminGroup.Patch("/updateTradeUserDetails", apiUpdateTradeUserDetails) + adminGroup.Patch("/updateTradeUserStatus", apiChangeTradeUserStatus) } diff --git a/src/apis/order/admin-order/index.go b/src/apis/order/admin-order/index.go new file mode 100644 index 0000000..aa2308d --- /dev/null +++ b/src/apis/order/admin-order/index.go @@ -0,0 +1,9 @@ +package adminorder + +import ( + "github.com/gofiber/fiber/v2" +) + +func AddAdminOrderRouter(router fiber.Router) { + +} diff --git a/src/apis/order/index.go b/src/apis/order/index.go new file mode 100644 index 0000000..52fb67c --- /dev/null +++ b/src/apis/order/index.go @@ -0,0 +1,14 @@ +package order + +import ( + "github.com/gofiber/fiber/v2" + adminorder "github.com/rpsoftech/bullion-server/src/apis/order/admin-order" + "github.com/rpsoftech/bullion-server/src/apis/order/user" + "github.com/rpsoftech/bullion-server/src/middleware" +) + +func AddOrderPackage(router fiber.Router) { + router.Use(middleware.AllowOnlyValidTokenMiddleWare) + adminorder.AddAdminOrderRouter(router.Group("/admin", middleware.AllowOnlyBigAdmins.Validate)) + user.AddUserOrderApis(router.Group("/user", middleware.AllowAllAdminsAndTradeUsers.Validate)) +} diff --git a/src/apis/order/user/index.go b/src/apis/order/user/index.go new file mode 100644 index 0000000..89591eb --- /dev/null +++ b/src/apis/order/user/index.go @@ -0,0 +1,8 @@ +package user + +import "github.com/gofiber/fiber/v2" + +func AddUserOrderApis(router fiber.Router) { + + // router.Use(middleware.AllowAllAdminsAndTradeUsers.Validate) +} diff --git a/src/env/index.go b/src/env/index.go index 8fbc332..5ed90f2 100644 --- a/src/env/index.go +++ b/src/env/index.go @@ -13,7 +13,8 @@ type EnvInterface struct { PORT int `json:"PORT" validate:"required,port"` DB_URL string `json:"DB_URL" validate:"required,url"` DB_NAME string `json:"DB_NAME_KEY" validate:"required,min=3"` - REDIS_DB_URL string `json:"REDIS_DB_URL" validate:"required"` + REDIS_DB_HOST string `json:"REDIS_DB_HOST" validate:"required"` + REDIS_DB_PORT int `json:"REDIS_DB_PORT" validate:"required,port"` REDIS_DB_PASSWORD string `json:"REDIS_DB_PASSWORD" validate:"required"` REDIS_DB_DATABASE int `json:"REDIS_DB_DATABASE" validate:"min=0,max=100"` ACCESS_TOKEN_KEY string `json:"ACCESS_TOKEN_KEY" validate:"required,min=100"` @@ -36,12 +37,18 @@ func init() { // ... handle error panic(err) } + redis_DB_PORT, err := strconv.Atoi(os.Getenv(redis_DB_PORT_KEY)) + if err != nil { + // ... handle error + panic(err) + } Env = &EnvInterface{ APP_ENV: appEnv, PORT: PORT, DB_NAME: os.Getenv(db_NAME_KEY), DB_URL: os.Getenv(db_URL_KEY), - REDIS_DB_URL: os.Getenv(redis_DB_URL_KEY), + REDIS_DB_PORT: redis_DB_PORT, + REDIS_DB_HOST: os.Getenv(redis_DB_HOST_KEY), REDIS_DB_PASSWORD: os.Getenv(redis_DB_PASSWORD_KEY), REDIS_DB_DATABASE: redis_DB_DATABASE, ACCESS_TOKEN_KEY: os.Getenv(access_TOKEN_KEY), @@ -51,7 +58,7 @@ func init() { } errs := validator.Validator.Validate(Env) if len(errs) > 0 { - println(err) - panic(err) + println(errs) + panic(errs[0]) } } diff --git a/src/env/keys.go b/src/env/keys.go index 4455976..09115ed 100644 --- a/src/env/keys.go +++ b/src/env/keys.go @@ -8,7 +8,8 @@ const access_TOKEN_KEY = "ACCESS_TOKEN_KEY" const refresh_TOKEN_KEY = "REFRESH_TOKEN_KEY" const firebase_JSON_STRING_KEY = "FIREBASE_JSON_STRING" const firebase_DATABASE_URL_KEY = "FIREBASE_DATABASE_URL" -const redis_DB_URL_KEY = "REDIS_DB_URL" +const redis_DB_HOST_KEY = "REDIS_DB_HOST" +const redis_DB_PORT_KEY = "REDIS_DB_PORT" const redis_DB_PASSWORD_KEY = "REDIS_DB_PASSWORD" const redis_DB_DATABASE_KEY = "REDIS_DB_DATABASE" diff --git a/src/events/bank-rate-calc.event.go b/src/events/bank-rate-calc.event.go new file mode 100644 index 0000000..f138924 --- /dev/null +++ b/src/events/bank-rate-calc.event.go @@ -0,0 +1,29 @@ +package events + +import ( + "github.com/rpsoftech/bullion-server/src/interfaces" +) + +type bankRateCalcEvent struct { + *BaseEvent `bson:"inline"` +} + +func (base *bankRateCalcEvent) Add() *bankRateCalcEvent { + base.ParentNames = []string{base.EventName, "BankRateCalcEvent"} + base.BaseEvent.CreateBaseEvent() + return base +} + +func BankRateCalcUpdatedEvent(entity *interfaces.BankRateCalcEntity, adminId string) *BaseEvent { + event := &bankRateCalcEvent{ + BaseEvent: &BaseEvent{ + BullionId: entity.BullionId, + KeyId: entity.ID, + AdminId: adminId, + Payload: entity, + EventName: "BankRateCalcUpdatedEvent", + }, + } + event.Add() + return event.BaseEvent +} diff --git a/src/events/order.events.go b/src/events/order.events.go new file mode 100644 index 0000000..e5f1828 --- /dev/null +++ b/src/events/order.events.go @@ -0,0 +1,53 @@ +package events + +import ( + "github.com/rpsoftech/bullion-server/src/interfaces" +) + +type OrderEvent struct { + *BaseEvent `bson:"inline"` +} + +func (base *OrderEvent) Add() *OrderEvent { + base.ParentNames = []string{base.EventName, "OrderEvent"} + base.BaseEvent.CreateBaseEvent() + return base +} + +func orderStatusUpdatedEvent(entity *interfaces.OrderEntity, adminId string, eventName string) *BaseEvent { + event := &OrderEvent{ + BaseEvent: &BaseEvent{ + BullionId: entity.BullionId, + KeyId: entity.ID, + AdminId: adminId, + Payload: entity, + EventName: eventName, + }, + } + event.Add() + return event.BaseEvent +} + +func OrderPlacedEvent(entity *interfaces.OrderEntity, adminId string) *BaseEvent { + return orderStatusUpdatedEvent(entity, adminId, "OrderPlacedEvent") +} + +func LimitPlacedEvent(entity *interfaces.OrderEntity, adminId string) *BaseEvent { + return orderStatusUpdatedEvent(entity, adminId, "LimitPlacedEvent") +} + +func LimitDeletedEvent(entity *interfaces.OrderEntity, adminId string) *BaseEvent { + return orderStatusUpdatedEvent(entity, adminId, "LimitDeletedEvent") +} + +func LimitPassedEvent(entity *interfaces.OrderEntity, adminId string) *BaseEvent { + return orderStatusUpdatedEvent(entity, adminId, "LimitPassedEvent") +} + +func LimitCanceledEvent(entity *interfaces.OrderEntity, adminId string) *BaseEvent { + return orderStatusUpdatedEvent(entity, adminId, "LimitCanceledEvent") +} + +func OrderDeliveredEvent(entity *interfaces.OrderEntity, adminId string) *BaseEvent { + return orderStatusUpdatedEvent(entity, adminId, "OrderDeliveredEvent") +} diff --git a/src/interfaces/bank-rate.calc.entity.go b/src/interfaces/bank-rate.calc.entity.go new file mode 100644 index 0000000..5cf66c7 --- /dev/null +++ b/src/interfaces/bank-rate.calc.entity.go @@ -0,0 +1,35 @@ +package interfaces + +type ( + BankRateCalcEntity struct { + *BaseEntity `bson:"inline"` + BullionId string `bson:"bullionId" json:"bullionId" validate:"required,uuid"` + GOLD_SPOT *BankRateCalcBase `bson:"goldSpot" json:"goldSpot" validate:"required"` + SILVER_SPOT *BankRateCalcBase `bson:"silverSpot" json:"silverSpot" validate:"required"` + } + + BankRateCalcBase struct { + Premium float64 `bson:"premium" json:"premium" validate:"min=-1000,max=1000"` + Conv float64 `bson:"conv" json:"conv" validate:"required,min=0.1,max=1000"` + Duty int `bson:"duty" json:"duty" validate:"min=0"` + Margin int `bson:"margin" json:"margin" validate:"min=0"` + Gst int `bson:"gst" json:"gst" validate:"min=0,max=100"` + DivBy int `bson:"divBy" json:"divBy" validate:"min=1,max=100"` + MultiBy int `bson:"multiBy" json:"multiBy" validate:"min=1,max=100"` + } +) + +func (b *BankRateCalcBase) CalculatePrice(symbolPrice, inrPrice float64) float64 { + premium, conv, margin, duty, gst, divBy, multiBy := b.Premium, b.Conv, float64(b.Margin), float64(b.Duty), float64(b.Gst), float64(b.DivBy), float64(b.MultiBy) + finalPrice := (symbolPrice + premium) * conv * inrPrice + finalPrice += margin + duty + finalPrice *= 1 + gst/100 + finalPrice /= divBy + return finalPrice * multiBy +} + +func (b *BankRateCalcEntity) CreateNewBankRateCalc() *BankRateCalcEntity { + b.BaseEntity = &BaseEntity{} + b.createNewId() + return b +} diff --git a/src/interfaces/base-entity.go b/src/interfaces/base-entity.go index 587f415..37da61e 100644 --- a/src/interfaces/base-entity.go +++ b/src/interfaces/base-entity.go @@ -32,17 +32,17 @@ type BaseEntity struct { // return json.Marshal(base) // } -func (b *BaseEntity) AddTimeStamps() (r *BaseEntity) { +func (b *BaseEntity) AddTimeStamps() *BaseEntity { b.CreatedAtExported = b.CreatedAt b.ModifiedAtExported = b.ModifiedAt return b } -func (b *BaseEntity) RestoreTimeStamp() (r *BaseEntity) { +func (b *BaseEntity) RestoreTimeStamp() *BaseEntity { b.CreatedAt = b.CreatedAtExported b.ModifiedAt = b.ModifiedAtExported return b } -func (b *BaseEntity) createNewId() (r *BaseEntity) { +func (b *BaseEntity) createNewId() *BaseEntity { id := uuid.New().String() b.ID = id b.CreatedAt = time.Now() @@ -50,7 +50,7 @@ func (b *BaseEntity) createNewId() (r *BaseEntity) { return b } -func (b *BaseEntity) Updated() (r *BaseEntity) { +func (b *BaseEntity) Updated() *BaseEntity { b.ModifiedAt = time.Now() return b } diff --git a/src/interfaces/base-enum-validator.interface.go b/src/interfaces/base-enum-validator.interface.go new file mode 100644 index 0000000..7fca385 --- /dev/null +++ b/src/interfaces/base-enum-validator.interface.go @@ -0,0 +1,10 @@ +package interfaces + +type EnumValidatorBase struct { + Data map[string]interface{} +} + +func (v *EnumValidatorBase) Validate(value string) bool { + _, ok := v.Data[value] + return ok +} diff --git a/src/interfaces/base-symbol.enum.go b/src/interfaces/base-symbol.enum.go index 0c35fb6..0dbb5e5 100644 --- a/src/interfaces/base-symbol.enum.go +++ b/src/interfaces/base-symbol.enum.go @@ -1,14 +1,20 @@ package interfaces -import "github.com/rpsoftech/bullion-server/src/validator" +import ( + "github.com/rpsoftech/bullion-server/src/validator" +) type SymbolsEnum string +type SourceSymbolEnum string + +const ( + SOURCE_SYMBOL_GOLD SourceSymbolEnum = "GOLD" + SOURCE_SYMBOL_SILVER SourceSymbolEnum = "SILVER" +) const ( SYMBOL_GOLD SymbolsEnum = "GOLD" SYMBOL_SILVER SymbolsEnum = "SILVER" - SYMBOL_GOLD_MCX SymbolsEnum = "GOLD_MCX" - SYMBOL_SILVER_MCX SymbolsEnum = "SILVER_MCX" SYMBOL_GOLD_NEXT SymbolsEnum = "GOLD_NEXT" SYMBOL_SILVER_NEXT SymbolsEnum = "SILVER_NEXT" SYMBOL_GOLD_SPOT SymbolsEnum = "GOLD_SPOT" @@ -17,26 +23,52 @@ const ( ) var ( - symbolEnumMap = map[string]SymbolsEnum{ - "GOLD": SYMBOL_GOLD, - "SILVER": SYMBOL_SILVER, - "GOLD_MCX": SYMBOL_GOLD_MCX, - "SILVER_MCX": SYMBOL_SILVER_MCX, - "GOLD_NEXT": SYMBOL_GOLD_NEXT, - "SILVER_NEXT": SYMBOL_SILVER_NEXT, - "GOLD_SPOT": SYMBOL_GOLD_SPOT, - "SILVER_SPOT": SYMBOL_SILVER_SPOT, - "INR": SYMBOL_INR, + SymbolsEnumArray = []SymbolsEnum{ + SYMBOL_GOLD, + SYMBOL_SILVER, + SYMBOL_GOLD_NEXT, + SYMBOL_SILVER_NEXT, + SYMBOL_GOLD_SPOT, + SYMBOL_SILVER_SPOT, + SYMBOL_INR, + } + + SourceSymbolEnumArray = []SourceSymbolEnum{ + SOURCE_SYMBOL_GOLD, + SOURCE_SYMBOL_SILVER, + } + + sourceSymbolEnumMap = EnumValidatorBase{ + Data: map[string]interface{}{ + "GOLD": SOURCE_SYMBOL_GOLD, + "SILVER": SOURCE_SYMBOL_SILVER, + }, + } + + symbolEnumMap = EnumValidatorBase{ + Data: map[string]interface{}{ + "GOLD": SYMBOL_GOLD, + "GOLD_SPOT": SYMBOL_GOLD_SPOT, + "GOLD_NEXT": SYMBOL_GOLD_NEXT, + "SILVER": SYMBOL_SILVER, + "SILVER_NEXT": SYMBOL_SILVER_NEXT, + "SILVER_SPOT": SYMBOL_SILVER_SPOT, + "INR": SYMBOL_INR, + }, } ) func init() { - validator.RegisterEnumValidatorFunc("SymbolsEnum", ValidateEnumSymbolsEnum) + validator.RegisterEnumValidatorFunc("SymbolsEnum", symbolEnumMap.Validate) + validator.RegisterEnumValidatorFunc("SourceSymbolEnum", sourceSymbolEnumMap.Validate) } -func ValidateEnumSymbolsEnum(value string) bool { - _, ok := symbolEnumMap[value] - return ok +func SymbolsEnumFromString(d string) SymbolsEnum { + enumValue := symbolEnumMap.Data[d] + if enumValue != nil { + return enumValue.(SymbolsEnum) + } + return "" } func (s SymbolsEnum) String() string { @@ -45,10 +77,6 @@ func (s SymbolsEnum) String() string { return "GOLD" case SYMBOL_SILVER: return "SILVER" - case SYMBOL_GOLD_MCX: - return "GOLD_MCX" - case SYMBOL_SILVER_MCX: - return "SILVER_MCX" case SYMBOL_GOLD_NEXT: return "GOLD_NEXT" case SYMBOL_SILVER_NEXT: @@ -69,8 +97,6 @@ func (s SymbolsEnum) IsValid() bool { case SYMBOL_GOLD, SYMBOL_SILVER, - SYMBOL_GOLD_MCX, - SYMBOL_SILVER_MCX, SYMBOL_GOLD_NEXT, SYMBOL_SILVER_NEXT, SYMBOL_GOLD_SPOT, @@ -81,3 +107,32 @@ func (s SymbolsEnum) IsValid() bool { return false } + +func (s SourceSymbolEnum) String() string { + switch s { + case SOURCE_SYMBOL_GOLD: + return "GOLD" + case SOURCE_SYMBOL_SILVER: + return "SILVER" + } + return "unknown" +} + +func (s SourceSymbolEnum) IsValid() bool { + switch s { + case + SOURCE_SYMBOL_GOLD, + SOURCE_SYMBOL_SILVER: + return true + } + + return false +} + +func (s SourceSymbolEnum) ToSymbolEnum() SymbolsEnum { + if SOURCE_SYMBOL_GOLD == s { + return SYMBOL_GOLD + } else { + return SYMBOL_SILVER + } +} diff --git a/src/interfaces/bullion-site-info.interface.go b/src/interfaces/bullion-site-info.interface.go index 421604e..7117c00 100644 --- a/src/interfaces/bullion-site-info.interface.go +++ b/src/interfaces/bullion-site-info.interface.go @@ -10,19 +10,28 @@ type bullionConfigs struct { DefaultGroupIdForTradeUser string `bson:"defaultGroupIdForTradeUser" json:"defaultGroupIdForTradeUser" validate:"required,uuid"` } +type limitConfigs struct { + FreezeTop int `bson:"freezeTop" json:"freezeTop" validate:"required"` + FreezeBottom int `bson:"freezeBottom" json:"freezeBottom" validate:"required"` +} + type BullionSiteBasicInfo struct { Name string `bson:"name" json:"name" validate:"required"` ShortName string `bson:"shortName" json:"shortName" validate:"required"` Domain string `bson:"domain" json:"domain" validate:"required"` } -type BullionSiteInfoEntity struct { +type BullionPublicInfo struct { *BaseEntity `bson:"inline"` *BullionSiteBasicInfo `bson:"inline"` - BullionConfigs *bullionConfigs `bson:"bullionConfigs" json:"-" validate:"required"` - GeneralUserInfo *bullionGeneralUserConfig `bson:"generalUserInfo" json:"-" validate:"required"` +} +type BullionSiteInfoEntity struct { + *BullionPublicInfo `bson:"inline"` + LimitConfigs *limitConfigs `bson:"limitConfigs" json:"-" validate:"required"` + BullionConfigs *bullionConfigs `bson:"bullionConfigs" json:"-" validate:"required"` + GeneralUserInfo *bullionGeneralUserConfig `bson:"generalUserInfo" json:"generalUserInfo" validate:"required"` } -func (b *BullionSiteInfoEntity) AddGeneralUserInfo(AutoApprove bool, AutoLogin bool) (r *BullionSiteInfoEntity) { +func (b *BullionSiteInfoEntity) AddGeneralUserInfo(AutoApprove bool, AutoLogin bool) *BullionSiteInfoEntity { b.GeneralUserInfo = &bullionGeneralUserConfig{ AutoApprove: AutoApprove, AutoLogin: AutoLogin, @@ -32,11 +41,26 @@ func (b *BullionSiteInfoEntity) AddGeneralUserInfo(AutoApprove bool, AutoLogin b func CreateNewBullionSiteInfo(name string, shortName string, domain string) *BullionSiteInfoEntity { b := BullionSiteInfoEntity{ - BaseEntity: &BaseEntity{}, - BullionSiteBasicInfo: &BullionSiteBasicInfo{ - Name: name, - ShortName: shortName, - Domain: domain, + BullionPublicInfo: &BullionPublicInfo{ + BaseEntity: &BaseEntity{}, + BullionSiteBasicInfo: &BullionSiteBasicInfo{ + Name: name, + ShortName: shortName, + Domain: domain, + }, + }, + BullionConfigs: &bullionConfigs{ + OTPLength: 5, + HaveCustomWhatsappAgent: true, + DefaultGroupIdForTradeUser: "", + }, + LimitConfigs: &limitConfigs{ + FreezeTop: 20, + FreezeBottom: 100, + }, + GeneralUserInfo: &bullionGeneralUserConfig{ + AutoApprove: false, + AutoLogin: true, }, } b.createNewId() diff --git a/src/interfaces/calculate-on-price.enum.go b/src/interfaces/calculate-on-price.enum.go index e4532c2..f50f93b 100644 --- a/src/interfaces/calculate-on-price.enum.go +++ b/src/interfaces/calculate-on-price.enum.go @@ -4,32 +4,43 @@ import "github.com/rpsoftech/bullion-server/src/validator" type CalculateOnPriceType string +type CalculationPriceMethod string + const ( - CALCULATE_ON_BID_ASK CalculateOnPriceType = "BID_ASK" - CALCULATE_ON_BID CalculateOnPriceType = "BID" - CALCULATE_ON_ASK CalculateOnPriceType = "ASK" + CALCULATION_PRICE_TYPE_FIX CalculationPriceMethod = "FIX" + CALCULATION_PRICE_TYPE_BANK CalculationPriceMethod = "BANK" + CALCULATION_PRICE_TYPE_EXEC CalculationPriceMethod = "EXEC" + CALCULATE_ON_BID_ASK CalculateOnPriceType = "BID_ASK" + CALCULATE_ON_BID CalculateOnPriceType = "BID" + CALCULATE_ON_ASK CalculateOnPriceType = "ASK" ) var ( - calculateOnPriceTypeMap = map[string]CalculateOnPriceType{ - "BID_ASK": CALCULATE_ON_BID_ASK, - "BID": CALCULATE_ON_BID, - "ASK": CALCULATE_ON_ASK, + calculationPriceMethodMap = EnumValidatorBase{ + Data: map[string]interface{}{ + "FIX": CALCULATION_PRICE_TYPE_FIX, + "BANK": CALCULATION_PRICE_TYPE_BANK, + "EXEC": CALCULATION_PRICE_TYPE_EXEC, + }, + } + calculateOnPriceTypeMap = EnumValidatorBase{ + Data: map[string]interface{}{ + "BID_ASK": CALCULATE_ON_BID_ASK, + "ASK": CALCULATE_ON_ASK, + "BID": CALCULATE_ON_BID, + }, } ) func init() { - validator.RegisterEnumValidatorFunc("CalculateOnPriceType", ValidateEnumCalculateOnPriceType) + validator.RegisterEnumValidatorFunc("CalculateOnPriceType", calculateOnPriceTypeMap.Validate) + validator.RegisterEnumValidatorFunc("CalculationPriceMethod", calculationPriceMethodMap.Validate) } // ValidateEnumCalculateOnPriceType checks if the given value is a valid calculateOnPriceType. // // value: the value to be validated as a calculateOnPriceType. // Returns: true if the value is a valid calculateOnPriceType, false otherwise. -func ValidateEnumCalculateOnPriceType(value string) bool { - _, ok := calculateOnPriceTypeMap[value] - return ok -} func (s CalculateOnPriceType) String() string { switch s { @@ -54,3 +65,26 @@ func (s CalculateOnPriceType) IsValid() bool { return false } + +func (s CalculationPriceMethod) String() string { + switch s { + case CALCULATION_PRICE_TYPE_FIX: + return "FIX" + case CALCULATION_PRICE_TYPE_BANK: + return "BANK" + case CALCULATION_PRICE_TYPE_EXEC: + return "EXEC" + } + return "unknown" +} + +func (s CalculationPriceMethod) IsValid() bool { + switch s { + case + CALCULATION_PRICE_TYPE_FIX, + CALCULATION_PRICE_TYPE_BANK, + CALCULATION_PRICE_TYPE_EXEC: + return true + } + return false +} diff --git a/src/interfaces/calculate-symbol.enum.go b/src/interfaces/calculate-symbol.enum.go index dd750ab..6692384 100644 --- a/src/interfaces/calculate-symbol.enum.go +++ b/src/interfaces/calculate-symbol.enum.go @@ -1,6 +1,8 @@ package interfaces -import "github.com/rpsoftech/bullion-server/src/validator" +import ( + "github.com/rpsoftech/bullion-server/src/validator" +) type BaseSymbolEnum string @@ -10,19 +12,16 @@ const ( ) var ( - baseSymbolEnumMap = map[string]BaseSymbolEnum{ - "GOLD": BASE_SYMBOL_GOLD, - "SILVER": BASE_SYMBOL_SILVER, + baseSymbolEnumMap = EnumValidatorBase{ + Data: map[string]interface{}{ + "GOLD": BASE_SYMBOL_GOLD, + "SILVER": BASE_SYMBOL_SILVER, + }, } ) func init() { - validator.RegisterEnumValidatorFunc("BaseSymbolEnum", ValidateBaseSymbolEnum) -} - -func ValidateBaseSymbolEnum(value string) bool { - _, ok := baseSymbolEnumMap[value] - return ok + validator.RegisterEnumValidatorFunc("BaseSymbolEnum", baseSymbolEnumMap.Validate) } func (s BaseSymbolEnum) String() string { diff --git a/src/interfaces/error-code.enum.go b/src/interfaces/error-code.enum.go index af33e89..41b6989 100644 --- a/src/interfaces/error-code.enum.go +++ b/src/interfaces/error-code.enum.go @@ -33,4 +33,13 @@ const ( ERROR_OTP_REQ_NOT_FOUND = 228 ERROR_TOO_MANY_ATTEMPTS = 229 ERROR_WHILE_FETCHING_MESSAGE_TEMPLATE = 230 + ERROR_INVALID_ORDER_STATUS_FOR_DELIVERY = 231 + ERROR_INVALID_WEIGHT_FOR_DELIVERY = 232 + ERROR_INSUFFICIENT_MARGIN = 233 + ERROR_INVALID_VOLUME = 234 + ERROR_TRADING_IS_DISABLED = 235 + ERROR_TRADING_IS_DISABLED_FOR_GROUP = 236 + ERROR_TRADING_IS_DISABLED_FOR_PRODUCT = 237 + ERROR_GROUP_MAP_NOT_FOUND = 238 + ERROR_LIVE_RATE_NOT_FOUND = 239 ) diff --git a/src/interfaces/general-type.go b/src/interfaces/general-type.go new file mode 100644 index 0000000..f72e678 --- /dev/null +++ b/src/interfaces/general-type.go @@ -0,0 +1,3 @@ +package interfaces + +type LiveRateData map[SymbolsEnum]map[PriceKeyEnum]float64 diff --git a/src/interfaces/general-user-req-auth-status.enum.go b/src/interfaces/general-user-req-auth-status.enum.go index 9852833..6822137 100644 --- a/src/interfaces/general-user-req-auth-status.enum.go +++ b/src/interfaces/general-user-req-auth-status.enum.go @@ -11,22 +11,18 @@ const ( ) var ( - generalUserAuthStatusMap = map[string]GeneralUserAuthStatus{ - "Authorized": GENERAL_USER_AUTH_STATUS_AUTHORIZED, - "Requested": GENERAL_USER_AUTH_STATUS_REQUESTED, - "Rejected": GENERAL_USER_AUTH_STATUS_REJECTED, + generalUserAuthStatusMap = EnumValidatorBase{ + Data: map[string]interface{}{ + "Authorized": GENERAL_USER_AUTH_STATUS_AUTHORIZED, + "Requested": GENERAL_USER_AUTH_STATUS_REQUESTED, + "Rejected": GENERAL_USER_AUTH_STATUS_REJECTED, + }, } ) func init() { - validator.RegisterEnumValidatorFunc("GeneralUserAuthStatus", validateEnumGeneralUserAuthStatus) + validator.RegisterEnumValidatorFunc("GeneralUserAuthStatus", generalUserAuthStatusMap.Validate) } - -func validateEnumGeneralUserAuthStatus(value string) bool { - _, ok := generalUserAuthStatusMap[value] - return ok -} - func (s GeneralUserAuthStatus) String() string { switch s { case GENERAL_USER_AUTH_STATUS_AUTHORIZED: diff --git a/src/interfaces/general-user-req.entity.go b/src/interfaces/general-user-req.entity.go index 553b964..bbe1e1c 100644 --- a/src/interfaces/general-user-req.entity.go +++ b/src/interfaces/general-user-req.entity.go @@ -7,7 +7,7 @@ type GeneralUserReqEntity struct { Status GeneralUserAuthStatus `bson:"status" json:"status" validate:"required,enum=GeneralUserAuthStatus"` } -func CreateNewGeneralUserReq(generalUserId string, bullionId string, status GeneralUserAuthStatus) (r *GeneralUserReqEntity) { +func CreateNewGeneralUserReq(generalUserId string, bullionId string, status GeneralUserAuthStatus) *GeneralUserReqEntity { b := &GeneralUserReqEntity{ GeneralUserId: generalUserId, BullionId: bullionId, diff --git a/src/interfaces/general-users.entity.go b/src/interfaces/general-users.entity.go index b2af7db..7fc12f2 100644 --- a/src/interfaces/general-users.entity.go +++ b/src/interfaces/general-users.entity.go @@ -18,7 +18,7 @@ type GeneralUserEntity struct { GeneralUser `bson:"inline"` } -func CreateNewGeneralUser(user GeneralUser) (r *GeneralUserEntity) { +func CreateNewGeneralUser(user GeneralUser) *GeneralUserEntity { b := &GeneralUserEntity{ UserRolesInterface: UserRolesInterface{ Role: ROLE_GENERAL_USER, diff --git a/src/interfaces/order-entity.interface.go b/src/interfaces/order-entity.interface.go new file mode 100644 index 0000000..82d99c6 --- /dev/null +++ b/src/interfaces/order-entity.interface.go @@ -0,0 +1,129 @@ +package interfaces + +import ( + "net/http" + "slices" + "time" +) + +type ( + OrderEntity struct { + *BaseEntity `bson:"inline"` + *OrderBase `bson:"inline"` + *LimitWatcherRequired `bson:"inline"` + *AfterSuccessOrder `bson:"inline,omitempty"` + *Identity `bson:"inline"` + FromAdmin *FromAdmin `bson:"inline,omitempty"` + DeliveryData *[]DeliveryData `bson:"inline,omitempty"` + } + OrderBase struct { + BullionId string `bson:"bullionId" json:"bullionId" validate:"required,uuid"` + OrderType OrderType `bson:"orderType" json:"orderType" validate:"required,enum=OrderStatus"` + OrderStatus OrderStatus `bson:"orderStatus" json:"orderStatus" validate:"required,enum=OrderStatus"` + BuySell BuySell `bson:"buySell" json:"buySell" validate:"required,enum=BuySell"` + ProductName string `bson:"productName" json:"productName" validate:"required,min=3,max=100"` + } + LimitWatcherRequired struct { + ProductId string `json:"productId" bson:"productId" validate:"required,uuid"` + GroupId string `json:"groupId" bson:"groupId" validate:"required,uuid"` + ProductGroupMapId string `json:"productGroupMapId" bson:"productGroupMapId" validate:"required,uuid"` + Volume float64 `json:"volume" bson:"volume" validate:"required"` + Weight int `json:"weight" bson:"weight" validate:"required"` + Price float64 `json:"price" bson:"price" validate:"required"` + } + + FromAdmin struct { + PlacedBy string `bson:"placedBy,omitempty" json:"placedBy,omitempty" validate:"uuid"` + AutoRate bool `bson:"autoRate,omitempty" json:"autoRate,omitempty" validate:"boolean"` + } + + AfterSuccessOrder struct { + CalcSnapShot *CalcSnapshotStruct `json:"calcSnapShot" bson:"calcSnapShot" validate:"required"` + CalcSnapShotId string `json:"calcSnapShotId" bson:"calcSnapShotId" validate:"required,uuid"` + PgmSnapShot *GroupPremiumBase `json:"pgmSnapShot" bson:"pgmSnapShot" validate:"required"` + BankRateCalc *BankRateCalcBase `json:"bankRateCalc" bson:"bankRateCalc"` + McxPrice float64 `json:"mcxPrice" bson:"mcxPrice" validate:"required"` + ExecutedOn time.Time `json:"executedOn" bson:"executedOn" validate:"required"` + McxOrderJSON interface{} `json:"mcxOrder,omitempty" bson:"mcxOrder,omitempty"` + } + + Identity struct { + UserId string `bson:"userId" json:"userId" validate:"required,uuid"` + MachineId string `bson:"machineId,omitempty" json:"machineId,omitempty" validate:"required"` + MachineName string `bson:"machineName,omitempty" json:"machineName,omitempty" validate:"required,min=3,max=100"` + IpAddress string `bson:"ipAddress" json:"ipAddress"` + } + + DeliveryData struct { + Weight int `bson:"weight" json:"weight" validate:"required"` + PendingWeight int `bson:"pendingWeight" json:"pendingWeight" validate:"required"` + DeliveredOn time.Time `bson:"deliveredOn" json:"deliveredOn" validate:"required"` + } +) + +var AvailableForDelivery = &[]OrderStatus{OrderPlaced, LimitPassed, OrderPartialDelivered} + +func (e *OrderEntity) DeliverWeight(weight int) (*OrderEntity, error) { + if !slices.Contains(*AvailableForDelivery, e.OrderStatus) { + return nil, &RequestError{ + StatusCode: http.StatusBadRequest, + Code: ERROR_INVALID_ORDER_STATUS_FOR_DELIVERY, + Message: "Cannot deliver weight. Order status is not available for delivery", + Name: "OrderStatusNotAvailableForDelivery", + } + } + pendingWeight := e.Weight + if e.DeliveryData != nil { + for _, dataD := range *e.DeliveryData { + pendingWeight = pendingWeight - dataD.Weight + } + } else { + e.DeliveryData = &[]DeliveryData{} + } + + if weight > pendingWeight { + return nil, &RequestError{ + StatusCode: http.StatusBadRequest, + Code: ERROR_INVALID_WEIGHT_FOR_DELIVERY, + Message: "Cannot deliver weight. Weight is not available for delivery", + Name: "InvalidWeightForDelivery", + } + } + + *e.DeliveryData = append(*e.DeliveryData, DeliveryData{ + Weight: weight, + PendingWeight: pendingWeight - weight, + DeliveredOn: time.Now(), + }) + pendingWeight = pendingWeight - weight + e.OrderStatus = OrderPartialDelivered + + if pendingWeight == 0 { + e.OrderStatus = OrderDelivered + } + + return e, nil +} + +func (e *OrderEntity) LimitPassedOrOrderPlaced(mcxPrice float64, calcSnapShot *CalcSnapshotStruct, calcSnapShotId string, pgmSnapShot *GroupPremiumBase, mcxOrderJSON interface{}) *OrderEntity { + e.AfterSuccessOrder = &AfterSuccessOrder{ + CalcSnapShot: calcSnapShot, + CalcSnapShotId: calcSnapShotId, + PgmSnapShot: pgmSnapShot, + McxPrice: mcxPrice, + ExecutedOn: time.Now(), + McxOrderJSON: mcxOrderJSON, + } + return e +} + +func CreateNewOrderEntity(base *OrderBase, limitWatch *LimitWatcherRequired, identity *Identity) *OrderEntity { + b := &OrderEntity{ + BaseEntity: &BaseEntity{}, + OrderBase: base, + LimitWatcherRequired: limitWatch, + Identity: identity, + } + b.createNewId() + return b +} diff --git a/src/interfaces/order.enum.go b/src/interfaces/order.enum.go new file mode 100644 index 0000000..c6cb389 --- /dev/null +++ b/src/interfaces/order.enum.go @@ -0,0 +1,139 @@ +package interfaces + +import "github.com/rpsoftech/bullion-server/src/validator" + +type ( + OrderStatus string + OrderType string + BuySell string +) + +const ( + Buy BuySell = "BUY" + Sell BuySell = "SELL" + + Market OrderType = "Market" + Limit OrderType = "Limit" + + OrderPlaced OrderStatus = "OrderPlaced" + LimitPlaced OrderStatus = "LimitPlaced" + LimitPassed OrderStatus = "LimitPassed" + LimitExpired OrderStatus = "LimitExpired" + LimitCanceled OrderStatus = "LimitCanceled" + LimitDeletedByAdmin OrderStatus = "LimitDeletedByAdmin" + OrderDelivered OrderStatus = "OrderDelivered" + OrderPartialDelivered OrderStatus = "OrderPartialDelivered" +) + +var ( + OrderStatusEnumValidator = EnumValidatorBase{ + Data: map[string]interface{}{ + "OrderPlaced": OrderPlaced, + "LimitPlaced": LimitPlaced, + "LimitPassed": LimitPassed, + "LimitExpired": LimitExpired, + "LimitCanceled": LimitCanceled, + "LimitDeletedByAdmin": LimitDeletedByAdmin, + "OrderDelivered": OrderDelivered, + "OrderPartialDelivered": OrderPartialDelivered, + }, + } + + OrderTypeEnumValidator = EnumValidatorBase{ + Data: map[string]interface{}{ + "Market": Market, + "Limit": Limit, + }, + } + + BuySellEnumValidator = EnumValidatorBase{ + Data: map[string]interface{}{ + "BUY": Buy, + "SELL": Sell, + }, + } +) + +func init() { + validator.RegisterEnumValidatorFunc("OrderStatus", OrderStatusEnumValidator.Validate) + validator.RegisterEnumValidatorFunc("OrderType", OrderTypeEnumValidator.Validate) + validator.RegisterEnumValidatorFunc("BuySell", BuySellEnumValidator.Validate) +} +func (s OrderStatus) String() string { + switch s { + case OrderPlaced: + return "OrderPlaced" + case LimitPlaced: + return "LimitPlaced" + case LimitPassed: + return "LimitPassed" + case LimitExpired: + return "LimitExpired" + case LimitCanceled: + return "LimitCanceled" + case LimitDeletedByAdmin: + return "LimitDeletedByAdmin" + case OrderDelivered: + return "OrderDelivered" + case OrderPartialDelivered: + return "OrderPartialDelivered" + } + return "unknown" +} + +func (s OrderStatus) IsValid() bool { + switch s { + case + OrderPlaced, + LimitPlaced, + LimitPassed, + LimitExpired, + LimitCanceled, + LimitDeletedByAdmin, + OrderDelivered, + OrderPartialDelivered: + return true + } + + return false +} + +func (s OrderType) String() string { + switch s { + case Market: + return "Market" + case Limit: + return "Limit" + } + return "unknown" +} + +func (s OrderType) IsValid() bool { + switch s { + case + Market, + Limit: + return true + } + return false +} + +func (s BuySell) String() string { + switch s { + case Buy: + return "BUY" + case Sell: + return "SELL" + } + return "unknown" +} + +func (s BuySell) IsValid() bool { + switch s { + case + Buy, + Sell: + return true + } + return false +} diff --git a/src/interfaces/price-key.enum.go b/src/interfaces/price-key.enum.go new file mode 100644 index 0000000..b8ada4d --- /dev/null +++ b/src/interfaces/price-key.enum.go @@ -0,0 +1,83 @@ +package interfaces + +import "github.com/rpsoftech/bullion-server/src/validator" + +type PriceKeyEnum string + +const ( + PRICE_KEY_BID_HIGH PriceKeyEnum = "bid-high" + PRICE_KEY_BID_LOW PriceKeyEnum = "bid-low" + PRICE_KEY_ASK_HIGH PriceKeyEnum = "ask-high" + PRICE_KEY_ASK_LOW PriceKeyEnum = "ask-low" + PRICE_KEY_LAST_HIGH PriceKeyEnum = "last-high" + PRICE_KEY_LAST_LOW PriceKeyEnum = "last-low" + PRICE_BID PriceKeyEnum = "bid" + PRICE_ASK PriceKeyEnum = "ask" + PRICE_OPEN PriceKeyEnum = "open" + PRICE_CLOSE PriceKeyEnum = "close" +) + +var ( + priceKeyEnumMap = EnumValidatorBase{ + Data: map[string]interface{}{ + "bid-high": PRICE_KEY_BID_HIGH, + "bid-low": PRICE_KEY_BID_LOW, + "ask-high": PRICE_KEY_ASK_HIGH, + "ask-low": PRICE_KEY_ASK_LOW, + "last-high": PRICE_KEY_LAST_HIGH, + "last-low": PRICE_KEY_LAST_LOW, + "bid": PRICE_BID, + "ask": PRICE_ASK, + "open": PRICE_OPEN, + "close": PRICE_CLOSE, + }, + } +) + +func init() { + validator.RegisterEnumValidatorFunc("PriceKeyEnum", priceKeyEnumMap.Validate) +} + +func (s PriceKeyEnum) String() string { + switch s { + case PRICE_KEY_BID_HIGH: + return "bid-high" + case PRICE_KEY_BID_LOW: + return "bid-low" + case PRICE_KEY_ASK_HIGH: + return "ask-high" + case PRICE_KEY_ASK_LOW: + return "ask-low" + case PRICE_KEY_LAST_HIGH: + return "last-high" + case PRICE_KEY_LAST_LOW: + return "last-low" + case PRICE_BID: + return "bid" + case PRICE_ASK: + return "ask" + case PRICE_OPEN: + return "open" + case PRICE_CLOSE: + return "close" + } + return "unknown" +} + +func (s PriceKeyEnum) IsValid() bool { + switch s { + case + PRICE_KEY_BID_HIGH, + PRICE_KEY_BID_LOW, + PRICE_KEY_ASK_HIGH, + PRICE_KEY_ASK_LOW, + PRICE_KEY_LAST_HIGH, + PRICE_KEY_LAST_LOW, + PRICE_BID, + PRICE_ASK, + PRICE_OPEN, + PRICE_CLOSE: + return true + } + return false +} diff --git a/src/interfaces/product-entity.interface.go b/src/interfaces/product-entity.interface.go index fef405d..d20aebf 100644 --- a/src/interfaces/product-entity.interface.go +++ b/src/interfaces/product-entity.interface.go @@ -11,14 +11,14 @@ type ( Sell CshPremiumBuySellSnapshot `bson:"sell" json:"sell" validate:"required"` } ProductBaseStruct struct { - BullionId string `bson:"bullionId" json:"bullionId" validate:"required,uuid"` - Name string `bson:"name" json:"name" validate:"required"` - SourceSymbol SymbolsEnum `bson:"sourceSymbol" json:"sourceSymbol" validate:"required,enum=SymbolsEnum"` - CalculationSymbol SymbolsEnum `bson:"calculationSymbol" json:"calculationSymbol" validate:"required,enum=SymbolsEnum"` - IsActive bool `bson:"isActive" json:"isActive" validate:"boolean"` - IsHedging bool `bson:"isHedging" json:"isHedging" validate:"boolean"` - FloatPoint int `bson:"floatPoint" json:"floatPoint" validate:"min=0,max=4"` - CalculatedOnPriceOf CalculateOnPriceType `bson:"calculatedOnPriceOf" json:"calculatedOnPriceOf" validate:"required,enum=CalculateOnPriceType"` + BullionId string `bson:"bullionId" json:"bullionId" validate:"required,uuid"` + Name string `bson:"name" json:"name" validate:"required"` + SourceSymbol SourceSymbolEnum `bson:"sourceSymbol" json:"sourceSymbol" validate:"required,enum=SourceSymbolEnum"` + IsActive bool `bson:"isActive" json:"isActive" validate:"boolean"` + IsHedging bool `bson:"isHedging" json:"isHedging" validate:"boolean"` + FloatPoint int `bson:"floatPoint" json:"floatPoint" validate:"min=0,max=4"` + CalcPriceMethod CalculationPriceMethod `bson:"calcPriceMethod" json:"calcPriceMethod" validate:"required,enum=CalculationPriceMethod"` + CalculatedOnPriceOf CalculateOnPriceType `bson:"calculatedOnPriceOf" json:"calculatedOnPriceOf" validate:"required,enum=CalculateOnPriceType"` } ProductEntity struct { @@ -46,7 +46,7 @@ type ( } ) -func CreateNewProduct(productBase *ProductBaseStruct, calcSnapShot *CalcSnapshotStruct, sequence int) (r *ProductEntity) { +func CreateNewProduct(productBase *ProductBaseStruct, calcSnapShot *CalcSnapshotStruct, sequence int) *ProductEntity { b := &ProductEntity{ ProductBaseStruct: productBase, CalcSnapshot: calcSnapShot, @@ -56,3 +56,8 @@ func CreateNewProduct(productBase *ProductBaseStruct, calcSnapShot *CalcSnapshot b.createNewId() return b } + +func Calculate(symbol float64, snapshot *CshPremiumBuySellSnapshot) float64 { + price := symbol + float64(snapshot.Premium) + return price * (1 + float64(snapshot.Tax)/100) +} diff --git a/src/interfaces/product-entity.interface_test.go b/src/interfaces/product-entity.interface_test.go new file mode 100644 index 0000000..661f2dc --- /dev/null +++ b/src/interfaces/product-entity.interface_test.go @@ -0,0 +1,39 @@ +package interfaces + +import ( + "testing" +) + +func TestCalculate(t *testing.T) { + snapshot := &CshPremiumBuySellSnapshot{ + Premium: 10, + Tax: 5, + } + + t.Run("Positive values calculation", func(t *testing.T) { + symbol := 100.0 + expected := 115.5 + result := Calculate(symbol, snapshot) + if result != expected { + t.Errorf("Expected %f, but got %f", expected, result) + } + }) + + t.Run("Negative values calculation", func(t *testing.T) { + symbol := -50.0 + expected := -42.0 + result := Calculate(symbol, snapshot) + if result != expected { + t.Errorf("Expected %f, but got %f", expected, result) + } + }) + + t.Run("Zero values calculation", func(t *testing.T) { + symbol := 0.0 + expected := 10.5 + result := Calculate(symbol, snapshot) + if result != expected { + t.Errorf("Expected %f, but got %f", expected, result) + } + }) +} diff --git a/src/interfaces/trade-user-group.entity.go b/src/interfaces/trade-user-group.entity.go index 98459eb..d685a85 100644 --- a/src/interfaces/trade-user-group.entity.go +++ b/src/interfaces/trade-user-group.entity.go @@ -52,6 +52,17 @@ func (r *TradeUserGroupMapEntity) CreateNew() *TradeUserGroupMapEntity { return r } +func (r *TradeUserGroupMapEntity) ValidateVolume(weight int) bool { + if weight < r.GroupVolumeBase.OneClick { + return false + } else if weight > r.GroupVolumeBase.Total { + return false + } else if weight == r.GroupVolumeBase.OneClick || weight == r.GroupVolumeBase.Total { + return true + } + return (weight-r.GroupVolumeBase.OneClick)%r.GroupVolumeBase.Step == 0 +} + func (r *TradeUserGroupMapBase) UpdateDetails(base *TradeUserGroupMapBase) *TradeUserGroupMapBase { r.IsActive = base.IsActive r.CanTrade = base.CanTrade diff --git a/src/interfaces/trade-user.entity.go b/src/interfaces/trade-user.entity.go index b0120a2..f9618f9 100644 --- a/src/interfaces/trade-user.entity.go +++ b/src/interfaces/trade-user.entity.go @@ -1,5 +1,7 @@ package interfaces +import "net/http" + type ( TradeUserBase struct { BullionId string `bson:"bullionId" json:"bullionId" validate:"required,uuid"` @@ -18,8 +20,8 @@ type ( UNumber string `bson:"uNumber" json:"uNumber" validate:"required"` } UserMarginsDataStruct struct { - Gold int32 `bson:"gold" json:"gold" validate:"min=0"` - Silver int32 `bson:"silver" json:"silver" validate:"min=0"` + Gold int `bson:"gold" json:"gold" validate:"min=0"` + Silver int `bson:"silver" json:"silver" validate:"min=0"` } TradeUserMargins struct { @@ -41,17 +43,43 @@ type ( } ) -func (user *TradeUserEntity) CreateNew() (r *TradeUserEntity) { +func (user *TradeUserEntity) CreateNew() *TradeUserEntity { user.createNewId() return user } -func (user *TradeUserEntity) UpdateUser() (r *TradeUserEntity) { +func (user *TradeUserEntity) UpdateUser() *TradeUserEntity { user.passwordEntity = CreatePasswordEntity(user.RawPassword) user.Updated() return user } -func (user *TradeUserEntity) DeletePassword() (r *TradeUserEntity) { +func (user *TradeUserEntity) DeletePassword() *TradeUserEntity { user.RawPassword = "" return user } + +func (user *TradeUserEntity) UpdateMarginAfterOrder(weight int, symbol SourceSymbolEnum) (*TradeUserEntity, error) { + availableMargin := 0 + if symbol == SOURCE_SYMBOL_GOLD { + availableMargin = user.AllotedMargins.Gold - user.UsedMargins.Gold + } else if symbol == SOURCE_SYMBOL_SILVER { + availableMargin = user.AllotedMargins.Silver - user.UsedMargins.Silver + } + + if availableMargin < 0 || availableMargin-weight < 0 { + return nil, &RequestError{ + StatusCode: http.StatusBadRequest, + Code: ERROR_INSUFFICIENT_MARGIN, + Message: "Insufficient Margin", + Name: "INSUFFICIENT_MARGIN", + Extra: map[string]interface{}{"margins": user.TradeUserMargins, "weight": weight}, + } + } + + if symbol == SOURCE_SYMBOL_GOLD { + user.UsedMargins.Gold = user.UsedMargins.Gold + weight + } else if symbol == SOURCE_SYMBOL_SILVER { + user.UsedMargins.Silver = user.UsedMargins.Silver + weight + } + return user, nil +} diff --git a/src/interfaces/user-roles.enum.go b/src/interfaces/user-roles.enum.go index d0c3408..92e08e7 100644 --- a/src/interfaces/user-roles.enum.go +++ b/src/interfaces/user-roles.enum.go @@ -14,23 +14,24 @@ const ( ) var ( - userRolesMap = map[string]UserRoles{ - "RATE_ADMIN": ROLE_RATE_ADMIN, - "SUPER_ADMIN": ROLE_SUPER_ADMIN, - "ADMIN": ROLE_ADMIN, - "GENERAL_USER": ROLE_GENERAL_USER, - "TRADE_USER": ROLE_TRADE_USER, - "GOD": ROLE_GOD, + userRolesMap = EnumValidatorBase{ + Data: map[string]interface{}{ + "RATE_ADMIN": ROLE_RATE_ADMIN, + "SUPER_ADMIN": ROLE_SUPER_ADMIN, + "ADMIN": ROLE_ADMIN, + "GENERAL_USER": ROLE_GENERAL_USER, + "TRADE_USER": ROLE_TRADE_USER, + "GOD": ROLE_GOD, + }, } ) func init() { - validator.RegisterEnumValidatorFunc("UserRoles", ValidateEnumUserRole) + validator.RegisterEnumValidatorFunc("UserRoles", userRolesMap.Validate) } func ValidateEnumUserRole(value string) bool { - _, ok := userRolesMap[value] - return ok + return userRolesMap.Validate(value) } func (s UserRoles) String() string { diff --git a/src/mongodb/repos/bank-rate-calc.repo.go b/src/mongodb/repos/bank-rate-calc.repo.go new file mode 100644 index 0000000..fd53733 --- /dev/null +++ b/src/mongodb/repos/bank-rate-calc.repo.go @@ -0,0 +1,103 @@ +package repos + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "time" + + "github.com/rpsoftech/bullion-server/src/env" + "github.com/rpsoftech/bullion-server/src/interfaces" + "github.com/rpsoftech/bullion-server/src/mongodb" + "github.com/rpsoftech/bullion-server/src/redis" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" +) + +type BankRateCalcRepoStruct struct { + collection *mongo.Collection + redis *redis.RedisClientStruct + // historyCollection *mongo.Collection +} + +const bankRateCalcRepoCollectionName = "BankRateCalc" +const bankRateRedisCollection = "bankRate" + +// const bankRateCalcHistoryRepoCollectionName = "BankRateCalcHistory" + +var BankRateCalcRepo *BankRateCalcRepoStruct + +func init() { + if env.Env.APP_ENV == env.APP_ENV_DEVELOPE { + return + } + coll := mongodb.MongoDatabase.Collection(bankRateCalcRepoCollectionName) + BankRateCalcRepo = &BankRateCalcRepoStruct{ + collection: coll, + redis: redis.InitRedisAndRedisClient(), + } + addUniqueIndexesToCollection([]string{"id", "bullionId"}, BankRateCalcRepo.collection) +} + +func (repo *BankRateCalcRepoStruct) cacheDataToRedis(entity *interfaces.BankRateCalcEntity) { + if entityStringBytes, err := json.Marshal(entity); err == nil { + entityString := string(entityStringBytes) + repo.redis.SetStringDataWithExpiry(fmt.Sprintf("%s/%s", bankRateRedisCollection, entity.BullionId), entityString, time.Duration(24)*time.Hour) + } +} + +func (repo *BankRateCalcRepoStruct) Save(entity *interfaces.BankRateCalcEntity) (*interfaces.BankRateCalcEntity, error) { + var result interfaces.BankRateCalcEntity + err := repo.collection.FindOneAndUpdate(mongodb.MongoCtx, bson.D{{ + Key: "_id", Value: entity.ID, + }}, bson.D{{Key: "$set", Value: entity}}, findOneAndUpdateOptions).Decode(&result) + entity.Updated() + if err != nil { + if !errors.Is(err, mongo.ErrNoDocuments) { + err = &interfaces.RequestError{ + StatusCode: 500, + Code: interfaces.ERROR_INTERNAL_SERVER, + Message: fmt.Sprintf("Internal Server Error: %s", err.Error()), + Name: "INTERNAL_ERROR", + } + } else { + err = nil + } + } + go repo.cacheDataToRedis(entity) + return &result, err +} + +func (repo *BankRateCalcRepoStruct) FindOneByBullionId(id string) (*interfaces.BankRateCalcEntity, error) { + result := new(interfaces.BankRateCalcEntity) + if redisData := repo.redis.GetStringData(fmt.Sprintf("%s/%s", bankRateRedisCollection, id)); redisData != "" { + if err := json.Unmarshal([]byte(redisData), result); err == nil { + return result, err + } + } + err := repo.collection.FindOne(mongodb.MongoCtx, bson.D{{ + Key: "bullionId", Value: id, + }}).Decode(result) + + if err != nil { + if errors.Is(err, mongo.ErrNoDocuments) { + // This error means your query did not match any documents. + err = &interfaces.RequestError{ + StatusCode: http.StatusBadRequest, + Code: interfaces.ERROR_ENTITY_NOT_FOUND, + Message: fmt.Sprintf("Bullion Entity identified by id %s not found", id), + Name: "ENTITY_NOT_FOUND", + } + } else { + err = &interfaces.RequestError{ + StatusCode: 500, + Code: interfaces.ERROR_INTERNAL_SERVER, + Message: fmt.Sprintf("Internal Server Error: %s", err.Error()), + Name: "INTERNAL_ERROR", + } + } + } + go repo.cacheDataToRedis(result) + return result, err +} diff --git a/src/mongodb/repos/order.repo.go b/src/mongodb/repos/order.repo.go new file mode 100644 index 0000000..1d22776 --- /dev/null +++ b/src/mongodb/repos/order.repo.go @@ -0,0 +1,203 @@ +package repos + +import ( + "errors" + "fmt" + "net/http" + "time" + + "github.com/rpsoftech/bullion-server/src/env" + "github.com/rpsoftech/bullion-server/src/interfaces" + "github.com/rpsoftech/bullion-server/src/mongodb" + "github.com/rpsoftech/bullion-server/src/utility" + "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" +) + +type ( + OrderRepoStruct struct { + collection *mongo.Collection + } +) + +const orderCollectionName = "Order" + +var OrderRepo *OrderRepoStruct + +func init() { + if env.Env.APP_ENV == env.APP_ENV_DEVELOPE { + return + } + coll := mongodb.MongoDatabase.Collection(orderCollectionName) + OrderRepo = &OrderRepoStruct{ + collection: coll, + } + addUniqueIndexesToCollection([]string{"id"}, OrderRepo.collection) + addIndexesToCollection([]string{"userId", "productGroupMapId", "groupId", "productId", "orderStatus", "createdAt"}, OrderRepo.collection) +} + +func (repo *OrderRepoStruct) Save(entity *interfaces.OrderEntity) (*interfaces.OrderEntity, error) { + if err := utility.ValidateStructAndReturnReqError(entity, &interfaces.RequestError{ + StatusCode: http.StatusBadRequest, + Code: interfaces.ERROR_INVALID_ENTITY, + Message: "", + Name: "ERROR_INVALID_ENTITY", + }); err != nil { + return entity, err + } + entity.Updated() + err := repo.collection.FindOneAndUpdate(mongodb.MongoCtx, bson.D{{ + Key: "_id", Value: entity.ID, + }}, bson.D{{Key: "$set", Value: entity}}, findOneAndUpdateOptions).Err() + if err != nil { + if !errors.Is(err, mongo.ErrNoDocuments) { + err = &interfaces.RequestError{ + StatusCode: 500, + Code: interfaces.ERROR_INTERNAL_SERVER, + Message: fmt.Sprintf("Internal Server Error: %s", err.Error()), + Name: "INTERNAL_ERROR", + } + } else { + err = nil + } + } + return entity, err +} + +func (repo *OrderRepoStruct) BulkUpdate(entities *[]interfaces.OrderEntity) (*[]interfaces.OrderEntity, error) { + models := make([]mongo.WriteModel, len(*entities)) + for i, entity := range *entities { + if err := utility.ValidateStructAndReturnReqError(entity, &interfaces.RequestError{ + StatusCode: http.StatusBadRequest, + Code: interfaces.ERROR_INVALID_ENTITY, + Message: "", + Name: "ERROR_INVALID_ENTITY", + }); err != nil { + return nil, err + } + entity.Updated() + models[i] = mongo.NewUpdateOneModel().SetFilter(bson.D{{Key: "_id", Value: entity.ID}}).SetUpdate( + bson.D{{Key: "$set", Value: entity}}).SetUpsert(true) + } + _, err := repo.collection.BulkWrite(mongodb.MongoCtx, models) + if err != nil { + if !errors.Is(err, mongo.ErrNoDocuments) { + err = &interfaces.RequestError{ + StatusCode: 500, + Code: interfaces.ERROR_INTERNAL_SERVER, + Message: fmt.Sprintf("Internal Server Error: %s", err.Error()), + Name: "INTERNAL_ERROR", + } + } else { + err = nil + } + } + return entities, err +} + +func (repo *OrderRepoStruct) findByFilter(filter *mongoDbFilter) (*[]interfaces.OrderEntity, error) { + var result []interfaces.OrderEntity + opt := options.Find() + if filter.sort != nil { + opt.SetSort(filter.sort) + } + if filter.limit > 0 { + opt.SetLimit(filter.limit) + } + if filter.skip > 0 { + opt.SetSkip(filter.skip) + } + cursor, err := repo.collection.Find(mongodb.MongoCtx, filter.conditions, opt) + if err == nil { + err = cursor.All(mongodb.MongoCtx, &result) + } + if err != nil { + if errors.Is(err, mongo.ErrNoDocuments) { + // This error means your query did not match any documents. + err = &interfaces.RequestError{ + StatusCode: http.StatusBadRequest, + Code: interfaces.ERROR_ENTITY_NOT_FOUND, + Message: fmt.Sprintf("Feeds Entities filtered By %v not found", filter), + Name: "ENTITY_NOT_FOUND", + } + } else { + err = &interfaces.RequestError{ + StatusCode: 500, + Code: interfaces.ERROR_INTERNAL_SERVER, + Message: fmt.Sprintf("Internal Server Error: %s", err.Error()), + Name: "INTERNAL_ERROR", + } + } + } + return &result, err +} + +func (repo *OrderRepoStruct) FindOne(id string) (*interfaces.OrderEntity, error) { + var result interfaces.OrderEntity + + err := repo.collection.FindOne(mongodb.MongoCtx, bson.D{{ + Key: "id", Value: id, + }}).Decode(&result) + + if err != nil { + if errors.Is(err, mongo.ErrNoDocuments) { + // This error means your query did not match any documents. + err = &interfaces.RequestError{ + StatusCode: http.StatusBadRequest, + Code: interfaces.ERROR_ENTITY_NOT_FOUND, + Message: fmt.Sprintf("Feeds Entity identified by id %s not found", id), + Name: "ENTITY_NOT_FOUND", + } + } else { + err = &interfaces.RequestError{ + StatusCode: 500, + Code: interfaces.ERROR_INTERNAL_SERVER, + Message: fmt.Sprintf("Internal Server Error: %s", err.Error()), + Name: "INTERNAL_ERROR", + } + } + } + return &result, err +} + +func (repo *OrderRepoStruct) DeleteOrderHistoryById(id string) error { + _, err := repo.collection.DeleteOne(mongodb.MongoCtx, bson.D{{ + Key: "_id", Value: id, + }}) + return err +} + +func (repo *OrderRepoStruct) GetOrdersByBullionIdWithDateRangeAndOrderStatus(bullionId string, startDate time.Time, endDate time.Time, orderStatusArray *[]interfaces.OrderStatus) (*[]interfaces.OrderEntity, error) { + return repo.findByFilter(&mongoDbFilter{ + sort: &bson.D{{Key: "createdAt", Value: -1}}, + conditions: &bson.D{ + {Key: "bullionId", Value: bullionId}, + {Key: "createdAt", Value: bson.D{{Key: "$gte", Value: startDate}, {Key: "$lte", Value: endDate}}}, + {Key: "orderStatus", Value: bson.D{{Key: "$in", Value: *orderStatusArray}}}, + }, + }) +} + +func (repo *OrderRepoStruct) GetUsersOrderPaginated(userId string, page int64, limit int64) (*[]interfaces.OrderEntity, error) { + return repo.findByFilter(&mongoDbFilter{ + sort: &bson.D{{Key: "createdAt", Value: -1}}, + conditions: &bson.D{ + {Key: "userId", Value: userId}, + }, + limit: limit, + skip: page * limit, + }) +} + +func (repo *OrderRepoStruct) GetUsersOrderPaginatedWithOrderStatusArray(userId string, orderStatusArray *[]interfaces.OrderStatus, page int64, limit int64) (*[]interfaces.OrderEntity, error) { + return repo.findByFilter(&mongoDbFilter{ + sort: &bson.D{{Key: "createdAt", Value: -1}}, + conditions: &bson.D{ + {Key: "userId", Value: userId}, + {Key: "orderStatus", Value: bson.D{{Key: "$in", Value: *orderStatusArray}}}, + }, + limit: limit, + skip: page * limit, + }) +} diff --git a/src/mongodb/repos/trade-user.repo.go b/src/mongodb/repos/trade-user.repo.go index aa312f3..82f4b81 100644 --- a/src/mongodb/repos/trade-user.repo.go +++ b/src/mongodb/repos/trade-user.repo.go @@ -1,9 +1,11 @@ package repos import ( + "encoding/json" "errors" "fmt" "net/http" + "time" "github.com/rpsoftech/bullion-server/src/env" "github.com/rpsoftech/bullion-server/src/interfaces" @@ -68,6 +70,7 @@ func (repo *TradeUserRepoStruct) Save(entity *interfaces.TradeUserEntity) (*inte err = nil } } + go repo.cacheDataToRedis(entity) return entity, err } @@ -131,11 +134,15 @@ func (repo *TradeUserRepoStruct) findByFilter(filter *mongoDbFilter) (*[]interfa } func (repo *TradeUserRepoStruct) FindOne(id string) (*interfaces.TradeUserEntity, error) { - var result interfaces.TradeUserEntity - + result := new(interfaces.TradeUserEntity) + if redisData := repo.redis.GetStringData(fmt.Sprintf("tradeUser/%s", id)); redisData != "" { + if err := json.Unmarshal([]byte(redisData), result); err == nil { + return result, err + } + } err := repo.collection.FindOne(mongodb.MongoCtx, bson.D{{ Key: "id", Value: id, - }}).Decode(&result) + }}).Decode(result) if err != nil { if errors.Is(err, mongo.ErrNoDocuments) { @@ -155,9 +162,16 @@ func (repo *TradeUserRepoStruct) FindOne(id string) (*interfaces.TradeUserEntity } } } - return &result, err + go repo.cacheDataToRedis(result) + return result, err } +func (repo *TradeUserRepoStruct) cacheDataToRedis(entity *interfaces.TradeUserEntity) { + if entityStringBytes, err := json.Marshal(entity); err == nil { + entityString := string(entityStringBytes) + repo.redis.SetStringDataWithExpiry(fmt.Sprintf("tradeUser/%s", entity.ID), entityString, time.Duration(24)*time.Hour) + } +} func (repo *TradeUserRepoStruct) findOneByCondition(bullionId string, condition *bson.E) (*interfaces.TradeUserEntity, error) { var result interfaces.TradeUserEntity diff --git a/src/redis/index.go b/src/redis/index.go index 58d40dc..a4d037a 100644 --- a/src/redis/index.go +++ b/src/redis/index.go @@ -2,6 +2,7 @@ package redis import ( "context" + "fmt" "time" "github.com/redis/go-redis/v9" @@ -21,6 +22,7 @@ func init() { if env.Env.APP_ENV == env.APP_ENV_DEVELOPE { return } + // RedisClient.redisClient.Subscribe() } func InitRedisAndRedisClient() *RedisClientStruct { @@ -28,7 +30,7 @@ func InitRedisAndRedisClient() *RedisClientStruct { return RedisClient } client := redis.NewClient(&redis.Options{ - Addr: env.Env.REDIS_DB_URL, + Addr: fmt.Sprintf("%v:%d", env.Env.REDIS_DB_HOST, env.Env.REDIS_DB_PORT), Password: env.Env.REDIS_DB_PASSWORD, // no password set DB: env.Env.REDIS_DB_DATABASE, // use default DB }) @@ -52,9 +54,16 @@ func DeferFunction() { } } +func (r *RedisClientStruct) SubscribeToChannels(channels ...string) *redis.PubSub { + return r.redisClient.Subscribe(RedisCTX, channels...) +} + func (r *RedisClientStruct) PublishEvent(event *events.BaseEvent) { r.redisClient.Publish(RedisCTX, event.GetEventName(), event.GetPayloadString()) } +func (r *RedisClientStruct) GetHashValue(key string) map[string]string { + return r.redisClient.HGetAll(RedisCTX, key).Val() +} func (r *RedisClientStruct) GetStringData(key string) string { return r.redisClient.Get(RedisCTX, key).Val() } @@ -63,5 +72,8 @@ func (r *RedisClientStruct) RemoveKey(key ...string) { r.redisClient.Del(RedisCTX, key...) } func (r *RedisClientStruct) SetStringData(key string, value string, expiresIn int) { - r.redisClient.Set(RedisCTX, key, value, time.Duration(expiresIn)*time.Second) + r.SetStringDataWithExpiry(key, value, time.Duration(expiresIn)*time.Second) +} +func (r *RedisClientStruct) SetStringDataWithExpiry(key string, value string, expiresIn time.Duration) { + r.redisClient.Set(RedisCTX, key, value, expiresIn) } diff --git a/src/services/bank-rate-cacl.service.go b/src/services/bank-rate-cacl.service.go new file mode 100644 index 0000000..b47f3a4 --- /dev/null +++ b/src/services/bank-rate-cacl.service.go @@ -0,0 +1,57 @@ +package services + +import ( + "github.com/rpsoftech/bullion-server/src/events" + "github.com/rpsoftech/bullion-server/src/interfaces" + "github.com/rpsoftech/bullion-server/src/mongodb/repos" +) + +type bankRateService struct { + bankRateRepo *repos.BankRateCalcRepoStruct + eventBus *eventBusService + firebaseDatabaseService *firebaseDatabaseService +} + +var BankRateCalcService *bankRateService + +func init() { + getBankRateService() +} + +func getBankRateService() *bankRateService { + if BankRateCalcService == nil { + BankRateCalcService = &bankRateService{ + eventBus: getEventBusService(), + bankRateRepo: repos.BankRateCalcRepo, + firebaseDatabaseService: getFirebaseRealTimeDatabase(), + } + println("Bank Rate Service Initialized") + } + return BankRateCalcService +} + +func (service *bankRateService) GetBankRateCalcByBullionId(bullionId string) (*interfaces.BankRateCalcEntity, error) { + return service.bankRateRepo.FindOneByBullionId(bullionId) +} + +func (service *bankRateService) SaveBankRateCalc(gold *interfaces.BankRateCalcBase, silver *interfaces.BankRateCalcBase, bullionId string, adminId string) (*interfaces.BankRateCalcEntity, error) { + entity, err := service.GetBankRateCalcByBullionId(bullionId) + if err != nil { + entity = &interfaces.BankRateCalcEntity{ + BullionId: bullionId, + GOLD_SPOT: gold, + SILVER_SPOT: silver, + } + entity.CreateNewBankRateCalc() + } else { + entity.GOLD_SPOT = gold + entity.SILVER_SPOT = silver + } + _, err = service.bankRateRepo.Save(entity) + if err != nil { + return nil, err + } + service.eventBus.Publish(events.BankRateCalcUpdatedEvent(entity, adminId)) + service.firebaseDatabaseService.SetPublicData(bullionId, []string{"bankRateCalc"}, entity) + return entity, nil +} diff --git a/src/services/bullion-details.service.go b/src/services/bullion-details.service.go index c1be9b8..6114865 100644 --- a/src/services/bullion-details.service.go +++ b/src/services/bullion-details.service.go @@ -37,7 +37,8 @@ func (service *bullionDetailsService) GetBullionDetailsByShortName(shortName str if err != nil { return nil, err } - service.bullionSiteInfoMapById[shortName] = bullion + service.bullionSiteInfoMapById[bullion.ID] = bullion + service.bullionSiteInfoMapByShortName[shortName] = bullion return bullion, nil } func (service *bullionDetailsService) GetBullionDetailsByBullionId(id string) (*interfaces.BullionSiteInfoEntity, error) { @@ -49,6 +50,7 @@ func (service *bullionDetailsService) GetBullionDetailsByBullionId(id string) (* return nil, err } service.bullionSiteInfoMapById[id] = bullion + service.bullionSiteInfoMapByShortName[bullion.ShortName] = bullion return bullion, nil } diff --git a/src/services/general-user.service.go b/src/services/general-user.service.go index fc71bed..fe5986e 100644 --- a/src/services/general-user.service.go +++ b/src/services/general-user.service.go @@ -69,7 +69,10 @@ func (service *generalUserService) RegisterNew(bullionId string, user interface{ return nil, err } - service.GeneralUserRepo.Save(entity) + _, err = service.GeneralUserRepo.Save(entity) + if err != nil { + return nil, err + } _, err = service.sendApprovalRequest(entity, Bullion) if err != nil { diff --git a/src/services/live-rate.service.go b/src/services/live-rate.service.go new file mode 100644 index 0000000..49f27bd --- /dev/null +++ b/src/services/live-rate.service.go @@ -0,0 +1,90 @@ +package services + +import ( + "encoding/json" + "time" + + "github.com/rpsoftech/bullion-server/src/interfaces" + "github.com/rpsoftech/bullion-server/src/redis" +) + +type liveRateServiceStruct struct { + redisRepo *redis.RedisClientStruct + LastRateMap interfaces.LiveRateData +} + +var LiveRateService *liveRateServiceStruct + +func init() { + service := getLiveRateService() + service.lastRateReaderFromRedis() + service.subscribeToRedisForRate() +} + +func getLiveRateService() *liveRateServiceStruct { + if LiveRateService == nil { + LiveRateService = &liveRateServiceStruct{ + redisRepo: redis.InitRedisAndRedisClient(), + LastRateMap: make(interfaces.LiveRateData), + } + for _, k := range interfaces.SymbolsEnumArray { + LiveRateService.LastRateMap[k] = make(map[interfaces.PriceKeyEnum]float64) + } + println("Live Rate Service Initialized") + } + return LiveRateService +} + +func (s *liveRateServiceStruct) GetLastRate() *interfaces.LiveRateData { + return &s.LastRateMap +} + +func (s *liveRateServiceStruct) GetLiveRate(symbol interfaces.SymbolsEnum, priceKey interfaces.PriceKeyEnum) float64 { + rateMap := s.LastRateMap[symbol] + if len(rateMap) == 0 { + return 0 + } + return rateMap[priceKey] +} + +func (s *liveRateServiceStruct) lastRateReaderFromRedis() { + go func() { + for { + data := s.redisRepo.GetHashValue("LastRate") + for keyString, value := range data { + key := interfaces.SymbolsEnumFromString(keyString) + if key != "" { + symbolMap := s.LastRateMap[key] + json.Unmarshal([]byte(value), &symbolMap) + } + } + time.Sleep(15 * time.Second) + } + }() +} + +// SubscribeToRedisForRate subscribes to the minirate Redis channel and +// updates the live rate service with the latest data from Redis. +func (s *liveRateServiceStruct) subscribeToRedisForRate() { + psc := redis.RedisClient.SubscribeToChannels("minirate") + + go func() { + // Listen to messages from the Redis channel + for msg := range psc.Channel() { + // Unmarshal the JSON data from the Redis message payload + data := new(interfaces.LiveRateData) + if err := json.Unmarshal([]byte(msg.Payload), data); err == nil { + // Loop through each symbol in the data + for symbol, rates := range *data { + // If the symbol does not already exist in the live rate map + // Add the symbol and its rates to the live rate map + if _, ok := s.LastRateMap[symbol]; ok { + for priceKey, v1 := range rates { + s.LastRateMap[symbol][priceKey] = v1 + } + } + } + } + } + }() +} diff --git a/src/services/order-general.service.go b/src/services/order-general.service.go new file mode 100644 index 0000000..d5535d3 --- /dev/null +++ b/src/services/order-general.service.go @@ -0,0 +1,255 @@ +package services + +import ( + "net/http" + + "github.com/rpsoftech/bullion-server/src/interfaces" + "github.com/rpsoftech/bullion-server/src/mongodb/repos" +) + +type orderGeneralService struct { + eventBus *eventBusService + firebaseDb *firebaseDatabaseService + bullionService *bullionDetailsService + flagService *FlagServiceStruct + liveRateService *liveRateServiceStruct + groupService *tradeUserGroupServiceStruct + orderRepo *repos.OrderRepoStruct + bankRateService *bankRateService + userService *tradeUserServiceStruct + productService *productService +} + +var OrderGeneralService *orderGeneralService + +func init() { + getOrderGeneralService() +} +func getOrderGeneralService() *orderGeneralService { + if OrderGeneralService == nil { + OrderGeneralService = &orderGeneralService{ + eventBus: getEventBusService(), + firebaseDb: getFirebaseRealTimeDatabase(), + bullionService: getBullionService(), + groupService: getTradeUserGroupService(), + flagService: getFlagService(), + liveRateService: getLiveRateService(), + userService: getTradeUserService(), + bankRateService: getBankRateService(), + productService: getProductService(), + orderRepo: repos.OrderRepo, + } + println("Order General Service Initialized") + } + return OrderGeneralService +} + +func (service *orderGeneralService) ValidateUserAndGroupMapWithWeight(user *interfaces.TradeUserEntity, group *interfaces.TradeUserGroupEntity, groupMap *interfaces.TradeUserGroupMapEntity, weight int) (bool, error) { + + // Check for User Activation + if !user.IsActive { + return false, &interfaces.RequestError{ + StatusCode: http.StatusUnauthorized, + Code: interfaces.ERROR_PERMISSION_NOT_ALLOWED, + Message: "Account Is Not Active Please Contact Admin", + Name: "ERROR_PERMISSION_NOT_ALLOWED", + } + } + + if flags, err := service.flagService.GetFlags(user.BullionId); err != nil { + return false, err + } else if !flags.CanTrade { + // Check If Trading Is Disabled + return false, &interfaces.RequestError{ + StatusCode: 400, + Code: interfaces.ERROR_TRADING_IS_DISABLED, + Message: "Trading is disabled. Contact User", + Name: "BULLION_NOT_ACTIVE", + } + } + // Check for Group Activation + if !group.IsActive { + return false, &interfaces.RequestError{ + StatusCode: http.StatusUnauthorized, + Code: interfaces.ERROR_PERMISSION_NOT_ALLOWED, + Message: "Group Is Not Active Please Contact Admin", + Name: "ERROR_PERMISSION_NOT_ALLOWED", + } + } + // Check for Group User Can Trade + if !group.CanTrade { + return false, &interfaces.RequestError{ + StatusCode: 400, + Code: interfaces.ERROR_TRADING_IS_DISABLED_FOR_GROUP, + Message: "Trading is disabled for your group. Contact User", + Name: "GROUP_NOT_ACTIVE", + } + } + + // Check for Group Map Activation + if !groupMap.IsActive { + return false, &interfaces.RequestError{ + StatusCode: http.StatusUnauthorized, + Code: interfaces.ERROR_PERMISSION_NOT_ALLOWED, + Message: "Group Map Is Not Active Please Contact Admin", + Name: "ERROR_PERMISSION_NOT_ALLOWED", + } + } + + // Check for Group Map Can Trade + if !groupMap.CanTrade { + return false, &interfaces.RequestError{ + StatusCode: 400, + Code: interfaces.ERROR_TRADING_IS_DISABLED_FOR_PRODUCT, + Message: "Trading is disabled for your group map. Contact Admin", + Name: "GROUP_NOT_ACTIVE", + } + } + + return service.validateVolumeForGroupMap(groupMap, weight) +} + +func (service *orderGeneralService) validateVolumeForGroupMap(groupMap *interfaces.TradeUserGroupMapEntity, weight int) (bool, error) { + if !groupMap.ValidateVolume(weight) { + return false, &interfaces.RequestError{ + StatusCode: 400, + Code: interfaces.ERROR_INVALID_VOLUME, + Message: "Invalid Volume", + Name: "INVALID_VOLUME", + } + } + return true, nil +} + +func (service *orderGeneralService) findOrderDetailsAndValidate(userId string, groupId string, groupMapId string, weight int) (*interfaces.TradeUserEntity, *interfaces.TradeUserGroupEntity, *interfaces.TradeUserGroupMapEntity, error) { + // Get User + user, err := service.userService.GetTradeUserById(userId) + if err != nil { + return nil, nil, nil, err + } + + if user.GroupId != groupId { + return nil, nil, nil, &interfaces.RequestError{ + StatusCode: 400, + Code: interfaces.ERROR_PERMISSION_NOT_ALLOWED, + Message: "MissMatch Group Id", + Name: "MISS_MATCH_GROUP_ID", + } + } + // Get Group + group, err := service.groupService.GetGroupByGroupId(groupId, user.BullionId) + if err != nil { + return nil, nil, nil, err + } + + // Get Group Map + groupMaps, err := service.groupService.GetGroupMapByGroupId(groupId, user.BullionId) + if err != nil { + return nil, nil, nil, err + } + var groupMap *interfaces.TradeUserGroupMapEntity + + for _, v := range *groupMaps { + if v.ID == groupMapId { + groupMap = &v + break + } + } + if groupMap == nil { + return nil, nil, nil, &interfaces.RequestError{ + StatusCode: 400, + Code: interfaces.ERROR_GROUP_MAP_NOT_FOUND, + Message: "Group Map Not Found", + Name: "GROUP_MAP_NOT_FOUND", + } + + } + service.ValidateUserAndGroupMapWithWeight(user, group, groupMap, weight) + return user, group, groupMap, nil +} +func (service *orderGeneralService) PlaceOrder(orderType interfaces.OrderStatus, userId string, groupId string, groupMapId string, buySell interfaces.BuySell, weight int, price float64, placedBy string) (*interfaces.OrderEntity, error) { + + user, group, groupMap, err := service.findOrderDetailsAndValidate(userId, groupId, groupMapId, weight) + if err != nil { + return nil, err + } + // product + product, err := service.productService.GetProductsById(group.BullionId, groupMap.ProductId) + if err != nil { + return nil, err + } + + // TODO Validate Pricing + priceReadKey := interfaces.PRICE_ASK + if product.CalculatedOnPriceOf == interfaces.CALCULATE_ON_BID { + priceReadKey = interfaces.PRICE_BID + } else if product.CalculatedOnPriceOf == interfaces.CALCULATE_ON_BID_ASK { + if buySell == interfaces.Sell { + priceReadKey = interfaces.PRICE_ASK + } else { + priceReadKey = interfaces.PRICE_BID + } + } + + productSymbol := product.SourceSymbol.ToSymbolEnum() + + if product.CalcPriceMethod == interfaces.CALCULATION_PRICE_TYPE_BANK { + if product.SourceSymbol == interfaces.SOURCE_SYMBOL_GOLD { + productSymbol = interfaces.SYMBOL_GOLD_SPOT + } else { + productSymbol = interfaces.SYMBOL_SILVER_SPOT + } + } + + rate := service.liveRateService.GetLiveRate(productSymbol, priceReadKey) + + if rate == 0 { + return nil, &interfaces.RequestError{ + StatusCode: 400, + Code: interfaces.ERROR_LIVE_RATE_NOT_FOUND, + Message: "Live Rate Not Found", + Name: "LIVE_RATE_NOT_FOUND", + } + } + + if product.CalcPriceMethod == interfaces.CALCULATION_PRICE_TYPE_BANK { + bankRate, err := service.bankRateService.GetBankRateCalcByBullionId(group.BullionId) + if err != nil { + return nil, err + } + inrRate := service.liveRateService.GetLiveRate(interfaces.SYMBOL_INR, priceReadKey) + calcFunc := bankRate.GOLD_SPOT.CalculatePrice + if product.SourceSymbol == interfaces.SOURCE_SYMBOL_SILVER { + calcFunc = bankRate.SILVER_SPOT.CalculatePrice + } + rate = calcFunc(rate, inrRate) + // rate = bankRate.Rate + } + calcSnapshot := &product.CalcSnapshot.Sell + if buySell == interfaces.Buy { + calcSnapshot = &product.CalcSnapshot.Buy + } + finalRate := interfaces.Calculate(rate, calcSnapshot) + + println("Final Rate", finalRate) + // order := &interfaces.OrderEntity{ + + // TODO Check Hedging And Place Order + + // TODO Update Order Entity in DB + + _, err = user.UpdateMarginAfterOrder(weight, product.SourceSymbol) + if err != nil { + return nil, err + } + + // TODO Check Hedging And Place Order + return nil, nil + // return service.orderRepo.PlaceOrder(orderType, user, group, groupMap, price, placedBy) + // return service.orderRepo.PlaceOrder(orderType, user, group, groupMap, price, placedBy) +} + +// check user is valid +// check group is valid +// check group map is valid +// check for volume diff --git a/src/services/trade-user-group.service.go b/src/services/trade-user-group.service.go index 37f8d03..4bb2e69 100644 --- a/src/services/trade-user-group.service.go +++ b/src/services/trade-user-group.service.go @@ -6,11 +6,9 @@ import ( "github.com/rpsoftech/bullion-server/src/events" "github.com/rpsoftech/bullion-server/src/interfaces" "github.com/rpsoftech/bullion-server/src/mongodb/repos" - "github.com/rpsoftech/bullion-server/src/redis" ) type tradeUserGroupServiceStruct struct { - redisRepo *redis.RedisClientStruct eventBus *eventBusService firebaseDb *firebaseDatabaseService bullionService *bullionDetailsService @@ -31,7 +29,6 @@ func init() { func getTradeUserGroupService() *tradeUserGroupServiceStruct { if TradeUserGroupService == nil { TradeUserGroupService = &tradeUserGroupServiceStruct{ - redisRepo: redis.InitRedisAndRedisClient(), eventBus: getEventBusService(), firebaseDb: getFirebaseRealTimeDatabase(), bullionService: getBullionService(), diff --git a/src/services/trade-user.service.go b/src/services/trade-user.service.go index e2a96b9..316a1dd 100644 --- a/src/services/trade-user.service.go +++ b/src/services/trade-user.service.go @@ -27,15 +27,23 @@ type tradeUserServiceStruct struct { var TradeUserService *tradeUserServiceStruct func init() { - TradeUserService = &tradeUserServiceStruct{ - tradeUserRepo: repos.TradeUserRepo, - accessTokenService: AccessTokenService, - eventBus: getEventBusService(), - firebaseDb: getFirebaseRealTimeDatabase(), - sendMsgService: getSendMsgService(), - bullionService: getBullionService(), - realtimeDatabase: getFirebaseRealTimeDatabase(), + getTradeUserService() +} + +func getTradeUserService() *tradeUserServiceStruct { + if TradeUserService == nil { + TradeUserService = &tradeUserServiceStruct{ + tradeUserRepo: repos.TradeUserRepo, + accessTokenService: AccessTokenService, + eventBus: getEventBusService(), + firebaseDb: getFirebaseRealTimeDatabase(), + sendMsgService: getSendMsgService(), + bullionService: getBullionService(), + realtimeDatabase: getFirebaseRealTimeDatabase(), + } + println("Trade User Service Initialized") } + return TradeUserService } func (service *tradeUserServiceStruct) VerifyAndSendOtpForNewUser(tradeUser *interfaces.TradeUserBase, bullionId string) (*string, error) { @@ -209,7 +217,9 @@ func (service *tradeUserServiceStruct) RegisterNewTradeUser(base *interfaces.Tra if err := utility.ValidateReqInput(entity); err != nil { return nil, err } - service.tradeUserRepo.Save(entity) + if _, err := service.tradeUserRepo.Save(entity); err != nil { + return nil, err + } service.firebaseDb.setPrivateData("tradeUsersNumbers", []string{entity.BullionId}, newUserNumber) go service.afterSuccessFullRegistration(entity.ID) return entity, nil @@ -270,6 +280,10 @@ func (service *tradeUserServiceStruct) LoginWithNumberAndPassword(number string, return service.generateTokensForTradeUserWithPasswordMatching(tradeUser, password) } +func (service *tradeUserServiceStruct) GetTradeUserById(id string) (*interfaces.TradeUserEntity, error) { + return service.tradeUserRepo.FindOne(id) +} + func (service *tradeUserServiceStruct) UpdateTradeUser(entity *interfaces.TradeUserEntity, adminId string) error { user, err := service.tradeUserRepo.FindOne(entity.ID) if err != nil { @@ -286,7 +300,9 @@ func (service *tradeUserServiceStruct) UpdateTradeUser(entity *interfaces.TradeU user.TradeUserBase = entity.TradeUserBase user.TradeUserAdvanced.IsActive = entity.TradeUserAdvanced.IsActive user.TradeUserMargins = entity.TradeUserMargins - service.tradeUserRepo.Save(user) + if _, err := service.tradeUserRepo.Save(entity); err != nil { + return err + } service.eventBus.Publish(events.CreateTradeUserUpdated(entity.BullionId, user, adminId)) return nil } @@ -329,7 +345,10 @@ func (service *tradeUserServiceStruct) TradeUserChangeStatus(id string, bullionI return err } entity.IsActive = isActive - service.tradeUserRepo.Save(entity) + + if _, err := service.tradeUserRepo.Save(entity); err != nil { + return err + } if isActive { service.eventBus.Publish(events.CreateTradeUserActivatedEvent(entity.BullionId, entity, adminId)) } else { diff --git a/src/utility/firebase/firebase-app.go b/src/utility/firebase/firebase-app.go index 5036b51..345f6d1 100644 --- a/src/utility/firebase/firebase-app.go +++ b/src/utility/firebase/firebase-app.go @@ -33,6 +33,7 @@ func init() { log.Fatalf("error initializing app: %v\n", err) } firebaseApp = app + log.Print("Firebase App initialized") firebaseDb, err := firebaseApp.DatabaseWithURL(FirebaseCtx, env.Env.FIREBASE_DATABASE_URL) if err != nil { log.Fatalf("error initializing Firebase Database: %v\n", err) diff --git a/src/utility/jwt/jwt.go b/src/utility/jwt/jwt.go index f3a22e6..d43ef96 100644 --- a/src/utility/jwt/jwt.go +++ b/src/utility/jwt/jwt.go @@ -90,7 +90,7 @@ func (t *TokenService) VerifyToken(token string) (*GeneralUserAccessRefreshToken } claim, ok := claimRaw.Claims.(*GeneralUserAccessRefreshToken) - if !ok && err == nil { + if !ok { err = &interfaces.RequestError{ StatusCode: 401, Code: interfaces.ERROR_INVALID_TOKEN, @@ -155,7 +155,7 @@ func (t *TokenService) VerifyTokenGeneralPurpose(token string) (*GeneralPurposeT } claim, ok := claimRaw.Claims.(*GeneralPurposeTokenGeneration) - if !ok && err == nil { + if !ok { err = &interfaces.RequestError{ StatusCode: 401, Code: interfaces.ERROR_INVALID_TOKEN,