package main import ( "crypto/rand" "encoding/json" "flag" "fmt" "io" "net/http" "os" "path" "sync/atomic" "time" "log/slog" "go.balki.me/collage-maker/collage" ) var ( imagesDir string collageDir string ) func main() { flag.StringVar(&imagesDir, "images-dir", "images", "Sets the images dir") flag.StringVar(&collageDir, "collages-dir", "collages", "Sets the collages dir") flag.Parse() nameGen := NewNameGen() imagesDirFs := os.DirFS(imagesDir) http.HandleFunc("/make-collage", func(w http.ResponseWriter, r *http.Request) { collageReq := collage.Request{} body, err := io.ReadAll(r.Body) if err != nil { slog.Error("failed to read request body", "error", err) return } if err := json.Unmarshal(body, &collageReq); err != nil { slog.Error("failed to unmarshal json request", "error", err) return } collageFile := fmt.Sprintf("collage-%s.jpg", nameGen.Next()) out, err := os.Create(path.Join(collageDir, collageFile)) if err != nil { slog.Error("failed to create collage output file", "error", err) } if err := collage.Make(collageReq, imagesDirFs, out); err != nil { slog.Error("failed to make collage", "error", err) return } w.Write([]byte(collageFile)) }) if err := http.ListenAndServe(":8767", nil); err != nil { slog.Error("http ListenAndServe failed", "error", err) } } type nameGen struct { prefix string counter atomic.Uint64 } func NewNameGen() *nameGen { currentTime := time.Now().Unix() randBytes := make([]byte, 8) _, err := rand.Read(randBytes) if err != nil { panic(err) } alpha := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ") uniqRunID := "" for _, b := range randBytes { uniqRunID += string(alpha[int(b)%len(alpha)]) } return &nameGen{ prefix: fmt.Sprintf("%d-%s", currentTime, uniqRunID), counter: atomic.Uint64{}, } } func (n *nameGen) Next() string { return fmt.Sprintf("%s-%d", n.prefix, n.counter.Add(1)) } // curl -D - --json @req.json http://localhost:8767/make-collage