-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
126 lines (103 loc) · 3 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
package main
import (
"context"
"flag"
"fmt"
"guess_my_word/app"
"guess_my_word/internal/datastore"
"guess_my_word/internal/sessions"
"guess_my_word/internal/words"
"log"
"log/slog"
"net/http"
"os"
"os/signal"
"strconv"
"time"
"github.com/go-chi/chi/v5"
gsessions "github.com/gorilla/sessions"
"github.com/quasoft/memstore"
)
func main() {
port := flag.Int("port", 3000, "port to listen on")
help := flag.Bool("help", false, "displays help text and exits")
flag.Parse()
bind := fmt.Sprintf(":%d", *port)
if help != nil && *help {
fmt.Fprintf(os.Stderr, "This application serves the Guess My Word app at %s\n", bind)
os.Exit(0)
}
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
r := chi.NewRouter()
if err := setupStores(ctx, r); err != nil {
log.Fatalf("Unable to set up datastore: %s", err)
}
// Add all HTTP handlers
if err := app.AddHandlers(r); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Printf("Listening and serving HTTP on port %s\n", bind)
srv := http.Server{Addr: bind, Handler: r}
done := make(chan interface{})
go gracefulShutdown(ctx, &srv, done)
_ = srv.ListenAndServe()
<-done
}
func setupStores(ctx context.Context, r chi.Router) error {
var dataClient datastore.Client
var sessionClient gsessions.Store
var err error
if addr, ok := os.LookupEnv("REDIS_ADDR"); ok {
sessionClient, err = sessions.NewRedis(addr, []byte("secret"))
if err != nil {
return fmt.Errorf("redis setup failure: %w", err)
}
dataClient = datastore.NewRedis(addr)
} else if host, ok := os.LookupEnv("REDIS_HOST"); ok {
db := 0
if dbParsed, err := strconv.ParseInt(os.Getenv("REDIS_DB"), 10, 64); err == nil {
db = int(dbParsed)
}
port := os.Getenv("REDIS_PORT")
user := os.Getenv("REDIS_USER")
pass := os.Getenv("REDIS_PASSWORD")
sessionClient, err = sessions.NewRedisSecure(host, port, user, pass, db, []byte("secret"))
if err != nil {
return fmt.Errorf("redis setup failure: %w", err)
}
dataClient = datastore.NewRedisSecure(
host,
port,
user,
pass,
db,
)
} else {
slog.Warn("No REDIS_ADDR or REDIS_HOST env var set. Falling back upon in-memory store")
sessionClient = memstore.NewMemStore([]byte("secret"))
dataClient = datastore.NewMemory()
}
// Set up data storage
app.SetupStores(
words.NewListStore(dataClient),
words.NewWordStore(dataClient),
)
// Set up session management
sessions.Configure(r, "guessmyword", sessionClient)
return words.PopulateDefaultLists(ctx, dataClient)
}
func gracefulShutdown(ctx context.Context, srv *http.Server, done chan<- interface{}) {
const drainTimeout = time.Minute
// Wait for the process to be interrupted
<-ctx.Done()
fmt.Fprintf(os.Stderr, "Interrupted. Shutting down\n")
// Gracefully drain all connections
drainCtx, cancel := context.WithTimeout(context.Background(), drainTimeout)
defer cancel()
if err := srv.Shutdown(drainCtx); err != nil {
fmt.Fprintf(os.Stderr, "failed to gracefully shut down: %s\n", err)
}
done <- true
}