From ee873584417494e75627d1a1bc42401ab7d05a2c Mon Sep 17 00:00:00 2001 From: Pavel Date: Fri, 1 Nov 2024 15:11:17 +0300 Subject: [PATCH] fix layout --- app/app.go | 220 ------------------ cmd/main.go | 35 +++ go.mod | 6 +- go.sum | 45 +--- internal/app/app.go | 163 +++++++++++++ {app => internal/app}/logrecords.go | 0 .../http_controllers/common/common.go | 80 +++---- .../http_controllers/common/route.go | 13 ++ {dal => internal/dal}/dal.go | 4 +- internal/initialize/config.go | 39 ++++ internal/initialize/controllers.go | 40 ++++ internal/initialize/dals.go | 30 +++ internal/initialize/minio.go | 17 ++ internal/initialize/redis.go | 21 ++ {models => internal/models}/hlog_events.go | 0 {savewc => internal/savewc}/for_client.go | 0 {savewc => internal/savewc}/for_respondent.go | 0 internal/server/http/http_server.go | 73 ++++++ main.go | 11 - pkg/closer/closer.go | 37 +++ 20 files changed, 515 insertions(+), 319 deletions(-) delete mode 100644 app/app.go create mode 100644 cmd/main.go create mode 100644 internal/app/app.go rename {app => internal/app}/logrecords.go (100%) rename service/service.go => internal/controllers/http_controllers/common/common.go (90%) create mode 100644 internal/controllers/http_controllers/common/route.go rename {dal => internal/dal}/dal.go (72%) create mode 100644 internal/initialize/config.go create mode 100644 internal/initialize/controllers.go create mode 100644 internal/initialize/dals.go create mode 100644 internal/initialize/minio.go create mode 100644 internal/initialize/redis.go rename {models => internal/models}/hlog_events.go (100%) rename {savewc => internal/savewc}/for_client.go (100%) rename {savewc => internal/savewc}/for_respondent.go (100%) create mode 100644 internal/server/http/http_server.go delete mode 100644 main.go create mode 100644 pkg/closer/closer.go diff --git a/app/app.go b/app/app.go deleted file mode 100644 index 9a658ea..0000000 --- a/app/app.go +++ /dev/null @@ -1,220 +0,0 @@ -package app - -import ( - "context" - "errors" - "fmt" - "github.com/go-redis/redis/v8" - "github.com/gofiber/fiber/v2" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" - "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" - dalBS "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/dal" - "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/models" - "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/savewc" - "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/service" - "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/common.git/utils" - "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"` - MinioEP string `env:"MINIO_EP" default:"localhost:3002"` - MinioAK string `env:"MINIO_AK" default:"minio"` - MinioSK string `env:"MINIO_SK" default:"miniostorage"` - NumberPort string `env:"PORT" default:"1490"` - CrtFile string `env:"CRT" default:"server.crt"` - KeyFile string `env:"KEY" default:"server.key"` - PostgresCredentials string `env:"PG_CRED" default:"host=localhost port=5432 user=squiz password=Redalert2 dbname=squiz sslmode=disable"` - RedisHost string `env:"REDIS_HOST"` - RedisPassword string `env:"REDIS_PASSWORD"` - RedisDB uint64 `env:"REDIS_DB"` - RedirectURL string `env:"REDIRECT_URL" default:"https://squiz.pena.digital"` - PubKey string `env:"PUBLIC_KEY"` - PrivKey string `env:"PRIVATE_KEY"` - TrashLogHost string `env:"TRASH_LOG_HOST" default:"localhost:7113"` - ModuleLogger string `env:"MODULE_LOGGER" default:"answerer-local"` -} - -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{}) - - // Initialize minio client object. - minioClient, err := minio.New(options.MinioEP, &minio.Options{ - Creds: credentials.NewStaticV4(options.MinioAK, options.MinioSK, ""), - Secure: options.IsProd, - }) - if err != nil { - fmt.Println("MINIOERR", options.MinioEP, err) - return nil, err - } - pgdal, err := dal.New(ctx, options.PostgresCredentials, minioClient) - if err != nil { - return nil, err - } - - zapLogger.Info("config", zap.Any("options", options)) - //init redis - redisClient := redis.NewClient(&redis.Options{ - Addr: options.RedisHost, - Password: options.RedisPassword, - DB: int(options.RedisDB), - }) - - encrypt := utils.NewEncrypt(options.PubKey, options.PrivKey) - - workerSendClientCh := make(chan model.Answer, 50) - workerRespondentCh := make(chan []model.Answer, 50) - blobstore, err := dalBS.New(ctx, minioClient) - if err != nil { - return nil, err - } - //svc := service.New(blobstore, pgdal, workerRespondentCh, workerSendClientCh) - svc := service.New(service.ServiceDeps{ - Store: blobstore, - Dal: pgdal, - WorkerSendClientCh: workerSendClientCh, - WorkerRespondentCh: workerRespondentCh, - Encrypt: encrypt, - RedirectURl: options.RedirectURL, - }) - - saveRespWcData := savewc.DepsForResp{ - WorkerRespondentCh: workerRespondentCh, - Redis: redisClient, - } - - saveClientWcData := savewc.DepsForClient{ - WorkerSendClientCh: workerSendClientCh, - Redis: redisClient, - } - - saveRespWorker := savewc.NewSaveRespWorker(saveRespWcData, errChan, loggerHlog) - - saveClientWorker := savewc.NewSaveClientWorker(saveClientWcData, errChan, loggerHlog) - - go saveRespWorker.Start(ctx) - go saveClientWorker.Start(ctx) - - app := fiber.New(fiber.Config{BodyLimit: 70 * 1024 * 1024}) - app.Use(middleware.AnswererChain()) - app.Use(log_mw.ContextLogger(loggerHlog)) - app.Get("/liveness", healthchecks.Liveness) - app.Get("/readiness", healthchecks.Readiness(&workerErr)) //todo parametrized readiness. should discuss ready reason - app = svc.Register(app) - - fmt.Println("SERVERSTART", fmt.Sprintf(":%s", options.NumberPort)) - - loggerHlog.Emit(InfoSvcReady{}) - - go func() { - defer func() { - //if pgdal != nil { - // pgdal.CloseAnswerer() - //} - err := app.Shutdown() - if err != nil { - 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..32524ac --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,35 @@ +package main + +import ( + "context" + "go.uber.org/zap" + "log" + "os" + "os/signal" + "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/internal/app" + "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/internal/initialize" + "syscall" +) + +var ( + commit string = os.Getenv("COMMIT") + buildTime string = os.Getenv("BUILD_TIME") + version string = os.Getenv("VERSION") +) + +func main() { + config, err := initialize.LoadConfig() + if err != nil { + log.Fatal("Failed to load config", zap.Error(err)) + } + + ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer stop() + + if err = app.New(ctx, *config, app.Build{ + Commit: commit, + Version: version, + }); err != nil { + log.Fatal("App exited with error", zap.Error(err)) + } +} diff --git a/go.mod b/go.mod index 07200d5..b2314e2 100644 --- a/go.mod +++ b/go.mod @@ -5,16 +5,16 @@ go 1.22.0 toolchain go1.22.2 require ( + github.com/caarlos0/env/v8 v8.0.0 github.com/go-redis/redis/v8 v8.11.5 github.com/gofiber/fiber/v2 v2.52.4 + github.com/joho/godotenv v1.5.1 github.com/minio/minio-go/v7 v7.0.70 github.com/rs/xid v1.5.0 - github.com/skeris/appInit v1.0.2 github.com/themakers/hlog v0.0.0-20191205140925-235e0e4baddf go.uber.org/zap v1.27.0 penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240607142502-8257e6c4aa5a - penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240919124706-53cacd30903a - penahub.gitlab.yandexcloud.net/devops/linters/golang.git v0.0.0-20240829220549-d35409b619a3 + penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20241025130405-8ab347d96f1f penahub.gitlab.yandexcloud.net/external/trashlog v0.1.5 ) diff --git a/go.sum b/go.sum index b10c39f..6f557f1 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,11 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0= github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= 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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= @@ -33,11 +34,11 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -74,7 +75,6 @@ github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pioz/faker v1.7.2 h1:fterUcYCbMJ4TgFN8DSqhlwkgbSn9DfsQM1cadbPPw0= github.com/pioz/faker v1.7.2/go.mod h1:xSpay5w/oz1a6+ww0M3vfpe40pSIykeUPeWEc3TvVlc= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -82,14 +82,12 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/skeris/appInit v1.0.2 h1:Hr4KbXYd6kolTVq4cXGqDpgnpmaauiOiKizA1+Ep4KQ= github.com/skeris/appInit v1.0.2/go.mod h1:4ElEeXWVGzU3dlYq/eMWJ/U5hd+LKisc1z3+ySh1XmY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE= @@ -106,77 +104,46 @@ github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVS github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo= google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= 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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tucnak/telebot.v2 v2.5.0 h1:i+NynLo443Vp+Zn3Gv9JBjh3Z/PaiKAQwcnhNI7y6Po= gopkg.in/tucnak/telebot.v2 v2.5.0/go.mod h1:BgaIIx50PSRS9pG59JH+geT82cfvoJU/IaI5TJdN3v8= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240607142502-8257e6c4aa5a h1:dGW8ErUVdwGJBq6uc5AHYn6Yt10CDNkMIpV+yrDDTLs= penahub.gitlab.yandexcloud.net/backend/penahub_common v0.0.0-20240607142502-8257e6c4aa5a/go.mod h1:+bPxq2wfW5S1gd+83vZYmHm33AE7nEBfznWS8AM1TKE= -penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240608210553-4b0f100655ae h1:rwSxoF9nrR+1X+wSVVuCgirbyzLPDDefRPxVwshC1ss= -penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240608210553-4b0f100655ae/go.mod h1:n66zm88Dh12+idyfqh0vU5nd9BZYxM6Pv0XYnmy0398= -penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240918162234-cd187815251c h1:dSjFYdQq04yhDjoZ22xZAOZA67dUxB4Usz57QIUKSx8= -penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240918162234-cd187815251c/go.mod h1:uOuosXduBzd2WbLH6TDZO7ME7ZextulA662oZ6OsoB0= -penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240919124706-53cacd30903a h1:loD4hoCkx1ZTqw0mg/Br4RT2GI1vT2WvEh0I/j+DPo8= -penahub.gitlab.yandexcloud.net/backend/quiz/common.git v0.0.0-20240919124706-53cacd30903a/go.mod h1:uOuosXduBzd2WbLH6TDZO7ME7ZextulA662oZ6OsoB0= -penahub.gitlab.yandexcloud.net/devops/linters/golang.git v0.0.0-20240829220549-d35409b619a3 h1:sf6e2mp582L3i/FMDd2q6QuWm1njRXzYpIX0SipsvM4= -penahub.gitlab.yandexcloud.net/devops/linters/golang.git v0.0.0-20240829220549-d35409b619a3/go.mod h1:i7M72RIpkSjcQtHID6KKj9RT/EYZ1rxS6tIPKWa/BSY= +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/external/trashlog v0.1.5 h1:amsK0bkSJxBisk334aFo5ZmVPvN1dBT0Sv5j3V5IsT8= penahub.gitlab.yandexcloud.net/external/trashlog v0.1.5/go.mod h1:J8kQNEP4bL7ZNKHxuT4tfe6a3FHyovpAPkyytN4qllc= diff --git a/internal/app/app.go b/internal/app/app.go new file mode 100644 index 0000000..5aae22f --- /dev/null +++ b/internal/app/app.go @@ -0,0 +1,163 @@ +package app + +import ( + "context" + "errors" + "github.com/gofiber/fiber/v2/log" + "github.com/themakers/hlog" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/internal/initialize" + "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/internal/models" + savewc2 "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/internal/savewc" + "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/internal/server/http" + "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/pkg/closer" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/utils" + "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 New(ctx context.Context, cfg initialize.Config, build Build) error { + var ( + err error + zapLogger *zap.Logger + errChan = make(chan error) + ) + + defer func() { + if r := recover(); r != nil { + log.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() + + // Initialize minio client object. + minioClient, err := initialize.NewMinio(cfg) + if err != nil { + zapLogger.Error("failed to initialize minio client", zap.Error(err)) + return err + } + + dalS, err := initialize.NewDALs(ctx, cfg, minioClient) + if err != nil { + zapLogger.Error("failed to initialize dals", zap.Error(err)) + return err + } + + redisClient, err := initialize.Redis(ctx, cfg) + if err != nil { + zapLogger.Error("failed to initialize redis client", zap.Error(err)) + return err + } + + encrypt := utils.NewEncrypt(cfg.PubKey, cfg.PrivKey) + + workerSendClientCh := make(chan model.Answer, 50) + workerRespondentCh := make(chan []model.Answer, 50) + + saveRespWcData := savewc2.DepsForResp{ + WorkerRespondentCh: workerRespondentCh, + Redis: redisClient, + } + saveClientWcData := savewc2.DepsForClient{ + WorkerSendClientCh: workerSendClientCh, + Redis: redisClient, + } + + saveRespWorker := savewc2.NewSaveRespWorker(saveRespWcData, errChan, loggerHlog) + saveClientWorker := savewc2.NewSaveClientWorker(saveClientWcData, errChan, loggerHlog) + + go saveRespWorker.Start(ctx) + go saveClientWorker.Start(ctx) + + controllers := initialize.NewControllers(initialize.ControllerDeps{ + DALs: dalS, + Config: cfg, + RedisClient: redisClient, + WorkerSendClientCh: workerSendClientCh, + WorkerRespondentCh: workerRespondentCh, + Encrypt: encrypt, + }) + + srv := http.NewServer(http.ServerConfig{ + Logger: zapLogger, + Controllers: []http.Controller{controllers.HttpControllers.Common}, + Hlogger: loggerHlog, + }) + + go func() { + if err := srv.Start(cfg.HttpHost + ":" + cfg.NumberPort); err != nil { + zapLogger.Error("HTTP server startup error", zap.Error(err)) + cancel() + } + }() + + srv.ListRoutes() + loggerHlog.Emit(InfoSvcReady{}) + + shutdownGroup.Add(closer.CloserFunc(srv.Shutdown)) + shutdownGroup.Add(closer.CloserFunc(dalS.PgDAL.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) { + zapLogger.Error("Shutdown timed out", zap.Error(err)) + } else { + zapLogger.Error("Failed to shutdown services gracefully", zap.Error(err)) + } + return err + } + + zapLogger.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/service/service.go b/internal/controllers/http_controllers/common/common.go similarity index 90% rename from service/service.go rename to internal/controllers/http_controllers/common/common.go index c6c6b2a..4fe3c3e 100644 --- a/service/service.go +++ b/internal/controllers/http_controllers/common/common.go @@ -1,13 +1,14 @@ -package service +package common import ( "encoding/json" "fmt" "github.com/gofiber/fiber/v2" + "github.com/rs/xid" "net/url" "penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw" - "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/dal" - "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/models" + "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/internal/dal" + "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/internal/models" quizdal "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" @@ -16,16 +17,18 @@ import ( "strings" "sync" "time" - - "github.com/rs/xid" ) -const ( - quizIdCookie = "qud" - fingerprintCookie = "fp" -) +type Deps struct { + Store *dal.Storer + Dal *quizdal.DAL + WorkerRespondentCh chan<- []model.Answer + WorkerSendClientCh chan<- model.Answer + Encrypt *utils.Encrypt + RedirectURl string +} -type Service struct { +type Common struct { store *dal.Storer dal *quizdal.DAL batch []model.Answer @@ -36,17 +39,8 @@ type Service struct { redirectURl string } -type ServiceDeps struct { - Store *dal.Storer - Dal *quizdal.DAL - WorkerRespondentCh chan<- []model.Answer - WorkerSendClientCh chan<- model.Answer - Encrypt *utils.Encrypt - RedirectURl string -} - -func New(deps ServiceDeps) *Service { - return &Service{ +func NewCommonController(deps Deps) *Common { + return &Common{ store: deps.Store, dal: deps.Dal, m: sync.Mutex{}, @@ -58,12 +52,10 @@ func New(deps ServiceDeps) *Service { } } -func (s *Service) Register(app *fiber.App) *fiber.App { - app.Post("/answer", s.PutAnswersOnePiece) - app.Post("/settings", s.GetQuizData) - app.Get("/logo", s.MiniPart) - return app -} +const ( + quizIdCookie = "qud" + fingerprintCookie = "fp" +) // GetQuizDataReq request data for get data for user type GetQuizDataReq struct { @@ -105,7 +97,7 @@ type ShavedQuestion struct { } // GetQuizData handler for obtaining data for quiz front rendering -func (s *Service) GetQuizData(c *fiber.Ctx) error { +func (receiver *Common) GetQuizData(c *fiber.Ctx) error { hlogger := log_mw.ExtractLogger(c) var req GetQuizDataReq if err := c.BodyParser(&req); err != nil { @@ -120,7 +112,7 @@ func (s *Service) GetQuizData(c *fiber.Ctx) error { return c.Status(fiber.StatusLengthRequired).SendString("no data requested") } - quiz, err := s.dal.QuizRepo.GetQuizByQid(c.Context(), req.QuizId) + quiz, err := receiver.dal.QuizRepo.GetQuizByQid(c.Context(), req.QuizId) if err != nil { return c.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -146,7 +138,7 @@ func (s *Service) GetQuizData(c *fiber.Ctx) error { return c.Status(fiber.StatusGone).SendString("quiz timeouted") } - account, err := s.dal.AccountRepo.GetAccountByID(c.Context(), quiz.AccountId) + account, err := receiver.dal.AccountRepo.GetAccountByID(c.Context(), quiz.AccountId) if err != nil { return c.Status(fiber.StatusInternalServerError).SendString("can`t get account by quiz.AccountId") @@ -162,7 +154,7 @@ func (s *Service) GetQuizData(c *fiber.Ctx) error { } } - questions, cnt, err := s.dal.QuestionRepo.GetQuestionList( + questions, cnt, err := receiver.dal.QuestionRepo.GetQuestionList( c.Context(), req.Limit, req.Page*req.Limit, @@ -223,7 +215,7 @@ func (s *Service) GetQuizData(c *fiber.Ctx) error { return c.Status(fiber.StatusUnauthorized).SendString("no session in cookie") } - answers, errs := s.dal.AnswerRepo.CreateAnswers(c.Context(), []model.Answer{{ + answers, errs := receiver.dal.AnswerRepo.CreateAnswers(c.Context(), []model.Answer{{ Content: "start", QuestionId: questions[0].Id, QuizId: quiz.Id, @@ -299,7 +291,7 @@ type PutAnswersResponse struct { // todo не отдавать ответы с типом start но сохранять брать из контента ответа // поле бул также в GetQuizdata не отдавать его, но по запросам идет GetQuizdata получаем данные квиза // аотом отправляем ответы в PutAnswers и сохраяняем их подумать как делать -func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { +func (receiver *Common) PutAnswersOnePiece(c *fiber.Ctx) error { cs, ok := c.Context().Value(middleware.ContextKey(middleware.SessionKey)).(string) if !ok { return c.Status(fiber.StatusUnauthorized).SendString("no session in cookie") @@ -316,10 +308,10 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { if len(answersStr) == 0 { return c.Status(fiber.StatusFailedDependency).SendString("no answers provided") } - + versionStr, ok := form.Value["version"] var version int64 - + if ok && len(versionStr) > 0 { version, err = strconv.ParseInt(versionStr[0], 10, 32) if err != nil { @@ -353,7 +345,7 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { fp = cfp } - quiz, err := s.dal.QuizRepo.GetQuizByQid(c.Context(), quizID[0]) + quiz, err := receiver.dal.QuizRepo.GetQuizByQid(c.Context(), quizID[0]) if err != nil { return c.Status(fiber.StatusInternalServerError).SendString("can not get quiz") } @@ -386,7 +378,7 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { } fname := fmt.Sprintf("%s.%s", xid.New().String(), filetail) - if err := s.store.PutAnswer(c.Context(), r, quizID[0], fname, ans.QuestionId, fileparts[0].Size); err != nil { + if err := receiver.store.PutAnswer(c.Context(), r, quizID[0], fname, ans.QuestionId, fileparts[0].Size); err != nil { return c.Status(fiber.StatusInternalServerError).SendString("can not upload file answers") } ans.Content = fname @@ -419,7 +411,7 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { CtxSession: cs, }) - s.workerSendClientCh <- ans + receiver.workerSendClientCh <- ans trueRes = append(trueRes, ans) } } @@ -431,10 +423,10 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { } if quizConfig.Mailing.When == "email" && len(trueRes) > 0 { - s.workerRespondentCh <- trueRes + receiver.workerRespondentCh <- trueRes } - stored, ers := s.dal.AnswerRepo.CreateAnswers(c.Context(), answers, cs, fp, quiz.Id) + stored, ers := receiver.dal.AnswerRepo.CreateAnswers(c.Context(), answers, cs, fp, quiz.Id) if len(ers) != 0 { for _, err := range ers { if strings.Contains(err.Error(), "duplicate key value") { @@ -484,7 +476,7 @@ func (s *Service) PutAnswersOnePiece(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(response) } -func (s *Service) MiniPart(ctx *fiber.Ctx) error { +func (receiver *Common) MiniPart(ctx *fiber.Ctx) error { qid := ctx.Query("q") if qid == "" { return ctx.Status(fiber.StatusBadRequest).SendString("qid is nil") @@ -494,12 +486,12 @@ func (s *Service) MiniPart(ctx *fiber.Ctx) error { Value: qid, }) - userID, err := s.dal.AccountRepo.GetQidOwner(ctx.Context(), qid) + userID, err := receiver.dal.AccountRepo.GetQidOwner(ctx.Context(), qid) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } - shifr, err := s.encrypt.EncryptStr(userID) + shifr, err := receiver.encrypt.EncryptStr(userID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } @@ -509,5 +501,5 @@ func (s *Service) MiniPart(ctx *fiber.Ctx) error { Value: url.QueryEscape(string(shifr)), }) - return ctx.Redirect(s.redirectURl, fiber.StatusFound) + return ctx.Redirect(receiver.redirectURl, fiber.StatusFound) } diff --git a/internal/controllers/http_controllers/common/route.go b/internal/controllers/http_controllers/common/route.go new file mode 100644 index 0000000..0e16828 --- /dev/null +++ b/internal/controllers/http_controllers/common/route.go @@ -0,0 +1,13 @@ +package common + +import "github.com/gofiber/fiber/v2" + +func (receiver *Common) Register(router fiber.Router) { + router.Post("/answer", receiver.PutAnswersOnePiece) + router.Post("/settings", receiver.GetQuizData) + router.Get("/logo", receiver.MiniPart) +} + +func (receiver *Common) Name() string { + return "" +} diff --git a/dal/dal.go b/internal/dal/dal.go similarity index 72% rename from dal/dal.go rename to internal/dal/dal.go index 29a79bf..b2a6c89 100644 --- a/dal/dal.go +++ b/internal/dal/dal.go @@ -8,7 +8,7 @@ import ( ) const ( - bucket = "3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b" + bucket = "3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b" bucketAnswers = "squizanswer" ) @@ -23,7 +23,7 @@ func New(ctx context.Context, minioClient *minio.Client) (*Storer, error) { } func (s *Storer) PutAnswer(ctx context.Context, file io.Reader, quizId, name string, question uint64, size int64) error { - if _, err := s.client.PutObject(ctx, bucket, bucketAnswers + "/" +fmt.Sprintf("%s/%d/%s", quizId, question, name), file, size, + if _, err := s.client.PutObject(ctx, bucket, bucketAnswers+"/"+fmt.Sprintf("%s/%d/%s", quizId, question, name), file, size, minio.PutObjectOptions{}); err != nil { return err } diff --git a/internal/initialize/config.go b/internal/initialize/config.go new file mode 100644 index 0000000..5b9bd62 --- /dev/null +++ b/internal/initialize/config.go @@ -0,0 +1,39 @@ +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"` + MinioEP string `env:"MINIO_EP" envDefault:"localhost:3002"` + MinioAK string `env:"MINIO_AK" envDefault:"minio"` + MinioSK string `env:"MINIO_SK" envDefault:"miniostorage"` + NumberPort string `env:"PORT" envDefault:"1490"` + 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=5432 user=squiz password=Redalert2 dbname=squiz sslmode=disable"` + RedisHost string `env:"REDIS_HOST"` + RedisPassword string `env:"REDIS_PASSWORD"` + RedisDB uint64 `env:"REDIS_DB"` + RedirectURL string `env:"REDIRECT_URL" envDefault:"https://squiz.pena.digital"` + PubKey string `env:"PUBLIC_KEY"` + PrivKey string `env:"PRIVATE_KEY"` + TrashLogHost string `env:"TRASH_LOG_HOST" envDefault:"localhost:7113"` + ModuleLogger string `env:"MODULE_LOGGER" envDefault:"answerer-local"` +} + +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..474d10e --- /dev/null +++ b/internal/initialize/controllers.go @@ -0,0 +1,40 @@ +package initialize + +import ( + "github.com/go-redis/redis/v8" + "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/internal/controllers/http_controllers/common" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/utils" +) + +type ControllerDeps struct { + DALs *DALs + Config Config + RedisClient *redis.Client + WorkerRespondentCh chan<- []model.Answer + WorkerSendClientCh chan<- model.Answer + Encrypt *utils.Encrypt +} + +type Controller struct { + HttpControllers HttpControllers +} + +type HttpControllers struct { + Common *common.Common +} + +func NewControllers(deps ControllerDeps) *Controller { + return &Controller{ + HttpControllers: HttpControllers{ + Common: common.NewCommonController(common.Deps{ + Store: deps.DALs.BlobStore, + Dal: deps.DALs.PgDAL, + WorkerRespondentCh: deps.WorkerRespondentCh, + WorkerSendClientCh: deps.WorkerSendClientCh, + Encrypt: deps.Encrypt, + RedirectURl: deps.Config.RedirectURL, + }), + }, + } +} diff --git a/internal/initialize/dals.go b/internal/initialize/dals.go new file mode 100644 index 0000000..f6f342c --- /dev/null +++ b/internal/initialize/dals.go @@ -0,0 +1,30 @@ +package initialize + +import ( + "context" + "github.com/minio/minio-go/v7" + dalBS "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/internal/dal" + "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" +) + +type DALs struct { + PgDAL *dal.DAL + BlobStore *dalBS.Storer +} + +func NewDALs(ctx context.Context, cfg Config, minioClient *minio.Client) (*DALs, error) { + pgDAL, err := dal.New(ctx, cfg.PostgresCredentials, minioClient) + if err != nil { + return nil, err + } + + blobStore, err := dalBS.New(ctx, minioClient) + if err != nil { + return nil, err + } + + return &DALs{ + PgDAL: pgDAL, + BlobStore: blobStore, + }, nil +} diff --git a/internal/initialize/minio.go b/internal/initialize/minio.go new file mode 100644 index 0000000..49cea81 --- /dev/null +++ b/internal/initialize/minio.go @@ -0,0 +1,17 @@ +package initialize + +import ( + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" +) + +func NewMinio(cfg Config) (*minio.Client, error) { + minioClient, err := minio.New(cfg.MinioEP, &minio.Options{ + Creds: credentials.NewStaticV4(cfg.MinioAK, cfg.MinioSK, ""), + Secure: cfg.IsProd, + }) + if err != nil { + return nil, err + } + return minioClient, nil +} 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/savewc/for_client.go b/internal/savewc/for_client.go similarity index 100% rename from savewc/for_client.go rename to internal/savewc/for_client.go diff --git a/savewc/for_respondent.go b/internal/savewc/for_respondent.go similarity index 100% rename from savewc/for_respondent.go rename to internal/savewc/for_respondent.go diff --git a/internal/server/http/http_server.go b/internal/server/http/http_server.go new file mode 100644 index 0000000..c1fa1d0 --- /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(fiber.Config{BodyLimit: 70 * 1024 * 1024}) + app.Use(middleware.AnswererChain()) + 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/main.go b/main.go deleted file mode 100644 index cae8b60..0000000 --- a/main.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import ( - "github.com/skeris/appInit" - "penahub.gitlab.yandexcloud.net/backend/quiz/answerer/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 +}