From da9c292adfd6e5799c57825c2ed9f99700a6408e Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 25 Oct 2024 18:26:03 +0300 Subject: [PATCH] formatting to our pj layout --- openapi.yaml => api/openapi.yaml | 0 app/app.go | 259 ------------------ cmd/main.go | 41 +++ go.mod | 3 +- go.sum | 6 + initialize/rpc_controllers.go | 16 -- internal/app/app.go | 187 +++++++++++++ {app => internal/app}/logrecords.go | 0 {brokers => internal/brokers}/producer.go | 0 {clients => internal/clients}/auth/auth.go | 0 {clients => internal/clients}/telegram/tg.go | 0 .../http_controllers/account/account.go | 97 ++++--- .../http_controllers/account/route.go | 21 ++ .../http_controllers/question/question.go | 54 ++-- .../http_controllers/question/route.go | 16 ++ .../controllers/http_controllers/quiz/quiz.go | 61 +++-- .../http_controllers/quiz/route.go | 19 ++ .../http_controllers/result/result.go | 60 ++-- .../http_controllers/result/route.go | 15 + .../http_controllers/statistic/route.go | 15 + .../http_controllers/statistic/statistic.go | 40 ++- .../http_controllers/telegram/route.go | 14 + .../http_controllers/telegram/telegram.go | 44 ++- .../rpc_controllers}/mail_notify.go | 14 +- internal/initialize/clients.go | 25 ++ internal/initialize/config.go | 43 +++ internal/initialize/controllers.go | 73 +++++ internal/initialize/dals.go | 28 ++ {initialize => internal/initialize}/kafka.go | 0 internal/initialize/redis.go | 21 ++ {models => internal/models}/hlog_events.go | 0 .../proto}/notifyer/notifyer.pb.go | 0 .../proto}/notifyer/notifyer_grpc.pb.go | 0 .../server/grpc/rpc_server.go | 6 +- internal/server/http/http_server.go | 73 +++++ {tools => internal/tools}/publishPriv.go | 0 {tools => internal/tools}/tools.go | 61 ----- {workers => internal/workers}/tg_worker.go | 2 +- main.go | 11 - pkg/closer/closer.go | 37 +++ schema/000001_init.down.sql | 24 -- schema/000001_init.up.sql | 120 -------- schema/000002_init.down.sql | 1 - schema/000002_init.up.sql | 1 - schema/000003_init.down.sql | 1 - schema/000003_init.up.sql | 1 - schema/000004_init.down.sql | 2 - schema/000004_init.up.sql | 2 - schema/000005_init.down.sql | 2 - schema/000005_init.up.sql | 2 - schema/000006_init.down.sql | 6 - schema/000006_init.up.sql | 6 - schema/000007_init.down.sql | 2 - schema/000007_init.up.sql | 2 - schema/000008_init.down.sql | 4 - schema/000008_init.up.sql | 4 - schema/000009_init.down.sql | 2 - schema/000009_init.up.sql | 2 - service/service.go | 101 ------- tests/mailNotify_test.go | 2 +- 60 files changed, 886 insertions(+), 763 deletions(-) rename openapi.yaml => api/openapi.yaml (100%) delete mode 100644 app/app.go create mode 100644 cmd/main.go delete mode 100644 initialize/rpc_controllers.go create mode 100644 internal/app/app.go rename {app => internal/app}/logrecords.go (100%) rename {brokers => internal/brokers}/producer.go (100%) rename {clients => internal/clients}/auth/auth.go (100%) rename {clients => internal/clients}/telegram/tg.go (100%) rename service/account_svc.go => internal/controllers/http_controllers/account/account.go (78%) create mode 100644 internal/controllers/http_controllers/account/route.go rename service/question_svc.go => internal/controllers/http_controllers/question/question.go (87%) create mode 100644 internal/controllers/http_controllers/question/route.go rename service/quiz_svc.go => internal/controllers/http_controllers/quiz/quiz.go (91%) create mode 100644 internal/controllers/http_controllers/quiz/route.go rename service/result_svc.go => internal/controllers/http_controllers/result/result.go (78%) create mode 100644 internal/controllers/http_controllers/result/route.go create mode 100644 internal/controllers/http_controllers/statistic/route.go rename service/statistic_svc.go => internal/controllers/http_controllers/statistic/statistic.go (76%) create mode 100644 internal/controllers/http_controllers/telegram/route.go rename service/telegram_svc.go => internal/controllers/http_controllers/telegram/telegram.go (82%) rename {rpc_service => internal/controllers/rpc_controllers}/mail_notify.go (61%) create mode 100644 internal/initialize/clients.go create mode 100644 internal/initialize/config.go create mode 100644 internal/initialize/controllers.go create mode 100644 internal/initialize/dals.go rename {initialize => internal/initialize}/kafka.go (100%) create mode 100644 internal/initialize/redis.go rename {models => internal/models}/hlog_events.go (100%) rename {proto => internal/proto}/notifyer/notifyer.pb.go (100%) rename {proto => internal/proto}/notifyer/notifyer_grpc.pb.go (100%) rename server/grpc.go => internal/server/grpc/rpc_server.go (88%) create mode 100644 internal/server/http/http_server.go rename {tools => internal/tools}/publishPriv.go (100%) rename {tools => internal/tools}/tools.go (84%) rename {workers => internal/workers}/tg_worker.go (97%) delete mode 100644 main.go create mode 100644 pkg/closer/closer.go delete mode 100644 schema/000001_init.down.sql delete mode 100644 schema/000001_init.up.sql delete mode 100644 schema/000002_init.down.sql delete mode 100644 schema/000002_init.up.sql delete mode 100644 schema/000003_init.down.sql delete mode 100644 schema/000003_init.up.sql delete mode 100644 schema/000004_init.down.sql delete mode 100644 schema/000004_init.up.sql delete mode 100644 schema/000005_init.down.sql delete mode 100644 schema/000005_init.up.sql delete mode 100644 schema/000006_init.down.sql delete mode 100644 schema/000006_init.up.sql delete mode 100644 schema/000007_init.down.sql delete mode 100644 schema/000007_init.up.sql delete mode 100644 schema/000008_init.down.sql delete mode 100644 schema/000008_init.up.sql delete mode 100644 schema/000009_init.down.sql delete mode 100644 schema/000009_init.up.sql delete mode 100644 service/service.go diff --git a/openapi.yaml b/api/openapi.yaml similarity index 100% rename from openapi.yaml rename to api/openapi.yaml diff --git a/app/app.go b/app/app.go deleted file mode 100644 index d5c332d..0000000 --- a/app/app.go +++ /dev/null @@ -1,259 +0,0 @@ -package app - -import ( - "context" - "errors" - "fmt" - "github.com/go-redis/redis/v8" - "github.com/gofiber/fiber/v2" - "github.com/skeris/appInit" - "github.com/themakers/hlog" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" - "penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw" - "penahub.gitlab.yandexcloud.net/backend/penahub_common/privilege" - "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" - "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/healthchecks" - "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/middleware" - "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/brokers" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/clients/auth" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/clients/telegram" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/initialize" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/models" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/server" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/service" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/tools" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/workers" - "penahub.gitlab.yandexcloud.net/external/trashlog/wrappers/zaptrashlog" - "time" -) - -type App struct { - logger *zap.Logger - err chan error -} - -func (a App) GetLogger() *zap.Logger { - return a.logger -} - -func (a App) GetErr() chan error { - return a.err -} - -var ( - errInvalidOptions = errors.New("invalid options") -) - -var zapOptions = []zap.Option{ - zap.AddCaller(), - zap.AddCallerSkip(2), - zap.AddStacktrace(zap.ErrorLevel), -} - -var _ appInit.CommonApp = (*App)(nil) - -type Options struct { - LoggerProdMode bool `env:"IS_PROD_LOG" default:"false"` - IsProd bool `env:"IS_PROD" default:"false"` - NumberPort string `env:"PORT" default:"1488"` - CrtFile string `env:"CRT" default:"server.crt"` - KeyFile string `env:"KEY" default:"server.key"` - PostgresCredentials string `env:"PG_CRED" default:"host=localhost port=35432 user=squiz password=Redalert2 dbname=squiz sslmode=disable"` - HubAdminUrl string `env:"HUB_ADMIN_URL" default:"http://localhost:8001/"` - ServiceName string `env:"SERVICE_NAME" default:"squiz"` - AuthServiceURL string `env:"AUTH_URL" default:"http://localhost:8000/"` - GrpcHost string `env:"GRPC_HOST" default:"localhost"` - GrpcPort string `env:"GRPC_PORT" default:"9000"` - KafkaBrokers string `env:"KAFKA_BROKERS" default:"localhost:9092"` - KafkaTopic string `env:"KAFKA_TOPIC" default:"test-topic"` - KafkaGroup string `env:"KAFKA_GROUP" default:"mailnotifier"` - TrashLogHost string `env:"TRASH_LOG_HOST" default:"localhost:7113"` - ModuleLogger string `env:"MODULE_LOGGER" default:"core-local"` - ClickHouseCred string `env:"CLICK_HOUSE_CRED" default:"tcp://10.8.0.15:9000/default?sslmode=disable"` - RedisHost string `env:"REDIS_HOST" default:"localhost:6379"` - RedisPassword string `env:"REDIS_PASSWORD" default:"admin"` - RedisDB uint64 `env:"REDIS_DB" default:"2"` - S3Prefix string `env:"S3_PREFIX"` -} - -func New(ctx context.Context, opts interface{}, ver appInit.Version) (appInit.CommonApp, error) { - var ( - err, workerErr error - zapLogger *zap.Logger - errChan = make(chan error) - options Options - ok bool - ) - - if options, ok = opts.(Options); !ok { - return App{}, errInvalidOptions - } - - if options.LoggerProdMode { - zapLogger, err = zap.NewProduction(zapOptions...) - if err != nil { - return nil, err - } - } else { - zapLogger, err = zap.NewDevelopment(zapOptions...) - if err != nil { - return nil, err - } - } - - zapLogger = zapLogger.With( - zap.String("SvcCommit", ver.Commit), - zap.String("SvcVersion", ver.Release), - zap.String("SvcBuildTime", ver.BuildTime), - ) - - clickHouseLogger, err := zaptrashlog.NewCore(ctx, zap.InfoLevel, options.TrashLogHost, ver.Release, ver.Commit, time.Now().Unix()) - if err != nil { - panic(err) - } - - loggerForHlog := zapLogger.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core { - return zapcore.NewTee(core, clickHouseLogger) - })) - - loggerHlog := hlog.New(loggerForHlog).Module(options.ModuleLogger) - loggerHlog.With(models.AllFields{}) - loggerHlog.Emit(InfoSvcStarted{}) - - authClient := auth.NewAuthClient(options.AuthServiceURL) - - pgdal, err := dal.New(ctx, options.PostgresCredentials, nil) - if err != nil { - fmt.Println("NEW", err) - return nil, err - } - - chDal, err := dal.NewClickHouseDAL(ctx, options.ClickHouseCred) - if err != nil { - fmt.Println("failed init clickhouse", err) - return nil, err - } - - kafkaClient, err := initialize.KafkaInit(ctx, initialize.KafkaDeps{ - KafkaGroup: options.KafkaGroup, - KafkaBrokers: options.KafkaBrokers, - KafkaTopic: options.KafkaTopic, - }) - if err != nil { - return nil, err - } - - producer := brokers.NewProducer(brokers.ProducerDeps{ - KafkaClient: kafkaClient, - Logger: zapLogger, - }) - - redisClient := redis.NewClient(&redis.Options{ - Addr: options.RedisHost, - Password: options.RedisPassword, - DB: int(options.RedisDB), - }) - err = redisClient.Ping(ctx).Err() - if err != nil { - panic(fmt.Sprintf("error ping to redis db %v", err)) - } - - clientData := privilege.Client{ - URL: options.HubAdminUrl, - ServiceName: options.ServiceName, - Privileges: model.Privileges, - } - fiberClient := &fiber.Client{} - privilegeController := privilege.NewPrivilege(clientData, fiberClient) - go tools.PublishPrivilege(privilegeController, 10, 5*time.Minute) - - tgClient, err := telegram.NewTelegramClient(ctx, pgdal) - if err != nil { - panic(fmt.Sprintf("failed init tg clietns: %v", err)) - } - - tgWC := workers.NewTgListenerWC(workers.Deps{ - BotID: int64(6712573453), // todo убрать - Redis: redisClient, - Dal: pgdal, - TgClient: tgClient, - }) - - go tgWC.Start(ctx) - - // todo подумать над реализацией всего а то пока мне кажется что немного каша получается такой предикт что через некоторое время - // сложно будет разобраться что есть где - grpcControllers := initialize.InitRpcControllers(pgdal) - grpc, err := server.NewGRPC(zapLogger) - if err != nil { - fmt.Println("error:", err) - panic("err init grpc server") - } - grpc.Register(grpcControllers) - go grpc.Run(server.DepsGrpcRun{ - Host: options.GrpcHost, - Port: options.GrpcPort, - }) - - app := fiber.New() - app.Use(middleware.JWTAuth()) - app.Use(log_mw.ContextLogger(loggerHlog)) - app.Get("/liveness", healthchecks.Liveness) - app.Get("/readiness", healthchecks.Readiness(&workerErr)) //todo parametrized readiness. should discuss ready reason - - svc := service.New(service.Deps{ - Dal: pgdal, - AuthClient: authClient, - Producer: producer, - ServiceName: options.ServiceName, - ChDAL: chDal, - TelegramClient: tgClient, - RedisClient: redisClient, - S3Prefix: options.S3Prefix, - }) - - svc.Register(app) - - loggerHlog.Emit(InfoSvcReady{}) - - go func() { - defer func() { - if pgdal != nil { - pgdal.Close() - } - if chDal != nil { - if derr := chDal.Close(ctx); derr != nil { - fmt.Printf("error closing clickhouse: %v", derr) - } - } - err := grpc.Stop(ctx) - err = app.Shutdown() - loggerHlog.Emit(InfoSvcShutdown{Signal: err.Error()}) - }() - - if options.IsProd { - if err := app.ListenTLS(fmt.Sprintf(":%s", options.NumberPort), options.CrtFile, options.KeyFile); err != nil { - loggerHlog.Emit(ErrorCanNotServe{ - Err: err, - }) - errChan <- err - } - } else { - if err := app.Listen(fmt.Sprintf(":%s", options.NumberPort)); err != nil { - loggerHlog.Emit(ErrorCanNotServe{ - Err: err, - }) - errChan <- err - } - } - - errChan <- nil - }() - // todo implement helper func for service app type. such as server preparing, logger preparing, healthchecks and etc. - return &App{ - logger: zapLogger, - err: errChan, - }, err -} diff --git a/cmd/main.go b/cmd/main.go new file mode 100644 index 0000000..7aa3f6a --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,41 @@ +package main + +import ( + "context" + "fmt" + "go.uber.org/zap" + "os" + "os/signal" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/app" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/initialize" + "syscall" +) + +var ( + commit string = os.Getenv("COMMIT") + buildTime string = os.Getenv("BUILD_TIME") + version string = os.Getenv("VERSION") +) + +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, app.Build{ + Commit: commit, + Version: version, + }); err != nil { + logger.Fatal("App exited with error", zap.Error(err)) + } +} diff --git a/go.mod b/go.mod index 128a6f4..309059e 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( google.golang.org/grpc v1.64.0 google.golang.org/protobuf v1.34.2 penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240607202348-efe5f2bf3e8c - penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240711133242-0b8534fae5b2 + penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20241025130405-8ab347d96f1f penahub.gitlab.yandexcloud.net/backend/quiz/worker.git v0.0.0-20240421230341-0e086fcbb990 penahub.gitlab.yandexcloud.net/backend/tdlib v0.0.0-20240701075856-1731684c936f penahub.gitlab.yandexcloud.net/external/trashlog v0.1.6-0.20240827173635-78ce9878c387 @@ -29,6 +29,7 @@ require ( require ( github.com/ClickHouse/clickhouse-go v1.5.4 // indirect github.com/andybalholm/brotli v1.1.0 // indirect + github.com/caarlos0/env/v8 v8.0.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index 3cf5969..f820251 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer5 github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk= github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= +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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -140,6 +142,7 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM= github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33 h1:N9f/Q+2Ssa+yDcbfaoLTYvXmdeyUUxsJKdPUVsjSmiA= github.com/themakers/bdd v0.0.0-20210316111417-6b1dfe326f33/go.mod h1:rpcH99JknBh8seZmlOlUg51gasZH6QH34oXNsIwYT6E= github.com/themakers/hlog v0.0.0-20191205140925-235e0e4baddf h1:TJJm6KcBssmbWzplF5lzixXl1RBAi/ViPs1GaSOkhwo= @@ -260,6 +263,7 @@ google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6h google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= @@ -284,6 +288,8 @@ penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240607202348-efe5 penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240607202348-efe5f2bf3e8c/go.mod h1:+bPxq2wfW5S1gd+83vZYmHm33AE7nEBfznWS8AM1TKE= penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240711133242-0b8534fae5b2 h1:0t6pQHJvA3jMeBB3FPUmA+hw8rWlksTah8KJEtf2KD8= penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240711133242-0b8534fae5b2/go.mod h1:uOuosXduBzd2WbLH6TDZO7ME7ZextulA662oZ6OsoB0= +penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20241025130405-8ab347d96f1f h1:HMXxbIkNmFeQTFI/MlsDBIRUTu8MTbn3i2PVcpQbXEI= +penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20241025130405-8ab347d96f1f/go.mod h1:uOuosXduBzd2WbLH6TDZO7ME7ZextulA662oZ6OsoB0= penahub.gitlab.yandexcloud.net/backend/quiz/worker.git v0.0.0-20240421230341-0e086fcbb990 h1:jiO8GWO+3sCnDAV8/NAV8tQIUwae/I6/xiDilW7zf0o= penahub.gitlab.yandexcloud.net/backend/quiz/worker.git v0.0.0-20240421230341-0e086fcbb990/go.mod h1:zswBuTwmEsFHBVRu1nkG3/Fwylk5Vcm8OUm9iWxccSE= penahub.gitlab.yandexcloud.net/backend/tdlib v0.0.0-20240701075856-1731684c936f h1:Qli89wgu0T7nG4VECXZOZ40fjE/hVVfxF3hTaSYS008= diff --git a/initialize/rpc_controllers.go b/initialize/rpc_controllers.go deleted file mode 100644 index f65b8a0..0000000 --- a/initialize/rpc_controllers.go +++ /dev/null @@ -1,16 +0,0 @@ -package initialize - -import ( - "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/rpc_service" -) - -type RpcRegister struct { - MailNotify *rpc_service.MailNotify -} - -func InitRpcControllers(dal *dal.DAL) *RpcRegister { - return &RpcRegister{ - MailNotify: rpc_service.NewMailNotify(dal), - } -} diff --git a/internal/app/app.go b/internal/app/app.go new file mode 100644 index 0000000..856285a --- /dev/null +++ b/internal/app/app.go @@ -0,0 +1,187 @@ +package app + +import ( + "context" + "errors" + "github.com/gofiber/fiber/v2" + "github.com/themakers/hlog" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "penahub.gitlab.yandexcloud.net/backend/penahub_common/privilege" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/brokers" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/initialize" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/models" + server "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/server/grpc" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/server/http" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/tools" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/workers" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/pkg/closer" + "penahub.gitlab.yandexcloud.net/external/trashlog/wrappers/zaptrashlog" + "time" +) + +type Build struct { + Commit string + Version string +} + +var zapOptions = []zap.Option{ + zap.AddCaller(), + zap.AddCallerSkip(2), + zap.AddStacktrace(zap.ErrorLevel), +} + +func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger, build Build) error { + var ( + err error + zapLogger *zap.Logger + ) + + defer func() { + if r := recover(); r != nil { + logger.Error("Recovered from a panic", zap.Any("error", r)) + } + }() + + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + if cfg.LoggerProdMode { + zapLogger, err = zap.NewProduction(zapOptions...) + if err != nil { + return err + } + } else { + zapLogger, err = zap.NewDevelopment(zapOptions...) + if err != nil { + return err + } + } + + zapLogger = zapLogger.With( + zap.String("SvcCommit", build.Commit), + zap.String("SvcVersion", build.Version), + zap.String("SvcBuildTime", time.Now().String()), + ) + + clickHouseLogger, err := zaptrashlog.NewCore(ctx, zap.InfoLevel, cfg.TrashLogHost, build.Version, build.Commit, time.Now().Unix()) + if err != nil { + panic(err) + } + + loggerForHlog := zapLogger.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core { + return zapcore.NewTee(core, clickHouseLogger) + })) + + loggerHlog := hlog.New(loggerForHlog).Module(cfg.ModuleLogger) + loggerHlog.With(models.AllFields{}) + loggerHlog.Emit(InfoSvcStarted{}) + + shutdownGroup := closer.NewCloserGroup() + + dalS, err := initialize.NewDALs(ctx, cfg) + if err != nil { + logger.Error("Error initializing dals", zap.Error(err)) + return err + } + + kafkaClient, err := initialize.KafkaInit(ctx, initialize.KafkaDeps{ + KafkaGroup: cfg.KafkaGroup, + KafkaBrokers: cfg.KafkaBrokers, + KafkaTopic: cfg.KafkaTopic, + }) + if err != nil { + logger.Error("Error initializing kafka", zap.Error(err)) + return err + } + + producer := brokers.NewProducer(brokers.ProducerDeps{ + KafkaClient: kafkaClient, + Logger: zapLogger, + }) + + redisClient, err := initialize.Redis(ctx, cfg) + if err != nil { + logger.Error("Error initializing redis", zap.Error(err)) + return err + } + + go tools.PublishPrivilege(privilege.NewPrivilege(privilege.Client{ + URL: cfg.HubAdminUrl, + ServiceName: cfg.ServiceName, + Privileges: model.Privileges, + }, &fiber.Client{}), 10, 5*time.Minute) + + clients, err := initialize.NewClients(ctx, cfg, dalS.PgDAL) + if err != nil { + logger.Error("Error initializing clients", zap.Error(err)) + return err + } + + tgWC := workers.NewTgListenerWC(workers.Deps{ + BotID: int64(6712573453), // todo убрать + Redis: redisClient, + Dal: dalS.PgDAL, + TgClient: clients.TgClient, + }) + + go tgWC.Start(ctx) + + controllers := initialize.NewControllers(initialize.ControllerDeps{ + Clients: clients, + DALs: dalS, + Config: cfg, + Producer: producer, + RedisClient: redisClient, + }) + + grpc, err := server.NewGRPC(zapLogger) + if err != nil { + logger.Error("Error initializing grpc", zap.Error(err)) + return err + } + grpc.Register(controllers.GRpcControllers) + + srv := http.NewServer(http.ServerConfig{ + Logger: logger, + Controllers: []http.Controller{controllers.HttpControllers.Account, controllers.HttpControllers.Telegram, controllers.HttpControllers.Result, + controllers.HttpControllers.Question, controllers.HttpControllers.Quiz, controllers.HttpControllers.Statistic}, + Hlogger: loggerHlog, + }) + + go func() { + if err := srv.Start(cfg.HttpHost + ":" + cfg.NumberPort); err != nil { + logger.Error("HTTP server startup error", zap.Error(err)) + cancel() + } + }() + + go grpc.Run(server.DepsGrpcRun{ + Host: cfg.GrpcHost, + Port: cfg.GrpcPort, + }) + + srv.ListRoutes() + + shutdownGroup.Add(closer.CloserFunc(srv.Shutdown)) + shutdownGroup.Add(closer.CloserFunc(grpc.Stop)) + shutdownGroup.Add(closer.CloserFunc(dalS.PgDAL.Close)) + shutdownGroup.Add(closer.CloserFunc(dalS.ChDAL.Close)) + + <-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("Application has stopped") + return nil +} diff --git a/app/logrecords.go b/internal/app/logrecords.go similarity index 100% rename from app/logrecords.go rename to internal/app/logrecords.go diff --git a/brokers/producer.go b/internal/brokers/producer.go similarity index 100% rename from brokers/producer.go rename to internal/brokers/producer.go diff --git a/clients/auth/auth.go b/internal/clients/auth/auth.go similarity index 100% rename from clients/auth/auth.go rename to internal/clients/auth/auth.go diff --git a/clients/telegram/tg.go b/internal/clients/telegram/tg.go similarity index 100% rename from clients/telegram/tg.go rename to internal/clients/telegram/tg.go diff --git a/service/account_svc.go b/internal/controllers/http_controllers/account/account.go similarity index 78% rename from service/account_svc.go rename to internal/controllers/http_controllers/account/account.go index 3366be4..0ae231c 100644 --- a/service/account_svc.go +++ b/internal/controllers/http_controllers/account/account.go @@ -1,21 +1,50 @@ -package service +package account import ( "database/sql" "encoding/json" "errors" "fmt" + "github.com/go-redis/redis/v8" "github.com/gofiber/fiber/v2" "penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/middleware" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/pj_errors" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/brokers" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/models" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/brokers" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/clients/auth" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/models" "strconv" "time" ) +type Deps struct { + Dal *dal.DAL + AuthClient *auth.AuthClient + Producer *brokers.Producer + ServiceName string + RedisClient *redis.Client +} + +type Account struct { + dal *dal.DAL + authClient *auth.AuthClient + producer *brokers.Producer + serviceName string + redisClient *redis.Client +} + +func NewAccountController(deps Deps) *Account { + return &Account{ + dal: deps.Dal, + authClient: deps.AuthClient, + producer: deps.Producer, + serviceName: deps.ServiceName, + redisClient: deps.RedisClient, + } +} + type CreateAccountReq struct { UserID string `json:"userId"` } @@ -51,13 +80,13 @@ type GetAccountsResp struct { } // getCurrentAccount обработчик для получения текущего аккаунта -func (s *Service) getCurrentAccount(ctx *fiber.Ctx) error { +func (r *Account) GetCurrentAccount(ctx *fiber.Ctx) error { accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") } - account, err := s.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) + account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) if err != nil && err != sql.ErrNoRows { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -71,14 +100,14 @@ func (s *Service) getCurrentAccount(ctx *fiber.Ctx) error { } // createAccount обработчик для создания нового аккаунта -func (s *Service) createAccount(ctx *fiber.Ctx) error { +func (r *Account) CreateAccount(ctx *fiber.Ctx) error { accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") } hlogger := log_mw.ExtractLogger(ctx) - existingAccount, err := s.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) + existingAccount, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) if err != nil && err != sql.ErrNoRows { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -86,7 +115,7 @@ func (s *Service) createAccount(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusConflict).SendString("user with this ID already exists") } - email, err := s.authClient.GetUserEmail(accountID) + email, err := r.authClient.GetUserEmail(accountID) if err != nil { return err } @@ -105,11 +134,11 @@ func (s *Service) createAccount(ctx *fiber.Ctx) error { }, } - createdAcc, err := s.dal.AccountRepo.CreateAccount(ctx.Context(), &newAccount) + createdAcc, err := r.dal.AccountRepo.CreateAccount(ctx.Context(), &newAccount) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } - _, err = s.dal.AccountRepo.PostLeadTarget(ctx.Context(), model.LeadTarget{ + _, err = r.dal.AccountRepo.PostLeadTarget(ctx.Context(), model.LeadTarget{ AccountID: accountID, Target: email, Type: model.LeadTargetEmail, @@ -124,10 +153,10 @@ func (s *Service) createAccount(ctx *fiber.Ctx) error { CtxAccountID: createdAcc.ID, }) - err = s.producer.ToMailNotify(ctx.Context(), brokers.Message{ + err = r.producer.ToMailNotify(ctx.Context(), brokers.Message{ AccountID: accountID, Email: email, - ServiceKey: s.serviceName, + ServiceKey: r.serviceName, SendAt: time.Now(), }) if err != nil { @@ -140,18 +169,18 @@ func (s *Service) createAccount(ctx *fiber.Ctx) error { } // deleteAccount обработчик для удаления текущего аккаунта -func (s *Service) deleteAccount(ctx *fiber.Ctx) error { +func (r *Account) DeleteAccount(ctx *fiber.Ctx) error { accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") } - account, err := s.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) + account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } - if err := s.dal.AccountRepo.DeleteAccount(ctx.Context(), account.ID); err != nil { + if err := r.dal.AccountRepo.DeleteAccount(ctx.Context(), account.ID); err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -161,13 +190,13 @@ func (s *Service) deleteAccount(ctx *fiber.Ctx) error { } // getPrivilegeByUserID обработчик для получения привилегий аккаунта по ID пользователя -func (s *Service) getPrivilegeByUserID(ctx *fiber.Ctx) error { +func (r *Account) GetPrivilegeByUserID(ctx *fiber.Ctx) error { var req GetPrivilegeByUserIDReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } - privilege, err := s.dal.AccountRepo.GetPrivilegesByAccountID(ctx.Context(), req.UserID) + privilege, err := r.dal.AccountRepo.GetPrivilegesByAccountID(ctx.Context(), req.UserID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -176,13 +205,13 @@ func (s *Service) getPrivilegeByUserID(ctx *fiber.Ctx) error { } // deleteAccountByUserID обработчик для удаления аккаунта по ID пользователя -func (s *Service) deleteAccountByUserID(ctx *fiber.Ctx) error { +func (r *Account) DeleteAccountByUserID(ctx *fiber.Ctx) error { var req DeleteAccountByUserIDReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } - existingAccount, err := s.dal.AccountRepo.GetAccountByID(ctx.Context(), req.UserID) + existingAccount, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), req.UserID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -191,7 +220,7 @@ func (s *Service) deleteAccountByUserID(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusInternalServerError).SendString("user with this ID not found") } - if err := s.dal.AccountRepo.DeleteAccount(ctx.Context(), existingAccount.ID); err != nil { + if err := r.dal.AccountRepo.DeleteAccount(ctx.Context(), existingAccount.ID); err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -201,7 +230,7 @@ func (s *Service) deleteAccountByUserID(ctx *fiber.Ctx) error { } // getAccounts обработчик для получения списка аккаунтов с пагинацией -func (s *Service) getAccounts(ctx *fiber.Ctx) error { +func (r *Account) GetAccounts(ctx *fiber.Ctx) error { var req GetAccountsReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -212,7 +241,7 @@ func (s *Service) getAccounts(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") } - accounts, totalCount, err := s.dal.AccountRepo.GetAccounts(ctx.Context(), req.Limit, req.Page) + accounts, totalCount, err := r.dal.AccountRepo.GetAccounts(ctx.Context(), req.Limit, req.Page) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -225,7 +254,7 @@ func (s *Service) getAccounts(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusOK).JSON(response) } -func (s *Service) ManualDone(ctx *fiber.Ctx) error { +func (r *Account) ManualDone(ctx *fiber.Ctx) error { var req struct { Id string `json:"id"` } @@ -237,7 +266,7 @@ func (s *Service) ManualDone(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusBadRequest).SendString("User id is required") } - err := s.dal.AccountRepo.ManualDone(ctx.Context(), req.Id) + err := r.dal.AccountRepo.ManualDone(ctx.Context(), req.Id) if err != nil { if errors.Is(err, pj_errors.ErrNotFound) { return ctx.Status(fiber.StatusNotFound).SendString("user don't have this privilege") @@ -248,7 +277,7 @@ func (s *Service) ManualDone(ctx *fiber.Ctx) error { return ctx.SendStatus(fiber.StatusOK) } -func (s *Service) PostLeadTarget(ctx *fiber.Ctx) error { +func (r *Account) PostLeadTarget(ctx *fiber.Ctx) error { var req struct { Type string `json:"type"` QuizID int32 `json:"quizID"` @@ -276,7 +305,7 @@ func (s *Service) PostLeadTarget(ctx *fiber.Ctx) error { switch req.Type { case "mail": - _, err := s.dal.AccountRepo.PostLeadTarget(ctx.Context(), model.LeadTarget{ + _, err := r.dal.AccountRepo.PostLeadTarget(ctx.Context(), model.LeadTarget{ AccountID: accountID, Target: req.Target, Type: model.LeadTargetType(req.Type), @@ -287,7 +316,7 @@ func (s *Service) PostLeadTarget(ctx *fiber.Ctx) error { } return ctx.SendStatus(fiber.StatusOK) case "telegram": - targets, err := s.dal.AccountRepo.GetLeadTarget(ctx.Context(), accountID, req.QuizID) + targets, err := r.dal.AccountRepo.GetLeadTarget(ctx.Context(), accountID, req.QuizID) if err != nil && !errors.Is(err, pj_errors.ErrNotFound) { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -311,7 +340,7 @@ func (s *Service) PostLeadTarget(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } - if err := s.redisClient.Set(ctx.Context(), taskKey, taskData, 0).Err(); err != nil { + if err := r.redisClient.Set(ctx.Context(), taskKey, taskData, 0).Err(); err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } case "whatsapp": @@ -321,21 +350,21 @@ func (s *Service) PostLeadTarget(ctx *fiber.Ctx) error { return nil } -func (s *Service) DeleteLeadTarget(ctx *fiber.Ctx) error { +func (r *Account) DeleteLeadTarget(ctx *fiber.Ctx) error { leadIDStr := ctx.Params("id") leadID, err := strconv.ParseInt(leadIDStr, 10, 64) if err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid lead ID format") } - err = s.dal.AccountRepo.DeleteLeadTarget(ctx.Context(), leadID) + err = r.dal.AccountRepo.DeleteLeadTarget(ctx.Context(), leadID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return ctx.SendStatus(fiber.StatusOK) } -func (s *Service) GetLeadTarget(ctx *fiber.Ctx) error { +func (r *Account) GetLeadTarget(ctx *fiber.Ctx) error { accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") @@ -347,7 +376,7 @@ func (s *Service) GetLeadTarget(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid quiz ID format") } - result, err := s.dal.AccountRepo.GetLeadTarget(ctx.Context(), accountID, int32(quizID)) + result, err := r.dal.AccountRepo.GetLeadTarget(ctx.Context(), accountID, int32(quizID)) if err != nil { switch { case errors.Is(err, pj_errors.ErrNotFound): @@ -360,7 +389,7 @@ func (s *Service) GetLeadTarget(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusOK).JSON(result) } -func (s *Service) UpdateLeadTarget(ctx *fiber.Ctx) error { +func (r *Account) UpdateLeadTarget(ctx *fiber.Ctx) error { var req struct { ID int64 `json:"id"` Target string `json:"target"` @@ -374,7 +403,7 @@ func (s *Service) UpdateLeadTarget(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusBadRequest).SendString("ID and Target don't be nil") } - result, err := s.dal.AccountRepo.UpdateLeadTarget(ctx.Context(), model.LeadTarget{ + result, err := r.dal.AccountRepo.UpdateLeadTarget(ctx.Context(), model.LeadTarget{ ID: req.ID, Target: req.Target, }) diff --git a/internal/controllers/http_controllers/account/route.go b/internal/controllers/http_controllers/account/route.go new file mode 100644 index 0000000..8276adf --- /dev/null +++ b/internal/controllers/http_controllers/account/route.go @@ -0,0 +1,21 @@ +package account + +import "github.com/gofiber/fiber/v2" + +func (r *Account) Register(router fiber.Router) { + router.Get("/account/get", r.GetCurrentAccount) + router.Post("/account/create", r.CreateAccount) + router.Delete("/account/delete", r.DeleteAccount) + router.Get("/accounts", r.GetAccounts) + router.Get("/privilege/:userId", r.GetPrivilegeByUserID) + router.Delete("/account/:userId", r.DeleteAccountByUserID) + router.Post("/account/manualdone", r.ManualDone) + router.Post("/account/leadtarget", r.PostLeadTarget) + router.Delete("/account/leadtarget/:id", r.DeleteLeadTarget) + router.Get("/account/leadtarget/:quizID", r.GetLeadTarget) + router.Put("/account/leadtarget", r.UpdateLeadTarget) +} + +func (r *Account) Name() string { + return "" +} diff --git a/service/question_svc.go b/internal/controllers/http_controllers/question/question.go similarity index 87% rename from service/question_svc.go rename to internal/controllers/http_controllers/question/question.go index 5111490..fb76581 100644 --- a/service/question_svc.go +++ b/internal/controllers/http_controllers/question/question.go @@ -1,15 +1,28 @@ -package service +package question import ( "github.com/gofiber/fiber/v2" "github.com/lib/pq" "penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/middleware" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/models" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/models" "unicode/utf8" ) +type Deps struct { + DAL *dal.DAL +} + +type Question struct { + dal *dal.DAL +} + +func NewQuestionController(deps Deps) *Question { + return &Question{dal: deps.DAL} +} + // QuestionCreateReq request structure for creating Question type QuestionCreateReq struct { QuizId uint64 `json:"quiz_id"` // relation to quiz @@ -23,7 +36,7 @@ type QuestionCreateReq struct { } // CreateQuestion service handler for creating question for quiz -func (s *Service) CreateQuestion(ctx *fiber.Ctx) error { +func (r *Question) CreateQuestion(ctx *fiber.Ctx) error { accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") @@ -64,7 +77,7 @@ func (s *Service) CreateQuestion(ctx *fiber.Ctx) error { Content: req.Content, } - questionID, err := s.dal.QuestionRepo.CreateQuestion(ctx.Context(), &result) + questionID, err := r.dal.QuestionRepo.CreateQuestion(ctx.Context(), &result) if err != nil { if e, ok := err.(*pq.Error); ok { if e.Constraint == "quiz_relation" { @@ -104,7 +117,7 @@ type GetQuestionListResp struct { } // GetQuestionList handler for paginated list question -func (s *Service) GetQuestionList(ctx *fiber.Ctx) error { +func (r *Question) GetQuestionList(ctx *fiber.Ctx) error { var req GetQuestionListReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -127,7 +140,7 @@ func (s *Service) GetQuestionList(ctx *fiber.Ctx) error { "'test','none','file', 'button','select','checkbox'") } - res, cnt, err := s.dal.QuestionRepo.GetQuestionList(ctx.Context(), + res, cnt, err := r.dal.QuestionRepo.GetQuestionList(ctx.Context(), req.Limit, req.Page*req.Limit, uint64(req.From), @@ -165,7 +178,7 @@ type UpdateResp struct { } // UpdateQuestion handler for update question -func (s *Service) UpdateQuestion(ctx *fiber.Ctx) error { +func (r *Question) UpdateQuestion(ctx *fiber.Ctx) error { var req UpdateQuestionReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -195,7 +208,7 @@ func (s *Service) UpdateQuestion(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusNotAcceptable).SendString("type must be only test,button,file,checkbox,select, none or empty string") } - question, err := s.dal.QuestionRepo.MoveToHistoryQuestion(ctx.Context(), req.Id) + question, err := r.dal.QuestionRepo.MoveToHistoryQuestion(ctx.Context(), req.Id) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -227,7 +240,7 @@ func (s *Service) UpdateQuestion(ctx *fiber.Ctx) error { question.Content = req.Content } - if err := s.dal.QuestionRepo.UpdateQuestion(ctx.Context(), question); err != nil { + if err := r.dal.QuestionRepo.UpdateQuestion(ctx.Context(), question); err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -243,7 +256,7 @@ type CopyQuestionReq struct { } // CopyQuestion handler for copy question -func (s *Service) CopyQuestion(ctx *fiber.Ctx) error { +func (r *Question) CopyQuestion(ctx *fiber.Ctx) error { var req CopyQuestionReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -253,7 +266,7 @@ func (s *Service) CopyQuestion(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusFailedDependency).SendString("no id provided") } - question, err := s.dal.QuestionRepo.CopyQuestion(ctx.Context(), req.Id, req.QuizId) + question, err := r.dal.QuestionRepo.CopyQuestion(ctx.Context(), req.Id, req.QuizId) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -271,8 +284,8 @@ type GetQuestionHistoryReq struct { } // GetQuestionHistory handler for history of quiz -func (s *Service) GetQuestionHistory(ctx *fiber.Ctx) error { - var req GetQuizHistoryReq +func (r *Question) GetQuestionHistory(ctx *fiber.Ctx) error { + var req GetQuestionHistoryReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } @@ -281,7 +294,7 @@ func (s *Service) GetQuestionHistory(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusFailedDependency).SendString("no id provided") } - history, err := s.dal.QuestionRepo.QuestionHistory(ctx.Context(), req.Id, req.Limit, req.Page*req.Limit) + history, err := r.dal.QuestionRepo.QuestionHistory(ctx.Context(), req.Id, req.Limit, req.Page*req.Limit) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -289,15 +302,22 @@ func (s *Service) GetQuestionHistory(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusOK).JSON(history) } +type DeactivateResp struct { + Deactivated uint64 `json:"deactivated"` +} + // DeleteQuestion handler for fake delete question -func (s *Service) DeleteQuestion(ctx *fiber.Ctx) error { +func (r *Question) DeleteQuestion(ctx *fiber.Ctx) error { accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") } hlogger := log_mw.ExtractLogger(ctx) - var req DeactivateReq + var req struct { + Id uint64 `json:"id"` + } + if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } @@ -306,7 +326,7 @@ func (s *Service) DeleteQuestion(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusFailedDependency).SendString("id for deleting question is required") } - deleted, err := s.dal.QuestionRepo.DeleteQuestion(ctx.Context(), req.Id) + deleted, err := r.dal.QuestionRepo.DeleteQuestion(ctx.Context(), req.Id) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } diff --git a/internal/controllers/http_controllers/question/route.go b/internal/controllers/http_controllers/question/route.go new file mode 100644 index 0000000..bc152d6 --- /dev/null +++ b/internal/controllers/http_controllers/question/route.go @@ -0,0 +1,16 @@ +package question + +import "github.com/gofiber/fiber/v2" + +func (r *Question) Register(router fiber.Router) { + router.Post("/create", r.CreateQuestion) + router.Post("/getList", r.GetQuestionList) + router.Patch("/edit", r.UpdateQuestion) + router.Post("/copy", r.CopyQuestion) + router.Post("/history", r.GetQuestionHistory) + router.Delete("/delete", r.DeleteQuestion) +} + +func (r *Question) Name() string { + return "question" +} diff --git a/service/quiz_svc.go b/internal/controllers/http_controllers/quiz/quiz.go similarity index 91% rename from service/quiz_svc.go rename to internal/controllers/http_controllers/quiz/quiz.go index 54610c2..21e6736 100644 --- a/service/quiz_svc.go +++ b/internal/controllers/http_controllers/quiz/quiz.go @@ -1,17 +1,30 @@ -package service +package quiz import ( + "fmt" "github.com/gofiber/fiber/v2" "penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/middleware" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/repository/quiz" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/models" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/models" "time" "unicode/utf8" - "fmt" ) +type Deps struct { + DAL *dal.DAL +} + +type Quiz struct { + dal *dal.DAL +} + +func NewQuizController(deps Deps) *Quiz { + return &Quiz{dal: deps.DAL} +} + type CreateQuizReq struct { Fingerprinting bool `json:"fingerprinting"` // true if you need to store device id Repeatable bool `json:"repeatable"` // make it true for allow more than one quiz checkouting @@ -36,7 +49,7 @@ type CreateQuizReq struct { } // CreateQuiz handler for quiz creating request -func (s *Service) CreateQuiz(ctx *fiber.Ctx) error { +func (r *Quiz) CreateQuiz(ctx *fiber.Ctx) error { var req CreateQuizReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -95,7 +108,7 @@ func (s *Service) CreateQuiz(ctx *fiber.Ctx) error { GroupId: req.GroupId, } - quizID, err := s.dal.QuizRepo.CreateQuiz(ctx.Context(), &record) + quizID, err := r.dal.QuizRepo.CreateQuiz(ctx.Context(), &record) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -129,7 +142,7 @@ type GetQuizListResp struct { } // GetQuizList handler for paginated list quiz -func (s *Service) GetQuizList(ctx *fiber.Ctx) error { +func (r *Quiz) GetQuizList(ctx *fiber.Ctx) error { var req GetQuizListReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -151,7 +164,7 @@ func (s *Service) GetQuizList(ctx *fiber.Ctx) error { "'stop','start','draft', 'template','timeout','offlimit'") } - res, cnt, err := s.dal.QuizRepo.GetQuizList(ctx.Context(), + res, cnt, err := r.dal.QuizRepo.GetQuizList(ctx.Context(), quiz.GetQuizListDeps{ Limit: req.Limit, Offset: req.Limit * req.Page, @@ -195,7 +208,11 @@ type UpdateQuizReq struct { GroupId uint64 `json:"group_id"` } -func (s *Service) UpdateQuiz(ctx *fiber.Ctx) error { +type UpdateResp struct { + Updated uint64 `json:"updated"` +} + +func (r *Quiz) UpdateQuiz(ctx *fiber.Ctx) error { var req UpdateQuizReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -236,7 +253,7 @@ func (s *Service) UpdateQuiz(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusConflict).SendString("you can pause quiz only if it has deadline for passing") } - quiz, err := s.dal.QuizRepo.MoveToHistoryQuiz(ctx.Context(), req.Id, accountId) + quiz, err := r.dal.QuizRepo.MoveToHistoryQuiz(ctx.Context(), req.Id, accountId) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -308,7 +325,7 @@ func (s *Service) UpdateQuiz(ctx *fiber.Ctx) error { quiz.ParentIds = append(quiz.ParentIds, int32(quiz.Id)) - if err := s.dal.QuizRepo.UpdateQuiz(ctx.Context(), accountId, quiz); err != nil { + if err := r.dal.QuizRepo.UpdateQuiz(ctx.Context(), accountId, quiz); err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -336,7 +353,7 @@ type CopyQuizReq struct { } // CopyQuiz request handler for copy quiz -func (s *Service) CopyQuiz(ctx *fiber.Ctx) error { +func (r *Quiz) CopyQuiz(ctx *fiber.Ctx) error { var req CopyQuizReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -351,7 +368,7 @@ func (s *Service) CopyQuiz(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusFailedDependency).SendString("no id provided") } - quiz, err := s.dal.QuizRepo.CopyQuiz(ctx.Context(), accountId, req.Id) + quiz, err := r.dal.QuizRepo.CopyQuiz(ctx.Context(), accountId, req.Id) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -369,7 +386,7 @@ type GetQuizHistoryReq struct { } // GetQuizHistory handler for history of quiz -func (s *Service) GetQuizHistory(ctx *fiber.Ctx) error { +func (r *Quiz) GetQuizHistory(ctx *fiber.Ctx) error { var req GetQuizHistoryReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -383,7 +400,7 @@ func (s *Service) GetQuizHistory(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusFailedDependency).SendString("no id provided") } - history, err := s.dal.QuizRepo.QuizHistory(ctx.Context(), quiz.QuizHistoryDeps{ + history, err := r.dal.QuizRepo.QuizHistory(ctx.Context(), quiz.QuizHistoryDeps{ Id: req.Id, Limit: req.Limit, Offset: req.Page * req.Limit, @@ -406,7 +423,7 @@ type DeactivateResp struct { } // DeleteQuiz handler for fake delete quiz -func (s *Service) DeleteQuiz(ctx *fiber.Ctx) error { +func (r *Quiz) DeleteQuiz(ctx *fiber.Ctx) error { var req DeactivateReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -422,7 +439,7 @@ func (s *Service) DeleteQuiz(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusFailedDependency).SendString("id for deleting is required") } - deleted, err := s.dal.QuizRepo.DeleteQuiz(ctx.Context(), accountId, req.Id) + deleted, err := r.dal.QuizRepo.DeleteQuiz(ctx.Context(), accountId, req.Id) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -438,7 +455,7 @@ func (s *Service) DeleteQuiz(ctx *fiber.Ctx) error { } // ArchiveQuiz handler for archiving quiz -func (s *Service) ArchiveQuiz(ctx *fiber.Ctx) error { +func (r *Quiz) ArchiveQuiz(ctx *fiber.Ctx) error { var req DeactivateReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -453,7 +470,7 @@ func (s *Service) ArchiveQuiz(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusFailedDependency).SendString("id for archive quiz is required") } - archived, err := s.dal.QuizRepo.DeleteQuiz(ctx.Context(), accountId, req.Id) + archived, err := r.dal.QuizRepo.DeleteQuiz(ctx.Context(), accountId, req.Id) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -467,7 +484,7 @@ type QuizMoveReq struct { Qid, AccountID string } -func (s *Service) QuizMove(ctx *fiber.Ctx) error { +func (r *Quiz) QuizMove(ctx *fiber.Ctx) error { var req QuizMoveReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -477,7 +494,7 @@ func (s *Service) QuizMove(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request qid and accountID is required") } - resp, err := s.dal.QuizRepo.QuizMove(ctx.Context(), req.Qid, req.AccountID) + resp, err := r.dal.QuizRepo.QuizMove(ctx.Context(), req.Qid, req.AccountID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -485,7 +502,7 @@ func (s *Service) QuizMove(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusOK).JSON(resp) } -func (s *Service) TemplateCopy(ctx *fiber.Ctx) error { +func (r *Quiz) TemplateCopy(ctx *fiber.Ctx) error { accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") @@ -504,7 +521,7 @@ func (s *Service) TemplateCopy(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request qid is required") } - qizID, err := s.dal.QuizRepo.TemplateCopy(ctx.Context(), accountID, req.Qid) + qizID, err := r.dal.QuizRepo.TemplateCopy(ctx.Context(), accountID, req.Qid) if err != nil { fmt.Println("TEMPLERR", err) return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) diff --git a/internal/controllers/http_controllers/quiz/route.go b/internal/controllers/http_controllers/quiz/route.go new file mode 100644 index 0000000..a267542 --- /dev/null +++ b/internal/controllers/http_controllers/quiz/route.go @@ -0,0 +1,19 @@ +package quiz + +import "github.com/gofiber/fiber/v2" + +func (r *Quiz) Register(router fiber.Router) { + router.Post("/create", r.CreateQuiz) + router.Post("/getList", r.GetQuizList) + router.Patch("/edit", r.UpdateQuiz) + router.Post("/copy", r.CopyQuiz) + router.Post("/history", r.GetQuizHistory) + router.Delete("/delete", r.DeleteQuiz) + router.Patch("/archive", r.ArchiveQuiz) + router.Post("/move", r.QuizMove) + router.Post("/template", r.TemplateCopy) +} + +func (r *Quiz) Name() string { + return "quiz" +} diff --git a/service/result_svc.go b/internal/controllers/http_controllers/result/result.go similarity index 78% rename from service/result_svc.go rename to internal/controllers/http_controllers/result/result.go index b06a40c..5902b30 100644 --- a/service/result_svc.go +++ b/internal/controllers/http_controllers/result/result.go @@ -1,16 +1,34 @@ -package service +package result import ( "bytes" "github.com/gofiber/fiber/v2" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/middleware" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/repository/result" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/tools" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/tools" "strconv" "time" ) +type Deps struct { + DAL *dal.DAL + S3Prefix string +} + +type Result struct { + dal *dal.DAL + s3Prefix string +} + +func NewResultController(deps Deps) *Result { + return &Result{ + dal: deps.DAL, + s3Prefix: deps.S3Prefix, + } +} + type ReqExport struct { To, From time.Time New bool @@ -23,7 +41,7 @@ type ReqExportResponse struct { Results []model.AnswerExport `json:"results"` } -func (s *Service) GetResultsByQuizID(ctx *fiber.Ctx) error { +func (r *Result) GetResultsByQuizID(ctx *fiber.Ctx) error { payment := true // параметр для определения существования текущих привилегий юзера accountID, ok := middleware.GetAccountId(ctx) @@ -42,7 +60,7 @@ func (s *Service) GetResultsByQuizID(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid quiz ID format") } - account, err := s.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) + account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -52,7 +70,7 @@ func (s *Service) GetResultsByQuizID(ctx *fiber.Ctx) error { } } - results, totalCount, err := s.dal.ResultRepo.GetQuizResults(ctx.Context(), quizID, result.GetQuizResDeps{ + results, totalCount, err := r.dal.ResultRepo.GetQuizResults(ctx.Context(), quizID, result.GetQuizResDeps{ To: req.To, From: req.From, New: req.New, @@ -71,7 +89,7 @@ func (s *Service) GetResultsByQuizID(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusOK).JSON(resp) } -func (s *Service) DelResultByID(ctx *fiber.Ctx) error { +func (r *Result) DelResultByID(ctx *fiber.Ctx) error { accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("could not get account ID from token") @@ -83,7 +101,7 @@ func (s *Service) DelResultByID(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid result ID format") } - isOwner, err := s.dal.ResultRepo.CheckResultOwner(ctx.Context(), resultID, accountID) + isOwner, err := r.dal.ResultRepo.CheckResultOwner(ctx.Context(), resultID, accountID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -92,7 +110,7 @@ func (s *Service) DelResultByID(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusUnauthorized).SendString("not the owner of the result") } - if err := s.dal.ResultRepo.SoftDeleteResultByID(ctx.Context(), resultID); err != nil { + if err := r.dal.ResultRepo.SoftDeleteResultByID(ctx.Context(), resultID); err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -104,7 +122,7 @@ type ReqSeen struct { Answers []int64 } -func (s *Service) SetStatus(ctx *fiber.Ctx) error { +func (r *Result) SetStatus(ctx *fiber.Ctx) error { var req ReqSeen if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -115,7 +133,7 @@ func (s *Service) SetStatus(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusUnauthorized).SendString("could not get account ID from token") } - answers, err := s.dal.ResultRepo.CheckResultsOwner(ctx.Context(), req.Answers, accountID) + answers, err := r.dal.ResultRepo.CheckResultsOwner(ctx.Context(), req.Answers, accountID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -124,14 +142,14 @@ func (s *Service) SetStatus(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusNotAcceptable).SendString("could not update some answers because you don't have rights") } - if err := s.dal.ResultRepo.UpdateAnswersStatus(ctx.Context(), accountID, answers); err != nil { + if err := r.dal.ResultRepo.UpdateAnswersStatus(ctx.Context(), accountID, answers); err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return ctx.Status(fiber.StatusOK).JSON(nil) } -func (s *Service) ExportResultsToCSV(ctx *fiber.Ctx) error { +func (r *Result) ExportResultsToCSV(ctx *fiber.Ctx) error { accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") @@ -148,7 +166,7 @@ func (s *Service) ExportResultsToCSV(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusBadRequest).SendString("invalid request body") } - account, err := s.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) + account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -159,17 +177,17 @@ func (s *Service) ExportResultsToCSV(ctx *fiber.Ctx) error { } } - quiz, err := s.dal.QuizRepo.GetQuizById(ctx.Context(), accountID, quizID) + quiz, err := r.dal.QuizRepo.GetQuizById(ctx.Context(), accountID, quizID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString("failed to get quiz") } - questions, err := s.dal.ResultRepo.GetQuestions(ctx.Context(), quizID) + questions, err := r.dal.ResultRepo.GetQuestions(ctx.Context(), quizID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString("failed to get questions") } - answers, err := s.dal.ResultRepo.GetQuizResultsCSV(ctx.Context(), quizID, result.GetQuizResDeps{ + answers, err := r.dal.ResultRepo.GetQuizResultsCSV(ctx.Context(), quizID, result.GetQuizResDeps{ To: req.To, From: req.From, New: req.New, @@ -182,7 +200,7 @@ func (s *Service) ExportResultsToCSV(ctx *fiber.Ctx) error { buffer := new(bytes.Buffer) - if err := tools.WriteDataToExcel(buffer, questions, answers, s.s3Prefix + quiz.Qid + "/"); err != nil { + if err := tools.WriteDataToExcel(buffer, questions, answers, r.s3Prefix+quiz.Qid+"/"); err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString("failed to write data to Excel") } @@ -192,7 +210,7 @@ func (s *Service) ExportResultsToCSV(ctx *fiber.Ctx) error { return ctx.Send(buffer.Bytes()) } -func (s *Service) GetResultAnswers(ctx *fiber.Ctx) error { +func (r *Result) GetResultAnswers(ctx *fiber.Ctx) error { accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") @@ -203,7 +221,7 @@ func (s *Service) GetResultAnswers(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusBadRequest).SendString("invalid quiz ID") } - account, err := s.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) + account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -214,11 +232,11 @@ func (s *Service) GetResultAnswers(ctx *fiber.Ctx) error { } } - answers, err := s.dal.ResultRepo.GetResultAnswers(ctx.Context(), resultID) + answers, err := r.dal.ResultRepo.GetResultAnswers(ctx.Context(), resultID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString("failed to get result answers") } - sortedAnswers, err := s.dal.QuestionRepo.ForSortingResults(ctx.Context(), answers) + sortedAnswers, err := r.dal.QuestionRepo.ForSortingResults(ctx.Context(), answers) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString("failed sort result answers") } diff --git a/internal/controllers/http_controllers/result/route.go b/internal/controllers/http_controllers/result/route.go new file mode 100644 index 0000000..a70af9d --- /dev/null +++ b/internal/controllers/http_controllers/result/route.go @@ -0,0 +1,15 @@ +package result + +import "github.com/gofiber/fiber/v2" + +func (r *Result) Register(router fiber.Router) { + router.Post("/results/getResults/:quizId", r.GetResultsByQuizID) + router.Delete("/results/delete/:resultId", r.DelResultByID) + router.Patch("/result/seen", r.SetStatus) + router.Post("/results/:quizID/export", r.ExportResultsToCSV) + router.Get("/result/:resultID", r.GetResultAnswers) +} + +func (r *Result) Name() string { + return "" +} diff --git a/internal/controllers/http_controllers/statistic/route.go b/internal/controllers/http_controllers/statistic/route.go new file mode 100644 index 0000000..ce7d661 --- /dev/null +++ b/internal/controllers/http_controllers/statistic/route.go @@ -0,0 +1,15 @@ +package statistic + +import "github.com/gofiber/fiber/v2" + +func (r *Statistic) Register(router fiber.Router) { + router.Post("/statistic/:quizID/devices", r.GetDeviceStatistics) + router.Post("/statistic/:quizID/general", r.GetGeneralStatistics) + router.Post("/statistic/:quizID/questions", r.GetQuestionsStatistics) + router.Post("/statistic", r.AllServiceStatistics) + router.Get("/statistics/:quizID/pipelines", r.GetPipelinesStatistics) +} + +func (r *Statistic) Name() string { + return "" +} diff --git a/service/statistic_svc.go b/internal/controllers/http_controllers/statistic/statistic.go similarity index 76% rename from service/statistic_svc.go rename to internal/controllers/http_controllers/statistic/statistic.go index 432ef2b..e4c2572 100644 --- a/service/statistic_svc.go +++ b/internal/controllers/http_controllers/statistic/statistic.go @@ -1,17 +1,35 @@ -package service +package statistic import ( "github.com/gofiber/fiber/v2" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/repository/statistics" "strconv" ) +type Deps struct { + DAL *dal.DAL + ChDAL *dal.ClickHouseDAL +} + +type Statistic struct { + dal *dal.DAL + chDAL *dal.ClickHouseDAL +} + +func NewStatisticController(deps Deps) *Statistic { + return &Statistic{ + dal: deps.DAL, + chDAL: deps.ChDAL, + } +} + type DeviceStatReq struct { From uint64 // временные границы выбора статистики To uint64 } -func (s *Service) GetDeviceStatistics(ctx *fiber.Ctx) error { +func (r *Statistic) GetDeviceStatistics(ctx *fiber.Ctx) error { quizIDStr := ctx.Params("quizID") quizID, err := strconv.ParseInt(quizIDStr, 10, 64) @@ -24,7 +42,7 @@ func (s *Service) GetDeviceStatistics(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } - deviceStats, err := s.dal.StatisticsRepo.GetDeviceStatistics(ctx.Context(), statistics.DeviceStatReq{ + deviceStats, err := r.dal.StatisticsRepo.GetDeviceStatistics(ctx.Context(), statistics.DeviceStatReq{ QuizId: quizID, From: req.From, To: req.To, @@ -40,7 +58,7 @@ type GeneralStatsResp struct { Open, Result, AvTime, Conversion map[uint64]uint64 } -func (s *Service) GetGeneralStatistics(ctx *fiber.Ctx) error { +func (r *Statistic) GetGeneralStatistics(ctx *fiber.Ctx) error { quizIDStr := ctx.Params("quizID") quizID, err := strconv.ParseInt(quizIDStr, 10, 64) if err != nil { @@ -52,7 +70,7 @@ func (s *Service) GetGeneralStatistics(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } - generalStats, err := s.dal.StatisticsRepo.GetGeneralStatistics(ctx.Context(), statistics.DeviceStatReq{ + generalStats, err := r.dal.StatisticsRepo.GetGeneralStatistics(ctx.Context(), statistics.DeviceStatReq{ QuizId: quizID, From: req.From, To: req.To, @@ -64,7 +82,7 @@ func (s *Service) GetGeneralStatistics(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusOK).JSON(generalStats) } -func (s *Service) GetQuestionsStatistics(ctx *fiber.Ctx) error { +func (r *Statistic) GetQuestionsStatistics(ctx *fiber.Ctx) error { quizIDStr := ctx.Params("quizID") quizID, err := strconv.ParseInt(quizIDStr, 0, 64) if err != nil { @@ -76,7 +94,7 @@ func (s *Service) GetQuestionsStatistics(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } - questionsStats, err := s.dal.StatisticsRepo.GetQuestionsStatistics(ctx.Context(), statistics.DeviceStatReq{ + questionsStats, err := r.dal.StatisticsRepo.GetQuestionsStatistics(ctx.Context(), statistics.DeviceStatReq{ QuizId: quizID, From: req.From, To: req.To, @@ -92,13 +110,13 @@ type StatisticReq struct { From, To uint64 // временные границы выбора статистики } -func (s *Service) AllServiceStatistics(ctx *fiber.Ctx) error { +func (r *Statistic) AllServiceStatistics(ctx *fiber.Ctx) error { var req StatisticReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } - allSvcStats, err := s.dal.StatisticsRepo.AllServiceStatistics(ctx.Context(), req.From, req.To) + allSvcStats, err := r.dal.StatisticsRepo.AllServiceStatistics(ctx.Context(), req.From, req.To) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -106,7 +124,7 @@ func (s *Service) AllServiceStatistics(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusOK).JSON(allSvcStats) } -func (s *Service) GetPipelinesStatistics(ctx *fiber.Ctx) error { +func (r *Statistic) GetPipelinesStatistics(ctx *fiber.Ctx) error { var req StatisticReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -118,7 +136,7 @@ func (s *Service) GetPipelinesStatistics(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid quiz ID format") } - result, err := s.chDAL.StatisticClickRepo.GetPipelinesStatistics(ctx.Context(), quizID, req.From, req.To) + result, err := r.chDAL.StatisticClickRepo.GetPipelinesStatistics(ctx.Context(), quizID, req.From, req.To) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } diff --git a/internal/controllers/http_controllers/telegram/route.go b/internal/controllers/http_controllers/telegram/route.go new file mode 100644 index 0000000..811b963 --- /dev/null +++ b/internal/controllers/http_controllers/telegram/route.go @@ -0,0 +1,14 @@ +package telegram + +import "github.com/gofiber/fiber/v2" + +func (r *Telegram) Register(router fiber.Router) { + router.Get("/pool", r.GetPoolTgAccounts) + router.Post("/create", r.AddingTgAccount) + router.Delete("/:id", r.DeleteTgAccountByID) + router.Post("/setCode", r.SettingTgCode) +} + +func (r *Telegram) Name() string { + return "telegram" +} diff --git a/service/telegram_svc.go b/internal/controllers/http_controllers/telegram/telegram.go similarity index 82% rename from service/telegram_svc.go rename to internal/controllers/http_controllers/telegram/telegram.go index f2862fe..1b09e91 100644 --- a/service/telegram_svc.go +++ b/internal/controllers/http_controllers/telegram/telegram.go @@ -1,4 +1,4 @@ -package service +package telegram import ( "errors" @@ -6,20 +6,38 @@ import ( "github.com/gofiber/fiber/v2" "github.com/rs/xid" "path/filepath" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/pj_errors" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/clients/telegram" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/clients/telegram" "penahub.gitlab.yandexcloud.net/backend/tdlib/client" "strconv" ) +type Deps struct { + DAL *dal.DAL + TelegramClient *telegram.TelegramClient +} + +type Telegram struct { + dal *dal.DAL + telegramClient *telegram.TelegramClient +} + +func NewTelegramController(deps Deps) *Telegram { + return &Telegram{ + dal: deps.DAL, + telegramClient: deps.TelegramClient, + } +} + type Message struct { Type string `json:"type"` Data string `json:"data"` } -func (s *Service) GetPoolTgAccounts(ctx *fiber.Ctx) error { - allAccounts, err := s.dal.TgRepo.GetAllTgAccounts(ctx.Context()) +func (r *Telegram) GetPoolTgAccounts(ctx *fiber.Ctx) error { + allAccounts, err := r.dal.TgRepo.GetAllTgAccounts(ctx.Context()) if err != nil { switch { case errors.Is(err, pj_errors.ErrNotFound): @@ -31,7 +49,7 @@ func (s *Service) GetPoolTgAccounts(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusOK).JSON(allAccounts) } -func (s *Service) AddingTgAccount(ctx *fiber.Ctx) error { +func (r *Telegram) AddingTgAccount(ctx *fiber.Ctx) error { var req telegram.AuthTgUserReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") @@ -39,7 +57,7 @@ func (s *Service) AddingTgAccount(ctx *fiber.Ctx) error { if req.ApiID == 0 || req.ApiHash == "" || req.Password == "" || req.PhoneNumber == "" { return ctx.Status(fiber.StatusBadRequest).SendString("empty required fields") } - allAccounts, err := s.dal.TgRepo.GetAllTgAccounts(ctx.Context()) + allAccounts, err := r.dal.TgRepo.GetAllTgAccounts(ctx.Context()) if err != nil && !errors.Is(err, pj_errors.ErrNotFound) { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -84,7 +102,7 @@ func (s *Service) AddingTgAccount(ctx *fiber.Ctx) error { fmt.Println("new client failed", err) return } - s.telegramClient.SaveTgAccount(req.ApiID, req.ApiHash, tdlibClient) + r.telegramClient.SaveTgAccount(req.ApiID, req.ApiHash, tdlibClient) fmt.Println("i am down") }() if goErr != nil { @@ -102,7 +120,7 @@ func (s *Service) AddingTgAccount(ctx *fiber.Ctx) error { authorizer.PhoneNumber <- req.PhoneNumber case client.TypeAuthorizationStateWaitCode: signature := xid.New() - s.telegramClient.AddedToMap(telegram.WaitingClient{ + r.telegramClient.AddedToMap(telegram.WaitingClient{ PreviousReq: req, Authorizer: authorizer, }, signature.String()) @@ -114,7 +132,7 @@ func (s *Service) AddingTgAccount(ctx *fiber.Ctx) error { } } -func (s *Service) SettingTgCode(ctx *fiber.Ctx) error { +func (r *Telegram) SettingTgCode(ctx *fiber.Ctx) error { var req struct { Code string `json:"code"` Signature string `json:"signature"` @@ -127,7 +145,7 @@ func (s *Service) SettingTgCode(ctx *fiber.Ctx) error { return ctx.Status(fiber.StatusBadRequest).SendString("empty required fields") } - data, ok := s.telegramClient.GetFromMap(req.Signature) + data, ok := r.telegramClient.GetFromMap(req.Signature) if !ok { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid id, don't have data") } @@ -140,7 +158,7 @@ func (s *Service) SettingTgCode(ctx *fiber.Ctx) error { fmt.Println("currnet state:", state) switch state.AuthorizationStateType() { case client.TypeAuthorizationStateReady: - id, err := s.dal.TgRepo.CreateTgAccount(ctx.Context(), model.TgAccount{ + id, err := r.dal.TgRepo.CreateTgAccount(ctx.Context(), model.TgAccount{ ApiID: data.PreviousReq.ApiID, ApiHash: data.PreviousReq.ApiHash, PhoneNumber: data.PreviousReq.PhoneNumber, @@ -159,12 +177,12 @@ func (s *Service) SettingTgCode(ctx *fiber.Ctx) error { } } -func (s *Service) DeleteTgAccountByID(ctx *fiber.Ctx) error { +func (r *Telegram) DeleteTgAccountByID(ctx *fiber.Ctx) error { id, err := strconv.ParseInt(ctx.Params("id"), 10, 64) if err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("invalid id format") } - err = s.dal.TgRepo.SoftDeleteTgAccount(ctx.Context(), id) + err = r.dal.TgRepo.SoftDeleteTgAccount(ctx.Context(), id) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } diff --git a/rpc_service/mail_notify.go b/internal/controllers/rpc_controllers/mail_notify.go similarity index 61% rename from rpc_service/mail_notify.go rename to internal/controllers/rpc_controllers/mail_notify.go index 0ec5bf8..23f87bc 100644 --- a/rpc_service/mail_notify.go +++ b/internal/controllers/rpc_controllers/mail_notify.go @@ -1,13 +1,13 @@ -package rpc_service +package rpc_controllers import ( "context" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/proto/notifyer" + notifyer2 "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/proto/notifyer" ) type MailNotify struct { - notifyer.UnimplementedQuizServiceServer + notifyer2.UnimplementedQuizServiceServer dal *dal.DAL } @@ -17,23 +17,23 @@ func NewMailNotify(dal *dal.DAL) *MailNotify { } } -func (m *MailNotify) GetQuizzes(ctx context.Context, in *notifyer.GetQuizzesRequest) (*notifyer.GetQuizzesResponse, error) { +func (m *MailNotify) GetQuizzes(ctx context.Context, in *notifyer2.GetQuizzesRequest) (*notifyer2.GetQuizzesResponse, error) { ids, err := m.dal.QuizRepo.GetAllQuizzesID(ctx, in.AccountId) if err != nil { return nil, err } - resp := ¬ifyer.GetQuizzesResponse{ + resp := ¬ifyer2.GetQuizzesResponse{ QuizIds: ids, } return resp, nil } -func (m *MailNotify) GetStartedQuizzes(ctx context.Context, in *notifyer.GetStartedQuizzesRequest) (*notifyer.GetStartedQuizzesResponse, error) { +func (m *MailNotify) GetStartedQuizzes(ctx context.Context, in *notifyer2.GetStartedQuizzesRequest) (*notifyer2.GetStartedQuizzesResponse, error) { ids, err := m.dal.QuizRepo.GetStartedQuizzesID(ctx, in.AccountId) if err != nil { return nil, err } - resp := ¬ifyer.GetStartedQuizzesResponse{ + resp := ¬ifyer2.GetStartedQuizzesResponse{ QuizIds: ids, } return resp, nil diff --git a/internal/initialize/clients.go b/internal/initialize/clients.go new file mode 100644 index 0000000..d792494 --- /dev/null +++ b/internal/initialize/clients.go @@ -0,0 +1,25 @@ +package initialize + +import ( + "context" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/clients/auth" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/clients/telegram" +) + +type Clients struct { + AuthClient *auth.AuthClient + TgClient *telegram.TelegramClient +} + +func NewClients(ctx context.Context, cfg Config, pgDAL *dal.DAL) (*Clients, error) { + tgClient, err := telegram.NewTelegramClient(ctx, pgDAL) + if err != nil { + return nil, err + } + + return &Clients{ + TgClient: tgClient, + AuthClient: auth.NewAuthClient(cfg.AuthServiceURL), + }, nil +} diff --git a/internal/initialize/config.go b/internal/initialize/config.go new file mode 100644 index 0000000..c3d2af1 --- /dev/null +++ b/internal/initialize/config.go @@ -0,0 +1,43 @@ +package initialize + +import ( + "github.com/caarlos0/env/v8" + "github.com/joho/godotenv" + "log" +) + +type Config struct { + LoggerProdMode bool `env:"IS_PROD_LOG" envDefault:"false"` + IsProd bool `env:"IS_PROD" envDefault:"false"` + NumberPort string `env:"PORT" envDefault:"1488"` + HttpHost string `env:"HTTP_HOST" envDefault:"0.0.0.0"` + CrtFile string `env:"CRT" envDefault:"server.crt"` + KeyFile string `env:"KEY" envDefault:"server.key"` + PostgresCredentials string `env:"PG_CRED" envDefault:"host=localhost port=35432 user=squiz password=Redalert2 dbname=squiz sslmode=disable"` + HubAdminUrl string `env:"HUB_ADMIN_URL" envDefault:"http://localhost:8001/"` + ServiceName string `env:"SERVICE_NAME" envDefault:"squiz"` + AuthServiceURL string `env:"AUTH_URL" envDefault:"http://localhost:8000/"` + GrpcHost string `env:"GRPC_HOST" envDefault:"localhost"` + GrpcPort string `env:"GRPC_PORT" envDefault:"9000"` + KafkaBrokers string `env:"KAFKA_BROKERS" envDefault:"localhost:9092"` + KafkaTopic string `env:"KAFKA_TOPIC" envDefault:"test-topic"` + KafkaGroup string `env:"KAFKA_GROUP" envDefault:"mailnotifier"` + TrashLogHost string `env:"TRASH_LOG_HOST" envDefault:"localhost:7113"` + ModuleLogger string `env:"MODULE_LOGGER" envDefault:"core-local"` + ClickHouseCred string `env:"CLICK_HOUSE_CRED" envDefault:"tcp://10.8.0.15:9000/default?sslmode=disable"` + RedisHost string `env:"REDIS_HOST" envDefault:"localhost:6379"` + RedisPassword string `env:"REDIS_PASSWORD" envDefault:"admin"` + RedisDB uint64 `env:"REDIS_DB" envDefault:"2"` + S3Prefix string `env:"S3_PREFIX"` +} + +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 +} diff --git a/internal/initialize/controllers.go b/internal/initialize/controllers.go new file mode 100644 index 0000000..19879e1 --- /dev/null +++ b/internal/initialize/controllers.go @@ -0,0 +1,73 @@ +package initialize + +import ( + "github.com/go-redis/redis/v8" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/brokers" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/controllers/http_controllers/account" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/controllers/http_controllers/question" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/controllers/http_controllers/quiz" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/controllers/http_controllers/result" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/controllers/http_controllers/statistic" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/controllers/http_controllers/telegram" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/controllers/rpc_controllers" +) + +type ControllerDeps struct { + Clients *Clients + DALs *DALs + Config Config + Producer *brokers.Producer + RedisClient *redis.Client +} + +type Controller struct { + GRpcControllers GRpcControllers + HttpControllers HttpControllers +} + +type GRpcControllers struct { + MailNotify *rpc_controllers.MailNotify +} +type HttpControllers struct { + Account *account.Account + Question *question.Question + Quiz *quiz.Quiz + Result *result.Result + Statistic *statistic.Statistic + Telegram *telegram.Telegram +} + +func NewControllers(deps ControllerDeps) *Controller { + return &Controller{ + GRpcControllers: GRpcControllers{ + MailNotify: rpc_controllers.NewMailNotify(deps.DALs.PgDAL), + }, + HttpControllers: HttpControllers{ + Account: account.NewAccountController(account.Deps{ + Dal: deps.DALs.PgDAL, + AuthClient: deps.Clients.AuthClient, + Producer: deps.Producer, + ServiceName: deps.Config.ServiceName, + RedisClient: deps.RedisClient, + }), + Question: question.NewQuestionController(question.Deps{ + DAL: deps.DALs.PgDAL, + }), + Quiz: quiz.NewQuizController(quiz.Deps{ + DAL: deps.DALs.PgDAL, + }), + Result: result.NewResultController(result.Deps{ + DAL: deps.DALs.PgDAL, + S3Prefix: deps.Config.S3Prefix, + }), + Statistic: statistic.NewStatisticController(statistic.Deps{ + DAL: deps.DALs.PgDAL, + ChDAL: deps.DALs.ChDAL, + }), + Telegram: telegram.NewTelegramController(telegram.Deps{ + DAL: deps.DALs.PgDAL, + TelegramClient: deps.Clients.TgClient, + }), + }, + } +} diff --git a/internal/initialize/dals.go b/internal/initialize/dals.go new file mode 100644 index 0000000..143e783 --- /dev/null +++ b/internal/initialize/dals.go @@ -0,0 +1,28 @@ +package initialize + +import ( + "context" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" +) + +type DALs struct { + PgDAL *dal.DAL + ChDAL *dal.ClickHouseDAL +} + +func NewDALs(ctx context.Context, cfg Config) (*DALs, error) { + pgDal, err := dal.New(ctx, cfg.PostgresCredentials, nil) + if err != nil { + return nil, err + } + + chDal, err := dal.NewClickHouseDAL(ctx, cfg.ClickHouseCred) + if err != nil { + return nil, err + } + + return &DALs{ + PgDAL: pgDal, + ChDAL: chDal, + }, nil +} diff --git a/initialize/kafka.go b/internal/initialize/kafka.go similarity index 100% rename from initialize/kafka.go rename to internal/initialize/kafka.go diff --git a/internal/initialize/redis.go b/internal/initialize/redis.go new file mode 100644 index 0000000..eb14f05 --- /dev/null +++ b/internal/initialize/redis.go @@ -0,0 +1,21 @@ +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.RedisHost, + Password: cfg.RedisPassword, + DB: int(cfg.RedisDB), + }) + + status := rdb.Ping(ctx) + if err := status.Err(); err != nil { + return nil, err + } + + return rdb, nil +} diff --git a/models/hlog_events.go b/internal/models/hlog_events.go similarity index 100% rename from models/hlog_events.go rename to internal/models/hlog_events.go diff --git a/proto/notifyer/notifyer.pb.go b/internal/proto/notifyer/notifyer.pb.go similarity index 100% rename from proto/notifyer/notifyer.pb.go rename to internal/proto/notifyer/notifyer.pb.go diff --git a/proto/notifyer/notifyer_grpc.pb.go b/internal/proto/notifyer/notifyer_grpc.pb.go similarity index 100% rename from proto/notifyer/notifyer_grpc.pb.go rename to internal/proto/notifyer/notifyer_grpc.pb.go diff --git a/server/grpc.go b/internal/server/grpc/rpc_server.go similarity index 88% rename from server/grpc.go rename to internal/server/grpc/rpc_server.go index c416a0c..69ff851 100644 --- a/server/grpc.go +++ b/internal/server/grpc/rpc_server.go @@ -9,8 +9,8 @@ import ( "go.uber.org/zap" "google.golang.org/grpc" "net" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/initialize" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/proto/notifyer" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/initialize" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/proto/notifyer" "time" ) @@ -58,7 +58,7 @@ func (g *GRPC) Stop(_ context.Context) error { return nil } -func (g *GRPC) Register(reg *initialize.RpcRegister) *GRPC { +func (g *GRPC) Register(reg initialize.GRpcControllers) *GRPC { notifyer.RegisterQuizServiceServer(g.grpc, reg.MailNotify) // another return g diff --git a/internal/server/http/http_server.go b/internal/server/http/http_server.go new file mode 100644 index 0000000..be4ed40 --- /dev/null +++ b/internal/server/http/http_server.go @@ -0,0 +1,73 @@ +package http + +import ( + "context" + "fmt" + "github.com/gofiber/fiber/v2" + "github.com/themakers/hlog" + "go.uber.org/zap" + "penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/middleware" +) + +type ServerConfig struct { + Logger *zap.Logger + Controllers []Controller + Hlogger hlog.Logger +} + +type Server struct { + Logger *zap.Logger + Controllers []Controller + app *fiber.App +} + +func NewServer(config ServerConfig) *Server { + app := fiber.New() + app.Use(middleware.JWTAuth()) + app.Use(log_mw.ContextLogger(config.Hlogger)) + //app.Get("/liveness", healthchecks.Liveness) + //app.Get("/readiness", healthchecks.Readiness(&workerErr)) //todo parametrized readiness. should discuss ready reason + s := &Server{ + Logger: config.Logger, + Controllers: config.Controllers, + app: app, + } + + s.registerRoutes() + + return s +} + +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 +} + +func (s *Server) Shutdown(ctx context.Context) error { + return s.app.Shutdown() +} + +func (s *Server) registerRoutes() { + for _, c := range s.Controllers { + router := s.app.Group(c.Name()) + c.Register(router) + } +} + +type Controller interface { + Register(router fiber.Router) + Name() string +} + +func (s *Server) ListRoutes() { + fmt.Println("Registered routes:") + for _, stack := range s.app.Stack() { + for _, route := range stack { + fmt.Printf("%s %s\n", route.Method, route.Path) + } + } +} diff --git a/tools/publishPriv.go b/internal/tools/publishPriv.go similarity index 100% rename from tools/publishPriv.go rename to internal/tools/publishPriv.go diff --git a/tools/tools.go b/internal/tools/tools.go similarity index 84% rename from tools/tools.go rename to internal/tools/tools.go index c23a27c..9fa57f5 100644 --- a/tools/tools.go +++ b/internal/tools/tools.go @@ -187,66 +187,6 @@ func handleImage(file *excelize.File, sheet, cell, content string, count, row in fmt.Println(err.Error()) } } - count := 2 - for _, q := range questions { - if !q.Deleted && q.Type != model.TypeResult { - index := binarySearch(response, q.Id) - if index != -1 { - cell := ToAlphaString(count) + strconv.Itoa(row) - tipe := FileSearch(response[index].Content) - noAccept := make(map[string]struct{}) - todoMap := make(map[string]string) - if tipe != "Text" && q.Type == model.TypeImages || q.Type == model.TypeVarImages { - urle := ExtractImageURL(response[index].Content) - urlData := strings.Split(urle, " ") - if len(urlData) == 1 { - u, err := url.Parse(urle) - if err == nil && u.Scheme != "" && u.Host != "" { - picture, err := downloadImage(urle) - if err != nil { - fmt.Println(err.Error()) - } - err = file.SetColWidth(sheet, ToAlphaString(count), ToAlphaString(count), 50) - if err != nil { - fmt.Println(err.Error()) - } - err = file.SetRowHeight(sheet, row, 150) - if err != nil { - fmt.Println(err.Error()) - } - if err := file.AddPictureFromBytes(sheet, cell, picture); err != nil { - fmt.Println(err.Error()) - } - noAccept[response[index].Content] = struct{}{} - } else { - todoMap[response[index].Content] = cell - } - } else { - todoMap[response[index].Content] = cell - } - } else if tipe != "Text" && q.Type == model.TypeFile { - urle := ExtractImageURL(response[index].Content) - display, tooltip := urle, urle - if err := file.SetCellValue(sheet, cell, response[index].Content); err != nil { - fmt.Println(err.Error()) - } - if err := file.SetCellHyperLink(sheet, cell, urle, "External", excelize.HyperlinkOpts{ - Display: &display, - Tooltip: &tooltip, - }); err != nil { - fmt.Println(err.Error()) - } - noAccept[response[index].Content] = struct{}{} - } else { - todoMap[response[index].Content] = cell - } - for cnt, cel := range todoMap { - if _, ok := noAccept[cnt]; !ok { - if err := file.SetCellValue(sheet, cel, cnt); err != nil { - fmt.Println(err.Error()) - } - } - } mediaRow := row for i, imgContent := range multiImgArr { @@ -356,7 +296,6 @@ func handleImage(file *excelize.File, sheet, cell, content string, count, row in } } } - func handleFile(file *excelize.File, sheet, cell, content, s3Prefix string, noAccept map[string]struct{}) { urle := content if urle != "" && !strings.HasPrefix(urle, "https") { diff --git a/workers/tg_worker.go b/internal/workers/tg_worker.go similarity index 97% rename from workers/tg_worker.go rename to internal/workers/tg_worker.go index 80deb41..51d5894 100644 --- a/workers/tg_worker.go +++ b/internal/workers/tg_worker.go @@ -7,7 +7,7 @@ import ( "github.com/go-redis/redis/v8" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/clients/telegram" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/clients/telegram" "strconv" "time" ) diff --git a/main.go b/main.go deleted file mode 100644 index 4f07545..0000000 --- a/main.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import ( - "github.com/skeris/appInit" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/app" - _ "penahub.gitlab.yandexcloud.net/devops/linters/golang.git/pkg/dummy" -) - -func main() { - appInit.Initialize(app.New, app.Options{}) -} diff --git a/pkg/closer/closer.go b/pkg/closer/closer.go new file mode 100644 index 0000000..fdfbaf1 --- /dev/null +++ b/pkg/closer/closer.go @@ -0,0 +1,37 @@ +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 +} diff --git a/schema/000001_init.down.sql b/schema/000001_init.down.sql deleted file mode 100644 index 54966a0..0000000 --- a/schema/000001_init.down.sql +++ /dev/null @@ -1,24 +0,0 @@ --- Drop indexes -DROP INDEX IF EXISTS subquizes; -DROP INDEX IF EXISTS birthtime; -DROP INDEX IF EXISTS groups; -DROP INDEX IF EXISTS timeouted; -DROP INDEX IF EXISTS active ON quiz; -DROP INDEX IF EXISTS questiontype; -DROP INDEX IF EXISTS required; -DROP INDEX IF EXISTS relation; -DROP INDEX IF EXISTS active ON question; - --- Drop tables -DROP TABLE IF EXISTS privileges; -DROP TABLE IF EXISTS answer; -DROP TABLE IF EXISTS question; -DROP TABLE IF EXISTS quiz; -DROP TABLE IF EXISTS account; - --- Drop types -DO $$ -BEGIN -DROP TYPE IF EXISTS question_type; -DROP TYPE IF EXISTS quiz_status; -END$$; \ No newline at end of file diff --git a/schema/000001_init.up.sql b/schema/000001_init.up.sql deleted file mode 100644 index 9aefa67..0000000 --- a/schema/000001_init.up.sql +++ /dev/null @@ -1,120 +0,0 @@ --- Create types -DO $$ -BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'question_type') THEN -CREATE TYPE question_type AS ENUM ( - 'variant', - 'images', - 'varimg', - 'emoji', - 'text', - 'select', - 'date', - 'number', - 'file', - 'page', - 'rating' - ); -END IF; - - IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'quiz_status') THEN -CREATE TYPE quiz_status AS ENUM ( - 'draft', - 'template', - 'stop', - 'start', - 'timeout', - 'offlimit' - ); -END IF; - - CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -END$$; - --- Create tables -CREATE TABLE IF NOT EXISTS account ( - id UUID PRIMARY KEY, - user_id VARCHAR(24), - email VARCHAR(50), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - deleted BOOLEAN DEFAULT false - ); - -CREATE TABLE IF NOT EXISTS quiz ( - id bigserial UNIQUE NOT NULL PRIMARY KEY, - qid uuid DEFAULT uuid_generate_v4(), - accountid varchar(30) NOT NULL, - deleted boolean DEFAULT false, - archived boolean DEFAULT false, - fingerprinting boolean DEFAULT false, - repeatable boolean DEFAULT false, - note_prevented boolean DEFAULT false, - mail_notifications boolean DEFAULT false, - unique_answers boolean DEFAULT false, - super boolean DEFAULT false, - group_id bigint DEFAULT 0, - name varchar(280), - description text, - config text, - status quiz_status DEFAULT 'draft', - limit_answers integer DEFAULT 0, - due_to integer DEFAULT 0, - time_of_passing integer DEFAULT 0, - pausable boolean DEFAULT false, - version smallint DEFAULT 0, - version_comment text DEFAULT '', - parent_ids integer[], - created_at timestamp DEFAULT CURRENT_TIMESTAMP, - updated_at timestamp DEFAULT CURRENT_TIMESTAMP, - questions_count integer DEFAULT 0, - answers_count integer DEFAULT 0, - average_time_passing integer DEFAULT 0 - ); - -CREATE TABLE IF NOT EXISTS question ( - id bigserial UNIQUE NOT NULL PRIMARY KEY, - quiz_id bigint NOT NULL, - title varchar(512) NOT NULL, - description text, - questiontype question_type DEFAULT 'text', - required boolean DEFAULT false, - deleted boolean DEFAULT false, - page smallint DEFAULT 0, - content text, - version smallint DEFAULT 0, - parent_ids integer[], - created_at timestamp DEFAULT CURRENT_TIMESTAMP, - updated_at timestamp DEFAULT CURRENT_TIMESTAMP, - CONSTRAINT quiz_relation FOREIGN KEY(quiz_id) REFERENCES quiz(id) - ); - -CREATE TABLE IF NOT EXISTS answer ( - id bigserial UNIQUE NOT NULL PRIMARY KEY, - content text, - quiz_id bigint NOT NULL REFERENCES quiz(id), - question_id bigint NOT NULL REFERENCES question(id), - fingerprint varchar(1024), - session varchar(20), - created_at timestamp DEFAULT CURRENT_TIMESTAMP - ); - -CREATE TABLE IF NOT EXISTS privileges ( - id SERIAL PRIMARY KEY, - privilegeID VARCHAR(50), - account_id UUID, - privilege_name VARCHAR(255), - amount INT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (account_id) REFERENCES account (id) - ); - --- Create indexes -CREATE INDEX IF NOT EXISTS active ON question(deleted) WHERE deleted=false; -CREATE INDEX IF NOT EXISTS relation ON question(quiz_id DESC); -CREATE INDEX IF NOT EXISTS required ON question(required DESC); -CREATE INDEX IF NOT EXISTS questiontype ON question(questiontype); -CREATE INDEX IF NOT EXISTS active ON quiz(deleted, archived, status) WHERE deleted = false AND archived = false AND status = 'start'; -CREATE INDEX IF NOT EXISTS timeouted ON quiz(due_to DESC) WHERE deleted = false AND due_to <> 0 AND status <> 'timeout'; -CREATE INDEX IF NOT EXISTS groups ON quiz(super) WHERE super = true; -CREATE INDEX IF NOT EXISTS birthtime ON quiz(created_at DESC); -CREATE INDEX IF NOT EXISTS subquizes ON quiz(group_id DESC) WHERE group_id <> 0; diff --git a/schema/000002_init.down.sql b/schema/000002_init.down.sql deleted file mode 100644 index 0aa3d9e..0000000 --- a/schema/000002_init.down.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE answer DROP COLUMN IF EXISTS result; diff --git a/schema/000002_init.up.sql b/schema/000002_init.up.sql deleted file mode 100644 index 4083153..0000000 --- a/schema/000002_init.up.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE answer ADD COLUMN result BOOLEAN DEFAULT FALSE; \ No newline at end of file diff --git a/schema/000003_init.down.sql b/schema/000003_init.down.sql deleted file mode 100644 index 94e352f..0000000 --- a/schema/000003_init.down.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE quiz DROP COLUMN IF EXISTS sessions_count; diff --git a/schema/000003_init.up.sql b/schema/000003_init.up.sql deleted file mode 100644 index a292d62..0000000 --- a/schema/000003_init.up.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE quiz ADD COLUMN sessions_count integer; diff --git a/schema/000004_init.down.sql b/schema/000004_init.down.sql deleted file mode 100644 index b6d5ec4..0000000 --- a/schema/000004_init.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE quiz DROP COLUMN IF EXISTS new; -ALTER TABLE quiz DROP COLUMN IF EXISTS deleted; \ No newline at end of file diff --git a/schema/000004_init.up.sql b/schema/000004_init.up.sql deleted file mode 100644 index 9dc5591..0000000 --- a/schema/000004_init.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE answer ADD COLUMN new BOOLEAN DEFAULT TRUE; -ALTER TABLE answer ADD COLUMN deleted BOOLEAN DEFAULT FALSE; \ No newline at end of file diff --git a/schema/000005_init.down.sql b/schema/000005_init.down.sql deleted file mode 100644 index ccae4f5..0000000 --- a/schema/000005_init.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE answer DROP COLUMN IF EXISTS email; -DROP INDEX IF EXISTS answer_email_unique_idx; \ No newline at end of file diff --git a/schema/000005_init.up.sql b/schema/000005_init.up.sql deleted file mode 100644 index f2bfe97..0000000 --- a/schema/000005_init.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE answer ADD COLUMN email VARCHAR(50) NOT NULL DEFAULT ''; -CREATE UNIQUE INDEX IF NOT EXISTS answer_email_unique_idx ON answer (quiz_id, email) WHERE email <> ''; \ No newline at end of file diff --git a/schema/000006_init.down.sql b/schema/000006_init.down.sql deleted file mode 100644 index 355ba3b..0000000 --- a/schema/000006_init.down.sql +++ /dev/null @@ -1,6 +0,0 @@ -ALTER TABLE answer -DROP COLUMN device_type, -DROP COLUMN device, -DROP COLUMN os, -DROP COLUMN browser, -DROP COLUMN ip; \ No newline at end of file diff --git a/schema/000006_init.up.sql b/schema/000006_init.up.sql deleted file mode 100644 index 4dc1487..0000000 --- a/schema/000006_init.up.sql +++ /dev/null @@ -1,6 +0,0 @@ -ALTER TABLE answer -ADD COLUMN device_type VARCHAR(50) NOT NULL DEFAULT '', -ADD COLUMN device VARCHAR(100) NOT NULL DEFAULT '', -ADD COLUMN os VARCHAR(100) NOT NULL DEFAULT '', -ADD COLUMN browser VARCHAR(100) NOT NULL DEFAULT '', -ADD COLUMN ip VARCHAR(50) NOT NULL DEFAULT ''; \ No newline at end of file diff --git a/schema/000007_init.down.sql b/schema/000007_init.down.sql deleted file mode 100644 index 374c55d..0000000 --- a/schema/000007_init.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE answer -DROP COLUMN start; \ No newline at end of file diff --git a/schema/000007_init.up.sql b/schema/000007_init.up.sql deleted file mode 100644 index 6b41425..0000000 --- a/schema/000007_init.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE answer -ADD COLUMN start BOOLEAN NOT NULL DEFAULT FALSE; \ No newline at end of file diff --git a/schema/000008_init.down.sql b/schema/000008_init.down.sql deleted file mode 100644 index f2dbd88..0000000 --- a/schema/000008_init.down.sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE answer -ALTER COLUMN device TYPE VARCHAR(100), -ALTER COLUMN os TYPE VARCHAR(100), -ALTER COLUMN browser TYPE VARCHAR(100); diff --git a/schema/000008_init.up.sql b/schema/000008_init.up.sql deleted file mode 100644 index fcba321..0000000 --- a/schema/000008_init.up.sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE answer -ALTER COLUMN device TYPE VARCHAR(1024), -ALTER COLUMN os TYPE VARCHAR(1024), -ALTER COLUMN browser TYPE VARCHAR(1024); diff --git a/schema/000009_init.down.sql b/schema/000009_init.down.sql deleted file mode 100644 index 342ea06..0000000 --- a/schema/000009_init.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE quiz -ALTER COLUMN name TYPE VARCHAR(280); diff --git a/schema/000009_init.up.sql b/schema/000009_init.up.sql deleted file mode 100644 index 0c3038e..0000000 --- a/schema/000009_init.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE quiz -ALTER COLUMN name TYPE VARCHAR(1024); diff --git a/service/service.go b/service/service.go deleted file mode 100644 index e9fb2f2..0000000 --- a/service/service.go +++ /dev/null @@ -1,101 +0,0 @@ -package service - -import ( - "github.com/go-redis/redis/v8" - "github.com/gofiber/fiber/v2" - "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/brokers" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/clients/auth" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/clients/telegram" -) - -// Service is an entity for http requests handling -type Service struct { - dal *dal.DAL - authClient *auth.AuthClient - producer *brokers.Producer - serviceName string - chDAL *dal.ClickHouseDAL - telegramClient *telegram.TelegramClient - redisClient *redis.Client - s3Prefix string -} - -type Deps struct { - Dal *dal.DAL - AuthClient *auth.AuthClient - Producer *brokers.Producer - ServiceName string - ChDAL *dal.ClickHouseDAL - TelegramClient *telegram.TelegramClient - RedisClient *redis.Client - S3Prefix string -} - -func New(deps Deps) *Service { - return &Service{ - dal: deps.Dal, - authClient: deps.AuthClient, - producer: deps.Producer, - serviceName: deps.ServiceName, - chDAL: deps.ChDAL, - telegramClient: deps.TelegramClient, - redisClient: deps.RedisClient, - s3Prefix: deps.S3Prefix, - } -} - -// Register is a function for add handlers of service to external multiplexer -func (s *Service) Register(app *fiber.App) { - // quiz manipulating handlers - app.Post("/quiz/create", s.CreateQuiz) - app.Post("/quiz/getList", s.GetQuizList) - app.Patch("/quiz/edit", s.UpdateQuiz) - app.Post("/quiz/copy", s.CopyQuiz) - app.Post("/quiz/history", s.GetQuizHistory) - app.Delete("/quiz/delete", s.DeleteQuiz) - app.Patch("/quiz/archive", s.ArchiveQuiz) - app.Post("/quiz/move", s.QuizMove) - app.Post("/quiz/template", s.TemplateCopy) - - // question manipulating handlers - app.Post("/question/create", s.CreateQuestion) - app.Post("/question/getList", s.GetQuestionList) - app.Patch("/question/edit", s.UpdateQuestion) - app.Post("/question/copy", s.CopyQuestion) - app.Post("/question/history", s.GetQuestionHistory) - app.Delete("/question/delete", s.DeleteQuestion) - - // account handlers - app.Get("/account/get", s.getCurrentAccount) - app.Post("/account/create", s.createAccount) - app.Delete("/account/delete", s.deleteAccount) - app.Get("/accounts", s.getAccounts) - app.Get("/privilege/:userId", s.getPrivilegeByUserID) - app.Delete("/account/:userId", s.deleteAccountByUserID) - app.Post("/account/manualdone", s.ManualDone) - app.Post("/account/leadtarget", s.PostLeadTarget) - app.Delete("/account/leadtarget/:id", s.DeleteLeadTarget) - app.Get("/account/leadtarget/:quizID", s.GetLeadTarget) - app.Put("/account/leadtarget", s.UpdateLeadTarget) - - // result handlers - app.Post("/results/getResults/:quizId", s.GetResultsByQuizID) - app.Delete("/results/delete/:resultId", s.DelResultByID) - app.Patch("/result/seen", s.SetStatus) - app.Post("/results/:quizID/export", s.ExportResultsToCSV) - app.Get("/result/:resultID", s.GetResultAnswers) - - // statistics handlers - app.Post("/statistic/:quizID/devices", s.GetDeviceStatistics) - app.Post("/statistic/:quizID/general", s.GetGeneralStatistics) - app.Post("/statistic/:quizID/questions", s.GetQuestionsStatistics) - app.Post("/statistic", s.AllServiceStatistics) - app.Get("/statistics/:quizID/pipelines", s.GetPipelinesStatistics) - - //telegram handlers - app.Get("/telegram/pool", s.GetPoolTgAccounts) - app.Post("/telegram/create", s.AddingTgAccount) - app.Delete("/telegram/:id", s.DeleteTgAccountByID) - app.Post("/telegram/setCode", s.SettingTgCode) -} diff --git a/tests/mailNotify_test.go b/tests/mailNotify_test.go index 13483a5..2c76b14 100644 --- a/tests/mailNotify_test.go +++ b/tests/mailNotify_test.go @@ -5,8 +5,8 @@ import ( "github.com/pioz/faker" "go.uber.org/zap" "log" - "penahub.gitlab.yandexcloud.net/backend/quiz/core/brokers" "penahub.gitlab.yandexcloud.net/backend/quiz/core/initialize" + "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/brokers" "testing" "time" )