From 36eec4a2c0c69a881ec85f98c56f72e9034d07b0 Mon Sep 17 00:00:00 2001 From: skeris Date: Thu, 17 Apr 2025 15:44:25 +0300 Subject: [PATCH] fix layout --- app/app.go | 156 ------------------ cmd/main.go | 25 +++ go.mod | 2 + go.sum | 12 +- internal/app/app.go | 96 +++++++++++ {app => internal/app}/logrecords.go | 0 .../http_controllers/quiz_files/quiz_files.go | 53 +++--- .../http_controllers/quiz_files/route.go | 12 ++ internal/initialize/config.go | 30 ++++ internal/initialize/controllers.go | 25 +++ internal/initialize/dals.go | 30 ++++ internal/initialize/minio.go | 17 ++ dal/dal.go => internal/repository/s3.go | 26 +-- internal/server/http/http_server.go | 69 ++++++++ main.go | 11 -- pkg/closer/closer.go | 37 +++++ 16 files changed, 387 insertions(+), 214 deletions(-) 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/quiz_files/quiz_files.go (80%) create mode 100644 internal/controllers/http_controllers/quiz_files/route.go 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 rename dal/dal.go => internal/repository/s3.go (62%) 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 index c2afc20..e69de29 100644 --- a/app/app.go +++ b/app/app.go @@ -1,156 +0,0 @@ -package app - -import ( - "context" - "errors" - "fmt" - "gitea.pena/SQuiz/common/dal" - "gitea.pena/SQuiz/common/healthchecks" - "gitea.pena/SQuiz/common/middleware" - dalBS "gitea.pena/SQuiz/storer/dal" - "gitea.pena/SQuiz/storer/service" - "github.com/gofiber/fiber/v2" - - "gitea.pena/PenaSide/hlog" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" - "github.com/skeris/appInit" - "gitea.pena/PenaSide/hlog" - "go.uber.org/zap" -) - -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"` - S3Endpoint string `env:"S3_ENDPOINT" envDefault:"localhost:3002"` - S3AccessKey string `env:"S3_ACCESS_KEY" envDefault:"minio"` - S3SecretKey string `env:"S3_SECRET_KEY" envDefault:"miniostorage"` - ClientHttpURL string `env:"CLIENT_HTTP_URL" envDefault:"0.0.0.0:1489"` - CrtFile string `env:"CRT" default:"server.crt"` - KeyFile string `env:"KEY" default:"server.key"` - PostgresURL string `env:"POSTGRES_URL" envDefault:"host=localhost port=5432 user=squiz password=Redalert2 dbname=squiz sslmode=disable"` -} - -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), - ) - - logger := hlog.New(zapLogger) - logger.Emit(InfoSvcStarted{}) - - pgdal, err := dal.New(ctx, options.PostgresURL, nil) - if err != nil { - return nil, err - } - - // Initialize minio client object. - minioClient, err := minio.New(options.S3Endpoint, &minio.Options{ - Creds: credentials.NewStaticV4(options.S3AccessKey, options.S3SecretKey, ""), - Secure: options.IsProd, - }) - if err != nil { - fmt.Println("MINIOERR", options.S3Endpoint, err) - return nil, err - } - - blobstore, err := dalBS.New(ctx, minioClient) - if err != nil { - return nil, err - } - svc := service.New(blobstore, pgdal) - - app := fiber.New(fiber.Config{BodyLimit: 70 * 1024 * 1024}) - app.Use(middleware.JWTAuth()) - app.Get("/liveness", healthchecks.Liveness) - app.Get("/readiness", healthchecks.Readiness(&workerErr)) //todo parametrized readiness. should discuss ready reason - svc.Register(app) - - logger.Emit(InfoSvcReady{}) - - go func() { - defer func() { - //if pgdal != nil { - // pgdal.CloseStorer() - //} - err := app.Shutdown() - if err != nil { - logger.Emit(InfoSvcShutdown{Signal: err.Error()}) - } - }() - - if options.IsProd { - if err := app.ListenTLS(options.ClientHttpURL, options.CrtFile, options.KeyFile); err != nil { - logger.Emit(ErrorCanNotServe{ - Err: err, - }) - errChan <- err - } - } else { - if err := app.Listen(options.ClientHttpURL); err != nil { - logger.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..bab51f5 --- /dev/null +++ b/cmd/main.go @@ -0,0 +1,25 @@ +package main + +import ( + "context" + "gitea.pena/SQuiz/storer/internal/app" + "gitea.pena/SQuiz/storer/internal/initialize" + "go.uber.org/zap" + "log" + "os/signal" + "syscall" +) + +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.Run(ctx, *config); err != nil { + log.Fatal("App exited with error", zap.Error(err)) + } +} diff --git a/go.mod b/go.mod index 966c4a9..2bab72e 100644 --- a/go.mod +++ b/go.mod @@ -18,6 +18,7 @@ require ( gitea.pena/PenaSide/common v0.0.0-20250103085335-91ea31fee517 // indirect 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/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/go-ini/ini v1.67.0 // indirect @@ -25,6 +26,7 @@ require ( github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/joho/godotenv v1.5.1 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/lib/pq v1.10.9 // indirect diff --git a/go.sum b/go.sum index b4f923a..a1e763b 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,12 @@ gitea.pena/PenaSide/common v0.0.0-20250103085335-91ea31fee517 h1:EgBe8VcdPwmxbSz gitea.pena/PenaSide/common v0.0.0-20250103085335-91ea31fee517/go.mod h1:91EuBCgcqgJ6mG36n2pds8sPwwfaJytLWOzY3h2YFKU= gitea.pena/PenaSide/hlog v0.0.0-20241125221102-a54c29c002a9 h1:tBkXWNIt8icmkMMnq8MA421RWkUy4OZh5P7C3q8uCu4= gitea.pena/PenaSide/hlog v0.0.0-20241125221102-a54c29c002a9/go.mod h1:sanhSL8aEsfcq21P+eItYiAnKAre+B67nGJmDfk2cf0= -gitea.pena/SQuiz/common v0.0.0-20250207214652-9994f2d4d43f h1:458FCN98jVkjAqg3yyspgkUdJnKz3BNMiZosrVtPpv8= -gitea.pena/SQuiz/common v0.0.0-20250207214652-9994f2d4d43f/go.mod h1:/YR+uo4RouZshuHPkguk7nAJVKuFt3Z0mTFxUPdlzxQ= +gitea.pena/PenaSide/linters-golang v0.0.0-20241207122018-933207374735 h1:jDVeUhGBTXBibmW5dmtJg2m2+z5z2Rf6J4G0LpjVoJ0= +gitea.pena/PenaSide/linters-golang v0.0.0-20241207122018-933207374735/go.mod h1:gdd+vOT6up9STkEbxa2qESLIMZFjCmRbkcheFQCVgZU= +gitea.pena/SQuiz/common v0.0.0-20250205160239-4ed00d74894b h1:N3DdDWQyTXC0B5mI7OpjxHQuHtt6EJCK+vqRrLqcb1w= +gitea.pena/SQuiz/common v0.0.0-20250205160239-4ed00d74894b/go.mod h1:zCrUwDh0APpztKk6NUqTZv+zhjVbWpGBJiJ5z9dAH0U= +gitea.pena/SQuiz/common v0.0.0-20250221135056-f98c45e04909 h1:iCiqaJ6a7rGESAEUgtVA9IqhVn0oKiwRk7bryTWPV5w= +gitea.pena/SQuiz/common v0.0.0-20250221135056-f98c45e04909/go.mod h1:rQRjqLlLyM71FZcvbM95Nv3ciq44F9DFtUHPZmDK3T8= 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= @@ -11,6 +15,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 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/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -35,6 +41,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 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/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= diff --git a/internal/app/app.go b/internal/app/app.go new file mode 100644 index 0000000..83013be --- /dev/null +++ b/internal/app/app.go @@ -0,0 +1,96 @@ +package app + +import ( + "context" + "errors" + "gitea.pena/SQuiz/storer/internal/initialize" + "gitea.pena/SQuiz/storer/internal/server/http" + "gitea.pena/SQuiz/storer/pkg/closer" + "github.com/gofiber/fiber/v2/log" + "go.uber.org/zap" + "time" +) + +var zapOptions = []zap.Option{ + zap.AddCaller(), + zap.AddCallerSkip(2), + zap.AddStacktrace(zap.ErrorLevel), +} + +func Run(ctx context.Context, cfg initialize.Config) error { + var ( + err error + zapLogger *zap.Logger + ) + + 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 + } + } + + shutdownGroup := closer.NewCloserGroup() + + minioClient, err := initialize.NewMinio(cfg) + if err != nil { + zapLogger.Error("Error initializing minio", zap.Error(err)) + return err + } + + dalS, err := initialize.NewDALs(ctx, cfg, minioClient) + if err != nil { + zapLogger.Error("Error initializing dals", zap.Error(err)) + } + + controllers := initialize.NewControllers(initialize.ControllerDeps{ + DALs: dalS, + }) + + srv := http.NewServer(http.ServerConfig{ + Logger: zapLogger, + Controllers: []http.Controller{controllers.HttpControllers.QuizFiles}, + }) + + go func() { + if err := srv.Start(cfg.ClientHttpURL); err != nil { + zapLogger.Error("HTTP server startup error", zap.Error(err)) + cancel() + } + }() + + srv.ListRoutes() + + 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/quiz_files/quiz_files.go similarity index 80% rename from service/service.go rename to internal/controllers/http_controllers/quiz_files/quiz_files.go index c367487..d94707e 100644 --- a/service/service.go +++ b/internal/controllers/http_controllers/quiz_files/quiz_files.go @@ -1,41 +1,30 @@ -package service +package quiz_files import ( "errors" - "github.com/gofiber/fiber/v2" - "io" - quizdal "gitea.pena/SQuiz/common/dal" + "gitea.pena/SQuiz/common/dal" mw "gitea.pena/SQuiz/common/middleware" - "gitea.pena/SQuiz/storer/dal" + "gitea.pena/SQuiz/storer/internal/repository" + "github.com/gofiber/fiber/v2" + "github.com/rs/xid" + "io" "strconv" "sync" - - "github.com/rs/xid" ) -type Service struct { - store *dal.Storer - dal *quizdal.DAL +type QuizFiles struct { + stDal *repository.S3 + pgDal *dal.DAL } -func New(s *dal.Storer, q *quizdal.DAL) *Service { - return &Service{ - store: s, - dal: q, +func New(stDal *repository.S3, pgDal *dal.DAL) *QuizFiles { + return &QuizFiles{ + stDal: stDal, + pgDal: pgDal, } } -func (s *Service) Register(app *fiber.App) { - app.Put("/quiz/customization", s.UploadCustom) - app.Put("/quiz/putImages", s.UploadImages) -} - -// MB Size constants -const ( - MB = 1 << 20 -) - -func (s *Service) UploadCustom(c *fiber.Ctx) error { +func (r *QuizFiles) UploadCustom(c *fiber.Ctx) error { accountId, ok := mw.GetAccountId(c) if !ok { return nil @@ -57,7 +46,7 @@ func (s *Service) UploadCustom(c *fiber.Ctx) error { return c.Status(fiber.StatusBadRequest).SendString("not valid quiz id provided " + err.Error()) } - quiz, err := s.dal.QuizRepo.GetQuizById(c.Context(), accountId, uint64(squizId)) + quiz, err := r.pgDal.QuizRepo.GetQuizById(c.Context(), accountId, uint64(squizId)) if err != nil { return c.Status(fiber.StatusNotAcceptable).SendString("not exists quiz id provided " + err.Error()) } @@ -82,7 +71,7 @@ func (s *Service) UploadCustom(c *fiber.Ctx) error { } defer file.Close() - if err := s.store.UploadScript(c.Context(), quiz.Qid, file, fileHeader.Size); err != nil { + if err := r.stDal.UploadScript(c.Context(), quiz.Qid, file, fileHeader.Size); err != nil { errm.Lock() defer errm.Unlock() errs = append(errs, errors.New(err.Error()+" => script upload")) @@ -104,7 +93,7 @@ func (s *Service) UploadCustom(c *fiber.Ctx) error { } defer file.Close() - if err := s.store.UploadStyle(c.Context(), quiz.Qid, file, fileHeader.Size); err != nil { + if err := r.stDal.UploadStyle(c.Context(), quiz.Qid, file, fileHeader.Size); err != nil { errm.Lock() defer errm.Unlock() errs = append(errs, errors.New(err.Error()+" => style upload")) @@ -128,7 +117,7 @@ func (s *Service) UploadCustom(c *fiber.Ctx) error { wg.Add(1) go func() { defer wg.Done() - if errorsFontUploading := s.store.UploadFonts(c.Context(), quiz.Qid, files, sizes); err != nil { + if errorsFontUploading := r.stDal.UploadFonts(c.Context(), quiz.Qid, files, sizes); err != nil { errm.Lock() defer errm.Unlock() errs = append(errs, errorsFontUploading...) @@ -144,7 +133,7 @@ func (s *Service) UploadCustom(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) } -func (s *Service) UploadImages(c *fiber.Ctx) error { +func (r *QuizFiles) UploadImages(c *fiber.Ctx) error { accountId, ok := mw.GetAccountId(c) if !ok { return nil @@ -166,7 +155,7 @@ func (s *Service) UploadImages(c *fiber.Ctx) error { return c.Status(fiber.StatusBadRequest).SendString("not valid quiz id provided " + err.Error()) } - quiz, err := s.dal.QuizRepo.GetQuizById(c.Context(), accountId, uint64(squizId)) + quiz, err := r.pgDal.QuizRepo.GetQuizById(c.Context(), accountId, uint64(squizId)) if err != nil { return c.Status(fiber.StatusNotAcceptable).SendString("not exists quiz id provided " + err.Error()) } @@ -189,7 +178,7 @@ func (s *Service) UploadImages(c *fiber.Ctx) error { defer f.Close() } - if err := s.store.UploadImages(c.Context(), quiz.Qid, files, sizes); err != nil { + if err := r.stDal.UploadImages(c.Context(), quiz.Qid, files, sizes); err != nil { errs = append(errs, err...) } } diff --git a/internal/controllers/http_controllers/quiz_files/route.go b/internal/controllers/http_controllers/quiz_files/route.go new file mode 100644 index 0000000..aaf8d96 --- /dev/null +++ b/internal/controllers/http_controllers/quiz_files/route.go @@ -0,0 +1,12 @@ +package quiz_files + +import "github.com/gofiber/fiber/v2" + +func (r *QuizFiles) Register(router fiber.Router) { + router.Put("/customization", r.UploadCustom) + router.Put("/putImages", r.UploadImages) +} + +func (r *QuizFiles) Name() string { + return "quiz" +} diff --git a/internal/initialize/config.go b/internal/initialize/config.go new file mode 100644 index 0000000..3454df1 --- /dev/null +++ b/internal/initialize/config.go @@ -0,0 +1,30 @@ +package initialize + +import ( + "github.com/caarlos0/env/v8" + "github.com/joho/godotenv" + "log" +) + +type Config struct { + LoggerProdMode bool `env:"IS_PROD_LOG" default:"false"` + IsProd bool `env:"IS_PROD" default:"false"` + S3Endpoint string `env:"S3_ENDPOINT" envDefault:"localhost:9001"` + S3AccessKey string `env:"S3_ACCESS_KEY" envDefault:"admin"` + S3SecretKey string `env:"S3_SECRET_KEY" envDefault:"admin123"` + ClientHttpURL string `env:"CLIENT_HTTP_URL" envDefault:"0.0.0.0:1489"` + CrtFile string `env:"CRT" default:"server.crt"` + KeyFile string `env:"KEY" default:"server.key"` + PostgresURL string `env:"POSTGRES_URL" envDefault:"host=localhost port=35432 user=squiz password=Redalert2 dbname=squiz sslmode=disable"` +} + +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..4906203 --- /dev/null +++ b/internal/initialize/controllers.go @@ -0,0 +1,25 @@ +package initialize + +import ( + "gitea.pena/SQuiz/storer/internal/controllers/http_controllers/quiz_files" +) + +type ControllerDeps struct { + DALs *DALs +} + +type Controller struct { + HttpControllers HttpControllers +} + +type HttpControllers struct { + QuizFiles *quiz_files.QuizFiles +} + +func NewControllers(deps ControllerDeps) *Controller { + return &Controller{ + HttpControllers: HttpControllers{ + QuizFiles: quiz_files.New(deps.DALs.StDal, deps.DALs.PgDAL), + }, + } +} diff --git a/internal/initialize/dals.go b/internal/initialize/dals.go new file mode 100644 index 0000000..78e8477 --- /dev/null +++ b/internal/initialize/dals.go @@ -0,0 +1,30 @@ +package initialize + +import ( + "context" + "gitea.pena/SQuiz/common/dal" + "gitea.pena/SQuiz/storer/internal/repository" + "github.com/minio/minio-go/v7" +) + +type DALs struct { + PgDAL *dal.DAL + StDal *repository.S3 +} + +func NewDALs(ctx context.Context, cfg Config, minioClient *minio.Client) (*DALs, error) { + pgDal, err := dal.New(ctx, cfg.PostgresURL, nil) + if err != nil { + return nil, err + } + + stDal, err := repository.New(ctx, minioClient) + if err != nil { + return nil, err + } + + return &DALs{ + PgDAL: pgDal, + StDal: stDal, + }, nil +} diff --git a/internal/initialize/minio.go b/internal/initialize/minio.go new file mode 100644 index 0000000..c5d2a4f --- /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.S3Endpoint, &minio.Options{ + Creds: credentials.NewStaticV4(cfg.S3AccessKey, cfg.S3SecretKey, ""), + Secure: cfg.IsProd, + }) + if err != nil { + return nil, err + } + return minioClient, nil +} diff --git a/dal/dal.go b/internal/repository/s3.go similarity index 62% rename from dal/dal.go rename to internal/repository/s3.go index fd375d3..ff416b5 100644 --- a/dal/dal.go +++ b/internal/repository/s3.go @@ -1,4 +1,4 @@ -package dal +package repository import ( "context" @@ -9,25 +9,25 @@ import ( ) const ( - bucket = "3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b" - folderImages = "squizimages" + bucket = "3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b" + folderImages = "squizimages" bucketFonts = "squizfonts" bucketScripts = "squizscript" bucketStyle = "squizstyle" ) -type Storer struct { +type S3 struct { client *minio.Client } -func New(ctx context.Context, minioClient *minio.Client) (*Storer, error) { +func New(ctx context.Context, minioClient *minio.Client) (*S3, error) { - return &Storer{ + return &S3{ client: minioClient, }, nil } -func (s *Storer) UploadImages(ctx context.Context, quid string, files map[string]io.Reader, sizes map[string]int64) []error { +func (s *S3) UploadImages(ctx context.Context, quid string, files map[string]io.Reader, sizes map[string]int64) []error { var ( wg sync.WaitGroup m sync.Mutex @@ -57,7 +57,7 @@ func (s *Storer) UploadImages(ctx context.Context, quid string, files map[string return errs } -func (s *Storer) UploadFonts(ctx context.Context, quid string, files map[string]io.Reader, sizes map[string]int64) []error { +func (s *S3) UploadFonts(ctx context.Context, quid string, files map[string]io.Reader, sizes map[string]int64) []error { var ( wg sync.WaitGroup m sync.Mutex @@ -71,7 +71,7 @@ func (s *Storer) UploadFonts(ctx context.Context, quid string, files map[string] defer wg.Done() if _, err := s.client.PutObject(ctx, bucket, - bucketFonts + "/" +fmt.Sprintf("%s/%s", quid, fname), + bucketFonts+"/"+fmt.Sprintf("%s/%s", quid, fname), reader, sizes[fname], minio.PutObjectOptions{}); err != nil { @@ -87,10 +87,10 @@ func (s *Storer) UploadFonts(ctx context.Context, quid string, files map[string] return errs } -func (s *Storer) UploadScript(ctx context.Context, quid string, file io.Reader, size int64) error { +func (s *S3) UploadScript(ctx context.Context, quid string, file io.Reader, size int64) error { if _, err := s.client.PutObject(ctx, bucket, - bucketScripts + "/" + fmt.Sprintf("%s.js", quid), + bucketScripts+"/"+fmt.Sprintf("%s.js", quid), file, size, minio.PutObjectOptions{}); err != nil { @@ -100,10 +100,10 @@ func (s *Storer) UploadScript(ctx context.Context, quid string, file io.Reader, return nil } -func (s *Storer) UploadStyle(ctx context.Context, quid string, file io.Reader, size int64) error { +func (s *S3) UploadStyle(ctx context.Context, quid string, file io.Reader, size int64) error { if _, err := s.client.PutObject(ctx, bucket, - bucketStyle + "/" + fmt.Sprintf("%s.css", quid), + bucketStyle+"/"+fmt.Sprintf("%s.css", quid), file, size, minio.PutObjectOptions{}); err != nil { diff --git a/internal/server/http/http_server.go b/internal/server/http/http_server.go new file mode 100644 index 0000000..9771e5b --- /dev/null +++ b/internal/server/http/http_server.go @@ -0,0 +1,69 @@ +package http + +import ( + "context" + "fmt" + "gitea.pena/SQuiz/common/middleware" + "github.com/gofiber/fiber/v2" + "go.uber.org/zap" +) + +type ServerConfig struct { + Logger *zap.Logger + Controllers []Controller +} + +type Server struct { + Logger *zap.Logger + Controllers []Controller + app *fiber.App +} + +func NewServer(config ServerConfig) *Server { + app := fiber.New(fiber.Config{BodyLimit: 70 * 1024 * 1024}) + app.Use(middleware.JWTAuth()) + //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 ed98d78..0000000 --- a/main.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import ( - "github.com/skeris/appInit" - "gitea.pena/SQuiz/storer/app" - _ "gitea.pena/PenaSide/linters-golang/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 +}