mirror of
https://github.com/get-drexa/drive.git
synced 2026-02-02 13:21:17 +00:00
feat(backend): hide drive uuid and return root dir
This commit is contained in:
@@ -147,7 +147,7 @@ func includeParam(c *fiber.Ctx) []string {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param request body createDirectoryRequest true "Directory details"
|
||||
// @Param include query string false "Include additional fields" Enums(path)
|
||||
// @Success 200 {object} DirectoryInfo "Created directory"
|
||||
@@ -230,7 +230,7 @@ func (h *HTTPHandler) createDirectory(c *fiber.Ctx) error {
|
||||
// @Tags directories
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param directoryID path string true "Directory ID"
|
||||
// @Param include query string false "Include additional fields" Enums(path)
|
||||
// @Success 200 {object} DirectoryInfo "Directory metadata"
|
||||
@@ -274,7 +274,7 @@ func (h *HTTPHandler) fetchDirectory(c *fiber.Ctx) error {
|
||||
// @Tags directories
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param directoryID path string true "Directory ID (use 'root' for the root directory)"
|
||||
// @Param orderBy query string false "Sort field: name, createdAt, or updatedAt" Enums(name,createdAt,updatedAt)
|
||||
// @Param dir query string false "Sort direction: asc or desc" Enums(asc,desc)
|
||||
@@ -405,7 +405,7 @@ func (h *HTTPHandler) listDirectory(c *fiber.Ctx) error {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param directoryID path string true "Directory ID"
|
||||
// @Param request body patchDirectoryRequest true "Directory update"
|
||||
// @Success 200 {object} DirectoryInfo "Updated directory metadata"
|
||||
@@ -464,7 +464,7 @@ func (h *HTTPHandler) patchDirectory(c *fiber.Ctx) error {
|
||||
// @Description Delete a directory permanently or move it to trash. Deleting a directory also affects all its contents.
|
||||
// @Tags directories
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param directoryID path string true "Directory ID"
|
||||
// @Param trash query bool false "Move to trash instead of permanent delete" default(false)
|
||||
// @Success 200 {object} DirectoryInfo "Trashed directory info (when trash=true)"
|
||||
@@ -524,7 +524,7 @@ func (h *HTTPHandler) deleteDirectory(c *fiber.Ctx) error {
|
||||
// @Description Delete multiple directories permanently or move them to trash. Deleting directories also affects all their contents. All items must be directories.
|
||||
// @Tags directories
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param id query string true "Comma-separated list of directory IDs to delete" example:"kRp2XYTq9A55,xYz123AbC456"
|
||||
// @Param trash query bool false "Move to trash instead of permanent delete" default(false)
|
||||
// @Success 200 {array} DirectoryInfo "Trashed directories (when trash=true)"
|
||||
@@ -619,7 +619,7 @@ func (h *HTTPHandler) deleteDirectories(c *fiber.Ctx) error {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param directoryID path string true "Target directory ID"
|
||||
// @Param request body postDirectoryContentRequest true "Items to move"
|
||||
// @Success 200 {object} moveItemsToDirectoryResponse "Move operation results with moved, conflict, and error states"
|
||||
@@ -769,7 +769,7 @@ func decodeListChildrenCursor(s string) (*decodedListChildrenCursor, error) {
|
||||
// @Description Get all share links that include this directory
|
||||
// @Tags directories
|
||||
// @Produce json
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param directoryID path string true "Directory ID"
|
||||
// @Success 200 {array} sharing.Share "Array of shares"
|
||||
// @Failure 401 {string} string "Not authenticated"
|
||||
|
||||
@@ -64,7 +64,7 @@ func (h *HTTPHandler) currentFileMiddleware(c *fiber.Ctx) error {
|
||||
// @Tags files
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param fileID path string true "File ID"
|
||||
// @Success 200 {object} FileInfo "File metadata"
|
||||
// @Failure 401 {string} string "Not authenticated"
|
||||
@@ -91,7 +91,7 @@ func (h *HTTPHandler) fetchFile(c *fiber.Ctx) error {
|
||||
// @Tags files
|
||||
// @Produce application/octet-stream
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param fileID path string true "File ID"
|
||||
// @Success 200 {file} binary "File content stream"
|
||||
// @Success 307 {string} string "Redirect to download URL"
|
||||
@@ -140,7 +140,7 @@ func (h *HTTPHandler) downloadFile(c *fiber.Ctx) error {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param fileID path string true "File ID"
|
||||
// @Param request body patchFileRequest true "File update"
|
||||
// @Success 200 {object} FileInfo "Updated file metadata"
|
||||
@@ -201,7 +201,7 @@ func (h *HTTPHandler) patchFile(c *fiber.Ctx) error {
|
||||
// @Tags files
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param fileID path string true "File ID"
|
||||
// @Param trash query bool false "Move to trash instead of permanent delete" default(false)
|
||||
// @Success 200 {object} FileInfo "Trashed file info (when trash=true)"
|
||||
@@ -264,7 +264,7 @@ func (h *HTTPHandler) deleteFile(c *fiber.Ctx) error {
|
||||
// @Description Delete multiple files permanently or move them to trash. All items must be files.
|
||||
// @Tags files
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param id query string true "Comma-separated list of file IDs to delete" example:"mElnUNCm8F22,kRp2XYTq9A55"
|
||||
// @Param trash query bool false "Move to trash instead of permanent delete" default(false)
|
||||
// @Success 200 {array} FileInfo "Trashed files (when trash=true)"
|
||||
@@ -352,7 +352,7 @@ func (h *HTTPHandler) deleteFiles(c *fiber.Ctx) error {
|
||||
// @Description Get all share links that include this file
|
||||
// @Tags files
|
||||
// @Produce json
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param fileID path string true "File ID"
|
||||
// @Success 200 {array} sharing.Share "Array of shares"
|
||||
// @Failure 401 {string} string "Not authenticated"
|
||||
|
||||
@@ -164,6 +164,20 @@ func TestRegistrationFlow(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("drive info", func(t *testing.T) {
|
||||
var info struct {
|
||||
ID string `json:"id"`
|
||||
RootDirID string `json:"rootDirId"`
|
||||
}
|
||||
doJSON(t, s.App(), http.MethodGet, fmt.Sprintf("/api/my/drives/%s", reg.Drive.ID), reg.AccessToken, nil, http.StatusOK, &info)
|
||||
if info.ID != reg.Drive.ID {
|
||||
t.Fatalf("unexpected drive id: got %q want %q", info.ID, reg.Drive.ID)
|
||||
}
|
||||
if info.RootDirID == "" {
|
||||
t.Fatalf("expected rootDirId to be set")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("users/me", func(t *testing.T) {
|
||||
var me struct {
|
||||
ID string `json:"id"`
|
||||
|
||||
@@ -10,12 +10,12 @@ import (
|
||||
type Drive struct {
|
||||
bun.BaseModel `bun:"drives" swaggerignore:"true"`
|
||||
|
||||
ID uuid.UUID `bun:",pk,type:uuid" json:"id"`
|
||||
PublicID string `bun:"public_id,notnull" json:"publicId"`
|
||||
OrgID uuid.UUID `bun:"org_id,notnull,type:uuid" json:"orgId"`
|
||||
Name string `bun:"name,notnull" json:"name"`
|
||||
ID uuid.UUID `bun:",pk,type:uuid" json:"-"`
|
||||
PublicID string `bun:"public_id,notnull" json:"id"`
|
||||
OrgID uuid.UUID `bun:"org_id,notnull,type:uuid" json:"-"`
|
||||
Name string `bun:"name,notnull" json:"name"`
|
||||
|
||||
OwnerAccountID *uuid.UUID `bun:"owner_account_id,type:uuid" json:"ownerAccountId,omitempty"`
|
||||
OwnerAccountID *uuid.UUID `bun:"owner_account_id,type:uuid" json:"-"`
|
||||
|
||||
StorageUsageBytes int64 `bun:"storage_usage_bytes,notnull" json:"storageUsageBytes"`
|
||||
StorageQuotaBytes int64 `bun:"storage_quota_bytes,notnull" json:"storageQuotaBytes"`
|
||||
|
||||
@@ -10,10 +10,14 @@ import (
|
||||
"github.com/get-drexa/drexa/internal/user"
|
||||
"github.com/get-drexa/drexa/internal/virtualfs"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/google/uuid"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type driveInfoResponse struct {
|
||||
*Drive
|
||||
RootDirID string `json:"rootDirId"`
|
||||
}
|
||||
|
||||
type HTTPHandler struct {
|
||||
driveService *Service
|
||||
accountService *account.Service
|
||||
@@ -39,7 +43,7 @@ func (h *HTTPHandler) RegisterRoutes(api fiber.Router) *virtualfs.ScopedRouter {
|
||||
drive.Use(h.authMiddleware)
|
||||
drive.Use(h.driveMiddleware)
|
||||
|
||||
drive.Get("/", h.getDrive)
|
||||
drive.Get("/", h.getDriveInfo)
|
||||
|
||||
return &virtualfs.ScopedRouter{Router: drive}
|
||||
}
|
||||
@@ -69,12 +73,34 @@ func (h *HTTPHandler) listDrives(c *fiber.Ctx) error {
|
||||
return c.JSON(drives)
|
||||
}
|
||||
|
||||
func (h *HTTPHandler) getDrive(c *fiber.Ctx) error {
|
||||
func (h *HTTPHandler) getDriveInfo(c *fiber.Ctx) error {
|
||||
drive, ok := reqctx.CurrentDrive(c).(*Drive)
|
||||
if !ok || drive == nil {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
}
|
||||
return c.JSON(drive)
|
||||
|
||||
rootDirID, _ := c.Locals("rootDirId").(string)
|
||||
if rootDirID == "" {
|
||||
scopeAny := reqctx.VFSAccessScope(c)
|
||||
scope, ok := scopeAny.(*virtualfs.Scope)
|
||||
if !ok || scope == nil {
|
||||
return c.SendStatus(fiber.StatusUnauthorized)
|
||||
}
|
||||
|
||||
root, err := h.vfs.FindNode(c.Context(), h.db, scope.RootNodeID.String(), scope)
|
||||
if err != nil {
|
||||
if errors.Is(err, virtualfs.ErrNodeNotFound) || errors.Is(err, virtualfs.ErrAccessDenied) {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
}
|
||||
return httperr.Internal(err)
|
||||
}
|
||||
rootDirID = root.PublicID
|
||||
}
|
||||
|
||||
return c.JSON(driveInfoResponse{
|
||||
Drive: drive,
|
||||
RootDirID: rootDirID,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *HTTPHandler) driveMiddleware(c *fiber.Ctx) error {
|
||||
@@ -83,12 +109,8 @@ func (h *HTTPHandler) driveMiddleware(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
}
|
||||
|
||||
driveID, err := uuid.Parse(c.Params("driveID"))
|
||||
if err != nil {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
}
|
||||
|
||||
drive, err := h.driveService.DriveByID(c.Context(), h.db, driveID)
|
||||
driveID := c.Params("driveID")
|
||||
drive, err := h.driveService.DriveByPublicID(c.Context(), h.db, driveID)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrDriveNotFound) {
|
||||
return c.SendStatus(fiber.StatusNotFound)
|
||||
@@ -113,6 +135,8 @@ func (h *HTTPHandler) driveMiddleware(c *fiber.Ctx) error {
|
||||
return httperr.Internal(err)
|
||||
}
|
||||
|
||||
c.Locals("rootDirId", root.PublicID)
|
||||
|
||||
scope := &virtualfs.Scope{
|
||||
DriveID: drive.ID,
|
||||
RootNodeID: root.ID,
|
||||
|
||||
@@ -65,6 +65,18 @@ func (s *Service) DriveByID(ctx context.Context, db bun.IDB, id uuid.UUID) (*Dri
|
||||
return &drive, nil
|
||||
}
|
||||
|
||||
func (s *Service) DriveByPublicID(ctx context.Context, db bun.IDB, publicID string) (*Drive, error) {
|
||||
var drive Drive
|
||||
err := db.NewSelect().Model(&drive).Where("public_id = ?", publicID).Scan(ctx)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrDriveNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &drive, nil
|
||||
}
|
||||
|
||||
// ListAccessibleDrives returns drives a principal account can access:
|
||||
// - personal drives: owner_account_id = account.ID
|
||||
// - shared drives: owner_account_id IS NULL (future)
|
||||
|
||||
@@ -130,7 +130,7 @@ func (h *HTTPHandler) shareMiddleware(c *fiber.Ctx) error {
|
||||
// @Tags shares
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param shareID path string true "Share ID"
|
||||
// @Success 200 {object} Share "Share details"
|
||||
// @Failure 401 {string} string "Not authenticated"
|
||||
@@ -161,7 +161,7 @@ func (h *HTTPHandler) getShare(c *fiber.Ctx) error {
|
||||
// @Tags shares
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param request body createShareRequest true "Share details"
|
||||
// @Success 200 {object} Share "Created share"
|
||||
// @Failure 400 {object} map[string]string "Invalid request, items not in same directory, or root directory cannot be shared"
|
||||
@@ -244,7 +244,7 @@ func (h *HTTPHandler) createShare(c *fiber.Ctx) error {
|
||||
// @Tags shares
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param shareID path string true "Share ID"
|
||||
// @Param request body patchShareRequest true "Share details"
|
||||
// @Success 200 {object} Share "Updated share"
|
||||
@@ -313,7 +313,7 @@ func (h *HTTPHandler) updateShare(c *fiber.Ctx) error {
|
||||
// @Summary Delete share
|
||||
// @Description Delete a share link, revoking access for all users
|
||||
// @Tags shares
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param shareID path string true "Share ID"
|
||||
// @Success 204 {string} string "Share deleted"
|
||||
// @Failure 401 {string} string "Not authenticated"
|
||||
|
||||
@@ -51,7 +51,7 @@ func (h *HTTPHandler) RegisterRoutes(api *virtualfs.ScopedRouter) {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param request body createUploadRequest true "Upload details"
|
||||
// @Success 200 {object} Upload "Upload session created"
|
||||
// @Failure 400 {object} map[string]string "Parent is not a directory"
|
||||
@@ -107,7 +107,7 @@ func (h *HTTPHandler) Create(c *fiber.Ctx) error {
|
||||
// @Tags uploads
|
||||
// @Accept application/octet-stream
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param uploadID path string true "Upload session ID"
|
||||
// @Param file body []byte true "File content (binary)"
|
||||
// @Success 204 {string} string "Content received successfully"
|
||||
@@ -148,7 +148,7 @@ func (h *HTTPHandler) ReceiveContent(c *fiber.Ctx) error {
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Security BearerAuth
|
||||
// @Param driveID path string true "Drive ID" format(uuid)
|
||||
// @Param driveID path string true "Drive ID" example:"kRp2XYTq9A55"
|
||||
// @Param uploadID path string true "Upload session ID"
|
||||
// @Param request body updateUploadRequest true "Status update"
|
||||
// @Success 200 {object} Upload "Upload completed"
|
||||
|
||||
@@ -25,5 +25,5 @@ type Upload struct {
|
||||
// Internal target node reference
|
||||
TargetNode *virtualfs.Node `json:"-" swaggerignore:"true"`
|
||||
// URL to upload file content to
|
||||
UploadURL string `json:"uploadUrl" example:"https://api.example.com/api/drives/550e8400-e29b-41d4-a716-446655440000/uploads/xNq5RVBt3K88/content"`
|
||||
UploadURL string `json:"uploadUrl" example:"https://api.example.com/api/drives/kRp2XYTq9A55/uploads/xNq5RVBt3K88/content"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user