Compare commits
2 Commits
main
...
bpcodeword
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2dc837be38 | ||
c4f7c33c03 |
65
.env
65
.env
@ -1,65 +0,0 @@
|
||||
# General application settings
|
||||
APP_NAME=codeword
|
||||
HTTP_HOST="localhost"
|
||||
HTTP_PORT="8080"
|
||||
|
||||
# MongoDB settings
|
||||
MONGO_HOST="127.0.0.1"
|
||||
MONGO_PORT="27020"
|
||||
MONGO_USER="test"
|
||||
MONGO_PASSWORD="test"
|
||||
MONGO_DB="admin"
|
||||
MONGO_AUTH="admin"
|
||||
|
||||
# Redis settings
|
||||
REDIS_ADDR="localhost:6379"
|
||||
REDIS_PASS="admin"
|
||||
REDIS_DB=2
|
||||
|
||||
# Keys
|
||||
PUBLIC_CURVE_KEY="-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAEbnIvjIMle4rqVol6K2XUqOxHy1KJoNoZdKJrRUPKL4=\n-----END PUBLIC KEY-----"
|
||||
|
||||
PRIVATE_CURVE_KEY="-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIKn0BKwF3vZvODgWAnUIwQhd8de5oZhY48gc23EWfrfs\n-----END PRIVATE KEY-----"
|
||||
|
||||
JWT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICWwIBAAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2B
|
||||
iw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikH
|
||||
oKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAEC
|
||||
gYAOphnVPXbk6lpYzdkLC1Xn5EOEuNfOLLURLxBnPWozZo26r/Mtahu/9mYhrYlv
|
||||
PP8r6mxta3VIil8iOdZyOLa/4d1LPd+UehgEXIJEiYXLtn7RS5eUnoPuQxssfs1k
|
||||
OWjdN8p6SzppleegFTvGRX4KM3cDLfSphOk8JuBCrpSSYQJBAOdqizTSrdKMTuVe
|
||||
c7Jk1JOJkyFuFs+N5zeryyeFGH7IpRdWy0rkWMxIUAi8Ap1vYVBPHv4tDOo3sy5X
|
||||
VLc/knkCQQCE62pg+0TmsrhO/2Pgog6MLBkzlzXYMRp/01HbmznwYF+ejfPnzLkz
|
||||
hnUlxRUNK3lhXM/7H6oAjvqF2R72u/OPAkEAterkmdbQfEZ+MwNoEiH/lie9OLdx
|
||||
SSI1VGdBYcTYN7qFRW6eizYstBJYkDU0HQ0Uw+we4hMKJwk4W0KdvxxDiQJAeqlB
|
||||
V1QqBneBbK10PzVuFV8QtrJhJyxRVwrtbKq38iMNuqUnI4+ijXEUpJFWVvv6nKXo
|
||||
7McQvEk12dU/JNTX8wJAOlAtSNjp9tVwpMpC0w2St1eKc1L2SknjeohA5ldoBz8sGeZsPhTU3eHSD1neAZXLKN5K68z3zFBr20ubY9nyLw==
|
||||
-----END RSA PRIVATE KEY-----"
|
||||
|
||||
JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2Biw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikHoKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAE=\n-----END PUBLIC KEY-----"
|
||||
|
||||
JWT_ISSUER="pena-auth-service"
|
||||
|
||||
JWT_AUDIENCE="pena"
|
||||
# SIGN_SECRET="group"
|
||||
|
||||
SIGN_SECRET="secret"
|
||||
|
||||
# SMTP settings
|
||||
SMTP_API_URL="https://api.smtp.bz/v1/smtp/send"
|
||||
SMTP_HOST="connect.mailclient.bz"
|
||||
SMTP_PORT="587"
|
||||
SMTP_UNAME="kotilion.95@gmail.com"
|
||||
SMTP_PASS="vWwbCSg4bf0p"
|
||||
SMTP_API_KEY="P0YsjUB137upXrr1NiJefHmXVKW1hmBWlpev"
|
||||
SMTP_SENDER="noreply@mailing.pena.digital"
|
||||
|
||||
# URL settings
|
||||
DEFAULT_REDIRECTION_URL = "def.url"
|
||||
AUTH_EXCHANGE_URL = "http://localhost:8000/auth/exchange"
|
||||
DISCOUNT_ADDRESS = "http://CHANGEME:1234"
|
||||
RECOVERY_URL = "http://127.0.0.1:8080/recover/"
|
||||
|
||||
# Kafka settings
|
||||
KAFKA_BROKERS="localhost:9092"
|
||||
KAFKA_TOPIC_TARIFF="tariffs"
|
@ -1,4 +1,4 @@
|
||||
ProjectName: codeword
|
||||
templateProjectName: codeword
|
||||
Description: Service for exchanging codewords
|
||||
|
||||
Template:
|
||||
@ -12,3 +12,10 @@ Modules:
|
||||
- name: APP_NAME
|
||||
type: string
|
||||
default: "{{.ProjectName}}"
|
||||
openapi:
|
||||
model_save_path: ./internal/models
|
||||
controller_save_path: ./internal/controllers
|
||||
service_save_path: ./internal/service
|
||||
repository_save_path: ./internal/repository
|
||||
server_save_path: ./internal/server/http
|
||||
db: postgres
|
||||
|
@ -1,32 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"codeword/internal/app"
|
||||
"codeword/internal/initialize"
|
||||
"context"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func main() {
|
||||
logger, err := zap.NewProduction()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to initialize logger: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
config, err := initialize.LoadConfig()
|
||||
if err != nil {
|
||||
logger.Fatal("Failed to load config", zap.Error(err))
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
if err = app.Run(ctx, *config, logger); err != nil {
|
||||
logger.Fatal("App exited with error", zap.Error(err))
|
||||
}
|
||||
}
|
38
cmd/main.go
Normal file
38
cmd/main.go
Normal file
@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"codeword/internal/app"
|
||||
"context"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"github.com/caarlos0/env/v8"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfgLogger := zap.NewDevelopmentConfig()
|
||||
cfgLogger.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
|
||||
cfgLogger.EncoderConfig.ConsoleSeparator = " "
|
||||
|
||||
logger, err := cfgLogger.Build()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer cancel()
|
||||
|
||||
var config app.Config
|
||||
|
||||
err = env.Parse(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := app.Run(ctx, config, logger); err != nil {
|
||||
logger.Fatal("Failed to run app", zap.Any("Error", err))
|
||||
}
|
||||
}
|
48
database.puml
Normal file
48
database.puml
Normal file
@ -0,0 +1,48 @@
|
||||
@startuml Database
|
||||
|
||||
map Usage {
|
||||
key => **string**
|
||||
time => **string** //date-time//
|
||||
}
|
||||
|
||||
map Discount {
|
||||
factor => **integer**
|
||||
layer => **integer**
|
||||
target => **string**
|
||||
threshold => **integer**
|
||||
}
|
||||
|
||||
map Filter {
|
||||
active => **boolean**
|
||||
text => **string**
|
||||
}
|
||||
|
||||
map Privilege {
|
||||
amount => **integer**
|
||||
privilegeID => **string**
|
||||
}
|
||||
|
||||
map PromoCode {
|
||||
id => **string** //primary_key//
|
||||
createdAt => **string** //date-time//
|
||||
dueTo => **integer**
|
||||
offLimit => **boolean**
|
||||
codeword => **string**
|
||||
outdated => **boolean**
|
||||
activationCount => **integer**
|
||||
bonus => **bonus**
|
||||
delete => **boolean**
|
||||
description => **string**
|
||||
fastLinks => **[]string**
|
||||
greetings => **string**
|
||||
}
|
||||
|
||||
map Bonus {
|
||||
discount => **discount**
|
||||
privilege => **privilege**
|
||||
}
|
||||
|
||||
Bonus::discount -> Discount
|
||||
Bonus::privilege -> Privilege
|
||||
|
||||
@enduml
|
@ -1,28 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
mongo:
|
||||
image: mongo
|
||||
ports:
|
||||
- "27020:27017"
|
||||
environment:
|
||||
- MONGO_INITDB_ROOT_USERNAME=test
|
||||
- MONGO_INITDB_ROOT_PASSWORD=test
|
||||
- MONGO_INITDB_AUTH_MECHANISM=SCRAM-SHA-1
|
||||
volumes:
|
||||
- mongo_data:/data/db
|
||||
|
||||
redis:
|
||||
image: redis:latest
|
||||
ports:
|
||||
- "6379:6379"
|
||||
environment:
|
||||
- REDIS_PASSWORD=admin
|
||||
- REDIS_DB=2
|
||||
command: [ "redis-server", "--requirepass", "admin", "--databases", "16", "--maxmemory", "2gb", "--maxmemory-policy", "allkeys-lru" ]
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
|
||||
volumes:
|
||||
mongo_data:
|
||||
redis_data:
|
24
go.mod
24
go.mod
@ -1,6 +1,8 @@
|
||||
module codeword
|
||||
|
||||
go 1.21
|
||||
go 1.21.0
|
||||
|
||||
toolchain go1.23.1
|
||||
|
||||
require (
|
||||
github.com/caarlos0/env/v8 v8.0.0
|
||||
@ -10,10 +12,10 @@ require (
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/pioz/faker v1.7.3
|
||||
github.com/rs/xid v1.5.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/twmb/franz-go v1.15.4
|
||||
go.mongodb.org/mongo-driver v1.13.1
|
||||
go.uber.org/zap v1.26.0
|
||||
go.uber.org/zap v1.27.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac
|
||||
google.golang.org/grpc v1.60.1
|
||||
google.golang.org/protobuf v1.32.0
|
||||
@ -25,18 +27,27 @@ require (
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/getkin/kin-openapi v0.122.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/google/uuid v1.4.0 // indirect
|
||||
github.com/invopop/yaml v0.3.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.16.7 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // 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/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.19 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rs/zerolog v1.33.0 // 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.50.0 // indirect
|
||||
@ -49,10 +60,11 @@ require (
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/sync v0.4.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
penahub.gitlab.yandexcloud.net/pena-services/blueprint v0.0.0-20241114114746-100696f35c90 // indirect
|
||||
)
|
||||
|
36
go.sum
36
go.sum
@ -4,6 +4,8 @@ github.com/caarlos0/env/v8 v8.0.0 h1:POhxHhSpuxrLMIdvTGARuZqR4Jjm8AYmoi/JKlcScs0
|
||||
github.com/caarlos0/env/v8 v8.0.0/go.mod h1:7K4wMY9bH0esiXSSHlfHLX5xKGQMnkH5Fk4TDSSSzfo=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
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=
|
||||
@ -11,8 +13,15 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/getkin/kin-openapi v0.122.0 h1:WB9Jbl0Hp/T79/JF9xlSW5Kl9uYdk/AWD0yAd9HOM10=
|
||||
github.com/getkin/kin-openapi v0.122.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ=
|
||||
github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
|
||||
@ -28,24 +37,35 @@ 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/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
|
||||
github.com/invopop/yaml v0.3.1/go.mod h1:PMOp3nn4/12yEZUFfmOuNHJsZToEEOwoWsT+D81KkeA=
|
||||
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=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
|
||||
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
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/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/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.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
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/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/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
@ -54,20 +74,28 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
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.19 h1:tYLzDnjDXh9qIxSTKHwXwOYmm9d887Y7Y1ZkyXYHAN4=
|
||||
github.com/pierrec/lz4/v4 v4.1.19/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
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/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/twmb/franz-go v1.15.4 h1:qBCkHaiutetnrXjAUWA99D9FEcZVMt2AYwkH3vWEQTw=
|
||||
github.com/twmb/franz-go v1.15.4/go.mod h1:rC18hqNmfo8TMc1kz7CQmHL74PLNF8KVvhflxiiJZCU=
|
||||
github.com/twmb/franz-go/pkg/kmsg v1.7.0 h1:a457IbvezYfA5UkiBvyV3zj0Is3y1i8EJgqjJYoij2E=
|
||||
@ -95,6 +123,8 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
|
||||
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
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=
|
||||
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=
|
||||
@ -119,8 +149,11 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.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.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=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -151,6 +184,7 @@ google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
@ -160,3 +194,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
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/pena-services/blueprint v0.0.0-20241114114746-100696f35c90 h1:sSDKSdjexf5e6BOOxcuODf9FrctQGtDPb/udma5nq18=
|
||||
penahub.gitlab.yandexcloud.net/pena-services/blueprint v0.0.0-20241114114746-100696f35c90/go.mod h1:NJEEmcYZDD5IzHOMvGgCWwtE0T6nNnaCBEQKPRapHts=
|
||||
|
@ -1,66 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type AuthClientDeps struct {
|
||||
AuthUrl string
|
||||
FiberClient *fiber.Client
|
||||
Logger *zap.Logger
|
||||
}
|
||||
|
||||
type AuthClient struct {
|
||||
deps AuthClientDeps
|
||||
}
|
||||
|
||||
func NewAuthClient(deps AuthClientDeps) *AuthClient {
|
||||
if deps.FiberClient == nil {
|
||||
deps.FiberClient = fiber.AcquireClient()
|
||||
}
|
||||
return &AuthClient{
|
||||
deps: deps,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *AuthClient) RefreshAuthToken(userID, signature string) (*models.RefreshResponse, error) {
|
||||
body := models.AuthRequestBody{
|
||||
UserID: userID,
|
||||
Signature: signature,
|
||||
}
|
||||
|
||||
bodyBytes, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
a.deps.Logger.Error("Failed to encode request body", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
agent := a.deps.FiberClient.Post(a.deps.AuthUrl)
|
||||
agent.Set("Content-Type", "application/json").Body(bodyBytes)
|
||||
|
||||
statusCode, resBody, errs := agent.Bytes()
|
||||
if len(errs) > 0 {
|
||||
for _, err := range errs {
|
||||
a.deps.Logger.Error("Error in exchange auth token request", zap.Error(err))
|
||||
}
|
||||
return nil, fmt.Errorf("request failed: %v", errs)
|
||||
}
|
||||
|
||||
if statusCode != fiber.StatusOK {
|
||||
errorMessage := fmt.Sprintf("received an incorrect response from the authentication service: %d", statusCode)
|
||||
a.deps.Logger.Error(errorMessage, zap.Int("status", statusCode))
|
||||
return nil, fmt.Errorf(errorMessage)
|
||||
}
|
||||
|
||||
var tokens models.RefreshResponse
|
||||
if err := json.Unmarshal(resBody, &tokens); err != nil {
|
||||
a.deps.Logger.Error("failed to unmarshal auth service response", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &tokens, nil
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
"mime/multipart"
|
||||
)
|
||||
|
||||
type RecoveryEmailSenderDeps struct {
|
||||
SmtpApiUrl string
|
||||
SmtpHost string
|
||||
SmtpPort string
|
||||
SmtpSender string
|
||||
Username string
|
||||
Password string
|
||||
ApiKey string
|
||||
FiberClient *fiber.Client
|
||||
Logger *zap.Logger
|
||||
RecoveryUrl string
|
||||
}
|
||||
|
||||
type RecoveryEmailSender struct {
|
||||
deps RecoveryEmailSenderDeps
|
||||
}
|
||||
|
||||
func NewRecoveryEmailSender(deps RecoveryEmailSenderDeps) *RecoveryEmailSender {
|
||||
if deps.FiberClient == nil {
|
||||
deps.FiberClient = fiber.AcquireClient()
|
||||
}
|
||||
return &RecoveryEmailSender{
|
||||
deps: deps,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RecoveryEmailSender) SendRecoveryEmail(email string, signature string) error {
|
||||
url := r.deps.SmtpApiUrl
|
||||
|
||||
fmt.Println(email, signature)
|
||||
|
||||
message := r.deps.RecoveryUrl + signature
|
||||
|
||||
form := new(bytes.Buffer)
|
||||
writer := multipart.NewWriter(form)
|
||||
defer writer.Close()
|
||||
|
||||
fields := map[string]string{
|
||||
"from": r.deps.SmtpSender,
|
||||
"to": email,
|
||||
"subject": "Восстановление доступа",
|
||||
"html": message,
|
||||
}
|
||||
|
||||
for key, value := range fields {
|
||||
if err := writer.WriteField(key, value); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := writer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req := r.deps.FiberClient.Post(url).Body(form.Bytes()).ContentType(writer.FormDataContentType())
|
||||
if r.deps.ApiKey != "" {
|
||||
req.Set("Authorization", r.deps.ApiKey)
|
||||
}
|
||||
|
||||
statusCode, body, errs := req.Bytes()
|
||||
if errs != nil {
|
||||
r.deps.Logger.Error("Error sending request", zap.Error(errs[0]))
|
||||
return errs[0]
|
||||
}
|
||||
|
||||
if statusCode != fiber.StatusOK {
|
||||
err := fmt.Errorf("the SMTP service returned an error: %s Response body: %s", statusCode, body)
|
||||
r.deps.Logger.Error("Error sending email", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
//r.deps.Logger.Info("Recovery email sent", zap.String("email", email))
|
||||
return nil
|
||||
}
|
@ -1,159 +1,84 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"codeword/internal/controller/promocode"
|
||||
"codeword/internal/controller/recovery"
|
||||
"codeword/internal/initialize"
|
||||
"codeword/internal/controllers"
|
||||
"codeword/internal/repository"
|
||||
httpserver "codeword/internal/server/http"
|
||||
"codeword/internal/services"
|
||||
"codeword/internal/worker/purge_worker"
|
||||
"codeword/internal/worker/recovery_worker"
|
||||
"codeword/pkg/closer"
|
||||
"codeword/utils"
|
||||
"codeword/internal/server/http"
|
||||
"codeword/internal/service"
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/twmb/franz-go/pkg/kgo"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
|
||||
type Config struct {
|
||||
AppName string `env:"APP_NAME"`
|
||||
}
|
||||
|
||||
func Run(ctx context.Context, config Config, logger *zap.Logger) error {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Error("Recovered from a panic", zap.Any("error", r))
|
||||
}
|
||||
}()
|
||||
|
||||
logger.Info("Starting application", zap.String("AppName", cfg.AppName))
|
||||
logger.Info("App started", zap.Any("config", config))
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
shutdownGroup := closer.NewCloserGroup()
|
||||
// Инициализация репозиториев
|
||||
|
||||
mdb, err := initialize.MongoDB(ctx, cfg)
|
||||
if err != nil {
|
||||
logger.Error("Failed to initialize MongoDB", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
promocodeRepository := repository.NewPromocodeRepository()
|
||||
|
||||
if err = initialize.InitDatabaseIndexes(ctx, mdb, logger); err != nil {
|
||||
logger.Error("Failed to initialize db indexes", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
recoverRepository := repository.NewRecoverRepository()
|
||||
|
||||
kafkaTariffClient, err := kgo.NewClient(
|
||||
kgo.SeedBrokers(cfg.KafkaBrokers),
|
||||
kgo.ConsumeResetOffset(kgo.NewOffset().AtStart()),
|
||||
kgo.DefaultProduceTopic(cfg.KafkaTopic),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statsRepository := repository.NewStatsRepository()
|
||||
|
||||
err = kafkaTariffClient.Ping(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Инициализация сервисов
|
||||
|
||||
discountRpcClient, err := initialize.DiscountGRPCClient(cfg.DiscountServiceAddress)
|
||||
if err != nil {
|
||||
logger.Error("failed to connect to discount service", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
promocodeService := service.NewPromocodeService(promocodeRepository)
|
||||
|
||||
brokers := initialize.NewBrokers(initialize.BrokersDeps{
|
||||
Logger: logger,
|
||||
TariffClient: kafkaTariffClient,
|
||||
Topic: cfg.KafkaTopic,
|
||||
})
|
||||
recoverService := service.NewRecoverService(recoverRepository)
|
||||
|
||||
rdb, err := initialize.Redis(ctx, cfg)
|
||||
if err != nil {
|
||||
logger.Error("failed to connect to redis db", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
statsService := service.NewStatsService(statsRepository)
|
||||
|
||||
encrypt := initialize.Encrypt(cfg)
|
||||
// Инициализация контроллеров
|
||||
|
||||
promoCodeRepo := repository.NewPromoCodeRepository(mdb.Collection("promoCodes"))
|
||||
statsRepo := repository.NewStatsRepository(repository.Deps{Rdb: nil, Mdb: mdb.Collection("promoStats")})
|
||||
codewordRepo := repository.NewCodewordRepository(repository.Deps{Rdb: rdb, Mdb: mdb.Collection("codeword")})
|
||||
userRepo := repository.NewUserRepository(repository.Deps{Rdb: nil, Mdb: mdb.Collection("users")})
|
||||
promocodeController := controllers.NewPromocodeController(promocodeService)
|
||||
|
||||
recoveryEmailSender := initialize.RecoveryEmailSender(cfg, logger)
|
||||
authClient := initialize.AuthClient(cfg, logger)
|
||||
recoverController := controllers.NewRecoverController(recoverService)
|
||||
|
||||
recoveryService := services.NewRecoveryService(services.Deps{
|
||||
Logger: logger,
|
||||
CodewordRepository: codewordRepo,
|
||||
UserRepository: userRepo,
|
||||
Encrypt: encrypt,
|
||||
AuthClient: authClient,
|
||||
})
|
||||
statsController := controllers.NewStatsController(statsService)
|
||||
|
||||
promoService := services.NewPromoCodeService(services.PromoDeps{
|
||||
Logger: logger,
|
||||
PromoCodeRepo: promoCodeRepo,
|
||||
StatsRepo: statsRepo,
|
||||
Kafka: brokers.TariffProducer,
|
||||
DiscountClient: discountRpcClient,
|
||||
})
|
||||
// Создание сервера
|
||||
server := http.NewServer(http.ServerConfig{
|
||||
Controllers: []http.Controller{
|
||||
|
||||
jwtUtil := utils.NewJWT(&cfg)
|
||||
authMiddleware := utils.NewAuthenticator(jwtUtil)
|
||||
promocodeController,
|
||||
|
||||
recoveryController := recovery.NewRecoveryController(logger, recoveryService, cfg.DefaultRedirectionURL)
|
||||
promoCodeController := promocode.NewPromoCodeController(promocode.Deps{Logger: logger, PromoCodeService: promoService, AuthMiddleware: authMiddleware})
|
||||
recoverController,
|
||||
|
||||
recoveryWC := recovery_worker.NewRecoveryWC(recovery_worker.Deps{
|
||||
Logger: logger,
|
||||
Redis: rdb,
|
||||
EmailSender: recoveryEmailSender,
|
||||
Mongo: mdb.Collection("codeword"),
|
||||
})
|
||||
|
||||
purgeWC := purge_worker.NewPurgeWC(purge_worker.Deps{
|
||||
Logger: logger,
|
||||
Mongo: mdb.Collection("codeword"),
|
||||
})
|
||||
|
||||
go recoveryWC.Start(ctx)
|
||||
go purgeWC.Start(ctx)
|
||||
|
||||
server := httpserver.NewServer(httpserver.ServerConfig{
|
||||
Logger: logger,
|
||||
Controllers: []httpserver.Controller{recoveryController, promoCodeController},
|
||||
statsController,
|
||||
},
|
||||
})
|
||||
|
||||
go func() {
|
||||
if err := server.Start(cfg.HTTPHost + ":" + cfg.HTTPPort); err != nil {
|
||||
err := server.Start("Host + : + Port")
|
||||
if err != nil {
|
||||
logger.Error("Server startup error", zap.Error(err))
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
// Вывод маршрутов
|
||||
server.ListRoutes()
|
||||
|
||||
shutdownGroup.Add(closer.CloserFunc(server.Shutdown))
|
||||
shutdownGroup.Add(closer.CloserFunc(mdb.Client().Disconnect))
|
||||
shutdownGroup.Add(closer.CloserFunc(recoveryWC.Stop))
|
||||
shutdownGroup.Add(closer.CloserFunc(purgeWC.Stop))
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer timeoutCancel()
|
||||
if err := shutdownGroup.Call(timeoutCtx); err != nil {
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
logger.Error("Shutdown timed out", zap.Error(err))
|
||||
} else {
|
||||
logger.Error("Failed to shutdown services gracefully", zap.Error(err))
|
||||
}
|
||||
return err
|
||||
}
|
||||
logger.Info("App shutting down gracefully")
|
||||
|
||||
//TODO
|
||||
// Остановка сервера
|
||||
|
||||
logger.Info("Application has stopped")
|
||||
return nil
|
||||
}
|
||||
|
@ -1,206 +0,0 @@
|
||||
package promocode
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/repository"
|
||||
"codeword/internal/services"
|
||||
"errors"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
Logger *zap.Logger
|
||||
PromoCodeService *services.PromoCodeService
|
||||
AuthMiddleware func(*fiber.Ctx) error
|
||||
}
|
||||
|
||||
type PromoCodeController struct {
|
||||
logger *zap.Logger
|
||||
promoCodeService *services.PromoCodeService
|
||||
authMiddleware func(*fiber.Ctx) error
|
||||
}
|
||||
|
||||
func NewPromoCodeController(deps Deps) *PromoCodeController {
|
||||
return &PromoCodeController{
|
||||
logger: deps.Logger,
|
||||
promoCodeService: deps.PromoCodeService,
|
||||
authMiddleware: deps.AuthMiddleware,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PromoCodeController) CreatePromoCode(c *fiber.Ctx) error {
|
||||
var req models.PromoCode
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
if req.Codeword == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "codeword is required"})
|
||||
}
|
||||
|
||||
createdPromoCode, err := p.promoCodeService.CreatePromoCode(c.Context(), &req)
|
||||
if err != nil {
|
||||
p.logger.Error("Failed to create promocode", zap.Error(err))
|
||||
|
||||
if errors.Is(err, repository.ErrDuplicateCodeword) {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Duplicate Codeword"})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(createdPromoCode)
|
||||
}
|
||||
|
||||
func (p *PromoCodeController) EditPromoCode(c *fiber.Ctx) error {
|
||||
var req models.ReqEditPromoCode
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
if req.ID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "promocode ID is required"})
|
||||
}
|
||||
|
||||
editedPromoCode, err := p.promoCodeService.EditPromoCode(c.Context(), &req)
|
||||
if err != nil {
|
||||
p.logger.Error("Failed to edit promocode", zap.Error(err))
|
||||
|
||||
if errors.Is(err, repository.ErrPromoCodeNotFound) {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(editedPromoCode)
|
||||
}
|
||||
|
||||
func (p *PromoCodeController) GetList(c *fiber.Ctx) error {
|
||||
var req models.GetPromoCodesListReq
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
promoCodes, count, err := p.promoCodeService.GetPromoCodesList(c.Context(), &req)
|
||||
if err != nil {
|
||||
p.logger.Error("Failed to retrieve promocode list", zap.Error(err))
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
|
||||
resp := models.GetPromoCodesListResp{
|
||||
Count: count,
|
||||
Items: promoCodes,
|
||||
}
|
||||
return c.Status(fiber.StatusOK).JSON(resp)
|
||||
}
|
||||
|
||||
func (p *PromoCodeController) Activate(c *fiber.Ctx) error {
|
||||
err := p.authMiddleware(c)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err})
|
||||
}
|
||||
|
||||
userID := c.Locals(models.AuthJWTDecodedUserIDKey).(string)
|
||||
if userID == "" {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "failed to get jwt payload"})
|
||||
}
|
||||
|
||||
var req models.ActivateReq
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
if req.Codeword == "" && req.FastLink == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "codeword or fastlink is required"})
|
||||
}
|
||||
|
||||
greetings, err := p.promoCodeService.ActivatePromo(c.Context(), &req, userID)
|
||||
if err != nil {
|
||||
p.logger.Error("Failed to activate promocode", zap.Error(err))
|
||||
|
||||
switch {
|
||||
case errors.Is(err, repository.ErrPromoCodeNotFound):
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"})
|
||||
case errors.Is(err, repository.ErrPromoCodeAlreadyActivated):
|
||||
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "PromoCode already activated"})
|
||||
case errors.Is(err, repository.ErrPromoCodeExpired):
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
|
||||
case errors.Is(err, repository.ErrPromoCodeExhausted):
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode exhausted"})
|
||||
default:
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(models.ActivateResp{Greetings: greetings})
|
||||
}
|
||||
|
||||
func (p *PromoCodeController) Delete(c *fiber.Ctx) error {
|
||||
promoCodeID := c.Params("promocodeID")
|
||||
|
||||
if promoCodeID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode ID is required"})
|
||||
}
|
||||
|
||||
err := p.promoCodeService.DeletePromoCode(c.Context(), promoCodeID)
|
||||
if err != nil {
|
||||
p.logger.Error("Failed to delete promocode", zap.Error(err))
|
||||
|
||||
if errors.Is(err, repository.ErrPromoCodeNotFound) {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
func (p *PromoCodeController) CreateFastLink(c *fiber.Ctx) error {
|
||||
var req struct {
|
||||
PromoCodeID string `json:"id"`
|
||||
}
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
if req.PromoCodeID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode ID is required"})
|
||||
}
|
||||
|
||||
fastLink, err := p.promoCodeService.CreateFastLink(c.Context(), req.PromoCodeID)
|
||||
if err != nil {
|
||||
p.logger.Error("Failed to create fastlink", zap.Error(err))
|
||||
|
||||
if errors.Is(err, repository.ErrPromoCodeNotFound) {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "PromoCode not found"})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(fiber.Map{"fastlink": fastLink})
|
||||
}
|
||||
|
||||
func (p *PromoCodeController) GetStats(c *fiber.Ctx) error {
|
||||
var req struct {
|
||||
PromoCodeID string `json:"id"`
|
||||
}
|
||||
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
if req.PromoCodeID == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode ID is required"})
|
||||
}
|
||||
|
||||
promoStats, err := p.promoCodeService.GetStats(c.Context(), req.PromoCodeID)
|
||||
if err != nil {
|
||||
p.logger.Error("Failed getting promo stats", zap.Error(err))
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
return c.Status(fiber.StatusOK).JSON(promoStats)
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package promocode
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
|
||||
func (p *PromoCodeController) Register(router fiber.Router) {
|
||||
router.Post("/create", p.CreatePromoCode)
|
||||
router.Put("/edit", p.EditPromoCode)
|
||||
router.Post("/getList", p.GetList)
|
||||
router.Post("/activate", p.Activate)
|
||||
router.Delete("/:promocodeID", p.Delete)
|
||||
router.Post("/fastlink", p.CreateFastLink)
|
||||
router.Get("/stats", p.GetStats)
|
||||
|
||||
}
|
||||
|
||||
func (p *PromoCodeController) Name() string {
|
||||
return "promocode"
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
package recovery
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/repository"
|
||||
"codeword/internal/services"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RecoveryController struct {
|
||||
logger *zap.Logger
|
||||
service *services.RecoveryService
|
||||
defaultURL string
|
||||
}
|
||||
|
||||
func NewRecoveryController(logger *zap.Logger, service *services.RecoveryService, defaultRedirectionURL string) *RecoveryController {
|
||||
return &RecoveryController{
|
||||
logger: logger,
|
||||
service: service,
|
||||
defaultURL: defaultRedirectionURL,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RecoveryController) HandleLiveness(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
func (r *RecoveryController) HandlePingDB(c *fiber.Ctx) error {
|
||||
startTime := time.Now()
|
||||
if err := r.service.Ping(c.Context()); err != nil {
|
||||
r.logger.Error("Failed to ping the database", zap.Error(err))
|
||||
return c.Status(fiber.StatusServiceUnavailable).SendString("DB ping failed")
|
||||
}
|
||||
duration := time.Since(startTime)
|
||||
|
||||
durationMillis := duration.Milliseconds()
|
||||
responseMessage := fmt.Sprintf("DB ping success - Time taken: %d ms", durationMillis)
|
||||
|
||||
return c.Status(fiber.StatusOK).SendString(responseMessage)
|
||||
}
|
||||
|
||||
func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
|
||||
var req models.RecoveryRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
r.logger.Error("Failed to parse recovery request", zap.Error(err))
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Bad Request"})
|
||||
}
|
||||
|
||||
if req.Email == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "email is required"})
|
||||
}
|
||||
|
||||
referralURL := c.Get("Referrer")
|
||||
|
||||
if req.RedirectionURL == "" && referralURL != "" {
|
||||
req.RedirectionURL = referralURL
|
||||
} else if req.RedirectionURL == "" {
|
||||
req.RedirectionURL = r.defaultURL
|
||||
}
|
||||
|
||||
user, err := r.service.FindUserByEmail(c.Context(), req.Email)
|
||||
if err != nil || user == nil {
|
||||
r.logger.Error("Failed to find user by email", zap.Error(err))
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||||
}
|
||||
|
||||
key, err := r.service.GenerateKey()
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to generate key", zap.Error(err))
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
|
||||
signUrl := req.RedirectionURL
|
||||
sign := base64.URLEncoding.EncodeToString(key)
|
||||
|
||||
id, err := r.service.StoreRecoveryRecord(c.Context(), models.StoreRecDeps{UserID: user.ID.Hex(), Email: user.Email, Key: sign, Url: signUrl})
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to store recovery record", zap.Error(err))
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
|
||||
signWithID := sign + id // подпись с id записи
|
||||
|
||||
err = r.service.RecoveryEmailTask(c.Context(), models.RecEmailDeps{UserID: user.ID.Hex(), Email: req.Email, SignWithID: signWithID, ID: id})
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to send recovery email", zap.Error(err))
|
||||
|
||||
if errors.Is(err, repository.ErrAlreadyReported) {
|
||||
return c.Status(fiber.StatusAlreadyReported).JSON(fiber.Map{"error": "already reported"})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Recovery email sent successfully"})
|
||||
|
||||
}
|
||||
|
||||
func (r *RecoveryController) HandleRecoveryLink(c *fiber.Ctx) error {
|
||||
sign := c.Params("sign")
|
||||
|
||||
record, err := r.service.GetRecoveryRecord(c.Context(), sign)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to get recovery record", zap.Error(err))
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
|
||||
if time.Since(record.CreatedAt) > 15*time.Minute {
|
||||
r.logger.Error("Recovery link expired", zap.String("signature", sign))
|
||||
return c.Status(fiber.StatusNotAcceptable).JSON(fiber.Map{"error": "Recovery link expired"})
|
||||
}
|
||||
|
||||
tokens, err := r.service.ExchangeForTokens(record.UserID, record.Sign)
|
||||
if err != nil {
|
||||
r.logger.Error("Failed to exchange recovery link for tokens", zap.Error(err))
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
|
||||
c.Cookie(&fiber.Cookie{
|
||||
Name: "refreshToken",
|
||||
Value: tokens["refreshToken"],
|
||||
Domain: ".pena.digital",
|
||||
Expires: time.Now().Add(30 * 24 * time.Hour),
|
||||
Secure: true,
|
||||
HTTPOnly: true,
|
||||
})
|
||||
|
||||
return c.Redirect(record.SignUrl + "?auth=" + tokens["accessToken"])
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package recovery
|
||||
|
||||
import "github.com/gofiber/fiber/v2"
|
||||
|
||||
func (r *RecoveryController) Register(router fiber.Router) {
|
||||
router.Get("/liveness", r.HandleLiveness)
|
||||
router.Get("/readiness", r.HandlePingDB)
|
||||
router.Post("/recover", r.HandleRecoveryRequest)
|
||||
router.Get("/recover/:sign", r.HandleRecoveryLink)
|
||||
}
|
||||
|
||||
func (r *RecoveryController) Name() string {
|
||||
return ""
|
||||
}
|
135
internal/controllers/promocode.go
Normal file
135
internal/controllers/promocode.go
Normal file
@ -0,0 +1,135 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/service"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type PromocodeController struct {
|
||||
PromocodeService *service.PromocodeService
|
||||
}
|
||||
|
||||
func NewPromocodeController(service *service.PromocodeService) *PromocodeController {
|
||||
return &PromocodeController{
|
||||
PromocodeService: service,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *PromocodeController) Register(router fiber.Router) {
|
||||
|
||||
router.Post("/promocode/activate", c.Activate)
|
||||
|
||||
router.Post("/promocode/getList", c.Getlist)
|
||||
|
||||
router.Delete("/promocode/{promocodeID}", c.Delete)
|
||||
|
||||
router.Post("/promocode/create", c.Createpromocode)
|
||||
|
||||
router.Put("/promocode/edit", c.Editpromocode)
|
||||
|
||||
router.Post("/promocode/fastlink", c.Createfastlink)
|
||||
|
||||
}
|
||||
|
||||
func (c *PromocodeController) Name() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *PromocodeController) Activate(ctx *fiber.Ctx) error {
|
||||
// Обработчик для метода Activate
|
||||
|
||||
var request models.ActivateReq
|
||||
if err := ctx.BodyParser(&request); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
response, err := c.PromocodeService.Activate(ctx.Context(), &request)
|
||||
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
return ctx.Status(fiber.StatusOK).JSON(response)
|
||||
|
||||
}
|
||||
|
||||
func (c *PromocodeController) Getlist(ctx *fiber.Ctx) error {
|
||||
// Обработчик для метода Getlist
|
||||
|
||||
var request models.GetPromoCodesListReq
|
||||
if err := ctx.BodyParser(&request); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
response, err := c.PromocodeService.Getlist(ctx.Context(), &request)
|
||||
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
return ctx.Status(fiber.StatusOK).JSON(response)
|
||||
|
||||
}
|
||||
|
||||
func (c *PromocodeController) Delete(ctx *fiber.Ctx) error {
|
||||
// Обработчик для метода Delete
|
||||
|
||||
err := c.PromocodeService.Delete(ctx.Context())
|
||||
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
return ctx.SendStatus(fiber.StatusOK)
|
||||
|
||||
}
|
||||
|
||||
func (c *PromocodeController) Createpromocode(ctx *fiber.Ctx) error {
|
||||
// Обработчик для метода Createpromocode
|
||||
|
||||
var request models.PromoCodeReq
|
||||
if err := ctx.BodyParser(&request); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
response, err := c.PromocodeService.Createpromocode(ctx.Context(), &request)
|
||||
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
return ctx.Status(fiber.StatusOK).JSON(response)
|
||||
|
||||
}
|
||||
|
||||
func (c *PromocodeController) Editpromocode(ctx *fiber.Ctx) error {
|
||||
// Обработчик для метода Editpromocode
|
||||
|
||||
var request models.EditPromoCodeReq
|
||||
if err := ctx.BodyParser(&request); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
response, err := c.PromocodeService.Editpromocode(ctx.Context(), &request)
|
||||
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
return ctx.Status(fiber.StatusOK).JSON(response)
|
||||
|
||||
}
|
||||
|
||||
func (c *PromocodeController) Createfastlink(ctx *fiber.Ctx) error {
|
||||
// Обработчик для метода Createfastlink
|
||||
|
||||
var request models.CreateFastLinkReq
|
||||
if err := ctx.BodyParser(&request); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
response, err := c.PromocodeService.Createfastlink(ctx.Context(), &request)
|
||||
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
return ctx.Status(fiber.StatusOK).JSON(response)
|
||||
|
||||
}
|
87
internal/controllers/recover.go
Normal file
87
internal/controllers/recover.go
Normal file
@ -0,0 +1,87 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/service"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type RecoverController struct {
|
||||
RecoverService *service.RecoverService
|
||||
}
|
||||
|
||||
func NewRecoverController(service *service.RecoverService) *RecoverController {
|
||||
return &RecoverController{
|
||||
RecoverService: service,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RecoverController) Register(router fiber.Router) {
|
||||
|
||||
router.Get("/liveness", c.Liveness)
|
||||
|
||||
router.Post("/recover", c.Recovery)
|
||||
|
||||
router.Get("/recover/{sign}", c.Recoverylink)
|
||||
|
||||
router.Get("/readiness", c.Readiness)
|
||||
|
||||
}
|
||||
|
||||
func (c *RecoverController) Name() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *RecoverController) Liveness(ctx *fiber.Ctx) error {
|
||||
// Обработчик для метода Liveness
|
||||
|
||||
err := c.RecoverService.Liveness(ctx.Context())
|
||||
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
return ctx.SendStatus(fiber.StatusOK)
|
||||
|
||||
}
|
||||
|
||||
func (c *RecoverController) Recovery(ctx *fiber.Ctx) error {
|
||||
// Обработчик для метода Recovery
|
||||
|
||||
var request models.RecoveryReq
|
||||
if err := ctx.BodyParser(&request); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
err := c.RecoverService.Recovery(ctx.Context(), &request)
|
||||
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
return ctx.SendStatus(fiber.StatusOK)
|
||||
|
||||
}
|
||||
|
||||
func (c *RecoverController) Recoverylink(ctx *fiber.Ctx) error {
|
||||
// Обработчик для метода Recoverylink
|
||||
|
||||
err := c.RecoverService.Recoverylink(ctx.Context())
|
||||
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
return ctx.SendStatus(fiber.StatusOK)
|
||||
|
||||
}
|
||||
|
||||
func (c *RecoverController) Readiness(ctx *fiber.Ctx) error {
|
||||
// Обработчик для метода Readiness
|
||||
|
||||
err := c.RecoverService.Readiness(ctx.Context())
|
||||
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
return ctx.SendStatus(fiber.StatusOK)
|
||||
|
||||
}
|
45
internal/controllers/stats.go
Normal file
45
internal/controllers/stats.go
Normal file
@ -0,0 +1,45 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/service"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
type StatsController struct {
|
||||
StatsService *service.StatsService
|
||||
}
|
||||
|
||||
func NewStatsController(service *service.StatsService) *StatsController {
|
||||
return &StatsController{
|
||||
StatsService: service,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *StatsController) Register(router fiber.Router) {
|
||||
|
||||
router.Get("/promocode/stats", c.Getstats)
|
||||
|
||||
}
|
||||
|
||||
func (c *StatsController) Name() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c *StatsController) Getstats(ctx *fiber.Ctx) error {
|
||||
// Обработчик для метода Getstats
|
||||
|
||||
var request models.PromoCodeStatsReq
|
||||
if err := ctx.BodyParser(&request); err != nil {
|
||||
return ctx.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
|
||||
}
|
||||
|
||||
response, err := c.StatsService.Getstats(ctx.Context(), &request)
|
||||
|
||||
if err != nil {
|
||||
return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error")
|
||||
}
|
||||
return ctx.Status(fiber.StatusOK).JSON(response)
|
||||
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
package errors
|
||||
|
||||
// пока не нужен
|
@ -1,30 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"codeword/internal/adapters/client"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func RecoveryEmailSender(cfg Config, logger *zap.Logger) *client.RecoveryEmailSender {
|
||||
return client.NewRecoveryEmailSender(client.RecoveryEmailSenderDeps{
|
||||
SmtpApiUrl: cfg.SmtpApiUrl,
|
||||
SmtpHost: cfg.SmtpHost,
|
||||
SmtpPort: cfg.SmtpPort,
|
||||
SmtpSender: cfg.SmtpSender,
|
||||
Username: cfg.SmtpUsername,
|
||||
Password: cfg.SmtpPassword,
|
||||
ApiKey: cfg.SmtpApiKey,
|
||||
FiberClient: &fiber.Client{},
|
||||
Logger: logger,
|
||||
RecoveryUrl: cfg.RecoveryUrl,
|
||||
})
|
||||
}
|
||||
|
||||
func AuthClient(cfg Config, logger *zap.Logger) *client.AuthClient {
|
||||
return client.NewAuthClient(client.AuthClientDeps{
|
||||
AuthUrl: cfg.AuthURL,
|
||||
Logger: logger,
|
||||
FiberClient: &fiber.Client{},
|
||||
})
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"github.com/caarlos0/env/v8"
|
||||
"github.com/joho/godotenv"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
AppName string `env:"APP_NAME" envDefault:"codeword"`
|
||||
HTTPHost string `env:"HTTP_HOST" envDefault:"localhost"`
|
||||
HTTPPort string `env:"HTTP_PORT" envDefault:"3000"`
|
||||
MongoHost string `env:"MONGO_HOST" envDefault:"127.0.0.1"`
|
||||
MongoPort string `env:"MONGO_PORT" envDefault:"27020"`
|
||||
MongoUser string `env:"MONGO_USER" envDefault:"test"`
|
||||
MongoPassword string `env:"MONGO_PASSWORD" envDefault:"test"`
|
||||
MongoDatabase string `env:"MONGO_DB" envDefault:"admin"`
|
||||
MongoAuth string `env:"MONGO_AUTH" envDefault:"admin"`
|
||||
PublicCurveKey string `env:"PUBLIC_CURVE_KEY"`
|
||||
PrivateCurveKey string `env:"PRIVATE_CURVE_KEY"`
|
||||
SignSecret string `env:"SIGN_SECRET"`
|
||||
RedisAddr string `env:"REDIS_ADDR" envDefault:"localhost:6379"`
|
||||
RedisPassword string `env:"REDIS_PASS" envDefault:"admin"`
|
||||
RedisDB int `env:"REDIS_DB" envDefault:"2"`
|
||||
SmtpApiUrl string `env:"SMTP_API_URL"`
|
||||
SmtpHost string `env:"SMTP_HOST"`
|
||||
SmtpPort string `env:"SMTP_PORT"`
|
||||
SmtpUsername string `env:"SMTP_UNAME"`
|
||||
SmtpPassword string `env:"SMTP_PASS"`
|
||||
SmtpApiKey string `env:"SMTP_API_KEY"`
|
||||
SmtpSender string `env:"SMTP_SENDER"`
|
||||
DefaultRedirectionURL string `env:"DEFAULT_REDIRECTION_URL"`
|
||||
AuthURL string `env:"AUTH_EXCHANGE_URL"`
|
||||
KafkaBrokers string `env:"KAFKA_BROKERS"`
|
||||
KafkaTopic string `env:"KAFKA_TOPIC_TARIFF"`
|
||||
DiscountServiceAddress string `env:"DISCOUNT_ADDRESS"`
|
||||
RecoveryUrl string `env:"RECOVERY_URL"`
|
||||
PrivateKey string `env:"JWT_PRIVATE_KEY"`
|
||||
PublicKey string `env:"JWT_PUBLIC_KEY,required"`
|
||||
Issuer string `env:"JWT_ISSUER,required"`
|
||||
Audience string `env:"JWT_AUDIENCE,required"`
|
||||
}
|
||||
|
||||
func LoadConfig() (*Config, error) {
|
||||
if err := godotenv.Load(); err != nil {
|
||||
log.Print("No .env file found")
|
||||
}
|
||||
var config Config
|
||||
if err := env.Parse(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &config, nil
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"codeword/internal/utils/encrypt"
|
||||
)
|
||||
|
||||
func Encrypt(cfg Config) *encrypt.Encrypt {
|
||||
return encrypt.New(&encrypt.EncryptDeps{
|
||||
PublicKey: cfg.PublicCurveKey,
|
||||
PrivateKey: cfg.PrivateCurveKey,
|
||||
SignSecret: cfg.SignSecret,
|
||||
})
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"codeword/internal/proto/discount"
|
||||
"context"
|
||||
"google.golang.org/grpc"
|
||||
"time"
|
||||
)
|
||||
|
||||
func DiscountGRPCClient(address string) (discount.DiscountServiceClient, error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
options := []grpc.DialOption{
|
||||
grpc.WithInsecure(),
|
||||
//grpc.WithBlock(),
|
||||
}
|
||||
|
||||
conn, err := grpc.DialContext(ctx, address, options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
discountClient := discount.NewDiscountServiceClient(conn)
|
||||
return discountClient, nil
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"codeword/internal/kafka/tariff"
|
||||
"github.com/twmb/franz-go/pkg/kgo"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type BrokersDeps struct {
|
||||
Logger *zap.Logger
|
||||
TariffClient *kgo.Client
|
||||
Topic string
|
||||
}
|
||||
|
||||
type Brokers struct {
|
||||
TariffProducer *tariff.Producer
|
||||
}
|
||||
|
||||
func NewBrokers(deps BrokersDeps) *Brokers {
|
||||
return &Brokers{
|
||||
TariffProducer: tariff.NewProducer(tariff.ProducerDeps{
|
||||
Logger: deps.Logger,
|
||||
Client: deps.TariffClient,
|
||||
Topic: deps.Topic,
|
||||
}),
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"codeword/internal/repository"
|
||||
"context"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.uber.org/zap"
|
||||
mdb "penahub.gitlab.yandexcloud.net/backend/penahub_common/mongo"
|
||||
"time"
|
||||
)
|
||||
|
||||
func MongoDB(ctx context.Context, cfg Config) (*mongo.Database, error) {
|
||||
dbConfig := &mdb.Configuration{
|
||||
Host: cfg.MongoHost,
|
||||
Port: cfg.MongoPort,
|
||||
User: cfg.MongoUser,
|
||||
Password: cfg.MongoPassword,
|
||||
DatabaseName: cfg.MongoDatabase,
|
||||
Auth: cfg.MongoAuth,
|
||||
}
|
||||
|
||||
newCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
mongoDeps := &mdb.ConnectDeps{
|
||||
Configuration: dbConfig,
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
db, err := mdb.Connect(newCtx, mongoDeps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = db.Client().Ping(newCtx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func InitDatabaseIndexes(ctx context.Context, mdb *mongo.Database, logger *zap.Logger) error {
|
||||
if err := repository.InitPromoCodeIndexes(ctx, mdb.Collection("promoCodes")); err != nil {
|
||||
logger.Error("Failed to initialize promoCodes indexes", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
|
||||
func Redis(ctx context.Context, cfg Config) (*redis.Client, error) {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: cfg.RedisAddr,
|
||||
Password: cfg.RedisPassword,
|
||||
DB: cfg.RedisDB,
|
||||
})
|
||||
|
||||
status := rdb.Ping(ctx)
|
||||
if err := status.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return rdb, nil
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package tariff
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/utils/transfer"
|
||||
"context"
|
||||
"github.com/twmb/franz-go/pkg/kgo"
|
||||
"go.uber.org/zap"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"log"
|
||||
)
|
||||
|
||||
type ProducerDeps struct {
|
||||
Logger *zap.Logger
|
||||
Client *kgo.Client
|
||||
Topic string
|
||||
}
|
||||
|
||||
type Producer struct {
|
||||
logger *zap.Logger
|
||||
client *kgo.Client
|
||||
topic string
|
||||
}
|
||||
|
||||
func NewProducer(deps ProducerDeps) *Producer {
|
||||
if deps.Logger == nil {
|
||||
log.Panicln("logger is nil on <NewTariffProducer>")
|
||||
}
|
||||
|
||||
if deps.Client == nil {
|
||||
log.Panicln("Kafka client is nil on <NewTariffProducer>")
|
||||
}
|
||||
|
||||
return &Producer{
|
||||
logger: deps.Logger,
|
||||
client: deps.Client,
|
||||
topic: deps.Topic,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Producer) Send(ctx context.Context, userID string, tariff *models.Tariff) error {
|
||||
bytes, err := proto.Marshal(transfer.TariffModelToProtoMessage(userID, tariff))
|
||||
if err != nil {
|
||||
p.logger.Error("failed to marshal tariff model", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// упростил, возможно зря, но теперь возвращаем одну ошибку, просто прерываем цикл при первой встретившейся ошибке
|
||||
err = p.client.ProduceSync(ctx, &kgo.Record{Topic: p.topic, Value: bytes}).FirstErr()
|
||||
if err != nil {
|
||||
p.logger.Error("failed to send tariff to Kafka", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
8
internal/models/activatereq.go
Normal file
8
internal/models/activatereq.go
Normal file
@ -0,0 +1,8 @@
|
||||
package models
|
||||
|
||||
type ActivateReq struct {
|
||||
/* - Кодовое слово для активации промокода*/
|
||||
Codeword string `json:"codeword"`
|
||||
/* - Быстрая ссылка для активации промокода*/
|
||||
Fastlink string `json:"fastLink"`
|
||||
}
|
6
internal/models/activateresp.go
Normal file
6
internal/models/activateresp.go
Normal file
@ -0,0 +1,6 @@
|
||||
package models
|
||||
|
||||
type ActivateResp struct {
|
||||
/* - Слово успешной активации промокода*/
|
||||
Greetings string `json:"greetings"`
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
package models
|
||||
|
||||
type AuthRequestBody struct {
|
||||
UserID string `json:"userId"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
type RefreshResponse struct {
|
||||
AccessToken string `json:"accessToken"`
|
||||
RefreshToken string `json:"refreshToken"`
|
||||
}
|
||||
|
||||
const AuthJWTDecodedUserIDKey = "userID"
|
||||
const AuthJWTDecodedAccessTokenKey = "access-token"
|
@ -1,79 +1,8 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PromoCode struct {
|
||||
ID primitive.ObjectID `json:"id" bson:"_id"`
|
||||
Codeword string `json:"codeword" bson:"codeword"` // то, что будет вводить пользователь, чтобы получить плюшки
|
||||
Description string `json:"description" bson:"description"` // описание, необходимое менеджеру в админке
|
||||
Greetings string `json:"greetings" bson:"greetings"` // текст, выдаваемый пользователю в ответ на активацию промокода
|
||||
DueTo int64 `json:"dueTo" bson:"dueTo"` // таймштамп времени окончания работы активации промокода
|
||||
ActivationCount int64 `json:"activationCount" bson:"activationCount"` // предел количества активаций промокода
|
||||
Bonus struct {
|
||||
Privilege struct {
|
||||
PrivilegeID string `json:"privilegeID" bson:"privilegeID"` // айдишник привилегии, которая будет выдаваться
|
||||
Amount uint64 `json:"amount" bson:"amount"` // количество
|
||||
} `json:"privilege" bson:"privilege"`
|
||||
Discount struct {
|
||||
Layer uint32 `json:"layer" bson:"layer"` // 1|2
|
||||
Factor float64 `json:"factor" bson:"factor"` // процент скидки, вернее множитель, при котором достигается этот процент скидки
|
||||
Target string `json:"target" bson:"target"` // PrivilegeID или ServiceKey в зависимости от слоя
|
||||
Threshold int64 `json:"threshold" bson:"threshold"` // граничное значение, при пересечении которого применяется эта скидка
|
||||
} `json:"discount" bson:"discount"`
|
||||
} `json:"bonus" bson:"bonus"`
|
||||
Outdated bool `json:"outdated" bson:"outdated"`
|
||||
OffLimit bool `json:"offLimit" bson:"offLimit"`
|
||||
Delete bool `json:"delete" bson:"delete"`
|
||||
CreatedAt time.Time `json:"createdAt" bson:"createdAt"`
|
||||
FastLinks []string `json:"fastLinks" bson:"fastLinks"`
|
||||
}
|
||||
|
||||
type ReqEditPromoCode struct {
|
||||
ID string `json:"id" bson:"_id"` //айдишник промокода, который обновляем
|
||||
Description *string `json:"description,omitempty" bson:"description"` // описание, необходимое менеджеру в админке
|
||||
Greetings *string `json:"greetings,omitempty" bson:"greetings"` // текст, выдаваемый пользователю в ответ на активацию промокода
|
||||
|
||||
DueTo *int64 `json:"dueTo,omitempty" bson:"dueTo"` // таймштамп времени окончания работы активации промокода
|
||||
ActivationCount *int64 `json:"activationCount,omitempty" bson:"activationCount"` // предел количества активаций промокода
|
||||
|
||||
Delete *bool `json:"delete,omitempty" bson:"delete"`
|
||||
}
|
||||
|
||||
type GetPromoCodesListReqFilter struct {
|
||||
Text string `json:"text"` // полнотекстовый поиск пo Codeword, Decription, Greetings полям
|
||||
Active bool `json:"active"` // если true, то выбирать deleted==false && outdated== false && offlimit == false
|
||||
}
|
||||
|
||||
type GetPromoCodesListReq struct {
|
||||
Page int `json:"page"` //номер страницы выборки. начинается с 0. по сути, skip для выборки из mongodb
|
||||
Limit int `json:"limit"` //размер страницы выборки. больше 10, меньше 250. отвечает за skip = page*limit, и за limit
|
||||
Filter GetPromoCodesListReqFilter `json:"filter"`
|
||||
}
|
||||
|
||||
type GetPromoCodesListResp struct {
|
||||
Count int64 `json:"count"` // количество в выборке всего
|
||||
Items []PromoCode `json:"items"` // "страница" промокодов
|
||||
}
|
||||
|
||||
type ActivateReq struct {
|
||||
Codeword string `json:"codeword"`
|
||||
FastLink string `json:"fastLink"`
|
||||
}
|
||||
|
||||
type ActivateResp struct {
|
||||
Greetings string `json:"greetings"` // поле из активированного промокода
|
||||
}
|
||||
|
||||
type PromoCodeStats struct {
|
||||
ID string `bson:"_id,omitempty" json:"id,omitempty"`
|
||||
UsageCount int `bson:"usageCount" json:"usageCount"`
|
||||
UsageMap map[string][]Usage `bson:"usageMap" json:"usageMap"`
|
||||
}
|
||||
|
||||
type Usage struct {
|
||||
Key string `bson:"key" json:"key"`
|
||||
Time time.Time `bson:"time" json:"time"`
|
||||
type Bonus struct {
|
||||
/* - Скидка*/
|
||||
Discount Discount `json:"discount"`
|
||||
/* - Привилегия*/
|
||||
Privilege Privilege `json:"privilege"`
|
||||
}
|
||||
|
6
internal/models/createfastlinkreq.go
Normal file
6
internal/models/createfastlinkreq.go
Normal file
@ -0,0 +1,6 @@
|
||||
package models
|
||||
|
||||
type CreateFastLinkReq struct {
|
||||
/* - ID промокода, для которого нужно создать быструю ссылку*/
|
||||
ID string `json:"id"`
|
||||
}
|
6
internal/models/createfastlinkresp.go
Normal file
6
internal/models/createfastlinkresp.go
Normal file
@ -0,0 +1,6 @@
|
||||
package models
|
||||
|
||||
type CreateFastLinkResp struct {
|
||||
/* - Быстрая ссылка для активации промокода*/
|
||||
Fastlink string `json:"fastlink"`
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package models
|
||||
|
||||
type StoreRecDeps struct {
|
||||
UserID string
|
||||
Email string
|
||||
Key string
|
||||
Url string
|
||||
}
|
||||
|
||||
type RecEmailDeps struct {
|
||||
UserID string
|
||||
Email string
|
||||
SignWithID string
|
||||
ID string
|
||||
}
|
12
internal/models/discount.go
Normal file
12
internal/models/discount.go
Normal file
@ -0,0 +1,12 @@
|
||||
package models
|
||||
|
||||
type Discount struct {
|
||||
/* - Цель скидки*/
|
||||
Target string `json:"target"`
|
||||
/* - Порог скидки*/
|
||||
Threshold int `json:"threshold"`
|
||||
/* - Множитель скидки*/
|
||||
Factor int `json:"factor"`
|
||||
/* - Уровень скидки*/
|
||||
Layer int `json:"layer"`
|
||||
}
|
16
internal/models/editpromocodereq.go
Normal file
16
internal/models/editpromocodereq.go
Normal file
@ -0,0 +1,16 @@
|
||||
package models
|
||||
|
||||
type EditPromoCodeReq struct {
|
||||
/* - Приветственное сообщение после активации промокода*/
|
||||
Greetings string `json:"Greetings"`
|
||||
/* - Идентификатор промокода, который требуется обновить*/
|
||||
ID string `json:"ID"`
|
||||
/* - Количество активаций промокода*/
|
||||
Activationcount int `json:"ActivationCount"`
|
||||
/* - Флаг удаления промокода*/
|
||||
Delete bool `json:"Delete"`
|
||||
/* - Описание промокода*/
|
||||
Description string `json:"Description"`
|
||||
/* - Дата окончания промокода в формате Unix time*/
|
||||
Dueto int `json:"DueTo"`
|
||||
}
|
8
internal/models/filter.go
Normal file
8
internal/models/filter.go
Normal file
@ -0,0 +1,8 @@
|
||||
package models
|
||||
|
||||
type Filter struct {
|
||||
/* - Флаг для фильтрации активных промокодов*/
|
||||
Active bool `json:"active"`
|
||||
/* - Текстовый фильтр для поиска промокодов*/
|
||||
Text string `json:"text"`
|
||||
}
|
10
internal/models/getpromocodeslistreq.go
Normal file
10
internal/models/getpromocodeslistreq.go
Normal file
@ -0,0 +1,10 @@
|
||||
package models
|
||||
|
||||
type GetPromoCodesListReq struct {
|
||||
/* - Номер страницы*/
|
||||
Page int `json:"page"`
|
||||
/* - */
|
||||
Filter Filter `json:"filter"`
|
||||
/* - Максимальное количество элементов на странице*/
|
||||
Limit int `json:"limit"`
|
||||
}
|
8
internal/models/getpromocodeslistresp.go
Normal file
8
internal/models/getpromocodeslistresp.go
Normal file
@ -0,0 +1,8 @@
|
||||
package models
|
||||
|
||||
type GetPromoCodesListResp struct {
|
||||
/* - Общее количество промокодов*/
|
||||
Count int `json:"count"`
|
||||
/* - */
|
||||
Items []PromoCode `json:"items"`
|
||||
}
|
8
internal/models/privilege.go
Normal file
8
internal/models/privilege.go
Normal file
@ -0,0 +1,8 @@
|
||||
package models
|
||||
|
||||
type Privilege struct {
|
||||
/* - Идентификатор привилегии*/
|
||||
Privilegeid string `json:"privilegeID"`
|
||||
/* - Количество привилегии*/
|
||||
Amount int `json:"amount"`
|
||||
}
|
28
internal/models/promocode.go
Normal file
28
internal/models/promocode.go
Normal file
@ -0,0 +1,28 @@
|
||||
package models
|
||||
|
||||
type PromoCode struct {
|
||||
/* - Идентификатор промокода*/
|
||||
ID string `json:"id"`
|
||||
/* - Бонус, предоставляемый с промокодом*/
|
||||
Bonus Bonus `json:"bonus"`
|
||||
/* - Флаг*/
|
||||
Delete bool `json:"delete"`
|
||||
/* - Описание промокода*/
|
||||
Description string `json:"description"`
|
||||
/* - Список быстрых ссылок для активации промокода*/
|
||||
Fastlinks []string `json:"fastLinks"`
|
||||
/* - Приветственное сообщение после активации промокода*/
|
||||
Greetings string `json:"greetings"`
|
||||
/* - Количество активаций промокода*/
|
||||
Activationcount int `json:"activationCount"`
|
||||
/* - Флаг*/
|
||||
Outdated bool `json:"outdated"`
|
||||
/* - Кодовое слово для активации промокода*/
|
||||
Codeword string `json:"codeword"`
|
||||
/* - Дата и время создания промокода*/
|
||||
Createdat string `json:"createdAt"`
|
||||
/* - Дата истечения действия промокода в формате Unix time*/
|
||||
Dueto int `json:"dueTo"`
|
||||
/* - Флаг*/
|
||||
Offlimit bool `json:"offLimit"`
|
||||
}
|
18
internal/models/promocodereq.go
Normal file
18
internal/models/promocodereq.go
Normal file
@ -0,0 +1,18 @@
|
||||
package models
|
||||
|
||||
type PromoCodeReq struct {
|
||||
/* - Бонус*/
|
||||
Bonus Bonus `json:"bonus"`
|
||||
/* - Кодовое слово для активации промокода*/
|
||||
Codeword string `json:"codeword"`
|
||||
/* - Описание промокода*/
|
||||
Description string `json:"description"`
|
||||
/* - Дата истечения действия промокода в формате Unix time*/
|
||||
Dueto int `json:"dueTo"`
|
||||
/* - Список быстрых ссылок для активации промокода*/
|
||||
Fastlinks []string `json:"fastLinks"`
|
||||
/* - Приветственное сообщение после активации промокода*/
|
||||
Greetings string `json:"greetings"`
|
||||
/* - Количество активаций промокода*/
|
||||
Activationcount int `json:"activationCount"`
|
||||
}
|
6
internal/models/promocodestatsreq.go
Normal file
6
internal/models/promocodestatsreq.go
Normal file
@ -0,0 +1,6 @@
|
||||
package models
|
||||
|
||||
type PromoCodeStatsReq struct {
|
||||
/* - */
|
||||
Promocodeid string `json:"promoCodeID"`
|
||||
}
|
10
internal/models/promocodestatsresp.go
Normal file
10
internal/models/promocodestatsresp.go
Normal file
@ -0,0 +1,10 @@
|
||||
package models
|
||||
|
||||
type PromoCodeStatsResp struct {
|
||||
/* - Идентификатор промокода*/
|
||||
ID string `json:"id"`
|
||||
/* - Количество использований промокода*/
|
||||
Usagecount int `json:"usageCount"`
|
||||
/* - Карта использования промокода*/
|
||||
Usagemap Usagemap `json:"usageMap"`
|
||||
}
|
8
internal/models/recoveryreq.go
Normal file
8
internal/models/recoveryreq.go
Normal file
@ -0,0 +1,8 @@
|
||||
package models
|
||||
|
||||
type RecoveryReq struct {
|
||||
/* - Электронная почта, на которую нужно отправить инструкции по восстановлению*/
|
||||
Email string `json:"email"`
|
||||
/* - URL-адрес, на который перенаправляется пользователь*/
|
||||
Redirectionurl string `json:"redirectionURL"`
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"codeword/internal/proto/broker"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Tariff struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
Price uint64 `json:"price,omitempty"`
|
||||
IsCustom bool `json:"isCustom"`
|
||||
Privileges []Privilege `json:"privileges"`
|
||||
Deleted bool `json:"isDeleted"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
DeletedAt *time.Time `json:"deletedAt,omitempty"`
|
||||
}
|
||||
|
||||
type Privilege struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
PrivilegeID string `json:"privilegeId"`
|
||||
ServiceKey string `json:"serviceKey"`
|
||||
Description string `json:"description"`
|
||||
Amount uint64 `json:"amount"`
|
||||
Type PrivilegeType `json:"type"`
|
||||
Value string `json:"value"`
|
||||
Price uint64 `json:"price"`
|
||||
}
|
||||
|
||||
type PrivilegeType string
|
||||
|
||||
const (
|
||||
PrivilegeTypeCount = "count"
|
||||
PrivilegeTypeDay = "day"
|
||||
PrivilegeTypeFull = "full"
|
||||
)
|
||||
|
||||
var (
|
||||
PrivilegeBrokerTypeMap = map[PrivilegeType]broker.PrivilegeType{
|
||||
PrivilegeTypeFull: broker.PrivilegeType_Full,
|
||||
PrivilegeTypeDay: broker.PrivilegeType_Day,
|
||||
PrivilegeTypeCount: broker.PrivilegeType_Count,
|
||||
}
|
||||
)
|
8
internal/models/usage.go
Normal file
8
internal/models/usage.go
Normal file
@ -0,0 +1,8 @@
|
||||
package models
|
||||
|
||||
type Usage struct {
|
||||
/* - fastlink или codeword в зависимости от того что применялось*/
|
||||
Key string `json:"key"`
|
||||
/* - Время использования промокода*/
|
||||
Time string `json:"time"`
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"time"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID primitive.ObjectID `bson:"_id,omitempty"`
|
||||
Login string `bson:"login,omitempty"`
|
||||
Email string `bson:"email,omitempty"`
|
||||
Password string `bson:"password,omitempty"`
|
||||
PhoneNumber string `bson:"phoneNumber,omitempty"`
|
||||
IsDeleted bool `bson:"isDeleted,omitempty"`
|
||||
CreatedAt time.Time `bson:"createdAt,omitempty"`
|
||||
UpdatedAt time.Time `bson:"updatedAt,omitempty"`
|
||||
DeletedAt *time.Time `bson:"deletedAt,omitempty"`
|
||||
}
|
||||
|
||||
type RestoreRequest struct {
|
||||
ID primitive.ObjectID `bson:"_id,omitempty"`
|
||||
CreatedAt time.Time `bson:"created_at,omitempty"`
|
||||
Sign string `bson:"sign,omitempty"`
|
||||
SignUrl string `bson:"sign_url,omitempty"`
|
||||
SignID string `bson:"sign_id"`
|
||||
Email string `bson:"email,omitempty"`
|
||||
UserID string `bson:"user_id,omitempty"`
|
||||
Sent bool `bson:"sent"`
|
||||
SentAt time.Time `bson:"sent_at"`
|
||||
}
|
||||
|
||||
type RecoveryRecord struct {
|
||||
ID string
|
||||
UserID string
|
||||
Email string
|
||||
Key string
|
||||
}
|
||||
|
||||
type RecoveryRequest struct {
|
||||
Email string `json:"email"`
|
||||
RedirectionURL string `json:"redirectionURL"`
|
||||
}
|
||||
|
||||
type RecoveryLinkRequest struct {
|
||||
Sign string `json:"sign"`
|
||||
}
|
@ -1,314 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc (unknown)
|
||||
// source: kafka/models.proto
|
||||
|
||||
package broker
|
||||
|
||||
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 PrivilegeType int32
|
||||
|
||||
const (
|
||||
PrivilegeType_Full PrivilegeType = 0
|
||||
PrivilegeType_Day PrivilegeType = 1
|
||||
PrivilegeType_Count PrivilegeType = 2
|
||||
)
|
||||
|
||||
// Enum value maps for PrivilegeType.
|
||||
var (
|
||||
PrivilegeType_name = map[int32]string{
|
||||
0: "Full",
|
||||
1: "Day",
|
||||
2: "Count",
|
||||
}
|
||||
PrivilegeType_value = map[string]int32{
|
||||
"Full": 0,
|
||||
"Day": 1,
|
||||
"Count": 2,
|
||||
}
|
||||
)
|
||||
|
||||
func (x PrivilegeType) Enum() *PrivilegeType {
|
||||
p := new(PrivilegeType)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x PrivilegeType) String() string {
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (PrivilegeType) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_broker_models_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (PrivilegeType) Type() protoreflect.EnumType {
|
||||
return &file_broker_models_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x PrivilegeType) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PrivilegeType.Descriptor instead.
|
||||
func (PrivilegeType) EnumDescriptor() ([]byte, []int) {
|
||||
return file_broker_models_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
type PrivilegeMessage struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
PrivilegeID string `protobuf:"bytes,1,opt,name=PrivilegeID,proto3" json:"PrivilegeID,omitempty"`
|
||||
ServiceKey string `protobuf:"bytes,2,opt,name=ServiceKey,proto3" json:"ServiceKey,omitempty"`
|
||||
Type PrivilegeType `protobuf:"varint,3,opt,name=Type,proto3,enum=kafka.PrivilegeType" json:"Type,omitempty"`
|
||||
Value string `protobuf:"bytes,4,opt,name=Value,proto3" json:"Value,omitempty"`
|
||||
Amount uint64 `protobuf:"varint,5,opt,name=Amount,proto3" json:"Amount,omitempty"`
|
||||
}
|
||||
|
||||
func (x *PrivilegeMessage) Reset() {
|
||||
*x = PrivilegeMessage{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_broker_models_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *PrivilegeMessage) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PrivilegeMessage) ProtoMessage() {}
|
||||
|
||||
func (x *PrivilegeMessage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_broker_models_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 PrivilegeMessage.ProtoReflect.Descriptor instead.
|
||||
func (*PrivilegeMessage) Descriptor() ([]byte, []int) {
|
||||
return file_broker_models_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *PrivilegeMessage) GetPrivilegeID() string {
|
||||
if x != nil {
|
||||
return x.PrivilegeID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PrivilegeMessage) GetServiceKey() string {
|
||||
if x != nil {
|
||||
return x.ServiceKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PrivilegeMessage) GetType() PrivilegeType {
|
||||
if x != nil {
|
||||
return x.Type
|
||||
}
|
||||
return PrivilegeType_Full
|
||||
}
|
||||
|
||||
func (x *PrivilegeMessage) GetValue() string {
|
||||
if x != nil {
|
||||
return x.Value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *PrivilegeMessage) GetAmount() uint64 {
|
||||
if x != nil {
|
||||
return x.Amount
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type TariffMessage struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Privileges []*PrivilegeMessage `protobuf:"bytes,1,rep,name=Privileges,proto3" json:"Privileges,omitempty"`
|
||||
UserID string `protobuf:"bytes,2,opt,name=UserID,proto3" json:"UserID,omitempty"`
|
||||
}
|
||||
|
||||
func (x *TariffMessage) Reset() {
|
||||
*x = TariffMessage{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_broker_models_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *TariffMessage) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*TariffMessage) ProtoMessage() {}
|
||||
|
||||
func (x *TariffMessage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_broker_models_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 TariffMessage.ProtoReflect.Descriptor instead.
|
||||
func (*TariffMessage) Descriptor() ([]byte, []int) {
|
||||
return file_broker_models_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *TariffMessage) GetPrivileges() []*PrivilegeMessage {
|
||||
if x != nil {
|
||||
return x.Privileges
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *TariffMessage) GetUserID() string {
|
||||
if x != nil {
|
||||
return x.UserID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_broker_models_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_broker_models_proto_rawDesc = []byte{
|
||||
0x0a, 0x13, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x73, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x22, 0xad, 0x01,
|
||||
0x0a, 0x10, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x49,
|
||||
0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65,
|
||||
0x67, 0x65, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4b,
|
||||
0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
|
||||
0x65, 0x4b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x0e, 0x32, 0x15, 0x2e, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x69, 0x76,
|
||||
0x69, 0x6c, 0x65, 0x67, 0x65, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,
|
||||
0x14, 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05,
|
||||
0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18,
|
||||
0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x61, 0x0a,
|
||||
0x0d, 0x54, 0x61, 0x72, 0x69, 0x66, 0x66, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38,
|
||||
0x0a, 0x0a, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
|
||||
0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x69, 0x76,
|
||||
0x69, 0x6c, 0x65, 0x67, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x0a, 0x50, 0x72,
|
||||
0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x73, 0x65, 0x72,
|
||||
0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x55, 0x73, 0x65, 0x72, 0x49, 0x44,
|
||||
0x2a, 0x2d, 0x0a, 0x0d, 0x50, 0x72, 0x69, 0x76, 0x69, 0x6c, 0x65, 0x67, 0x65, 0x54, 0x79, 0x70,
|
||||
0x65, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x44,
|
||||
0x61, 0x79, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x10, 0x02, 0x42,
|
||||
0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_broker_models_proto_rawDescOnce sync.Once
|
||||
file_broker_models_proto_rawDescData = file_broker_models_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_broker_models_proto_rawDescGZIP() []byte {
|
||||
file_broker_models_proto_rawDescOnce.Do(func() {
|
||||
file_broker_models_proto_rawDescData = protoimpl.X.CompressGZIP(file_broker_models_proto_rawDescData)
|
||||
})
|
||||
return file_broker_models_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_broker_models_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_broker_models_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_broker_models_proto_goTypes = []interface{}{
|
||||
(PrivilegeType)(0), // 0: kafka.PrivilegeType
|
||||
(*PrivilegeMessage)(nil), // 1: kafka.PrivilegeMessage
|
||||
(*TariffMessage)(nil), // 2: kafka.TariffMessage
|
||||
}
|
||||
var file_broker_models_proto_depIdxs = []int32{
|
||||
0, // 0: kafka.PrivilegeMessage.Type:type_name -> kafka.PrivilegeType
|
||||
1, // 1: kafka.TariffMessage.Privileges:type_name -> kafka.PrivilegeMessage
|
||||
2, // [2:2] is the sub-list for method output_type
|
||||
2, // [2:2] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_broker_models_proto_init() }
|
||||
func file_broker_models_proto_init() {
|
||||
if File_broker_models_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_broker_models_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*PrivilegeMessage); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_broker_models_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*TariffMessage); 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_broker_models_proto_rawDesc,
|
||||
NumEnums: 1,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_broker_models_proto_goTypes,
|
||||
DependencyIndexes: file_broker_models_proto_depIdxs,
|
||||
EnumInfos: file_broker_models_proto_enumTypes,
|
||||
MessageInfos: file_broker_models_proto_msgTypes,
|
||||
}.Build()
|
||||
File_broker_models_proto = out.File
|
||||
file_broker_models_proto_rawDesc = nil
|
||||
file_broker_models_proto_goTypes = nil
|
||||
file_broker_models_proto_depIdxs = nil
|
||||
}
|
@ -1,192 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc (unknown)
|
||||
// source: discount/audit.model.proto
|
||||
|
||||
package discount
|
||||
|
||||
import (
|
||||
_ "google.golang.org/genproto/googleapis/api/annotations"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
_ "google.golang.org/protobuf/types/known/emptypb"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
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 Audit struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=UpdatedAt,proto3" json:"UpdatedAt,omitempty"`
|
||||
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=CreatedAt,proto3" json:"CreatedAt,omitempty"`
|
||||
DeletedAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=DeletedAt,proto3,oneof" json:"DeletedAt,omitempty"`
|
||||
Deleted bool `protobuf:"varint,4,opt,name=Deleted,proto3" json:"Deleted,omitempty"`
|
||||
}
|
||||
|
||||
func (x *Audit) Reset() {
|
||||
*x = Audit{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_discount_audit_model_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Audit) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Audit) ProtoMessage() {}
|
||||
|
||||
func (x *Audit) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_discount_audit_model_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 Audit.ProtoReflect.Descriptor instead.
|
||||
func (*Audit) Descriptor() ([]byte, []int) {
|
||||
return file_discount_audit_model_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *Audit) GetUpdatedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.UpdatedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Audit) GetCreatedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.CreatedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Audit) GetDeletedAt() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.DeletedAt
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Audit) GetDeleted() bool {
|
||||
if x != nil {
|
||||
return x.Deleted
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var File_discount_audit_model_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_discount_audit_model_proto_rawDesc = []byte{
|
||||
0x0a, 0x1a, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x61, 0x75, 0x64, 0x69, 0x74,
|
||||
0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x64, 0x69,
|
||||
0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61,
|
||||
0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70,
|
||||
0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x22, 0xe2, 0x01, 0x0a, 0x05, 0x41, 0x75, 0x64, 0x69, 0x74, 0x12, 0x38, 0x0a, 0x09,
|
||||
0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||
0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x55, 0x70, 0x64,
|
||||
0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
|
||||
0x64, 0x41, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
|
||||
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65,
|
||||
0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74,
|
||||
0x12, 0x3d, 0x0a, 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x48,
|
||||
0x00, 0x52, 0x09, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x88, 0x01, 0x01, 0x12,
|
||||
0x18, 0x0a, 0x07, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
|
||||
0x52, 0x07, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x44, 0x65,
|
||||
0x6c, 0x65, 0x74, 0x65, 0x64, 0x41, 0x74, 0x42, 0x0c, 0x5a, 0x0a, 0x2e, 0x2f, 0x64, 0x69, 0x73,
|
||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_discount_audit_model_proto_rawDescOnce sync.Once
|
||||
file_discount_audit_model_proto_rawDescData = file_discount_audit_model_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_discount_audit_model_proto_rawDescGZIP() []byte {
|
||||
file_discount_audit_model_proto_rawDescOnce.Do(func() {
|
||||
file_discount_audit_model_proto_rawDescData = protoimpl.X.CompressGZIP(file_discount_audit_model_proto_rawDescData)
|
||||
})
|
||||
return file_discount_audit_model_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_discount_audit_model_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_discount_audit_model_proto_goTypes = []interface{}{
|
||||
(*Audit)(nil), // 0: discount.Audit
|
||||
(*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp
|
||||
}
|
||||
var file_discount_audit_model_proto_depIdxs = []int32{
|
||||
1, // 0: discount.Audit.UpdatedAt:type_name -> google.protobuf.Timestamp
|
||||
1, // 1: discount.Audit.CreatedAt:type_name -> google.protobuf.Timestamp
|
||||
1, // 2: discount.Audit.DeletedAt:type_name -> google.protobuf.Timestamp
|
||||
3, // [3:3] is the sub-list for method output_type
|
||||
3, // [3:3] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_discount_audit_model_proto_init() }
|
||||
func file_discount_audit_model_proto_init() {
|
||||
if File_discount_audit_model_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_discount_audit_model_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Audit); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_discount_audit_model_proto_msgTypes[0].OneofWrappers = []interface{}{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_discount_audit_model_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_discount_audit_model_proto_goTypes,
|
||||
DependencyIndexes: file_discount_audit_model_proto_depIdxs,
|
||||
MessageInfos: file_discount_audit_model_proto_msgTypes,
|
||||
}.Build()
|
||||
File_discount_audit_model_proto = out.File
|
||||
file_discount_audit_model_proto_rawDesc = nil
|
||||
file_discount_audit_model_proto_goTypes = nil
|
||||
file_discount_audit_model_proto_depIdxs = nil
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,524 +0,0 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc (unknown)
|
||||
// source: discount/service.proto
|
||||
|
||||
package discount
|
||||
|
||||
import (
|
||||
_ "google.golang.org/genproto/googleapis/api/annotations"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
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 GetDiscountByIDRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
|
||||
}
|
||||
|
||||
func (x *GetDiscountByIDRequest) Reset() {
|
||||
*x = GetDiscountByIDRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_discount_service_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *GetDiscountByIDRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*GetDiscountByIDRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetDiscountByIDRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_discount_service_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 GetDiscountByIDRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetDiscountByIDRequest) Descriptor() ([]byte, []int) {
|
||||
return file_discount_service_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *GetDiscountByIDRequest) GetID() string {
|
||||
if x != nil {
|
||||
return x.ID
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ApplyDiscountRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
UserInformation *UserInformation `protobuf:"bytes,1,opt,name=UserInformation,proto3" json:"UserInformation,omitempty"`
|
||||
Products []*ProductInformation `protobuf:"bytes,2,rep,name=Products,proto3" json:"Products,omitempty"`
|
||||
Date *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=Date,proto3" json:"Date,omitempty"`
|
||||
Coupon *string `protobuf:"bytes,4,opt,name=Coupon,proto3,oneof" json:"Coupon,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ApplyDiscountRequest) Reset() {
|
||||
*x = ApplyDiscountRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_discount_service_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ApplyDiscountRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ApplyDiscountRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ApplyDiscountRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_discount_service_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 ApplyDiscountRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ApplyDiscountRequest) Descriptor() ([]byte, []int) {
|
||||
return file_discount_service_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *ApplyDiscountRequest) GetUserInformation() *UserInformation {
|
||||
if x != nil {
|
||||
return x.UserInformation
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ApplyDiscountRequest) GetProducts() []*ProductInformation {
|
||||
if x != nil {
|
||||
return x.Products
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ApplyDiscountRequest) GetDate() *timestamppb.Timestamp {
|
||||
if x != nil {
|
||||
return x.Date
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ApplyDiscountRequest) GetCoupon() string {
|
||||
if x != nil && x.Coupon != nil {
|
||||
return *x.Coupon
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ApplyDiscountResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Price uint64 `protobuf:"varint,1,opt,name=Price,proto3" json:"Price,omitempty"`
|
||||
AppliedDiscounts []*Discount `protobuf:"bytes,2,rep,name=AppliedDiscounts,proto3" json:"AppliedDiscounts,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ApplyDiscountResponse) Reset() {
|
||||
*x = ApplyDiscountResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_discount_service_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ApplyDiscountResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ApplyDiscountResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ApplyDiscountResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_discount_service_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 ApplyDiscountResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ApplyDiscountResponse) Descriptor() ([]byte, []int) {
|
||||
return file_discount_service_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *ApplyDiscountResponse) GetPrice() uint64 {
|
||||
if x != nil {
|
||||
return x.Price
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ApplyDiscountResponse) GetAppliedDiscounts() []*Discount {
|
||||
if x != nil {
|
||||
return x.AppliedDiscounts
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CreateDiscountRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"`
|
||||
Layer uint32 `protobuf:"varint,2,opt,name=Layer,proto3" json:"Layer,omitempty"`
|
||||
Description string `protobuf:"bytes,3,opt,name=Description,proto3" json:"Description,omitempty"`
|
||||
Condition *DiscountCondition `protobuf:"bytes,4,opt,name=Condition,proto3" json:"Condition,omitempty"`
|
||||
Target *DiscountCalculationTarget `protobuf:"bytes,5,opt,name=Target,proto3" json:"Target,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CreateDiscountRequest) Reset() {
|
||||
*x = CreateDiscountRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_discount_service_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *CreateDiscountRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CreateDiscountRequest) ProtoMessage() {}
|
||||
|
||||
func (x *CreateDiscountRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_discount_service_proto_msgTypes[3]
|
||||
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 CreateDiscountRequest.ProtoReflect.Descriptor instead.
|
||||
func (*CreateDiscountRequest) Descriptor() ([]byte, []int) {
|
||||
return file_discount_service_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *CreateDiscountRequest) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CreateDiscountRequest) GetLayer() uint32 {
|
||||
if x != nil {
|
||||
return x.Layer
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *CreateDiscountRequest) GetDescription() string {
|
||||
if x != nil {
|
||||
return x.Description
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CreateDiscountRequest) GetCondition() *DiscountCondition {
|
||||
if x != nil {
|
||||
return x.Condition
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CreateDiscountRequest) GetTarget() *DiscountCalculationTarget {
|
||||
if x != nil {
|
||||
return x.Target
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_discount_service_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_discount_service_proto_rawDesc = []byte{
|
||||
0x0a, 0x16, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69,
|
||||
0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61,
|
||||
0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
|
||||
0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62,
|
||||
0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d,
|
||||
0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x28, 0x0a,
|
||||
0x16, 0x47, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x22, 0xed, 0x01, 0x0a, 0x14, 0x41, 0x70, 0x70, 0x6c,
|
||||
0x79, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x12, 0x43, 0x0a, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x64, 0x69, 0x73, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d, 0x61,
|
||||
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x55, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74,
|
||||
0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x2e, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x72, 0x6d,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x73, 0x12,
|
||||
0x2e, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
|
||||
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
|
||||
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x44, 0x61, 0x74, 0x65, 0x12,
|
||||
0x1b, 0x0a, 0x06, 0x43, 0x6f, 0x75, 0x70, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48,
|
||||
0x00, 0x52, 0x06, 0x43, 0x6f, 0x75, 0x70, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07,
|
||||
0x5f, 0x43, 0x6f, 0x75, 0x70, 0x6f, 0x6e, 0x22, 0x6d, 0x0a, 0x15, 0x41, 0x70, 0x70, 0x6c, 0x79,
|
||||
0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
|
||||
0x12, 0x14, 0x0a, 0x05, 0x50, 0x72, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52,
|
||||
0x05, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x10, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65,
|
||||
0x64, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b,
|
||||
0x32, 0x12, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x52, 0x10, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x64, 0x44, 0x69, 0x73,
|
||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22, 0xdb, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74,
|
||||
0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
|
||||
0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
|
||||
0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x0d, 0x52, 0x05, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x44, 0x65,
|
||||
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x39, 0x0a, 0x09,
|
||||
0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x1b, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x43, 0x6f,
|
||||
0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3b, 0x0a, 0x06, 0x54, 0x61, 0x72, 0x67, 0x65,
|
||||
0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x43, 0x61, 0x6c, 0x63, 0x75,
|
||||
0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x52, 0x06, 0x54, 0x61,
|
||||
0x72, 0x67, 0x65, 0x74, 0x32, 0x80, 0x07, 0x0a, 0x0f, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x52, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x41,
|
||||
0x6c, 0x6c, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x16, 0x2e, 0x67, 0x6f,
|
||||
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d,
|
||||
0x70, 0x74, 0x79, 0x1a, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44,
|
||||
0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c,
|
||||
0x12, 0x0a, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x66, 0x0a, 0x10,
|
||||
0x47, 0x65, 0x74, 0x55, 0x73, 0x65, 0x72, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73,
|
||||
0x12, 0x20, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x44,
|
||||
0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x13, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69,
|
||||
0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x12,
|
||||
0x13, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f,
|
||||
0x7b, 0x49, 0x44, 0x7d, 0x12, 0x69, 0x0a, 0x12, 0x44, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e,
|
||||
0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x2e, 0x64, 0x69, 0x73,
|
||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x44, 0x69, 0x73, 0x63, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x64, 0x69, 0x73,
|
||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x22,
|
||||
0x1e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x18, 0x3a, 0x01, 0x2a, 0x22, 0x13, 0x2f, 0x64, 0x69, 0x73,
|
||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x64, 0x65, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x65, 0x12,
|
||||
0x6d, 0x0a, 0x0e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x73, 0x12, 0x1e, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x41, 0x70, 0x70,
|
||||
0x6c, 0x79, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x1f, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x41, 0x70, 0x70,
|
||||
0x6c, 0x79, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x3a, 0x01, 0x2a, 0x22, 0x0f, 0x2f,
|
||||
0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x5f,
|
||||
0x0a, 0x0f, 0x47, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x49,
|
||||
0x44, 0x12, 0x20, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74,
|
||||
0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44,
|
||||
0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12,
|
||||
0x0e, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x7b, 0x49, 0x44, 0x7d, 0x12,
|
||||
0x5b, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e,
|
||||
0x74, 0x12, 0x1f, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x43, 0x72, 0x65,
|
||||
0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69,
|
||||
0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x14, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0e, 0x3a, 0x01,
|
||||
0x2a, 0x22, 0x09, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x5c, 0x0a, 0x0f,
|
||||
0x52, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12,
|
||||
0x1a, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x1a, 0x12, 0x2e, 0x64, 0x69,
|
||||
0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22,
|
||||
0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x1a, 0x0e, 0x2f, 0x64, 0x69, 0x73,
|
||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2f, 0x7b, 0x49, 0x44, 0x7d, 0x12, 0x5b, 0x0a, 0x0e, 0x55, 0x70,
|
||||
0x64, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1a, 0x2e, 0x64,
|
||||
0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x1a, 0x12, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f,
|
||||
0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x19, 0x82, 0xd3,
|
||||
0xe4, 0x93, 0x02, 0x13, 0x3a, 0x01, 0x2a, 0x32, 0x0e, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x2f, 0x7b, 0x49, 0x44, 0x7d, 0x12, 0x5e, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74,
|
||||
0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x20, 0x2e, 0x64, 0x69, 0x73, 0x63,
|
||||
0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x42, 0x79, 0x49, 0x44, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x64, 0x69,
|
||||
0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x22,
|
||||
0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x2a, 0x0e, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x2f, 0x7b, 0x49, 0x44, 0x7d, 0x42, 0x0c, 0x5a, 0x0a, 0x2e, 0x2f, 0x64, 0x69, 0x73,
|
||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_discount_service_proto_rawDescOnce sync.Once
|
||||
file_discount_service_proto_rawDescData = file_discount_service_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_discount_service_proto_rawDescGZIP() []byte {
|
||||
file_discount_service_proto_rawDescOnce.Do(func() {
|
||||
file_discount_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_discount_service_proto_rawDescData)
|
||||
})
|
||||
return file_discount_service_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_discount_service_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_discount_service_proto_goTypes = []interface{}{
|
||||
(*GetDiscountByIDRequest)(nil), // 0: discount.GetDiscountByIDRequest
|
||||
(*ApplyDiscountRequest)(nil), // 1: discount.ApplyDiscountRequest
|
||||
(*ApplyDiscountResponse)(nil), // 2: discount.ApplyDiscountResponse
|
||||
(*CreateDiscountRequest)(nil), // 3: discount.CreateDiscountRequest
|
||||
(*UserInformation)(nil), // 4: discount.UserInformation
|
||||
(*ProductInformation)(nil), // 5: discount.ProductInformation
|
||||
(*timestamppb.Timestamp)(nil), // 6: google.protobuf.Timestamp
|
||||
(*Discount)(nil), // 7: discount.Discount
|
||||
(*DiscountCondition)(nil), // 8: discount.DiscountCondition
|
||||
(*DiscountCalculationTarget)(nil), // 9: discount.DiscountCalculationTarget
|
||||
(*emptypb.Empty)(nil), // 10: google.protobuf.Empty
|
||||
(*DiscountOptional)(nil), // 11: discount.DiscountOptional
|
||||
(*Discounts)(nil), // 12: discount.Discounts
|
||||
}
|
||||
var file_discount_service_proto_depIdxs = []int32{
|
||||
4, // 0: discount.ApplyDiscountRequest.UserInformation:type_name -> discount.UserInformation
|
||||
5, // 1: discount.ApplyDiscountRequest.Products:type_name -> discount.ProductInformation
|
||||
6, // 2: discount.ApplyDiscountRequest.Date:type_name -> google.protobuf.Timestamp
|
||||
7, // 3: discount.ApplyDiscountResponse.AppliedDiscounts:type_name -> discount.Discount
|
||||
8, // 4: discount.CreateDiscountRequest.Condition:type_name -> discount.DiscountCondition
|
||||
9, // 5: discount.CreateDiscountRequest.Target:type_name -> discount.DiscountCalculationTarget
|
||||
10, // 6: discount.DiscountService.GetAllDiscounts:input_type -> google.protobuf.Empty
|
||||
0, // 7: discount.DiscountService.GetUserDiscounts:input_type -> discount.GetDiscountByIDRequest
|
||||
1, // 8: discount.DiscountService.DetermineDiscounts:input_type -> discount.ApplyDiscountRequest
|
||||
1, // 9: discount.DiscountService.ApplyDiscounts:input_type -> discount.ApplyDiscountRequest
|
||||
0, // 10: discount.DiscountService.GetDiscountByID:input_type -> discount.GetDiscountByIDRequest
|
||||
3, // 11: discount.DiscountService.CreateDiscount:input_type -> discount.CreateDiscountRequest
|
||||
11, // 12: discount.DiscountService.ReplaceDiscount:input_type -> discount.DiscountOptional
|
||||
11, // 13: discount.DiscountService.UpdateDiscount:input_type -> discount.DiscountOptional
|
||||
0, // 14: discount.DiscountService.DeleteDiscount:input_type -> discount.GetDiscountByIDRequest
|
||||
12, // 15: discount.DiscountService.GetAllDiscounts:output_type -> discount.Discounts
|
||||
12, // 16: discount.DiscountService.GetUserDiscounts:output_type -> discount.Discounts
|
||||
12, // 17: discount.DiscountService.DetermineDiscounts:output_type -> discount.Discounts
|
||||
2, // 18: discount.DiscountService.ApplyDiscounts:output_type -> discount.ApplyDiscountResponse
|
||||
7, // 19: discount.DiscountService.GetDiscountByID:output_type -> discount.Discount
|
||||
7, // 20: discount.DiscountService.CreateDiscount:output_type -> discount.Discount
|
||||
7, // 21: discount.DiscountService.ReplaceDiscount:output_type -> discount.Discount
|
||||
7, // 22: discount.DiscountService.UpdateDiscount:output_type -> discount.Discount
|
||||
7, // 23: discount.DiscountService.DeleteDiscount:output_type -> discount.Discount
|
||||
15, // [15:24] is the sub-list for method output_type
|
||||
6, // [6:15] is the sub-list for method input_type
|
||||
6, // [6:6] is the sub-list for extension type_name
|
||||
6, // [6:6] is the sub-list for extension extendee
|
||||
0, // [0:6] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_discount_service_proto_init() }
|
||||
func file_discount_service_proto_init() {
|
||||
if File_discount_service_proto != nil {
|
||||
return
|
||||
}
|
||||
file_discount_discount_model_proto_init()
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_discount_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*GetDiscountByIDRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_discount_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ApplyDiscountRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_discount_service_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ApplyDiscountResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_discount_service_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*CreateDiscountRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
file_discount_service_proto_msgTypes[1].OneofWrappers = []interface{}{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_discount_service_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_discount_service_proto_goTypes,
|
||||
DependencyIndexes: file_discount_service_proto_depIdxs,
|
||||
MessageInfos: file_discount_service_proto_msgTypes,
|
||||
}.Build()
|
||||
File_discount_service_proto = out.File
|
||||
file_discount_service_proto_rawDesc = nil
|
||||
file_discount_service_proto_goTypes = nil
|
||||
file_discount_service_proto_depIdxs = nil
|
||||
}
|
@ -1,404 +0,0 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.3.0
|
||||
// - protoc (unknown)
|
||||
// source: discount/service.proto
|
||||
|
||||
package discount
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
// 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 (
|
||||
DiscountService_GetAllDiscounts_FullMethodName = "/discount.DiscountService/GetAllDiscounts"
|
||||
DiscountService_GetUserDiscounts_FullMethodName = "/discount.DiscountService/GetUserDiscounts"
|
||||
DiscountService_DetermineDiscounts_FullMethodName = "/discount.DiscountService/DetermineDiscounts"
|
||||
DiscountService_ApplyDiscounts_FullMethodName = "/discount.DiscountService/ApplyDiscounts"
|
||||
DiscountService_GetDiscountByID_FullMethodName = "/discount.DiscountService/GetDiscountByID"
|
||||
DiscountService_CreateDiscount_FullMethodName = "/discount.DiscountService/CreateDiscount"
|
||||
DiscountService_ReplaceDiscount_FullMethodName = "/discount.DiscountService/ReplaceDiscount"
|
||||
DiscountService_UpdateDiscount_FullMethodName = "/discount.DiscountService/UpdateDiscount"
|
||||
DiscountService_DeleteDiscount_FullMethodName = "/discount.DiscountService/DeleteDiscount"
|
||||
)
|
||||
|
||||
// DiscountServiceClient is the client API for DiscountService 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 DiscountServiceClient interface {
|
||||
GetAllDiscounts(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*Discounts, error)
|
||||
GetUserDiscounts(ctx context.Context, in *GetDiscountByIDRequest, opts ...grpc.CallOption) (*Discounts, error)
|
||||
DetermineDiscounts(ctx context.Context, in *ApplyDiscountRequest, opts ...grpc.CallOption) (*Discounts, error)
|
||||
ApplyDiscounts(ctx context.Context, in *ApplyDiscountRequest, opts ...grpc.CallOption) (*ApplyDiscountResponse, error)
|
||||
GetDiscountByID(ctx context.Context, in *GetDiscountByIDRequest, opts ...grpc.CallOption) (*Discount, error)
|
||||
CreateDiscount(ctx context.Context, in *CreateDiscountRequest, opts ...grpc.CallOption) (*Discount, error)
|
||||
ReplaceDiscount(ctx context.Context, in *DiscountOptional, opts ...grpc.CallOption) (*Discount, error)
|
||||
UpdateDiscount(ctx context.Context, in *DiscountOptional, opts ...grpc.CallOption) (*Discount, error)
|
||||
DeleteDiscount(ctx context.Context, in *GetDiscountByIDRequest, opts ...grpc.CallOption) (*Discount, error)
|
||||
}
|
||||
|
||||
type discountServiceClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewDiscountServiceClient(cc grpc.ClientConnInterface) DiscountServiceClient {
|
||||
return &discountServiceClient{cc}
|
||||
}
|
||||
|
||||
func (c *discountServiceClient) GetAllDiscounts(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*Discounts, error) {
|
||||
out := new(Discounts)
|
||||
err := c.cc.Invoke(ctx, DiscountService_GetAllDiscounts_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *discountServiceClient) GetUserDiscounts(ctx context.Context, in *GetDiscountByIDRequest, opts ...grpc.CallOption) (*Discounts, error) {
|
||||
out := new(Discounts)
|
||||
err := c.cc.Invoke(ctx, DiscountService_GetUserDiscounts_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *discountServiceClient) DetermineDiscounts(ctx context.Context, in *ApplyDiscountRequest, opts ...grpc.CallOption) (*Discounts, error) {
|
||||
out := new(Discounts)
|
||||
err := c.cc.Invoke(ctx, DiscountService_DetermineDiscounts_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *discountServiceClient) ApplyDiscounts(ctx context.Context, in *ApplyDiscountRequest, opts ...grpc.CallOption) (*ApplyDiscountResponse, error) {
|
||||
out := new(ApplyDiscountResponse)
|
||||
err := c.cc.Invoke(ctx, DiscountService_ApplyDiscounts_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *discountServiceClient) GetDiscountByID(ctx context.Context, in *GetDiscountByIDRequest, opts ...grpc.CallOption) (*Discount, error) {
|
||||
out := new(Discount)
|
||||
err := c.cc.Invoke(ctx, DiscountService_GetDiscountByID_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *discountServiceClient) CreateDiscount(ctx context.Context, in *CreateDiscountRequest, opts ...grpc.CallOption) (*Discount, error) {
|
||||
out := new(Discount)
|
||||
err := c.cc.Invoke(ctx, DiscountService_CreateDiscount_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *discountServiceClient) ReplaceDiscount(ctx context.Context, in *DiscountOptional, opts ...grpc.CallOption) (*Discount, error) {
|
||||
out := new(Discount)
|
||||
err := c.cc.Invoke(ctx, DiscountService_ReplaceDiscount_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *discountServiceClient) UpdateDiscount(ctx context.Context, in *DiscountOptional, opts ...grpc.CallOption) (*Discount, error) {
|
||||
out := new(Discount)
|
||||
err := c.cc.Invoke(ctx, DiscountService_UpdateDiscount_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *discountServiceClient) DeleteDiscount(ctx context.Context, in *GetDiscountByIDRequest, opts ...grpc.CallOption) (*Discount, error) {
|
||||
out := new(Discount)
|
||||
err := c.cc.Invoke(ctx, DiscountService_DeleteDiscount_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// DiscountServiceServer is the server API for DiscountService service.
|
||||
// All implementations should embed UnimplementedDiscountServiceServer
|
||||
// for forward compatibility
|
||||
type DiscountServiceServer interface {
|
||||
GetAllDiscounts(context.Context, *emptypb.Empty) (*Discounts, error)
|
||||
GetUserDiscounts(context.Context, *GetDiscountByIDRequest) (*Discounts, error)
|
||||
DetermineDiscounts(context.Context, *ApplyDiscountRequest) (*Discounts, error)
|
||||
ApplyDiscounts(context.Context, *ApplyDiscountRequest) (*ApplyDiscountResponse, error)
|
||||
GetDiscountByID(context.Context, *GetDiscountByIDRequest) (*Discount, error)
|
||||
CreateDiscount(context.Context, *CreateDiscountRequest) (*Discount, error)
|
||||
ReplaceDiscount(context.Context, *DiscountOptional) (*Discount, error)
|
||||
UpdateDiscount(context.Context, *DiscountOptional) (*Discount, error)
|
||||
DeleteDiscount(context.Context, *GetDiscountByIDRequest) (*Discount, error)
|
||||
}
|
||||
|
||||
// UnimplementedDiscountServiceServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedDiscountServiceServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedDiscountServiceServer) GetAllDiscounts(context.Context, *emptypb.Empty) (*Discounts, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetAllDiscounts not implemented")
|
||||
}
|
||||
func (UnimplementedDiscountServiceServer) GetUserDiscounts(context.Context, *GetDiscountByIDRequest) (*Discounts, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetUserDiscounts not implemented")
|
||||
}
|
||||
func (UnimplementedDiscountServiceServer) DetermineDiscounts(context.Context, *ApplyDiscountRequest) (*Discounts, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DetermineDiscounts not implemented")
|
||||
}
|
||||
func (UnimplementedDiscountServiceServer) ApplyDiscounts(context.Context, *ApplyDiscountRequest) (*ApplyDiscountResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ApplyDiscounts not implemented")
|
||||
}
|
||||
func (UnimplementedDiscountServiceServer) GetDiscountByID(context.Context, *GetDiscountByIDRequest) (*Discount, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetDiscountByID not implemented")
|
||||
}
|
||||
func (UnimplementedDiscountServiceServer) CreateDiscount(context.Context, *CreateDiscountRequest) (*Discount, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateDiscount not implemented")
|
||||
}
|
||||
func (UnimplementedDiscountServiceServer) ReplaceDiscount(context.Context, *DiscountOptional) (*Discount, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ReplaceDiscount not implemented")
|
||||
}
|
||||
func (UnimplementedDiscountServiceServer) UpdateDiscount(context.Context, *DiscountOptional) (*Discount, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateDiscount not implemented")
|
||||
}
|
||||
func (UnimplementedDiscountServiceServer) DeleteDiscount(context.Context, *GetDiscountByIDRequest) (*Discount, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DeleteDiscount not implemented")
|
||||
}
|
||||
|
||||
// UnsafeDiscountServiceServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to DiscountServiceServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeDiscountServiceServer interface {
|
||||
mustEmbedUnimplementedDiscountServiceServer()
|
||||
}
|
||||
|
||||
func RegisterDiscountServiceServer(s grpc.ServiceRegistrar, srv DiscountServiceServer) {
|
||||
s.RegisterService(&DiscountService_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _DiscountService_GetAllDiscounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(emptypb.Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DiscountServiceServer).GetAllDiscounts(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: DiscountService_GetAllDiscounts_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DiscountServiceServer).GetAllDiscounts(ctx, req.(*emptypb.Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _DiscountService_GetUserDiscounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetDiscountByIDRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DiscountServiceServer).GetUserDiscounts(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: DiscountService_GetUserDiscounts_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DiscountServiceServer).GetUserDiscounts(ctx, req.(*GetDiscountByIDRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _DiscountService_DetermineDiscounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ApplyDiscountRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DiscountServiceServer).DetermineDiscounts(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: DiscountService_DetermineDiscounts_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DiscountServiceServer).DetermineDiscounts(ctx, req.(*ApplyDiscountRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _DiscountService_ApplyDiscounts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ApplyDiscountRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DiscountServiceServer).ApplyDiscounts(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: DiscountService_ApplyDiscounts_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DiscountServiceServer).ApplyDiscounts(ctx, req.(*ApplyDiscountRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _DiscountService_GetDiscountByID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetDiscountByIDRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DiscountServiceServer).GetDiscountByID(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: DiscountService_GetDiscountByID_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DiscountServiceServer).GetDiscountByID(ctx, req.(*GetDiscountByIDRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _DiscountService_CreateDiscount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreateDiscountRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DiscountServiceServer).CreateDiscount(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: DiscountService_CreateDiscount_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DiscountServiceServer).CreateDiscount(ctx, req.(*CreateDiscountRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _DiscountService_ReplaceDiscount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DiscountOptional)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DiscountServiceServer).ReplaceDiscount(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: DiscountService_ReplaceDiscount_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DiscountServiceServer).ReplaceDiscount(ctx, req.(*DiscountOptional))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _DiscountService_UpdateDiscount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DiscountOptional)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DiscountServiceServer).UpdateDiscount(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: DiscountService_UpdateDiscount_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DiscountServiceServer).UpdateDiscount(ctx, req.(*DiscountOptional))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _DiscountService_DeleteDiscount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetDiscountByIDRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(DiscountServiceServer).DeleteDiscount(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: DiscountService_DeleteDiscount_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(DiscountServiceServer).DeleteDiscount(ctx, req.(*GetDiscountByIDRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// DiscountService_ServiceDesc is the grpc.ServiceDesc for DiscountService service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var DiscountService_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "discount.DiscountService",
|
||||
HandlerType: (*DiscountServiceServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetAllDiscounts",
|
||||
Handler: _DiscountService_GetAllDiscounts_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetUserDiscounts",
|
||||
Handler: _DiscountService_GetUserDiscounts_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DetermineDiscounts",
|
||||
Handler: _DiscountService_DetermineDiscounts_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ApplyDiscounts",
|
||||
Handler: _DiscountService_ApplyDiscounts_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetDiscountByID",
|
||||
Handler: _DiscountService_GetDiscountByID_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreateDiscount",
|
||||
Handler: _DiscountService_CreateDiscount_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ReplaceDiscount",
|
||||
Handler: _DiscountService_ReplaceDiscount_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateDiscount",
|
||||
Handler: _DiscountService_UpdateDiscount_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteDiscount",
|
||||
Handler: _DiscountService_DeleteDiscount_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "discount/service.proto",
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/readpref"
|
||||
"time"
|
||||
)
|
||||
|
||||
type CodewordRepository struct {
|
||||
mdb *mongo.Collection
|
||||
rdb *redis.Client
|
||||
}
|
||||
|
||||
func NewCodewordRepository(deps Deps) *CodewordRepository {
|
||||
|
||||
return &CodewordRepository{mdb: deps.Mdb, rdb: deps.Rdb}
|
||||
}
|
||||
|
||||
// сохраняем полученные данные о пользователе и подписи в бд
|
||||
func (r *CodewordRepository) StoreRecoveryRecord(ctx context.Context, deps models.StoreRecDeps) (string, error) {
|
||||
newID := primitive.NewObjectID()
|
||||
signID := deps.Key + newID.Hex()
|
||||
record := models.RestoreRequest{
|
||||
ID: newID,
|
||||
UserID: deps.UserID,
|
||||
Email: deps.Email,
|
||||
Sign: deps.Key,
|
||||
SignUrl: deps.Url,
|
||||
SignID: signID,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
_, err := r.mdb.InsertOne(ctx, record)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return newID.Hex(), nil
|
||||
}
|
||||
|
||||
// добавляем в очередь данные для отправки на почту в редис
|
||||
func (r *CodewordRepository) InsertToQueue(ctx context.Context, deps models.RecEmailDeps) error {
|
||||
sendLockKey := "email:sendLock:" + deps.Email
|
||||
ttl := 5 * time.Minute
|
||||
|
||||
lockSuccess, err := r.rdb.SetNX(ctx, sendLockKey, "1", ttl).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !lockSuccess {
|
||||
return ErrAlreadyReported
|
||||
}
|
||||
|
||||
task := models.RecoveryRecord{
|
||||
ID: deps.ID,
|
||||
UserID: deps.UserID,
|
||||
Email: deps.Email,
|
||||
Key: deps.SignWithID,
|
||||
}
|
||||
|
||||
taskBytes, err := json.Marshal(task)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.rdb.Set(ctx, "email:task:"+deps.Email, taskBytes, ttl).Err()
|
||||
}
|
||||
|
||||
// получаем данные юзера по подписи
|
||||
func (r *CodewordRepository) GetRecoveryRecord(ctx context.Context, key string) (*models.RestoreRequest, error) {
|
||||
var restoreRequest models.RestoreRequest
|
||||
|
||||
filter := bson.M{"sign_id": key}
|
||||
|
||||
err := r.mdb.FindOne(ctx, filter).Decode(&restoreRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &restoreRequest, nil
|
||||
}
|
||||
|
||||
// пингует в монгу чтобы проверить подключение
|
||||
func (r *CodewordRepository) Ping(ctx context.Context) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
if err := r.mdb.Database().Client().Ping(ctx, readpref.Primary()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := r.rdb.Ping(ctx).Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package repository
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrPromoUserNotFound = errors.New("user not found")
|
||||
ErrAlreadyReported = errors.New("already reported")
|
||||
ErrDuplicateCodeword = errors.New("duplicate codeword")
|
||||
ErrPromoCodeNotFound = errors.New("promo code not found")
|
||||
ErrPromoCodeExpired = errors.New("promo code is expired")
|
||||
ErrPromoCodeExhausted = errors.New("promo code is exhausted")
|
||||
ErrPromoCodeAlreadyActivated = errors.New("promo code is already activated")
|
||||
)
|
74
internal/repository/promocode.go
Normal file
74
internal/repository/promocode.go
Normal file
@ -0,0 +1,74 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type PromocodeRepository struct {
|
||||
queries *sqlcgen.Queries
|
||||
pool *sql.DB
|
||||
}
|
||||
|
||||
type PromocodeDeps struct {
|
||||
Queries *sqlcgen.Queries
|
||||
Pool *sql.DB
|
||||
}
|
||||
|
||||
func NewPromocodeRepository(deps PromocodeDeps) *PromocodeRepository {
|
||||
return &PromocodeRepository{
|
||||
queries: deps.Queries,
|
||||
pool: deps.Pool,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *PromocodeRepository) Activate(ctx context.Context, request *models.ActivateReq) (*models.ActivateResp, error) {
|
||||
//TODO:IMPLEMENT ME
|
||||
|
||||
return &models.ActivateResp{}, nil
|
||||
|
||||
}
|
||||
|
||||
func (r *PromocodeRepository) Getlist(ctx context.Context, request *models.GetPromoCodesListReq) (*models.GetPromoCodesListResp, error) {
|
||||
|
||||
rows, err := r.queries.Getlist(ctx, sqlcgen.GetlistParams{
|
||||
Page: request.Page,
|
||||
Limit: request.Size,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &models.GetPromoCodesListResp{}, nil
|
||||
|
||||
}
|
||||
|
||||
func (r *PromocodeRepository) Delete(ctx context.Context) error {
|
||||
//TODO:IMPLEMENT ME
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *PromocodeRepository) Createpromocode(ctx context.Context, request *models.PromoCodeReq) (*models.PromoCode, error) {
|
||||
//TODO:IMPLEMENT ME
|
||||
|
||||
return &models.PromoCode{}, nil
|
||||
|
||||
}
|
||||
|
||||
func (r *PromocodeRepository) Editpromocode(ctx context.Context, request *models.EditPromoCodeReq) (*models.PromoCode, error) {
|
||||
//TODO:IMPLEMENT ME
|
||||
|
||||
return &models.PromoCode{}, nil
|
||||
|
||||
}
|
||||
|
||||
func (r *PromocodeRepository) Createfastlink(ctx context.Context, request *models.CreateFastLinkReq) (*models.CreateFastLinkResp, error) {
|
||||
//TODO:IMPLEMENT ME
|
||||
|
||||
return &models.CreateFastLinkResp{}, nil
|
||||
|
||||
}
|
@ -1,272 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"context"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"time"
|
||||
)
|
||||
|
||||
// структура для горутины чтобы ошибки не пропускать
|
||||
type countResult struct {
|
||||
count int64
|
||||
err error
|
||||
}
|
||||
|
||||
type PromoCodeRepository struct {
|
||||
mdb *mongo.Collection
|
||||
}
|
||||
|
||||
func NewPromoCodeRepository(mdb *mongo.Collection) *PromoCodeRepository {
|
||||
return &PromoCodeRepository{mdb: mdb}
|
||||
}
|
||||
|
||||
func InitPromoCodeIndexes(ctx context.Context, mdb *mongo.Collection) error {
|
||||
uniqueIndexModel := mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "codeword", Value: 1},
|
||||
{Key: "delete", Value: 1},
|
||||
},
|
||||
Options: options.Index().SetUnique(true).SetPartialFilterExpression(bson.M{"delete": false}),
|
||||
}
|
||||
_, err := mdb.Indexes().CreateOne(ctx, uniqueIndexModel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
textIndexModel := mongo.IndexModel{
|
||||
Keys: bson.D{
|
||||
{Key: "codeword", Value: "text"},
|
||||
{Key: "description", Value: "text"},
|
||||
{Key: "greetings", Value: "text"},
|
||||
},
|
||||
Options: options.Index().SetName("TextIndex"),
|
||||
}
|
||||
_, err = mdb.Indexes().CreateOne(ctx, textIndexModel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PromoCodeRepository) CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error) {
|
||||
req.CreatedAt = time.Now()
|
||||
req.ID = primitive.NewObjectID()
|
||||
if req.FastLinks == nil {
|
||||
req.FastLinks = []string{}
|
||||
}
|
||||
|
||||
_, err := r.mdb.InsertOne(ctx, req)
|
||||
if err != nil {
|
||||
if writeErr, ok := err.(mongo.WriteException); ok {
|
||||
for _, writeError := range writeErr.WriteErrors {
|
||||
if writeError.Code == 11000 {
|
||||
return nil, ErrDuplicateCodeword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (r *PromoCodeRepository) EditPromoCode(ctx context.Context, req *models.ReqEditPromoCode) (*models.PromoCode, error) {
|
||||
promoCodeID, err := primitive.ObjectIDFromHex(req.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updateFields := bson.M{}
|
||||
if req.Description != nil {
|
||||
updateFields["description"] = *req.Description
|
||||
}
|
||||
if req.Greetings != nil {
|
||||
updateFields["greetings"] = *req.Greetings
|
||||
}
|
||||
if req.DueTo != nil {
|
||||
updateFields["dueTo"] = *req.DueTo
|
||||
}
|
||||
if req.ActivationCount != nil {
|
||||
updateFields["activationCount"] = *req.ActivationCount
|
||||
}
|
||||
if req.Delete != nil {
|
||||
updateFields["delete"] = *req.Delete
|
||||
}
|
||||
|
||||
if len(updateFields) == 0 {
|
||||
return r.GetPromoCodeByID(ctx, promoCodeID)
|
||||
}
|
||||
|
||||
update := bson.M{"$set": updateFields}
|
||||
result, err := r.mdb.UpdateOne(ctx, bson.M{"_id": promoCodeID}, update)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.MatchedCount == 0 {
|
||||
return nil, ErrPromoCodeNotFound
|
||||
}
|
||||
|
||||
return r.GetPromoCodeByID(ctx, promoCodeID)
|
||||
}
|
||||
|
||||
func (r *PromoCodeRepository) GetPromoCodeByID(ctx context.Context, promoCodeID primitive.ObjectID) (*models.PromoCode, error) {
|
||||
var promoCode models.PromoCode
|
||||
err := r.mdb.FindOne(ctx, bson.M{"_id": promoCodeID}).Decode(&promoCode)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return nil, ErrPromoCodeNotFound
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &promoCode, nil
|
||||
}
|
||||
|
||||
func (r *PromoCodeRepository) GetPromoCodesList(ctx context.Context, req *models.GetPromoCodesListReq) ([]models.PromoCode, int64, error) {
|
||||
filter := bson.M{}
|
||||
|
||||
if req.Filter.Text != "" {
|
||||
filter["$text"] = bson.M{"$search": req.Filter.Text}
|
||||
}
|
||||
|
||||
if req.Filter.Active {
|
||||
filter["delete"] = false
|
||||
filter["outdated"] = false
|
||||
filter["offLimit"] = false
|
||||
} else {
|
||||
filter["$or"] = []interface{}{
|
||||
bson.M{"delete": true},
|
||||
bson.M{"outdated": true},
|
||||
bson.M{"offLimit": true},
|
||||
}
|
||||
}
|
||||
|
||||
opt := options.Find().SetSkip(int64(req.Page * req.Limit)).SetLimit(int64(req.Limit))
|
||||
|
||||
var countChan = make(chan countResult)
|
||||
go func() {
|
||||
defer close(countChan)
|
||||
count, err := r.mdb.CountDocuments(ctx, filter)
|
||||
countChan <- countResult{count, err}
|
||||
}()
|
||||
|
||||
cursor, err := r.mdb.Find(ctx, filter, opt)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
|
||||
var promoCodes = make([]models.PromoCode, 0)
|
||||
for cursor.Next(ctx) {
|
||||
var p models.PromoCode
|
||||
if err := cursor.Decode(&p); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
promoCodes = append(promoCodes, p)
|
||||
}
|
||||
|
||||
if err := cursor.Err(); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
result := <-countChan
|
||||
if result.err != nil {
|
||||
return nil, 0, result.err
|
||||
}
|
||||
count := result.count
|
||||
|
||||
return promoCodes, count, nil
|
||||
}
|
||||
|
||||
func (r *PromoCodeRepository) ActivatePromo(ctx context.Context, req *models.ActivateReq) (*models.PromoCode, error) {
|
||||
var promoCode models.PromoCode
|
||||
|
||||
var filter bson.M
|
||||
if req.Codeword != "" {
|
||||
filter = bson.M{
|
||||
"codeword": req.Codeword,
|
||||
}
|
||||
} else if req.FastLink != "" {
|
||||
filter = bson.M{
|
||||
"fastLinks": req.FastLink,
|
||||
}
|
||||
}
|
||||
|
||||
opts := options.FindOneAndUpdate().SetReturnDocument(options.After)
|
||||
|
||||
err := r.mdb.FindOneAndUpdate(ctx, filter, bson.M{"$inc": bson.M{"activationCount": -1}}, opts).Decode(&promoCode)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return nil, ErrPromoCodeNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if promoCode.ActivationCount <= 0 && promoCode.DueTo > time.Now().Unix() {
|
||||
if !promoCode.OffLimit {
|
||||
update := bson.M{"$set": bson.M{"offLimit": true}}
|
||||
_, err := r.mdb.UpdateOne(ctx, filter, update)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return &promoCode, nil
|
||||
}
|
||||
|
||||
func (r *PromoCodeRepository) IncreaseActivationCount(ctx context.Context, promoCodeID primitive.ObjectID) error {
|
||||
filter := bson.M{"_id": promoCodeID}
|
||||
update := bson.M{"$inc": bson.M{"activationCount": 1}}
|
||||
_, err := r.mdb.UpdateOne(ctx, filter, update)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PromoCodeRepository) DeletePromoCode(ctx context.Context, promoCodeID string) error {
|
||||
id, err := primitive.ObjectIDFromHex(promoCodeID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result, err := r.mdb.UpdateOne(
|
||||
ctx,
|
||||
bson.M{"_id": id, "delete": false},
|
||||
bson.M{"$set": bson.M{"delete": true}},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if result.MatchedCount == 0 {
|
||||
return ErrPromoCodeNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *PromoCodeRepository) AddFastLink(ctx context.Context, promoCodeID primitive.ObjectID, xid string) error {
|
||||
filter := bson.M{"_id": promoCodeID, "delete": false}
|
||||
update := bson.M{"$push": bson.M{"fastLinks": xid}}
|
||||
|
||||
result, err := r.mdb.UpdateOne(ctx, filter, update)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if result.MatchedCount == 0 {
|
||||
return ErrPromoCodeNotFound
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"context"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"time"
|
||||
)
|
||||
|
||||
type StatsRepository struct {
|
||||
mdb *mongo.Collection
|
||||
}
|
||||
|
||||
func NewStatsRepository(deps Deps) *StatsRepository {
|
||||
|
||||
return &StatsRepository{mdb: deps.Mdb}
|
||||
}
|
||||
|
||||
func (r *StatsRepository) UpdateStatistics(ctx context.Context, req *models.ActivateReq, promoCode *models.PromoCode, userID string) error {
|
||||
filter := bson.M{"_id": promoCode.ID, "usageMap." + userID: bson.M{"$exists": true}}
|
||||
count, err := r.mdb.CountDocuments(ctx, filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count >= 1 {
|
||||
return ErrPromoCodeAlreadyActivated
|
||||
}
|
||||
|
||||
var key string
|
||||
if req.FastLink != "" {
|
||||
key = req.FastLink
|
||||
} else {
|
||||
key = req.Codeword
|
||||
}
|
||||
|
||||
usage := models.Usage{
|
||||
Key: key,
|
||||
Time: time.Now(),
|
||||
}
|
||||
|
||||
update := bson.M{
|
||||
"$inc": bson.M{"usageCount": 1},
|
||||
"$push": bson.M{
|
||||
"usageMap." + userID: usage,
|
||||
},
|
||||
}
|
||||
|
||||
opts := options.Update().SetUpsert(true)
|
||||
_, err = r.mdb.UpdateOne(ctx, bson.M{"_id": promoCode.ID}, update, opts)
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *StatsRepository) GetStatistics(ctx context.Context, promoCodeID string) (*models.PromoCodeStats, error) {
|
||||
objID, err := primitive.ObjectIDFromHex(promoCodeID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filter := bson.M{"_id": objID}
|
||||
|
||||
var promoCodeStats models.PromoCodeStats
|
||||
err = r.mdb.FindOne(ctx, filter).Decode(&promoCodeStats)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &promoCodeStats, nil
|
||||
}
|
52
internal/repository/recover.go
Normal file
52
internal/repository/recover.go
Normal file
@ -0,0 +1,52 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type RecoverRepository struct {
|
||||
queries *sqlcgen.Queries
|
||||
pool *sql.DB
|
||||
}
|
||||
|
||||
type RecoverDeps struct {
|
||||
Queries *sqlcgen.Queries
|
||||
Pool *sql.DB
|
||||
}
|
||||
|
||||
func NewRecoverRepository(deps RecoverDeps) *RecoverRepository {
|
||||
return &RecoverRepository{
|
||||
queries: deps.Queries,
|
||||
pool: deps.Pool,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RecoverRepository) Liveness(ctx context.Context) error {
|
||||
//TODO:IMPLEMENT ME
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *RecoverRepository) Recovery(ctx context.Context, request *models.RecoveryReq) error {
|
||||
//TODO:IMPLEMENT ME
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *RecoverRepository) Recoverylink(ctx context.Context) error {
|
||||
//TODO:IMPLEMENT ME
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (r *RecoverRepository) Readiness(ctx context.Context) error {
|
||||
//TODO:IMPLEMENT ME
|
||||
|
||||
return nil
|
||||
|
||||
}
|
31
internal/repository/stats.go
Normal file
31
internal/repository/stats.go
Normal file
@ -0,0 +1,31 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type StatsRepository struct {
|
||||
queries *sqlcgen.Queries
|
||||
pool *sql.DB
|
||||
}
|
||||
|
||||
type StatsDeps struct {
|
||||
Queries *sqlcgen.Queries
|
||||
Pool *sql.DB
|
||||
}
|
||||
|
||||
func NewStatsRepository(deps StatsDeps) *StatsRepository {
|
||||
return &StatsRepository{
|
||||
queries: deps.Queries,
|
||||
pool: deps.Pool,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *StatsRepository) Getstats(ctx context.Context, request *models.PromoCodeStatsReq) (*models.PromoCodeStatsResp, error) {
|
||||
//TODO:IMPLEMENT ME
|
||||
|
||||
return &models.PromoCodeStatsResp{}, nil
|
||||
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"context"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
Mdb *mongo.Collection
|
||||
Rdb *redis.Client
|
||||
}
|
||||
|
||||
type UserRepository struct {
|
||||
mdb *mongo.Collection
|
||||
}
|
||||
|
||||
func NewUserRepository(deps Deps) *UserRepository {
|
||||
|
||||
return &UserRepository{mdb: deps.Mdb}
|
||||
}
|
||||
|
||||
// ищем пользователя по мейлу в коллекции users
|
||||
func (r *UserRepository) FindByEmail(ctx context.Context, email string) (*models.User, error) {
|
||||
var user models.User
|
||||
|
||||
err := r.mdb.FindOne(ctx, bson.M{"login": email}).Decode(&user)
|
||||
if err != nil {
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, ErrPromoUserNotFound
|
||||
}
|
||||
return &user, nil
|
||||
}
|
@ -3,17 +3,15 @@ package http
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type ServerConfig struct {
|
||||
Logger *zap.Logger
|
||||
Controllers []Controller
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
Logger *zap.Logger
|
||||
Controllers []Controller
|
||||
app *fiber.App
|
||||
}
|
||||
@ -22,7 +20,6 @@ func NewServer(config ServerConfig) *Server {
|
||||
app := fiber.New()
|
||||
|
||||
s := &Server{
|
||||
Logger: config.Logger,
|
||||
Controllers: config.Controllers,
|
||||
app: app,
|
||||
}
|
||||
@ -34,7 +31,6 @@ func NewServer(config ServerConfig) *Server {
|
||||
|
||||
func (s *Server) Start(addr string) error {
|
||||
if err := s.app.Listen(addr); err != nil {
|
||||
s.Logger.Error("Failed to start server", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
77
internal/service/promocode.go
Normal file
77
internal/service/promocode.go
Normal file
@ -0,0 +1,77 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/repository"
|
||||
"context"
|
||||
)
|
||||
|
||||
type PromocodeService struct {
|
||||
PromocodeRepository *repository.PromocodeRepository
|
||||
}
|
||||
|
||||
func NewPromocodeService(repository *repository.PromocodeRepository) *PromocodeService {
|
||||
return &PromocodeService{
|
||||
PromocodeRepository: repository,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PromocodeService) Activate(ctx context.Context, request *models.ActivateReq) (*models.ActivateResp, error) {
|
||||
|
||||
response, err := s.PromocodeRepository.Activate(ctx, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
|
||||
}
|
||||
|
||||
func (s *PromocodeService) Getlist(ctx context.Context, request *models.GetPromoCodesListReq) (*models.GetPromoCodesListResp, error) {
|
||||
|
||||
response, err := s.PromocodeRepository.Getlist(ctx, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
|
||||
}
|
||||
|
||||
func (s *PromocodeService) Delete(ctx context.Context) error {
|
||||
|
||||
err := s.PromocodeRepository.Delete(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (s *PromocodeService) Createpromocode(ctx context.Context, request *models.PromoCodeReq) (*models.PromoCode, error) {
|
||||
|
||||
response, err := s.PromocodeRepository.Createpromocode(ctx, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
|
||||
}
|
||||
|
||||
func (s *PromocodeService) Editpromocode(ctx context.Context, request *models.EditPromoCodeReq) (*models.PromoCode, error) {
|
||||
|
||||
response, err := s.PromocodeRepository.Editpromocode(ctx, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
|
||||
}
|
||||
|
||||
func (s *PromocodeService) Createfastlink(ctx context.Context, request *models.CreateFastLinkReq) (*models.CreateFastLinkResp, error) {
|
||||
|
||||
response, err := s.PromocodeRepository.Createfastlink(ctx, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
|
||||
}
|
57
internal/service/recover.go
Normal file
57
internal/service/recover.go
Normal file
@ -0,0 +1,57 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/repository"
|
||||
"context"
|
||||
)
|
||||
|
||||
type RecoverService struct {
|
||||
RecoverRepository *repository.RecoverRepository
|
||||
}
|
||||
|
||||
func NewRecoverService(repository *repository.RecoverRepository) *RecoverService {
|
||||
return &RecoverService{
|
||||
RecoverRepository: repository,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *RecoverService) Liveness(ctx context.Context) error {
|
||||
|
||||
err := s.RecoverRepository.Liveness(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (s *RecoverService) Recovery(ctx context.Context, request *models.RecoveryReq) error {
|
||||
|
||||
err := s.RecoverRepository.Recovery(ctx, request)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (s *RecoverService) Recoverylink(ctx context.Context) error {
|
||||
|
||||
err := s.RecoverRepository.Recoverylink(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (s *RecoverService) Readiness(ctx context.Context) error {
|
||||
|
||||
err := s.RecoverRepository.Readiness(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
27
internal/service/stats.go
Normal file
27
internal/service/stats.go
Normal file
@ -0,0 +1,27 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/repository"
|
||||
"context"
|
||||
)
|
||||
|
||||
type StatsService struct {
|
||||
StatsRepository *repository.StatsRepository
|
||||
}
|
||||
|
||||
func NewStatsService(repository *repository.StatsRepository) *StatsService {
|
||||
return &StatsService{
|
||||
StatsRepository: repository,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StatsService) Getstats(ctx context.Context, request *models.PromoCodeStatsReq) (*models.PromoCodeStatsResp, error) {
|
||||
|
||||
response, err := s.StatsRepository.Getstats(ctx, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
|
||||
}
|
@ -1,211 +0,0 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"codeword/internal/kafka/tariff"
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/proto/discount"
|
||||
"codeword/internal/repository"
|
||||
"codeword/internal/utils/genID"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PromoCodeRepository interface {
|
||||
CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error)
|
||||
EditPromoCode(ctx context.Context, req *models.ReqEditPromoCode) (*models.PromoCode, error)
|
||||
GetPromoCodesList(ctx context.Context, req *models.GetPromoCodesListReq) ([]models.PromoCode, int64, error)
|
||||
ActivatePromo(ctx context.Context, req *models.ActivateReq) (*models.PromoCode, error)
|
||||
DeletePromoCode(ctx context.Context, promoCodeID string) error
|
||||
GetPromoCodeByID(ctx context.Context, promoCodeID primitive.ObjectID) (*models.PromoCode, error)
|
||||
AddFastLink(ctx context.Context, promoCodeID primitive.ObjectID, xid string) error
|
||||
IncreaseActivationCount(ctx context.Context, promoCodeID primitive.ObjectID) error
|
||||
}
|
||||
|
||||
type PromoStatsRepository interface {
|
||||
UpdateStatistics(ctx context.Context, req *models.ActivateReq, promoCode *models.PromoCode, userID string) error
|
||||
GetStatistics(ctx context.Context, promoCodeID string) (*models.PromoCodeStats, error)
|
||||
}
|
||||
|
||||
type PromoDeps struct {
|
||||
Logger *zap.Logger
|
||||
PromoCodeRepo PromoCodeRepository
|
||||
StatsRepo PromoStatsRepository
|
||||
Kafka *tariff.Producer
|
||||
DiscountClient discount.DiscountServiceClient
|
||||
}
|
||||
|
||||
type PromoCodeService struct {
|
||||
logger *zap.Logger
|
||||
promoCodeRepo PromoCodeRepository
|
||||
statsRepo PromoStatsRepository
|
||||
kafka *tariff.Producer
|
||||
discountClient discount.DiscountServiceClient
|
||||
}
|
||||
|
||||
func NewPromoCodeService(deps PromoDeps) *PromoCodeService {
|
||||
return &PromoCodeService{
|
||||
logger: deps.Logger,
|
||||
promoCodeRepo: deps.PromoCodeRepo,
|
||||
statsRepo: deps.StatsRepo,
|
||||
kafka: deps.Kafka,
|
||||
discountClient: deps.DiscountClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PromoCodeService) CreatePromoCode(ctx context.Context, req *models.PromoCode) (*models.PromoCode, error) {
|
||||
promoCode, err := s.promoCodeRepo.CreatePromoCode(ctx, req)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to add promocode in database", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return promoCode, nil
|
||||
}
|
||||
|
||||
func (s *PromoCodeService) EditPromoCode(ctx context.Context, req *models.ReqEditPromoCode) (*models.PromoCode, error) {
|
||||
editedPromoCode, err := s.promoCodeRepo.EditPromoCode(ctx, req)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to edit promocode in database", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return editedPromoCode, nil
|
||||
}
|
||||
|
||||
func (s *PromoCodeService) GetPromoCodesList(ctx context.Context, req *models.GetPromoCodesListReq) ([]models.PromoCode, int64, error) {
|
||||
promoCodes, count, err := s.promoCodeRepo.GetPromoCodesList(ctx, req)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to get list promocodes from database", zap.Error(err))
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return promoCodes, count, nil
|
||||
}
|
||||
|
||||
// todo одумать еще реализацию этого дела, надо уточнить как разделяется ответственность в бонусе между привилегией и скидкой
|
||||
// разделяется ли она или они всегда вместе, если разделяются то что-то из этого может быть пустым либо все заполеннное,
|
||||
// соответсвенно надо сделать соответствующие проверки до записи в кафку и до отправки в дискаунт сервис
|
||||
|
||||
func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.ActivateReq, userID string) (string, error) {
|
||||
promoCode, err := s.promoCodeRepo.ActivatePromo(ctx, req)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to activate promocode", zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
//todo такая реализация проверок кажется довольно массивной, думаю как то это стоит сделать параллельно обхаживая все условия
|
||||
if promoCode.DueTo < time.Now().Unix() && promoCode.OffLimit {
|
||||
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", fmt.Errorf("%w: expired on %s", repository.ErrPromoCodeExpired, time.Unix(promoCode.DueTo, 0).Format(time.RFC3339))
|
||||
}
|
||||
|
||||
if promoCode.DueTo == 0 && promoCode.ActivationCount < 0 {
|
||||
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", repository.ErrPromoCodeExhausted
|
||||
}
|
||||
|
||||
err = s.statsRepo.UpdateStatistics(ctx, req, promoCode, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, repository.ErrPromoCodeAlreadyActivated) {
|
||||
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "", repository.ErrPromoCodeAlreadyActivated
|
||||
}
|
||||
s.logger.Error("Failed add in stats", zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
|
||||
var postfix string
|
||||
|
||||
if req.FastLink != "" {
|
||||
postfix = fmt.Sprintf(":(%s)", req.FastLink)
|
||||
}
|
||||
|
||||
var privileges []models.Privilege
|
||||
privilege := models.Privilege{
|
||||
PrivilegeID: promoCode.Bonus.Privilege.PrivilegeID,
|
||||
Amount: promoCode.Bonus.Privilege.Amount,
|
||||
}
|
||||
privileges = append(privileges, privilege)
|
||||
|
||||
fakeTariff := &models.Tariff{
|
||||
Name: promoCode.Codeword + postfix,
|
||||
Privileges: privileges,
|
||||
Deleted: promoCode.Delete,
|
||||
CreatedAt: promoCode.CreatedAt,
|
||||
}
|
||||
if err := s.kafka.Send(ctx, userID, fakeTariff); err != nil {
|
||||
s.logger.Error("Failed to send fake tariff to Kafka", zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
|
||||
disOverHelm := true
|
||||
discountRequest := &discount.CreateDiscountRequest{
|
||||
Name: promoCode.Codeword + postfix,
|
||||
Layer: promoCode.Bonus.Discount.Layer,
|
||||
Description: "",
|
||||
Condition: &discount.DiscountCondition{
|
||||
Coupon: &promoCode.Codeword,
|
||||
User: &userID,
|
||||
},
|
||||
Target: &discount.DiscountCalculationTarget{
|
||||
Factor: promoCode.Bonus.Discount.Factor,
|
||||
Overhelm: &disOverHelm,
|
||||
},
|
||||
}
|
||||
|
||||
_, err = s.discountClient.CreateDiscount(ctx, discountRequest)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to create discount", zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
|
||||
return promoCode.Greetings, nil
|
||||
}
|
||||
|
||||
func (s *PromoCodeService) DeletePromoCode(ctx context.Context, promoCodeID string) error {
|
||||
err := s.promoCodeRepo.DeletePromoCode(ctx, promoCodeID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed simple delete promocode from database", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PromoCodeService) CreateFastLink(ctx context.Context, promoCodeID string) (string, error) {
|
||||
xid := genID.GenerateXID()
|
||||
promoID, err := primitive.ObjectIDFromHex(promoCodeID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed conversion promoCodeID to ObjectID", zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = s.promoCodeRepo.AddFastLink(ctx, promoID, xid)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to add fastlink for promocode by promocode id", zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
|
||||
return xid, nil
|
||||
}
|
||||
|
||||
func (s *PromoCodeService) GetStats(ctx context.Context, promoCodeID string) (*models.PromoCodeStats, error) {
|
||||
promoStats, err := s.statsRepo.GetStatistics(ctx, promoCodeID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed getting promo stats", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return promoStats, nil
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"codeword/internal/adapters/client"
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/utils/encrypt"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type CodewordRepository interface {
|
||||
StoreRecoveryRecord(ctx context.Context, deps models.StoreRecDeps) (string, error)
|
||||
InsertToQueue(ctx context.Context, deps models.RecEmailDeps) error
|
||||
Ping(ctx context.Context) error
|
||||
GetRecoveryRecord(ctx context.Context, key string) (*models.RestoreRequest, error)
|
||||
}
|
||||
|
||||
type UserRepository interface {
|
||||
FindByEmail(ctx context.Context, email string) (*models.User, error)
|
||||
}
|
||||
|
||||
type Deps struct {
|
||||
Logger *zap.Logger
|
||||
CodewordRepository CodewordRepository
|
||||
UserRepository UserRepository
|
||||
Encrypt *encrypt.Encrypt
|
||||
AuthClient *client.AuthClient
|
||||
}
|
||||
|
||||
type RecoveryService struct {
|
||||
logger *zap.Logger
|
||||
repositoryCodeword CodewordRepository
|
||||
repositoryUser UserRepository
|
||||
encrypt *encrypt.Encrypt
|
||||
authClient *client.AuthClient
|
||||
}
|
||||
|
||||
func NewRecoveryService(deps Deps) *RecoveryService {
|
||||
return &RecoveryService{
|
||||
logger: deps.Logger,
|
||||
repositoryCodeword: deps.CodewordRepository,
|
||||
repositoryUser: deps.UserRepository,
|
||||
encrypt: deps.Encrypt,
|
||||
authClient: deps.AuthClient,
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateKey генерирует ключ, используя шифрование на основе эллиптической кривой
|
||||
func (s *RecoveryService) GenerateKey() ([]byte, error) {
|
||||
key, err := s.encrypt.SignCommonSecret()
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to generate unique key for user", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// вызывает пингование в бд
|
||||
func (s *RecoveryService) Ping(ctx context.Context) error {
|
||||
err := s.repositoryCodeword.Ping(ctx)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to ping database", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindUserByEmail ищет пользователя по электронной почте
|
||||
func (s *RecoveryService) FindUserByEmail(ctx context.Context, email string) (*models.User, error) {
|
||||
user, err := s.repositoryUser.FindByEmail(ctx, email)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to find user by email", zap.String("email", email), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// StoreRecoveryRecord сохраняет запись восстановления в базе данных
|
||||
func (s *RecoveryService) StoreRecoveryRecord(ctx context.Context, deps models.StoreRecDeps) (string, error) {
|
||||
id, err := s.repositoryCodeword.StoreRecoveryRecord(ctx, models.StoreRecDeps{UserID: deps.UserID, Email: deps.Email, Key: deps.Key, Url: deps.Url})
|
||||
if err != nil {
|
||||
s.logger.Error("Failed save data in mongoDB for email", zap.String("email", deps.Email), zap.Error(err))
|
||||
return "", err
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// RecoveryEmailTask посылает письмо для восстановления доступа пользователю
|
||||
func (s *RecoveryService) RecoveryEmailTask(ctx context.Context, deps models.RecEmailDeps) error {
|
||||
err := s.repositoryCodeword.InsertToQueue(ctx, models.RecEmailDeps{UserID: deps.UserID, Email: deps.Email, SignWithID: deps.SignWithID, ID: deps.ID})
|
||||
if err != nil {
|
||||
s.logger.Error("Failed creating a task to send a worker by email", zap.String("email", deps.Email), zap.Error(err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRecoveryRecord получает запись восстановления из базы данных
|
||||
func (s *RecoveryService) GetRecoveryRecord(ctx context.Context, key string) (*models.RestoreRequest, error) {
|
||||
req, err := s.repositoryCodeword.GetRecoveryRecord(ctx, key)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to obtain signature recovery data", zap.String("signature", key), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
byteKey, err := base64.URLEncoding.DecodeString(req.Sign)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to decode string signature to []byte format", zap.String("signature", key), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// сомнительный вариант но как я думаю верный, что false==err
|
||||
result, err := s.encrypt.VerifySignature(byteKey)
|
||||
if err != nil || !result {
|
||||
s.logger.Error("Failed to verify signature", zap.String("signature", key), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// меняет подпись на токены идя в auth сервис
|
||||
func (s *RecoveryService) ExchangeForTokens(userID string, signature string) (map[string]string, error) {
|
||||
tokens, err := s.authClient.RefreshAuthToken(userID, signature)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to refresh auth token", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return map[string]string{
|
||||
"accessToken": tokens.AccessToken,
|
||||
"refreshToken": tokens.RefreshToken,
|
||||
}, nil
|
||||
}
|
5
internal/sqlc/db_query/queries.sql
Normal file
5
internal/sqlc/db_query/queries.sql
Normal file
@ -0,0 +1,5 @@
|
||||
-- name: Getlist :many
|
||||
SELECT *
|
||||
FROM {{ .TableName }} AS a
|
||||
ORDER BY a.created_at DESC
|
||||
LIMIT $2 OFFSET ($1-1)*$2;
|
@ -1,81 +0,0 @@
|
||||
package encrypt
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type EncryptDeps struct {
|
||||
PublicKey string
|
||||
PrivateKey string
|
||||
SignSecret string
|
||||
}
|
||||
|
||||
type Encrypt struct {
|
||||
publicKey string
|
||||
privateKey string
|
||||
signSecret string
|
||||
}
|
||||
|
||||
func New(deps *EncryptDeps) *Encrypt {
|
||||
return &Encrypt{
|
||||
publicKey: deps.PublicKey,
|
||||
privateKey: deps.PrivateKey,
|
||||
signSecret: deps.SignSecret,
|
||||
}
|
||||
}
|
||||
|
||||
func (receiver *Encrypt) VerifySignature(signature []byte) (isValid bool, err error) {
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
err = fmt.Errorf("recovered verify error on <VerifySignature> of <EncryptService>: %v", recovered)
|
||||
}
|
||||
}()
|
||||
|
||||
block, _ := pem.Decode([]byte(receiver.publicKey))
|
||||
if block == nil {
|
||||
return false, fmt.Errorf("public key block is nil")
|
||||
}
|
||||
|
||||
rawPublicKey, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed parse public key on <VerifySignature> of <EncryptService>: %w", err)
|
||||
}
|
||||
|
||||
publicKey, ok := rawPublicKey.(ed25519.PublicKey)
|
||||
if !ok {
|
||||
return false, errors.New("public key is not of type ed25519.PublicKey")
|
||||
}
|
||||
|
||||
return ed25519.Verify(publicKey, []byte(receiver.signSecret), signature), nil
|
||||
}
|
||||
|
||||
// TODO подумать над тем чтобы подпись генерилась каждый раз разгая
|
||||
func (receiver *Encrypt) SignCommonSecret() (signature []byte, err error) {
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
fmt.Printf("recovered sign error: \n%+v\n", receiver)
|
||||
err = fmt.Errorf("recovered sign error on <SignCommonSecret> of <EncryptService>: %v", recovered)
|
||||
}
|
||||
}()
|
||||
|
||||
block, _ := pem.Decode([]byte(receiver.privateKey))
|
||||
if block == nil {
|
||||
return []byte{}, fmt.Errorf("failed decode private key %s on <SignCommonSecret> of <EncryptService>: %w", receiver.privateKey, err)
|
||||
}
|
||||
|
||||
rawPrivateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
return []byte{}, fmt.Errorf("failed parse private key on <SignCommonSecret> of <EncryptService>: %w", err)
|
||||
}
|
||||
|
||||
privateKey, ok := rawPrivateKey.(ed25519.PrivateKey)
|
||||
if !ok {
|
||||
return []byte{}, fmt.Errorf("failed convert to ed25519.PrivateKey on <SignCommonSecret> of <EncryptService>: %w", err)
|
||||
}
|
||||
|
||||
return ed25519.Sign(privateKey, []byte(receiver.signSecret)), nil
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package genID
|
||||
|
||||
import "github.com/rs/xid"
|
||||
|
||||
func GenerateXID() string {
|
||||
id := xid.New()
|
||||
return id.String()
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
package transfer
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/proto/broker"
|
||||
)
|
||||
|
||||
func PrivilegeModelToProto(privilege *models.Privilege) *broker.PrivilegeMessage {
|
||||
if privilege == nil {
|
||||
return &broker.PrivilegeMessage{}
|
||||
}
|
||||
|
||||
return &broker.PrivilegeMessage{
|
||||
PrivilegeID: privilege.PrivilegeID,
|
||||
ServiceKey: privilege.ServiceKey,
|
||||
Type: models.PrivilegeBrokerTypeMap[privilege.Type],
|
||||
Value: privilege.Value,
|
||||
Amount: privilege.Amount,
|
||||
}
|
||||
}
|
||||
|
||||
func PrivilegeArrayModelToProto(privileges []models.Privilege) []*broker.PrivilegeMessage {
|
||||
privilegesProto := make([]*broker.PrivilegeMessage, len(privileges))
|
||||
|
||||
for index, privilege := range privileges {
|
||||
privilegeCopy := privilege
|
||||
privilegesProto[index] = PrivilegeModelToProto(&privilegeCopy)
|
||||
}
|
||||
|
||||
return privilegesProto
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
package transfer
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/proto/broker"
|
||||
)
|
||||
|
||||
func TariffModelToProtoMessage(userID string, tariffModel *models.Tariff) *broker.TariffMessage {
|
||||
if tariffModel == nil {
|
||||
return &broker.TariffMessage{}
|
||||
}
|
||||
|
||||
return &broker.TariffMessage{
|
||||
UserID: userID,
|
||||
Privileges: PrivilegeArrayModelToProto(tariffModel.Privileges),
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
package purge_worker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
Logger *zap.Logger
|
||||
Mongo *mongo.Collection
|
||||
}
|
||||
|
||||
type PurgeWorker struct {
|
||||
logger *zap.Logger
|
||||
mongo *mongo.Collection
|
||||
}
|
||||
|
||||
func NewPurgeWC(deps Deps) *PurgeWorker {
|
||||
return &PurgeWorker{
|
||||
logger: deps.Logger,
|
||||
mongo: deps.Mongo,
|
||||
}
|
||||
}
|
||||
|
||||
func (wc *PurgeWorker) Start(ctx context.Context) {
|
||||
ticker := time.NewTicker(1 * time.Hour)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
wc.processTasks(ctx)
|
||||
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wc *PurgeWorker) processTasks(ctx context.Context) {
|
||||
wc.logger.Info("Checking cleaning records")
|
||||
|
||||
oneHourAgo := time.Now().Add(-1 * time.Hour)
|
||||
|
||||
filter := bson.M{"created_at": bson.M{"$lt": oneHourAgo}}
|
||||
|
||||
result, err := wc.mongo.DeleteMany(ctx, filter)
|
||||
if err != nil {
|
||||
wc.logger.Error("Error when trying to delete old entries", zap.Error(err))
|
||||
} else {
|
||||
wc.logger.Info("Deleted documents", zap.Int64("count", result.DeletedCount))
|
||||
}
|
||||
}
|
||||
|
||||
func (wc *PurgeWorker) Stop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
@ -1,129 +0,0 @@
|
||||
package recovery_worker
|
||||
|
||||
import (
|
||||
"codeword/internal/adapters/client"
|
||||
"codeword/internal/models"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Deps struct {
|
||||
Logger *zap.Logger
|
||||
Redis *redis.Client
|
||||
EmailSender *client.RecoveryEmailSender
|
||||
Mongo *mongo.Collection
|
||||
}
|
||||
|
||||
type RecoveryWorker struct {
|
||||
logger *zap.Logger
|
||||
redis *redis.Client
|
||||
emailSender *client.RecoveryEmailSender
|
||||
mongo *mongo.Collection
|
||||
}
|
||||
|
||||
func NewRecoveryWC(deps Deps) *RecoveryWorker {
|
||||
return &RecoveryWorker{
|
||||
logger: deps.Logger,
|
||||
redis: deps.Redis,
|
||||
emailSender: deps.EmailSender,
|
||||
mongo: deps.Mongo,
|
||||
}
|
||||
}
|
||||
|
||||
func (wc *RecoveryWorker) Start(ctx context.Context) {
|
||||
ticker := time.NewTicker(10 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
wc.processTasks(ctx)
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wc *RecoveryWorker) processTasks(ctx context.Context) {
|
||||
var cursor uint64
|
||||
for {
|
||||
var keys []string
|
||||
var err error
|
||||
keys, cursor, err = wc.redis.Scan(ctx, cursor, "email:task:*", 0).Result()
|
||||
if err != nil {
|
||||
wc.logger.Error("Failed to scan for email tasks", zap.Error(err))
|
||||
break
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
taskBytes, err := wc.redis.GetDel(ctx, key).Result()
|
||||
if err == redis.Nil {
|
||||
continue
|
||||
} else if err != nil {
|
||||
wc.logger.Error("Failed to getdel recovery task", zap.String("key", key), zap.Error(err))
|
||||
continue
|
||||
}
|
||||
|
||||
var task models.RecoveryRecord
|
||||
if json.Unmarshal([]byte(taskBytes), &task) != nil {
|
||||
wc.logger.Error("Failed to unmarshal recovery task", zap.String("key", key), zap.String("task", taskBytes))
|
||||
continue
|
||||
}
|
||||
|
||||
err = wc.sendRecoveryTask(ctx, task)
|
||||
if err != nil {
|
||||
wc.logger.Error("Failed to send recovery task", zap.String("key", key), zap.Error(err))
|
||||
}
|
||||
}
|
||||
if cursor == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wc *RecoveryWorker) sendRecoveryTask(ctx context.Context, task models.RecoveryRecord) error {
|
||||
err := wc.emailSender.SendRecoveryEmail(task.Email, task.Key)
|
||||
if err != nil {
|
||||
wc.logger.Error("Failed to send recovery email", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
update := bson.M{
|
||||
"$set": bson.M{
|
||||
"sent": true,
|
||||
"sent_at": time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
objectID, err := primitive.ObjectIDFromHex(task.ID)
|
||||
if err != nil {
|
||||
wc.logger.Error("Invalid ObjectID", zap.String("ID", task.ID), zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
filter := bson.M{"_id": objectID}
|
||||
|
||||
result, err := wc.mongo.UpdateOne(ctx, filter, update)
|
||||
if err != nil {
|
||||
wc.logger.Error("Failed to update restore request", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
if result.ModifiedCount == 0 {
|
||||
wc.logger.Warn("No documents were updated - this may indicate the document was not found",
|
||||
zap.String("ID", task.ID))
|
||||
}
|
||||
|
||||
//wc.logger.Info("Recovery email sent and restore request updated successfully", zap.String("email", task.Email))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wc *RecoveryWorker) Stop(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
@ -45,7 +45,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RecoveryRequest'
|
||||
$ref: '#/components/schemas/RecoveryReq'
|
||||
responses:
|
||||
'200':
|
||||
description: Запрос на восстановление принят
|
||||
@ -109,7 +109,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ReqEditPromoCode'
|
||||
$ref: '#/components/schemas/EditPromoCodeReq'
|
||||
responses:
|
||||
'200':
|
||||
description: Промокод успешно обновлен
|
||||
@ -208,14 +208,14 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreateFastLinkRequest'
|
||||
$ref: '#/components/schemas/CreateFastLinkReq'
|
||||
responses:
|
||||
'200':
|
||||
description: Быстрая ссылка для промокода успешно создана
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreateFastLinkResponse'
|
||||
$ref: '#/components/schemas/CreateFastLinkResp'
|
||||
'400':
|
||||
description: Неверный запрос, отсутствует идентификатор промокода
|
||||
'404':
|
||||
@ -235,14 +235,14 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PromoCodeStatsRequest'
|
||||
$ref: '#/components/schemas/PromoCodeStatsReq'
|
||||
responses:
|
||||
'200':
|
||||
description: Статистика промокода успешно получена
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/PromoCodeStats'
|
||||
$ref: '#/components/schemas/PromoCodeStatsResp'
|
||||
'400':
|
||||
description: Неверный запрос
|
||||
'500':
|
||||
@ -251,7 +251,7 @@ paths:
|
||||
|
||||
components:
|
||||
schemas:
|
||||
RecoveryRequest:
|
||||
RecoveryReq:
|
||||
type: object
|
||||
required:
|
||||
- email
|
||||
@ -263,14 +263,14 @@ components:
|
||||
redirectionURL:
|
||||
type: string
|
||||
description: URL-адрес, на который перенаправляется пользователь
|
||||
PromoCodeStatsRequest:
|
||||
PromoCodeStatsReq:
|
||||
type: object
|
||||
properties:
|
||||
promoCodeID:
|
||||
type: string
|
||||
required:
|
||||
- promoCodeID
|
||||
PromoCodeStats:
|
||||
PromoCodeStatsResp:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
@ -298,7 +298,7 @@ components:
|
||||
format: date-time
|
||||
description: Время использования промокода
|
||||
|
||||
CreateFastLinkRequest:
|
||||
CreateFastLinkReq:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
@ -307,7 +307,7 @@ components:
|
||||
required:
|
||||
- id
|
||||
|
||||
CreateFastLinkResponse:
|
||||
CreateFastLinkResp:
|
||||
type: object
|
||||
properties:
|
||||
fastlink:
|
||||
@ -393,33 +393,8 @@ components:
|
||||
bonus:
|
||||
type: object
|
||||
description: Бонус, предоставляемый с промокодом
|
||||
properties:
|
||||
privilege:
|
||||
type: object
|
||||
description: Привилегия
|
||||
properties:
|
||||
privilegeID:
|
||||
type: string
|
||||
description: Идентификатор привилегии
|
||||
amount:
|
||||
type: integer
|
||||
description: Количество привилегии
|
||||
discount:
|
||||
type: object
|
||||
description: Скидка
|
||||
properties:
|
||||
layer:
|
||||
type: integer
|
||||
description: Уровень скидки
|
||||
factor:
|
||||
type: number
|
||||
description: Множитель скидки
|
||||
target:
|
||||
type: string
|
||||
description: Слой
|
||||
threshold:
|
||||
type: integer
|
||||
description: Граничное значение
|
||||
items:
|
||||
$ref: '#/components/schemas/Bonus'
|
||||
outdated:
|
||||
type: boolean
|
||||
description: Флаг
|
||||
@ -439,7 +414,7 @@ components:
|
||||
type: string
|
||||
description: Список быстрых ссылок для активации промокода
|
||||
|
||||
ReqEditPromoCode:
|
||||
EditPromoCodeReq:
|
||||
type: object
|
||||
properties:
|
||||
ID:
|
||||
@ -486,35 +461,48 @@ components:
|
||||
bonus:
|
||||
type: object
|
||||
description: Бонус
|
||||
properties:
|
||||
privilege:
|
||||
type: object
|
||||
description: Привилегия
|
||||
properties:
|
||||
privilegeID:
|
||||
type: string
|
||||
description: Идентификатор привилегии
|
||||
amount:
|
||||
type: integer
|
||||
description: Количество привилегии
|
||||
discount:
|
||||
type: object
|
||||
description: Скидка
|
||||
properties:
|
||||
layer:
|
||||
type: integer
|
||||
description: Уровень скидки
|
||||
factor:
|
||||
type: number
|
||||
description: Множитель скидки
|
||||
target:
|
||||
type: string
|
||||
description: Слой
|
||||
threshold:
|
||||
type: integer
|
||||
description: Граничное значение
|
||||
items:
|
||||
$ref: '#/components/schemas/Bonus'
|
||||
fastLinks:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Список быстрых ссылок для активации промокода
|
||||
|
||||
Bonus:
|
||||
type: object
|
||||
description: Бонус
|
||||
properties:
|
||||
privilege:
|
||||
$ref: '#/components/schemas/Privilege'
|
||||
discount:
|
||||
$ref: '#/components/schemas/Discount'
|
||||
|
||||
Privilege:
|
||||
type: object
|
||||
description: Привилегия
|
||||
properties:
|
||||
privilegeID:
|
||||
type: string
|
||||
description: Идентификатор привилегии
|
||||
amount:
|
||||
type: integer
|
||||
description: Количество привилегии
|
||||
|
||||
Discount:
|
||||
type: object
|
||||
description: Скидка
|
||||
properties:
|
||||
layer:
|
||||
type: integer
|
||||
description: Уровень скидки
|
||||
factor:
|
||||
type: integer
|
||||
description: Множитель скидки
|
||||
target:
|
||||
type: string
|
||||
description: Цель скидки
|
||||
threshold:
|
||||
type: integer
|
||||
description: Порог скидки
|
||||
|
@ -1,37 +0,0 @@
|
||||
package closer
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type Closer interface {
|
||||
Close(ctx context.Context) error
|
||||
}
|
||||
|
||||
type CloserFunc func(ctx context.Context) error
|
||||
|
||||
func (cf CloserFunc) Close(ctx context.Context) error {
|
||||
return cf(ctx)
|
||||
}
|
||||
|
||||
type CloserGroup struct {
|
||||
closers []Closer
|
||||
}
|
||||
|
||||
func NewCloserGroup() *CloserGroup {
|
||||
return &CloserGroup{}
|
||||
}
|
||||
|
||||
func (cg *CloserGroup) Add(c Closer) {
|
||||
cg.closers = append(cg.closers, c)
|
||||
}
|
||||
|
||||
func (cg *CloserGroup) Call(ctx context.Context) error {
|
||||
var closeErr error
|
||||
for i := len(cg.closers) - 1; i >= 0; i-- {
|
||||
if err := cg.closers[i].Close(ctx); err != nil && closeErr == nil {
|
||||
closeErr = err
|
||||
}
|
||||
}
|
||||
return closeErr
|
||||
}
|
15
sqlc.yaml
Normal file
15
sqlc.yaml
Normal file
@ -0,0 +1,15 @@
|
||||
version: "1"
|
||||
packages:
|
||||
- name: "sqlcgen"
|
||||
path: ".internal/sqlc/sqlcgen"
|
||||
queries: ".internal/sqlc/db_query/queries.sql"
|
||||
schema:
|
||||
- "todo"
|
||||
engine: "postgresql"
|
||||
emit_json_tags: true
|
||||
emit_db_tags: true
|
||||
emit_prepared_queries: false
|
||||
emit_interface: false
|
||||
emit_exact_table_names: false
|
||||
emit_empty_slices: false
|
||||
sql_package: "database/sql"
|
@ -7,14 +7,14 @@ import (
|
||||
|
||||
"{{.Vars.ProjectName}}/internal/app"
|
||||
|
||||
{{.Modules.logger.Import}}
|
||||
{{.Modules.logger.ImportCore}}
|
||||
"{{.Modules.logger.Import}}"
|
||||
"{{.Modules.logger.ImportCore}}"
|
||||
|
||||
{{.Modules.env.Import}}
|
||||
"{{.Modules.env.Import}}"
|
||||
)
|
||||
|
||||
func main() {
|
||||
{{.Modules.logger.Init}}
|
||||
{{.Modules.logger.Declaration "logger"}}
|
||||
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer cancel()
|
||||
@ -26,4 +26,4 @@ func main() {
|
||||
if err := app.Run(ctx, config, logger); err != nil {
|
||||
{{.Modules.logger.Message "Fatal" "Failed to run app" "Error" "err"}}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,17 +3,65 @@ package app
|
||||
import (
|
||||
"context"
|
||||
|
||||
{{.Modules.logger.Import}}
|
||||
"{{.Modules.logger.Import}}"
|
||||
)
|
||||
|
||||
{{.Modules.env.Struct}}
|
||||
|
||||
func Run(ctx context.Context, config Config, logger {{.Modules.logger.Type}}) error {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Error("Recovered from a panic", zap.Any("error", r))
|
||||
}
|
||||
}()
|
||||
|
||||
{{.Modules.logger.Message "info" "App started" "config" "config"}}
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
// Инициализация репозиториев
|
||||
{{range $key, $value := .LayersData.Repositories}}
|
||||
{{$key}}Repository := {{$value.PackageName}}.New{{$value.Name}}Repository()
|
||||
{{end}}
|
||||
|
||||
// Инициализация сервисов
|
||||
{{range $key, $value := .LayersData.Services}}
|
||||
{{$key}}Service := {{$value.PackageName}}.New{{$value.Name}}Service({{$key}}Repository)
|
||||
{{end}}
|
||||
|
||||
// Инициализация контроллеров
|
||||
{{range $key, $value := .LayersData.Controllers}}
|
||||
{{$key}}Controller := {{$value.PackageName}}.New{{$value.Name}}Controller({{$key}}Service)
|
||||
{{end}}
|
||||
|
||||
// Создание сервера
|
||||
server := {{.LayersData.ServerData}}.NewServer({{.LayersData.ServerData}}.ServerConfig{
|
||||
Controllers: []{{.LayersData.ServerData}}.Controller{
|
||||
{{range $key, $value := .LayersData.Controllers}}
|
||||
{{$key}}Controller,
|
||||
{{end}}
|
||||
},
|
||||
})
|
||||
|
||||
go func() {
|
||||
err := server.Start("Host + : + Port")
|
||||
if err != nil {
|
||||
logger.Error("Server startup error", zap.Error(err))
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
|
||||
// Вывод маршрутов
|
||||
server.ListRoutes()
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
logger.Info("App shutting down gracefully")
|
||||
|
||||
//TODO
|
||||
// Остановка сервера
|
||||
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
@ -1,599 +0,0 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/tests/helpers"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/pioz/faker"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var promoID string
|
||||
var fastLink string
|
||||
|
||||
// CreatePromoCode
|
||||
func TestCreatePromoCode(t *testing.T) {
|
||||
client := fiber.AcquireClient()
|
||||
|
||||
t.Run("CreatePromoCode-success", func(t *testing.T) {
|
||||
for i := 0; i < 10; i++ {
|
||||
jsonString := `{
|
||||
"codeword": "example",
|
||||
"description": "Example description",
|
||||
"greetings": "Example greetings",
|
||||
"dueTo": 1734429225,
|
||||
"activationCount": 100,
|
||||
"bonus": {
|
||||
"privilege": {
|
||||
"privilegeID": "examplePrivilegeID",
|
||||
"amount": 50
|
||||
},
|
||||
"discount": {
|
||||
"layer": 1,
|
||||
"factor": 0.2,
|
||||
"target": "exampleTarget",
|
||||
"threshold": 500
|
||||
}
|
||||
},
|
||||
"outdated": false,
|
||||
"offLimit": false,
|
||||
"delete": false
|
||||
}`
|
||||
|
||||
var reqBody models.PromoCode
|
||||
err := json.Unmarshal([]byte(jsonString), &reqBody)
|
||||
assert.NoError(t, err)
|
||||
if i != 0 {
|
||||
reqBody.Codeword = reqBody.Codeword + faker.String() + strconv.Itoa(i)
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/create").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusCreated, statusCode)
|
||||
|
||||
var response models.PromoCode
|
||||
err = json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
promoID = response.ID.Hex()
|
||||
fmt.Println(response)
|
||||
}
|
||||
|
||||
})
|
||||
t.Run("CreatePromoCode-duplicate", func(t *testing.T) {
|
||||
jsonString := `{
|
||||
"codeword": "example",
|
||||
"description": "Example description",
|
||||
"greetings": "Example greetings",
|
||||
"dueTo": 1734429225,
|
||||
"activationCount": 100,
|
||||
"bonus": {
|
||||
"privilege": {
|
||||
"privilegeID": "examplePrivilegeID",
|
||||
"amount": 50
|
||||
},
|
||||
"discount": {
|
||||
"layer": 1,
|
||||
"factor": 0.2,
|
||||
"target": "exampleTarget",
|
||||
"threshold": 500
|
||||
}
|
||||
},
|
||||
"outdated": false,
|
||||
"offLimit": false,
|
||||
"delete": false
|
||||
}`
|
||||
|
||||
var reqBody models.PromoCode
|
||||
err := json.Unmarshal([]byte(jsonString), &reqBody)
|
||||
assert.NoError(t, err)
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/create").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.Error(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err = json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response["error"])
|
||||
})
|
||||
t.Run("CreatePromoCode-invalid request payload", func(t *testing.T) {
|
||||
jsonString := `{
|
||||
"example": "example",
|
||||
"description": "Example description",
|
||||
"greetings": "Example greetings",
|
||||
"dueTo": 1734429225,
|
||||
"activationCount": 100,
|
||||
"bonus": {
|
||||
"privilege": {
|
||||
"privilegeID": "examplePrivilegeID",
|
||||
"amount": 50
|
||||
},
|
||||
"discount": {
|
||||
"layer": 1,
|
||||
"factor": 0.2,
|
||||
"target": "exampleTarget",
|
||||
"threshold": 500
|
||||
}
|
||||
},
|
||||
"outdated": false,
|
||||
"offLimit": false,
|
||||
"delete": false
|
||||
}`
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/create").Set("Content-Type", "application/json").Body([]byte(jsonString))
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.Error(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response["error"])
|
||||
})
|
||||
t.Run("CreatePromoCode-nil codeword", func(t *testing.T) {
|
||||
jsonString := `{
|
||||
"description": "Example description",
|
||||
"greetings": "Example greetings",
|
||||
"dueTo": 1734429225,
|
||||
"activationCount": 100,
|
||||
"bonus": {
|
||||
"privilege": {
|
||||
"privilegeID": "examplePrivilegeID",
|
||||
"amount": 50
|
||||
},
|
||||
"discount": {
|
||||
"layer": 1,
|
||||
"factor": 0.2,
|
||||
"target": "exampleTarget",
|
||||
"threshold": 500
|
||||
}
|
||||
},
|
||||
"outdated": false,
|
||||
"offLimit": false,
|
||||
"delete": false
|
||||
}`
|
||||
|
||||
var reqBody models.PromoCode
|
||||
err := json.Unmarshal([]byte(jsonString), &reqBody)
|
||||
assert.NoError(t, err)
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/create").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.Error(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err = json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response["error"])
|
||||
})
|
||||
}
|
||||
|
||||
// EditPromoCode
|
||||
func TestEditPromoCode(t *testing.T) {
|
||||
client := fiber.AcquireClient()
|
||||
|
||||
t.Run("EditPromoCode-success", func(t *testing.T) {
|
||||
reqBody := models.ReqEditPromoCode{
|
||||
ID: promoID,
|
||||
Description: toString("Updated description"),
|
||||
Greetings: toString("Updated greetings"),
|
||||
DueTo: toInt64(1734429225),
|
||||
ActivationCount: toInt64(150),
|
||||
Delete: toBool(false),
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
req := client.Put(BaseUrl+"/promocode/edit").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||
|
||||
var response models.PromoCode
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response)
|
||||
})
|
||||
|
||||
t.Run("EditPromoCode-success one column", func(t *testing.T) {
|
||||
reqBody := models.ReqEditPromoCode{
|
||||
ID: promoID,
|
||||
Greetings: toString("Updated greetings one"),
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
req := client.Put(BaseUrl+"/promocode/edit").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||
|
||||
var response models.PromoCode
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response)
|
||||
})
|
||||
|
||||
t.Run("EditPromoCode-promocod not found", func(t *testing.T) {
|
||||
reqBody := models.ReqEditPromoCode{
|
||||
ID: primitive.NewObjectID().Hex(),
|
||||
Description: toString("Updated description"),
|
||||
Greetings: toString("Updated greetings"),
|
||||
DueTo: toInt64(1734429225),
|
||||
ActivationCount: toInt64(150),
|
||||
Delete: toBool(false),
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
req := client.Put(BaseUrl+"/promocode/edit").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
assert.Equal(t, fiber.StatusNotFound, statusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response["error"])
|
||||
})
|
||||
|
||||
t.Run("EditPromoCode-invalid request payload", func(t *testing.T) {
|
||||
reqBody := map[string]interface{}{
|
||||
"invalid_field": "example",
|
||||
"description": "Updated description",
|
||||
"greetings": "Updated greetings",
|
||||
"dueTo": 1734429225,
|
||||
"activationCount": 150,
|
||||
"delete": false,
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
req := client.Put(BaseUrl+"/promocode/edit").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response["error"])
|
||||
})
|
||||
}
|
||||
|
||||
func toString(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func toInt64(i int64) *int64 {
|
||||
return &i
|
||||
}
|
||||
|
||||
func toBool(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
|
||||
// CreateFastLink
|
||||
func TestCreateFastLink(t *testing.T) {
|
||||
client := fiber.AcquireClient()
|
||||
|
||||
t.Run("CreateFastLink-success", func(t *testing.T) {
|
||||
reqBody := struct {
|
||||
PromoCodeID string `json:"id"`
|
||||
}{
|
||||
PromoCodeID: promoID,
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/fastlink").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
fmt.Println(string(resBody))
|
||||
assert.Equal(t, fiber.StatusCreated, statusCode)
|
||||
|
||||
var response map[string]string
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fastLink = response["fastlink"]
|
||||
fmt.Println(response["fastlink"])
|
||||
})
|
||||
|
||||
t.Run("CreateFastLink-missing promoCodeID", func(t *testing.T) {
|
||||
req := client.Post(BaseUrl+"/promocode/fastlink").Set("Content-Type", "application/json").Body([]byte(`{}`))
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.Error(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response["error"])
|
||||
})
|
||||
|
||||
t.Run("CreateFastLink-promocode not found", func(t *testing.T) {
|
||||
reqBody := map[string]string{"id": primitive.NewObjectID().Hex()}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/fastlink").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.Error(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusNotFound, statusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response["error"])
|
||||
})
|
||||
}
|
||||
|
||||
// GetPromoCodesList
|
||||
func TestGetPromoCodesList(t *testing.T) {
|
||||
client := fiber.AcquireClient()
|
||||
|
||||
t.Run("GetPromoCodesList-success", func(t *testing.T) {
|
||||
reqBody := models.GetPromoCodesListReq{
|
||||
Page: 0,
|
||||
Limit: 10,
|
||||
Filter: models.GetPromoCodesListReqFilter{
|
||||
Text: "example",
|
||||
Active: true,
|
||||
},
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
req := client.Post(BaseUrl+"/promocode/getList").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||
|
||||
var response models.GetPromoCodesListResp
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response)
|
||||
})
|
||||
|
||||
t.Run("GetPromoCodesList-invalid request payload", func(t *testing.T) {
|
||||
req := client.Post(BaseUrl+"/promocode/getList").Set("Content-Type", "application/json").Body([]byte("invalid json"))
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response["error"])
|
||||
})
|
||||
}
|
||||
|
||||
// ActivatePromoCode
|
||||
func TestActivatePromoCode(t *testing.T) {
|
||||
client := fiber.AcquireClient()
|
||||
jwtUtil := helpers.InitializeJWT()
|
||||
token, tokenErr := jwtUtil.Create(ExampleUserID)
|
||||
fmt.Println(token)
|
||||
if isNoError := assert.NoError(t, tokenErr); !isNoError {
|
||||
return
|
||||
}
|
||||
t.Run("ActivatePromoCode-success codeword", func(t *testing.T) {
|
||||
reqBody := models.ActivateReq{
|
||||
Codeword: "example",
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
|
||||
fmt.Println(string(resBody))
|
||||
|
||||
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||
fmt.Println(statusCode)
|
||||
var response models.ActivateResp
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response)
|
||||
})
|
||||
|
||||
t.Run("ActivatePromoCode-success fastLink", func(t *testing.T) {
|
||||
reqBody := models.ActivateReq{
|
||||
FastLink: fastLink,
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||
var response models.ActivateResp
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response)
|
||||
})
|
||||
|
||||
t.Run("ActivatePromoCode-missing userid", func(t *testing.T) {
|
||||
reqBody := models.ActivateReq{
|
||||
Codeword: "example",
|
||||
}
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.Error(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response["error"])
|
||||
})
|
||||
|
||||
t.Run("ActivatePromoCode-missing codeword and fastlink", func(t *testing.T) {
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(nil)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.Error(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response["error"])
|
||||
})
|
||||
|
||||
t.Run("ActivatePromoCode-promocode not found", func(t *testing.T) {
|
||||
reqBody := models.ActivateReq{
|
||||
Codeword: "none",
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/promocode/activate").Set("Content-Type", "application/json").Set("Authorization", "Bearer "+token).Body(reqJSON)
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.Error(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusNotFound, statusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response["error"])
|
||||
})
|
||||
}
|
||||
|
||||
// GetPromoStats
|
||||
func TestGetPromoStats(t *testing.T) {
|
||||
client := fiber.AcquireClient()
|
||||
|
||||
t.Run("GetAllStats", func(t *testing.T) {
|
||||
|
||||
reqBody := struct {
|
||||
PromoCodeID string `json:"id"`
|
||||
}{
|
||||
PromoCodeID: promoID,
|
||||
}
|
||||
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
req := client.Get(BaseUrl+"/promocode/stats").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||
|
||||
var response []models.PromoCodeStats
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response)
|
||||
})
|
||||
}
|
||||
|
||||
// DeletePromoCode
|
||||
func TestDeletePromoCode(t *testing.T) {
|
||||
client := fiber.AcquireClient()
|
||||
|
||||
t.Run("DeletePromoCode-success", func(t *testing.T) {
|
||||
|
||||
req := client.Delete(BaseUrl + "/promocode/" + promoID)
|
||||
|
||||
statusCode, _, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||
})
|
||||
|
||||
t.Run("DeletePromoCode-promocode not found", func(t *testing.T) {
|
||||
|
||||
req := client.Delete(BaseUrl + "/promocode/" + primitive.NewObjectID().Hex())
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.Error(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusNotFound, statusCode)
|
||||
|
||||
var response map[string]interface{}
|
||||
err := json.Unmarshal(resBody, &response)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(response["error"])
|
||||
})
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// todo добавить другие константы такие как exampleUserID
|
||||
const (
|
||||
BaseUrl = "http://localhost:8080"
|
||||
ValidSign = "GSiyv5zBITGshqnvYLHKtXE3e4yZjKGvruOVFWuUuj9Nvaps28-Zt6RDq9n47eaNUlay1-nUVld61I3xoAAgCA==65b286c2f13095d96792079d"
|
||||
ExampleUserID = "6597babdd1ba7e2dbd32d7e3"
|
||||
)
|
||||
|
||||
// post handler
|
||||
func TestRecoveryHandler(t *testing.T) {
|
||||
client := fiber.AcquireClient()
|
||||
|
||||
t.Run("HandleRecoveryRequest", func(t *testing.T) {
|
||||
reqBody := models.RecoveryRequest{
|
||||
Email: "adminSOLO",
|
||||
RedirectionURL: "http://redirect.com",
|
||||
}
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/recover").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, resBody, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||
|
||||
var responseMap map[string]interface{}
|
||||
err := json.Unmarshal(resBody, &responseMap)
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(responseMap)
|
||||
})
|
||||
|
||||
t.Run("HandleRecoveryRequest-AlreadyReported", func(t *testing.T) {
|
||||
reqBody := models.RecoveryRequest{
|
||||
Email: "adminSOLO",
|
||||
RedirectionURL: "http://redirect.com",
|
||||
}
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/recover").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, _, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusAlreadyReported, statusCode)
|
||||
})
|
||||
|
||||
t.Run("HandleRecoveryRequest_MissingEmail", func(t *testing.T) {
|
||||
reqBody := models.RecoveryRequest{
|
||||
RedirectionURL: "http://redirect.com",
|
||||
}
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/recover").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, _, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusBadRequest, statusCode)
|
||||
})
|
||||
|
||||
t.Run("HandleRecoveryRequest_UserNotFound", func(t *testing.T) {
|
||||
reqBody := models.RecoveryRequest{
|
||||
Email: "nonexistent@example.com",
|
||||
RedirectionURL: "http://redirect.com",
|
||||
}
|
||||
reqJSON, _ := json.Marshal(reqBody)
|
||||
|
||||
req := client.Post(BaseUrl+"/recover").Set("Content-Type", "application/json").Body(reqJSON)
|
||||
|
||||
statusCode, _, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusNotFound, statusCode)
|
||||
})
|
||||
}
|
||||
|
||||
// get handler
|
||||
func TestRecoveryLinkHandler(t *testing.T) {
|
||||
client := fiber.AcquireClient()
|
||||
|
||||
t.Run("HandleRecoveryLink_ValidSign", func(t *testing.T) {
|
||||
req := client.Get(BaseUrl + "/recover/" + ValidSign)
|
||||
statusCode, _, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, fiber.StatusOK, statusCode)
|
||||
|
||||
fmt.Println("Recovery link handled successfully")
|
||||
})
|
||||
time.Sleep(15 * time.Minute)
|
||||
t.Run("HandleRecoveryLink_ExpiredSign", func(t *testing.T) {
|
||||
req := client.Get(BaseUrl + "/recover/" + ValidSign)
|
||||
statusCode, _, errs := req.Bytes()
|
||||
if len(errs) != 0 {
|
||||
assert.NoError(t, errs[0])
|
||||
}
|
||||
assert.Equal(t, fiber.StatusNotAcceptable, statusCode)
|
||||
fmt.Println("Recovery link with expired sign handled correctly")
|
||||
})
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"codeword/internal/initialize"
|
||||
"codeword/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func InitializeJWT() *utils.JWT {
|
||||
publicKey := strings.Replace(`-----BEGIN PUBLIC KEY-----
|
||||
MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgnvr7O2tiApjJfid1orFnIGm69
|
||||
80fZp+Lpbjo+NC/0whMFga2Biw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6B
|
||||
dA4TS2kB9Kf0wn0+7wSlyikHoKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y
|
||||
+3GyaOY536H47qyXAgMBAAE=
|
||||
-----END PUBLIC KEY-----`, "\t", "", -1)
|
||||
|
||||
privateKey := strings.Replace(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICWwIBAAKBgHgnvr7O2tiApjJfid1orFnIGm6980fZp+Lpbjo+NC/0whMFga2B
|
||||
iw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6BdA4TS2kB9Kf0wn0+7wSlyikH
|
||||
oKhbtzwXHZl17GsyEi6wHnsqNBSauyIWhpha8i+Y+3GyaOY536H47qyXAgMBAAEC
|
||||
gYAOphnVPXbk6lpYzdkLC1Xn5EOEuNfOLLURLxBnPWozZo26r/Mtahu/9mYhrYlv
|
||||
PP8r6mxta3VIil8iOdZyOLa/4d1LPd+UehgEXIJEiYXLtn7RS5eUnoPuQxssfs1k
|
||||
OWjdN8p6SzppleegFTvGRX4KM3cDLfSphOk8JuBCrpSSYQJBAOdqizTSrdKMTuVe
|
||||
c7Jk1JOJkyFuFs+N5zeryyeFGH7IpRdWy0rkWMxIUAi8Ap1vYVBPHv4tDOo3sy5X
|
||||
VLc/knkCQQCE62pg+0TmsrhO/2Pgog6MLBkzlzXYMRp/01HbmznwYF+ejfPnzLkz
|
||||
hnUlxRUNK3lhXM/7H6oAjvqF2R72u/OPAkEAterkmdbQfEZ+MwNoEiH/lie9OLdx
|
||||
SSI1VGdBYcTYN7qFRW6eizYstBJYkDU0HQ0Uw+we4hMKJwk4W0KdvxxDiQJAeqlB
|
||||
V1QqBneBbK10PzVuFV8QtrJhJyxRVwrtbKq38iMNuqUnI4+ijXEUpJFWVvv6nKXo
|
||||
7McQvEk12dU/JNTX8wJAOlAtSNjp9tVwpMpC0w2St1eKc1L2SknjeohA5ldoBz8sGeZsPhTU3eHSD1neAZXLKN5K68z3zFBr20ubY9nyLw==
|
||||
-----END RSA PRIVATE KEY-----`, "\t", "", -1)
|
||||
|
||||
return utils.NewJWT(&initialize.Config{
|
||||
PrivateKey: privateKey,
|
||||
PublicKey: publicKey,
|
||||
Audience: "pena",
|
||||
Issuer: "pena-auth-service",
|
||||
})
|
||||
}
|
@ -1,518 +0,0 @@
|
||||
package repository_test
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/repository"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"log"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pioz/faker"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const mongoURI = "mongodb://test:test@127.0.0.1:27020/?authMechanism=SCRAM-SHA-256&authSource=admin&directConnection=true"
|
||||
|
||||
// codeword unit tests
|
||||
func TestFindByEmail(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
client, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to connect to MongoDB: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := client.Disconnect(ctx); err != nil {
|
||||
log.Fatalf("Failed to disconnect MongoDB client: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := client.Ping(ctx, nil); err != nil {
|
||||
log.Fatalf("Failed to ping MongoDB: %v", err)
|
||||
}
|
||||
|
||||
db := client.Database("admin")
|
||||
|
||||
userRepo := repository.NewUserRepository(repository.Deps{Rdb: nil, Mdb: db.Collection("users")})
|
||||
|
||||
t.Run("FindByEmail - existing user", func(t *testing.T) {
|
||||
user, err := userRepo.FindByEmail(ctx, "admin")
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, user)
|
||||
fmt.Println(user.Email)
|
||||
assert.Equal(t, "admin", user.Login)
|
||||
})
|
||||
|
||||
t.Run("FindByEmail - non-existing user", func(t *testing.T) {
|
||||
user, err := userRepo.FindByEmail(ctx, "neadmin")
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, user)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestStoreRecoveryRecord(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||
require.NoError(t, err)
|
||||
|
||||
defer func() {
|
||||
_ = mongoClient.Disconnect(ctx)
|
||||
}()
|
||||
|
||||
database := mongoClient.Database("admin")
|
||||
codeword := database.Collection("codeword")
|
||||
_ = codeword.Drop(ctx)
|
||||
|
||||
userRepo := repository.NewCodewordRepository(repository.Deps{Rdb: nil, Mdb: codeword})
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
userID := faker.String()
|
||||
email := faker.Email()
|
||||
key := "test_recovery_key"
|
||||
|
||||
id, err := userRepo.StoreRecoveryRecord(ctx, models.StoreRecDeps{UserID: userID, Email: email, Key: key, Url: "def.url"})
|
||||
assert.NoError(t, err)
|
||||
fmt.Println(id)
|
||||
|
||||
var storedRecord models.RestoreRequest
|
||||
err = codeword.FindOne(ctx, bson.M{"user_id": userID}).Decode(&storedRecord)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, email, storedRecord.Email)
|
||||
assert.Equal(t, key, storedRecord.Sign)
|
||||
}
|
||||
|
||||
_ = database.Drop(ctx)
|
||||
}
|
||||
|
||||
func TestGetRecoveryRecord(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||
require.NoError(t, err)
|
||||
|
||||
defer func() {
|
||||
_ = mongoClient.Disconnect(ctx)
|
||||
}()
|
||||
|
||||
database := mongoClient.Database("admin")
|
||||
codeword := database.Collection("codeword")
|
||||
_ = codeword.Drop(ctx)
|
||||
|
||||
userRepo := repository.NewCodewordRepository(repository.Deps{Rdb: nil, Mdb: codeword})
|
||||
|
||||
ID := primitive.NewObjectID()
|
||||
userID := "6597babdd1ba7e2dbd32d7e3"
|
||||
email := "test@mail.ru"
|
||||
key := "test_recovery_key"
|
||||
|
||||
record := models.RestoreRequest{
|
||||
ID: ID,
|
||||
UserID: userID,
|
||||
Email: email,
|
||||
Sign: key,
|
||||
SignUrl: "def.url",
|
||||
SignID: key + userID,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
_, err = codeword.InsertOne(ctx, record)
|
||||
assert.NoError(t, err)
|
||||
|
||||
result, err := userRepo.GetRecoveryRecord(ctx, key+userID)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, userID, result.UserID)
|
||||
assert.Equal(t, email, result.Email)
|
||||
assert.Equal(t, key, result.Sign)
|
||||
|
||||
_ = database.Drop(ctx)
|
||||
}
|
||||
|
||||
// promoCode unit tests
|
||||
|
||||
func TestInitPromoCodeIndexes(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||
require.NoError(t, err)
|
||||
|
||||
defer func() {
|
||||
_ = mongoClient.Disconnect(ctx)
|
||||
}()
|
||||
|
||||
database := mongoClient.Database("admin")
|
||||
promoCode := database.Collection("promoCode")
|
||||
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestCreatePromoCode(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
_ = mongoClient.Disconnect(ctx)
|
||||
}()
|
||||
|
||||
database := mongoClient.Database("admin")
|
||||
promoCode := database.Collection("promoCode")
|
||||
_ = promoCode.Drop(ctx)
|
||||
|
||||
userRepo := repository.NewPromoCodeRepository(promoCode)
|
||||
|
||||
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Run("CreatePromoCode - success", func(t *testing.T) {
|
||||
req := &models.PromoCode{
|
||||
Codeword: "test_codeword",
|
||||
Description: faker.String(),
|
||||
Greetings: faker.String(),
|
||||
DueTo: 1737280065,
|
||||
ActivationCount: 100,
|
||||
}
|
||||
createdPromoCode, err := userRepo.CreatePromoCode(ctx, req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdPromoCode)
|
||||
assert.Equal(t, "test_codeword", createdPromoCode.Codeword)
|
||||
})
|
||||
|
||||
t.Run("CreatePromoCode - duplicate codeword", func(t *testing.T) {
|
||||
req := &models.PromoCode{
|
||||
Codeword: "test_codeword",
|
||||
Description: faker.String(),
|
||||
Greetings: faker.String(),
|
||||
DueTo: 1737280065,
|
||||
ActivationCount: 100,
|
||||
}
|
||||
_, err := userRepo.CreatePromoCode(ctx, req)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestEditPromoCode(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
_ = mongoClient.Disconnect(ctx)
|
||||
}()
|
||||
|
||||
database := mongoClient.Database("admin")
|
||||
promoCode := database.Collection("promoCode")
|
||||
_ = promoCode.Drop(ctx)
|
||||
|
||||
userRepo := repository.NewPromoCodeRepository(promoCode)
|
||||
|
||||
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||
assert.NoError(t, err)
|
||||
|
||||
req := &models.PromoCode{
|
||||
Codeword: "test_codeword",
|
||||
Description: faker.String(),
|
||||
Greetings: faker.String(),
|
||||
DueTo: 1737280065,
|
||||
ActivationCount: 100,
|
||||
}
|
||||
createdPromoCode, err := userRepo.CreatePromoCode(ctx, req)
|
||||
require.NoError(t, err)
|
||||
|
||||
newDescription := "New Description"
|
||||
|
||||
t.Run("EditPromoCode - success", func(t *testing.T) {
|
||||
editReq := &models.ReqEditPromoCode{
|
||||
ID: createdPromoCode.ID.Hex(),
|
||||
Description: &newDescription,
|
||||
}
|
||||
editedPromoCode, err := userRepo.EditPromoCode(ctx, editReq)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, editedPromoCode)
|
||||
assert.Equal(t, "New Description", editedPromoCode.Description)
|
||||
})
|
||||
|
||||
t.Run("EditPromoCode - promo code not found", func(t *testing.T) {
|
||||
nonExistingID := primitive.NewObjectID().Hex()
|
||||
editReq := &models.ReqEditPromoCode{
|
||||
ID: nonExistingID,
|
||||
}
|
||||
_, err := userRepo.EditPromoCode(ctx, editReq)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetPromoCodeByID(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
_ = mongoClient.Disconnect(ctx)
|
||||
}()
|
||||
|
||||
database := mongoClient.Database("admin")
|
||||
promoCode := database.Collection("promoCode")
|
||||
_ = promoCode.Drop(ctx)
|
||||
|
||||
userRepo := repository.NewPromoCodeRepository(promoCode)
|
||||
|
||||
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||
assert.NoError(t, err)
|
||||
|
||||
req := &models.PromoCode{
|
||||
Codeword: "test_codeword",
|
||||
Description: faker.String(),
|
||||
Greetings: faker.String(),
|
||||
DueTo: 1737280065,
|
||||
ActivationCount: 100,
|
||||
}
|
||||
createdPromoCode, err := userRepo.CreatePromoCode(ctx, req)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("GetPromoCodeByID - success", func(t *testing.T) {
|
||||
result, err := userRepo.GetPromoCodeByID(ctx, createdPromoCode.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, result)
|
||||
assert.Equal(t, createdPromoCode.Codeword, result.Codeword)
|
||||
})
|
||||
|
||||
t.Run("GetPromoCodeByID - promo code not found", func(t *testing.T) {
|
||||
nonExistingID := primitive.NewObjectID()
|
||||
_, err := userRepo.GetPromoCodeByID(ctx, nonExistingID)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetPromoCodesList(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
_ = mongoClient.Disconnect(ctx)
|
||||
}()
|
||||
|
||||
database := mongoClient.Database("admin")
|
||||
promoCode := database.Collection("promoCode")
|
||||
_ = promoCode.Drop(ctx)
|
||||
|
||||
userRepo := repository.NewPromoCodeRepository(promoCode)
|
||||
|
||||
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||
assert.NoError(t, err)
|
||||
for i := 0; i < 1111; i++ {
|
||||
|
||||
req := &models.PromoCode{
|
||||
Codeword: "test" + faker.String() + strconv.Itoa(i),
|
||||
Description: faker.String(),
|
||||
Greetings: faker.String(),
|
||||
DueTo: 1737280065,
|
||||
ActivationCount: 100,
|
||||
Delete: faker.Bool(),
|
||||
Outdated: faker.Bool(),
|
||||
OffLimit: faker.Bool(),
|
||||
}
|
||||
createdPromoCode, err := userRepo.CreatePromoCode(ctx, req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdPromoCode)
|
||||
}
|
||||
t.Run("GetPromoCodesList - true", func(t *testing.T) {
|
||||
filter := models.GetPromoCodesListReqFilter{
|
||||
Text: "test",
|
||||
Active: true,
|
||||
}
|
||||
req := &models.GetPromoCodesListReq{
|
||||
Page: 0,
|
||||
Limit: 10,
|
||||
Filter: filter,
|
||||
}
|
||||
promoCodes, count, err := userRepo.GetPromoCodesList(ctx, req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, promoCodes)
|
||||
assert.True(t, count >= 0)
|
||||
})
|
||||
|
||||
t.Run("GetPromoCodesList - false", func(t *testing.T) {
|
||||
filter := models.GetPromoCodesListReqFilter{
|
||||
Text: "test",
|
||||
Active: false,
|
||||
}
|
||||
req := &models.GetPromoCodesListReq{
|
||||
Page: 0,
|
||||
Limit: 10,
|
||||
Filter: filter,
|
||||
}
|
||||
promoCodes, count, err := userRepo.GetPromoCodesList(ctx, req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, promoCodes)
|
||||
assert.True(t, count >= 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestActivatePromo(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
_ = mongoClient.Disconnect(ctx)
|
||||
}()
|
||||
|
||||
database := mongoClient.Database("admin")
|
||||
promoCode := database.Collection("promoCode")
|
||||
_ = promoCode.Drop(ctx)
|
||||
|
||||
userRepo := repository.NewPromoCodeRepository(promoCode)
|
||||
|
||||
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||
assert.NoError(t, err)
|
||||
|
||||
req := &models.PromoCode{
|
||||
Codeword: "test_codeword",
|
||||
Description: faker.String(),
|
||||
Greetings: faker.String(),
|
||||
DueTo: 1737280065,
|
||||
ActivationCount: 100,
|
||||
}
|
||||
createdPromoCode, err := userRepo.CreatePromoCode(ctx, req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdPromoCode)
|
||||
|
||||
xid := "test_xid"
|
||||
err = userRepo.AddFastLink(ctx, createdPromoCode.ID, xid)
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Run("ActivatePromo Codeword - success", func(t *testing.T) {
|
||||
req := &models.ActivateReq{
|
||||
UserID: "6597babdd1ba7e2dbd32d7e3",
|
||||
Codeword: "test_codeword",
|
||||
}
|
||||
activatedPromoCode, err := userRepo.ActivatePromo(ctx, req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, activatedPromoCode)
|
||||
})
|
||||
|
||||
t.Run("ActivatePromo FastLink - success", func(t *testing.T) {
|
||||
req := &models.ActivateReq{
|
||||
UserID: "6597babdd1ba7e2dbd32d7e3",
|
||||
FastLink: "test_xid",
|
||||
}
|
||||
activatedPromoCode, err := userRepo.ActivatePromo(ctx, req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, activatedPromoCode)
|
||||
})
|
||||
|
||||
t.Run("ActivatePromo - promo code not found", func(t *testing.T) {
|
||||
req := &models.ActivateReq{
|
||||
UserID: "6597babdd1ba7e2dbd32d7e3",
|
||||
Codeword: "non_existing_codeword",
|
||||
}
|
||||
_, err := userRepo.ActivatePromo(ctx, req)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDeletePromoCode(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
_ = mongoClient.Disconnect(ctx)
|
||||
}()
|
||||
|
||||
database := mongoClient.Database("admin")
|
||||
promoCode := database.Collection("promoCode")
|
||||
_ = promoCode.Drop(ctx)
|
||||
|
||||
userRepo := repository.NewPromoCodeRepository(promoCode)
|
||||
|
||||
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||
assert.NoError(t, err)
|
||||
|
||||
req := &models.PromoCode{
|
||||
Codeword: "test_codeword",
|
||||
Description: faker.String(),
|
||||
Greetings: faker.String(),
|
||||
DueTo: 1737280065,
|
||||
ActivationCount: 100,
|
||||
}
|
||||
createdPromoCode, err := userRepo.CreatePromoCode(ctx, req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdPromoCode)
|
||||
|
||||
t.Run("DeletePromoCode - success", func(t *testing.T) {
|
||||
err := userRepo.DeletePromoCode(ctx, createdPromoCode.ID.Hex())
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("DeletePromoCode - promo code not found", func(t *testing.T) {
|
||||
nonExistingID := primitive.NewObjectID().Hex()
|
||||
err := userRepo.DeletePromoCode(ctx, nonExistingID)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAddFastLink(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
mongoClient, err := mongo.Connect(ctx, options.Client().ApplyURI(mongoURI))
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
_ = mongoClient.Disconnect(ctx)
|
||||
}()
|
||||
|
||||
database := mongoClient.Database("admin")
|
||||
promoCode := database.Collection("promoCode")
|
||||
_ = promoCode.Drop(ctx)
|
||||
|
||||
userRepo := repository.NewPromoCodeRepository(promoCode)
|
||||
|
||||
err = repository.InitPromoCodeIndexes(ctx, promoCode)
|
||||
assert.NoError(t, err)
|
||||
|
||||
req := &models.PromoCode{
|
||||
Codeword: "test_codeword",
|
||||
Description: faker.String(),
|
||||
Greetings: faker.String(),
|
||||
DueTo: 1737280065,
|
||||
ActivationCount: 100,
|
||||
}
|
||||
createdPromoCode, err := userRepo.CreatePromoCode(ctx, req)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, createdPromoCode)
|
||||
|
||||
t.Run("AddFastLink - success", func(t *testing.T) {
|
||||
xid := "test_xid"
|
||||
err := userRepo.AddFastLink(ctx, createdPromoCode.ID, xid)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("AddFastLink - promo code not found", func(t *testing.T) {
|
||||
nonExistingID := primitive.NewObjectID()
|
||||
xid := "test_xid"
|
||||
err := userRepo.AddFastLink(ctx, nonExistingID, xid)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
prefix = "Bearer "
|
||||
)
|
||||
|
||||
func NewAuthenticator(jwtUtil *JWT) func(*fiber.Ctx) error {
|
||||
return func(c *fiber.Ctx) error {
|
||||
if jwtUtil == nil {
|
||||
return fmt.Errorf("jwt util is nil")
|
||||
}
|
||||
|
||||
if err := authenticate(c, jwtUtil); err != nil {
|
||||
return fmt.Errorf("authentication error:%d", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func authenticate(c *fiber.Ctx, jwtUtil *JWT) error {
|
||||
authHeader := c.Get("Authorization")
|
||||
if authHeader == "" || !strings.HasPrefix(authHeader, prefix) {
|
||||
return fmt.Errorf("failed to parse jws from request header: %s", authHeader)
|
||||
}
|
||||
|
||||
jws := strings.TrimPrefix(authHeader, prefix)
|
||||
|
||||
userID, validateErr := jwtUtil.Validate(jws)
|
||||
if validateErr != nil {
|
||||
return validateErr
|
||||
}
|
||||
|
||||
c.Locals(models.AuthJWTDecodedUserIDKey, userID)
|
||||
c.Locals(models.AuthJWTDecodedAccessTokenKey, jws)
|
||||
|
||||
return nil
|
||||
}
|
89
utils/jwt.go
89
utils/jwt.go
@ -1,89 +0,0 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"codeword/internal/initialize"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"time"
|
||||
)
|
||||
|
||||
type JWT struct {
|
||||
privateKey []byte
|
||||
publicKey []byte
|
||||
algorithm *jwt.SigningMethodRSA
|
||||
expiresIn time.Duration
|
||||
issuer string
|
||||
audience string
|
||||
}
|
||||
|
||||
func NewJWT(configuration *initialize.Config) *JWT {
|
||||
return &JWT{
|
||||
privateKey: []byte(configuration.PrivateKey),
|
||||
publicKey: []byte(configuration.PublicKey),
|
||||
issuer: configuration.Issuer,
|
||||
audience: configuration.Audience,
|
||||
algorithm: jwt.SigningMethodRS256,
|
||||
expiresIn: 15 * time.Minute,
|
||||
}
|
||||
}
|
||||
|
||||
func (j *JWT) Create(id string) (string, error) {
|
||||
privateKey, err := jwt.ParseRSAPrivateKeyFromPEM(j.privateKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse private key on <Create> of <JWT>: %w", err)
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
|
||||
claims := jwt.MapClaims{
|
||||
"id": id,
|
||||
"exp": now.Add(j.expiresIn).Unix(),
|
||||
"aud": j.audience,
|
||||
"iss": j.issuer,
|
||||
}
|
||||
|
||||
token, err := jwt.NewWithClaims(j.algorithm, claims).SignedString(privateKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to sing on <Create> of <JWT>: %w", err)
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (j *JWT) Validate(tokenString string) (string, error) {
|
||||
key, err := jwt.ParseRSAPublicKeyFromPEM(j.publicKey)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse rsa public key on <Validate> of <JWT>: %w", err)
|
||||
}
|
||||
|
||||
parseCallback := func(token *jwt.Token) (any, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %s", token.Header["alg"])
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
token, err := jwt.Parse(
|
||||
tokenString,
|
||||
parseCallback,
|
||||
jwt.WithAudience(j.audience),
|
||||
jwt.WithIssuer(j.issuer),
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse jwt token on <Validate> of <JWT>: %w", err)
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok || !token.Valid {
|
||||
return "", errors.New("token is invalid on <Validate> of <JWT>")
|
||||
}
|
||||
|
||||
data, ok := claims["id"].(string)
|
||||
if !ok {
|
||||
return "", errors.New("data is empty or not a string on <Validate> of <JWT>")
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user