90 lines
1.8 KiB
Go
90 lines
1.8 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"embed"
|
|
"errors"
|
|
"io/fs"
|
|
"log/slog"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/nmoniz/any2anexoj/internal/web"
|
|
"golang.org/x/sync/errgroup"
|
|
)
|
|
|
|
//go:embed all:static
|
|
var staticFS embed.FS
|
|
|
|
func main() {
|
|
if err := run(); err != nil {
|
|
slog.Error("any2anexoj-web: fatal error", slog.Any("err", err))
|
|
os.Exit(1)
|
|
}
|
|
slog.Info("shutdown gracefully")
|
|
}
|
|
|
|
// run wires up the server, installs signal-driven cancellation, and blocks on
|
|
// the HTTP server until it stops. It is split out from main so tests can drive
|
|
// the same code paths if needed.
|
|
func run() error {
|
|
addr := os.Getenv("PORT")
|
|
if addr == "" {
|
|
addr = ":8080"
|
|
}
|
|
// A bare "8080" value is friendlier than the net.Listen-style ":8080".
|
|
if addr[0] != ':' {
|
|
addr = ":" + addr
|
|
}
|
|
|
|
// Sub-mount the embedded FS so the static subtree is rooted at "" inside
|
|
// the web package (matches /static/...).
|
|
staticSub, err := fs.Sub(staticFS, "static")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
state := web.NewState()
|
|
srv, err := web.NewServer(state, staticSub)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
httpServer := &http.Server{
|
|
Addr: addr,
|
|
Handler: srv.Handler(),
|
|
ReadHeaderTimeout: 10 * time.Second,
|
|
}
|
|
|
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Kill, os.Interrupt, syscall.SIGTERM)
|
|
defer cancel()
|
|
|
|
eg, ctx := errgroup.WithContext(ctx)
|
|
|
|
eg.Go(func() error {
|
|
slog.Info("listening for http", slog.Any("port", addr))
|
|
err := httpServer.ListenAndServe()
|
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
|
|
eg.Go(func() error {
|
|
<-ctx.Done()
|
|
|
|
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer shutdownCancel()
|
|
|
|
if err := httpServer.Shutdown(shutdownCtx); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
|
|
return eg.Wait()
|
|
}
|