Skip to content
Snippets Groups Projects
listobjects.go 2.63 KiB
Newer Older
package routes

import (
	"net/http"
	"strconv"

	"owo.codes/whats-this/api/lib/apierrors"
	"owo.codes/whats-this/api/lib/db"
	"owo.codes/whats-this/api/lib/middleware"
Dean's avatar
Dean committed
	"owo.codes/whats-this/api/lib/ratelimiter"

	"github.com/go-chi/render"
	"github.com/rs/zerolog/log"
Dean's avatar
Dean committed
	"github.com/spf13/viper"
const (
	// Maximum objects per page
	maxLimit = 100

	// filter keys for file vs short link.
	filterFiles = "file"
	filterLinks = "link"
)

// listObjectsResponse is the response format for ListObjects.
type listObjectsResponse struct {
Spotlight Deveaux's avatar
Spotlight Deveaux committed
	Success      bool        `json:"success"`
	TotalObjects int         `json:"total_objects"`
	Data         []db.Object `json:"data"`
}

// ListObjects returns a paginated list of all objects owned by a user.
func ListObjects(w http.ResponseWriter, r *http.Request) {
	// Only authorized users can use this route
	user := middleware.GetAuthorizedUser(r)
	if user.ID == "" || user.IsBlocked {
		panic(apierrors.Unauthorized)
	}

Dean's avatar
Dean committed
	// Apply ratelimits
	bucket := middleware.GetBucket(r)
	err := bucket.TakeWithHeaders(w, viper.GetInt64("ratelimiter.listObjectsCost"))
	if err == ratelimiter.InsufficientTokens {
		panic(apierrors.InsufficientTokens)
	}
	if err != nil {
		panic(apierrors.InternalServerError)
	}

	// Determine offset, limit and filter params
	var (
		query = r.URL.Query()
		limit = maxLimit
		l     = query.Get("limit")
	)
	if l != "" {
		limit, err = strconv.Atoi(l)
		if err != nil || limit < 0 {
			panic(apierrors.InvalidOffsetOrLimit)
		}
	if limit > maxLimit {
		panic(apierrors.LimitTooLarge)
	}
	o := query.Get("offset")
	offset, err := strconv.Atoi(o)
	if err != nil || offset < 0 {
		panic(apierrors.InvalidOffsetOrLimit)
	}
	asc := false
	if query.Get("order") == "asc" {
		asc = true
	}
	filter := query.Get("type")
	if filter != "" && filter != filterFiles && filter != filterLinks {
		panic(apierrors.InvalidObjectFilter)
	}

	f := -1
	if filter == filterFiles {
		f = 0
	} else if filter == filterLinks {
		f = 1
	}

	// Get the data
Spotlight Deveaux's avatar
Spotlight Deveaux committed
	count, err := db.CountObjectsByAssociatedUser(user.ID, f)
	if err != nil {
		log.Error().Err(err).Msg("failed to count objects for user")
		panic(apierrors.InternalServerError)
	}

	objects, err := db.ListObjectsByAssociatedUser(user.ID, f, asc, offset, limit)
	if err != nil {
		log.Error().Err(err).Msg("failed to list objects for user")
		panic(apierrors.InternalServerError)
	}
	associatedWithCurrentUser := true
	for i := 0; i < len(objects); i++ {
		objects[i].AssociatedWithCurrentUser = &associatedWithCurrentUser
	}

	// Return response
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
Spotlight Deveaux's avatar
Spotlight Deveaux committed
	render.JSON(w, r, listObjectsResponse{true, count, objects})