carlos / movement Public

Clone
git clone https://amadan.net/carlos/movement

Plain git — no account needed to clone.

Download

Download this file

1// Command movement wires the app: resolve the platform's activation
2// argv, open the database, build the app, serve.
3package main
4
5import (
6 "log/slog"
7 "os"
8
9 "github.com/carlosframework/rastrillo"
10 "github.com/carlosframework/rastrillo/db"
11
12 movement "amadan.net/carlos/movement/internal/movement"
13)
14
15func main() {
16 logger := slog.New(slog.NewTextHandler(os.Stderr, nil))
17
18 // Resolve, not Run: this app opens its own database handle via
19 // db.Open — a *gorm.DB with a split reader/writer pool, not the
20 // bare *sql.DB Options.Router would hand back — so Options.DBPath
21 // must be empty again by the Serve call below, or Serve would open
22 // a second, unused connection to the same file.
23 opts, err := rastrillo.Resolve(rastrillo.Options{DBPath: "movement.db", Logger: logger})
24 if err != nil {
25 logger.Error("resolve activation", "err", err)
26 os.Exit(1)
27 }
28
29 d, err := db.Open(opts.DBPath, logger)
30 if err != nil {
31 logger.Error("open database", "err", err)
32 os.Exit(1)
33 }
34 defer d.Close()
35
36 // The scaffold defaulted a missing origin to localhost and warned. This
37 // app refuses instead. The origin is the base of every emailed link, the
38 // CSRF yardstick and the cookie's host, so a default that boots happily
39 // is a canary quietly mailing people into production. LoadEnv also
40 // refuses an unset instance key, and refuses to run without SMTP unless
41 // the origin is loopback — because rastrillo's mail fallback logs the
42 // confirmation token rather than sending it.
43 env, err := movement.LoadEnv()
44 if err != nil {
45 logger.Error("configuration", "err", err)
46 os.Exit(1)
47 }
48 movement.SetLogger(logger)
49
50 mux, err := movement.App(d, env)
51 if err != nil {
52 logger.Error("build app", "err", err)
53 os.Exit(1)
54 }
55
56 opts.Mux = mux
57 // rastrillo's default policy is form-action 'self', which a browser
58 // applies to the whole redirect chain a form submission starts — so the
59 // app's own CSP was refusing its own 303 to the sign-in provider.
60 opts.CSP = movement.CSP()
61 opts.DBPath = ""
62 // The same page a handler's own error path renders (render.go's
63 // ErrorPage — wire it to Ctx.ErrorPage too as the app grows
64 // handlers), so a panic and a handled failure look identical.
65 opts.ErrorPage = movement.ErrorPage
66 if err := rastrillo.Serve(opts); err != nil {
67 logger.Error("serve failed", "err", err)
68 os.Exit(1)
69 }
70}
71