package main import ( "context" "crypto/rand" "flag" "fmt" "log" "log/slog" "net" "net/http" "os" "github.com/go-chi/chi/v5" "git.wntrmute.dev/mc/mcdsl/auth" "git.wntrmute.dev/mc/mcdsl/csrf" "git.wntrmute.dev/mc/mcdsl/web" "git.wntrmute.dev/kyle/goutils/config" "git.wntrmute.dev/kyle/goutils/die" "git.wntrmute.dev/kyle/kls/links" ) const cookieName = "kls_session" var defaultConfigFile = config.DefaultConfigPath("kls", "kls.conf") func main() { cfgFile := flag.String("f", defaultConfigFile, "`path` to config file") flag.Parse() err := config.LoadFile(*cfgFile) die.If(err) logger := slog.New(slog.NewTextHandler(os.Stderr, nil)) ctx := context.Background() db, err := links.Connect(ctx) die.If(err) // MCIAS authenticator. authenticator, err := auth.New(auth.Config{ ServerURL: config.Get("MCIAS_SERVER_URL"), CACert: config.Get("MCIAS_CA_CERT"), ServiceName: config.GetDefault("MCIAS_SERVICE_NAME", "kls"), }, logger) die.If(err) // CSRF protection. csrfSecret := make([]byte, 32) if _, err := rand.Read(csrfSecret); err != nil { die.If(fmt.Errorf("generate CSRF secret: %w", err)) } csrfProtect := csrf.New(csrfSecret, "_csrf", "csrf_token") srv := &server{db: db, auth: authenticator, csrf: csrfProtect} r := chi.NewRouter() // Public: short code redirects (no auth). r.Get("/{short:[a-zA-Z0-9]+}", srv.redirect) // Public: login page. r.Get("/login", srv.handleLoginPage) r.Post("/login", csrfProtect.Middleware(http.HandlerFunc(srv.handleLogin)).ServeHTTP) // Static files. r.Get("/static/*", http.FileServer(http.FS(staticFiles)).ServeHTTP) // Authenticated routes. r.Group(func(r chi.Router) { r.Use(web.RequireAuth(authenticator, cookieName, "/login")) r.Use(csrfProtect.Middleware) r.Get("/", srv.handleIndex) r.Post("/", srv.postURL) r.Get("/list", srv.listAll) r.Post("/logout", srv.handleLogout) }) // $PORT is set by the MCP agent for containers with route declarations. // It takes precedence over config, matching the mcdsl convention. port := os.Getenv("PORT") if port == "" { port = config.GetDefault("HTTP_PORT", "8000") } addr := net.JoinHostPort(config.Get("HTTP_ADDR"), port) log.Print("listening on ", addr) log.Fatal(http.ListenAndServe(addr, r)) }