Skip to main content

File Storage Plugin

Upload and manage files with support for local storage and cloud providers.

Supported Providers

ProviderDescription
LocalFile system storage
AWS S3Amazon S3
Cloudflare R2S3-compatible storage
DigitalOcean SpacesS3-compatible
Backblaze B2Affordable cloud storage

Installation

npx autodeploybase add file-storage

Configuration

autodeploy.config.json
{
"plugins": {
"file-storage": {
"enabled": true,
"settings": {
"provider": "s3",
"bucket": "my-app-uploads",
"maxFileSize": 10485760,
"allowedTypes": ["image/*", "application/pdf"]
}
}
}
}

Environment Variables

AWS S3

STORAGE_PROVIDER=s3
S3_BUCKET=my-bucket
S3_REGION=us-east-1
S3_ACCESS_KEY=AKIA...
S3_SECRET_KEY=...
S3_ENDPOINT= # Optional, for S3-compatible services

Cloudflare R2

STORAGE_PROVIDER=r2
R2_ACCOUNT_ID=...
R2_ACCESS_KEY=...
R2_SECRET_KEY=...
R2_BUCKET=my-bucket

Local

STORAGE_PROVIDER=local
STORAGE_PATH=./uploads

Usage

Upload File

import { uploadFile } from '@/plugins/file-storage';

// From request
export async function POST(request: Request) {
const formData = await request.formData();
const file = formData.get('file') as File;

const result = await uploadFile(file, {
folder: 'avatars',
generateUniqueName: true
});

return Response.json({
url: result.url,
key: result.key,
size: result.size
});
}

Upload Buffer

import { uploadBuffer } from '@/plugins/file-storage';

const buffer = await generatePDF(data);

const result = await uploadBuffer(buffer, {
filename: 'report.pdf',
contentType: 'application/pdf',
folder: 'reports'
});

Get File URL

import { getFileUrl } from '@/plugins/file-storage';

// Public URL
const url = await getFileUrl('avatars/user-123.jpg');

// Signed URL (temporary access)
const signedUrl = await getFileUrl('private/document.pdf', {
signed: true,
expiresIn: 3600 // 1 hour
});

Delete File

import { deleteFile } from '@/plugins/file-storage';

await deleteFile('avatars/old-avatar.jpg');

List Files

import { listFiles } from '@/plugins/file-storage';

const files = await listFiles({
prefix: 'avatars/',
maxKeys: 100
});

// files: [{ key, size, lastModified }, ...]

React Components

FileUpload

import { FileUpload } from '@/plugins/file-storage/components';

export function AvatarUploader() {
const handleUpload = async (file: File) => {
const result = await uploadFile(file, { folder: 'avatars' });
console.log('Uploaded:', result.url);
};

return (
<FileUpload accept="image/*" maxSize={5 * 1024 * 1024} onUpload={handleUpload}>
<p>Drop image here or click to upload</p>
</FileUpload>
);
}

ImagePreview

import { ImagePreview } from '@/plugins/file-storage/components';

<ImagePreview
src={user.avatarUrl}
alt={user.name}
width={100}
height={100}
fallback="/default-avatar.png"
/>;

Image Processing

Resize on Upload

import { uploadImage } from '@/plugins/file-storage';

const result = await uploadImage(file, {
folder: 'products',
resize: {
width: 800,
height: 600,
fit: 'cover'
},
formats: ['webp', 'jpg']
});

// result.variants: { webp: url, jpg: url }

Generate Thumbnails

const result = await uploadImage(file, {
folder: 'products',
thumbnails: [
{ name: 'small', width: 100, height: 100 },
{ name: 'medium', width: 400, height: 400 },
{ name: 'large', width: 800, height: 800 }
]
});

// result.thumbnails: { small: url, medium: url, large: url }

Security

Validation

import { validateFile } from '@/plugins/file-storage';

const validation = validateFile(file, {
maxSize: 10 * 1024 * 1024, // 10MB
allowedTypes: ['image/jpeg', 'image/png', 'application/pdf'],
allowedExtensions: ['.jpg', '.png', '.pdf']
});

if (!validation.valid) {
return Response.json({ error: validation.error }, { status: 400 });
}

Private Files

// Upload as private
await uploadFile(file, {
folder: 'private',
acl: 'private'
});

// Access with signed URL
const url = await getFileUrl('private/doc.pdf', {
signed: true,
expiresIn: 3600
});

API Routes

Upload Endpoint

app/api/upload/route.ts
import { NextResponse } from 'next/server';
import { uploadFile, validateFile } from '@/plugins/file-storage';
import { requireAuth } from '@/lib/auth';

export async function POST(request: Request) {
const user = await requireAuth(request);

const formData = await request.formData();
const file = formData.get('file') as File;

const validation = validateFile(file, {
maxSize: 5 * 1024 * 1024,
allowedTypes: ['image/*']
});

if (!validation.valid) {
return NextResponse.json({ error: validation.error }, { status: 400 });
}

const result = await uploadFile(file, {
folder: `users/${user.id}`,
generateUniqueName: true
});

return NextResponse.json(result);
}

Database Integration

File Model

model File {
id String @id @default(cuid())
key String @unique
filename String
contentType String
size Int
url String
userId String
user User @relation(fields: [userId], references: [id])
createdAt DateTime @default(now())
}

Track Uploads

const result = await uploadFile(file, { folder: 'documents' });

await prisma.file.create({
data: {
key: result.key,
filename: file.name,
contentType: file.type,
size: file.size,
url: result.url,
userId: user.id
}
});