UNPKG

@synet/fs-digitalocean

Version:

DigitalOcean Spaces filesystem abstraction following FileSystem pattern

360 lines (269 loc) 8.92 kB
# @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**