An OIDC-protected index page for your homeserver.
1//go:generate go tool templ generate
2//go:generate go tool go-tw -i tailwind.css -o static/styles.css
3
4package main
5
6import (
7 "embed"
8 "errors"
9 "log"
10 "log/slog"
11 "net/http"
12 "os"
13
14 _ "github.com/joho/godotenv/autoload"
15 gonanoid "github.com/matoous/go-nanoid/v2"
16 "github.com/sblinch/kdl-go"
17 "github.com/zitadel/logging"
18
19 "github.com/a-h/templ"
20
21 "ladon/auth"
22 "ladon/views"
23)
24
25//go:embed static
26var content embed.FS
27
28func ServeRoot(am *auth.AuthManager) http.Handler {
29 f, err := os.Open("./data/links.kdl")
30 if err != nil {
31 am.Log.Error("ladon: failed to open KDL config")
32 panic(err)
33 }
34
35 doc, err := kdl.Parse(f)
36 if err != nil {
37 am.Log.Error("ladon: failed to pase KDL config")
38 panic(err)
39 }
40
41 return http.HandlerFunc(
42 func(w http.ResponseWriter, r *http.Request) {
43 claims, err := am.GetSession(r)
44
45 if errors.Is(err, auth.ErrNoSession) {
46 templ.Handler(views.Authenticate()).ServeHTTP(w, r)
47 return
48 } else if errors.Is(err, auth.ErrSessionExpired) {
49 am.DeleteSession(w)
50 am.HandleLogin().ServeHTTP(w, r)
51 return
52 } else if err != nil {
53 http.Error(w, err.Error(), http.StatusInternalServerError)
54 return
55 }
56
57 templ.Handler(views.Links(claims.PreferredUsername, doc)).ServeHTTP(w, r)
58 },
59 )
60}
61
62func main() {
63 logger := slog.New(
64 slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
65 AddSource: true,
66 Level: slog.LevelDebug,
67 }),
68 )
69
70 am := auth.NewAuthManager(logger)
71
72 // Handle static assets
73 fs := http.FileServer(http.FS(content))
74 http.Handle("/static/", fs)
75
76 // Serve pages
77 http.Handle("/", ServeRoot(am))
78
79 // Handle authentication
80 http.Handle("/login", am.HandleLogin())
81 http.Handle("/logout", am.HandleLogout())
82 http.Handle("/callback", am.HandleCallback())
83
84 mw := logging.Middleware(
85 logging.WithLogger(logger),
86 logging.WithGroup("server"),
87 logging.WithIDFunc(func() slog.Attr {
88 return slog.String("id", gonanoid.Must())
89 }),
90 )
91
92 log.Println("Listening on port 4000")
93 if err := http.ListenAndServe(":4000", mw(http.DefaultServeMux)); err != nil {
94 panic(err)
95 }
96}