first commit
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"secunda-test/internal/cache"
|
||||
"secunda-test/internal/config"
|
||||
"secunda-test/internal/email"
|
||||
"secunda-test/internal/handler"
|
||||
"secunda-test/internal/httpx"
|
||||
mysqlrepo "secunda-test/internal/repository/mysql"
|
||||
"secunda-test/internal/service"
|
||||
)
|
||||
|
||||
type Container struct {
|
||||
Config config.Config
|
||||
DB *sql.DB
|
||||
Redis *redis.Client
|
||||
Server *http.Server
|
||||
}
|
||||
|
||||
func New(ctx context.Context) (*Container, error) {
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db, err := sql.Open("mysql", cfg.Database.DSN)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
db.SetMaxOpenConns(cfg.Database.MaxOpenConns)
|
||||
db.SetMaxIdleConns(cfg.Database.MaxIdleConns)
|
||||
db.SetConnMaxLifetime(time.Hour)
|
||||
if err := db.PingContext(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
redisClient := redis.NewClient(&redis.Options{Addr: cfg.Redis.Addr})
|
||||
|
||||
requests := prometheus.NewCounterVec(prometheus.CounterOpts{Name: "http_requests_total", Help: "Total HTTP requests."}, []string{"method", "path", "status"})
|
||||
duration := prometheus.NewHistogramVec(prometheus.HistogramOpts{Name: "http_request_duration_seconds", Help: "HTTP request duration."}, []string{"method", "path"})
|
||||
prometheus.MustRegister(requests, duration)
|
||||
|
||||
users := mysqlrepo.NewUserRepository(db)
|
||||
teams := mysqlrepo.NewTeamRepository(db)
|
||||
tasks := mysqlrepo.NewTaskRepository(db)
|
||||
taskCache := cache.NewTaskCache(redisClient, time.Duration(cfg.Redis.TTLSeconds)*time.Second)
|
||||
emailSender := email.NewSender(cfg.Email.Endpoint)
|
||||
|
||||
authService := service.NewAuthService(users, cfg.JWT.Secret, time.Duration(cfg.JWT.TTLMinutes)*time.Minute)
|
||||
teamService := service.NewTeamService(teams, users, emailSender)
|
||||
taskService := service.NewTaskService(tasks, teams, taskCache)
|
||||
middleware := httpx.NewMiddleware(cfg.JWT.Secret, cfg.RateLimit.RequestsPerMinute, requests, duration)
|
||||
h := handler.New(authService, teamService, taskService, middleware)
|
||||
|
||||
root := http.NewServeMux()
|
||||
root.Handle("/", h.Routes())
|
||||
root.Handle("/metrics", promhttp.Handler())
|
||||
|
||||
return &Container{
|
||||
Config: cfg,
|
||||
DB: db,
|
||||
Redis: redisClient,
|
||||
Server: &http.Server{Addr: cfg.HTTP.Addr, Handler: root, ReadHeaderTimeout: 5 * time.Second},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Container) Migrate(ctx context.Context) error {
|
||||
files, err := filepath.Glob("migrations/*.sql")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(files) == 0 {
|
||||
files, err = filepath.Glob("/app/migrations/*.sql")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, file := range files {
|
||||
data, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, stmt := range strings.Split(string(data), ";") {
|
||||
stmt = strings.TrimSpace(stmt)
|
||||
if stmt == "" {
|
||||
continue
|
||||
}
|
||||
if _, err := c.DB.ExecContext(ctx, stmt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Container) Close() error {
|
||||
var errs []error
|
||||
if c.Redis != nil {
|
||||
errs = append(errs, c.Redis.Close())
|
||||
}
|
||||
if c.DB != nil {
|
||||
errs = append(errs, c.DB.Close())
|
||||
}
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
Reference in New Issue
Block a user