Add form-based storage endpoint for browser submissions
The POST /storage endpoint required Bearer token auth and JSON body, which didn't work with browser form submissions using cookies. Added new /storage/add endpoint that accepts form data and cookie auth. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
94
server.py
94
server.py
@@ -27,7 +27,7 @@ logging.basicConfig(
|
|||||||
)
|
)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
from fastapi import FastAPI, HTTPException, Request, Response, Depends, Cookie
|
from fastapi import FastAPI, HTTPException, Request, Response, Depends, Cookie, Form
|
||||||
from fastapi.responses import JSONResponse, HTMLResponse, RedirectResponse, FileResponse
|
from fastapi.responses import JSONResponse, HTMLResponse, RedirectResponse, FileResponse
|
||||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
@@ -3120,6 +3120,72 @@ async def add_storage(req: AddStorageRequest, user: User = Depends(get_required_
|
|||||||
return {"id": storage_id, "message": f"Storage provider added: {provider_name}"}
|
return {"id": storage_id, "message": f"Storage provider added: {provider_name}"}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/storage/add")
|
||||||
|
async def add_storage_form(
|
||||||
|
request: Request,
|
||||||
|
provider_type: str = Form(...),
|
||||||
|
provider_name: Optional[str] = Form(None),
|
||||||
|
capacity_gb: int = Form(5),
|
||||||
|
api_key: Optional[str] = Form(None),
|
||||||
|
secret_key: Optional[str] = Form(None),
|
||||||
|
api_token: Optional[str] = Form(None),
|
||||||
|
path: Optional[str] = Form(None),
|
||||||
|
):
|
||||||
|
"""Add a storage provider via HTML form (cookie auth)."""
|
||||||
|
username = get_user_from_cookie(request)
|
||||||
|
if not username:
|
||||||
|
return HTMLResponse('<div class="text-red-400">Not authenticated</div>', status_code=401)
|
||||||
|
|
||||||
|
# Validate provider type
|
||||||
|
if provider_type not in ["pinata", "web3storage", "local"]:
|
||||||
|
return HTMLResponse(f'<div class="text-red-400">Invalid provider type: {provider_type}</div>')
|
||||||
|
|
||||||
|
# Build config based on provider type
|
||||||
|
config = {}
|
||||||
|
if provider_type == "pinata":
|
||||||
|
if not api_key or not secret_key:
|
||||||
|
return HTMLResponse('<div class="text-red-400">Pinata requires API Key and Secret Key</div>')
|
||||||
|
config = {"api_key": api_key, "secret_key": secret_key}
|
||||||
|
elif provider_type == "web3storage":
|
||||||
|
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 == "local":
|
||||||
|
if not path:
|
||||||
|
return HTMLResponse('<div class="text-red-400">Local storage requires a path</div>')
|
||||||
|
config = {"path": path}
|
||||||
|
|
||||||
|
# Test the provider connection before saving
|
||||||
|
provider = storage_providers.create_provider(provider_type, {
|
||||||
|
**config,
|
||||||
|
"capacity_gb": capacity_gb
|
||||||
|
})
|
||||||
|
if not provider:
|
||||||
|
return HTMLResponse('<div class="text-red-400">Failed to create provider with given config</div>')
|
||||||
|
|
||||||
|
success, message = await provider.test_connection()
|
||||||
|
if not success:
|
||||||
|
return HTMLResponse(f'<div class="text-red-400">Provider connection failed: {message}</div>')
|
||||||
|
|
||||||
|
# Save to database
|
||||||
|
name = provider_name or f"{provider_type}-{username}"
|
||||||
|
storage_id = await db.add_user_storage(
|
||||||
|
username=username,
|
||||||
|
provider_type=provider_type,
|
||||||
|
provider_name=name,
|
||||||
|
config=config,
|
||||||
|
capacity_gb=capacity_gb
|
||||||
|
)
|
||||||
|
|
||||||
|
if not storage_id:
|
||||||
|
return HTMLResponse('<div class="text-red-400">Failed to save storage provider</div>')
|
||||||
|
|
||||||
|
return HTMLResponse(f'''
|
||||||
|
<div class="text-green-400 mb-2">Storage provider "{name}" added successfully!</div>
|
||||||
|
<script>setTimeout(() => window.location.reload(), 1500);</script>
|
||||||
|
''')
|
||||||
|
|
||||||
|
|
||||||
@app.get("/storage/{storage_id}")
|
@app.get("/storage/{storage_id}")
|
||||||
async def get_storage(storage_id: int, user: User = Depends(get_required_user)):
|
async def get_storage(storage_id: int, user: User = Depends(get_required_user)):
|
||||||
"""Get a specific storage provider."""
|
"""Get a specific storage provider."""
|
||||||
@@ -3312,7 +3378,7 @@ async def ui_storage_page(username: str, storages: list, request: Request) -> HT
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="add-form" class="hidden bg-dark-600 rounded-lg p-4">
|
<div id="add-form" class="hidden bg-dark-600 rounded-lg p-4">
|
||||||
<form id="storage-form" hx-post="/storage" hx-target="#add-result" hx-swap="innerHTML">
|
<form id="storage-form" hx-post="/storage/add" hx-target="#add-result" hx-swap="innerHTML">
|
||||||
<input type="hidden" name="provider_type" id="provider_type">
|
<input type="hidden" name="provider_type" id="provider_type">
|
||||||
|
|
||||||
<div id="pinata-fields" class="hidden space-y-4">
|
<div id="pinata-fields" class="hidden space-y-4">
|
||||||
@@ -3383,30 +3449,6 @@ async def ui_storage_page(username: str, storages: list, request: Request) -> HT
|
|||||||
function hideAddForm() {{
|
function hideAddForm() {{
|
||||||
document.getElementById('add-form').classList.add('hidden');
|
document.getElementById('add-form').classList.add('hidden');
|
||||||
}}
|
}}
|
||||||
|
|
||||||
// Handle form submission to build proper JSON
|
|
||||||
document.getElementById('storage-form').addEventListener('htmx:configRequest', function(evt) {{
|
|
||||||
const formData = new FormData(evt.detail.elt);
|
|
||||||
const providerType = formData.get('provider_type');
|
|
||||||
const config = {{}};
|
|
||||||
|
|
||||||
if (providerType === 'pinata') {{
|
|
||||||
config.api_key = formData.get('api_key');
|
|
||||||
config.secret_key = formData.get('secret_key');
|
|
||||||
}} else if (providerType === 'web3storage') {{
|
|
||||||
config.api_token = formData.get('api_token');
|
|
||||||
}} else if (providerType === 'local') {{
|
|
||||||
config.path = formData.get('path');
|
|
||||||
}}
|
|
||||||
|
|
||||||
evt.detail.headers['Content-Type'] = 'application/json';
|
|
||||||
evt.detail.parameters = JSON.stringify({{
|
|
||||||
provider_type: providerType,
|
|
||||||
provider_name: formData.get('provider_name') || null,
|
|
||||||
config: config,
|
|
||||||
capacity_gb: parseInt(formData.get('capacity_gb'))
|
|
||||||
}});
|
|
||||||
}});
|
|
||||||
</script>
|
</script>
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user