@paschendale/r2-presigned-url
Version:
Generate presigned URLs for Cloudflare R2 storage using AWS Signature Version 4 - Compatible with Cloudflare Workers
244 lines (186 loc) • 5.86 kB
Markdown
# R2 Presigned URL Generator
A lightweight, zero-dependency TypeScript library for generating presigned URLs for Cloudflare R2 storage using AWS Signature Version 4. Compatible with Cloudflare Workers and Node.js environments.
## Features
- ✅ **Zero Dependencies** - Uses only Web Crypto API and built-in browser/Node.js APIs
- ✅ **Cloudflare Workers Compatible** - No Node.js-specific dependencies
- ✅ **TypeScript Support** - Full type definitions included
- ✅ **AWS Signature Version 4** - Standard-compliant signature generation
- ✅ **Multiple HTTP Methods** - Support for PUT, GET, and POST requests
- ✅ **Comprehensive Validation** - Input validation and error handling
- ✅ **Testing Utilities** - Built-in test function for debugging
## Installation
```bash
npm install r2-presigned-url
```
## Quick Start
```typescript
import { generateR2PresignedUrl, R2Credentials } from 'r2-presigned-url';
const credentials: R2Credentials = {
R2_ACCESS_KEY_ID: 'your-access-key',
R2_SECRET_ACCESS_KEY: 'your-secret-key',
R2_ACCOUNT_ID: 'your-account-id',
R2_BUCKET: 'my-bucket'
};
const url = await generateR2PresignedUrl(
{
key: 'uploads/file.jpg',
contentType: 'image/jpeg',
expiresIn: 3600 // 1 hour
},
credentials
);
console.log('Presigned URL:', url);
```
## API Reference
### `generateR2PresignedUrl(options, credentials)`
Generates a presigned URL for R2 storage operations.
#### Parameters
- `options` (PresignedUrlOptions):
- `key` (string): The object key (path) in the R2 bucket
- `contentType` (string): MIME type of the file
- `expiresIn` (number): URL expiration time in seconds (max 604800 = 7 days)
- `method` (optional): HTTP method - 'PUT' (default), 'GET', or 'POST'
- `credentials` (R2Credentials):
- `R2_ACCESS_KEY_ID` (string): Cloudflare R2 Access Key ID
- `R2_SECRET_ACCESS_KEY` (string): Cloudflare R2 Secret Access Key
- `R2_ACCOUNT_ID` (string): Cloudflare Account ID
- `R2_BUCKET` (optional): R2 Bucket name (defaults to 'development')
#### Returns
- `Promise<string>`: The presigned URL
### `testPresignedUrlGeneration(credentials)`
Tests the presigned URL generation with sample data.
#### Parameters
- `credentials` (R2Credentials): R2 credentials to test with
#### Returns
- `Promise<TestResult>`: Test results with success status and debug info
## Usage Examples
### Upload a File
```typescript
import { generateR2PresignedUrl } from 'r2-presigned-url';
const uploadUrl = await generateR2PresignedUrl(
{
key: 'uploads/document.pdf',
contentType: 'application/pdf',
expiresIn: 1800 // 30 minutes
},
credentials
);
// Use the URL to upload the file
const response = await fetch(uploadUrl, {
method: 'PUT',
body: file,
headers: {
'Content-Type': 'application/pdf'
}
});
```
### Download a File
```typescript
const downloadUrl = await generateR2PresignedUrl(
{
key: 'uploads/document.pdf',
contentType: 'application/pdf',
expiresIn: 3600,
method: 'GET'
},
credentials
);
// Use the URL to download the file
const response = await fetch(downloadUrl);
const file = await response.blob();
```
### Testing
```typescript
import { testPresignedUrlGeneration } from 'r2-presigned-url';
const result = await testPresignedUrlGeneration(credentials);
if (result.success) {
console.log('✅ Test passed!');
console.log('Generated URL:', result.url);
console.log('Signature:', result.debug?.signature);
} else {
console.error('❌ Test failed:', result.error);
}
```
## Cloudflare Workers Example
```typescript
// workers/index.ts
import { generateR2PresignedUrl } from 'r2-presigned-url';
export default {
async fetch(request: Request, env: Env): Promise<Response> {
if (request.method === 'POST') {
const { key, contentType } = await request.json();
const credentials = {
R2_ACCESS_KEY_ID: env.R2_ACCESS_KEY_ID,
R2_SECRET_ACCESS_KEY: env.R2_SECRET_ACCESS_KEY,
R2_ACCOUNT_ID: env.R2_ACCOUNT_ID,
R2_BUCKET: env.R2_BUCKET
};
try {
const url = await generateR2PresignedUrl(
{
key,
contentType,
expiresIn: 3600
},
credentials
);
return new Response(JSON.stringify({ url }), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response(
JSON.stringify({ error: error.message }),
{ status: 400, headers: { 'Content-Type': 'application/json' } }
);
}
}
return new Response('Method not allowed', { status: 405 });
}
};
```
## Environment Variables
For Cloudflare Workers, add these to your `wrangler.toml`:
```toml
[vars]
R2_ACCESS_KEY_ID = "your-access-key"
R2_SECRET_ACCESS_KEY = "your-secret-key"
R2_ACCOUNT_ID = "your-account-id"
R2_BUCKET = "your-bucket-name"
```
## Error Handling
The library throws descriptive errors for common issues:
```typescript
try {
const url = await generateR2PresignedUrl(options, credentials);
} catch (error) {
if (error.message.includes('Missing required R2 credentials')) {
// Handle missing credentials
} else if (error.message.includes('Invalid parameters')) {
// Handle invalid parameters
} else {
// Handle other errors
}
}
```
## Browser Compatibility
This library uses the Web Crypto API, which is supported in:
- Chrome 37+
- Firefox 34+
- Safari 11+
- Edge 12+
- Node.js 15+
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests if applicable
5. Submit a pull request
## License
MIT License - see [LICENSE](LICENSE) file for details.
## Changelog
### 1.0.0
- Initial release
- AWS Signature Version 4 implementation
- Cloudflare Workers compatibility
- TypeScript support
- Comprehensive error handling