smithery/vanman2024

spaces-storage

DigitalOcean Spaces (S3-compatible) object storage patterns

Installation

$ npx skills add smithery/vanman2024 --skill spaces-storage

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/vanman2024 · top by installs.

npx skills add smithery/vanman2024

Browse all from smithery/vanman2024

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 7,802 B
  • docs SUMMARY.md 81 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

Spaces Object Storage Skill

Overview

DigitalOcean Spaces provides S3-compatible object storage. Use it for:

  • File uploads (images, documents, media)
  • Static asset hosting
  • Application backups
  • Data lake storage

Setup

Create Space

doctl spaces create my-space --region nyc3

CDN (Built-in)

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

SDK Integration

Node.js (@aws-sdk/client-s3)

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,
    })
  );
}

Python (boto3)

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)

Next.js Integration

API Route for File Upload

// 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 });
}

Client Upload Component

"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}
    />
  );
}

FastAPI Integration

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
    }

Environment Variables

# .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

CORS Configuration

Configure CORS via the DigitalOcean console or API:

{
  "CORSRules": [
    {
      "AllowedOrigins": ["https://myapp.com"],
      "AllowedMethods": ["GET", "PUT", "POST"],
      "AllowedHeaders": ["*"],
      "MaxAgeSeconds": 3000
    }
  ]
}