all repos

onasty @ a9ae72ba6d9d01fcc52c5cd4577e93e3aefb8602

a one-time notes service

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

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