func HandleCreateNote(ctx context.Context, w http.ResponseWriter, r *http.Request) { var note Note if err := json.NewDecoder(r.Body).Decode(¬e); err != nil { web.JSONErr(w, err.Error(), http.StatusBadRequest) return } acc, ok := auth.AuthRequired(pg.DB(ctx), w, r) if !ok { return } if errs := validateNote(¬e); len(errs) > 0 { web.JSONErrs(w, errs, http.StatusBadRequest) return } note.OwnerID = acc.AccountID n, err := CreateNote(pg.DB(ctx), note) if err != nil { log.Printf("cannot create note: %s", err) web.StdJSONErr(w, http.StatusInternalServerError) return } web.JSONResp(w, n, http.StatusCreated) }
func handleImageDetails(ctx context.Context, w http.ResponseWriter, r *http.Request) { db := sq.DB(ctx) img, err := ImageByID(db, web.Args(ctx).ByIndex(0)) switch err { case nil: // all good case sq.ErrNotFound: web.StdHTMLResp(w, http.StatusNotFound) return default: log.Error("cannot get image", "image", web.Args(ctx).ByIndex(0), "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } img.Tags, err = ImageTags(db, img.ImageID) if err != nil { log.Error("cannot get image", "image", web.Args(ctx).ByIndex(0), "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } web.JSONResp(w, img, http.StatusOK) }
func handleListBookmarks(ctx context.Context, w http.ResponseWriter, r *http.Request) { offset, _ := strconv.ParseInt(r.URL.Query().Get("offset"), 10, 64) if offset < 0 { offset = 0 } bookmarks := make([]*Bookmark, 0, 100) err := pg.DB(ctx).Select(&bookmarks, ` SELECT b.* FROM bookmarks b ORDER BY created DESC LIMIT $1 OFFSET $2 `, 500, offset) if err != nil { log.Error("cannot select bookmarks", "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } resp := struct { Bookmarks []*Bookmark `json:"bookmarks"` }{ Bookmarks: bookmarks, } web.JSONResp(w, resp, http.StatusOK) }
func HandlePasteUpdate(ctx context.Context, w http.ResponseWriter, r *http.Request) { var input struct { Content string } if err := json.NewDecoder(r.Body).Decode(&input); err != nil { web.StdJSONResp(w, http.StatusBadRequest) return } if input.Content == "" { web.JSONErr(w, `"Content" is required"`, http.StatusBadRequest) return } pid, _ := strconv.ParseInt(web.Args(ctx).ByIndex(0), 10, 64) db := pg.DB(ctx) paste, err := UpdatePaste(db, Paste{ ID: pid, Content: input.Content, }) switch err { case nil: web.JSONResp(w, paste, http.StatusOK) case pg.ErrNotFound: web.StdJSONResp(w, http.StatusNotFound) default: log.Printf("cannot update paste %d: %s", pid, err) web.StdJSONResp(w, http.StatusInternalServerError) } }
func handleGetNote(ctx context.Context, w http.ResponseWriter, r *http.Request) { var note Note key := datastore.NewKey(ctx, "Note", web.Args(ctx).ByIndex(0), 0, nil) if err := datastore.Get(ctx, key, ¬e); err != nil { log.Debug("cannot get note", "noteId", web.Args(ctx).ByIndex(0), "error", err.Error()) // XXX - what about not found? web.StdJSONResp(w, http.StatusInternalServerError) return } web.JSONResp(w, note, http.StatusOK) }
func HandlePasteDetails(ctx context.Context, w http.ResponseWriter, r *http.Request) { pid, _ := strconv.ParseInt(web.Args(ctx).ByIndex(0), 10, 64) db := pg.DB(ctx) paste, err := PasteByID(db, pid) switch err { case nil: web.JSONResp(w, paste, http.StatusOK) case pg.ErrNotFound: web.StdJSONResp(w, http.StatusNotFound) default: log.Printf("cannot get paste %d: %s", pid, err) web.StdJSONResp(w, http.StatusInternalServerError) } }
func handleListNotes(ctx context.Context, w http.ResponseWriter, r *http.Request) { notes, err := ListNotes(ctx) if err != nil { log.Error("cannot read note", "error", err.Error()) http.Error(w, err.Error(), http.StatusInternalServerError) return } resp := struct { Notes []*Note `json:"notes"` }{ Notes: notes, } web.JSONResp(w, resp, http.StatusOK) }
func HandlePasteList(ctx context.Context, w http.ResponseWriter, r *http.Request) { db := pg.DB(ctx) pastes, err := Pastes(db, 1000, 0) if err != nil { log.Printf("cannot list paste: %s", err) web.StdJSONResp(w, http.StatusInternalServerError) return } resp := struct { Pastes []*Paste }{ Pastes: pastes, } web.JSONResp(w, resp, http.StatusOK) }
func HandleUpdateNote(ctx context.Context, w http.ResponseWriter, r *http.Request) { var input Note if err := json.NewDecoder(r.Body).Decode(&input); err != nil { web.JSONErr(w, err.Error(), http.StatusBadRequest) return } if errs := validateNote(&input); len(errs) > 0 { web.JSONErrs(w, errs, http.StatusBadRequest) return } tx, err := pg.DB(ctx).Beginx() if err != nil { log.Printf("cannot start transaction: %s", err) web.StdJSONErr(w, http.StatusInternalServerError) return } defer tx.Rollback() acc, ok := auth.AuthRequired(tx, w, r) if !ok { return } noteID := stoint(web.Args(ctx).ByIndex(0)) if ok, err := IsNoteOwner(tx, noteID, acc.AccountID); err != nil { log.Printf("cannot check %d note owner: %s", noteID, err) web.StdJSONErr(w, http.StatusInternalServerError) return } else if !ok { web.JSONErr(w, "you are not owner of this note", http.StatusUnauthorized) return } note, err := UpdateNote(tx, input) if err != nil { log.Printf("cannot update %d note: %s", noteID, err) web.StdJSONErr(w, http.StatusInternalServerError) return } if err := tx.Commit(); err != nil { log.Printf("cannot commit transaction: %s", err) web.StdJSONErr(w, http.StatusInternalServerError) return } web.JSONResp(w, note, http.StatusOK) }
func handleAddBookmark(ctx context.Context, w http.ResponseWriter, r *http.Request) { var input struct { Url string `json:"url"` } if err := json.NewDecoder(r.Body).Decode(&input); err != nil { web.JSONErr(w, err.Error(), http.StatusBadRequest) return } resp, err := ctxhttp.Get(ctx, &crawler, input.Url) if err != nil { log.Error("cannot crawl", "url", input.Url, "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } defer resp.Body.Close() body := make([]byte, 1024*20) if n, err := resp.Body.Read(body); err != nil && err != io.EOF { log.Error("cannot read crawler response", "url", input.Url, "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } else { body = body[:n] } title := pageTitle(body) var b Bookmark err = pg.DB(ctx).Get(&b, ` INSERT INTO bookmarks (title, url, created) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING RETURNING * `, title, input.Url, time.Now()) if err != nil { log.Error("cannot create bookmark", "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } web.JSONResp(w, b, http.StatusCreated) }
func handleAddNote(ctx context.Context, w http.ResponseWriter, r *http.Request) { var input struct { Content string `json:"content"` Created time.Time `json:"created"` } if err := json.NewDecoder(r.Body).Decode(&input); err != nil { web.JSONErr(w, err.Error(), http.StatusBadRequest) return } var errs []string if input.Content == "" { errs = append(errs, `"content" is required`) } if len(errs) != 0 { web.JSONErrs(w, errs, http.StatusBadRequest) return } if input.Created.IsZero() { input.Created = time.Now() } n := Note{ NoteID: generateId(), Content: input.Content, Created: input.Created, } key := datastore.NewKey(ctx, "Note", n.NoteID, 0, nil) _, err := datastore.Put(ctx, key, &n) if err != nil { log.Debug("cannot put note", "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } web.JSONResp(w, &n, http.StatusCreated) }
func HandleNoteDetails(ctx context.Context, w http.ResponseWriter, r *http.Request) { args := web.Args(ctx) note, err := NoteByID(pg.DB(ctx), stoint(args.ByIndex(0))) if err != nil { if err == pg.ErrNotFound { web.StdJSONErr(w, http.StatusNotFound) } else { log.Printf("cannot get %q note: %s", args.ByIndex(0), err) web.StdJSONErr(w, http.StatusInternalServerError) } return } if !note.IsPublic { acc, ok := auth.Authenticated(pg.DB(ctx), r) if !ok || acc.AccountID != note.OwnerID { web.StdJSONErr(w, http.StatusUnauthorized) return } } web.JSONResp(w, note, http.StatusOK) }
func HandlePasteCreate(ctx context.Context, w http.ResponseWriter, r *http.Request) { var input struct { Content string } if err := json.NewDecoder(r.Body).Decode(&input); err != nil { web.StdJSONResp(w, http.StatusBadRequest) return } if input.Content == "" { web.JSONErr(w, `"Content" is required"`, http.StatusBadRequest) return } db := pg.DB(ctx) paste, err := CreatePaste(db, Paste{Content: input.Content}) if err != nil { log.Printf("cannot create paste: %s", err) web.StdJSONResp(w, http.StatusInternalServerError) return } web.JSONResp(w, paste, http.StatusCreated) }
func handleListImages(ctx context.Context, w http.ResponseWriter, r *http.Request) { offset, _ := strconv.ParseInt(r.URL.Query().Get("offset"), 10, 64) opts := ImagesOpts{ Offset: offset, Limit: 200, } // narrow to images tagged as specified for name, values := range r.URL.Query() { if !strings.HasPrefix(name, "tag_") { continue } for _, value := range values { opts.Tags = append(opts.Tags, KeyValue{ Key: name[4:], Value: value, }) } } imgs, err := Images(sq.DB(ctx), opts) if err != nil { log.Error("cannot list images", "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } if imgs == nil { imgs = make([]*Image, 0) } resp := struct { Images []*Image `json:"images"` }{ Images: imgs, } web.JSONResp(w, resp, http.StatusOK) }
func HandleListNotes(ctx context.Context, w http.ResponseWriter, r *http.Request) { db := pg.DB(ctx) acc, ok := auth.AuthRequired(db, w, r) if !ok { return } var offset int if raw := r.URL.Query().Get("offset"); raw != "" { if n, err := strconv.Atoi(raw); err != nil { web.JSONErr(w, "invalid 'offset' value", http.StatusBadRequest) return } else { offset = n } } notes, err := NotesByOwner(db, acc.AccountID, 300, offset) if err != nil { log.Printf("cannot fetch notes for %d: %s", acc.AccountID, err) web.StdJSONErr(w, http.StatusInternalServerError) return } if notes == nil { notes = make([]*Note, 0) // JSON api should return empty list } content := struct { Notes []*Note }{ Notes: notes, } web.JSONResp(w, content, http.StatusOK) }
func handleTagImage(ctx context.Context, w http.ResponseWriter, r *http.Request) { var input struct { Name string Value string } if err := json.NewDecoder(r.Body).Decode(&input); err != nil { web.JSONErr(w, err.Error(), http.StatusBadRequest) return } var errs []string if input.Name == "" { errs = append(errs, `"name" is required`) } if input.Value == "" { errs = append(errs, `"value" is required`) } if len(errs) != 0 { web.JSONErrs(w, errs, http.StatusBadRequest) return } db := sq.DB(ctx) img, err := ImageByID(db, web.Args(ctx).ByIndex(0)) switch err { case nil: // all good case sq.ErrNotFound: web.JSONErr(w, "parent image does not exist", http.StatusBadRequest) return default: log.Error("database error", "image", web.Args(ctx).ByIndex(0), "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } tag, err := CreateTag(db, Tag{ ImageID: img.ImageID, Name: input.Name, Value: input.Value, }) switch err { case nil: // all good, update storage meta case sq.ErrConflict: web.JSONResp(w, tag, http.StatusOK) return default: log.Error("cannot create object", "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } if img.Tags, err = ImageTags(db, img.ImageID); err != nil { log.Error("cannot get image tags", "image", img.ImageID, "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } fs := FileStore(ctx) if err := fs.PutMeta(img); err != nil { log.Error("cannot store image metadata", "image", img.ImageID, "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } web.JSONResp(w, tag, http.StatusCreated) }
func handleUploadImage(ctx context.Context, w http.ResponseWriter, r *http.Request) { if err := r.ParseMultipartForm(10 * megabyte); err != nil { web.JSONResp(w, err.Error(), http.StatusBadRequest) return } var header *multipart.FileHeader for _, headers := range r.MultipartForm.File { for _, h := range headers { log.Debug("uploading file", "name", h.Filename) if header != nil { web.JSONErr(w, "cannot upload more than one time at once", http.StatusBadRequest) return } header = h } } if header == nil { web.JSONErr(w, "image file missing", http.StatusBadRequest) return } if !strings.HasSuffix(strings.ToLower(header.Filename), ".jpg") { // XXX this is not the best validation web.JSONErr(w, "only JPEG format is allowed", http.StatusBadRequest) return } fd, err := header.Open() if err != nil { log.Error("cannot open uploaded file", "name", header.Filename, "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } defer fd.Close() image, err := imageMeta(fd) if err != nil { log.Error("cannot extract image metadata", "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } // store image in database db := sq.DB(ctx) image, err = CreateImage(db, *image) switch err { case nil: // all good case sq.ErrConflict: // image already exists, nothing more to do here web.JSONResp(w, image, http.StatusOK) return default: log.Error("cannot create object", "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } if _, err := fd.Seek(0, os.SEEK_SET); err != nil { log.Error("cannot seek image", "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } fs := FileStore(ctx) if err := fs.Put(image, fd); err != nil { log.Error("cannot store image", "error", err.Error()) web.StdJSONResp(w, http.StatusInternalServerError) return } log.Debug("image file created", "id", image.ImageID) web.JSONResp(w, image, http.StatusCreated) }