about summary refs log tree commit diff stats
path: root/server.go
diff options
context:
space:
mode:
authorAlan Pearce2024-04-17 22:39:45 +0200
committerAlan Pearce2024-04-18 09:14:23 +0200
commit22a2e624602212cf68b40d9cab54164c7051210c (patch)
treefab7dea20da38b2a1cdc00a5440da8285804d79e /server.go
parentc1d191576c80dada81671834539ed93ebcb63461 (diff)
downloadwebsite-22a2e624602212cf68b40d9cab54164c7051210c.tar.lz
website-22a2e624602212cf68b40d9cab54164c7051210c.tar.zst
website-22a2e624602212cf68b40d9cab54164c7051210c.zip
embed files
Diffstat (limited to 'server.go')
-rw-r--r--server.go148
1 files changed, 148 insertions, 0 deletions
diff --git a/server.go b/server.go
new file mode 100644
index 0000000..2a1eed0
--- /dev/null
+++ b/server.go
@@ -0,0 +1,148 @@
+package main
+
+import (
+	"embed"
+	"fmt"
+	"log"
+	"net/http"
+	"os"
+	"time"
+	"website/internal/config"
+
+	"github.com/ansrivas/fiberprometheus/v2"
+	"github.com/getsentry/sentry-go"
+	"github.com/gofiber/contrib/fibersentry"
+	"github.com/gofiber/fiber/v2"
+	"github.com/gofiber/fiber/v2/middleware/cache"
+	"github.com/gofiber/fiber/v2/middleware/compress"
+	"github.com/gofiber/fiber/v2/middleware/etag"
+	"github.com/gofiber/fiber/v2/middleware/filesystem"
+	"github.com/gofiber/fiber/v2/middleware/healthcheck"
+	"github.com/gofiber/fiber/v2/middleware/logger"
+	"github.com/gofiber/fiber/v2/middleware/recover"
+
+	"github.com/shengyanli1982/law"
+)
+
+// TODO prettify HTML
+// TODO purge CSS
+// TODO HTTP2 https://github.com/dgrr/http2
+
+type Host struct {
+	Fiber *fiber.App
+}
+
+//go:embed all:public/*
+var fs embed.FS
+
+func main() {
+	err := sentry.Init(sentry.ClientOptions{
+		Dsn:         os.Getenv("SENTRY_DSN"),
+		Release:     os.Getenv("FLY_MACHINE_VERSION"),
+		Environment: os.Getenv("ENV"),
+	})
+	if err != nil {
+		log.Panic("could not set up sentry")
+	}
+	defer sentry.Flush(2 * time.Second)
+
+	metricServer := fiber.New(fiber.Config{
+		GETOnly:                  true,
+		StrictRouting:            true,
+		DisableDefaultDate:       true,
+		DisableHeaderNormalizing: true,
+		DisableStartupMessage:    true,
+	})
+	prometheus := fiberprometheus.New("homestead")
+	prometheus.RegisterAt(metricServer, "/metrics")
+
+	hosts := map[string]*Host{}
+	config, err := config.GetConfig()
+	if err != nil {
+		log.Panic("config error", err)
+	}
+
+	internal := fiber.New(fiber.Config{
+		GETOnly:       true,
+		StrictRouting: true,
+	})
+	internal.Use(healthcheck.New(healthcheck.Config{}))
+	hosts["fly-internal"] = &Host{internal}
+
+	website := fiber.New(fiber.Config{
+		EnableTrustedProxyCheck: true,
+		TrustedProxies:          []string{"172.16.0.0/16"},
+		ProxyHeader:             "Fly-Client-IP",
+		GETOnly:                 true,
+		ReadTimeout:             5 * time.Minute,
+		WriteTimeout:            5 * time.Minute,
+		ServerHeader:            "Fiber",
+		StrictRouting:           true,
+		UnescapePath:            true,
+	})
+
+	website.Use(prometheus.Middleware)
+	website.Use(fibersentry.New(fibersentry.Config{}))
+
+	website.Use(compress.New())
+	website.Use(cache.New(cache.Config{
+		CacheControl:         true,
+		Expiration:           24 * time.Hour,
+		StoreResponseHeaders: true,
+	}))
+	// must be after compress to be encoding-independent
+	website.Use(etag.New(etag.Config{
+		Weak: true,
+	}))
+
+	website.Use(recover.New(recover.Config{}))
+
+	files := http.FS(fs)
+	notFoundHandler := func(c *fiber.Ctx) error {
+		c.Status(fiber.StatusNotFound).Type("html", "utf-8")
+		content, err := fs.ReadFile("public/404.html")
+		if err != nil {
+			content = []byte("404 Not Found")
+			c.Type("txt")
+		}
+		return c.Send(content)
+	}
+	website.Get("/404.html", notFoundHandler)
+	website.Use("/", filesystem.New(filesystem.Config{
+		Root:               files,
+		PathPrefix:         "public",
+		ContentTypeCharset: "utf-8",
+		MaxAge:             int((24 * time.Hour).Seconds()),
+	}))
+	website.Use(notFoundHandler)
+	hosts[config.BaseURL.Host] = &Host{website}
+
+	toplevel := fiber.New()
+	toplevel.Get("/health", func(c *fiber.Ctx) error {
+		return c.SendStatus(fiber.StatusOK)
+	})
+	toplevel.Use(logger.New(logger.Config{
+		Output: law.NewWriteAsyncer(os.Stdout, nil),
+		Format: "${protocol} ${method} ${status} ${host} ${url} ${respHeader:Location}\n",
+	}))
+	toplevel.Use(func(c *fiber.Ctx) error {
+		host := hosts[c.Hostname()]
+		if host == nil {
+			if config.RedirectOtherHostnames {
+				return c.Redirect(config.BaseURL.JoinPath(c.OriginalURL()).String())
+			} else {
+				hosts[config.BaseURL.Host].Fiber.Handler()(c.Context())
+				return nil
+			}
+		} else {
+			host.Fiber.Handler()(c.Context())
+			return nil
+		}
+	})
+
+	go func() {
+		err := metricServer.Listen(":9091")
+		log.Printf("failed to start metrics server: %v", err)
+	}()
+	log.Fatal(toplevel.Listen(fmt.Sprintf("%s:%d", "", config.Port)))
+}