package main import ( "fmt" "log" "net/http" "sync" "time" "git.makerforce.io/photos/photos/internal/httphelpers" lib "git.makerforce.io/photos/photos/pkg/bucket" "git.makerforce.io/photos/photos/pkg/credentials" "git.makerforce.io/photos/photos/pkg/signer" "github.com/davidbyttow/govips/pkg/vips" ) var creds *credentials.Client var sig *signer.Signer type resizeOperation struct { input []byte previewOption lib.PreviewOption output []byte err error took time.Duration } func main() { var err error // Setup bucket credential source creds, err = credentials.NewClientFromEnv() if err != nil { panic(err) } // Setup bucket signer sig, err = signer.NewSignerFromEnv() if err != nil { panic(err) } transport := &http.Transport{ MaxIdleConns: 4, MaxIdleConnsPerHost: 4, IdleConnTimeout: 60 * time.Second, DisableCompression: true, } http.DefaultClient = &http.Client{ Transport: transport, Timeout: 60 * time.Second, } // Setup vips vips.Startup(nil) defer vips.Shutdown() server := &http.Server{ Addr: ":8003", ReadTimeout: 5 * time.Second, WriteTimeout: 60 * time.Second, } http.HandleFunc("/update", update) err = server.ListenAndServe() if err != nil { panic(err) } } func update(w http.ResponseWriter, req *http.Request) { var err error if req.Method != http.MethodPost { err := fmt.Errorf("%w: %v", httphelpers.ErrorMethodNotAllowed, req.Method) httphelpers.ErrorResponse(w, err) return } bucket := lib.Bucket(req.FormValue("bucket")) photo := lib.Photo(req.FormValue("photo")) err = bucket.Validate() if err != nil { err := fmt.Errorf("%w: %v", httphelpers.ErrorBadRequest, err.Error()) httphelpers.ErrorResponse(w, err) return } err = photo.Validate() if err != nil { err := fmt.Errorf("%w: %v", httphelpers.ErrorBadRequest, err.Error()) httphelpers.ErrorResponse(w, err) return } // Obtain bucket credentials cred, err := creds.Get(bucket) if err != nil { err := fmt.Errorf("%w: error getting credentials: %v", httphelpers.ErrorBadRequest, err.Error()) httphelpers.ErrorResponse(w, err) return } log.Printf("Got credentials") // Obtain preview options /* bucketMetadata, err := client.GetBucketMetadata(bucket) previewOptions := bucketMetadata.PreviewOptions */ previewOptions := lib.DefaultPreviewOptions() log.Printf("Preview options: %v", previewOptions) // Get photo original, err := getPhoto(bucket, cred, photo) if err != nil { httphelpers.ErrorResponse(w, err) return } log.Printf("Original photo filesize: %v", len(original)) // First, extract photo size width, height, err := readsize(original) if err != nil { httphelpers.ErrorResponse(w, err) return } s := lib.PhotoMetadataSize{ Width: width, Height: height, } log.Printf("Photo size: %v", s) err = putPhotoMetadataSize(bucket, cred, photo, s) if err != nil { httphelpers.ErrorResponse(w, err) return } // Do other resizes log.Printf("Starting resizes") resizes := make([]resizeOperation, len(previewOptions)) for i := range resizes { resizes[i] = resizeOperation{ input: original, previewOption: previewOptions[i], } } var wg sync.WaitGroup for i := range resizes { op := &resizes[i] wg.Add(1) // TODO: timeouts go func() { start := time.Now() log.Printf("Resizing photo %s at h%dq%d", photo, op.previewOption.Height, op.previewOption.Quality) output, err := resize( op.input, op.previewOption.Height, toVipsImageType(op.previewOption.Format), op.previewOption.Quality, ) op.output = output op.err = err end := time.Now() op.took = end.Sub(start) wg.Done() }() } wg.Wait() // Check for errors for _, op := range resizes { if op.err != nil { httphelpers.ErrorResponse(w, err) return } } // Save for i := range resizes { op := &resizes[i] wg.Add(1) go func() { preview := photo.GetPreview(op.previewOption) err := putPreview(bucket, cred, preview, op.output) log.Printf("Uploaded photo %s at h%dq%d: %v", photo, op.previewOption.Height, op.previewOption.Quality, err) op.err = err wg.Done() }() } wg.Wait() // Check for errors for _, op := range resizes { if op.err != nil { httphelpers.ErrorResponse(w, err) return } } timings := make([]time.Duration, 0) for _, op := range resizes { timings = append(timings, op.took) } log.Printf("Resizes took: %v", timings) w.Header().Add("X-Debug-Resize-Timings", fmt.Sprintf("%v", timings)) }