reorganised entrypoint code

This commit is contained in:
Sergey Chubaryan 2024-08-25 03:11:05 +03:00
parent df1596312d
commit 69e24ec8ba
2 changed files with 205 additions and 147 deletions

159
main.go
View File

@ -1,160 +1,25 @@
package main package main
import ( import (
"backend/src/args_parser" "backend/src"
"backend/src/client_notifier"
"backend/src/config"
"backend/src/core/models"
"backend/src/core/repos"
"backend/src/core/services"
"backend/src/core/utils"
"backend/src/logger"
"backend/src/server"
"context" "context"
"crypto/rsa"
"crypto/x509"
"database/sql"
"encoding/pem"
"os" "os"
"os/signal" "strings"
"runtime/pprof"
"syscall"
"time"
"github.com/jackc/pgx"
"github.com/jackc/pgx/stdlib"
) )
func main() { func main() {
debugMode := false env := map[string]string{}
for _, envEntry := range os.Environ() {
args, err := args_parser.Parse(os.Args) kv := strings.SplitN(envEntry, "=", 2)
if err != nil { env[kv[0]] = kv[1]
panic(err)
} }
logger, err := logger.New(logger.NewLoggerOpts{ app := &src.App{}
Debug: debugMode, app.Run(
OutputFile: args.GetLogPath(), src.RunParams{
}) Ctx: context.Background(),
if err != nil { OsArgs: os.Args,
panic(err) EnvVars: env,
}
logger.Log().Msg("initializing service...")
defer logger.Log().Msg("service stopped")
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(context.Background(), signals...)
defer stop()
conf, err := config.NewFromFile(args.GetConfigPath())
if err != nil {
logger.Fatal().Err(err).Msg("failed parsing config file")
}
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")
}
}
var sqlDb *sql.DB
{
pgConnStr := conf.GetPostgresUrl()
connConf, err := pgx.ParseConnectionString(pgConnStr)
if err != nil {
logger.Fatal().Err(err).Msg("failed parsing postgres connection string")
}
sqlDb = stdlib.OpenDB(connConf)
if err := sqlDb.Ping(); err != nil {
logger.Fatal().Err(err).Msg("failed pinging postgres db")
}
}
jwtUtil := utils.NewJwtUtil(key)
passwordUtil := utils.NewPasswordUtil()
userRepo := repos.NewUserRepo(sqlDb)
emailRepo := repos.NewEmailRepo()
actionTokenRepo := repos.NewActionTokenRepo(sqlDb)
userCache := repos.NewCacheInmem[string, models.UserDTO](60 * 60)
go func() {
tmr := time.NewTicker(30 * time.Second)
defer tmr.Stop()
for {
select {
case <-ctx.Done():
return
case <-tmr.C:
userCache.CheckExpired()
}
}
}()
clientNotifier := client_notifier.NewBasicNotifier()
userService := services.NewUserService(
services.UserServiceDeps{
Jwt: jwtUtil,
Password: passwordUtil,
UserRepo: userRepo,
UserCache: userCache,
EmailRepo: emailRepo,
ActionTokenRepo: actionTokenRepo,
}, },
) )
linkService := services.NewShortlinkSevice(
services.NewShortlinkServiceParams{
Cache: repos.NewCacheInmem[string, string](7 * 24 * 60 * 60),
},
)
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,
Notifier: clientNotifier,
ShortlinkService: linkService,
UserService: userService,
},
)
srv.Run(ctx, conf.GetPort())
} }

193
src/app.go Normal file
View File

@ -0,0 +1,193 @@
package src
import (
"backend/src/args_parser"
"backend/src/client_notifier"
"backend/src/config"
"backend/src/core/models"
"backend/src/core/repos"
"backend/src/core/services"
"backend/src/core/utils"
"backend/src/logger"
"backend/src/server"
"context"
"crypto/rsa"
"crypto/x509"
"database/sql"
"encoding/pem"
"log"
"os"
"os/signal"
"runtime/pprof"
"syscall"
"time"
"github.com/jackc/pgx"
"github.com/jackc/pgx/stdlib"
)
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 to flag from conf
)
//-----------------------------------------
args, err := args_parser.Parse(osArgs)
if err != nil {
log.Fatalf("failed to parse os args: %v\n", err)
}
logger, err := logger.New(logger.NewLoggerOpts{
Debug: debugMode,
OutputFile: args.GetLogPath(),
})
if err != nil {
log.Fatalf("failed to create logger object: %v\n", err)
}
conf, err := config.NewFromFile(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")
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()
var sqlDb *sql.DB // TODO: move to integrations package
{
pgConnStr := conf.GetPostgresUrl()
connConf, err := pgx.ParseConnectionString(pgConnStr)
if err != nil {
logger.Fatal().Err(err).Msg("failed parsing postgres connection string")
}
sqlDb = stdlib.OpenDB(connConf)
if err := sqlDb.Ping(); err != nil {
logger.Fatal().Err(err).Msg("failed pinging postgres db")
}
}
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")
}
}
// Build business-logic objects
var (
userService services.UserService
shortlinkService services.ShortlinkService
)
{
var (
jwtUtil = utils.NewJwtUtil(key)
passwordUtil = utils.NewPasswordUtil()
userRepo = repos.NewUserRepo(sqlDb)
emailRepo = repos.NewEmailRepo()
actionTokenRepo = repos.NewActionTokenRepo(sqlDb)
linksCache = repos.NewCacheInmem[string, string](7 * 24 * 60 * 60)
userCache = repos.NewCacheInmem[string, models.UserDTO](60 * 60)
)
// Periodically trigger cache cleanup
go func() {
tmr := time.NewTicker(5 * time.Minute)
defer tmr.Stop()
for {
select {
case <-ctx.Done():
return
case <-tmr.C:
userCache.CheckExpired()
linksCache.CheckExpired()
}
}
}()
userService = services.NewUserService(
services.UserServiceDeps{
Jwt: jwtUtil,
Password: passwordUtil,
UserRepo: userRepo,
UserCache: userCache,
EmailRepo: emailRepo,
ActionTokenRepo: actionTokenRepo,
},
)
shortlinkService = services.NewShortlinkSevice(
services.NewShortlinkServiceParams{
Cache: linksCache,
},
)
}
clientNotifier := client_notifier.NewBasicNotifier()
// 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,
Notifier: clientNotifier,
ShortlinkService: shortlinkService,
UserService: userService,
},
)
srv.Run(ctx, conf.GetPort())
}