package webserver import ( "database/sql" "fmt" "html/template" "io/fs" "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 } type WebServer struct { db *sql.DB baseURL string tmpl *template.Template } func Start(cfg Config) error { templateFS, err := fs.Sub(web.Content, "templates") if err != nil { return fmt.Errorf("template fs: %w", err) } tmpl, err := template.ParseFS(templateFS, "*.html") if err != nil { return 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, } fmt.Printf("Web UI listening on %s\n", cfg.Addr) return srv.ListenAndServe() }