com um clique
DigitalOcean Spaces (S3-compatible) object storage patterns
npx skills add https://github.com/vanman2024/ai-dev-marketplace --skill spaces-storageCopie e cole este comando no Claude Code para instalar a skill
DigitalOcean Spaces (S3-compatible) object storage patterns
npx skills add https://github.com/vanman2024/ai-dev-marketplace --skill spaces-storageCopie e cole este comando no Claude Code para instalar a skill
Google File Search API patterns for managed RAG with Gemini. Covers both TypeScript (@google/genai) and Python (google-genai) SDKs. Use when building File Search integrations, implementing RAG with Google AI, or when user mentions Google File Search, Gemini RAG, document indexing, or semantic search.
BullMQ queue configuration patterns including connection pooling, job options, rate limiting, and TypeScript types. Use when setting up queues or configuring job behavior.
BullMQ worker implementation patterns including job processors, concurrency, error handling, and graceful shutdown. Use when creating workers or handling job processing.
DigitalOcean Managed Database provisioning and connection patterns
DigitalOcean networking patterns - VPCs, firewalls, load balancers, DNS
LangGraph checkpointing patterns for state persistence with memory, SQLite, and Postgres backends. Use when implementing state recovery or human-in-the-loop workflows.
| name | spaces-storage |
| description | DigitalOcean Spaces (S3-compatible) object storage patterns |
DigitalOcean Spaces provides S3-compatible object storage. Use it for:
doctl spaces create my-space --region nyc3
Enable CDN for faster global access to your assets.
# CDN endpoint
https://my-space.nyc3.cdn.digitaloceanspaces.com/file.jpg
# Direct endpoint
https://my-space.nyc3.digitaloceanspaces.com/file.jpg
import {
S3Client,
PutObjectCommand,
GetObjectCommand,
DeleteObjectCommand,
ListObjectsV2Command,
} from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
const s3 = new S3Client({
endpoint: 'https://nyc3.digitaloceanspaces.com',
region: 'nyc3',
credentials: {
accessKeyId: process.env.DIGITALOCEAN_SPACES_ACCESS_KEY!,
secretAccessKey: process.env.DIGITALOCEAN_SPACES_SECRET_KEY!,
},
});
const BUCKET = 'my-space';
// Upload file
async function uploadFile(key: string, body: Buffer, contentType: string) {
await s3.send(
new PutObjectCommand({
Bucket: BUCKET,
Key: key,
Body: body,
ContentType: contentType,
ACL: 'public-read', // or "private"
})
);
return `https://${BUCKET}.nyc3.cdn.digitaloceanspaces.com/${key}`;
}
// Download file
async function downloadFile(key: string) {
const response = await s3.send(
new GetObjectCommand({
Bucket: BUCKET,
Key: key,
})
);
return response.Body;
}
// Generate presigned URL (for private uploads)
async function getPresignedUploadUrl(key: string, expiresIn = 3600) {
const command = new PutObjectCommand({
Bucket: BUCKET,
Key: key,
});
return getSignedUrl(s3, command, { expiresIn });
}
// Generate presigned download URL (for private files)
async function getPresignedDownloadUrl(key: string, expiresIn = 3600) {
const command = new GetObjectCommand({
Bucket: BUCKET,
Key: key,
});
return getSignedUrl(s3, command, { expiresIn });
}
// List files
async function listFiles(prefix = '') {
const response = await s3.send(
new ListObjectsV2Command({
Bucket: BUCKET,
Prefix: prefix,
})
);
return response.Contents || [];
}
// Delete file
async function deleteFile(key: string) {
await s3.send(
new DeleteObjectCommand({
Bucket: BUCKET,
Key: key,
})
);
}
import boto3
from botocore.config import Config
session = boto3.session.Session()
s3 = session.client(
's3',
region_name='nyc3',
endpoint_url='https://nyc3.digitaloceanspaces.com',
aws_access_key_id=os.environ['DIGITALOCEAN_SPACES_ACCESS_KEY'],
aws_secret_access_key=os.environ['DIGITALOCEAN_SPACES_SECRET_KEY']
)
BUCKET = 'my-space'
# Upload file
def upload_file(key: str, file_path: str, content_type: str = None):
extra_args = {'ACL': 'public-read'}
if content_type:
extra_args['ContentType'] = content_type
s3.upload_file(file_path, BUCKET, key, ExtraArgs=extra_args)
return f'https://{BUCKET}.nyc3.cdn.digitaloceanspaces.com/{key}'
# Upload from memory
def upload_bytes(key: str, data: bytes, content_type: str):
s3.put_object(
Bucket=BUCKET,
Key=key,
Body=data,
ContentType=content_type,
ACL='public-read'
)
return f'https://{BUCKET}.nyc3.cdn.digitaloceanspaces.com/{key}'
# Download file
def download_file(key: str, file_path: str):
s3.download_file(BUCKET, key, file_path)
# Generate presigned URL
def get_presigned_url(key: str, expires_in: int = 3600):
return s3.generate_presigned_url(
'get_object',
Params={'Bucket': BUCKET, 'Key': key},
ExpiresIn=expires_in
)
# List files
def list_files(prefix: str = ''):
response = s3.list_objects_v2(Bucket=BUCKET, Prefix=prefix)
return response.get('Contents', [])
# Delete file
def delete_file(key: str):
s3.delete_object(Bucket=BUCKET, Key=key)
// app/api/upload/route.ts
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { nanoid } from 'nanoid';
const s3 = new S3Client({
endpoint: 'https://nyc3.digitaloceanspaces.com',
region: 'nyc3',
credentials: {
accessKeyId: process.env.DIGITALOCEAN_SPACES_ACCESS_KEY!,
secretAccessKey: process.env.DIGITALOCEAN_SPACES_SECRET_KEY!,
},
});
export async function POST(request: Request) {
const { filename, contentType } = await request.json();
const key = `uploads/${nanoid()}-${filename}`;
const command = new PutObjectCommand({
Bucket: process.env.DIGITALOCEAN_SPACES_BUCKET!,
Key: key,
ContentType: contentType,
ACL: 'public-read',
});
const uploadUrl = await getSignedUrl(s3, command, { expiresIn: 3600 });
const publicUrl = `https://${process.env.DIGITALOCEAN_SPACES_BUCKET}.nyc3.cdn.digitaloceanspaces.com/${key}`;
return Response.json({ uploadUrl, publicUrl, key });
}
"use client";
import { useState } from "react";
export function FileUpload({ onUpload }: { onUpload: (url: string) => void }) {
const [uploading, setUploading] = useState(false);
async function handleUpload(e: React.ChangeEvent<HTMLInputElement>) {
const file = e.target.files?.[0];
if (!file) return;
setUploading(true);
try {
// Get presigned URL
const response = await fetch("/api/upload", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
filename: file.name,
contentType: file.type,
}),
});
const { uploadUrl, publicUrl } = await response.json();
// Upload directly to Spaces
await fetch(uploadUrl, {
method: "PUT",
headers: { "Content-Type": file.type },
body: file,
});
onUpload(publicUrl);
} finally {
setUploading(false);
}
}
return (
<input
type="file"
onChange={handleUpload}
disabled={uploading}
/>
);
}
from fastapi import FastAPI, UploadFile
import boto3
from nanoid import generate
app = FastAPI()
s3 = boto3.client(
's3',
region_name='nyc3',
endpoint_url='https://nyc3.digitaloceanspaces.com',
aws_access_key_id=os.environ['DIGITALOCEAN_SPACES_ACCESS_KEY'],
aws_secret_access_key=os.environ['DIGITALOCEAN_SPACES_SECRET_KEY']
)
BUCKET = os.environ['DIGITALOCEAN_SPACES_BUCKET']
@app.post("/upload")
async def upload_file(file: UploadFile):
key = f"uploads/{generate()}-{file.filename}"
s3.upload_fileobj(
file.file,
BUCKET,
key,
ExtraArgs={
'ACL': 'public-read',
'ContentType': file.content_type
}
)
return {
"url": f"https://{BUCKET}.nyc3.cdn.digitaloceanspaces.com/{key}",
"key": key
}
# .env
DIGITALOCEAN_SPACES_ACCESS_KEY=your_access_key
DIGITALOCEAN_SPACES_SECRET_KEY=your_secret_key
DIGITALOCEAN_SPACES_BUCKET=my-space
DIGITALOCEAN_SPACES_REGION=nyc3
DIGITALOCEAN_SPACES_ENDPOINT=https://nyc3.digitaloceanspaces.com
Configure CORS via the DigitalOcean console or API:
{
"CORSRules": [
{
"AllowedOrigins": ["https://myapp.com"],
"AllowedMethods": ["GET", "PUT", "POST"],
"AllowedHeaders": ["*"],
"MaxAgeSeconds": 3000
}
]
}