ente/server/pkg/api/collection.go
2024-03-11 15:41:18 +05:30

435 lines
14 KiB
Go

package api
import (
"fmt"
"net/http"
"strconv"
"github.com/ente-io/stacktrace"
log "github.com/sirupsen/logrus"
"github.com/ente-io/museum/ente"
"github.com/ente-io/museum/pkg/controller"
"github.com/ente-io/museum/pkg/utils/auth"
"github.com/ente-io/museum/pkg/utils/handler"
"github.com/ente-io/museum/pkg/utils/time"
"github.com/gin-gonic/gin"
)
// CollectionHandler exposes request handlers for all collection related requests
type CollectionHandler struct {
Controller *controller.CollectionController
}
// Create creates a collection
func (h *CollectionHandler) Create(c *gin.Context) {
log.Info("Collection create")
var collection ente.Collection
if err := c.ShouldBindJSON(&collection); err != nil {
handler.Error(c, stacktrace.Propagate(err, "Could not bind request params"))
return
}
collection.App = string(auth.GetApp(c))
collection.UpdationTime = time.Microseconds()
collection, err := h.Controller.Create(collection,
auth.GetUserID(c.Request.Header))
if err != nil {
handler.Error(c, stacktrace.Propagate(err, "Could not create collection"))
return
}
c.JSON(http.StatusOK, gin.H{
"collection": collection,
})
}
// GetCollectionByID returns the collection for given ID.
func (h *CollectionHandler) GetCollectionByID(c *gin.Context) {
cID, err := strconv.ParseInt(c.Param("collectionID"), 10, 64)
if err != nil {
handler.Error(c, ente.ErrBadRequest)
return
}
userID := auth.GetUserID(c.Request.Header)
collection, err := h.Controller.GetCollection(c, userID, cID)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.JSON(http.StatusOK, gin.H{
"collection": collection,
})
}
// Deprecated: Remove once rps goes to 0.
// Get returns the list of collections accessible to a user.
func (h *CollectionHandler) Get(c *gin.Context) {
userID := auth.GetUserID(c.Request.Header)
sinceTime, _ := strconv.ParseInt(c.Query("sinceTime"), 10, 64)
app := auth.GetApp(c)
// TODO: Compute both with a single query
ownedCollections, err := h.Controller.GetOwned(userID, sinceTime, app)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, "Failed to get owned collections"))
return
}
sharedCollections, err := h.Controller.GetSharedWith(userID, sinceTime, app)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, "Failed to get shared collections"))
return
}
c.JSON(http.StatusOK, gin.H{
"collections": append(ownedCollections, sharedCollections...),
})
}
// GetV2 returns the list of collections accessible to a user
func (h *CollectionHandler) GetV2(c *gin.Context) {
userID := auth.GetUserID(c.Request.Header)
sinceTime, _ := strconv.ParseInt(c.Query("sinceTime"), 10, 64)
app := auth.GetApp(c)
ownedCollections, err := h.Controller.GetOwnedV2(userID, sinceTime, app)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, "Failed to get owned collections"))
return
}
sharedCollections, err := h.Controller.GetSharedWith(userID, sinceTime, app)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, "Failed to get shared collections"))
return
}
c.JSON(http.StatusOK, gin.H{
"collections": append(ownedCollections, sharedCollections...),
})
}
// Share shares a collection with a user
func (h *CollectionHandler) Share(c *gin.Context) {
var request ente.AlterShareRequest
if err := c.ShouldBindJSON(&request); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
resp, err := h.Controller.Share(c, request)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.JSON(http.StatusOK, gin.H{
"sharees": resp,
})
}
// ShareURL generates a publicly sharable url
func (h *CollectionHandler) ShareURL(c *gin.Context) {
var request ente.CreatePublicAccessTokenRequest
if err := c.ShouldBindJSON(&request); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
// todo:[2/Sep/23] change device limit to 0 once both web and mobile clients are updated
request.DeviceLimit = controller.DeviceLimitThreshold
response, err := h.Controller.ShareURL(c, auth.GetUserID(c.Request.Header), request)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.JSON(http.StatusOK, gin.H{
"result": response,
})
}
// UpdateShareURL generates a publicly sharable url
func (h *CollectionHandler) UpdateShareURL(c *gin.Context) {
var req ente.UpdatePublicAccessTokenRequest
if err := c.ShouldBindJSON(&req); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
if req.DeviceLimit == nil && req.ValidTill == nil && req.DisablePassword == nil &&
req.Nonce == nil && req.PassHash == nil && req.EnableDownload == nil && req.EnableCollect == nil {
handler.Error(c, stacktrace.Propagate(ente.ErrBadRequest, "all parameters are missing"))
return
}
if req.DeviceLimit != nil && (*req.DeviceLimit < 0 || *req.DeviceLimit > controller.DeviceLimitThreshold) {
handler.Error(c, stacktrace.Propagate(ente.ErrBadRequest, fmt.Sprintf("device limit: %d out of range", *req.DeviceLimit)))
return
}
if req.ValidTill != nil && *req.ValidTill != 0 && *req.ValidTill < time.Microseconds() {
handler.Error(c, stacktrace.Propagate(ente.ErrBadRequest, "valid till should be greater than current timestamp"))
return
}
var allPassParamsMissing = req.Nonce == nil && req.PassHash == nil && req.MemLimit == nil && req.OpsLimit == nil
var allPassParamsPresent = req.Nonce != nil && req.PassHash != nil && req.MemLimit != nil && req.OpsLimit != nil
if !(allPassParamsMissing || allPassParamsPresent) {
handler.Error(c, stacktrace.Propagate(ente.ErrBadRequest, "all password params should be either present or missing"))
return
}
if allPassParamsPresent && req.DisablePassword != nil && *req.DisablePassword {
handler.Error(c, stacktrace.Propagate(ente.ErrBadRequest, "can not set and disable password in same request"))
return
}
response, err := h.Controller.UpdateShareURL(c, auth.GetUserID(c.Request.Header), req)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.JSON(http.StatusOK, gin.H{
"result": response,
})
}
// UnShareURL disable all shared urls for the given collectionID
func (h *CollectionHandler) UnShareURL(c *gin.Context) {
cID, err := strconv.ParseInt(c.Param("collectionID"), 10, 64)
if err != nil {
handler.Error(c, stacktrace.Propagate(ente.ErrBadRequest, ""))
return
}
userID := auth.GetUserID(c.Request.Header)
err = h.Controller.DisableSharedURL(c, userID, cID)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.Status(http.StatusOK)
}
// UnShare unshares a collection with a user
func (h *CollectionHandler) UnShare(c *gin.Context) {
var request ente.AlterShareRequest
if err := c.ShouldBindJSON(&request); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
resp, err := h.Controller.UnShare(c, request.CollectionID, auth.GetUserID(c.Request.Header), request.Email)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.JSON(http.StatusOK, gin.H{
"sharees": resp,
})
}
// Leave allows user to leave a shared collection, which is not owned by them
func (h *CollectionHandler) Leave(c *gin.Context) {
cID, err := strconv.ParseInt(c.Param("collectionID"), 10, 64)
if err != nil {
handler.Error(c, stacktrace.Propagate(ente.ErrBadRequest, ""))
return
}
err = h.Controller.Leave(c, cID)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.Status(http.StatusOK)
}
// AddFiles adds files to a collection
func (h *CollectionHandler) AddFiles(c *gin.Context) {
var request ente.AddFilesRequest
if err := c.ShouldBindJSON(&request); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
if len(request.Files) > DefaultMaxBatchSize {
handler.Error(c, stacktrace.Propagate(ente.ErrBatchSizeTooLarge, ""))
return
}
if err := h.Controller.AddFiles(c, auth.GetUserID(c.Request.Header), request.Files, request.CollectionID); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.Status(http.StatusOK)
}
// RestoreFiles adds files from trash to given collection
func (h *CollectionHandler) RestoreFiles(c *gin.Context) {
var request ente.AddFilesRequest
if err := c.ShouldBindJSON(&request); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
if len(request.Files) > DefaultMaxBatchSize {
handler.Error(c, stacktrace.Propagate(ente.ErrBatchSizeTooLarge, ""))
return
}
if err := h.Controller.RestoreFiles(c, auth.GetUserID(c.Request.Header), request.CollectionID, request.Files); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.Status(http.StatusOK)
}
// Movefiles from one collection to another
func (h *CollectionHandler) MoveFiles(c *gin.Context) {
var request ente.MoveFilesRequest
if err := c.ShouldBindJSON(&request); err != nil {
handler.Error(c,
stacktrace.Propagate(ente.ErrBadRequest, fmt.Sprintf("Request binding failed %s", err)))
return
}
if len(request.Files) > DefaultMaxBatchSize {
handler.Error(c, stacktrace.Propagate(ente.ErrBatchSizeTooLarge, ""))
return
}
if request.ToCollectionID == request.FromCollectionID {
handler.Error(c, stacktrace.Propagate(ente.ErrBadRequest, "to and fromCollection should be different"))
return
}
if err := h.Controller.MoveFiles(c, request); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.Status(http.StatusOK)
}
// RemoveFilesV3 allow removing files from a collection when files and collection belong to two different users
func (h *CollectionHandler) RemoveFilesV3(c *gin.Context) {
var request ente.RemoveFilesV3Request
if err := c.ShouldBindJSON(&request); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
if len(request.FileIDs) > DefaultMaxBatchSize {
handler.Error(c, stacktrace.Propagate(ente.ErrBatchSizeTooLarge, ""))
return
}
if err := h.Controller.RemoveFilesV3(c, request); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.Status(http.StatusOK)
}
// GetDiffV2 returns the diff within a collection since a timestamp
func (h *CollectionHandler) GetDiffV2(c *gin.Context) {
userID := auth.GetUserID(c.Request.Header)
cID, _ := strconv.ParseInt(c.Query("collectionID"), 10, 64)
sinceTime, _ := strconv.ParseInt(c.Query("sinceTime"), 10, 64)
files, hasMore, err := h.Controller.GetDiffV2(c, cID, userID, sinceTime)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.JSON(http.StatusOK, gin.H{
"diff": files,
"hasMore": hasMore,
})
}
// GetFile returns the diff within a collection since a timestamp
func (h *CollectionHandler) GetFile(c *gin.Context) {
cID, _ := strconv.ParseInt(c.Query("collectionID"), 10, 64)
fileID, _ := strconv.ParseInt(c.Query("fileID"), 10, 64)
file, err := h.Controller.GetFile(c, cID, fileID)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.JSON(http.StatusOK, gin.H{
"file": file,
})
}
// GetSharees returns the list of users a collection has been shared with
func (h *CollectionHandler) GetSharees(c *gin.Context) {
userID := auth.GetUserID(c.Request.Header)
cID, _ := strconv.ParseInt(c.Query("collectionID"), 10, 64)
sharees, err := h.Controller.GetSharees(c, cID, userID)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.JSON(http.StatusOK, gin.H{
"sharees": sharees,
})
}
func (h *CollectionHandler) TrashV3(c *gin.Context) {
var req ente.TrashCollectionV3Request
if err := c.ShouldBindQuery(&req); err != nil {
handler.Error(c,
stacktrace.Propagate(ente.ErrBadRequest, fmt.Sprintf("Request binding failed %s", err)))
return
}
err := h.Controller.TrashV3(c, req)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.Status(http.StatusOK)
}
// Rename updates the collection's name
func (h *CollectionHandler) Rename(c *gin.Context) {
var request ente.RenameRequest
if err := c.ShouldBindJSON(&request); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
if err := h.Controller.Rename(auth.GetUserID(c.Request.Header), request.CollectionID, request.EncryptedName, request.NameDecryptionNonce); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.Status(http.StatusOK)
}
// Updates the magic metadata for a collection
func (h *CollectionHandler) PrivateMagicMetadataUpdate(c *gin.Context) {
var request ente.UpdateCollectionMagicMetadata
if err := c.ShouldBindJSON(&request); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
if err := h.Controller.UpdateMagicMetadata(c, request, false); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.Status(http.StatusOK)
}
// PublicMagicMetadataUpdate updates the public magic metadata for a collection
func (h *CollectionHandler) PublicMagicMetadataUpdate(c *gin.Context) {
var request ente.UpdateCollectionMagicMetadata
if err := c.ShouldBindJSON(&request); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
if err := h.Controller.UpdateMagicMetadata(c, request, true); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.Status(http.StatusOK)
}
// ShareeMagicMetadataUpdate updates sharees magic metadata for shared collection.
func (h *CollectionHandler) ShareeMagicMetadataUpdate(c *gin.Context) {
var request ente.UpdateCollectionMagicMetadata
if err := c.ShouldBindJSON(&request); err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
err := h.Controller.UpdateShareeMagicMetadata(c, request)
if err != nil {
handler.Error(c, stacktrace.Propagate(err, ""))
return
}
c.Status(http.StatusOK)
}