200 lines
4.4 KiB
Go
200 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"backend/cmd/backend/args_parser"
|
|
"backend/cmd/backend/server"
|
|
"backend/internal/core/models"
|
|
"backend/internal/core/repos"
|
|
"backend/internal/core/services"
|
|
"backend/internal/core/utils"
|
|
"backend/internal/integrations"
|
|
"backend/pkg/cache"
|
|
"backend/pkg/logger"
|
|
"context"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"runtime/pprof"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
type App struct{}
|
|
|
|
type RunParams struct {
|
|
Ctx context.Context
|
|
OsArgs []string
|
|
EnvVars map[string]string
|
|
}
|
|
|
|
func (a *App) Run(p RunParams) {
|
|
var (
|
|
ctx = p.Ctx
|
|
osArgs = p.OsArgs
|
|
_ = p.EnvVars
|
|
debugMode = false // TODO: replace with flag from conf
|
|
)
|
|
|
|
signals := []os.Signal{
|
|
os.Kill,
|
|
os.Interrupt,
|
|
syscall.SIGINT,
|
|
syscall.SIGTERM,
|
|
syscall.SIGKILL,
|
|
syscall.SIGSTOP,
|
|
syscall.SIGQUIT,
|
|
syscall.SIGABRT,
|
|
syscall.SIGHUP,
|
|
}
|
|
|
|
ctx, stop := signal.NotifyContext(ctx, signals...)
|
|
defer stop()
|
|
|
|
//-----------------------------------------
|
|
|
|
args, err := args_parser.Parse(osArgs)
|
|
if err != nil {
|
|
log.Fatalf("failed to parse os args: %v\n", err)
|
|
}
|
|
|
|
logger, err := logger.New(
|
|
ctx,
|
|
logger.NewLoggerOpts{
|
|
Debug: debugMode,
|
|
OutputFile: args.GetLogPath(),
|
|
},
|
|
)
|
|
if err != nil {
|
|
log.Fatalf("failed to create logger object: %v\n", err)
|
|
}
|
|
|
|
conf, err := LoadConfig(args.GetConfigPath())
|
|
if err != nil {
|
|
logger.Fatal().Err(err).Msg("failed to parse config file")
|
|
}
|
|
|
|
//-----------------------------------------
|
|
|
|
logger.Log().Msg("initializing service...")
|
|
defer logger.Log().Msg("service stopped")
|
|
|
|
sqlDb, err := integrations.NewPostgresConn(ctx, conf.GetPostgresUrl())
|
|
if err != nil {
|
|
logger.Fatal().Err(err).Msg("failed connecting to postgres")
|
|
}
|
|
|
|
kafka := integrations.NewKafka(conf.GetKafkaUrl(), conf.GetKafkaTopic())
|
|
|
|
var key *rsa.PrivateKey
|
|
{
|
|
keyRawBytes, err := os.ReadFile(conf.GetJwtSigningKey())
|
|
if err != nil {
|
|
logger.Fatal().Err(err).Msg("failed reading signing key file")
|
|
}
|
|
|
|
keyPem, _ := pem.Decode(keyRawBytes)
|
|
key, err = x509.ParsePKCS1PrivateKey(keyPem.Bytes)
|
|
if err != nil {
|
|
logger.Fatal().Err(err).Msg("failed parsing signing key")
|
|
}
|
|
}
|
|
|
|
tracer, err := integrations.NewTracer("backend")
|
|
if err != nil {
|
|
logger.Fatal().Err(err).Msg("failed initializing tracer")
|
|
}
|
|
|
|
// Build business-logic objects
|
|
var (
|
|
userService services.UserService
|
|
shortlinkService services.ShortlinkService
|
|
)
|
|
{
|
|
var (
|
|
jwtUtil = utils.NewJwtUtil(key)
|
|
passwordUtil = utils.NewPasswordUtil()
|
|
|
|
userRepo = repos.NewUserRepo(sqlDb, tracer)
|
|
actionTokenRepo = repos.NewActionTokenRepo(sqlDb)
|
|
shortlinkRepo = repos.NewShortlinkRepo(sqlDb, tracer)
|
|
eventRepo = repos.NewEventRepo(kafka)
|
|
|
|
userCache = cache.NewCacheInmemSharded[models.UserDTO](cache.ShardingTypeInteger)
|
|
jwtCache = cache.NewCacheInmemSharded[string](cache.ShardingTypeJWT)
|
|
linksCache = cache.NewCacheInmem[string, string]()
|
|
)
|
|
|
|
// Periodically trigger cache cleanup
|
|
go func() {
|
|
tmr := time.NewTicker(5 * time.Minute)
|
|
defer tmr.Stop()
|
|
|
|
batchSize := 100
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-tmr.C:
|
|
userCache.CheckExpired(batchSize)
|
|
jwtCache.CheckExpired(batchSize)
|
|
linksCache.CheckExpired(batchSize)
|
|
}
|
|
}
|
|
}()
|
|
|
|
userService = services.NewUserService(
|
|
services.UserServiceDeps{
|
|
Jwt: jwtUtil,
|
|
Password: passwordUtil,
|
|
UserRepo: userRepo,
|
|
UserCache: userCache,
|
|
JwtCache: jwtCache,
|
|
EventRepo: *eventRepo,
|
|
ActionTokenRepo: actionTokenRepo,
|
|
},
|
|
)
|
|
shortlinkService = services.NewShortlinkSevice(
|
|
services.NewShortlinkServiceParams{
|
|
Cache: linksCache,
|
|
Repo: shortlinkRepo,
|
|
},
|
|
)
|
|
|
|
// TODO: Run cleanup routine
|
|
// go shortlinkService.ShortlinkRoutine(ctx)
|
|
}
|
|
|
|
// Start profiling
|
|
if args.GetProfilePath() != "" {
|
|
pprofFile, err := os.Create(args.GetProfilePath())
|
|
if err != nil {
|
|
logger.Fatal().Err(err).Msg("can not create profile file")
|
|
}
|
|
if err := pprof.StartCPUProfile(pprofFile); err != nil {
|
|
logger.Fatal().Err(err).Msg("can not start cpu profiling")
|
|
}
|
|
|
|
defer func() {
|
|
logger.Log().Msg("stopping profiling...")
|
|
|
|
pprof.StopCPUProfile()
|
|
pprofFile.Close()
|
|
}()
|
|
}
|
|
|
|
srv := server.New(
|
|
server.NewServerOpts{
|
|
DebugMode: debugMode,
|
|
Logger: logger,
|
|
ShortlinkService: shortlinkService,
|
|
UserService: userService,
|
|
Tracer: tracer,
|
|
},
|
|
)
|
|
srv.Run(ctx, conf.GetPort())
|
|
}
|