diff options
Diffstat (limited to 'server.go')
-rw-r--r-- | server.go | 148 |
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))) +} |