about summary refs log tree commit diff stats
path: root/cmd/server
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/server')
-rw-r--r--cmd/server/server.go136
1 files changed, 136 insertions, 0 deletions
diff --git a/cmd/server/server.go b/cmd/server/server.go
new file mode 100644
index 0000000..1af6fdf
--- /dev/null
+++ b/cmd/server/server.go
@@ -0,0 +1,136 @@
+package main
+
+import (
+	"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
+}
+
+func main() {
+	err := sentry.Init(sentry.ClientOptions{
+		Dsn:         os.Getenv("SENTRY_DSN"),
+		Release:     os.Getenv("FLY_MACHINE_VERSION"),
+		Environment: os.Getenv("ENVIRONMENT"),
+	})
+	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{}))
+
+	notFoundHandler := func(c *fiber.Ctx) error {
+		return c.Status(fiber.StatusNotFound).SendFile("public/404.html")
+	}
+	website.Get("/404.html", notFoundHandler)
+	website.Use("/", filesystem.New(filesystem.Config{
+		Root:               http.Dir("./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)))
+}