"""
Storage provider routes for L1 server.
Manages user storage backends (Pinata, web3.storage, local, etc.)
"""
from typing import Optional, Dict, Any
from fastapi import APIRouter, Request, Depends, HTTPException, Form
from fastapi.responses import HTMLResponse, RedirectResponse
from pydantic import BaseModel
from artdag_common import render
from artdag_common.middleware import wants_html, wants_json
from ..dependencies import get_database, get_current_user, require_auth, get_templates
from ..services.auth_service import UserContext
from ..services.storage_service import StorageService, STORAGE_PROVIDERS_INFO, VALID_PROVIDER_TYPES
router = APIRouter()
# Import storage_providers module
import storage_providers as sp_module
def get_storage_service():
"""Get storage service instance."""
import database
return StorageService(database, sp_module)
class AddStorageRequest(BaseModel):
provider_type: str
config: Dict[str, Any]
capacity_gb: int = 5
provider_name: Optional[str] = None
class UpdateStorageRequest(BaseModel):
config: Optional[Dict[str, Any]] = None
capacity_gb: Optional[int] = None
is_active: Optional[bool] = None
@router.get("")
async def list_storage(
request: Request,
storage_service: StorageService = Depends(get_storage_service),
):
"""List user's storage providers. HTML for browsers, JSON for API."""
from ..services.auth_service import AuthService
from ..dependencies import get_redis_client
auth_service = AuthService(get_redis_client())
ctx = auth_service.get_user_from_cookie(request)
if not ctx:
if wants_json(request):
raise HTTPException(401, "Authentication required")
return RedirectResponse(url="/auth", status_code=302)
storages = await storage_service.list_storages(ctx.actor_id)
if wants_json(request):
return {"storages": storages}
# Render HTML template
templates = get_templates(request)
return render(templates, "storage/list.html", request,
storages=storages,
user=ctx,
providers_info=STORAGE_PROVIDERS_INFO,
)
@router.post("")
async def add_storage(
req: AddStorageRequest,
request: Request,
storage_service: StorageService = Depends(get_storage_service),
):
"""Add a storage provider via API."""
ctx = await require_auth(request)
storage_id, error = await storage_service.add_storage(
actor_id=ctx.actor_id,
provider_type=req.provider_type,
config=req.config,
capacity_gb=req.capacity_gb,
provider_name=req.provider_name,
)
if error:
raise HTTPException(400, error)
return {"id": storage_id, "message": "Storage provider added"}
@router.post("/add")
async def add_storage_form(
request: Request,
provider_type: str = Form(...),
provider_name: Optional[str] = Form(None),
description: 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),
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),
storage_service: StorageService = Depends(get_storage_service),
):
"""Add a storage provider via HTML form."""
from ..services.auth_service import AuthService
from ..dependencies import get_redis_client
auth_service = AuthService(get_redis_client())
ctx = auth_service.get_user_from_cookie(request)
if not ctx:
return HTMLResponse('
Not authenticated
', status_code=401)
# Build config from form
form_data = {
"api_key": api_key,
"secret_key": secret_key,
"api_token": api_token,
"project_id": project_id,
"project_secret": project_secret,
"access_key": access_key,
"bucket": bucket,
"path": path,
}
config, error = storage_service.build_config_from_form(provider_type, form_data)
if error:
return HTMLResponse(f'{error}
')
storage_id, error = await storage_service.add_storage(
actor_id=ctx.actor_id,
provider_type=provider_type,
config=config,
capacity_gb=capacity_gb,
provider_name=provider_name,
description=description,
)
if error:
return HTMLResponse(f'{error}
')
return HTMLResponse(f'''
Storage provider added successfully!
''')
@router.get("/{storage_id}")
async def get_storage(
storage_id: int,
request: Request,
storage_service: StorageService = Depends(get_storage_service),
):
"""Get a specific storage provider."""
ctx = await require_auth(request)
storage = await storage_service.get_storage(storage_id, ctx.actor_id)
if not storage:
raise HTTPException(404, "Storage provider not found")
return storage
@router.patch("/{storage_id}")
async def update_storage(
storage_id: int,
req: UpdateStorageRequest,
request: Request,
storage_service: StorageService = Depends(get_storage_service),
):
"""Update a storage provider."""
ctx = await require_auth(request)
success, error = await storage_service.update_storage(
storage_id=storage_id,
actor_id=ctx.actor_id,
config=req.config,
capacity_gb=req.capacity_gb,
is_active=req.is_active,
)
if error:
raise HTTPException(400, error)
return {"message": "Storage provider updated"}
@router.delete("/{storage_id}")
async def delete_storage(
storage_id: int,
request: Request,
storage_service: StorageService = Depends(get_storage_service),
):
"""Remove a storage provider."""
from ..services.auth_service import AuthService
from ..dependencies import get_redis_client
auth_service = AuthService(get_redis_client())
ctx = auth_service.get_user_from_cookie(request)
if not ctx:
raise HTTPException(401, "Not authenticated")
success, error = await storage_service.delete_storage(storage_id, ctx.actor_id)
if error:
raise HTTPException(400, error)
if wants_html(request):
return HTMLResponse("")
return {"message": "Storage provider removed"}
@router.post("/{storage_id}/test")
async def test_storage(
storage_id: int,
request: Request,
storage_service: StorageService = Depends(get_storage_service),
):
"""Test storage provider connectivity."""
from ..services.auth_service import AuthService
from ..dependencies import get_redis_client
auth_service = AuthService(get_redis_client())
ctx = auth_service.get_user_from_cookie(request)
if not ctx:
if wants_html(request):
return HTMLResponse('Not authenticated', status_code=401)
raise HTTPException(401, "Not authenticated")
success, message = await storage_service.test_storage(storage_id, ctx.actor_id)
if wants_html(request):
color = "green" if success else "red"
return HTMLResponse(f'{message}')
return {"success": success, "message": message}
@router.get("/type/{provider_type}")
async def storage_type_page(
provider_type: str,
request: Request,
storage_service: StorageService = Depends(get_storage_service),
):
"""Page for managing storage configs of a specific type."""
from ..services.auth_service import AuthService
from ..dependencies import get_redis_client
auth_service = AuthService(get_redis_client())
ctx = auth_service.get_user_from_cookie(request)
if not ctx:
return RedirectResponse(url="/auth", status_code=302)
if provider_type not in STORAGE_PROVIDERS_INFO:
raise HTTPException(404, "Invalid provider type")
storages = await storage_service.list_by_type(ctx.actor_id, provider_type)
provider_info = STORAGE_PROVIDERS_INFO[provider_type]
templates = get_templates(request)
return render(templates, "storage/type.html", request,
provider_type=provider_type,
provider_info=provider_info,
storages=storages,
user=ctx,
)