-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
154 lines (122 loc) · 3.25 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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package main
import (
"context"
"embed"
"flag"
"fmt"
"log/slog"
"os"
"os/signal"
"sync"
"github.com/coreos/go-systemd/v22/daemon"
"github.com/theandrew168/bloggulus/backend/config"
fetch "github.com/theandrew168/bloggulus/backend/fetch/web"
"github.com/theandrew168/bloggulus/backend/finder"
"github.com/theandrew168/bloggulus/backend/postgres"
"github.com/theandrew168/bloggulus/backend/repository"
"github.com/theandrew168/bloggulus/backend/service"
"github.com/theandrew168/bloggulus/backend/web"
)
//go:embed public
var publicFS embed.FS
//go:embed migrations
var migrationsFS embed.FS
func main() {
code := 0
err := run()
if err != nil {
slog.Error("error running application",
"error", err.Error(),
)
code = 1
}
os.Exit(code)
}
func run() error {
// Check for the config file path flag.
configFilePath := flag.String("conf", "bloggulus.conf", "app config file")
// Check for any specific action flags.
migrate := flag.Bool("migrate", false, "apply migrations and exit")
flag.Parse()
// Load the application's config file.
conf, err := config.ReadFile(*configFilePath)
if err != nil {
return err
}
// Open a database connection pool.
pool, err := postgres.ConnectPool(conf.DatabaseURI)
if err != nil {
return err
}
defer pool.Close()
// Apply any pending database migrations.
applied, err := postgres.Migrate(pool, migrationsFS)
if err != nil {
return err
}
for _, migration := range applied {
slog.Info("applied migration", "name", migration)
}
// Exit now if just applying migrations.
if *migrate {
return nil
}
// Init the database storage interfaces.
repo := repository.New(pool)
find := finder.New(pool)
// Init the sync service and do an initial sync.
feedFetcher := fetch.NewFeedFetcher()
pageFetcher := fetch.NewPageFetcher()
syncService := service.NewSyncService(repo, feedFetcher, pageFetcher)
// Init the session service and clear any expired session tokens.
sessionService := service.NewSessionService(repo)
// Let systemd know that we are good to go (no-op if not using systemd).
daemon.SdNotify(false, daemon.SdNotifyReady)
// Create a context that cancels upon receiving an interrupt signal.
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
defer stop()
var wg sync.WaitGroup
webHandler := web.Handler(publicFS, conf, repo, find, pageFetcher, syncService)
// Let the web server port be overridden by an env var.
port := conf.Port
if os.Getenv("PORT") != "" {
port = os.Getenv("PORT")
}
addr := fmt.Sprintf("127.0.0.1:%s", port)
// Start the web server in the background.
wg.Add(1)
go func() {
defer wg.Done()
err := web.Run(ctx, webHandler, addr)
if err != nil {
slog.Error("error running web server",
"error", err.Error(),
)
}
}()
// Start the sync service in the background.
wg.Add(1)
go func() {
defer wg.Done()
err := syncService.Run(ctx)
if err != nil {
slog.Error("error running sync service",
"error", err.Error(),
)
}
}()
// Start the session cleanup service in the background.
wg.Add(1)
go func() {
defer wg.Done()
err := sessionService.Run(ctx)
if err != nil {
slog.Error("error running session service",
"error", err.Error(),
)
}
}()
// Wait for all services to stop.
wg.Wait()
return nil
}