@synet/fs-digitalocean
Version:
DigitalOcean Spaces filesystem abstraction following FileSystem pattern
360 lines (269 loc) • 8.92 kB
Markdown
# @synet/fs-digitalocean
**DigitalOcean Spaces FileSystem Adapter** - High-performance object storage with S3-compatible API, built-in CDN, and developer-friendly pricing.
Perfect for applications requiring reliable cloud storage with predictable costs and global edge locations.
## Features
- **S3-Compatible API**: Full compatibility with AWS S3 SDKs and tools
- **Built-in CDN**: Global content delivery with 300+ edge locations
- **Developer Pricing**: Simple, predictable pricing starting at $5/month
- **High Performance**: SSD-backed storage with low latency
- **Global Regions**: 8+ regions worldwide for optimal performance
- **TypeScript First**: Complete type safety with full interface definitions
## Installation
```bash
npm install @synet/fs-digitalocean
```
## Quick Start
```typescript
import { DigitalOceanSpacesFileSystem } from '@synet/fs-digitalocean';
const doFs = new DigitalOceanSpacesFileSystem({
endpoint: 'https://nyc3.digitaloceanspaces.com',
accessKeyId: process.env.DO_ACCESS_KEY!,
secretAccessKey: process.env.DO_SECRET_KEY!,
bucket: 'my-app-storage',
region: 'nyc3',
prefix: 'app-data/' // Optional: acts as root directory
});
// File operations
await doFs.writeFile('/config.json', JSON.stringify({ app: 'myapp' }));
const config = await doFs.readFile('/config.json');
// Directory operations
await doFs.ensureDir('/uploads');
const files = await doFs.readDir('/uploads');
```
## Configuration
### Environment Variables
```bash
# .env
DO_ACCESS_KEY=your_access_key
DO_SECRET_KEY=your_secret_key
DO_REGION=nyc3
DO_BUCKET=my-storage
DO_ENDPOINT=https://nyc3.digitaloceanspaces.com
```
### DigitalOcean Setup
1. **Create a Space** in DigitalOcean Control Panel
2. **Generate API Keys**:
- Go to API → Spaces Keys
- Generate new key pair
- Note the access key and secret key
3. **Configure CDN** (Optional):
- Enable CDN for global distribution
- Set custom domain if needed
### Regional Endpoints
| Region | Endpoint | Location |
|--------|----------|----------|
| `nyc3` | `https://nyc3.digitaloceanspaces.com` | New York |
| `ams3` | `https://ams3.digitaloceanspaces.com` | Amsterdam |
| `sgp1` | `https://sgp1.digitaloceanspaces.com` | Singapore |
| `sfo3` | `https://sfo3.digitaloceanspaces.com` | San Francisco |
| `fra1` | `https://fra1.digitaloceanspaces.com` | Frankfurt |
## Advanced Usage
### Multi-Environment Setup
```typescript
const createSpacesFS = (environment: string) => {
return new DigitalOceanSpacesFileSystem({
endpoint: `https://${process.env.DO_REGION}.digitaloceanspaces.com`,
accessKeyId: process.env.DO_ACCESS_KEY!,
secretAccessKey: process.env.DO_SECRET_KEY!,
bucket: process.env.DO_BUCKET!,
region: process.env.DO_REGION!,
prefix: `${environment}/`
});
};
const prodFs = createSpacesFS('production');
const stagingFs = createSpacesFS('staging');
```
### CDN Integration
```typescript
const spacesFs = new DigitalOceanSpacesFileSystem({
endpoint: 'https://nyc3.digitaloceanspaces.com',
accessKeyId: process.env.DO_ACCESS_KEY!,
secretAccessKey: process.env.DO_SECRET_KEY!,
bucket: 'my-cdn-space',
region: 'nyc3'
});
// Upload assets
await spacesFs.writeFile('/assets/logo.png', imageBuffer);
// CDN URL (if CDN is enabled)
const cdnUrl = `https://my-cdn-space.nyc3.cdn.digitaloceanspaces.com/assets/logo.png`;
```
### Backup and Sync
```typescript
class SpacesBackup {
private spaces: DigitalOceanSpacesFileSystem;
constructor() {
this.spaces = new DigitalOceanSpacesFileSystem({
endpoint: 'https://nyc3.digitaloceanspaces.com',
accessKeyId: process.env.DO_ACCESS_KEY!,
secretAccessKey: process.env.DO_SECRET_KEY!,
bucket: 'backups',
region: 'nyc3',
prefix: 'daily/'
});
}
async backupData(data: any): Promise<void> {
const timestamp = new Date().toISOString().split('T')[0];
await this.spaces.writeFile(`/backup-${timestamp}.json`, JSON.stringify(data));
}
}
```
## API Reference
### Constructor Options
```typescript
interface DigitalOceanSpacesOptions {
endpoint: string; // DigitalOcean Spaces endpoint
accessKeyId: string; // DO Spaces access key
secretAccessKey: string; // DO Spaces secret key
bucket: string; // Space name
region: string; // DO region
prefix?: string; // Optional prefix for all operations
}
```
### File Operations
- `writeFile(path: string, data: string): Promise<void>` - Upload file
- `readFile(path: string): Promise<string>` - Download file content
- `deleteFile(path: string): Promise<void>` - Delete file
- `exists(path: string): Promise<boolean>` - Check if file exists
### Directory Operations
- `ensureDir(path: string): Promise<void>` - Ensure directory exists (no-op)
- `deleteDir(path: string): Promise<void>` - Delete all files with prefix
- `readDir(path: string): Promise<string[]>` - List files with prefix
### Metadata Operations
- `stat(path: string): Promise<FileStats>` - Get file metadata
## Testing
```bash
# Run tests
npm test
# Run with coverage
npm run coverage
# Type checking
npm run type-check
```
### Test Configuration
```typescript
import { DigitalOceanSpacesFileSystem } from '@synet/fs-digitalocean';
const testSpaces = new DigitalOceanSpacesFileSystem({
endpoint: 'https://nyc3.digitaloceanspaces.com',
accessKeyId: process.env.DO_TEST_ACCESS_KEY!,
secretAccessKey: process.env.DO_TEST_SECRET_KEY!,
bucket: 'test-space',
region: 'nyc3',
prefix: 'test-data/'
});
describe('DigitalOcean Spaces Integration', () => {
it('should handle file operations', async () => {
await testSpaces.writeFile('/test.json', '{"test": true}');
const content = await testSpaces.readFile('/test.json');
expect(JSON.parse(content)).toEqual({ test: true });
});
});
```
## Performance
### Concurrent Operations
```typescript
// Parallel uploads
const uploadPromises = files.map(file =>
spacesFs.writeFile(file.path, file.content)
);
await Promise.all(uploadPromises);
// Batch operations
const contents = await Promise.all(
paths.map(path => spacesFs.readFile(path))
);
```
### Caching
```typescript
class CachedSpacesFS {
private spaces: DigitalOceanSpacesFileSystem;
private cache = new Map<string, { content: string; timestamp: number }>();
private ttl = 5 * 60 * 1000; // 5 minutes
async readFile(path: string): Promise<string> {
const cached = this.cache.get(path);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.content;
}
const content = await this.spaces.readFile(path);
this.cache.set(path, { content, timestamp: Date.now() });
return content;
}
}
```
## Error Handling
```typescript
try {
await spacesFs.writeFile('/config.json', JSON.stringify(config));
} catch (error: any) {
if (error.name === 'NoSuchBucket') {
console.error('Space does not exist');
} else if (error.name === 'AccessDenied') {
console.error('Invalid credentials or permissions');
} else if (error.statusCode === 403) {
console.error('Access denied - check API keys');
} else {
console.error('Upload failed:', error.message);
}
}
```
### Retry Logic
```typescript
async function writeWithRetry(path: string, content: string, maxRetries = 3): Promise<void> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
await spacesFs.writeFile(path, content);
return;
} catch (error: any) {
if (attempt === maxRetries || error.statusCode === 403) {
throw error;
}
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
}
```
## Cost Optimization
### Storage Classes
- **Standard**: For frequently accessed data
- **Archive**: For long-term backup (coming soon)
### Transfer Optimization
```typescript
// Minimize API calls with batch operations
const batchSize = 10;
for (let i = 0; i < files.length; i += batchSize) {
const batch = files.slice(i, i + batchSize);
await Promise.all(batch.map(file => spacesFs.writeFile(file.path, file.content)));
}
```
## Development
```bash
# Install dependencies
npm install
# Build package
npm run build
# Run linting
npm run lint
# Format code
npm run format
```
## License
MIT License - see [LICENSE](LICENSE) file for details.
## Related Packages
- **[@synet/fs](https://www.npmjs.com/package/@synet/fs)** - Core filesystem abstraction
- **[@synet/fs-azure](https://www.npmjs.com/package/@synet/fs-azure)** - Azure Blob Storage adapter
- **[@synet/fs-gcs](https://www.npmjs.com/package/@synet/fs-gcs)** - Google Cloud Storage adapter
- **[@synet/fs-s3](https://www.npmjs.com/package/@synet/fs-s3)** - AWS S3 storage adapter
- **[@synet/fs-linode](https://www.npmjs.com/package/@synet/fs-linode)** - Linode Object Storage adapter
**Built with ❤️ by the SYNET Team**