package main import ( "fmt" "html/template" "log" "log/slog" "net/http" "os" "strconv" "github.com/dreamsofcode-io/scrape-me/internal/middleware" "gopkg.in/yaml.v3" ) type Config struct { Pages []Page `yaml:"pages"` Status []Status `yaml:"statuses"` Redirects []Redirect `yaml:"redirects"` } type Link struct { Name string `yaml:"name"` URL string `yaml:"url"` } type Redirect struct { Path string `yaml:"path"` To string `yaml:"to"` } type Page struct { Path string `yaml:"path"` Title string `yaml:"title"` Links []Link `yaml:"links"` Extra *Link `yaml:"extra"` } type Status struct { Path string `yaml:"path"` StatusCode int `yaml:"status"` } type wrappedWriter struct { http.ResponseWriter statusCode int } func (w *wrappedWriter) WriteHeader(statusCode int) { w.ResponseWriter.WriteHeader(statusCode) w.statusCode = statusCode } func handleError(tmpl *template.Template, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { wrapped := &wrappedWriter{ ResponseWriter: w, statusCode: http.StatusOK, } next.ServeHTTP(wrapped, r) if wrapped.statusCode >= 400 { tmpl.ExecuteTemplate(w, "error.html", map[string]string{ "ErrorMessage": http.StatusText(wrapped.statusCode), "Status": strconv.Itoa(wrapped.statusCode), }) } }) } func main() { logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{})) files := http.FileServer(http.Dir("./static")) r := http.NewServeMux() r.Handle("GET /static/", http.StripPrefix("/static", files)) tmpl := template.Must(template.New("").ParseGlob("./templates/*")) r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) }) cfgFile, err := os.ReadFile("./config.yaml") if err != nil { log.Fatalln("failed to read config file") } var cfg Config if err := yaml.Unmarshal(cfgFile, &cfg); err != nil { log.Fatalln("failed to decode file", err) } for _, page := range cfg.Pages { r.HandleFunc( fmt.Sprintf("GET %s", page.Path), func(w http.ResponseWriter, r *http.Request) { tmpl.ExecuteTemplate(w, "index.html", page) }, ) } for _, status := range cfg.Status { r.HandleFunc( fmt.Sprintf("GET %s", status.Path), func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(status.StatusCode) }, ) } for _, redirect := range cfg.Redirects { r.HandleFunc( fmt.Sprintf("GET %s", redirect.Path), func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, redirect.To, http.StatusFound) }, ) } s := http.Server{ Addr: ":8080", Handler: middleware.NoCache( middleware.Logging(logger, handleError(tmpl, r)), ), } logger.Info("Server starting", slog.String("port", "8080")) s.ListenAndServe() }