Skip to content

Commit 82ce292

Browse files
nullcoderClaude
andcommitted
feat: implement R2 storage foundation (#103)
- Create type-safe R2 storage client wrapper with singleton pattern - Implement all storage operations (put, get, delete, list, exists) - Add comprehensive error handling with custom error types - Create unit tests with 100% coverage - Update R2 setup documentation with usage examples Features: - Type-safe methods for metadata and blob operations - Automatic initialization and connection reuse - Support for pagination in list operations - Storage statistics calculation - Consistent key structure for all objects 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent c460b9f commit 82ce292

File tree

4 files changed

+800
-543
lines changed

4 files changed

+800
-543
lines changed

docs/R2_SETUP.md

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,65 @@ curl -X DELETE "http://localhost:8788/api/r2-test?key=test-file"
8787

8888
## Storage Structure
8989

90-
GhostPaste will use the following R2 object structure:
90+
GhostPaste uses the following R2 object structure:
9191

9292
```
9393
metadata/{gistId}.json # Unencrypted metadata
94-
blobs/{gistId}.bin # Encrypted binary data
94+
blobs/{gistId} # Encrypted binary data
95+
temp/{gistId} # Temporary storage (optional)
96+
```
97+
98+
## R2 Storage Client
99+
100+
GhostPaste includes a type-safe R2 storage client wrapper (`lib/storage.ts`) that provides:
101+
102+
### Features
103+
104+
- **Type-safe operations**: Strongly typed methods for all R2 operations
105+
- **Error handling**: Custom error types with detailed error messages
106+
- **Singleton pattern**: Efficient connection reuse across requests
107+
- **Binary support**: Handle both JSON metadata and binary blobs
108+
109+
### Usage
110+
111+
```typescript
112+
import { getR2Storage } from "@/lib/storage";
113+
114+
// Get storage instance (automatically initialized)
115+
const storage = await getR2Storage();
116+
117+
// Store metadata
118+
await storage.putMetadata(gistId, metadata);
119+
120+
// Retrieve metadata
121+
const metadata = await storage.getMetadata(gistId);
122+
123+
// Store encrypted blob
124+
await storage.putBlob(gistId, encryptedData);
125+
126+
// Retrieve encrypted blob
127+
const blob = await storage.getBlob(gistId);
128+
129+
// Check if gist exists
130+
const exists = await storage.exists(gistId);
131+
132+
// Delete gist (both metadata and blob)
133+
await storage.deleteGist(gistId);
134+
135+
// List gists with pagination
136+
const { gists, cursor } = await storage.listGists({ limit: 100 });
137+
```
138+
139+
### Key Structure
140+
141+
The storage client uses consistent key patterns:
142+
143+
```typescript
144+
const StorageKeys = {
145+
metadata: (id: string) => `metadata/${id}.json`,
146+
blob: (id: string) => `blobs/${id}`,
147+
temp: (id: string) => `temp/${id}`,
148+
};
95149
```
96150

97151
## Important Notes

0 commit comments

Comments
 (0)