fix layout

This commit is contained in:
skeris 2025-04-17 15:44:25 +03:00
parent 64d15aff7f
commit 36eec4a2c0
16 changed files with 387 additions and 214 deletions

@ -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
}

25
cmd/main.go Normal file

@ -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))
}
}

2
go.mod

@ -18,6 +18,7 @@ require (
gitea.pena/PenaSide/common v0.0.0-20250103085335-91ea31fee517 // indirect gitea.pena/PenaSide/common v0.0.0-20250103085335-91ea31fee517 // indirect
github.com/ClickHouse/clickhouse-go v1.5.4 // indirect github.com/ClickHouse/clickhouse-go v1.5.4 // indirect
github.com/andybalholm/brotli v1.1.0 // 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/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/go-ini/ini v1.67.0 // 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-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/uuid v1.6.0 // 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/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/lib/pq v1.10.9 // indirect github.com/lib/pq v1.10.9 // indirect

12
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/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 h1:tBkXWNIt8icmkMMnq8MA421RWkUy4OZh5P7C3q8uCu4=
gitea.pena/PenaSide/hlog v0.0.0-20241125221102-a54c29c002a9/go.mod h1:sanhSL8aEsfcq21P+eItYiAnKAre+B67nGJmDfk2cf0= 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/PenaSide/linters-golang v0.0.0-20241207122018-933207374735 h1:jDVeUhGBTXBibmW5dmtJg2m2+z5z2Rf6J4G0LpjVoJ0=
gitea.pena/SQuiz/common v0.0.0-20250207214652-9994f2d4d43f/go.mod h1:/YR+uo4RouZshuHPkguk7nAJVKuFt3Z0mTFxUPdlzxQ= 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/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 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0=
github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI= 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/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 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk=
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= 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 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80= 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= 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 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/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/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 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=

96
internal/app/app.go Normal file

@ -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
}

@ -1,41 +1,30 @@
package service package quiz_files
import ( import (
"errors" "errors"
"github.com/gofiber/fiber/v2" "gitea.pena/SQuiz/common/dal"
"io"
quizdal "gitea.pena/SQuiz/common/dal"
mw "gitea.pena/SQuiz/common/middleware" 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" "strconv"
"sync" "sync"
"github.com/rs/xid"
) )
type Service struct { type QuizFiles struct {
store *dal.Storer stDal *repository.S3
dal *quizdal.DAL pgDal *dal.DAL
} }
func New(s *dal.Storer, q *quizdal.DAL) *Service { func New(stDal *repository.S3, pgDal *dal.DAL) *QuizFiles {
return &Service{ return &QuizFiles{
store: s, stDal: stDal,
dal: q, pgDal: pgDal,
} }
} }
func (s *Service) Register(app *fiber.App) { func (r *QuizFiles) UploadCustom(c *fiber.Ctx) error {
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 {
accountId, ok := mw.GetAccountId(c) accountId, ok := mw.GetAccountId(c)
if !ok { if !ok {
return nil 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()) 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 { if err != nil {
return c.Status(fiber.StatusNotAcceptable).SendString("not exists quiz id provided " + err.Error()) 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() 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() errm.Lock()
defer errm.Unlock() defer errm.Unlock()
errs = append(errs, errors.New(err.Error()+" => script upload")) errs = append(errs, errors.New(err.Error()+" => script upload"))
@ -104,7 +93,7 @@ func (s *Service) UploadCustom(c *fiber.Ctx) error {
} }
defer file.Close() 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() errm.Lock()
defer errm.Unlock() defer errm.Unlock()
errs = append(errs, errors.New(err.Error()+" => style upload")) errs = append(errs, errors.New(err.Error()+" => style upload"))
@ -128,7 +117,7 @@ func (s *Service) UploadCustom(c *fiber.Ctx) error {
wg.Add(1) wg.Add(1)
go func() { go func() {
defer wg.Done() 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() errm.Lock()
defer errm.Unlock() defer errm.Unlock()
errs = append(errs, errorsFontUploading...) errs = append(errs, errorsFontUploading...)
@ -144,7 +133,7 @@ func (s *Service) UploadCustom(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusOK) 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) accountId, ok := mw.GetAccountId(c)
if !ok { if !ok {
return nil 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()) 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 { if err != nil {
return c.Status(fiber.StatusNotAcceptable).SendString("not exists quiz id provided " + err.Error()) 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() 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...) errs = append(errs, err...)
} }
} }

@ -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"
}

@ -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
}

@ -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),
},
}
}

@ -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
}

@ -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
}

@ -1,4 +1,4 @@
package dal package repository
import ( import (
"context" "context"
@ -9,25 +9,25 @@ import (
) )
const ( const (
bucket = "3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b" bucket = "3c580be9-cf31f296-d055-49cf-b39e-30c7959dc17b"
folderImages = "squizimages" folderImages = "squizimages"
bucketFonts = "squizfonts" bucketFonts = "squizfonts"
bucketScripts = "squizscript" bucketScripts = "squizscript"
bucketStyle = "squizstyle" bucketStyle = "squizstyle"
) )
type Storer struct { type S3 struct {
client *minio.Client 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, client: minioClient,
}, nil }, 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 ( var (
wg sync.WaitGroup wg sync.WaitGroup
m sync.Mutex m sync.Mutex
@ -57,7 +57,7 @@ func (s *Storer) UploadImages(ctx context.Context, quid string, files map[string
return errs 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 ( var (
wg sync.WaitGroup wg sync.WaitGroup
m sync.Mutex m sync.Mutex
@ -71,7 +71,7 @@ func (s *Storer) UploadFonts(ctx context.Context, quid string, files map[string]
defer wg.Done() defer wg.Done()
if _, err := s.client.PutObject(ctx, if _, err := s.client.PutObject(ctx,
bucket, bucket,
bucketFonts + "/" +fmt.Sprintf("%s/%s", quid, fname), bucketFonts+"/"+fmt.Sprintf("%s/%s", quid, fname),
reader, reader,
sizes[fname], sizes[fname],
minio.PutObjectOptions{}); err != nil { minio.PutObjectOptions{}); err != nil {
@ -87,10 +87,10 @@ func (s *Storer) UploadFonts(ctx context.Context, quid string, files map[string]
return errs 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, if _, err := s.client.PutObject(ctx,
bucket, bucket,
bucketScripts + "/" + fmt.Sprintf("%s.js", quid), bucketScripts+"/"+fmt.Sprintf("%s.js", quid),
file, file,
size, size,
minio.PutObjectOptions{}); err != nil { minio.PutObjectOptions{}); err != nil {
@ -100,10 +100,10 @@ func (s *Storer) UploadScript(ctx context.Context, quid string, file io.Reader,
return nil 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, if _, err := s.client.PutObject(ctx,
bucket, bucket,
bucketStyle + "/" + fmt.Sprintf("%s.css", quid), bucketStyle+"/"+fmt.Sprintf("%s.css", quid),
file, file,
size, size,
minio.PutObjectOptions{}); err != nil { minio.PutObjectOptions{}); err != nil {

@ -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)
}
}
}

11
main.go

@ -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{})
}

37
pkg/closer/closer.go Normal file

@ -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
}