func BucketDeleteItem(ctx context.Context, w http.ResponseWriter, r *http.Request) { bucket, err := NewBucket(chi.URLParams(ctx)["bucket"]) if err != nil { respond.JSON(w, 422, map[string]interface{}{"error": err.Error()}) return } pUrl := r.URL.Query().Get("url") if pUrl != "" { pKey := sha1Hash(pUrl) // transform to what is expected.. chi.URLParams(ctx)["key"] = pKey } imageKey := chi.URLParams(ctx)["key"] if imageKey == "" { respond.JSON(w, 422, map[string]interface{}{ "error": "Unable to determine the key for the delete operation", }) return } err = bucket.DbDelImage(ctx, imageKey) if err != nil { respond.JSON(w, 422, map[string]interface{}{"error": err.Error()}) return } respond.JSON(w, 200, []byte{}) }
func BucketAddItems(ctx context.Context, w http.ResponseWriter, r *http.Request) { bucket, err := NewBucket(chi.URLParams(ctx)["bucket"]) if err != nil { respond.JSON(w, 422, map[string]interface{}{"error": err.Error()}) return } urls := r.URL.Query()["url[]"] urls = append(urls, r.URL.Query()["url"]...) if len(urls) == 0 { respond.JSON(w, 422, map[string]interface{}{"error": "Url or urls parameter required"}) return } images, err := bucket.AddImagesFromUrls(ctx, urls) if err != nil { // TODO: refactor.. ApiError will cache invalid image errors, // but for an array of urls, we shouldn't cache the entire response if len(urls) == 1 { respond.ApiError(w, 422, err) } else { respond.JSON(w, 422, map[string]interface{}{"error": err.Error()}) } return } respond.JSON(w, 200, images) }
// Set the parent context in the middleware chain to something else. Useful // in the instance of having a global server context to signal all requests. func ParentContext(parent context.Context) func(next chi.Handler) chi.Handler { return func(next chi.Handler) chi.Handler { fn := func(ctx context.Context, w http.ResponseWriter, r *http.Request) { pctx := context.WithValue(parent, chi.URLParamsCtxKey, chi.URLParams(ctx)) next.ServeHTTPC(pctx, w, r) } return chi.HandlerFunc(fn) } }
func BucketFetchItem(ctx context.Context, w http.ResponseWriter, r *http.Request) { bucket, err := NewBucket(chi.URLParams(ctx)["bucket"]) if err != nil { respond.ImageError(w, 422, err) return } fetchUrl := r.URL.Query().Get("url") if fetchUrl == "" { respond.ImageError(w, 422, ErrInvalidURL) return } u, err := urlx.Parse(fetchUrl) if err != nil { respond.ImageError(w, 422, ErrInvalidURL) return } fetchUrl = u.String() imKey := sha1Hash(fetchUrl) // transform to what is expected.. chi.URLParams(ctx)["key"] = imKey // First check if we have the original.. a bit of extra overhead, but its okay _, err = bucket.DbFindImage(ctx, imKey, nil) if err != nil && err != ErrImageNotFound { respond.ImageError(w, 422, err) return } // Fetch the image on-demand and add to bucket if we dont have it if err == ErrImageNotFound { // TODO: add image sizing throttler here.... _, err := bucket.AddImagesFromUrls(ctx, []string{fetchUrl}) if err != nil { lg.Errorf("Fetching failed for %s because %s", fetchUrl, err) respond.ImageError(w, 422, err) return } } BucketGetItem(ctx, w, r) }
//GetParamValueAsID accepts the context and try to parse an id into int64 func GetParamValueAsID(ctx context.Context, param string) (int64, error) { value := chi.URLParams(ctx)[param] id, err := strconv.ParseInt(value, 10, 64) if err != nil { return -1, err } return id, nil }
func ArticleCtx(next chi.Handler) chi.Handler { return chi.HandlerFunc(func(ctx context.Context, w http.ResponseWriter, r *http.Request) { articleID := chi.URLParams(ctx)["articleID"] article, err := dbGetArticle(articleID) if err != nil { http.Error(w, http.StatusText(404), 404) return } ctx = context.WithValue(ctx, "article", article) next.ServeHTTPC(ctx, w, r) }) }
func initChi() { h := chi.NewRouter() h.Get("/", func(ctx context.Context, w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") fmt.Fprintf(w, "Hello, World") }) h.Get("/:name", func(ctx context.Context, w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") fmt.Fprintf(w, "Hello, %s", chi.URLParams(ctx)["name"]) }) registerHandler("chi", h) }
func BucketGetItem(ctx context.Context, w http.ResponseWriter, r *http.Request) { // TODO: bucket binding should happen in a middleware... refactor all handlers // that use it.. bucket, err := NewBucket(chi.URLParams(ctx)["bucket"]) if err != nil { lg.Errorf("Failed to create bucket for %s cause: %s", r.URL, err) respond.ImageError(w, 422, err) return } sizing, err := imgry.NewSizingFromQuery(r.URL.RawQuery) if err != nil { lg.Errorf("Failed to create sizing for %s cause: %s", r.URL, err) respond.ImageError(w, 422, err) return } im, err := bucket.GetImageSize(ctx, chi.URLParams(ctx)["key"], sizing) if err != nil { lg.Errorf("Failed to get image for %s cause: %s", r.URL, err) respond.ImageError(w, 422, err) return } w.Header().Set("Content-Type", im.MimeType()) w.Header().Set("X-Meta-Width", fmt.Sprintf("%d", im.Width)) w.Header().Set("X-Meta-Height", fmt.Sprintf("%d", im.Height)) w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%d", app.Config.CacheMaxAge)) // If requested, only return the image details instead of the data if r.URL.Query().Get("info") != "" { // TODO: eventually, once the ruby stack is updated, we should // return an ImageInfo packet here instead.. respond.JSON(w, http.StatusOK, im) return } respond.Data(w, 200, im.Data) }
// A completely separate router for administrator routes func adminRouter() http.Handler { // or chi.Router { r := chi.NewRouter() r.Use(AdminOnly) r.Get("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("admin: index")) }) r.Get("/accounts", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("admin: list accounts..")) }) r.Get("/users/:userId", func(ctx context.Context, w http.ResponseWriter, r *http.Request) { w.Write([]byte(fmt.Sprintf("admin: view user id %v", chi.URLParams(ctx)["userId"]))) }) return r }
// DEPRECATED func BucketV0FetchItem(ctx context.Context, w http.ResponseWriter, r *http.Request) { chi.URLParams(ctx)["bucket"] = "tmp" // we imply the bucket name.. BucketFetchItem(ctx, w, r) }
// Image upload to an s3 bucket, respond with a direct url to the uploaded // image. Avoid using respond.ApiError() here to prevent any of the responses // from being cached. func BucketImageUpload(ctx context.Context, w http.ResponseWriter, r *http.Request) { var url string var err error var data []byte var im *Image file, header, err := r.FormFile("file") switch err { case nil: defer file.Close() data, err = ioutil.ReadAll(file) if err != nil { respond.JSON(w, 422, map[string]interface{}{"error": err.Error()}) return } im = NewImageFromSrcUrl(header.Filename) case http.ErrMissingFile: base64file := r.FormValue("base64file") fileLen := len(base64file) if fileLen < 100 { respond.JSON(w, 422, map[string]interface{}{"error": "invalid file upload"}) return } data, err = base64.StdEncoding.DecodeString(base64file) if err != nil { respond.JSON(w, 422, map[string]interface{}{"error": err.Error()}) return } // balance collision chance vs hash time if fileLen > 10000 { fileLen = 10000 } im = NewImageFromSrcUrl(string(base64file[0:fileLen])) default: respond.JSON(w, 422, map[string]interface{}{"error": err.Error()}) return } defer im.Release() im.Data = data if err = im.LoadImage(); err != nil { respond.JSON(w, 422, map[string]interface{}{"error": err.Error()}) return } s3Bucket := getS3Bucket(app.Config.Chainstore.S3AccessKey, app.Config.Chainstore.S3SecretKey, app.Config.Chainstore.S3Bucket) path := s3Path(chi.URLParams(ctx)["bucket"], im.Data, im.Format) url, err = s3Upload(s3Bucket, path, im) if err != nil { respond.JSON(w, 422, map[string]interface{}{"error": err.Error()}) return } imfo := imgry.ImageInfo{ URL: url, Format: im.Format, Mimetype: im.MimeType(), Width: im.Width, Height: im.Height, AspectRatio: float64(im.Width) / float64(im.Height), ContentLength: len(im.Data), } respond.JSON(w, 200, imfo) }
func CreateJob(ctx context.Context, w http.ResponseWriter, r *http.Request) { // Low, high and urgent priorities only (high is default). priority := r.URL.Query().Get("priority") switch priority { case "low", "high", "urgent": // NOP. case "": priority = "high" default: http.Error(w, "unknown priority \""+priority+"\"", 422) return } // Decode request data. var req *api.ScriptsRequest err := json.NewDecoder(r.Body).Decode(&req) if err != nil { http.Error(w, "parse request body: "+err.Error(), 422) return } req.Script = chi.URLParams(ctx)["filename"] // Make sure ASYNC callback is valid URL. if req.CallbackURL != "" { req.CallbackURL, err = urlx.NormalizeString(req.CallbackURL) if err != nil { http.Error(w, "parse request body: "+err.Error(), 422) return } } // Enqueue the request. data, err := json.Marshal(req) if err != nil { http.Error(w, err.Error(), 500) return } lg.Debugf("Handler:\tEnqueue \"%v\" request", priority) job, err := Qmd.Enqueue(string(data), priority) if err != nil { http.Error(w, err.Error(), 500) return } // Async. if req.CallbackURL != "" { resp, _ := Qmd.GetAsyncResponse(req, job.ID) w.Write(resp) lg.Debugf("Handler:\tResponded with job %s ASYNC result", job.ID) go func() { //TODO: Retry callback if it failed? err := Qmd.PostResponseCallback(req, job.ID) if err != nil { lg.Errorf("can't post callback to %v", err) } }() return } // Sync. lg.Debugf("Handler:\tWaiting for job %s", job.ID) resp, _ := Qmd.GetResponse(job.ID) w.Write(resp) lg.Debugf("Handler:\tResponded with job %s result", job.ID) // // Kill the job, if client closes the connection before // // it receives the data. // done := make(chan struct{}) // defer close(done) // connClosed := w.(http.CloseNotifier).CloseNotify() // go func() { // select { // case <-connClosed: // job.Kill() // case <-done: // } // }() }
func getAccount(ctx context.Context, w http.ResponseWriter, r *http.Request) { accountID := chi.URLParams(ctx)["accountID"] account := ctx.Value("account").(string) w.Write([]byte(fmt.Sprintf("get account id:%s details:%s", accountID, account))) }