package webserver import ( "crypto/tls" "database/sql" "fmt" "html/template" "io/fs" "log/slog" "net/http" "time" "git.wntrmute.dev/kyle/eng-pad-server/web" "github.com/go-chi/chi/v5" ) type Config struct { Addr string DB *sql.DB BaseURL string TLSCert string TLSKey string } type WebServer struct { db *sql.DB baseURL string tmpl *template.Template } func Start(cfg Config) (*http.Server, error) { templateFS, err := fs.Sub(web.Content, "templates") if err != nil { return nil, fmt.Errorf("template fs: %w", err) } tmpl, err := template.ParseFS(templateFS, "*.html") if err != nil { return nil, fmt.Errorf("parse templates: %w", err) } ws := &WebServer{ db: cfg.DB, baseURL: cfg.BaseURL, tmpl: tmpl, } r := chi.NewRouter() // Static files staticFS, _ := fs.Sub(web.Content, "static") r.Handle("/static/*", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS)))) // Public routes r.Get("/login", ws.handleLoginPage) r.Post("/login", ws.handleLoginSubmit) // Share routes (no auth) r.Get("/s/{token}", ws.handleShareNotebook) r.Get("/s/{token}/pages/{num}", ws.handleSharePage) // Authenticated routes r.Group(func(r chi.Router) { r.Use(ws.authMiddleware) r.Get("/", http.RedirectHandler("/notebooks", http.StatusFound).ServeHTTP) r.Get("/notebooks", ws.handleNotebooks) r.Get("/notebooks/{id}", ws.handleNotebook) r.Get("/notebooks/{id}/pages/{num}", ws.handlePage) r.Get("/logout", ws.handleLogout) }) srv := &http.Server{ Addr: cfg.Addr, Handler: r, ReadTimeout: 30 * time.Second, WriteTimeout: 30 * time.Second, IdleTimeout: 120 * time.Second, } if cfg.TLSCert != "" && cfg.TLSKey != "" { tlsCert, err := tls.LoadX509KeyPair(cfg.TLSCert, cfg.TLSKey) if err != nil { return nil, fmt.Errorf("load TLS cert: %w", err) } srv.TLSConfig = &tls.Config{ Certificates: []tls.Certificate{tlsCert}, MinVersion: tls.VersionTLS13, } slog.Info("web UI started", "addr", cfg.Addr, "tls", true) go func() { _ = srv.ListenAndServeTLS("", "") }() } else { slog.Info("web UI started", "addr", cfg.Addr, "tls", false) go func() { _ = srv.ListenAndServe() }() } return srv, nil }