Add support for more decentralized storage providers
Added 4 new storage providers: - NFT.Storage (free for NFT data) - Infura IPFS (5GB free) - Filebase (5GB free, S3-compatible IPFS) - Storj (25GB free, decentralized cloud) Updated UI with 7 total storage options in a 4-column grid, each with distinct colored borders for visibility. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
119
server.py
119
server.py
@@ -3089,7 +3089,8 @@ async def list_storage(request: Request, user: User = Depends(get_optional_user)
|
||||
async def add_storage(req: AddStorageRequest, user: User = Depends(get_required_user)):
|
||||
"""Add a storage provider."""
|
||||
# Validate provider type
|
||||
if req.provider_type not in ["pinata", "web3storage", "local"]:
|
||||
valid_types = ["pinata", "web3storage", "nftstorage", "infura", "filebase", "storj", "local"]
|
||||
if req.provider_type not in valid_types:
|
||||
raise HTTPException(400, f"Invalid provider type: {req.provider_type}")
|
||||
|
||||
# Test the provider connection before saving
|
||||
@@ -3129,6 +3130,10 @@ async def add_storage_form(
|
||||
api_key: Optional[str] = Form(None),
|
||||
secret_key: Optional[str] = Form(None),
|
||||
api_token: Optional[str] = Form(None),
|
||||
project_id: Optional[str] = Form(None),
|
||||
project_secret: Optional[str] = Form(None),
|
||||
access_key: Optional[str] = Form(None),
|
||||
bucket: Optional[str] = Form(None),
|
||||
path: Optional[str] = Form(None),
|
||||
):
|
||||
"""Add a storage provider via HTML form (cookie auth)."""
|
||||
@@ -3137,7 +3142,8 @@ async def add_storage_form(
|
||||
return HTMLResponse('<div class="text-red-400">Not authenticated</div>', status_code=401)
|
||||
|
||||
# Validate provider type
|
||||
if provider_type not in ["pinata", "web3storage", "local"]:
|
||||
valid_types = ["pinata", "web3storage", "nftstorage", "infura", "filebase", "storj", "local"]
|
||||
if provider_type not in valid_types:
|
||||
return HTMLResponse(f'<div class="text-red-400">Invalid provider type: {provider_type}</div>')
|
||||
|
||||
# Build config based on provider type
|
||||
@@ -3150,6 +3156,22 @@ async def add_storage_form(
|
||||
if not api_token:
|
||||
return HTMLResponse('<div class="text-red-400">web3.storage requires API Token</div>')
|
||||
config = {"api_token": api_token}
|
||||
elif provider_type == "nftstorage":
|
||||
if not api_token:
|
||||
return HTMLResponse('<div class="text-red-400">NFT.Storage requires API Token</div>')
|
||||
config = {"api_token": api_token}
|
||||
elif provider_type == "infura":
|
||||
if not project_id or not project_secret:
|
||||
return HTMLResponse('<div class="text-red-400">Infura requires Project ID and Project Secret</div>')
|
||||
config = {"project_id": project_id, "project_secret": project_secret}
|
||||
elif provider_type == "filebase":
|
||||
if not access_key or not secret_key or not bucket:
|
||||
return HTMLResponse('<div class="text-red-400">Filebase requires Access Key, Secret Key, and Bucket</div>')
|
||||
config = {"access_key": access_key, "secret_key": secret_key, "bucket": bucket}
|
||||
elif provider_type == "storj":
|
||||
if not access_key or not secret_key or not bucket:
|
||||
return HTMLResponse('<div class="text-red-400">Storj requires Access Key, Secret Key, and Bucket</div>')
|
||||
config = {"access_key": access_key, "secret_key": secret_key, "bucket": bucket}
|
||||
elif provider_type == "local":
|
||||
if not path:
|
||||
return HTMLResponse('<div class="text-red-400">Local storage requires a path</div>')
|
||||
@@ -3359,21 +3381,41 @@ async def ui_storage_page(username: str, storages: list, request: Request) -> HT
|
||||
|
||||
<h2 class="text-lg font-semibold text-white mb-4">Add Storage Provider</h2>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-4">
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-3 mb-4">
|
||||
<button onclick="showAddForm('pinata')"
|
||||
class="p-4 bg-dark-800 hover:bg-dark-600 border-2 border-blue-600 hover:border-blue-400 rounded-lg text-left transition-colors">
|
||||
<div class="font-semibold text-white">Pinata</div>
|
||||
<div class="text-sm text-gray-400">IPFS pinning service</div>
|
||||
class="p-3 bg-dark-800 hover:bg-dark-600 border-2 border-blue-600 hover:border-blue-400 rounded-lg text-left transition-colors">
|
||||
<div class="font-semibold text-white text-sm">Pinata</div>
|
||||
<div class="text-xs text-gray-400">1GB free, IPFS</div>
|
||||
</button>
|
||||
<button onclick="showAddForm('web3storage')"
|
||||
class="p-4 bg-dark-800 hover:bg-dark-600 border-2 border-green-600 hover:border-green-400 rounded-lg text-left transition-colors">
|
||||
<div class="font-semibold text-white">web3.storage</div>
|
||||
<div class="text-sm text-gray-400">IPFS + Filecoin</div>
|
||||
class="p-3 bg-dark-800 hover:bg-dark-600 border-2 border-green-600 hover:border-green-400 rounded-lg text-left transition-colors">
|
||||
<div class="font-semibold text-white text-sm">web3.storage</div>
|
||||
<div class="text-xs text-gray-400">IPFS + Filecoin</div>
|
||||
</button>
|
||||
<button onclick="showAddForm('nftstorage')"
|
||||
class="p-3 bg-dark-800 hover:bg-dark-600 border-2 border-pink-600 hover:border-pink-400 rounded-lg text-left transition-colors">
|
||||
<div class="font-semibold text-white text-sm">NFT.Storage</div>
|
||||
<div class="text-xs text-gray-400">Free for NFTs</div>
|
||||
</button>
|
||||
<button onclick="showAddForm('infura')"
|
||||
class="p-3 bg-dark-800 hover:bg-dark-600 border-2 border-orange-600 hover:border-orange-400 rounded-lg text-left transition-colors">
|
||||
<div class="font-semibold text-white text-sm">Infura IPFS</div>
|
||||
<div class="text-xs text-gray-400">5GB free</div>
|
||||
</button>
|
||||
<button onclick="showAddForm('filebase')"
|
||||
class="p-3 bg-dark-800 hover:bg-dark-600 border-2 border-cyan-600 hover:border-cyan-400 rounded-lg text-left transition-colors">
|
||||
<div class="font-semibold text-white text-sm">Filebase</div>
|
||||
<div class="text-xs text-gray-400">5GB free, S3+IPFS</div>
|
||||
</button>
|
||||
<button onclick="showAddForm('storj')"
|
||||
class="p-3 bg-dark-800 hover:bg-dark-600 border-2 border-indigo-600 hover:border-indigo-400 rounded-lg text-left transition-colors">
|
||||
<div class="font-semibold text-white text-sm">Storj</div>
|
||||
<div class="text-xs text-gray-400">25GB free</div>
|
||||
</button>
|
||||
<button onclick="showAddForm('local')"
|
||||
class="p-4 bg-dark-800 hover:bg-dark-600 border-2 border-purple-600 hover:border-purple-400 rounded-lg text-left transition-colors">
|
||||
<div class="font-semibold text-white">Local Storage</div>
|
||||
<div class="text-sm text-gray-400">Your own disk</div>
|
||||
class="p-3 bg-dark-800 hover:bg-dark-600 border-2 border-purple-600 hover:border-purple-400 rounded-lg text-left transition-colors">
|
||||
<div class="font-semibold text-white text-sm">Local Storage</div>
|
||||
<div class="text-xs text-gray-400">Your own disk</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -3399,6 +3441,54 @@ async def ui_storage_page(username: str, storages: list, request: Request) -> HT
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="nftstorage-fields" class="hidden space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1">API Token</label>
|
||||
<input type="password" name="api_token" class="w-full px-3 py-2 bg-dark-700 border border-dark-500 rounded text-white">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="infura-fields" class="hidden space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1">Project ID</label>
|
||||
<input type="text" name="project_id" class="w-full px-3 py-2 bg-dark-700 border border-dark-500 rounded text-white">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1">Project Secret</label>
|
||||
<input type="password" name="project_secret" class="w-full px-3 py-2 bg-dark-700 border border-dark-500 rounded text-white">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="filebase-fields" class="hidden space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1">Access Key</label>
|
||||
<input type="text" name="access_key" class="w-full px-3 py-2 bg-dark-700 border border-dark-500 rounded text-white">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1">Secret Key</label>
|
||||
<input type="password" name="secret_key" class="w-full px-3 py-2 bg-dark-700 border border-dark-500 rounded text-white">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1">Bucket Name</label>
|
||||
<input type="text" name="bucket" placeholder="my-bucket" class="w-full px-3 py-2 bg-dark-700 border border-dark-500 rounded text-white">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="storj-fields" class="hidden space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1">Access Key</label>
|
||||
<input type="text" name="access_key" class="w-full px-3 py-2 bg-dark-700 border border-dark-500 rounded text-white">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1">Secret Key</label>
|
||||
<input type="password" name="secret_key" class="w-full px-3 py-2 bg-dark-700 border border-dark-500 rounded text-white">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1">Bucket Name</label>
|
||||
<input type="text" name="bucket" placeholder="my-bucket" class="w-full px-3 py-2 bg-dark-700 border border-dark-500 rounded text-white">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="local-fields" class="hidden space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm text-gray-400 mb-1">Storage Path</label>
|
||||
@@ -3438,9 +3528,8 @@ async def ui_storage_page(username: str, storages: list, request: Request) -> HT
|
||||
document.getElementById('provider_type').value = type;
|
||||
|
||||
// Hide all field groups
|
||||
document.getElementById('pinata-fields').classList.add('hidden');
|
||||
document.getElementById('web3storage-fields').classList.add('hidden');
|
||||
document.getElementById('local-fields').classList.add('hidden');
|
||||
const fieldGroups = ['pinata', 'web3storage', 'nftstorage', 'infura', 'filebase', 'storj', 'local'];
|
||||
fieldGroups.forEach(g => document.getElementById(g + '-fields').classList.add('hidden'));
|
||||
|
||||
// Show the relevant fields
|
||||
document.getElementById(type + '-fields').classList.remove('hidden');
|
||||
|
||||
Reference in New Issue
Block a user