diff options
| -rw-r--r-- | cmd/home/main.go | 196 | ||||
| -rw-r--r-- | go.mod | 2 | ||||
| -rw-r--r-- | go.sum | 4 | ||||
| -rw-r--r-- | web/web.go | 46 |
4 files changed, 187 insertions, 61 deletions
diff --git a/cmd/home/main.go b/cmd/home/main.go index 3f04b39..6854299 100644 --- a/cmd/home/main.go +++ b/cmd/home/main.go @@ -1,32 +1,198 @@ package main import ( + "errors" + "fmt" + "log" + "net/http" "os" - "os/signal" - "syscall" + "path" + "path/filepath" + "strings" + "time" - "git.sr.ht/~a73x/home/web" + "git.sr.ht/~a73x/home/pages" + "github.com/fsnotify/fsnotify" "go.uber.org/zap" ) -func main() { - logger, _ := zap.NewProduction() - server, err := web.New(logger) +func Build(directory string) error { + pages, err := pages.Collect(directory) + if err != nil { + return err + } + + var errs []error + for _, page := range pages { + fmt.Println("building", page.Path) + err = writeFile(path.Join("public", page.Path), []byte(page.Content)) + if err != nil { + errs = append(errs, err) + } + } + + if errs != nil { + return errors.Join(errs...) + } + + return nil +} + +func Serve() error { + logger, err := zap.NewProduction() if err != nil { - logger.Fatal("failed to start webserver", zap.Error(err)) + return err + } + + loggingMiddleware := func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + next.ServeHTTP(w, r) + logger.Info("request received", + zap.String("url", r.URL.Path), + zap.String("method", r.Method), + zap.Duration("duration", time.Since(start)), + zap.String("user-agent", r.UserAgent()), + ) + }) + } + + mux := http.NewServeMux() + + mux.HandleFunc("GET /", serveFile) + + server := http.Server{ + Addr: ":8080", + Handler: loggingMiddleware(mux), + } + + return server.ListenAndServe() +} + +func serveFile(w http.ResponseWriter, r *http.Request) { + fsPath := strings.TrimRight(r.URL.Path, "/") + + if fsPath == "" { + fsPath = "index" + } + + if ext := filepath.Ext(fsPath); ext == "" { + fsPath += ".html" } - done := make(chan os.Signal, 0) - signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) - logger.Info("Starting web server") + http.ServeFile(w, r, path.Join("public", fsPath)) +} + +func watchDir(watchDir string) error { + // Directory to watch + + // Ensure the directory exists + if _, err := os.Stat(watchDir); os.IsNotExist(err) { + return err + } + // Create a new watcher + watcher, err := fsnotify.NewWatcher() + if err != nil { + return err + } + + // Start a goroutine to process events go func() { - if err := server.ListenAndServe(); err != nil { - logger.Error("fail running web server", zap.Error(err)) + defer watcher.Close() + + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + log.Printf("Event: %s", event) + + // Trigger a command when a change is detected + if event.Op&fsnotify.Write == fsnotify.Write || + event.Op&fsnotify.Create == fsnotify.Create || + event.Op&fsnotify.Remove == fsnotify.Remove { + err := Build(watchDir) + if err != nil { + fmt.Println(err) + } else { + fmt.Println("built") + } + + } + + case err, ok := <-watcher.Errors: + if !ok { + return + } + log.Printf("Error: %v", err) + } } - done <- nil }() - <-done - logger.Info("Stopping web server") + // Add the directory to the watcher + err = filepath.Walk(watchDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + log.Printf("Watching directory: %s", path) + return watcher.Add(path) + } + return nil + }) + + if err != nil { + return err + } + + return nil +} +func Run() error { + contentDir := "content" + actualPath, err := filepath.EvalSymlinks(contentDir) + if err != nil { + return err + } + + if err := Build(actualPath); err != nil { + return err + } + + // watcher + if err := watchDir(actualPath); err != nil { + return err + } + + if err := Serve(); err != nil { + return err + } + + return nil +} + +func writeFile(name string, contents []byte) error { + folders := path.Dir(name) + _, err := os.Stat(folders) + if os.IsNotExist(err) { + if err := os.MkdirAll(folders, 0744); err != nil { + return fmt.Errorf("failed to mkdir %s\n%w", folders, err) + } + } else if err != nil { + return fmt.Errorf("failed to stat folder %s\n%w", folders, err) + } + + err = os.WriteFile(name, contents, 0666) + if err != nil { + return fmt.Errorf("failed to write file %s\n%w", name, err) + } + + return nil +} + +func main() { + if err := Run(); err != nil { + log.Panic(err) + } } @@ -5,6 +5,7 @@ go 1.23 require ( github.com/adrg/frontmatter v0.2.0 github.com/alecthomas/chroma v0.10.0 + github.com/fsnotify/fsnotify v1.8.0 github.com/gomarkdown/markdown v0.0.0-20240730141124-034f12af3bf6 go.uber.org/zap v1.27.0 ) @@ -14,5 +15,6 @@ require ( github.com/dlclark/regexp2 v1.4.0 // indirect github.com/stretchr/testify v1.9.0 // indirect go.uber.org/multierr v1.10.0 // indirect + golang.org/x/sys v0.13.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect ) @@ -10,6 +10,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gomarkdown/markdown v0.0.0-20240730141124-034f12af3bf6 h1:ZPy+2XJ8u0bB3sNFi+I72gMEMS7MTg7aZCCXPOjV8iw= github.com/gomarkdown/markdown v0.0.0-20240730141124-034f12af3bf6/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -24,6 +26,8 @@ go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= diff --git a/web/web.go b/web/web.go deleted file mode 100644 index 96f85d2..0000000 --- a/web/web.go +++ /dev/null @@ -1,46 +0,0 @@ -package web - -import ( - "net/http" - "time" - - "git.sr.ht/~a73x/home/pages" - "go.uber.org/zap" -) - -func New(logger *zap.Logger) (*http.Server, error) { - loggingMiddleware := func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - start := time.Now() - next.ServeHTTP(w, r) - logger.Info("request received", - zap.String("url", r.URL.Path), - zap.String("method", r.Method), - zap.Duration("duration", time.Since(start)), - zap.String("user-agent", r.UserAgent()), - ) - }) - } - - mux := http.NewServeMux() - pages, err := pages.Collect("./content") - if err != nil { - return nil, err - } - - staticFs := http.FileServer(http.Dir("./public/static")) - - mux.Handle("GET /static/", http.StripPrefix("/static/", staticFs)) - for _, page := range pages { - mux.HandleFunc("GET "+page.Path, func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(page.Content)) - }) - } - - server := http.Server{ - Addr: ":8080", - Handler: loggingMiddleware(mux), - } - - return &server, nil -} |
