Merge branch 'staging'

This commit is contained in:
skeris 2024-05-06 22:57:01 +03:00
commit 4dcc23c946
62 changed files with 1994 additions and 2332 deletions

1
.gitignore vendored

@ -4,4 +4,5 @@ vendor/
.idea/
.vscode
.env
main

@ -14,8 +14,6 @@ deploy-to-staging:
extends: .deploy_template
rules:
- if: "$CI_COMMIT_BRANCH == $STAGING_BRANCH"
after_script:
- ls
deploy-to-prod:
rules:

@ -1,5 +1,5 @@
# BUILD
FROM golang:1.20.3-alpine AS build
FROM golang:1.22.0-alpine AS build
# Update packages and clear cache
RUN apk add --no-cache curl
@ -16,30 +16,10 @@ ADD ./tools/migrate /bin/golang-migrate/
ADD . .
RUN git config --global url."https://buildToken:glpat-axA8ttckx3aPf_xd2Dym@penahub.gitlab.yandexcloud.net/".insteadOf "https://penahub.gitlab.yandexcloud.net/"
# Download go depences
RUN go mod download
RUN go mod download
# Build app
RUN GOOS=linux go build -o bin ./...
# TEST
FROM alpine:3.18.3 AS test
# Install packages
RUN apk --no-cache add ca-certificates
# Create home directory
WORKDIR /app
# Copy build file
COPY --from=build /app/bin/app ./app
# Copy migration dir
COPY --from=build /app/migrations/test ./migrations
# Install migrate tool
COPY --from=build /bin/golang-migrate /usr/local/bin
# CMD
CMD [ "./app" ]
# PRODUCTION
FROM alpine:3.18.3 AS production

@ -16,6 +16,8 @@ tags:
description: кошелёк
- name: history
description: история
- name: statistic
description: статистика
paths:
/account:
@ -742,6 +744,85 @@ paths:
schema:
$ref: "#/components/schemas/Error"
/quizlogo/stat:
post:
tags:
- statistic
summary: статистика пользователей, перешедших по логотипу в опросах
operationId: quizLogoStat
security:
- Bearer: [ ]
requestBody:
content:
'application/json':
schema:
type: object
properties:
to:
description: таймштамп времени, до которого выбирать статистику. если 0 или не передано - этого ограничения нет. верхняя граница времени
type: integer
from:
description: таймштамп времени, после которого выбирать статистику. если 0 или не передано - этого ограничения нет. нижняя граница времени
type: integer
page:
description: страница выборки
type: integer
limit:
description: лимит выборки
type: integer
responses:
'200':
description: Успешный ответ с данными статистики
content:
application/json:
schema:
$ref: '#/components/schemas/QuizLogoStat'
'500':
description: Внутренняя ошибка сервера
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/promocode/ltv:
post:
tags:
- statistic
summary: статистика по промокодам в разрезе регистраций и оплат
operationId: promocodeLTV
security:
- Bearer: [ ]
requestBody:
required: true
content:
'application/json':
schema:
type: object
properties:
to:
description: таймштамп времени, до которого выбирать статистику
type: integer
from:
description: таймштамп времени, после которого выбирать статистику
type: integer
required:
- from
- to
responses:
'200':
description: Успешный ответ с данными статистики
content:
application/json:
schema:
$ref: '#/components/schemas/PromoLtvStat'
'500':
description: Внутренняя ошибка сервера
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
components:
schemas:
@ -931,6 +1012,52 @@ components:
message:
type: string
example: user not found
QuizLogoStat:
type: array
items:
# count:
# type: integer
# description: Общее количество
type: object
properties:
id:
type: string
description: user id
money:
type: integer
description: Количество денег
regs:
type: integer
description: Количество регистраций
quizes:
type: array
items:
type: object
properties:
quiz:
type: string
description: qid quiz
regs:
type: integer
description: Количество регистраций
money:
type: integer
description: Количество денег
PromoLtvStat:
type: object
properties:
stats:
type: object
description: мапа ключ id промо, знчение количество регистраций и количество денег
additionalProperties:
type: object
properties:
regs:
type: integer
description: количество регистраций
money:
type: integer
description: количество денег
securitySchemes:
Bearer: # arbitrary name for the security scheme

@ -0,0 +1,23 @@
syntax = "proto3";
package codeword;
option go_package = "./codeword_rpc";
message Time {
int64 from = 1;
int64 to = 2;
}
service PromoCodeService {
rpc GetAllPromoActivations(Time) returns (stream PromoActivationResp);
}
message PromoActivationResp {
message UserTime {
string UserID = 1;
int64 Time = 2;
}
string ID = 1;
repeated UserTime Users = 2;
}

@ -32,11 +32,19 @@ services:
- KAFKA_BROKERS=customer-redpanda:9092
- KAFKA_TOPIC_TARIFF=tariffs
- AUTH_MICROSERVICE_USER_URL=http://pena-auth-service:8000/user
- HUBADMIN_MICROSERVICE_TARIFF_URL=http://hub-admin-backend-service:8000/tariff
- CURRENCY_MICROSERVICE_TRANSLATE_URL=http://cbrfworker-service:8000/change
- DISCOUNT_MICROSERVICE_GRPC_HOST=discount-service:9000
- PAYMENT_MICROSERVICE_GRPC_HOST=treasurer-service:9085
- AUTH_MICROSERVICE_USER_URL=http://10.6.0.11:59300/user
- HUBADMIN_MICROSERVICE_TARIFF_URL=http://10.6.0.11:59303/tariff
- CURRENCY_MICROSERVICE_TRANSLATE_URL=http://10.6.0.11:3131/change
- DISCOUNT_MICROSERVICE_GRPC_HOST=10.6.0.11:9001
- PAYMENT_MICROSERVICE_GRPC_HOST=10.6.0.11:9085
- VERIFICATION_MICROSERVICE_USER_URL=http://10.6.0.11:7035/verification
- TEMPLATEGEN_MICROSERVICE_URL=10.6.0.17
- API_URL=https://api.smtp.bz/v1/smtp/send
- MAIL_SENDER=noreply@mailing.pena.digital
- MAIL_API_KEY=P0YsjUB137upXrr1NiJefHmXVKW1hmBWlpev
- MAIL_AUTH_USERNAME=kotilion.95@gmail.com
- MAIL_AUTH_PASSWORD=vWwbCSg4bf0p
- MAIL_ADDRESS=sells@pena.digital
ports:
- 8082:8000
- 9092:9000

@ -31,6 +31,7 @@ services:
- PAYMENT_MICROSERVICE_GRPC_HOST=10.8.0.8:9085
- VERIFICATION_MICROSERVICE_USER_URL=http://10.8.0.8:7035/verification
- TEMPLATEGEN_MICROSERVICE_URL=10.6.0.17
- CODEWORD_MICROSERVICE_GRPC_HOST=10.8.0.8:59665
- API_URL=https://api.smtp.bz/v1/smtp/send
- MAIL_SENDER=noreply@noreply.pena.digital
- MAIL_API_KEY=8tv2xcsfCMBX3TCQxzgeeEwAEYyQrPUp0ggw
@ -42,6 +43,8 @@ services:
- JWT_PUBLIC_KEY=$JWT_PUBLIC_KEY
- JWT_ISSUER=pena-auth-service
- JWT_AUDIENCE=pena
- PUBLIC_KEY=$USER_PKEY
- PRIVATE_KEY=$USER_PRIVATEKEY
ports:
- 10.8.0.8:8065:8065
- 10.8.0.8:9065:9065

@ -11,27 +11,28 @@ services:
- HTTP_PORT=8065
- GRPC_HOST=0.0.0.0
- GRPC_PORT=9085
- GRPC_DOMEN=http://customer-service:9085
- GRPC_PORT=9065
- GRPC_DOMEN=10.8.0.6:9065
- MONGO_HOST=10.6.0.11
- MONGO_HOST=10.8.0.6
- MONGO_PORT=27017
- MONGO_USER=$MONGO_USER
- MONGO_PASSWORD=$MONGO_PASSWORD
- MONGO_DB_NAME=customer
- MONGO_AUTH=customer
- KAFKA_BROKERS=10.6.0.11:9092
- KAFKA_BROKERS=10.8.0.6:9092
- KAFKA_TOPIC_TARIFF=tariffs
- AUTH_MICROSERVICE_USER_URL=http://10.6.0.11:59300/user
- HUBADMIN_MICROSERVICE_TARIFF_URL=http://10.6.0.11:59303/tariff
- CURRENCY_MICROSERVICE_TRANSLATE_URL=http://10.6.0.11:3131/change
- DISCOUNT_MICROSERVICE_GRPC_HOST=10.6.0.11:9001
- PAYMENT_MICROSERVICE_GRPC_HOST=10.6.0.11:9085
- VERIFICATION_MICROSERVICE_USER_URL=http://10.6.0.11:7035/verification
- AUTH_MICROSERVICE_USER_URL=http://10.8.0.6:59300/user
- HUBADMIN_MICROSERVICE_TARIFF_URL=http://10.8.0.6:59303/tariff
- CURRENCY_MICROSERVICE_TRANSLATE_URL=http://10.8.0.6:3131/change
- DISCOUNT_MICROSERVICE_GRPC_HOST=10.8.0.6:9001
- PAYMENT_MICROSERVICE_GRPC_HOST=10.8.0.6:9085
- VERIFICATION_MICROSERVICE_USER_URL=http://10.8.0.6:7035/verification
- TEMPLATEGEN_MICROSERVICE_URL=10.6.0.17
- API_URL=https://api.smtp.bz/v1/smtp/send
- CODEWORD_MICROSERVICE_GRPC_HOST=10.8.0.6:59665
- MAIL_SENDER=noreply@mailing.pena.digital
- MAIL_API_KEY=P0YsjUB137upXrr1NiJefHmXVKW1hmBWlpev
- MAIL_AUTH_USERNAME=kotilion.95@gmail.com
@ -41,8 +42,10 @@ services:
- JWT_PUBLIC_KEY=$JWT_PUBLIC_KEY
- JWT_ISSUER=pena-auth-service
- JWT_AUDIENCE=pena
- PUBLIC_KEY=$USER_PKEY
- PRIVATE_KEY=$USER_PRIVATEKEY
ports:
- 8065:8065
- 9065:9065
- 10.8.0.6:8065:8065
- 10.8.0.6:9065:9065
networks:
- default

68
go.mod

@ -1,6 +1,6 @@
module penahub.gitlab.yandexcloud.net/pena-services/customer
go 1.21
go 1.22.0
require (
github.com/deepmap/oapi-codegen v1.16.2
@ -12,121 +12,63 @@ require (
github.com/joho/godotenv v1.5.1
github.com/labstack/echo/v4 v4.11.4
github.com/oapi-codegen/runtime v1.1.1
github.com/pioz/faker v1.7.3
github.com/sethvargo/go-envconfig v1.0.0
github.com/stretchr/testify v1.8.4
github.com/twmb/franz-go v1.16.1
github.com/twmb/franz-go/pkg/kadm v1.11.0
go.mongodb.org/mongo-driver v1.14.0
go.uber.org/zap v1.27.0
google.golang.org/genproto v0.0.0-20240221002015-b0ce06bbee7c
google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c
google.golang.org/grpc v1.62.0
google.golang.org/protobuf v1.32.0
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240220080149-ae9c991d3ece
penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240412164014-6ce70d76fedc
)
require (
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20231115204500-e097f827e652.1 // indirect
connectrpc.com/connect v1.14.0 // indirect
connectrpc.com/otelconnect v0.7.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/bufbuild/buf v1.29.0 // indirect
github.com/bufbuild/protocompile v0.8.0 // indirect
github.com/bufbuild/protovalidate-go v0.5.0 // indirect
github.com/bufbuild/protoyaml-go v0.1.7 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.5.0 // indirect
github.com/docker/cli v24.0.7+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v25.0.0+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.1 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/fgprof v0.9.3 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-chi/chi/v5 v5.0.11 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.20.2 // indirect
github.com/go-openapi/swag v0.22.9 // indirect
github.com/gofrs/uuid/v5 v5.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect
github.com/google/cel-go v0.19.0 // indirect
github.com/google/go-containerregistry v0.18.0 // indirect
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/invopop/yaml v0.2.0 // indirect
github.com/jdx/go-netrc v1.0.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.7 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/labstack/gommon v0.4.2 // indirect
github.com/mailru/easyjson v0.7.7 // 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/mitchellh/go-homedir v1.1.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.7.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/cors v1.10.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/cobra v1.8.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/tetratelabs/wazero v1.6.0 // indirect
github.com/tealeg/xlsx v1.0.5 // indirect
github.com/twmb/franz-go/pkg/kmsg v1.7.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.52.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
github.com/vbatts/tar-split v0.11.5 // 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
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect
go.opentelemetry.io/otel v1.22.0 // indirect
go.opentelemetry.io/otel/metric v1.22.0 // indirect
go.opentelemetry.io/otel/sdk v1.22.0 // indirect
go.opentelemetry.io/otel/trace v1.22.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/term v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.17.0 // indirect
google.golang.org/genproto v0.0.0-20240221002015-b0ce06bbee7c // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

268
go.sum

@ -1,119 +1,43 @@
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20231115204500-e097f827e652.1 h1:u0olL4yf2p7Tl5jfsAK5keaFi+JFJuv1CDHrbiXkxkk=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.32.0-20231115204500-e097f827e652.1/go.mod h1:tiTMKD8j6Pd/D2WzREoweufjzaJKHZg35f/VGcZ2v3I=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
connectrpc.com/connect v1.14.0 h1:PDS+J7uoz5Oui2VEOMcfz6Qft7opQM9hPiKvtGC01pA=
connectrpc.com/connect v1.14.0/go.mod h1:uoAq5bmhhn43TwhaKdGKN/bZcGtzPW1v+ngDTn5u+8s=
connectrpc.com/otelconnect v0.7.0 h1:ZH55ZZtcJOTKWWLy3qmL4Pam4RzRWBJFOqTPyAqCXkY=
connectrpc.com/otelconnect v0.7.0/go.mod h1:Bt2ivBymHZHqxvo4HkJ0EwHuUzQN6k2l0oH+mp/8nwc=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/bufbuild/buf v1.29.0 h1:llP6HqOcCaSGBxOfnrp/mwvcY1O/dciEOl1QaMEOB3M=
github.com/bufbuild/buf v1.29.0/go.mod h1:UTjvPXTObvKQiGqxod32wt9zRz70TJsMpaigpbIZGuc=
github.com/bufbuild/protocompile v0.8.0 h1:9Kp1q6OkS9L4nM3FYbr8vlJnEwtbpDPQlQOVXfR+78s=
github.com/bufbuild/protocompile v0.8.0/go.mod h1:+Etjg4guZoAqzVk2czwEQP12yaxLJ8DxuqCJ9qHdH94=
github.com/bufbuild/protovalidate-go v0.5.0 h1:xFery2RlLh07FQTvB7hlasKqPrDK2ug+uw6DUiuadjo=
github.com/bufbuild/protovalidate-go v0.5.0/go.mod h1:3XAwFeJ2x9sXyPLgkxufH9sts1tQRk8fdt1AW93NiUU=
github.com/bufbuild/protoyaml-go v0.1.7 h1:3uKIoNb/l5zrZ93u+Xzsg6cdAO06lveZE/K7UUbUQLw=
github.com/bufbuild/protoyaml-go v0.1.7/go.mod h1:R8vE2+l49bSiIExP4VJpxOXleHE+FDzZ6HVxr3cYunw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU=
github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk=
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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=
github.com/deepmap/oapi-codegen v1.1.1 h1:6vwgopsCgvIYG6rWc+GnWakgN7oznZYj238Xta0hE9Y=
github.com/deepmap/oapi-codegen v1.1.1/go.mod h1:dAc3+1joSEZQ6WX0WNaqZAN1J1rCX3ptKLX9bghP7Y0=
github.com/deepmap/oapi-codegen v1.12.4 h1:pPmn6qI9MuOtCz82WY2Xaw46EQjgvxednXXrP7g5Q2s=
github.com/deepmap/oapi-codegen v1.12.4/go.mod h1:3lgHGMu6myQ2vqbbTXH2H1o4eXFTGnFiDaOaKKl5yas=
github.com/deepmap/oapi-codegen v1.16.2 h1:xGHx0dNqYfy9gE8a7AVgVM8Sd5oF9SEgePzP+UPAUXI=
github.com/deepmap/oapi-codegen v1.16.2/go.mod h1:rdYoEA2GE+riuZ91DvpmBX9hJbQpuY9wchXpfQ3n+ho=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg=
github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v25.0.0+incompatible h1:g9b6wZTblhMgzOT2tspESstfw6ySZ9kdm94BLDKaZac=
github.com/docker/docker v25.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo=
github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/getkin/kin-openapi v0.116.0 h1:o986hwgMzR972JzOG5j6+WTwWqllZLs1EJKMKCivs2E=
github.com/getkin/kin-openapi v0.116.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc=
github.com/getkin/kin-openapi v0.123.0 h1:zIik0mRwFNLyvtXK274Q6ut+dPh6nlxBp0x7mNrPhs8=
github.com/getkin/kin-openapi v0.123.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA=
github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
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/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.9 h1:XX2DssF+mQKM2DHsbgZK74y/zj4mo9I99+89xUmuZCE=
github.com/go-openapi/swag v0.22.9/go.mod h1:3/OXnFfnMAwBD099SwYRk7GD3xOrr1iL7d/XNLXVVwE=
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8=
github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/UE=
github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/gofiber/fiber/v2 v2.52.1 h1:1RoU2NS+b98o1L77sdl5mboGPiW+0Ypsi5oLmcYlgHI=
github.com/gofiber/fiber/v2 v2.52.1/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@ -124,42 +48,20 @@ github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaW
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/cel-go v0.19.0 h1:vVgaZoHPBDd1lXCYGQOh5A06L4EtuIfmqQ/qnSXSKiU=
github.com/google/cel-go v0.19.0/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.18.0 h1:ShE7erKNPqRh5ue6Z9DUOlk04WsnFWPO6YGr3OxnfoQ=
github.com/google/go-containerregistry v0.18.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815 h1:WzfWbQz/Ze8v6l++GGbGNFZnUShVpP/0xffCPLL+ax8=
github.com/google/pprof v0.0.0-20240117000934-35fc243c5815/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc=
github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
github.com/jdx/go-netrc v1.0.0 h1:QbLMLyCZGj0NA8glAhxUpf1zDg6cxnWgMBbjq40W0gQ=
github.com/jdx/go-netrc v1.0.0/go.mod h1:Gh9eFQJnoTNIRHXl2j5bJXA1u84hQWJWgGh569zF3v8=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@ -167,195 +69,105 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4=
github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ=
github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8=
github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8=
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
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/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
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/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo=
github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A=
github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro=
github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI=
github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/oapi-codegen/testutil v1.0.0 h1:1GI2IiMMLh2vDHr1OkNacaYU/VaApKdcmfgl4aeXAa8=
github.com/oapi-codegen/testutil v1.0.0/go.mod h1:ttCaYbHvJtHuiyeBF0tPIX+4uhEPTeizXKx28okijLw=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw=
github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pioz/faker v1.7.3 h1:Tez8Emuq0UN+/d6mo3a9m/9ZZ/zdfJk0c5RtRatrceM=
github.com/pioz/faker v1.7.3/go.mod h1:xSpay5w/oz1a6+ww0M3vfpe40pSIykeUPeWEc3TvVlc=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
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=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo=
github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sethvargo/go-envconfig v0.9.0 h1:Q6FQ6hVEeTECULvkJZakq3dZMeBQ3JUpcKMfPQbKMDE=
github.com/sethvargo/go-envconfig v0.9.0/go.mod h1:Iz1Gy1Sf3T64TQlJSvee81qDhf7YIlt8GMUX6yyNFs0=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/sethvargo/go-envconfig v1.0.0 h1:1C66wzy4QrROf5ew4KdVw942CQDa55qmlYmw9FZxZdU=
github.com/sethvargo/go-envconfig v1.0.0/go.mod h1:Lzc75ghUn5ucmcRGIdGQ33DKJrcjk4kihFYgSTBmjIc=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
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/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzlP3g=
github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
github.com/twmb/franz-go v1.13.6 h1:DRh06Hy3GthZuA+fQhDo+IMV+QUZHQfS2TIiWf/rCw8=
github.com/twmb/franz-go v1.13.6/go.mod h1:jm/FtYxmhxDTN0gNSb26XaJY0irdSVcsckLiR5tQNMk=
github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
github.com/twmb/franz-go v1.16.1 h1:rpWc7fB9jd7TgmCyfxzenBI+QbgS8ZfJOUQE+tzPtbE=
github.com/twmb/franz-go v1.16.1/go.mod h1:/pER254UPPGp/4WfGqRi+SIRGE50RSQzVubQp6+N4FA=
github.com/twmb/franz-go/pkg/kadm v1.8.1 h1:SrzL855I7gQTGdMtOYGTHhebs7TPgPN29FPtjusqwlE=
github.com/twmb/franz-go/pkg/kadm v1.8.1/go.mod h1:qUSM7pxoMCU1UNu5H4USE64ODcVmeG9LS96mysv1nu8=
github.com/twmb/franz-go/pkg/kadm v1.11.0 h1:FfeWJ0qadntFpAcQt8JzNXW4dijjytZNLrzJuzzzuxA=
github.com/twmb/franz-go/pkg/kadm v1.11.0/go.mod h1:qrhkdH+SWS3ivmbqOgHbpgVHamhaKcjH0UM+uOp0M1A=
github.com/twmb/franz-go/pkg/kmsg v1.4.0 h1:tbp9hxU6m8qZhQTlpGiaIJOm4BXix5lsuEZ7K00dF0s=
github.com/twmb/franz-go/pkg/kmsg v1.4.0/go.mod h1:SxG/xJKhgPu25SamAq0rrucfp7lbzCpEXOC+vH/ELrY=
github.com/twmb/franz-go/pkg/kmsg v1.7.0 h1:a457IbvezYfA5UkiBvyV3zj0Is3y1i8EJgqjJYoij2E=
github.com/twmb/franz-go/pkg/kmsg v1.7.0/go.mod h1:se9Mjdt0Nwzc9lnjJ0HyDtLyBnaBDAd7pCje47OhSyw=
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
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.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
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/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
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/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts=
github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
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-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
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/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk=
go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
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.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw=
go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y=
go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI=
go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg=
go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY=
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.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0=
go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -363,16 +175,10 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
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.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o=
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
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=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@ -381,8 +187,6 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -392,15 +196,10 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/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-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -411,8 +210,6 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -421,27 +218,15 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -449,22 +234,15 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
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.6/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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
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.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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=
@ -479,8 +257,6 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -490,18 +266,10 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
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-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20230223222841-637eb2293923 h1:znp6mq/drrY+6khTAlJUDNFFcDGV2ENLYKpMq8SyCds=
google.golang.org/genproto v0.0.0-20230223222841-637eb2293923/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw=
google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac h1:ZL/Teoy/ZGnzyrqK/Optxxp2pmVh+fmJ97slxSRyzUg=
google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k=
google.golang.org/genproto v0.0.0-20240221002015-b0ce06bbee7c h1:Zmyn5CV/jxzKnF+3d+xzbomACPwLQqVpLTpyXN5uTaQ=
google.golang.org/genproto v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe h1:0poefMBYvYbs7g5UkjS6HcxBPaTRAmznle9jnxYoAI8=
google.golang.org/genproto/googleapis/api v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c h1:9g7erC9qu44ks7UK4gDNlnk4kOxZG707xKm4jVniy6o=
google.golang.org/genproto/googleapis/api v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe h1:bQnxqljG/wqi4NTXu2+DJ3n7APcEA882QZ1JvhQAq9o=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@ -509,27 +277,19 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
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.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc=
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y=
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.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@ -537,7 +297,7 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240202120244-c4ef330cfe5d h1:gbaDt35HMDqOK84WYmDIlXMI7rstUcRqNttaT6Kx1do=
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240202120244-c4ef330cfe5d/go.mod h1:lTmpjry+8evVkXWbEC+WMOELcFkRD1lFMc7J09mOndM=
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240220080149-ae9c991d3ece h1:CsjgNNqssfa05B7iDNMyK2wWR7SZ/kglLTMAVrhGLtY=
penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240220080149-ae9c991d3ece/go.mod h1:lTmpjry+8evVkXWbEC+WMOELcFkRD1lFMc7J09mOndM=
penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240412164014-6ce70d76fedc h1:B9X8pOrqWPGbWZNXSJEUk/8GWeBDGQmMKgQ0F+PSliQ=
penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240412164014-6ce70d76fedc/go.mod h1:/DcyAjBh41IbomuDu5QzhL9flZW6lWO3ZAEbUXKobk0=

@ -18,6 +18,7 @@ import (
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/closer"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/kafka"
qutils "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/utils"
)
const (
@ -66,6 +67,11 @@ func Run(config *models.Config, logger *zap.Logger) (appErr error) {
TariffClient: kafkaTariffClient,
})
err = kafkaTariffClient.Ping(ctx)
if err != nil {
return err
}
clients := initialize.NewClients(initialize.ClientsDeps{
Logger: logger,
AuthURL: &config.Service.AuthMicroservice.URL,
@ -101,7 +107,9 @@ func Run(config *models.Config, logger *zap.Logger) (appErr error) {
return fmt.Errorf("failed to loading openapi spec: %w", err)
}
api := swagger.NewAPI2(logger, mongoDB, config, brokers.TariffConsumer, brokers.TariffProducer)
encrypt := qutils.NewEncrypt(config.Service.PubKey, config.Service.PrivKey)
api := swagger.NewAPI2(logger, mongoDB, config, brokers.TariffConsumer, brokers.TariffProducer, encrypt)
serverHTTP, httpErr := server.NewHTTP(server.DepsHTTP{
Logger: logger,

@ -4,11 +4,6 @@ import (
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/grpc/customer"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/grpc/payment"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/rest/account"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/rest/cart"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/rest/currency"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/rest/history"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/rest/wallet"
)
type ControllersDeps struct {
@ -17,38 +12,12 @@ type ControllersDeps struct {
}
type Controllers struct {
AccountController *account.Controller
CurrencyController *currency.Controller
CartController *cart.Controller
HistoryController *history.Controller
WalletController *wallet.Controller
PaymentController *payment.Controller
CustomerController *customer.Controller
}
func NewControllers(deps ControllersDeps) *Controllers {
return &Controllers{
AccountController: account.New(account.Deps{
Logger: deps.Logger,
Service: deps.Services.AccountService,
}),
CurrencyController: currency.New(currency.Deps{
Logger: deps.Logger,
CurrencyService: deps.Services.CurrencyService,
}),
CartController: cart.New(cart.Deps{
Logger: deps.Logger,
CartService: deps.Services.CartService,
}),
HistoryController: history.New(history.Deps{
Logger: deps.Logger,
HistoryService: deps.Services.HistoryService,
}),
WalletController: wallet.New(wallet.Deps{
Logger: deps.Logger,
WalletService: deps.Services.WalletService,
PaymentService: deps.Services.PaymentService,
}),
PaymentController: payment.New(payment.Deps{
Logger: deps.Logger,
PaymentCallbackService: deps.Services.PaymentCallbackService,

@ -53,11 +53,6 @@ func TestNewControllers(t *testing.T) {
})
assert.NotNil(t, controllers)
assert.NotNil(t, controllers.AccountController)
assert.NotNil(t, controllers.CurrencyController)
assert.NotNil(t, controllers.CartController)
assert.NotNil(t, controllers.HistoryController)
assert.NotNil(t, controllers.WalletController)
})
})
}

@ -3,13 +3,9 @@ package initialize
import (
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/account"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/broker/tariff"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/callback"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/cart"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/currency"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/history"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/payment"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/wallet"
)
@ -22,12 +18,8 @@ type ServicesDeps struct {
}
type Services struct {
AccountService *account.Service
CurrencyService *currency.Service
CartService *cart.Service
HistoryService *history.Service
WalletService *wallet.Service
PaymentService *payment.Service
PaymentCallbackService *callback.PaymentCallbackService
TariffBrokerService *tariff.Service
}
@ -58,30 +50,6 @@ func NewServices(deps ServicesDeps) *Services {
return &Services{
WalletService: walletService,
HistoryService: historyService,
AccountService: account.New(account.Deps{
Logger: deps.Logger,
Repository: deps.Repositories.AccountRepository,
AuthClient: deps.Clients.AuthClient,
}),
CurrencyService: currency.New(currency.Deps{
Logger: deps.Logger,
Repository: deps.Repositories.CurrencyRepository,
}),
CartService: cart.New(cart.Deps{
Logger: deps.Logger,
Repository: deps.Repositories.AccountRepository,
HubadminClient: deps.Clients.HubadminClient,
DiscountClient: deps.Clients.DiscountClient,
WalletService: walletService,
HistoryService: historyService,
TariffBrokerService: tariffBrokerService,
}),
PaymentService: payment.New(payment.Deps{
Logger: deps.Logger,
ConfigurationGRPC: deps.ConfigurationGRPC,
PaymentClient: deps.Clients.PaymentClient,
AuthClient: deps.Clients.AuthClient,
}),
PaymentCallbackService: callback.NewPaymentCallbackService(callback.PaymentCallbackServiceDeps{
Logger: deps.Logger,
AccountRepository: deps.Repositories.AccountRepository,

@ -48,12 +48,8 @@ func TestNewServices(t *testing.T) {
})
assert.NotNil(t, services)
assert.NotNil(t, services.AccountService)
assert.NotNil(t, services.CartService)
assert.NotNil(t, services.CurrencyService)
assert.NotNil(t, services.HistoryService)
assert.NotNil(t, services.WalletService)
assert.NotNil(t, services.PaymentService)
assert.NotNil(t, services.PaymentCallbackService)
assert.NotNil(t, services.TariffBrokerService)
})

@ -0,0 +1,81 @@
package client
import (
"context"
"fmt"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"io"
"log"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
codeword_rpc "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/codeword"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate"
)
type CodewordClientDeps struct {
Logger *zap.Logger
CodewordServiceHost string
}
type CodewordClient struct {
logger *zap.Logger
codewordServiceHost string
}
func NewCodewordClient(deps CodewordClientDeps) *CodewordClient {
if deps.Logger == nil {
log.Panicln("logger is nil on <NewCodewordClient>")
}
if validate.IsStringEmpty(deps.CodewordServiceHost) {
log.Panicln("codeword host is empty on <NewCodewordClient>")
}
return &CodewordClient{
logger: deps.Logger,
codewordServiceHost: deps.CodewordServiceHost,
}
}
func (receiver *CodewordClient) GetAllPromoActivations(ctx context.Context, req *codeword_rpc.Time) (map[string][]*codeword_rpc.PromoActivationResp_UserTime, errors.Error) {
connection, err := grpc.Dial(receiver.codewordServiceHost, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
receiver.logger.Error("failed to connect on <GetAllPromoActivations> of <CodewordClient>", zap.Error(err), zap.String("codeword host", receiver.codewordServiceHost))
return nil, errors.New(fmt.Errorf("failed connect to codeword service: %w", err), errors.ErrInternalError)
}
defer func() {
if closeErr := connection.Close(); closeErr != nil {
receiver.logger.Error("failed to close connection on <GetAllPromoActivations> of <CodewordClient>", zap.Error(closeErr))
}
}()
client := codeword_rpc.NewPromoCodeServiceClient(connection)
stream, err := client.GetAllPromoActivations(ctx, req)
if err != nil {
receiver.logger.Error("failed getting stats resp on <GetAllPromoActivations> of <DiscountClient>", zap.Error(err))
return nil, errors.New(fmt.Errorf("failed getting stats resp from codeword: %w", err), errors.ErrInternalError)
}
response := make(map[string][]*codeword_rpc.PromoActivationResp_UserTime)
for {
activations, err := stream.Recv()
if err != nil {
if err == io.EOF {
break
}
return nil, errors.New(fmt.Errorf("failed to receive response stream in codeword rpc client: %w", err), errors.ErrInternalError)
}
if activations == nil {
continue
}
response[activations.ID] = append(response[activations.ID], activations.Users...)
}
return response, nil
}

@ -60,3 +60,26 @@ func (receiver *DiscountClient) Apply(ctx context.Context, request *discount.App
return response, nil
}
func (receiver *DiscountClient) DeleteDiscount(ctx context.Context, request *discount.GetDiscountByIDRequest) (*discount.Discount, errors.Error) {
connection, err := grpc.Dial(receiver.discountServiceHost, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
receiver.logger.Error("failed to connect on <DeleteDiscount> of <DiscountClient>", zap.Error(err), zap.String("discount host", receiver.discountServiceHost))
return nil, errors.New(fmt.Errorf("failed connect to discount service: %w", err), errors.ErrInternalError)
}
defer func() {
if closeErr := connection.Close(); closeErr != nil {
receiver.logger.Error("failed to close connection on <DeleteDiscount> of <DiscountClient>", zap.Error(closeErr))
}
}()
client := discount.NewDiscountServiceClient(connection)
response, err := client.DeleteDiscount(ctx, request)
if err != nil {
receiver.logger.Error("failed to apply discounts on <DeleteDiscount> of <DiscountClient>", zap.Error(err), zap.Any("request", request))
return nil, errors.New(fmt.Errorf("failed to delete discount by id: %w", err), errors.ErrInternalError)
}
return response, nil
}

@ -63,7 +63,7 @@ func (receiver *MailClient) SendMessage(userEmail string, verification *models.V
return handleError(receiver.deps.Logger, "Error closing form writer", err)
}
fmt.Println("SEEEEEND", receiver.deps.ApiUrl)
fmt.Println("SEEEEEND", receiver.deps.ApiUrl)
req := receiver.deps.FiberClient.Post(receiver.deps.ApiUrl).Body(form.Bytes()).ContentType(writer.FormDataContentType())
if receiver.deps.ApiKey != "" {
req.Set("Authorization", receiver.deps.ApiKey)

@ -42,7 +42,7 @@ func (receiver *Controller) InsertHistory(ctx context.Context, in *customer.Hist
UserID: in.UserID,
Comment: in.Comment,
Key: in.Key,
RawDetails: in.RawDetails,
//RawDetails: in.RawDetails,
}); err != nil {
receiver.logger.Error("failed to insert history on <InsertHistory> of <HistoryController>", zap.Error(err))
return nil, errors.GRPC("failed to insert history", err)

@ -45,6 +45,7 @@ func (receiver *Controller) OnSuccess(ctx context.Context, in *payment_callback.
Currency: in.Payment.Currency,
Amount: in.Payment.Amount,
UserID: in.Payment.UserID,
Type: in.Payment.Type,
}); err != nil {
receiver.logger.Error("failed to send success event on <OnSuccess> of <PaymentController>", zap.Error(err))
return nil, errors.GRPC("failed to send success event", err)

@ -1,166 +0,0 @@
package account
import (
"fmt"
"log"
"net/http"
"github.com/labstack/echo/v4"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/account"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/echotools"
)
type Deps struct {
Logger *zap.Logger
Service *account.Service
}
type Controller struct {
logger *zap.Logger
service *account.Service
}
func New(deps Deps) *Controller {
if deps.Logger == nil {
log.Panicln("logger is nil on <New (account controller)>")
}
if deps.Service == nil {
log.Panicln("account service is nil on <New (account controller)>")
}
return &Controller{
logger: deps.Logger,
service: deps.Service,
}
}
func (receiver *Controller) GetAccount(ctx echo.Context) error {
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
if !ok {
receiver.logger.Error("failed to convert jwt payload to string on <GetAccount> of <AccountController>")
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to convert jwt payload to string: %s", userID),
errors.ErrInvalidArgs,
))
}
account, err := receiver.service.GetAccountByUserID(ctx.Request().Context(), userID)
if err != nil {
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, account)
}
func (receiver *Controller) GetDirectAccount(ctx echo.Context, userID string) error {
account, err := receiver.service.GetAccountByUserID(ctx.Request().Context(), userID)
if err != nil {
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, account)
}
func (receiver *Controller) GetAccounts(ctx echo.Context, params swagger.PaginationAccountsParams) error {
response, err := receiver.service.GetAccountsList(
ctx.Request().Context(),
utils.DeterminePagination(params.Page, params.Limit),
)
if err != nil {
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, response)
}
func (receiver *Controller) CreateAccount(ctx echo.Context) error {
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
if !ok {
receiver.logger.Error("failed to convert jwt payload to string on <CreateAccount> of <AccountController>")
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to convert jwt payload to string: %s", userID),
errors.ErrInvalidArgs,
))
}
account, err := receiver.service.CreateAccountByUserID(ctx.Request().Context(), userID)
if err != nil {
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, account)
}
func (receiver *Controller) RemoveAccount(ctx echo.Context) error {
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
if !ok {
receiver.logger.Error("failed to convert jwt payload to string on <RemoveAccount> of <AccountController>")
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to convert jwt payload to string: %s", userID),
errors.ErrInvalidArgs,
))
}
account, err := receiver.service.RemoveAccount(ctx.Request().Context(), userID)
if err != nil {
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, account)
}
func (receiver *Controller) RemoveDirectAccount(ctx echo.Context, userID string) error {
account, err := receiver.service.RemoveAccount(ctx.Request().Context(), userID)
if err != nil {
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, account)
}
func (receiver *Controller) SetVerificationStatus(ctx echo.Context, userID string) error {
request, bindErr := echotools.Bind[models.SetAccountStatus](ctx)
if bindErr != nil {
receiver.logger.Error("failed to bind json request on <SetVerificationStatus> of <AccountController>", zap.Error(bindErr))
return errors.HTTP(ctx, errors.New(fmt.Errorf("failed to bind json: %w", bindErr), errors.ErrInternalError))
}
account, err := receiver.service.SetVerificationStatus(ctx.Request().Context(), userID, request.Status)
if err != nil {
receiver.logger.Error("failed set status on <SetVerificationStatus> of <AccountController>", zap.Error(err))
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, account)
}
func (receiver *Controller) UpdateAccountName(ctx echo.Context) error {
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
if !ok {
receiver.logger.Error("failed to convert jwt payload to string on <UpdateAccountName> of <AccountController>")
return errors.HTTP(ctx, errors.New(fmt.Errorf("failed to convert jwt payload to string: %s", userID), errors.ErrInvalidArgs))
}
request, bindErr := echotools.Bind[models.Name](ctx)
if bindErr != nil {
receiver.logger.Error("failed to bind json request on <UpdateAccountName> of <AccountController>", zap.Error(bindErr))
return errors.HTTP(ctx, errors.New(fmt.Errorf("failed to bind json: %w", bindErr), errors.ErrInternalError))
}
account, err := receiver.service.UpdateAccountName(ctx.Request().Context(), userID, request)
if err != nil {
receiver.logger.Error("failed to update account name on <UpdateAccountName> of <AccountController>", zap.Error(err))
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, account)
}

@ -1,118 +0,0 @@
package cart
import (
"fmt"
"log"
"net/http"
"github.com/labstack/echo/v4"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/cart"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate"
)
type Deps struct {
Logger *zap.Logger
CartService *cart.Service
}
type Controller struct {
logger *zap.Logger
cartService *cart.Service
}
func New(deps Deps) *Controller {
if deps.Logger == nil {
log.Panicln("logger is nil on <New (cart controller)>")
}
if deps.CartService == nil {
log.Panicln("cart service is nil on <New (cart controller)>")
}
return &Controller{
logger: deps.Logger,
cartService: deps.CartService,
}
}
func (receiver *Controller) Remove(ctx echo.Context, params swagger.RemoveFromCartParams) error {
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
if !ok {
receiver.logger.Error("failed to convert jwt payload to string on <Remove> of <CartController>")
return errors.HTTP(ctx, errors.NewWithMessage("failed to convert jwt payload to string", errors.ErrInvalidArgs))
}
if validate.IsStringEmpty(params.Id) {
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to remove cart item from user <%s>: empty item id", userID),
errors.ErrInvalidArgs,
))
}
cartItems, err := receiver.cartService.Remove(ctx.Request().Context(), userID, params.Id)
if err != nil {
receiver.logger.Error("failed to remove item from cart on <Remove> of <CartController>", zap.Error(err))
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, cartItems)
}
func (receiver *Controller) Add(ctx echo.Context, params swagger.Add2cartParams) error {
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
if !ok {
receiver.logger.Error("failed to convert jwt payload to string on <Add> of <CartController>")
return errors.HTTP(ctx, errors.NewWithMessage("failed to convert jwt payload to string", errors.ErrInvalidArgs))
}
token, ok := ctx.Get(models.AuthJWTDecodedAccessTokenKey).(string)
if !ok {
receiver.logger.Error("failed to convert access token payload to string on <Add> of <CartController>")
return errors.HTTP(ctx, errors.NewWithMessage("failed to convert access token payload to string", errors.ErrInvalidArgs))
}
if validate.IsStringEmpty(params.Id) {
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to add cart item to user <%s>: empty item id", userID),
errors.ErrInvalidArgs,
))
}
cartItems, err := receiver.cartService.Add(ctx.Request().Context(), &models.AddItemToCart{
UserID: userID,
TariffID: params.Id,
AccessToken: token,
})
if err != nil {
receiver.logger.Error("failed to add item to cart on <Add> of <CartController>", zap.Error(err))
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, cartItems)
}
func (receiver *Controller) Pay(ctx echo.Context) error {
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
if !ok {
receiver.logger.Error("failed to convert jwt payload to string on <Pay> of <CartController>")
return errors.HTTP(ctx, errors.NewWithMessage("failed to convert jwt payload to string", errors.ErrInvalidArgs))
}
token, ok := ctx.Get(models.AuthJWTDecodedAccessTokenKey).(string)
if !ok {
receiver.logger.Error("failed to convert access token payload to string on <Pay> of <CartController>")
return errors.HTTP(ctx, errors.NewWithMessage("failed to convert access token payload to string", errors.ErrInvalidArgs))
}
account, err := receiver.cartService.Pay(ctx.Request().Context(), token, userID)
if err != nil {
receiver.logger.Error("failed to pay cart on <Pay> of <CartController>", zap.Error(err))
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, account)
}

@ -1,82 +0,0 @@
package currency
import (
"fmt"
"log"
"net/http"
"github.com/labstack/echo/v4"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/currency"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/echotools"
)
type Deps struct {
Logger *zap.Logger
CurrencyService *currency.Service
}
type Controller struct {
logger *zap.Logger
currencyService *currency.Service
}
func New(deps Deps) *Controller {
if deps.Logger == nil {
log.Panicln("logger is nil on <New (account controller)>")
}
if deps.CurrencyService == nil {
log.Panicln("currency service is nil on <New (account controller)>")
}
return &Controller{
logger: deps.Logger,
currencyService: deps.CurrencyService,
}
}
func (receiver *Controller) GetCurrencies(ctx echo.Context) error {
currencies, err := receiver.currencyService.GetCurrencies(ctx.Request().Context())
if err != nil {
receiver.logger.Error(
"failed to get currencies on <GetCurrencies> of <CurrencyController>",
zap.Error(err),
)
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, currencies)
}
func (receiver *Controller) PutCurrencies(ctx echo.Context) error {
currencies, bindErr := echotools.Bind[[]string](ctx)
if bindErr != nil {
receiver.logger.Error(
"failed to parse body on <PutCurrencies> of <CurrencyController>",
zap.Error(bindErr),
)
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to parse body: %w", bindErr),
errors.ErrInvalidArgs,
))
}
currenciesCopy := *currencies
if len(currenciesCopy) < 1 {
currenciesCopy = make([]string, 0)
}
updatedCurrencies, err := receiver.currencyService.PutCurrencies(ctx.Request().Context(), currenciesCopy)
if err != nil {
receiver.logger.Error(
"failed to put currencies on <PutCurrencies> of <CurrencyController>",
zap.Error(err),
)
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, updatedCurrencies)
}

@ -1,135 +0,0 @@
package history
import (
"fmt"
"log"
"net/http"
"github.com/labstack/echo/v4"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/history"
)
type Deps struct {
Logger *zap.Logger
HistoryService *history.Service
}
type Controller struct {
logger *zap.Logger
historyService *history.Service
}
func New(deps Deps) *Controller {
if deps.Logger == nil {
log.Panicln("logger is nil on <New (history controller)>")
}
if deps.HistoryService == nil {
log.Panicln("HistoryService is nil on <New (history controller)>")
}
return &Controller{
logger: deps.Logger,
historyService: deps.HistoryService,
}
}
func (receiver *Controller) GetHistoryList(ctx echo.Context, params swagger.GetHistoryParams) error {
var userID string
if params.AccountID != nil && *params.AccountID != "" {
userID = *params.AccountID
} else {
userID, _ = ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
}
histories, err := receiver.historyService.GetHistoryList(ctx.Request().Context(), &history.GetHistories{
Type: params.Type,
UserID: userID,
Pagination: &models.Pagination{
Page: int64(*params.Page),
Limit: int64(*params.Limit),
},
})
if err != nil {
receiver.logger.Error("failed to get histories on <GetHistoryList> of <HistoryController>", zap.Error(err))
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, histories)
}
// TODO:tests.
func (receiver *Controller) GetRecentTariffs(ctx echo.Context) error {
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
if !ok {
receiver.logger.Error("failed to convert jwt payload to string on <GetRecentTariffs> of <HistoryController>")
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to convert jwt payload to string: %s", userID),
errors.ErrInvalidArgs,
))
}
tariffs, err := receiver.historyService.GetRecentTariffs(ctx.Request().Context(), userID)
if err != nil {
receiver.logger.Error("failed to get recent tariffs on <GetRecentTariffs> of <HistoryController>",
zap.String("userId", userID),
zap.Error(err),
)
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, tariffs)
}
// TODO:tests.
func (receiver *Controller) SendReport(ctx echo.Context) error {
historyID := ctx.Param("id")
err := receiver.historyService.GetHistoryByID(ctx.Request().Context(), historyID)
if err != nil {
receiver.logger.Error("failed to send report on <SendReport> of <HistoryController>", zap.Error(err))
return errors.HTTP(ctx, err)
}
return ctx.NoContent(http.StatusOK)
}
// TODO:tests.
func (receiver *Controller) CalculateLTV(ctx echo.Context) error {
var req swagger.CalculateLTVJSONBody
if err := ctx.Bind(&req); err != nil {
receiver.logger.Error("failed to bind request", zap.Error(err))
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to bind request: %s", err),
errors.ErrInvalidArgs,
))
}
if req.From > req.To && req.To != 0 {
receiver.logger.Error("From timestamp must be less than To timestamp unless To is 0")
return errors.HTTP(ctx, errors.New(
fmt.Errorf("From timestamp must be less than To timestamp unless To is 0"),
errors.ErrInvalidArgs,
))
}
ltv, err := receiver.historyService.CalculateCustomerLTV(ctx.Request().Context(), req.From, req.To)
if err != nil {
receiver.logger.Error("failed to calculate LTV", zap.Error(err))
return errors.HTTP(ctx, err)
}
response := struct {
LTV int64 `json:"LTV"`
}{
LTV: ltv,
}
return ctx.JSON(http.StatusOK, response)
}

@ -1,161 +0,0 @@
package wallet
import (
"fmt"
"log"
"net/http"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger"
"github.com/labstack/echo/v4"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/payment"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/wallet"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/echotools"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate"
)
type Deps struct {
Logger *zap.Logger
WalletService *wallet.Service
PaymentService *payment.Service
}
type Controller struct {
logger *zap.Logger
walletService *wallet.Service
paymentService *payment.Service
}
func New(deps Deps) *Controller {
if deps.Logger == nil {
log.Panicln("logger is nil on <New (wallet controller)>")
}
if deps.WalletService == nil {
log.Panicln("wallet service is nil on <New (wallet controller)>")
}
if deps.PaymentService == nil {
log.Panicln("payment service is nil on <New (wallet controller)>")
}
return &Controller{
logger: deps.Logger,
walletService: deps.WalletService,
paymentService: deps.PaymentService,
}
}
func (receiver *Controller) ChangeCurrency(ctx echo.Context) error {
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
if !ok {
receiver.logger.Error("failed to convert jwt payload to string on <ChangeCurrency> of <WallerController>")
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to convert jwt payload to string: %s", userID),
errors.ErrInvalidArgs,
))
}
request, bindErr := echotools.Bind[models.ChangeCurrency](ctx)
if bindErr != nil {
receiver.logger.Error("failed to bind body on <ChangeCurrency> of <WalletController>", zap.Error(bindErr))
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to parse body on <ChangeCurrency> of <WalletController>: %w", bindErr),
errors.ErrInvalidArgs,
))
}
if validate.IsStringEmpty(request.Currency) {
return errors.HTTP(ctx, errors.New(
fmt.Errorf("empty currency key on <ChangeCurrency> of <WalletController>: %w", errors.ErrInvalidArgs),
errors.ErrInvalidArgs,
))
}
account, err := receiver.walletService.ChangeCurrency(ctx.Request().Context(), userID, request.Currency)
if err != nil {
receiver.logger.Error("failed to put money on <ChangeCurrency> of <WalletController>", zap.Error(err))
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, account)
}
func (receiver *Controller) GetPaymentLink(ctx echo.Context) error {
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
if !ok {
receiver.logger.Error("failed to convert jwt payload to string on <GetPaymentLink> of <WallerController>")
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to convert jwt payload to string: %s", userID),
errors.ErrInvalidArgs,
))
}
request, bindErr := echotools.Bind[models.GetPaymentLinkBody](ctx)
if bindErr != nil {
receiver.logger.Error("failed to bind body on <GetPaymentLink> of <WalletController>", zap.Error(bindErr))
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to parse body on <GetPaymentLink> of <WalletController>: %w", bindErr),
errors.ErrInvalidArgs,
))
}
if validateErr := utils.ValidateGetPaymentLinkBody(request); validateErr != nil {
receiver.logger.Error("failed to validate body on <GetPaymentLink> of <WalletController>", zap.Error(validateErr))
return errors.HTTP(ctx, validateErr)
}
link, err := receiver.paymentService.GetPaymentLink(ctx.Request().Context(), &models.GetPaymentLinkRequest{
Body: request,
UserID: userID,
ClientIP: ctx.RealIP(),
})
if err != nil {
receiver.logger.Error("failed to get payment link on <GetPaymentLink> of <WalletController>", zap.Error(err))
return errors.HTTP(ctx, err)
}
return ctx.JSON(http.StatusOK, &models.GetPaymentLinkResponse{Link: link})
}
func (receiver *Controller) PostWalletRspay(ctx echo.Context) error {
userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
if !ok {
receiver.logger.Error("failed to convert jwt payload to string on <PostWalletRspay> of <WallerController>")
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to convert jwt payload to string: %s", userID),
errors.ErrInvalidArgs,
))
}
var req swagger.PostWalletRspayJSONBody
if err := ctx.Bind(&req); err != nil {
receiver.logger.Error("failed to bind request", zap.Error(err))
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to bind request: %s", err),
errors.ErrInvalidArgs,
))
}
token := ctx.Request().Header.Get("Authorization")
fmt.Println("HEADERS", ctx.Request().Header)
if err := receiver.walletService.PostWalletRspay(ctx.Request().Context(), token, userID, req); err != nil {
if err == errors.ErrNoAccess {
return errors.HTTP(ctx, err)
}
return errors.HTTP(ctx, errors.New(
fmt.Errorf("failed to process rspay: %w", err),
errors.ErrInternalError,
))
}
return ctx.NoContent(http.StatusOK)
}

@ -174,35 +174,6 @@ func (receiver *AccountRepository) Remove(ctx context.Context, id string) (*mode
return &account, nil
}
func (receiver *AccountRepository) Delete(ctx context.Context, id string) (*models.Account, errors.Error) {
account := models.Account{}
filter := bson.M{
fields.Account.UserID: id,
fields.Account.IsDeleted: false,
}
if err := receiver.mongoDB.FindOneAndDelete(ctx, filter).Decode(&account); err != nil {
receiver.logger.Error("failed delete account on <Delete> of <AccountRepository>",
zap.String("id", id),
zap.Error(err),
)
removeErr := errors.New(
fmt.Errorf("failed to remove account with <%s> on <Delete> of <AccountRepository>: %w", id, err),
errors.ErrInternalError,
)
if err == mongo.ErrNoDocuments {
removeErr.SetType(errors.ErrNotFound)
}
return nil, removeErr
}
return &account, nil
}
func (receiver *AccountRepository) CountAll(ctx context.Context) (int64, errors.Error) {
count, err := receiver.mongoDB.CountDocuments(ctx, bson.M{fields.Account.IsDeleted: false})
if err != nil {
@ -431,3 +402,114 @@ func (receiver *AccountRepository) UpdateName(ctx context.Context, userID string
return &account, nil
}
type QuizLogoStatDeps struct {
From *int
Limit *int
Page *int
To *int
}
type QuizLogoStats struct {
ID string `bson:"_id"`
Regs int `bson:"regs"`
Money int64 `bson:"money"`
Quizes []Quiz `bson:"quizes"`
}
type Quiz struct {
QuizID string `bson:"quiz"`
Regs int `bson:"regs"`
Money int64 `bson:"money"`
}
func (receiver *AccountRepository) QuizLogoStat(ctx context.Context, req QuizLogoStatDeps) ([]QuizLogoStats, error) {
var pipeline mongo.Pipeline
if req.From != nil && req.To != nil {
match := bson.D{
{"$match", bson.D{
{"createdAt", bson.D{{"$gte", time.Unix(int64(*req.From), 0)}}},
{"createdAt", bson.D{{"$lte", time.Unix(int64(*req.To), 0)}}},
{"from", bson.D{{"$exists", true}, {"$ne", ""}}},
{"partner", bson.D{{"$exists", true}, {"$ne", ""}}},
}},
}
pipeline = append(pipeline, match)
} else {
match := bson.D{
{"$match", bson.D{
{"from", bson.D{{"$exists", true}, {"$ne", ""}}},
{"partner", bson.D{{"$exists", true}, {"$ne", ""}}},
}},
}
pipeline = append(pipeline, match)
}
pipeline = append(pipeline, mongo.Pipeline{
{
{"$lookup", bson.D{
{"from", "histories"},
{"localField", "userId"},
{"foreignField", "userId"},
{"as", "history"},
}},
},
{
{"$unwind", bson.D{
{"path", "$history"},
{"preserveNullAndEmptyArrays", true},
}},
},
{
{"$match", bson.D{
{"$or", bson.A{
bson.D{{"history", bson.D{{"$exists", false}}}},
bson.D{{"history.key", "payment.succeeded"}},
}},
}},
},
{
{"$group", bson.D{
{"_id", "$userId"},
{"partner", bson.D{{"$first", "$partner"}}},
{"from", bson.D{{"$first", "$from"}}},
{"sum", bson.D{{"$sum", "$history.rawDetails.price"}}},
}},
},
{
{"$group", bson.D{
{"_id", "$from"},
{"partner", bson.D{{"$first", "$partner"}}},
{"regs", bson.D{{"$count", bson.D{}}}},
{"money", bson.D{{"$sum", "$sum"}}},
}},
},
{
{"$group", bson.D{
{"_id", "$partner"},
{"regs", bson.D{{"$sum", "$regs"}}},
{"money", bson.D{{"$sum", "$money"}}},
{"quizes", bson.D{{"$push", bson.D{
{"quiz", "$_id"},
{"regs", "$regs"},
{"money", "$money"},
}}}}},
},
},
}...)
var results []QuizLogoStats
cursor, err := receiver.mongoDB.Aggregate(ctx, pipeline)
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
if err := cursor.All(ctx, &results); err != nil {
return nil, err
}
return results, nil
}

@ -4,6 +4,8 @@ import (
"context"
"fmt"
"log"
codeword_rpc "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/codeword"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
"time"
"go.mongodb.org/mongo-driver/bson"
@ -345,3 +347,69 @@ func (receiver *HistoryRepository) CalculateCustomerLTV(ctx context.Context, fro
averageLTV := int64(results[0].AverageLTV)
return averageLTV, nil
}
func (receiver *HistoryRepository) GetHistoryByListUsers(ctx context.Context, accounts []models.Account) (map[string][]models.History, error) {
historyMap := make(map[string][]models.History)
var IDs []string
for _, account := range accounts {
IDs = append(IDs, account.UserID)
}
filter := bson.M{"userId": bson.M{"$in": IDs}}
cursor, err := receiver.mongoDB.Find(ctx, filter)
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
for cursor.Next(ctx) {
var history models.History
if err := cursor.Decode(&history); err != nil {
return nil, err
}
historyMap[history.UserID] = append(historyMap[history.UserID], history)
}
if err := cursor.Err(); err != nil {
return nil, err
}
return historyMap, nil
}
func (receiver *HistoryRepository) GetPayUsersPromoHistory(ctx context.Context, codewordData map[string][]*codeword_rpc.PromoActivationResp_UserTime, from, to int64) (map[string]int64, error) {
match := utils.MatchGen(codewordData, from, to)
pipeline := []bson.M{
match,
{
"$group": bson.M{
"_id": "$userId",
"priceSum": bson.M{"$sum": "$rawDetails.price"},
},
},
}
cursor, err := receiver.mongoDB.Aggregate(ctx, pipeline)
if err != nil {
return nil, err
}
defer cursor.Close(ctx)
userSum := make(map[string]int64)
for cursor.Next(ctx) {
var result struct {
UserID string `bson:"_id"`
PriceSum int64 `bson:"priceSum"`
}
if err := cursor.Decode(&result); err != nil {
return nil, err
}
userSum[result.UserID] = result.PriceSum
}
if err := cursor.Err(); err != nil {
return nil, err
}
return userSum, nil
}

@ -5,7 +5,9 @@ import (
"github.com/gofiber/fiber/v2"
"math"
"net/http"
"net/url"
"os"
codeword_rpc "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/codeword"
"sync"
"time"
@ -13,6 +15,7 @@ import (
"go.mongodb.org/mongo-driver/mongo"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/timestamppb"
qutils "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/broker/tariff"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/client"
@ -37,6 +40,7 @@ type API2 struct {
consumer *tariff.Consumer
clients clients
grpc models.ConfigurationGRPC
encrypt *qutils.Encrypt
}
type clients struct {
@ -48,11 +52,12 @@ type clients struct {
verify *client.VerificationClient
template *client.TemplateClient
mail *client.MailClient
codeword *client.CodewordClient
}
var _ ServerInterface = (*API2)(nil)
func NewAPI2(logger *zap.Logger, db *mongo.Database, config *models.Config, consumer *tariff.Consumer, producer *tariff.Producer) API2 {
func NewAPI2(logger *zap.Logger, db *mongo.Database, config *models.Config, consumer *tariff.Consumer, producer *tariff.Producer, encrypt *qutils.Encrypt) API2 {
return API2{
logger: logger,
history: repository.NewHistoryRepository2(logger, db.Collection("histories")),
@ -60,6 +65,7 @@ func NewAPI2(logger *zap.Logger, db *mongo.Database, config *models.Config, cons
account: repository.NewAccountRepository2(logger, db.Collection("accounts")),
consumer: consumer,
producer: producer,
encrypt: encrypt,
grpc: config.GRPC,
clients: clients{
auth: client.NewAuthClient(client.AuthClientDeps{Logger: logger, URLs: &config.Service.AuthMicroservice.URL}),
@ -81,6 +87,7 @@ func NewAPI2(logger *zap.Logger, db *mongo.Database, config *models.Config, cons
FiberClient: fiber.AcquireClient(),
MailAddress: config.Service.Mail.MailAddress,
}),
codeword: client.NewCodewordClient(client.CodewordClientDeps{Logger: logger, CodewordServiceHost: config.Service.CodewordMicroservice.HostGRPC}),
},
}
}
@ -181,6 +188,20 @@ func (api *API2) AddAccount(ctx echo.Context) error {
return api.noauth(ctx)
}
var quizFrom, quizUser string
cookie, er := ctx.Request().Cookie("quizFrom")
if er == nil {
quizFrom = cookie.Value
}
cookie, er = ctx.Request().Cookie("quizUser")
if er == nil {
quizUser, er = url.QueryUnescape(cookie.Value)
if er == nil {
quizUser, er = api.encrypt.DecryptStr([]byte(quizUser))
}
}
account, err := api.account.FindByUserID(ctx.Request().Context(), userID)
if err != nil && err.Type() != errors.ErrNotFound {
return api.errorOld(ctx, err)
@ -195,7 +216,8 @@ func (api *API2) AddAccount(ctx echo.Context) error {
return api.errorOld(ctx, err)
}
account, err = api.account.Insert(ctx.Request().Context(), &models.Account{UserID: user.ID, Wallet: models.Wallet{Currency: defaultCurrency}})
account, err = api.account.Insert(ctx.Request().Context(), &models.Account{
UserID: user.ID, Wallet: models.Wallet{Currency: defaultCurrency}, From: quizFrom, Partner: quizUser})
if err != nil {
return api.errorOld(ctx, err)
}
@ -340,7 +362,7 @@ func (api *API2) PayCart(ctx echo.Context) error {
UserInformation: &discount.UserInformation{
ID: account.UserID,
Type: string(account.Status),
PurchasesAmount: uint64(account.Wallet.PurchasesAmount),
PurchasesAmount: uint64(account.Wallet.Spent),
CartPurchasesAmount: tariffsAmount,
},
Products: transfer.TariffsToProductInformations(tariffs),
@ -365,6 +387,18 @@ func (api *API2) PayCart(ctx echo.Context) error {
return api.error(ctx, http.StatusPaymentRequired, "insufficient funds: %d", int64(discountResponse.Price)-account.Wallet.Money)
}
for _, applied := range discountResponse.AppliedDiscounts {
if applied.Condition.User != nil && *applied.Condition.User != "" {
_, err := api.clients.discount.DeleteDiscount(ctx.Request().Context(), &discount.GetDiscountByIDRequest{
ID: applied.ID,
})
if err != nil {
return api.error(ctx, http.StatusInternalServerError, "failed delete discount by id:%s", applied.ID)
}
}
}
// WithdrawAccountWalletMoney
request := models.WithdrawAccountWallet{
@ -415,10 +449,13 @@ func (api *API2) PayCart(ctx echo.Context) error {
}
if _, err := api.history.Insert(ctx.Request().Context(), &models.History{
Key: models.CustomerHistoryKeyPayCart,
UserID: account.UserID,
Comment: "Успешная оплата корзины",
RawDetails: tariffs,
Key: models.CustomerHistoryKeyPayCart,
UserID: account.UserID,
Comment: "Успешная оплата корзины",
RawDetails: models.RawDetails{
Tariffs: tariffs,
Price: int64(discountResponse.Price),
},
}); err != nil {
return api.errorOld(ctx, err)
}
@ -509,8 +546,17 @@ func (api *API2) UpdateCurrencies(ctx echo.Context) error {
// History
func (api *API2) GetHistory(ctx echo.Context, params GetHistoryParams) error {
var userID string
if params.AccountID != nil && *params.AccountID != "" {
userID = *params.AccountID
} else {
userID, _ = ctx.Get(models.AuthJWTDecodedUserIDKey).(string)
}
dto := &history.GetHistories{
Type: params.Type,
UserID: userID,
Type: params.Type,
Pagination: &models.Pagination{
Page: int64(*params.Page),
Limit: int64(*params.Limit),
@ -849,3 +895,93 @@ func (api *API2) PostWalletRspay(ctx echo.Context) error {
return ctx.NoContent(http.StatusOK)
}
type QuizLogoStat2 struct {
Count int64 `json:"count,omitempty"`
Items map[string]Item `json:"items,omitempty"`
}
type Item struct {
Money int64 `json:"money,omitempty"`
Quizes map[string][2]int `json:"quizes,omitempty"`
Regs int `json:"regs,omitempty"`
}
func (api *API2) QuizLogoStat(ctx echo.Context) error {
var req QuizLogoStatJSONRequestBody
if err := ctx.Bind(&req); err != nil {
api.logger.Error("failed to bind request", zap.Error(err))
return api.error(ctx, http.StatusBadRequest, "failed to bind request")
}
result, err := api.account.QuizLogoStat(ctx.Request().Context(), repository.QuizLogoStatDeps{
Page: req.Page,
Limit: req.Limit,
From: req.From,
To: req.To,
})
if err != nil {
return api.error(ctx, http.StatusInternalServerError, fmt.Sprint("failed getting quiz logo stat", err.Error()))
}
return ctx.JSON(http.StatusOK, result)
}
func (api *API2) PromocodeLTV(ctx echo.Context) error {
var req PromocodeLTVJSONRequestBody
if err := ctx.Bind(&req); err != nil {
api.logger.Error("failed to bind request", zap.Error(err))
return api.error(ctx, http.StatusBadRequest, "failed to bind request")
}
// получаем мапу вида [promoID] = []{userid,timeActivate}
// отдаются только первые использованые на аккаунте промокоды, соответсвенно подсчет идет сугубо по ним
// если в запросе время различается с временем активации - если меньше, то учитывается только после применения
// если больше, то учитывается только с начала переданного from
codewordData, err := api.clients.codeword.GetAllPromoActivations(ctx.Request().Context(), &codeword_rpc.Time{
To: int64(req.To),
From: int64(req.From),
})
if err != nil {
return api.error(ctx, http.StatusInternalServerError, fmt.Sprint("failed getting codeword data", err.Error()))
}
userSumMap, er := api.history.GetPayUsersPromoHistory(ctx.Request().Context(), codewordData, int64(req.From), int64(req.To))
if er != nil {
return api.error(ctx, http.StatusInternalServerError, fmt.Sprint("failed clculate promo users paid sum", er.Error()))
}
resp := make(map[string]struct {
Regs int
Money int64
})
for promoID, data := range codewordData {
fmt.Println("PROTOMOTO", promoID,data)
for _, value := range data {
paids, ok := userSumMap[value.UserID]
if !ok {
paids = 0
}
fmt.Println("PROTOMOTO1", paids, value)
if value.Time >= int64(req.From) && value.Time <= int64(req.To) {
if _, ok := resp[promoID]; !ok {
resp[promoID] = struct {
Regs int
Money int64
}{Regs: 1, Money: paids}
continue
}
current := resp[promoID]
current.Regs += 1
current.Money += paids
resp[promoID] = current
}
}
}
return ctx.JSON(http.StatusOK, resp)
}

@ -65,6 +65,12 @@ type ServerInterface interface {
// Расчет среднего времени жизни платящего клиента (LTV)
// (POST /history/ltv)
CalculateLTV(ctx echo.Context) error
// статистика по промокодам в разрезе регистраций и оплат
// (POST /promocode/ltv)
PromocodeLTV(ctx echo.Context) error
// статистика пользователей, перешедших по логотипу в опросах
// (POST /quizlogo/stat)
QuizLogoStat(ctx echo.Context) error
// Получение недавних тарифов
// (GET /recent)
GetRecentTariffs(ctx echo.Context) error
@ -333,6 +339,28 @@ func (w *ServerInterfaceWrapper) CalculateLTV(ctx echo.Context) error {
return err
}
// PromocodeLTV converts echo context to params.
func (w *ServerInterfaceWrapper) PromocodeLTV(ctx echo.Context) error {
var err error
ctx.Set(BearerScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.PromocodeLTV(ctx)
return err
}
// QuizLogoStat converts echo context to params.
func (w *ServerInterfaceWrapper) QuizLogoStat(ctx echo.Context) error {
var err error
ctx.Set(BearerScopes, []string{})
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.QuizLogoStat(ctx)
return err
}
// GetRecentTariffs converts echo context to params.
func (w *ServerInterfaceWrapper) GetRecentTariffs(ctx echo.Context) error {
var err error
@ -431,6 +459,8 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
router.PUT(baseURL+"/currencies", wrapper.UpdateCurrencies)
router.GET(baseURL+"/history", wrapper.GetHistory)
router.POST(baseURL+"/history/ltv", wrapper.CalculateLTV)
router.POST(baseURL+"/promocode/ltv", wrapper.PromocodeLTV)
router.POST(baseURL+"/quizlogo/stat", wrapper.QuizLogoStat)
router.GET(baseURL+"/recent", wrapper.GetRecentTariffs)
router.POST(baseURL+"/sendReport", wrapper.SendReport)
router.PATCH(baseURL+"/wallet", wrapper.ChangeCurrency)
@ -442,83 +472,90 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
"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",
"sViZVwAA",
"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",
}
// GetSwagger returns the content of the embedded swagger specification file

@ -1,178 +0,0 @@
package swagger
import (
"log"
"github.com/labstack/echo/v4"
)
type accountController interface {
RemoveAccount(ctx echo.Context) error
GetAccount(ctx echo.Context) error
CreateAccount(ctx echo.Context) error
RemoveDirectAccount(ctx echo.Context, userID string) error
GetDirectAccount(ctx echo.Context, userID string) error
GetAccounts(echo.Context, PaginationAccountsParams) error
SetVerificationStatus(ctx echo.Context, userID string) error
UpdateAccountName(ctx echo.Context) error
}
type currencyController interface {
GetCurrencies(ctx echo.Context) error
PutCurrencies(ctx echo.Context) error
}
type cartController interface {
Remove(echo.Context, RemoveFromCartParams) error
Add(echo.Context, Add2cartParams) error
Pay(echo.Context) error
}
type walletController interface {
ChangeCurrency(ctx echo.Context) error
GetPaymentLink(ctx echo.Context) error
PostWalletRspay(ctx echo.Context) error
}
type historyController interface {
GetHistoryList(ctx echo.Context, params GetHistoryParams) error
GetRecentTariffs(ctx echo.Context) error
SendReport(ctx echo.Context) error
CalculateLTV(ctx echo.Context) error
}
type Deps struct {
AccountController accountController
CurrencyController currencyController
CartController cartController
WalletController walletController
HistoryController historyController
}
type API struct {
accountController accountController
currencyController currencyController
cartController cartController
walletController walletController
historyController historyController
}
func New(deps Deps) *API {
if deps.AccountController == nil {
log.Panicln("AccountController is nil on <New (API)>")
}
if deps.CurrencyController == nil {
log.Panicln("currencyController is nil on <New (API)>")
}
if deps.CartController == nil {
log.Panicln("cartController is nil on <New (API)>")
}
if deps.WalletController == nil {
log.Panicln("walletController is nil on <New (API)>")
}
if deps.HistoryController == nil {
log.Panicln("historyController is nil on <New (API)>")
}
return &API{
accountController: deps.AccountController,
currencyController: deps.CurrencyController,
cartController: deps.CartController,
walletController: deps.WalletController,
historyController: deps.HistoryController,
}
}
// Account
func (receiver *API) DeleteAccount(ctx echo.Context) error {
return receiver.accountController.RemoveAccount(ctx)
}
func (receiver *API) ChangeAccount(ctx echo.Context) error {
return receiver.accountController.UpdateAccountName(ctx)
}
func (receiver *API) SetAccountVerificationStatus(ctx echo.Context, userID string) error {
return receiver.accountController.SetVerificationStatus(ctx, userID)
}
func (receiver *API) GetAccount(ctx echo.Context) error {
return receiver.accountController.GetAccount(ctx)
}
func (receiver *API) AddAccount(ctx echo.Context) error {
return receiver.accountController.CreateAccount(ctx)
}
func (receiver *API) DeleteDirectAccount(ctx echo.Context, userID string) error {
return receiver.accountController.RemoveDirectAccount(ctx, userID)
}
func (receiver *API) GetDirectAccount(ctx echo.Context, userID string) error {
return receiver.accountController.GetDirectAccount(ctx, userID)
}
func (receiver *API) PaginationAccounts(ctx echo.Context, params PaginationAccountsParams) error {
return receiver.accountController.GetAccounts(ctx, params)
}
// Cart
func (receiver *API) RemoveFromCart(ctx echo.Context, params RemoveFromCartParams) error {
return receiver.cartController.Remove(ctx, params)
}
func (receiver *API) Add2cart(ctx echo.Context, params Add2cartParams) error {
return receiver.cartController.Add(ctx, params)
}
func (receiver *API) PayCart(ctx echo.Context) error {
return receiver.cartController.Pay(ctx)
}
// Currency
func (receiver *API) GetCurrencies(ctx echo.Context) error {
return receiver.currencyController.GetCurrencies(ctx)
}
func (receiver *API) UpdateCurrencies(ctx echo.Context) error {
return receiver.currencyController.PutCurrencies(ctx)
}
// History
func (receiver *API) GetHistory(ctx echo.Context, params GetHistoryParams) error {
return receiver.historyController.GetHistoryList(ctx, params)
}
func (receiver *API) GetRecentTariffs(ctx echo.Context) error {
return receiver.historyController.GetRecentTariffs(ctx)
}
func (receiver *API) SendReport(ctx echo.Context) error {
return receiver.historyController.SendReport(ctx)
}
func (receiver *API) CalculateLTV(ctx echo.Context) error {
return receiver.historyController.CalculateLTV(ctx)
}
// Wallet
func (receiver *API) RequestMoney(ctx echo.Context) error {
return receiver.walletController.GetPaymentLink(ctx)
}
func (receiver *API) ChangeCurrency(ctx echo.Context) error {
return receiver.walletController.ChangeCurrency(ctx)
}
func (receiver *API) PostWalletRspay(ctx echo.Context) error {
return receiver.walletController.PostWalletRspay(ctx)
}

@ -100,6 +100,40 @@ type Name struct {
// PaymentType defines model for PaymentType.
type PaymentType string
// PromoLtvStat defines model for PromoLtvStat.
type PromoLtvStat struct {
// Stats мапа ключ id промо, знчение количество регистраций и количество денег
Stats *map[string]struct {
// Money количество денег
Money *int `json:"money,omitempty"`
// Regs количество регистраций
Regs *int `json:"regs,omitempty"`
} `json:"stats,omitempty"`
}
// QuizLogoStat defines model for QuizLogoStat.
type QuizLogoStat = []struct {
// Id user id
Id *string `json:"id,omitempty"`
// Money Количество денег
Money *int `json:"money,omitempty"`
Quizes *[]struct {
// Money Количество денег
Money *int `json:"money,omitempty"`
// Quiz qid quiz
Quiz *string `json:"quiz,omitempty"`
// Regs Количество регистраций
Regs *int `json:"regs,omitempty"`
} `json:"quizes,omitempty"`
// Regs Количество регистраций
Regs *int `json:"regs,omitempty"`
}
// TariffID defines model for TariffID.
type TariffID struct {
ID *string `json:"ID,omitempty"`
@ -174,6 +208,30 @@ type CalculateLTVJSONBody struct {
To int64 `json:"to"`
}
// PromocodeLTVJSONBody defines parameters for PromocodeLTV.
type PromocodeLTVJSONBody struct {
// From таймштамп времени, после которого выбирать статистику
From int `json:"from"`
// To таймштамп времени, до которого выбирать статистику
To int `json:"to"`
}
// QuizLogoStatJSONBody defines parameters for QuizLogoStat.
type QuizLogoStatJSONBody struct {
// From таймштамп времени, после которого выбирать статистику. если 0 или не передано - этого ограничения нет. нижняя граница времени
From *int `json:"from,omitempty"`
// Limit лимит выборки
Limit *int `json:"limit,omitempty"`
// Page страница выборки
Page *int `json:"page,omitempty"`
// To таймштамп времени, до которого выбирать статистику. если 0 или не передано - этого ограничения нет. верхняя граница времени
To *int `json:"to,omitempty"`
}
// GetRecentTariffsJSONBody defines parameters for GetRecentTariffs.
type GetRecentTariffsJSONBody struct {
Id string `json:"id"`
@ -219,6 +277,12 @@ type UpdateCurrenciesJSONRequestBody = UpdateCurrenciesJSONBody
// CalculateLTVJSONRequestBody defines body for CalculateLTV for application/json ContentType.
type CalculateLTVJSONRequestBody CalculateLTVJSONBody
// PromocodeLTVJSONRequestBody defines body for PromocodeLTV for application/json ContentType.
type PromocodeLTVJSONRequestBody PromocodeLTVJSONBody
// QuizLogoStatJSONRequestBody defines body for QuizLogoStat for application/json ContentType.
type QuizLogoStatJSONRequestBody QuizLogoStatJSONBody
// GetRecentTariffsJSONRequestBody defines body for GetRecentTariffs for application/json ContentType.
type GetRecentTariffsJSONRequestBody GetRecentTariffsJSONBody

@ -27,12 +27,10 @@ func (api *API2) GetPaymentLink(ctx context.Context, request *models.GetPaymentL
return api.GetPaymentLinkYooMoney(ctx, request)
case models.PaymentTypeSberPay:
return api.GetPaymentLinkSberPay(ctx, request)
case models.PaymentTypeSberB2B:
return api.GetPaymentLinkSBP(ctx, request)
case models.PaymentTypeSBP:
return api.GetPaymentLinkSBP(ctx, request)
case models.PaymentTypeTinkoff:
return api.GetPaymentLinkTinkoff(ctx, request)
case models.PaymentTypeSBP:
return api.GetPaymentLinkSBP(ctx, request)
}
return "", errors.NewWithMessage("invalid payment method type", errors.ErrInvalidArgs)

@ -13,6 +13,8 @@ type Account struct {
CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"`
DeletedAt *time.Time `json:"deletedAt,omitempty" bson:"deletedAt,omitempty"`
From string `json:"from" bson:"from"`
Partner string `json:"partner" bson:"partner"`
}
func (receiver *Account) Sanitize() *Account {
@ -25,6 +27,9 @@ func (receiver *Account) Sanitize() *Account {
receiver.Status = DefaultAccountStatus
receiver.CreatedAt = now
receiver.UpdatedAt = now
// закоментил, потому что при инзерте они всегда будут ""
//receiver.From = ""
//receiver.Partner = ""
receiver.DeletedAt = nil
receiver.Deleted = false

@ -30,12 +30,15 @@ type ServiceConfiguration struct {
HubadminMicroservice HubadminMicroserviceConfiguration
CurrencyMicroservice CurrencyMicroserviceConfiguration
DiscountMicroservice DiscountMicroserviceConfiguration
CodewordMicroservice CodewordMicroserviceConfiguration
PaymentMicroservice PaymentMicroserviceConfiguration
VerificationMicroservice VerificationMicroserviceConfiguration
TemplategenMicroserviceURL TemplategenMicroserviceConfiguration
JWT JWTConfiguration
Kafka KafkaConfiguration
Mail MailConfiguration
PubKey string `env:"PUBLIC_KEY"`
PrivKey string `env:"PRIVATE_KEY"`
}
type KafkaConfiguration struct {
@ -76,6 +79,10 @@ type TemplategenMicroserviceConfiguration struct {
URL TemplategenMicroserviceURL
}
type CodewordMicroserviceConfiguration struct {
HostGRPC string `env:"CODEWORD_MICROSERVICE_GRPC_HOST,required"`
}
type PaymentMicroserviceConfiguration struct {
HostGRPC string `env:"PAYMENT_MICROSERVICE_GRPC_HOST,required"`
}

@ -7,7 +7,7 @@ type History struct {
UserID string `json:"userId" bson:"userId"`
Comment string `json:"comment" bson:"comment"`
Key string `json:"key" bson:"key"`
RawDetails any `json:"rawDetails" bson:"rawDetails"`
RawDetails RawDetails `json:"rawDetails" bson:"rawDetails"`
Deleted bool `json:"isDeleted" bson:"isDeleted"`
CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
UpdatedAt time.Time `json:"updatedAt" bson:"updatedAt"`
@ -33,6 +33,7 @@ type ReportHistory struct {
type RawDetails struct {
Tariffs []Tariff `json:"tariffs" bson:"tariffs"`
Price int64 `json:"price" bson:"price"`
Comment string `json:"comment" bson:"comment"`
}
func (receiver *History) Sanitize() *History {

@ -44,5 +44,6 @@ type PaymentEvent struct {
PaymentID string
UserID string
Currency string
Type string
Amount int64
}

@ -0,0 +1,305 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0-devel
// protoc v3.14.0
// source: promo.proto
package codeword_rpc
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Time struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
From int64 `protobuf:"varint,1,opt,name=from,proto3" json:"from,omitempty"`
To int64 `protobuf:"varint,2,opt,name=to,proto3" json:"to,omitempty"`
}
func (x *Time) Reset() {
*x = Time{}
if protoimpl.UnsafeEnabled {
mi := &file_promo_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Time) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Time) ProtoMessage() {}
func (x *Time) ProtoReflect() protoreflect.Message {
mi := &file_promo_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Time.ProtoReflect.Descriptor instead.
func (*Time) Descriptor() ([]byte, []int) {
return file_promo_proto_rawDescGZIP(), []int{0}
}
func (x *Time) GetFrom() int64 {
if x != nil {
return x.From
}
return 0
}
func (x *Time) GetTo() int64 {
if x != nil {
return x.To
}
return 0
}
type PromoActivationResp struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
Users []*PromoActivationResp_UserTime `protobuf:"bytes,2,rep,name=Users,proto3" json:"Users,omitempty"`
}
func (x *PromoActivationResp) Reset() {
*x = PromoActivationResp{}
if protoimpl.UnsafeEnabled {
mi := &file_promo_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PromoActivationResp) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PromoActivationResp) ProtoMessage() {}
func (x *PromoActivationResp) ProtoReflect() protoreflect.Message {
mi := &file_promo_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PromoActivationResp.ProtoReflect.Descriptor instead.
func (*PromoActivationResp) Descriptor() ([]byte, []int) {
return file_promo_proto_rawDescGZIP(), []int{1}
}
func (x *PromoActivationResp) GetID() string {
if x != nil {
return x.ID
}
return ""
}
func (x *PromoActivationResp) GetUsers() []*PromoActivationResp_UserTime {
if x != nil {
return x.Users
}
return nil
}
type PromoActivationResp_UserTime struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
UserID string `protobuf:"bytes,1,opt,name=UserID,proto3" json:"UserID,omitempty"`
Time int64 `protobuf:"varint,2,opt,name=Time,proto3" json:"Time,omitempty"`
}
func (x *PromoActivationResp_UserTime) Reset() {
*x = PromoActivationResp_UserTime{}
if protoimpl.UnsafeEnabled {
mi := &file_promo_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PromoActivationResp_UserTime) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PromoActivationResp_UserTime) ProtoMessage() {}
func (x *PromoActivationResp_UserTime) ProtoReflect() protoreflect.Message {
mi := &file_promo_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PromoActivationResp_UserTime.ProtoReflect.Descriptor instead.
func (*PromoActivationResp_UserTime) Descriptor() ([]byte, []int) {
return file_promo_proto_rawDescGZIP(), []int{1, 0}
}
func (x *PromoActivationResp_UserTime) GetUserID() string {
if x != nil {
return x.UserID
}
return ""
}
func (x *PromoActivationResp_UserTime) GetTime() int64 {
if x != nil {
return x.Time
}
return 0
}
var File_promo_proto protoreflect.FileDescriptor
var file_promo_proto_rawDesc = []byte{
0x0a, 0x0b, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x63,
0x6f, 0x64, 0x65, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x2a, 0x0a, 0x04, 0x54, 0x69, 0x6d, 0x65, 0x12,
0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x66,
0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52,
0x02, 0x74, 0x6f, 0x22, 0x9b, 0x01, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x41, 0x63, 0x74,
0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x49,
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x12, 0x3c, 0x0a, 0x05, 0x55,
0x73, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x6f, 0x64,
0x65, 0x77, 0x6f, 0x72, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x41, 0x63, 0x74, 0x69, 0x76,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x54, 0x69,
0x6d, 0x65, 0x52, 0x05, 0x55, 0x73, 0x65, 0x72, 0x73, 0x1a, 0x36, 0x0a, 0x08, 0x55, 0x73, 0x65,
0x72, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x12, 0x0a,
0x04, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x54, 0x69, 0x6d,
0x65, 0x32, 0x5d, 0x0a, 0x10, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x49, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x50,
0x72, 0x6f, 0x6d, 0x6f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12,
0x0e, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x77, 0x6f, 0x72, 0x64, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x1a,
0x1d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x77, 0x6f, 0x72, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f,
0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x30, 0x01,
0x42, 0x10, 0x5a, 0x0e, 0x2e, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x72,
0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_promo_proto_rawDescOnce sync.Once
file_promo_proto_rawDescData = file_promo_proto_rawDesc
)
func file_promo_proto_rawDescGZIP() []byte {
file_promo_proto_rawDescOnce.Do(func() {
file_promo_proto_rawDescData = protoimpl.X.CompressGZIP(file_promo_proto_rawDescData)
})
return file_promo_proto_rawDescData
}
var file_promo_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_promo_proto_goTypes = []interface{}{
(*Time)(nil), // 0: codeword.Time
(*PromoActivationResp)(nil), // 1: codeword.PromoActivationResp
(*PromoActivationResp_UserTime)(nil), // 2: codeword.PromoActivationResp.UserTime
}
var file_promo_proto_depIdxs = []int32{
2, // 0: codeword.PromoActivationResp.Users:type_name -> codeword.PromoActivationResp.UserTime
0, // 1: codeword.PromoCodeService.GetAllPromoActivations:input_type -> codeword.Time
1, // 2: codeword.PromoCodeService.GetAllPromoActivations:output_type -> codeword.PromoActivationResp
2, // [2:3] is the sub-list for method output_type
1, // [1:2] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_promo_proto_init() }
func file_promo_proto_init() {
if File_promo_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_promo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Time); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_promo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PromoActivationResp); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_promo_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PromoActivationResp_UserTime); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_promo_proto_rawDesc,
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_promo_proto_goTypes,
DependencyIndexes: file_promo_proto_depIdxs,
MessageInfos: file_promo_proto_msgTypes,
}.Build()
File_promo_proto = out.File
file_promo_proto_rawDesc = nil
file_promo_proto_goTypes = nil
file_promo_proto_depIdxs = nil
}

@ -0,0 +1,128 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
package codeword_rpc
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// PromoCodeServiceClient is the client API for PromoCodeService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type PromoCodeServiceClient interface {
GetAllPromoActivations(ctx context.Context, in *Time, opts ...grpc.CallOption) (PromoCodeService_GetAllPromoActivationsClient, error)
}
type promoCodeServiceClient struct {
cc grpc.ClientConnInterface
}
func NewPromoCodeServiceClient(cc grpc.ClientConnInterface) PromoCodeServiceClient {
return &promoCodeServiceClient{cc}
}
func (c *promoCodeServiceClient) GetAllPromoActivations(ctx context.Context, in *Time, opts ...grpc.CallOption) (PromoCodeService_GetAllPromoActivationsClient, error) {
stream, err := c.cc.NewStream(ctx, &PromoCodeService_ServiceDesc.Streams[0], "/codeword.PromoCodeService/GetAllPromoActivations", opts...)
if err != nil {
return nil, err
}
x := &promoCodeServiceGetAllPromoActivationsClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type PromoCodeService_GetAllPromoActivationsClient interface {
Recv() (*PromoActivationResp, error)
grpc.ClientStream
}
type promoCodeServiceGetAllPromoActivationsClient struct {
grpc.ClientStream
}
func (x *promoCodeServiceGetAllPromoActivationsClient) Recv() (*PromoActivationResp, error) {
m := new(PromoActivationResp)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// PromoCodeServiceServer is the server API for PromoCodeService service.
// All implementations must embed UnimplementedPromoCodeServiceServer
// for forward compatibility
type PromoCodeServiceServer interface {
GetAllPromoActivations(*Time, PromoCodeService_GetAllPromoActivationsServer) error
mustEmbedUnimplementedPromoCodeServiceServer()
}
// UnimplementedPromoCodeServiceServer must be embedded to have forward compatible implementations.
type UnimplementedPromoCodeServiceServer struct {
}
func (UnimplementedPromoCodeServiceServer) GetAllPromoActivations(*Time, PromoCodeService_GetAllPromoActivationsServer) error {
return status.Errorf(codes.Unimplemented, "method GetAllPromoActivations not implemented")
}
func (UnimplementedPromoCodeServiceServer) mustEmbedUnimplementedPromoCodeServiceServer() {}
// UnsafePromoCodeServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to PromoCodeServiceServer will
// result in compilation errors.
type UnsafePromoCodeServiceServer interface {
mustEmbedUnimplementedPromoCodeServiceServer()
}
func RegisterPromoCodeServiceServer(s grpc.ServiceRegistrar, srv PromoCodeServiceServer) {
s.RegisterService(&PromoCodeService_ServiceDesc, srv)
}
func _PromoCodeService_GetAllPromoActivations_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(Time)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(PromoCodeServiceServer).GetAllPromoActivations(m, &promoCodeServiceGetAllPromoActivationsServer{stream})
}
type PromoCodeService_GetAllPromoActivationsServer interface {
Send(*PromoActivationResp) error
grpc.ServerStream
}
type promoCodeServiceGetAllPromoActivationsServer struct {
grpc.ServerStream
}
func (x *promoCodeServiceGetAllPromoActivationsServer) Send(m *PromoActivationResp) error {
return x.ServerStream.SendMsg(m)
}
// PromoCodeService_ServiceDesc is the grpc.ServiceDesc for PromoCodeService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var PromoCodeService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "codeword.PromoCodeService",
HandlerType: (*PromoCodeServiceServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "GetAllPromoActivations",
Handler: _PromoCodeService_GetAllPromoActivations_Handler,
ServerStreams: true,
},
},
Metadata: "promo.proto",
}

@ -0,0 +1,306 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.33.0
// protoc (unknown)
// source: codeword/promo.proto
package codeword_rpc
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Time struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
From int64 `protobuf:"varint,1,opt,name=from,proto3" json:"from,omitempty"`
To int64 `protobuf:"varint,2,opt,name=to,proto3" json:"to,omitempty"`
}
func (x *Time) Reset() {
*x = Time{}
if protoimpl.UnsafeEnabled {
mi := &file_codeword_promo_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Time) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Time) ProtoMessage() {}
func (x *Time) ProtoReflect() protoreflect.Message {
mi := &file_codeword_promo_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Time.ProtoReflect.Descriptor instead.
func (*Time) Descriptor() ([]byte, []int) {
return file_codeword_promo_proto_rawDescGZIP(), []int{0}
}
func (x *Time) GetFrom() int64 {
if x != nil {
return x.From
}
return 0
}
func (x *Time) GetTo() int64 {
if x != nil {
return x.To
}
return 0
}
type PromoActivationResp struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
Users []*PromoActivationResp_UserTime `protobuf:"bytes,2,rep,name=Users,proto3" json:"Users,omitempty"`
}
func (x *PromoActivationResp) Reset() {
*x = PromoActivationResp{}
if protoimpl.UnsafeEnabled {
mi := &file_codeword_promo_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PromoActivationResp) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PromoActivationResp) ProtoMessage() {}
func (x *PromoActivationResp) ProtoReflect() protoreflect.Message {
mi := &file_codeword_promo_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PromoActivationResp.ProtoReflect.Descriptor instead.
func (*PromoActivationResp) Descriptor() ([]byte, []int) {
return file_codeword_promo_proto_rawDescGZIP(), []int{1}
}
func (x *PromoActivationResp) GetID() string {
if x != nil {
return x.ID
}
return ""
}
func (x *PromoActivationResp) GetUsers() []*PromoActivationResp_UserTime {
if x != nil {
return x.Users
}
return nil
}
type PromoActivationResp_UserTime struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
UserID string `protobuf:"bytes,1,opt,name=UserID,proto3" json:"UserID,omitempty"`
Time int64 `protobuf:"varint,2,opt,name=Time,proto3" json:"Time,omitempty"`
}
func (x *PromoActivationResp_UserTime) Reset() {
*x = PromoActivationResp_UserTime{}
if protoimpl.UnsafeEnabled {
mi := &file_codeword_promo_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PromoActivationResp_UserTime) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PromoActivationResp_UserTime) ProtoMessage() {}
func (x *PromoActivationResp_UserTime) ProtoReflect() protoreflect.Message {
mi := &file_codeword_promo_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PromoActivationResp_UserTime.ProtoReflect.Descriptor instead.
func (*PromoActivationResp_UserTime) Descriptor() ([]byte, []int) {
return file_codeword_promo_proto_rawDescGZIP(), []int{1, 0}
}
func (x *PromoActivationResp_UserTime) GetUserID() string {
if x != nil {
return x.UserID
}
return ""
}
func (x *PromoActivationResp_UserTime) GetTime() int64 {
if x != nil {
return x.Time
}
return 0
}
var File_codeword_promo_proto protoreflect.FileDescriptor
var file_codeword_promo_proto_rawDesc = []byte{
0x0a, 0x14, 0x63, 0x6f, 0x64, 0x65, 0x77, 0x6f, 0x72, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x6d, 0x6f,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x77, 0x6f, 0x72, 0x64,
0x22, 0x2a, 0x0a, 0x04, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d,
0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02,
0x74, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x74, 0x6f, 0x22, 0x9b, 0x01, 0x0a,
0x13, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x52, 0x65, 0x73, 0x70, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x02, 0x49, 0x44, 0x12, 0x3c, 0x0a, 0x05, 0x55, 0x73, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x77, 0x6f, 0x72, 0x64, 0x2e, 0x50,
0x72, 0x6f, 0x6d, 0x6f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65,
0x73, 0x70, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x05, 0x55, 0x73, 0x65,
0x72, 0x73, 0x1a, 0x36, 0x0a, 0x08, 0x55, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16,
0x0a, 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x55, 0x73, 0x65, 0x72, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x02,
0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x54, 0x69, 0x6d, 0x65, 0x32, 0x5d, 0x0a, 0x10, 0x50, 0x72,
0x6f, 0x6d, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x49,
0x0a, 0x16, 0x47, 0x65, 0x74, 0x41, 0x6c, 0x6c, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x41, 0x63, 0x74,
0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x0e, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x77,
0x6f, 0x72, 0x64, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x1a, 0x1d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x77,
0x6f, 0x72, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x30, 0x01, 0x42, 0x10, 0x5a, 0x0e, 0x2e, 0x2f, 0x63,
0x6f, 0x64, 0x65, 0x77, 0x6f, 0x72, 0x64, 0x5f, 0x72, 0x70, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x33,
}
var (
file_codeword_promo_proto_rawDescOnce sync.Once
file_codeword_promo_proto_rawDescData = file_codeword_promo_proto_rawDesc
)
func file_codeword_promo_proto_rawDescGZIP() []byte {
file_codeword_promo_proto_rawDescOnce.Do(func() {
file_codeword_promo_proto_rawDescData = protoimpl.X.CompressGZIP(file_codeword_promo_proto_rawDescData)
})
return file_codeword_promo_proto_rawDescData
}
var file_codeword_promo_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_codeword_promo_proto_goTypes = []interface{}{
(*Time)(nil), // 0: codeword.Time
(*PromoActivationResp)(nil), // 1: codeword.PromoActivationResp
(*PromoActivationResp_UserTime)(nil), // 2: codeword.PromoActivationResp.UserTime
}
var file_codeword_promo_proto_depIdxs = []int32{
2, // 0: codeword.PromoActivationResp.Users:type_name -> codeword.PromoActivationResp.UserTime
0, // 1: codeword.PromoCodeService.GetAllPromoActivations:input_type -> codeword.Time
1, // 2: codeword.PromoCodeService.GetAllPromoActivations:output_type -> codeword.PromoActivationResp
2, // [2:3] is the sub-list for method output_type
1, // [1:2] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_codeword_promo_proto_init() }
func file_codeword_promo_proto_init() {
if File_codeword_promo_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_codeword_promo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Time); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_codeword_promo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PromoActivationResp); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_codeword_promo_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PromoActivationResp_UserTime); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_codeword_promo_proto_rawDesc,
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_codeword_promo_proto_goTypes,
DependencyIndexes: file_codeword_promo_proto_depIdxs,
MessageInfos: file_codeword_promo_proto_msgTypes,
}.Build()
File_codeword_promo_proto = out.File
file_codeword_promo_proto_rawDesc = nil
file_codeword_promo_proto_goTypes = nil
file_codeword_promo_proto_depIdxs = nil
}

@ -0,0 +1,134 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.3.0
// - protoc (unknown)
// source: codeword/promo.proto
package codeword_rpc
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
const (
PromoCodeService_GetAllPromoActivations_FullMethodName = "/codeword.PromoCodeService/GetAllPromoActivations"
)
// PromoCodeServiceClient is the client API for PromoCodeService service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type PromoCodeServiceClient interface {
GetAllPromoActivations(ctx context.Context, in *Time, opts ...grpc.CallOption) (PromoCodeService_GetAllPromoActivationsClient, error)
}
type promoCodeServiceClient struct {
cc grpc.ClientConnInterface
}
func NewPromoCodeServiceClient(cc grpc.ClientConnInterface) PromoCodeServiceClient {
return &promoCodeServiceClient{cc}
}
func (c *promoCodeServiceClient) GetAllPromoActivations(ctx context.Context, in *Time, opts ...grpc.CallOption) (PromoCodeService_GetAllPromoActivationsClient, error) {
stream, err := c.cc.NewStream(ctx, &PromoCodeService_ServiceDesc.Streams[0], PromoCodeService_GetAllPromoActivations_FullMethodName, opts...)
if err != nil {
return nil, err
}
x := &promoCodeServiceGetAllPromoActivationsClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type PromoCodeService_GetAllPromoActivationsClient interface {
Recv() (*PromoActivationResp, error)
grpc.ClientStream
}
type promoCodeServiceGetAllPromoActivationsClient struct {
grpc.ClientStream
}
func (x *promoCodeServiceGetAllPromoActivationsClient) Recv() (*PromoActivationResp, error) {
m := new(PromoActivationResp)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// PromoCodeServiceServer is the server API for PromoCodeService service.
// All implementations should embed UnimplementedPromoCodeServiceServer
// for forward compatibility
type PromoCodeServiceServer interface {
GetAllPromoActivations(*Time, PromoCodeService_GetAllPromoActivationsServer) error
}
// UnimplementedPromoCodeServiceServer should be embedded to have forward compatible implementations.
type UnimplementedPromoCodeServiceServer struct {
}
func (UnimplementedPromoCodeServiceServer) GetAllPromoActivations(*Time, PromoCodeService_GetAllPromoActivationsServer) error {
return status.Errorf(codes.Unimplemented, "method GetAllPromoActivations not implemented")
}
// UnsafePromoCodeServiceServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to PromoCodeServiceServer will
// result in compilation errors.
type UnsafePromoCodeServiceServer interface {
mustEmbedUnimplementedPromoCodeServiceServer()
}
func RegisterPromoCodeServiceServer(s grpc.ServiceRegistrar, srv PromoCodeServiceServer) {
s.RegisterService(&PromoCodeService_ServiceDesc, srv)
}
func _PromoCodeService_GetAllPromoActivations_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(Time)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(PromoCodeServiceServer).GetAllPromoActivations(m, &promoCodeServiceGetAllPromoActivationsServer{stream})
}
type PromoCodeService_GetAllPromoActivationsServer interface {
Send(*PromoActivationResp) error
grpc.ServerStream
}
type promoCodeServiceGetAllPromoActivationsServer struct {
grpc.ServerStream
}
func (x *promoCodeServiceGetAllPromoActivationsServer) Send(m *PromoActivationResp) error {
return x.ServerStream.SendMsg(m)
}
// PromoCodeService_ServiceDesc is the grpc.ServiceDesc for PromoCodeService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var PromoCodeService_ServiceDesc = grpc.ServiceDesc{
ServiceName: "codeword.PromoCodeService",
HandlerType: (*PromoCodeServiceServer)(nil),
Methods: []grpc.MethodDesc{},
Streams: []grpc.StreamDesc{
{
StreamName: "GetAllPromoActivations",
Handler: _PromoCodeService_GetAllPromoActivations_Handler,
ServerStreams: true,
},
},
Metadata: "codeword/promo.proto",
}

@ -1,239 +0,0 @@
package account
import (
"context"
"fmt"
"log"
"math"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
)
type accountRepository interface {
FindByUserID(ctx context.Context, id string) (*models.Account, errors.Error)
FindMany(ctx context.Context, page, limit int64) ([]models.Account, errors.Error)
Insert(ctx context.Context, account *models.Account) (*models.Account, errors.Error)
Remove(ctx context.Context, id string) (*models.Account, errors.Error)
Delete(ctx context.Context, id string) (*models.Account, errors.Error)
CountAll(ctx context.Context) (int64, errors.Error)
SetStatus(ctx context.Context, userID string, status models.AccountStatus) (*models.Account, errors.Error)
UpdateName(ctx context.Context, userID string, name *models.Name) (*models.Account, errors.Error)
}
const defaultCurrency = "RUB"
type authClient interface {
GetUser(ctx context.Context, userID string) (*models.User, errors.Error)
}
type Deps struct {
Logger *zap.Logger
Repository accountRepository
AuthClient authClient
}
type Service struct {
logger *zap.Logger
repository accountRepository
authClient authClient
}
func New(deps Deps) *Service {
if deps.Logger == nil {
log.Panicln("logger is nil on <New (account service)>")
}
if deps.Repository == nil {
log.Panicln("repository is nil on <New (account service)>")
}
if deps.AuthClient == nil {
log.Panicln("auth client is nil on <New (account service)>")
}
return &Service{
logger: deps.Logger,
repository: deps.Repository,
authClient: deps.AuthClient,
}
}
func (receiver *Service) GetAccountByUserID(ctx context.Context, userID string) (*models.Account, errors.Error) {
account, err := receiver.repository.FindByUserID(ctx, userID)
if err != nil {
receiver.logger.Error("failed to get account by id on <GetAccountByUserID> of <AccountService>",
zap.Error(err),
zap.String("userID", userID),
)
return nil, err
}
return account, nil
}
func (receiver *Service) GetAccountsList(ctx context.Context, pagination *models.Pagination) (*models.PaginationResponse[models.Account], errors.Error) {
if pagination == nil {
return nil, errors.New(
fmt.Errorf("pagination is nil on <GetAccountsList> of <AccountService>: %w", errors.ErrInternalError),
errors.ErrInternalError,
)
}
count, err := receiver.repository.CountAll(ctx)
if err != nil {
receiver.logger.Error("failed to count accounts on <GetAccountsList> of <AccountService>",
zap.Error(err),
)
return nil, err
}
if count == 0 {
return &models.PaginationResponse[models.Account]{TotalPages: 0, Records: []models.Account{}}, nil
}
totalPages := int64(math.Ceil(float64(count) / float64(pagination.Limit)))
accounts, err := receiver.repository.FindMany(ctx, pagination.Page, pagination.Limit)
if err != nil {
receiver.logger.Error("failed to get accounts list on <GetAccountsList> of <AccountService>",
zap.Error(err),
zap.Int64("page", pagination.Page),
zap.Int64("limit", pagination.Limit),
)
return nil, err
}
return &models.PaginationResponse[models.Account]{
TotalPages: totalPages,
Records: accounts,
}, nil
}
func (receiver *Service) CreateAccount(ctx context.Context, account *models.Account) (*models.Account, errors.Error) {
findedAccount, err := receiver.GetAccountByUserID(ctx, account.UserID)
if err != nil && err.Type() != errors.ErrNotFound {
receiver.logger.Error("failed to find account on <CreateAccount> of <AccountService>",
zap.Error(err),
)
return nil, err
}
if findedAccount != nil {
return nil, errors.New(
fmt.Errorf("failed to create account with <%s> on <CreateAccount> of <AccountService>: %w",
account.UserID,
errors.ErrConflict,
),
errors.ErrConflict,
)
}
createdAccount, err := receiver.repository.Insert(ctx, account)
if err != nil {
receiver.logger.Error("failed to create account on <CreateAccount> of <AccountService>",
zap.Error(err),
zap.Any("account", account),
)
return nil, err
}
return createdAccount, nil
}
func (receiver *Service) CreateAccountByUserID(ctx context.Context, userID string) (*models.Account, errors.Error) {
account, err := receiver.GetAccountByUserID(ctx, userID)
if err != nil && err.Type() != errors.ErrNotFound {
receiver.logger.Error("failed to find account on <CreateAccountByUserID> of <AccountService>",
zap.Error(err),
)
return nil, err
}
if account != nil {
return nil, errors.New(
fmt.Errorf("failed to create account with <%s> on <CreateAccountByUserID> of <AccountService>: %w",
userID,
errors.ErrConflict,
),
errors.ErrConflict,
)
}
user, err := receiver.authClient.GetUser(ctx, userID)
if err != nil {
receiver.logger.Error("failed to get user on <CreateAccountByUserID> of <AccountService>",
zap.Error(err),
zap.String("userID", userID),
)
return nil, err
}
createdAccount, err := receiver.repository.Insert(ctx, &models.Account{UserID: user.ID, Wallet: models.Wallet{Currency: defaultCurrency}})
if err != nil {
receiver.logger.Error("failed to create account on <CreateAccountByUserID> of <AccountService>",
zap.Error(err),
zap.String("userID", userID),
)
return nil, err
}
return createdAccount, nil
}
func (receiver *Service) RemoveAccount(ctx context.Context, userID string) (*models.Account, errors.Error) {
account, err := receiver.repository.Remove(ctx, userID)
if err != nil {
receiver.logger.Error("failed to remove account on <RemoveAccount> of <AccountService>",
zap.Error(err),
zap.String("userID", userID),
)
return nil, err
}
return account, nil
}
func (receiver *Service) DeleteAccount(ctx context.Context, userID string) (*models.Account, errors.Error) {
account, err := receiver.repository.Delete(ctx, userID)
if err != nil {
receiver.logger.Error("failed to delete account on <DeleteAccount> of <AccountService>",
zap.Error(err),
zap.String("userID", userID),
)
return nil, err
}
return account, nil
}
func (receiver *Service) SetVerificationStatus(ctx context.Context, userID string, status models.AccountStatus) (*models.Account, errors.Error) {
account, err := receiver.repository.SetStatus(ctx, userID, status)
if err != nil {
receiver.logger.Error("failed to set status on <SetVerificationStatus> of <AccountService>", zap.Error(err))
return nil, err
}
return account, nil
}
func (receiver *Service) UpdateAccountName(ctx context.Context, userID string, name *models.Name) (*models.Account, errors.Error) {
account, err := receiver.repository.UpdateName(ctx, userID, name)
if err != nil {
receiver.logger.Error("failed to update account name on <UpdateAccountName> of <AccountService>", zap.Error(err))
return nil, err
}
return account, nil
}

@ -2,7 +2,6 @@ package callback
import (
"context"
"fmt"
"log"
"go.uber.org/zap"
@ -83,16 +82,20 @@ func (receiver *PaymentCallbackService) SuccessEvent(ctx context.Context, event
return err
}
go func() {
//go func() {
if _, err := receiver.historyService.CreateHistory(ctx, &models.History{
UserID: account.UserID,
Comment: event.Message,
Key: event.Key,
RawDetails: fmt.Sprintf("%d%s", event.Amount, event.Currency),
RawDetails: models.RawDetails{
Price: event.Amount,
Comment: event.Type + ":" + event.Currency,
},
}); err != nil {
receiver.logger.Error("failed to create history on <SuccessEvent> of <PaymentCallbackService>", zap.Error(err))
return err
}
}()
//}()
return nil
}
@ -102,7 +105,7 @@ func (receiver *PaymentCallbackService) FailureEvent(ctx context.Context, event
UserID: event.UserID,
Comment: event.Message,
Key: event.Key,
RawDetails: fmt.Sprintf("%d%s", event.Amount, event.Currency),
// RawDetails: fmt.Sprintf("%d%s", event.Amount, event.Currency),
}); err != nil {
receiver.logger.Error("failed to create history on <FailureEvent> of <PaymentCallbackService>", zap.Error(err))
return err

@ -1,238 +0,0 @@
package cart
import (
"context"
"fmt"
"log"
"time"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/timestamppb"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/discount"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils/transfer"
)
type accountRepository interface {
FindByUserID(ctx context.Context, id string) (*models.Account, errors.Error)
AddItemToCart(ctx context.Context, userID, itemID string) (*models.Account, errors.Error)
RemoveItemFromCart(ctx context.Context, userID, itemID string) (*models.Account, errors.Error)
ClearCart(ctx context.Context, userID string) (*models.Account, errors.Error)
}
type hubadminClient interface {
GetTariff(ctx context.Context, accessToken, tariffID string) (*models.Tariff, errors.Error)
GetTariffs(ctx context.Context, accessToken string, tarriffIDs []string) ([]models.Tariff, errors.Error)
}
type discountClient interface {
Apply(context.Context, *discount.ApplyDiscountRequest) (*discount.ApplyDiscountResponse, errors.Error)
}
type walletService interface {
ReplenishAccountWallet(context.Context, *models.ReplenishAccountWallet) (*models.Account, errors.Error)
WithdrawAccountWalletMoney(context.Context, *models.WithdrawAccountWallet) (*models.Account, errors.Error)
}
type historyService interface {
CreateHistory(ctx context.Context, history *models.History) (*models.History, errors.Error)
}
type tariffBrokerService interface {
SendMany(ctx context.Context, userID string, tariffs []models.Tariff) []errors.Error
}
type Deps struct {
Logger *zap.Logger
Repository accountRepository
HubadminClient hubadminClient
DiscountClient discountClient
WalletService walletService
HistoryService historyService
TariffBrokerService tariffBrokerService
}
type Service struct {
logger *zap.Logger
repository accountRepository
hubadminClient hubadminClient
discountClient discountClient
walletService walletService
historyService historyService
tariffBrokerService tariffBrokerService
}
func New(deps Deps) *Service {
if deps.Logger == nil {
log.Panicln("logger is nil on <New (cart service)>")
}
if deps.Repository == nil {
log.Panicln("repository is nil on <New (cart service)>")
}
if deps.HubadminClient == nil {
log.Panicln("HubadminClient is nil on <New (cart service)>")
}
if deps.DiscountClient == nil {
log.Panicln("DiscountClient is nil on <New (cart service)>")
}
if deps.WalletService == nil {
log.Panicln("WalletService is nil on <New (cart service)>")
}
if deps.HistoryService == nil {
log.Panicln("HistoryService is nil on <New (cart service)>")
}
if deps.TariffBrokerService == nil {
log.Panicln("TariffBrokerService is nil on <New (cart service)>")
}
return &Service{
logger: deps.Logger,
repository: deps.Repository,
hubadminClient: deps.HubadminClient,
discountClient: deps.DiscountClient,
walletService: deps.WalletService,
historyService: deps.HistoryService,
tariffBrokerService: deps.TariffBrokerService,
}
}
func (receiver *Service) Remove(ctx context.Context, userID, itemID string) (*models.Account, errors.Error) {
account, err := receiver.repository.RemoveItemFromCart(ctx, userID, itemID)
if err != nil {
receiver.logger.Error("failed to remove item from cart on <Remove> of <CartService>", zap.Error(err))
return nil, err
}
return account, nil
}
func (receiver *Service) Add(ctx context.Context, request *models.AddItemToCart) (*models.Account, errors.Error) {
tariff, err := receiver.hubadminClient.GetTariff(ctx, request.AccessToken, request.TariffID)
if err != nil {
receiver.logger.Error("failed to get tariff on <Add> of <CartService>",
zap.Error(err),
zap.String("tariffID", request.TariffID),
zap.String("accessToken", request.AccessToken),
)
return nil, err
}
if tariff == nil {
return nil, errors.New(
fmt.Errorf("failed to get tariff <%s> on <Add> of <CartService>: tariff not found", request.TariffID),
errors.ErrNotFound,
)
}
account, err := receiver.repository.AddItemToCart(ctx, request.UserID, request.TariffID)
if err != nil {
receiver.logger.Error("failed to add item to cart on <Add> of <CartService>", zap.Error(err))
return nil, err
}
return account, nil
}
func (receiver *Service) Pay(ctx context.Context, accessToken string, userID string) (*models.Account, errors.Error) {
account, err := receiver.repository.FindByUserID(ctx, userID)
if err != nil {
receiver.logger.Error("failed to find account on <Pay> of <CartService>", zap.String("userID", userID), zap.Error(err))
return nil, err
}
receiver.logger.Info("account for pay", zap.Any("acc", account))
tariffs, err := receiver.hubadminClient.GetTariffs(ctx, accessToken, account.Cart)
if err != nil {
receiver.logger.Error("failed to get tarrifs on <Pay> of <CartService>", zap.Strings("cart", account.Cart), zap.Error(err))
return nil, err
}
receiver.logger.Info("tariffs for pay", zap.Any("acc", tariffs))
tariffsAmount := utils.CalculateCartPurchasesAmount(tariffs)
discountResponse, err := receiver.discountClient.Apply(ctx, &discount.ApplyDiscountRequest{
UserInformation: &discount.UserInformation{
ID: account.UserID,
Type: string(account.Status),
PurchasesAmount: uint64(account.Wallet.Spent),
CartPurchasesAmount: tariffsAmount,
},
Products: transfer.TariffsToProductInformations(tariffs),
Date: timestamppb.New(time.Now()),
})
if err != nil {
receiver.logger.Error("failed to discount on <Pay> of <CartService>", zap.Error(err))
return nil, err
}
receiver.logger.Info("discountResponse for pay", zap.Any("acc", discount.ApplyDiscountRequest{
UserInformation: &discount.UserInformation{
ID: account.UserID,
Type: string(account.Status),
PurchasesAmount: uint64(account.Wallet.Spent),
CartPurchasesAmount: tariffsAmount,
},
Products: transfer.TariffsToProductInformations(tariffs),
Date: timestamppb.New(time.Now()),
}))
if account.Wallet.Money < int64(discountResponse.Price) {
receiver.logger.Error("insufficient funds on <Pay> of <CartService>")
return nil, errors.New(fmt.Errorf("insufficient funds: %d", int64(discountResponse.Price)-account.Wallet.Money), errors.ErrInsufficientFunds)
}
updatedAccount, err := receiver.walletService.WithdrawAccountWalletMoney(ctx, &models.WithdrawAccountWallet{
Money: int64(discountResponse.Price),
Account: account,
})
if err != nil {
receiver.logger.Error("failed to withdraw money on <Pay> of <CartService>", zap.Error(err))
return nil, err
}
go func(tariffs []models.Tariff) {
if _, historyErr := receiver.historyService.CreateHistory(ctx, &models.History{
Key: models.CustomerHistoryKeyPayCart,
UserID: account.UserID,
Comment: "Успешная оплата корзины",
RawDetails: map[string]any{
"tariffs": tariffs,
"price": discountResponse.Price,
},
}); historyErr != nil {
receiver.logger.Error("failed to insert history on <Pay> of <CartService>", zap.Error(historyErr))
}
}(tariffs)
// TODO: обработать ошибки при отправке сообщений
receiver.logger.Error("send to redpanda", zap.String("userID", account.UserID), zap.Any("bought tariffs", tariffs))
if sendErrors := receiver.tariffBrokerService.SendMany(ctx, account.UserID, tariffs); len(sendErrors) > 0 {
for _, err := range sendErrors {
receiver.logger.Error("failed to send tariffs to broker on <Pay> of <CartService>", zap.Error(err))
}
return nil, errors.NewWithMessage("failed to send tariffs to broker", errors.ErrInternalError)
}
if _, err := receiver.repository.ClearCart(ctx, account.UserID); err != nil {
receiver.logger.Error("failed to clear cart on <Pay> of <CartService>", zap.Error(err))
return nil, err
}
updatedAccount.Cart = []string{}
return updatedAccount, nil
}

@ -1,93 +0,0 @@
package currency
import (
"context"
"log"
"go.uber.org/zap"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
)
type currencyRepository interface {
FindCurrenciesList(ctx context.Context, name string) (*models.CurrencyList, errors.Error)
ReplaceCurrencies(ctx context.Context, list *models.CurrencyList) (*models.CurrencyList, errors.Error)
Insert(ctx context.Context, list *models.CurrencyList) (*models.CurrencyList, errors.Error)
}
type Deps struct {
Logger *zap.Logger
Repository currencyRepository
}
type Service struct {
logger *zap.Logger
repository currencyRepository
}
func New(deps Deps) *Service {
if deps.Logger == nil {
log.Panicln("logger is nil on <New (currency service)>")
}
if deps.Repository == nil {
log.Panicln("repository is nil on <New (currency service)>")
}
return &Service{
logger: deps.Logger,
repository: deps.Repository,
}
}
func (receiver *Service) GetCurrencies(ctx context.Context) ([]string, errors.Error) {
currencyList, err := receiver.repository.FindCurrenciesList(ctx, models.DefaultCurrencyListName)
if err != nil && err.Type() != errors.ErrNotFound {
receiver.logger.Error(
"failed to get currencies on <GetCurrencies> of <CurrencyService>",
zap.Error(err),
)
return []string{}, err
}
if err != nil && err.Type() == errors.ErrNotFound {
return []string{}, nil
}
return currencyList.Currencies, nil
}
func (receiver *Service) PutCurrencies(ctx context.Context, currencies []string) ([]string, errors.Error) {
currencyList, err := receiver.repository.ReplaceCurrencies(ctx, &models.CurrencyList{
Name: models.DefaultCurrencyListName,
Currencies: currencies,
})
if err != nil && err.Type() != errors.ErrNotFound {
receiver.logger.Error(
"failed to put currencies on <PutCurrencies> of <CurrencyService>",
zap.Error(err),
)
return []string{}, err
}
if err != nil && err.Type() == errors.ErrNotFound {
newCurrencyList, err := receiver.repository.Insert(ctx, &models.CurrencyList{
Name: models.DefaultCurrencyListName,
Currencies: currencies,
})
if err != nil && err.Type() != errors.ErrNotFound {
receiver.logger.Error(
"failed to insert new currency list on <PutCurrencies> of <CurrencyService>",
zap.Error(err),
)
return []string{}, err
}
return newCurrencyList.Currencies, nil
}
return currencyList.Currencies, nil
}

@ -2,14 +2,9 @@ package history
import (
"context"
"fmt"
"log"
"math"
"os"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.uber.org/zap"
"log"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/fields"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
@ -89,44 +84,6 @@ func New(deps Deps) *Service {
}
}
func (receiver *Service) GetHistoryList(ctx context.Context, dto *GetHistories) (*models.PaginationResponse[models.History], errors.Error) {
if dto == nil {
return nil, errors.New(
fmt.Errorf("pagination is nil on <GetHistoryList> of <HistoryService>: %w", errors.ErrInternalError),
errors.ErrInternalError,
)
}
count, err := receiver.repository.CountAll(ctx, dto)
if err != nil {
receiver.logger.Error("failed to count histories on <GetHistoryList> of <HistoryService>",
zap.Error(err),
)
return nil, err
}
if count == 0 {
return &models.PaginationResponse[models.History]{TotalPages: 0, Records: []models.History{}}, nil
}
totalPages := int64(math.Ceil(float64(count) / float64(dto.Pagination.Limit)))
histories, err := receiver.repository.FindMany(ctx, dto)
if err != nil {
receiver.logger.Error("failed to get historiy list on <GetHistoryList> of <HistoryService>",
zap.Error(err),
)
return nil, err
}
return &models.PaginationResponse[models.History]{
TotalPages: totalPages,
Records: histories,
}, nil
}
func (receiver *Service) CreateHistory(ctx context.Context, history *models.History) (*models.History, errors.Error) {
createdHistory, err := receiver.repository.Insert(ctx, history)
if err != nil {
@ -136,138 +93,3 @@ func (receiver *Service) CreateHistory(ctx context.Context, history *models.Hist
return createdHistory, nil
}
// TODO:tests.
func (receiver *Service) GetRecentTariffs(ctx context.Context, userID string) ([]models.TariffID, errors.Error) {
if userID == "" {
receiver.logger.Error("user id is missing in <GetRecentTariffs> of <HistoryService>")
return nil, errors.New(
fmt.Errorf("user id is missing: %w", errors.ErrInvalidArgs),
errors.ErrInvalidArgs,
)
}
tariffs, err := receiver.repository.GetRecentTariffs(ctx, userID)
if err != nil {
receiver.logger.Error(
"failed to get recent tariffs in <GetRecentTariffs> of <HistoryService>",
zap.String("userId", userID),
zap.Error(err),
)
return nil, err
}
return tariffs, nil
}
func (receiver *Service) GetHistoryByID(ctx context.Context, historyID string) errors.Error {
if historyID == "" {
receiver.logger.Error("history id is missing in <GetHistoryById> of <HistoryService>")
return errors.New(
fmt.Errorf("history id is missing: %w", errors.ErrInvalidArgs),
errors.ErrInvalidArgs,
)
}
tariffs, err := receiver.repository.GetHistoryByID(ctx, historyID)
if err != nil {
receiver.logger.Error(
"failed to get history by id in <GetHistoryById> of <HistoryService>",
zap.String("historyID", historyID),
zap.Error(err),
)
return err
}
if tariffs.Key != models.CustomerHistoryKeyPayCart {
receiver.logger.Error(
"invalid history record key",
zap.String("historyID", historyID),
zap.Error(err),
)
return err
}
historyMap, err := receiver.repository.GetDocNumber(ctx, tariffs.UserID)
if err != nil {
receiver.logger.Error(
"failed to get history of sorting by date created in <GetDocNumber> of <HistoryService>",
zap.String("historyID", historyID),
zap.Error(err),
)
return err
}
verifuser, err := receiver.VerificationClient.GetVerification(ctx, tariffs.UserID)
if err != nil {
receiver.logger.Error("failed to get user verification on <GetHistoryById> of <HistoryService>",
zap.Error(err),
zap.String("userID", tariffs.UserID),
)
return err
}
if !verifuser.Accepted {
receiver.logger.Error(
"verification not accepted",
zap.String("userID", tariffs.UserID),
zap.Error(err),
)
return err
}
authuser, err := receiver.AuthClient.GetUser(ctx, tariffs.UserID)
if err != nil {
receiver.logger.Error("failed to get user on <GetHistoryById> of <HistoryService>",
zap.Error(err),
zap.String("userID", tariffs.UserID),
)
return err
}
fileContents, readerr := os.ReadFile("./report.docx")
if readerr != nil {
return errors.New(
fmt.Errorf("failed to read file: %w", errors.ErrInternalError),
errors.ErrInternalError,
)
}
for _, tariff := range tariffs.RawDetails.Tariffs {
totalAmount := uint64(0)
for _, privilege := range tariff.Privileges {
totalAmount += privilege.Amount
}
data := models.RespGeneratorService{
DocNumber: historyMap[historyID] + 1,
Date: time.Now().Format("2006-01-02"),
OrgTaxNum: verifuser.TaxNumber,
OrgName: models.Name{Orgname: "Orgname"},
Name: tariff.Name,
Amount: totalAmount,
Price: tariffs.RawDetails.Price,
Sum: tariffs.RawDetails.Price,
}
err = receiver.TemlategenClient.SendData(ctx, data, fileContents, authuser.Email)
if err != nil {
receiver.logger.Error("failed to send report to user on <GetHistoryById> of <HistoryService>",
zap.Error(err),
zap.String("userID", tariffs.UserID),
)
return err
}
}
return nil
}
func (receiver *Service) CalculateCustomerLTV(ctx context.Context, from, to int64) (int64, errors.Error) {
ltv, err := receiver.repository.CalculateCustomerLTV(ctx, from, to)
if err != nil {
receiver.logger.Error("failed to calculate LTV", zap.Error(err))
return 0, err
}
return ltv, nil
}

@ -2,13 +2,10 @@ package wallet
import (
"context"
"fmt"
"log"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger"
"go.uber.org/zap"
"log"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate"
)
@ -119,7 +116,8 @@ func (receiver *Service) ReplenishAccountWallet(ctx context.Context, request *mo
updatedAccount, changeErr := receiver.repository.ChangeWallet(ctx, request.Account.UserID, &models.Wallet{
Cash: request.Account.Wallet.Cash + cash,
Money: request.Account.Wallet.Money + request.Cash,
PurchasesAmount: request.Account.Wallet.PurchasesAmount + request.Cash,
Spent: request.Account.Wallet.Spent,
PurchasesAmount: request.Account.Wallet.PurchasesAmount,
Currency: request.Account.Wallet.Currency,
LastPaymentID: request.PaymentID,
})
@ -134,17 +132,17 @@ func (receiver *Service) ReplenishAccountWallet(ctx context.Context, request *mo
return nil, changeErr
}
go func() {
if _, historyErr := receiver.historyService.CreateHistory(ctx, &models.History{
Key: models.CustomerHistoryKeyReplenish,
UserID: request.Account.UserID,
Comment: "Успешное пополнение средств (Без конвертации валюты)",
RawDetails: fmt.Sprintf("%d%s", cash, models.InternalCurrencyKey),
}); historyErr != nil {
receiver.logger.Error("failed to insert history on <ReplenishAccountWallet> of <WalletService>", zap.Error(historyErr))
}
}()
// go func() {
// if _, historyErr := receiver.historyService.CreateHistory(ctx, &models.History{
// Key: models.CustomerHistoryKeyReplenish,
// UserID: request.Account.UserID,
// Comment: "Успешное пополнение средств (Без конвертации валюты)",
// // RawDetails: fmt.Sprintf("%d%s", cash, models.InternalCurrencyKey),
// }); historyErr != nil {
// receiver.logger.Error("failed to insert history on <ReplenishAccountWallet> of <WalletService>", zap.Error(historyErr))
// }
// }()
//
return updatedAccount, nil
}
@ -178,154 +176,16 @@ func (receiver *Service) ReplenishAccountWallet(ctx context.Context, request *mo
return nil, err
}
go func() {
if _, historyErr := receiver.historyService.CreateHistory(ctx, &models.History{
Key: models.CustomerHistoryKeyReplenish,
UserID: request.Account.UserID,
Comment: "Успешное пополнение средств (C конвертацией валюты)",
RawDetails: fmt.Sprintf("%d%s", cash, request.Currency),
}); historyErr != nil {
receiver.logger.Error("failed to insert history on <ReplenishAccountWallet> of <WalletService>", zap.Error(historyErr))
}
}()
// go func() {
// if _, historyErr := receiver.historyService.CreateHistory(ctx, &models.History{
// Key: models.CustomerHistoryKeyReplenish,
// UserID: request.Account.UserID,
// Comment: "Успешное пополнение средств (C конвертацией валюты)",
// //RawDetails: fmt.Sprintf("%d%s", cash, request.Currency),
// }); historyErr != nil {
// receiver.logger.Error("failed to insert history on <ReplenishAccountWallet> of <WalletService>", zap.Error(historyErr))
// }
// }()
return updatedAccount, nil
}
func (receiver *Service) WithdrawAccountWalletMoney(ctx context.Context, request *models.WithdrawAccountWallet) (*models.Account, errors.Error) {
if validate.IsStringEmpty(request.Account.Wallet.Currency) {
request.Account.Wallet.Currency = models.InternalCurrencyKey
}
if request.Account.Wallet.Currency == models.InternalCurrencyKey {
updatedAccount, err := receiver.repository.ChangeWallet(ctx, request.Account.UserID, &models.Wallet{
Cash: request.Account.Wallet.Cash - request.Money,
Money: request.Account.Wallet.Money - request.Money,
Spent: request.Account.Wallet.Spent + request.Money,
PurchasesAmount: request.Account.Wallet.PurchasesAmount,
Currency: request.Account.Wallet.Currency,
})
if err != nil {
receiver.logger.Error("failed to replenish wallet on <ReplenishAccountWallet> of <WalletService>",
zap.Error(err),
zap.String("Currency", request.Account.Wallet.Currency),
zap.Int64("Money", request.Account.Wallet.Money-request.Money),
zap.Int64("Cash", request.Account.Wallet.Cash+request.Money),
)
return nil, err
}
return updatedAccount, nil
}
cash, err := receiver.currencyClient.Translate(ctx, &models.TranslateCurrency{
Money: request.Money,
From: models.InternalCurrencyKey,
To: request.Account.Wallet.Currency,
})
if err != nil {
receiver.logger.Error("failed to translate money on <WithdrawAccountWalletMoney> of <WalletService>", zap.Error(err))
return nil, err
}
updatedAccount, err := receiver.repository.ChangeWallet(ctx, request.Account.UserID, &models.Wallet{
Cash: request.Account.Wallet.Cash - cash,
Money: request.Account.Wallet.Money - request.Money,
Spent: request.Account.Wallet.Spent + request.Money,
PurchasesAmount: request.Account.Wallet.PurchasesAmount,
Currency: request.Account.Wallet.Currency,
})
if err != nil {
receiver.logger.Error("failed to replenish wallet on <ReplenishAccountWallet> of <WalletService>",
zap.Error(err),
zap.String("Currency", request.Account.Wallet.Currency),
zap.Int64("Money", request.Account.Wallet.Money-request.Money),
zap.Int64("Cash", request.Account.Wallet.Cash+cash),
)
return nil, err
}
return updatedAccount, nil
}
func (receiver *Service) ChangeCurrency(ctx context.Context, userID string, currency models.CurrencyKey) (*models.Account, errors.Error) {
account, err := receiver.repository.FindByUserID(ctx, userID)
if err != nil {
receiver.logger.Error("failed to find account on <ChangeCurrency> of <WalletService>",
zap.Error(err),
zap.String("userID", userID),
zap.Any("currency", currency),
)
return nil, err
}
cash, err := receiver.currencyClient.Translate(ctx, &models.TranslateCurrency{
Money: account.Wallet.Cash,
From: account.Wallet.Currency,
To: currency,
})
if err != nil {
receiver.logger.Error("failed to translate currency on <ChangeCurrency> of <WalletService>", zap.Error(err))
return nil, err
}
updatedAccount, err := receiver.repository.ChangeWallet(ctx, account.UserID, &models.Wallet{
Cash: cash,
Currency: currency,
Money: account.Wallet.Money,
})
if err != nil {
receiver.logger.Error("failed to update wallet on <ChangeCurrency> of <WalletService>", zap.Error(err))
return nil, err
}
return updatedAccount, nil
}
func (receiver *Service) PostWalletRspay(ctx context.Context, token, userID string, req swagger.PostWalletRspayJSONBody) errors.Error {
user, err := receiver.repository.FindByUserID(ctx, userID)
if err != nil {
return err
}
if user.Status != models.AccountStatusNko && user.Status != models.AccountStatusOrg {
return errors.New(
fmt.Errorf("not allowed for non organizations"),
errors.ErrNoAccess,
)
}
verification, err := receiver.verificationClient.GetVerification(ctx, token, userID)
if err == errors.ErrNotFound {
return errors.New(
fmt.Errorf("no verification data found"),
errors.ErrNoAccess,
)
}
if user.Status == models.AccountStatusOrg && len(verification.Files) != 3 {
return errors.New(
fmt.Errorf("not enough verification files"),
errors.ErrNoAccess,
)
} else if user.Status == models.AccountStatusNko && len(verification.Files) != 4 {
return errors.New(
fmt.Errorf("not enough verification files"),
errors.ErrNoAccess,
)
}
authData, err := receiver.authClient.GetUser(ctx, userID)
if err != nil {
return err
}
err = receiver.mailClient.SendMessage(authData.Login, verification, *req.Money)
if err != nil {
return err
}
return nil
}

@ -0,0 +1,34 @@
package utils
import (
"go.mongodb.org/mongo-driver/bson"
codeword_rpc "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/codeword"
"time"
)
func MatchGen(codewordData map[string][]*codeword_rpc.PromoActivationResp_UserTime, from, to int64) bson.M {
userActivations := make(map[string]int64)
for _, values := range codewordData {
for _, activation := range values {
userActivations[activation.UserID] = activation.Time
}
}
var conditions []bson.M
for userID, activation := range userActivations {
var condition bson.M
if activation < from {
condition = bson.M{"userId": userID, "createdAt": bson.M{"$gte": time.Unix(from, 0), "$lte": time.Unix(to, 0)}, "key": "payment.succeeded"}
} else {
condition = bson.M{"userId": userID, "createdAt": bson.M{"$gte": time.Unix(activation, 0), "$lte": time.Unix(to, 0)}, "key": "payment.succeeded"}
}
conditions = append(conditions, condition)
}
match := bson.M{
"$match": bson.M{"$or": conditions},
}
return match
}

@ -15,9 +15,8 @@ func TariffsToProductInformations(tarrifs []models.Tariff) []*discount.ProductIn
}
if tariff.Price != 0 {
for i := range productInformations {
productInformations[i].Price = tariff.Price
}
price := tariff.Price
productInformations[len(productInformations) - 1].Usage = &price
}
}

@ -2,7 +2,6 @@ package client
import (
"context"
"fmt"
"github.com/go-resty/resty/v2"
)
@ -46,7 +45,6 @@ func makeRequest[T any, R any](url string, requestMethod func(url string) (*rest
Error: responseBody,
}, nil
}
fmt.Println("OO5OO", responseInstance.RawResponse, responseInstance.StatusCode())
responseBody, parseErr := parseResponse[T](responseInstance.Body(), responseInstance.RawResponse)
if parseErr != nil {

@ -2,15 +2,15 @@ package client
import (
"bytes"
"fmt"
"net/http"
"fmt"
"penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/json"
)
func parseResponse[T any](body []byte, response *http.Response) (*T, error) {
isJSONResponse := response.Header.Get("Content-Type") == "application/json"
fmt.Println("1OOOO", response.Header.Get("Content-Type"), string(body))
fmt.Println("1OOOO", response.Header.Get("Content-Type"), string(body))
if !isJSONResponse {
responseBody, unmarshalErr := json.Unmarshal[T](body)

@ -25,7 +25,7 @@ func TestAddAccount(t *testing.T) {
}
responseAddAccount, errAddAccount := client.Post[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8000/account",
URL: "http://localhost:8082/account",
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
QueryParams: map[string]string{"id": "64e5d9830fcca0596d82c0c1"},
})

@ -25,7 +25,7 @@ func TestBuyTariff(t *testing.T) {
}
responseAddCart, errAddCart := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8000/cart",
URL: "http://localhost:8082/cart",
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
QueryParams: map[string]string{"id": "64e6105384368b75221a5c3e"},
})
@ -42,7 +42,7 @@ func TestBuyTariff(t *testing.T) {
if isUserIDValid && isCartItemValid && isCartLengthValid {
responsePay, errPay := client.Post[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8000/cart/pay",
URL: "http://localhost:8082/cart/pay",
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
})
if isNoError := assert.NoError(t, errPay); !isNoError {

@ -37,7 +37,7 @@ func TestCalculateLTV(t *testing.T) {
fmt.Println(from, to)
response, err := client.Post[interface{}, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8000/history/ltv",
URL: "http://localhost:8082/history/ltv",
Body: swagger.CalculateLTVJSONBody{From: from, To: to},
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
})

@ -32,7 +32,7 @@ func TestChangeAccount(t *testing.T) {
}
responseChangeAccount, errChangeAccount := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8000/account",
URL: "http://localhost:8082/account",
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
Body: updateRequest,
})
@ -64,7 +64,7 @@ func TestChangeAccount(t *testing.T) {
}
responseStatusAccount, errStatusAccount := client.Patch[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8000/account/64e5d9830fcca0596d82c0c7",
URL: "http://localhost:8082/account/64e5d9830fcca0596d82c0c7",
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
Body: statusRequest,
})

@ -25,7 +25,7 @@ func TestGetAccount(t *testing.T) {
}
responseGetAccount, errGetAccount := client.Get[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8000/account",
URL: "http://localhost:8082/account",
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
})
if isNoError := assert.NoError(t, errGetAccount); !isNoError {
@ -48,7 +48,7 @@ func TestGetAccount(t *testing.T) {
}
responseGetAccount, errGetAccount := client.Get[models.Account, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8000/account/64e5d9830fcca0596d82c0c7",
URL: "http://localhost:8082/account/64e5d9830fcca0596d82c0c7",
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
})
if isNoError := assert.NoError(t, errGetAccount); !isNoError {
@ -75,7 +75,7 @@ func TestGetAccount(t *testing.T) {
}
responseGetAccount, errGetAccount := client.Get[models.PaginationResponse[models.Account], models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8000/accounts",
URL: "http://localhost:8082/accounts",
Body: params,
})
if isNoError := assert.NoError(t, errGetAccount); !isNoError {

@ -24,7 +24,7 @@ func TestGetRecentTariffs(t *testing.T) {
assert.NoError(t, tokenErr)
responseGetRecentTariffs, errGetRecentTariffs := client.Get[[]models.TariffID, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8000/recent",
URL: "http://localhost:8082/recent",
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
})
assert.NoError(t, errGetRecentTariffs)

@ -24,7 +24,7 @@ func TestGetAccount(t *testing.T) {
}
response, err := client.Post[interface{}, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8000/wallet/rspay",
URL: "http://localhost:8082/wallet/rspay",
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
})
if isNoError := assert.NoError(t, err); !isNoError {

@ -26,7 +26,7 @@ func TestSendReport(t *testing.T) {
assert.NoError(t, tokenErr)
responseSendReport, errSendReport := client.Post[interface{}, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8000/sendReport",
URL: "http://localhost:8082/sendReport",
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
Body: map[string]interface{}{"id": historyID},
})

@ -17,7 +17,7 @@ func TestCurrencies(t *testing.T) {
assert.NotPanics(t, func() {
responseGetCurrencies, errCurrencies := client.Get[[]models.CurrencyList, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://localhost:8000/currencies",
URL: "http://localhost:8082/currencies",
})
if isNoError := assert.NoError(t, errCurrencies); !isNoError {
return

@ -22,7 +22,7 @@ func TestHistoryReport(t *testing.T) {
}
response, err := client.Post[struct{}, models.ResponseErrorHTTP](ctx, &client.RequestSettings{
URL: "http://" + "localhost:8000" + "/sendReport",
URL: "http://" + "localhost:8082" + "/sendReport",
Body: swagger.SendReportJSONBody{Id: "10002"},
Headers: map[string]string{"Authorization": fmt.Sprintf("Bearer %s", token)},
})

@ -0,0 +1,110 @@
package integration
import (
"context"
"github.com/labstack/echo/v4"
"github.com/pioz/faker"
"go.uber.org/zap"
"log"
"net/http"
"net/http/httptest"
"penahub.gitlab.yandexcloud.net/backend/penahub_common/mongo"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/repository"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/swagger"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"strconv"
"testing"
"time"
)
func TestLogostat(t *testing.T) {
logger, err := zap.NewProduction(zap.AddStacktrace(zap.DPanicLevel))
if err != nil {
log.Fatalf("failed to init zap logger: %v", err)
}
ctx := context.Background()
mongoDB, err := mongo.Connect(ctx, &mongo.ConnectDeps{
Configuration: &mongo.Configuration{
Host: "localhost",
Port: "27020",
User: "test",
Password: "test",
Auth: "admin",
DatabaseName: "admin",
},
Timeout: 10 * time.Second,
})
repoAc := repository.NewAccountRepository2(logger, mongoDB.Collection("accounts"))
repoHi := repository.NewHistoryRepository2(logger, mongoDB.Collection("histories"))
InsertToDB(ctx, repoAc, repoHi)
api := swagger.NewAPI2(logger, mongoDB, nil, nil, nil, nil)
e := echo.New()
req := httptest.NewRequest(http.MethodGet, "/", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
requestBody := swagger.QuizLogoStatJSONRequestBody{
From: new(int),
Limit: new(int),
Page: new(int),
To: new(int),
}
*requestBody.From = 1713087258
*requestBody.Limit = 10
*requestBody.Page = 1
*requestBody.To = 1713260058
c.SetRequest(c.Request().WithContext(context.WithValue(c.Request().Context(), "requestBody", requestBody)))
err = api.QuizLogoStat(c)
}
func InsertToDB(ctx context.Context, acc repository.AccountRepository, history repository.HistoryRepository) {
partner1 := "partner1"
partner2 := "partner2"
qid1 := "qid1"
qid2 := "qid2"
acc.Insert(ctx, &models.Account{
ID: "1",
UserID: partner1,
CreatedAt: time.Now(),
})
acc.Insert(ctx, &models.Account{
ID: "2",
UserID: partner2,
CreatedAt: time.Now(),
})
for i := 1; i < 101; i++ {
var partnerID string
var qid string
if i%2 == 0 {
partnerID = partner2
qid = qid2
} else {
partnerID = partner1
qid = qid1
}
acc.Insert(ctx, &models.Account{
ID: "IDUSER" + strconv.Itoa(i),
UserID: strconv.Itoa(i),
CreatedAt: time.Now(),
From: qid,
Partner: partnerID,
})
history.Insert(ctx, &models.History{
ID: "IDHISTORY" + strconv.Itoa(i),
UserID: strconv.Itoa(i),
RawDetails: models.RawDetails{
Price: int64(faker.Uint64()),
},
})
}
}

@ -0,0 +1,88 @@
package integration
import (
"context"
"fmt"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"log"
"penahub.gitlab.yandexcloud.net/backend/penahub_common/mongo"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/repository"
codeword_rpc "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/codeword"
"testing"
"time"
)
func Test_PromoLTV(t *testing.T) {
logger, err := zap.NewProduction(zap.AddStacktrace(zap.DPanicLevel))
if err != nil {
log.Fatalf("failed to init zap logger: %v", err)
}
ctx := context.Background()
codeword := client.NewCodewordClient(client.CodewordClientDeps{
Logger: logger,
CodewordServiceHost: "localhost:9000",
})
mdb, err := mongo.Connect(ctx, &mongo.ConnectDeps{
Configuration: &mongo.Configuration{
Host: "localhost",
Port: "27020",
User: "test",
Password: "test",
Auth: "admin",
DatabaseName: "admin",
},
Timeout: 10 * time.Second,
})
from := int64(0)
to := int64(1714291104)
historyRepo := repository.NewHistoryRepository2(logger, mdb.Collection("histories"))
codewordData, err := codeword.GetAllPromoActivations(ctx, &codeword_rpc.Time{
From: from,
To: to,
})
assert.NoError(t, err)
userSumMap, err := historyRepo.GetPayUsersPromoHistory(ctx, codewordData, from, to)
assert.NoError(t, err)
fmt.Println("userSumMap", userSumMap)
resp := make(map[string]struct {
Regs int
Money int64
})
for promoID, data := range codewordData {
for _, value := range data {
paids, ok := userSumMap[value.UserID]
if !ok {
paids = 0
}
if value.Time >= from && value.Time <= to {
if _, ok := resp[promoID]; !ok {
resp[promoID] = struct {
Regs int
Money int64
}{Regs: 1, Money: paids}
continue
}
current := resp[promoID]
current.Regs += 1
current.Money += paids
}
}
}
fmt.Println(resp)
}