An experimental IndieWeb site built in Go.
1package main
2
3//go:generate templ generate
4//go:generate deno run --allow-all npm:tailwindcss -i config/main.css -o static/styles.css -c config/tailwind.config.ts --minify
5
6import (
7 "os"
8 "time"
9
10 "log"
11 "net/http"
12 "strconv"
13
14 "github.com/puregarlic/space/handlers"
15 "github.com/puregarlic/space/models"
16 "github.com/puregarlic/space/storage"
17
18 "github.com/go-chi/chi/v5"
19 "github.com/go-chi/chi/v5/middleware"
20 "github.com/go-chi/cors"
21 "github.com/go-chi/httprate"
22
23 "go.hacdias.com/indielib/indieauth"
24
25 _ "github.com/joho/godotenv/autoload"
26)
27
28func main() {
29 port, profileURL := validateRuntimeConfiguration()
30 defer storage.CleanupCaches()
31
32 storage.GORM().AutoMigrate(&models.Post{})
33
34 r := chi.NewRouter()
35
36 r.Use(middleware.RequestID)
37 r.Use(middleware.RealIP)
38 r.Use(middleware.Logger)
39 r.Use(middleware.Recoverer)
40 r.Use(httprate.LimitByIP(100, 1*time.Minute))
41
42 // CORS be enabled for browser-based agents to fetch `rel` elements.
43 // We'll enable it just on the root route since it should be used as the profile URL
44 r.With(cors.AllowAll().Handler).Get("/", handlers.ServeHomePage)
45
46 // Content pages
47 r.Get("/posts/{slug}", handlers.ServePostPage)
48
49 // Static asset handlers
50 r.Get("/media/*", handlers.ServeMedia)
51 r.Get("/static/*", http.StripPrefix(
52 "/static",
53 http.FileServer(http.Dir("static")),
54 ).ServeHTTP)
55
56 // Service handlers
57 handlers.AttachIndieAuth(r, "/authorization", profileURL)
58 handlers.AttachMicropub(r, "/micropub", profileURL)
59
60 // Start it!
61 log.Printf("Listening on http://localhost:%d", port)
62 log.Printf("Listening on %s", profileURL)
63 if err := http.ListenAndServe(":"+strconv.Itoa(port), r); err != nil {
64 log.Fatal(err)
65 }
66}
67
68func validateRuntimeConfiguration() (portNumber int, profileURL string) {
69 var port int
70 if portStr, ok := os.LookupEnv("PORT"); !ok {
71 port = 80
72 } else {
73 portInt, err := strconv.Atoi(portStr)
74 if err != nil {
75 log.Fatal(err)
76 }
77
78 port = portInt
79 }
80
81 profileURL, ok := os.LookupEnv("PROFILE_URL")
82 if !ok {
83 profileURL = "http://localhost/"
84 }
85
86 // Validate the given Client ID before starting the HTTP server.
87 err := indieauth.IsValidProfileURL(profileURL)
88 if err != nil {
89 log.Fatal(err)
90 }
91
92 return port, profileURL
93}