monorepo/cloud/maplefile-backend/pkg/maplefile/client/files.go

191 lines
6.3 KiB
Go

// Package client provides a Go SDK for interacting with the MapleFile API.
package client
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
)
// CreatePendingFile creates a new file in pending state.
func (c *Client) CreatePendingFile(ctx context.Context, input *CreateFileInput) (*PendingFile, error) {
var resp PendingFile
if err := c.doRequest(ctx, "POST", "/api/v1/files/pending", input, &resp, true); err != nil {
return nil, err
}
return &resp, nil
}
// GetFile returns a single file by ID.
func (c *Client) GetFile(ctx context.Context, id string) (*File, error) {
path := fmt.Sprintf("/api/v1/file/%s", id)
var resp File
if err := c.doRequest(ctx, "GET", path, nil, &resp, true); err != nil {
return nil, err
}
return &resp, nil
}
// UpdateFile updates a file's metadata.
func (c *Client) UpdateFile(ctx context.Context, id string, input *UpdateFileInput) (*File, error) {
path := fmt.Sprintf("/api/v1/file/%s", id)
var resp File
if err := c.doRequest(ctx, "PUT", path, input, &resp, true); err != nil {
return nil, err
}
return &resp, nil
}
// DeleteFile soft-deletes a file.
func (c *Client) DeleteFile(ctx context.Context, id string) error {
path := fmt.Sprintf("/api/v1/file/%s", id)
return c.doRequest(ctx, "DELETE", path, nil, nil, true)
}
// DeleteMultipleFiles deletes multiple files at once.
func (c *Client) DeleteMultipleFiles(ctx context.Context, fileIDs []string) error {
input := DeleteMultipleFilesInput{FileIDs: fileIDs}
return c.doRequest(ctx, "POST", "/api/v1/files/delete-multiple", input, nil, true)
}
// GetPresignedUploadURL gets a presigned URL for uploading file content.
func (c *Client) GetPresignedUploadURL(ctx context.Context, fileID string) (*PresignedURL, error) {
path := fmt.Sprintf("/api/v1/file/%s/upload-url", fileID)
var resp PresignedURL
if err := c.doRequest(ctx, "GET", path, nil, &resp, true); err != nil {
return nil, err
}
return &resp, nil
}
// CompleteFileUpload marks the file upload as complete and transitions it to active state.
func (c *Client) CompleteFileUpload(ctx context.Context, fileID string, input *CompleteUploadInput) (*File, error) {
path := fmt.Sprintf("/api/v1/file/%s/complete", fileID)
var resp File
if err := c.doRequest(ctx, "POST", path, input, &resp, true); err != nil {
return nil, err
}
return &resp, nil
}
// GetPresignedDownloadURL gets a presigned URL for downloading file content.
func (c *Client) GetPresignedDownloadURL(ctx context.Context, fileID string) (*PresignedDownloadResponse, error) {
path := fmt.Sprintf("/api/v1/file/%s/download-url", fileID)
var resp PresignedDownloadResponse
if err := c.doRequest(ctx, "GET", path, nil, &resp, true); err != nil {
return nil, err
}
return &resp, nil
}
// ReportDownloadCompleted reports that a file download has completed.
func (c *Client) ReportDownloadCompleted(ctx context.Context, fileID string) error {
path := fmt.Sprintf("/api/v1/file/%s/download-completed", fileID)
return c.doRequest(ctx, "POST", path, nil, nil, true)
}
// ArchiveFile archives a file.
func (c *Client) ArchiveFile(ctx context.Context, id string) (*File, error) {
path := fmt.Sprintf("/api/v1/file/%s/archive", id)
var resp File
if err := c.doRequest(ctx, "PUT", path, nil, &resp, true); err != nil {
return nil, err
}
return &resp, nil
}
// RestoreFile restores an archived file.
func (c *Client) RestoreFile(ctx context.Context, id string) (*File, error) {
path := fmt.Sprintf("/api/v1/file/%s/restore", id)
var resp File
if err := c.doRequest(ctx, "PUT", path, nil, &resp, true); err != nil {
return nil, err
}
return &resp, nil
}
// ListFilesByCollection returns all files in a collection.
func (c *Client) ListFilesByCollection(ctx context.Context, collectionID string) ([]*File, error) {
path := fmt.Sprintf("/api/v1/collection/%s/files", collectionID)
var resp struct {
Files []*File `json:"files"`
}
if err := c.doRequest(ctx, "GET", path, nil, &resp, true); err != nil {
return nil, err
}
return resp.Files, nil
}
// ListRecentFiles returns the user's recent files.
func (c *Client) ListRecentFiles(ctx context.Context) ([]*File, error) {
var resp struct {
Files []*File `json:"files"`
}
if err := c.doRequest(ctx, "GET", "/api/v1/files/recent", nil, &resp, true); err != nil {
return nil, err
}
return resp.Files, nil
}
// SyncFiles fetches file changes since the given cursor.
func (c *Client) SyncFiles(ctx context.Context, input *SyncInput) (*FileSyncResponse, error) {
var resp FileSyncResponse
if err := c.doRequest(ctx, "POST", "/api/v1/files/sync", input, &resp, true); err != nil {
return nil, err
}
return &resp, nil
}
// UploadToPresignedURL uploads data to an S3 presigned URL.
// This is a helper method for uploading encrypted file content directly to S3.
func (c *Client) UploadToPresignedURL(ctx context.Context, presignedURL string, data []byte, contentType string) error {
req, err := http.NewRequestWithContext(ctx, "PUT", presignedURL, bytes.NewReader(data))
if err != nil {
return fmt.Errorf("failed to create upload request: %w", err)
}
req.Header.Set("Content-Type", contentType)
req.ContentLength = int64(len(data))
resp, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("failed to upload to presigned URL: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("upload failed with status %d: %s", resp.StatusCode, string(body))
}
return nil
}
// DownloadFromPresignedURL downloads data from an S3 presigned URL.
// This is a helper method for downloading encrypted file content directly from S3.
func (c *Client) DownloadFromPresignedURL(ctx context.Context, presignedURL string) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, "GET", presignedURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to create download request: %w", err)
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, fmt.Errorf("failed to download from presigned URL: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
body, _ := io.ReadAll(resp.Body)
return nil, fmt.Errorf("download failed with status %d: %s", resp.StatusCode, string(body))
}
data, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read download response: %w", err)
}
return data, nil
}