Skip to main content

Drive API Integration Guide

Overview

This document provides a comprehensive guide for integrating the Drive feature into the Dheyo application. The Drive feature acts as a local file system similar to Google Drive, allowing users to organize files and folders in a hierarchical structure.


🎯 Current State

  • ✅ Basic UI with empty state
  • ✅ View mode toggle (Grid/List)
  • ✅ Navigation link in sidebar beneath "My Creations"
  • ❌ No API integration yet
  • ❌ No data fetching or state management

🔌 Available Drive APIs

All Drive APIs are available under the /api/v1/drive endpoint and require authentication via bearer token.

1. Root Drive Management

GET /api/v1/drive

Purpose: Get the authenticated user's root folder ID

Authentication: Required (Bearer token)

Response:

{
"root_folder_id": "5JFvPLWsT7"
}

Status Codes:

  • 200 - Success
  • 401 - Unauthorized
  • 404 - Root folder not found
  • 500 - Internal server error

Usage: Call this endpoint on page load to initialize the drive and get the starting folder.


2. Folder Operations

POST /api/v1/drive/folders

Purpose: Create a new folder

Authentication: Required (Bearer token)

Request Body:

{
"parent_folder_id": "5JFvPLWsT7",
"name": "my-folder"
}

Response:

{
"folder_id": "8KpQmNxWz3"
}

Status Codes:

  • 201 - Folder created successfully
  • 400 - Invalid folder name or parent folder ID
  • 401 - Unauthorized
  • 403 - User does not have write permission on parent folder
  • 404 - Parent folder not found
  • 500 - Internal server error

Usage: Implement the "New Folder" button functionality.


GET /api/v1/drive/folders/{folder_id}

Purpose: List contents of a folder with cursor-based pagination

Authentication: Required (Bearer token)

Path Parameters:

  • folder_id (string, required) - Folder ID (base58 encoded)

Query Parameters:

  • limit (integer, optional) - Maximum number of items to return (default: 50, max: 100)
  • cursor (string, optional) - Cursor for pagination (base58 encoded HeyoId)

Response:

{
"folder_id": "5JFvPLWsT7",
"items": [
{
"heyo_id": "8KpQmNxWz3",
"heyo_type": "Folder"
},
{
"heyo_id": "9MqRnOyXa4",
"heyo_type": "File"
}
],
"next_cursor": "7JoPlMwVy2"
}

Status Codes:

  • 200 - Success
  • 400 - Invalid folder ID or cursor format
  • 401 - Unauthorized
  • 403 - User does not have read permission
  • 404 - Folder not found
  • 500 - Internal server error

Usage: Main content display and navigation. Use next_cursor for pagination (null when no more items).

Item Types:

  • "File" - Regular file
  • "Folder" - Subfolder
  • "GenT2I" - AI generated image
  • "ProjStoryBoard" - Storyboard project
  • Other content types may appear

DELETE /api/v1/drive/folders/{folder_id}

Purpose: Delete a folder

Authentication: Required (Bearer token)

Path Parameters:

  • folder_id (string, required) - Folder ID (base58 encoded)

Response: 204 No Content

Status Codes:

  • 204 - Folder deleted successfully
  • 400 - Invalid folder ID format
  • 401 - Unauthorized
  • 403 - User does not have delete permission
  • 404 - Folder not found
  • 500 - Internal server error

Usage: Delete folder action in context menu.


POST /api/v1/drive/folders/{folder_id}/move

Purpose: Move a folder to a different parent folder

Authentication: Required (Bearer token)

Path Parameters:

  • folder_id (string, required) - Folder ID to move (base58 encoded)

Request Body:

{
"destination_folder_id": "7JoPlMwVy2"
}

Response:

{
"folder_id": "8KpQmNxWz3",
"parent_folder_id": "7JoPlMwVy2"
}

Status Codes:

  • 200 - Success
  • 400 - Invalid folder ID format or cannot move root folder
  • 401 - Unauthorized
  • 403 - User does not have write permission on folder or destination folder
  • 404 - Folder or destination folder not found
  • 500 - Internal server error

Usage: Drag-and-drop operations, move folder to different location.

Note: Moving a folder also updates the content_location table.


PUT /api/v1/drive/folders/{folder_id}/upload

Purpose: Upload a file to a folder

Authentication: Required (Bearer token)

Path Parameters:

  • folder_id (string, required) - Folder ID or use 0 for user's root folder (base58 encoded)

Query Parameters:

  • filename (string, required) - Filename for the uploaded file
  • storage (string, optional) - Storage backend: "r2", "s3", or "local" (defaults to "r2" if available, then "local")

Request Body: Raw file content as application/octet-stream

Headers:

  • Content-Type - MIME type of the file (e.g., "image/png", "video/mp4")

Response:

{
"id": "9MqRnOyXa4",
"name": "example.png",
"sha256": "abc123...",
"mime_type": "image/png",
"size": 1048576,
"external_url": "https://...",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}

Status Codes:

  • 201 - File uploaded successfully
  • 400 - Empty body, missing filename, or invalid folder ID
  • 401 - Unauthorized
  • 403 - User does not have write permission on the folder
  • 404 - Folder not found
  • 409 - File with same SHA256 already exists and user has access
  • 500 - Internal server error

Usage: File upload functionality. The file is streamed while calculating its SHA256 hash.

Important Notes:

  • A database record is created with file metadata
  • A content_location entry links the file to the folder
  • Default user ACL permissions are applied to the file

3. File Operations

GET /api/v1/drive/file/{file_id}

Purpose: Get file metadata by ID

Authentication: Required (Bearer token)

Path Parameters:

  • file_id (string, required) - File ID (base58 encoded)

Response:

{
"id": "9MqRnOyXa4",
"name": "example.png",
"sha256": "abc123...",
"mime_type": "image/png",
"size": 1048576,
"external_url": "https://...",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}

Status Codes:

  • 200 - Success
  • 400 - Invalid file ID format
  • 401 - Unauthorized
  • 403 - User does not have read permission
  • 404 - File not found
  • 500 - Internal server error

Usage: Get file details for preview, display properties.


GET /api/v1/drive/file/{file_id}/data

Purpose: Download file content from storage (R2)

Authentication: Required (Bearer token)

Path Parameters:

  • file_id (string, required) - File ID (base58 encoded)

Response: Raw binary file data (application/octet-stream)

Status Codes:

  • 200 - Success
  • 400 - Invalid file ID format
  • 401 - Unauthorized
  • 403 - User does not have read permission
  • 404 - File not found or no R2 URL available
  • 500 - Internal server error

Usage: File download, file preview generation.


PATCH /api/v1/drive/file/{file_id}

Purpose: Rename a file

Authentication: Required (Bearer token)

Path Parameters:

  • file_id (string, required) - File ID (base58 encoded)

Request Body:

{
"name": "new-filename.png"
}

Response:

{
"id": "9MqRnOyXa4",
"name": "new-filename.png",
"sha256": "abc123...",
"mime_type": "image/png",
"size": 1048576,
"external_url": "https://...",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:35:00Z"
}

Status Codes:

  • 200 - Success
  • 400 - Invalid file ID format or invalid file name
  • 401 - Unauthorized
  • 403 - User does not have write permission
  • 404 - File not found
  • 500 - Internal server error

Usage: File rename functionality in context menu.


DELETE /api/v1/drive/file/{file_id}

Purpose: Delete a file (removes user's access)

Authentication: Required (Bearer token)

Path Parameters:

  • file_id (string, required) - File ID (base58 encoded)

Response: 204 No Content

Status Codes:

  • 204 - Success
  • 400 - Invalid file ID format
  • 401 - Unauthorized
  • 403 - User does not have delete permission
  • 404 - File not found
  • 500 - Internal server error

Usage: Delete file action in context menu.

Note: This removes the user's ACL entry. The file data remains for other users who have access.


POST /api/v1/drive/file/{file_id}/move

Purpose: Move a file to a different folder

Authentication: Required (Bearer token)

Path Parameters:

  • file_id (string, required) - File ID (base58 encoded)

Request Body:

{
"destination_folder_id": "7JoPlMwVy2"
}

Response:

{
"file_id": "9MqRnOyXa4",
"folder_id": "7JoPlMwVy2"
}

Status Codes:

  • 200 - Success
  • 400 - Invalid file ID or folder ID format
  • 401 - Unauthorized
  • 403 - User does not have write permission on file or destination folder
  • 404 - File or destination folder not found
  • 500 - Internal server error

Usage: Drag-and-drop operations, move file to different folder.


📋 Implementation Plan

Phase 1: Core Functionality (MVP)

Step 1: Create Service Layer

Create /UI/divq/src/services/driveApi.ts with these functions:

import { client } from './api'

// Get root folder ID
export const getDriveRoot = async (): Promise<{ root_folder_id: string }> => {
// Implementation
}

// List folder contents
export const listFolder = async (
folderId: string,
limit: number = 50,
cursor?: string
): Promise<{
folder_id: string
items: Array<{ heyo_id: string; heyo_type: string }>
next_cursor: string | null
}> => {
// Implementation
}

// Create new folder
export const createFolder = async (
parentFolderId: string,
name: string
): Promise<{ folder_id: string }> => {
// Implementation
}

// Upload file
export const uploadFile = async (
folderId: string,
file: File,
filename: string
): Promise<DriveFile> => {
// Implementation
}

// Delete folder
export const deleteFolder = async (folderId: string): Promise<void> => {
// Implementation
}

// Delete file
export const deleteFile = async (fileId: string): Promise<void> => {
// Implementation
}

Step 2: Create Type Definitions

Create /UI/divq/src/types/drive.ts:

export interface DriveFile {
id: string
name: string
sha256: string
mime_type: string
size: number
external_url?: string
created_at: string
updated_at: string
}

export interface FolderContent {
heyo_id: string
heyo_type: string // "File", "Folder", "GenT2I", "ProjStoryBoard", etc.
}

export interface ListFolderResponse {
folder_id: string
items: FolderContent[]
next_cursor: string | null
}

export interface FolderPathItem {
id: string
name: string
}

Step 3: Update DrivePage Component

Implement in /UI/divq/src/pages/DrivePage.tsx:

  • Fetch root folder on mount
  • Display folder contents (files & folders)
  • Handle pagination with infinite scroll
  • Implement "New Folder" modal
  • Implement "Upload Files" functionality
  • Show loading states and proper empty states

Step 4: React Query Integration

// Custom hooks for data fetching
export const useDriveRoot = () => {
return useQuery({
queryKey: ['drive', 'root'],
queryFn: getDriveRoot,
staleTime: Infinity, // Root folder doesn't change
})
}

export const useFolderContents = (folderId: string) => {
return useInfiniteQuery({
queryKey: ['drive', 'folder', folderId],
queryFn: ({ pageParam }) => listFolder(folderId, 50, pageParam),
initialPageParam: undefined,
getNextPageParam: (lastPage) => lastPage.next_cursor,
})
}

export const useCreateFolder = () => {
const queryClient = useQueryClient()
return useMutation({
mutationFn: ({ parentId, name }: { parentId: string; name: string }) =>
createFolder(parentId, name),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['drive', 'folder'] })
},
})
}

export const useUploadFile = () => {
const queryClient = useQueryClient()
return useMutation({
mutationFn: ({
folderId,
file,
filename,
}: {
folderId: string
file: File
filename: string
}) => uploadFile(folderId, file, filename),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['drive', 'folder'] })
},
})
}

Phase 2: Enhanced Features

File Operations

  1. Rename Files

    • Context menu option
    • Modal for name input
    • PATCH /api/v1/drive/file/{file_id}
  2. Download Files

    • Download button/menu option
    • GET /api/v1/drive/file/{file_id}/data
    • Handle different MIME types
  3. File Preview

    • Image preview (lightbox)
    • Video player
    • Use external_url or fetch data endpoint
  4. Context Menu

    • Rename, Move, Delete
    • Download, Share
    • View Properties

Folder Navigation

  1. Breadcrumb Trail

    • Clickable path segments
    • Store folder path in state
    • Navigate to any parent folder
  2. Back/Forward Navigation

    • Browser history integration
    • useNavigate with state
  3. Folder Tree Sidebar (Optional)

    • Collapsible tree view
    • Quick navigation
  4. Move Operations

    • Drag & drop to move files/folders
    • POST /api/v1/drive/file/{file_id}/move
    • POST /api/v1/drive/folders/{folder_id}/move

Advanced Features

  1. Search

    • Search within current folder
    • Global drive search (if API supports)
  2. Sorting

    • By name, date, size, type
    • Ascending/descending
  3. Multi-Select

    • Checkbox selection
    • Batch operations (delete, move)
  4. Drag & Drop

    • Upload by dropping files
    • Move by dragging items
  5. File Upload Queue

    • Show upload progress
    • Multiple simultaneous uploads
    • Cancel uploads

🔑 Key Implementation Notes

Authentication

All Drive API calls require authentication. The bearer token is automatically included by the API client:

import { client } from './api'
// Token is handled by the client

Error Handling

Implement proper error handling for common scenarios:

  • 401 Unauthorized - Redirect to login
  • 403 Forbidden - Show permission error
  • 404 Not Found - Handle missing resources
  • 409 Conflict - File already exists (deduplication)
  • 500 Server Error - Show generic error message

Content Type Handling

The API returns heyo_type to distinguish between different content types:

const renderItem = (item: FolderContent) => {
switch (item.heyo_type) {
case 'File':
return <FileItem id={item.heyo_id} />
case 'Folder':
return <FolderItem id={item.heyo_id} />
case 'GenT2I':
return <GeneratedImageItem id={item.heyo_id} />
case 'ProjStoryBoard':
return <StoryboardItem id={item.heyo_id} />
default:
return <GenericItem id={item.heyo_id} type={item.heyo_type} />
}
}

Pagination Strategy

Implement cursor-based pagination for large folders:

const [folderContents, setFolderContents] = useState<FolderContent[]>([])
const [cursor, setCursor] = useState<string | null>(null)
const [hasMore, setHasMore] = useState(true)

const loadMore = async () => {
const response = await listFolder(currentFolderId, 50, cursor)
setFolderContents([...folderContents, ...response.items])
setCursor(response.next_cursor)
setHasMore(response.next_cursor !== null)
}

File Upload Implementation

The upload endpoint requires raw binary data:

const uploadFile = async (
folderId: string,
file: File,
filename: string
): Promise<DriveFile> => {
const arrayBuffer = await file.arrayBuffer()
const uint8Array = new Uint8Array(arrayBuffer)

const response = await client.drive.uploadFile(folderId, {
filename,
file: uint8Array,
contentType: file.type,
})

return response
}

Track the folder path as users navigate:

const [folderPath, setFolderPath] = useState<FolderPathItem[]>([
{ id: 'root', name: 'My Drive' },
])

const navigateToFolder = (folderId: string, folderName: string) => {
// Add to path or truncate if navigating up
const existingIndex = folderPath.findIndex((f) => f.id === folderId)

if (existingIndex !== -1) {
// Navigating up - truncate path
setFolderPath(folderPath.slice(0, existingIndex + 1))
} else {
// Navigating down - append to path
setFolderPath([...folderPath, { id: folderId, name: folderName }])
}

setCurrentFolderId(folderId)
}

🔒 ACL Permissions

The Drive system uses ACL (Access Control List) permissions:

  • USER_READ - View file/folder contents
  • USER_WRITE - Modify, move, rename
  • USER_DELETE - Delete files/folders

Files and folders automatically get default user ACL permissions when created.


📊 Data Models

File Schema

interface File {
id: string // Base58 encoded HeyoId
name: string // Filename with extension
sha256: string // File hash for deduplication
mime_type: string // e.g., "image/png", "video/mp4"
size: number // File size in bytes
external_url?: string // Public URL if available
created_at: string // ISO 8601 timestamp
updated_at: string // ISO 8601 timestamp
}

Folder Content Schema

interface FolderObject {
heyo_id: string // Base58 encoded content ID
heyo_type: string // "File", "Folder", "GenT2I", etc.
}

🚀 Quick Start Checklist

  • Create driveApi.ts service file
  • Create drive.ts types file
  • Implement getDriveRoot API call
  • Implement listFolder API call
  • Add React Query hooks
  • Update DrivePage to fetch data
  • Display files and folders
  • Implement "New Folder" button
  • Implement "Upload Files" button
  • Add loading and error states
  • Add pagination/infinite scroll
  • Test basic CRUD operations


🐛 Troubleshooting

Common Issues

Issue: "Root folder not found" error

  • Solution: Ensure the user's root folder is created during account initialization

Issue: File upload returns 409 Conflict

  • Solution: This is expected behavior when the same file (by SHA256) already exists. The user already has access to this file.

Issue: Pagination not working

  • Solution: Ensure you're passing the next_cursor from the previous response, not creating your own cursor

Issue: Cannot move folder to itself

  • Solution: Add validation to prevent circular references before calling the API

📝 Notes

  • The Drive system uses content_location table to track which content is in which folder
  • Files are deduplicated by SHA256 hash
  • Deleting a file only removes the user's access, not the actual file data
  • The system supports multiple storage backends: R2 (default), S3, and local filesystem
  • All IDs are base58 encoded for URL-safe representation

🔄 Future Enhancements

  • Sharing files/folders with other users
  • Folder permissions management
  • File versioning
  • Trash/recycle bin
  • Starred/favorite files
  • Recent files view
  • Storage quota management
  • Collaborative editing
  • File comments and annotations

Last Updated: December 2024