// 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 }