-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
109 lines (101 loc) · 2.81 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package main
import (
"context"
"errors"
"log"
"log/slog"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"github.com/emersion/go-smtp"
"github.com/rntrp/mailheap/internal/config"
"github.com/rntrp/mailheap/internal/httpsrv"
"github.com/rntrp/mailheap/internal/msg"
"github.com/rntrp/mailheap/internal/rest"
"github.com/rntrp/mailheap/internal/smtprecv"
"github.com/rntrp/mailheap/internal/storage"
)
func main() {
config.Load()
slog.Info("📮 Initializing services...")
rest.InitIndex()
slog.Info("🗜️ Static web UI resources minified & compressed")
storage, err := storage.New()
if err != nil {
log.Fatal(err)
}
slog.Info("🥞 Database connection established")
addMailSvc := msg.NewAddMailSvc(storage)
recv := smtprecv.Init(addMailSvc)
sig := make(chan os.Signal, 1)
srv := httpsrv.New(rest.New(storage, addMailSvc), sig)
shutdown := make(chan error)
go shutdownMonitor(sig, shutdown, storage, recv, srv)
slog.Info("🔌 Set up graceful shutdown monitor")
out := make(chan<- error)
go startRecv(out, recv)
go startSrv(out, srv)
logShutdown(<-shutdown)
if err := storage.Shutdown(); err != nil {
slog.Error("DB shutdown failed", "error", err.Error())
}
}
func startRecv(out chan<- error, recv *smtp.Server) {
slog.Info("📧 Receiving SMTP connections",
"domain", recv.Domain,
"addr", recv.Addr)
out <- recv.ListenAndServe()
}
func startSrv(out chan<- error, srv *http.Server) {
slog.Info("🌐 Listening to HTTP connections", "addr", srv.Addr)
switch {
case len(srv.Addr) == 0:
slog.Info("💡 Type http://localhost in your browser for UI")
case srv.Addr[0] == ':':
slog.Info("💡 Type http://localhost" + srv.Addr + " in your browser for UI")
default:
slog.Info("💡 Type http://" + srv.Addr + " in your browser for UI")
}
out <- srv.ListenAndServe()
}
func shutdownMonitor(sig chan os.Signal, out chan error,
mailStorage storage.MailStorage, switches ...shutdownSwitch) {
timeout := config.GetShutdownTimeout()
signal.Notify(sig, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
sigName := (<-sig).String()
slog.Info("Shutdown signal received", "signal", sigName)
wg := new(sync.WaitGroup)
num := len(switches)
err := make([]error, num+1)
wg.Add(num)
for i, s := range switches {
go func(i int, s shutdownSwitch) {
defer wg.Done()
ctx := context.Background()
if timeout > 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, timeout)
defer cancel()
}
err[i] = s.Shutdown(ctx)
}(i, s)
}
wg.Wait()
err[num] = mailStorage.Shutdown()
out <- errors.Join(err...)
}
func logShutdown(err error) {
switch err {
case nil:
slog.Info("Mailheap was shut down gracefully. Bye.")
case http.ErrServerClosed:
slog.Info(err.Error())
default:
slog.Error(err.Error())
}
}
type shutdownSwitch interface {
Shutdown(ctx context.Context) error
}