all repos

onasty @ 9169390bcd74a5d25bca1482e47d926e260c205d

a one-time notes service

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

Smirnov Oleksandr Smirnov Oleksandr
ss2316544@gmail.com
feat: keep metadate on note removal (#96)..., 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
		cfg.AppURL,
100
	)
101
102
	notecache := notecache.New(redisDB, cfg.CacheNoteTTL)
103
	noterepo := noterepo.New(psqlDB)
104
	notesrv := notesrv.New(noterepo, notePasswordHasher, notecache)
105
106
	rateLimiterConfig := ratelimit.Config{
107
		RPS:   cfg.RateLimiterRPS,
108
		TTL:   cfg.RateLimiterTTL,
109
		Burst: cfg.RateLimiterBurst,
110
	}
111
112
	handler := httptransport.NewTransport(
113
		usersrv,
114
		notesrv,
115
		rateLimiterConfig,
116
	)
117
118
	// http server
119
	srv := httpserver.NewServer(cfg.ServerPort, handler.Handler())
120
	go func() {
121
		slog.Info("starting http server", "port", cfg.ServerPort)
122
		if err := srv.Start(); !errors.Is(err, http.ErrServerClosed) {
123
			slog.Error("failed to start http server", "error", err)
124
		}
125
	}()
126
127
	// metrics
128
	if cfg.MetricsEnabled {
129
		mSrv := httpserver.NewServer(cfg.MetricsPort, metrics.Handler())
130
		go func() {
131
			slog.Info("starting metrics server", "port", cfg.MetricsPort)
132
			if err := mSrv.Start(); !errors.Is(err, http.ErrServerClosed) {
133
				slog.Error("failed to start metrics server", "error", err)
134
			}
135
		}()
136
	}
137
138
	// graceful shutdown
139
	quit := make(chan os.Signal, 1)
140
	signal.Notify(quit, os.Interrupt)
141
	<-quit
142
143
	if err := srv.Stop(ctx); err != nil {
144
		return errors.Join(errors.New("failed to stop http server"), err)
145
	}
146
147
	if err := psqlDB.Close(); err != nil {
148
		return errors.Join(errors.New("failed to close postgres connection"), err)
149
	}
150
151
	if err := redisDB.Close(); err != nil {
152
		return errors.Join(errors.New("failed to close redis connection"), err)
153
	}
154
155
	return nil
156
}