all repos

onasty @ 00703b2100cc499d21bc752a66d4a055f70f167f

a one-time notes service

onasty/cmd/server/main.go (view raw)

Smirnov Oleksandr Smirnov Oleksandr
ss2316544@gmail.com
feat: add password support to notes (#41)..., 1 year ago
1
package main
2
3
import (
4
	"context"
5
	"errors"
6
	"fmt"
7
	"log/slog"
8
	"net/http"
9
	"os"
10
	"os/signal"
11
12
	"github.com/gin-gonic/gin"
13
	"github.com/olexsmir/onasty/internal/config"
14
	"github.com/olexsmir/onasty/internal/hasher"
15
	"github.com/olexsmir/onasty/internal/jwtutil"
16
	"github.com/olexsmir/onasty/internal/logger"
17
	"github.com/olexsmir/onasty/internal/mailer"
18
	"github.com/olexsmir/onasty/internal/metrics"
19
	"github.com/olexsmir/onasty/internal/service/notesrv"
20
	"github.com/olexsmir/onasty/internal/service/usersrv"
21
	"github.com/olexsmir/onasty/internal/store/psql/noterepo"
22
	"github.com/olexsmir/onasty/internal/store/psql/sessionrepo"
23
	"github.com/olexsmir/onasty/internal/store/psql/userepo"
24
	"github.com/olexsmir/onasty/internal/store/psql/vertokrepo"
25
	"github.com/olexsmir/onasty/internal/store/psqlutil"
26
	"github.com/olexsmir/onasty/internal/store/rdb"
27
	"github.com/olexsmir/onasty/internal/store/rdb/usercache"
28
	httptransport "github.com/olexsmir/onasty/internal/transport/http"
29
	"github.com/olexsmir/onasty/internal/transport/http/httpserver"
30
	"github.com/olexsmir/onasty/internal/transport/http/ratelimit"
31
)
32
33
func main() {
34
	if err := run(context.Background()); err != nil {
35
		fmt.Fprintf(os.Stderr, "error: %v\n", err)
36
		os.Exit(1)
37
	}
38
}
39
40
//nolint:err113
41
func run(ctx context.Context) error {
42
	ctx, cancel := context.WithCancel(ctx)
43
	defer cancel()
44
45
	cfg := config.NewConfig()
46
47
	// logger
48
	logger, err := logger.NewCustomLogger(cfg.LogLevel, cfg.LogFormat, cfg.LogShowLine)
49
	if err != nil {
50
		return err
51
	}
52
53
	slog.SetDefault(logger)
54
55
	// semi dev mode
56
	if !cfg.IsDevMode() {
57
		gin.SetMode(gin.ReleaseMode)
58
	}
59
60
	// app deps
61
	psqlDB, err := psqlutil.Connect(ctx, cfg.PostgresDSN)
62
	if err != nil {
63
		return err
64
	}
65
66
	redisDB, err := rdb.Connect(ctx, cfg.RedisAddr, cfg.RedisPassword, cfg.RedisDB)
67
	if err != nil {
68
		return err
69
	}
70
71
	userPasswordHasher := hasher.NewSHA256Hasher(cfg.PasswordSalt)
72
	notePasswordHasher := hasher.NewSHA256Hasher(cfg.NotePassowrdSalt)
73
	jwtTokenizer := jwtutil.NewJWTUtil(cfg.JwtSigningKey, cfg.JwtAccessTokenTTL)
74
	mailGunMailer := mailer.NewMailgun(cfg.MailgunFrom, cfg.MailgunDomain, cfg.MailgunAPIKey)
75
76
	sessionrepo := sessionrepo.New(psqlDB)
77
	vertokrepo := vertokrepo.New(psqlDB)
78
79
	userepo := userepo.New(psqlDB)
80
	usercache := usercache.New(redisDB, cfg.CacheUsersTTL)
81
	usersrv := usersrv.New(
82
		userepo,
83
		sessionrepo,
84
		vertokrepo,
85
		userPasswordHasher,
86
		jwtTokenizer,
87
		mailGunMailer,
88
		usercache,
89
		cfg.JwtRefreshTokenTTL,
90
		cfg.VerificationTokenTTL,
91
		cfg.AppURL,
92
	)
93
94
	noterepo := noterepo.New(psqlDB)
95
	notesrv := notesrv.New(noterepo, notePasswordHasher)
96
97
	rateLimiterConfig := ratelimit.Config{
98
		RPS:   cfg.RateLimiterRPS,
99
		TTL:   cfg.RateLimiterTTL,
100
		Burst: cfg.RateLimiterBurst,
101
	}
102
103
	handler := httptransport.NewTransport(
104
		usersrv,
105
		notesrv,
106
		rateLimiterConfig,
107
	)
108
109
	// http server
110
	srv := httpserver.NewServer(cfg.ServerPort, handler.Handler())
111
	go func() {
112
		slog.Info("starting http server", "port", cfg.ServerPort)
113
		if err := srv.Start(); !errors.Is(err, http.ErrServerClosed) {
114
			slog.Error("failed to start http server", "error", err)
115
		}
116
	}()
117
118
	// metrics
119
	if cfg.MetricsEnabled {
120
		mSrv := httpserver.NewServer(cfg.MetricsPort, metrics.Handler())
121
		go func() {
122
			slog.Info("starting metrics server", "port", cfg.MetricsPort)
123
			if err := mSrv.Start(); !errors.Is(err, http.ErrServerClosed) {
124
				slog.Error("failed to start metrics server", "error", err)
125
			}
126
		}()
127
	}
128
129
	// graceful shutdown
130
	quit := make(chan os.Signal, 1)
131
	signal.Notify(quit, os.Interrupt)
132
	<-quit
133
134
	if err := srv.Stop(ctx); err != nil {
135
		return errors.Join(errors.New("failed to stop http server"), err)
136
	}
137
138
	if err := psqlDB.Close(); err != nil {
139
		return errors.Join(errors.New("failed to close postgres connection"), err)
140
	}
141
142
	if err := redisDB.Close(); err != nil {
143
		return errors.Join(errors.New("failed to close redis connection"), err)
144
	}
145
146
	return nil
147
}