""" Storage provider routes for L2 server. Manages user storage backends. """ import logging from typing import Optional, Dict, Any from fastapi import APIRouter, Request, Depends, HTTPException, Form from fastapi.responses import HTMLResponse from pydantic import BaseModel from artdag_common import render from artdag_common.middleware import wants_html, wants_json from ..config import settings from ..dependencies import get_templates, require_auth, get_user_from_cookie router = APIRouter() logger = logging.getLogger(__name__) STORAGE_PROVIDERS_INFO = { "pinata": {"name": "Pinata", "desc": "1GB free, IPFS pinning", "color": "blue"}, "web3storage": {"name": "web3.storage", "desc": "IPFS + Filecoin", "color": "green"}, "nftstorage": {"name": "NFT.Storage", "desc": "Free for NFTs", "color": "pink"}, "infura": {"name": "Infura IPFS", "desc": "5GB free", "color": "orange"}, "filebase": {"name": "Filebase", "desc": "5GB free, S3+IPFS", "color": "cyan"}, "storj": {"name": "Storj", "desc": "25GB free", "color": "indigo"}, "local": {"name": "Local Storage", "desc": "Your own disk", "color": "purple"}, } class AddStorageRequest(BaseModel): provider_type: str config: Dict[str, Any] capacity_gb: int = 5 provider_name: Optional[str] = None @router.get("") async def list_storage(request: Request): """List user's storage providers.""" import db username = get_user_from_cookie(request) if not username: if wants_json(request): raise HTTPException(401, "Authentication required") from fastapi.responses import RedirectResponse return RedirectResponse(url="/login", status_code=302) storages = await db.get_user_storage(username) if wants_json(request): return {"storages": storages} templates = get_templates(request) return render(templates, "storage/list.html", request, storages=storages, user={"username": username}, providers_info=STORAGE_PROVIDERS_INFO, active_tab="storage", ) @router.post("") async def add_storage( req: AddStorageRequest, user: dict = Depends(require_auth), ): """Add a storage provider.""" import db import storage_providers if req.provider_type not in STORAGE_PROVIDERS_INFO: raise HTTPException(400, f"Invalid provider type: {req.provider_type}") # Test connection provider = storage_providers.create_provider(req.provider_type, { **req.config, "capacity_gb": req.capacity_gb, }) if not provider: raise HTTPException(400, "Failed to create provider") success, message = await provider.test_connection() if not success: raise HTTPException(400, f"Connection failed: {message}") # Save storage_id = await db.add_user_storage( username=user["username"], provider_type=req.provider_type, provider_name=req.provider_name, config=req.config, capacity_gb=req.capacity_gb, ) return {"id": storage_id, "message": "Storage provider added"} @router.post("/add", response_class=HTMLResponse) 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), 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 storage via HTML form.""" import db import storage_providers username = get_user_from_cookie(request) if not username: return HTMLResponse('
Not authenticated
', status_code=401) # Build config config = {} if provider_type == "pinata": if not api_key or not secret_key: return HTMLResponse('
Pinata requires API Key and Secret Key
') config = {"api_key": api_key, "secret_key": secret_key} elif provider_type in ["web3storage", "nftstorage"]: if not api_token: return HTMLResponse(f'
{provider_type} requires API Token
') config = {"api_token": api_token} elif provider_type == "infura": if not project_id or not project_secret: return HTMLResponse('
Infura requires Project ID and Secret
') config = {"project_id": project_id, "project_secret": project_secret} elif provider_type in ["filebase", "storj"]: if not access_key or not secret_key or not bucket: return HTMLResponse('
Requires Access Key, Secret Key, and Bucket
') config = {"access_key": access_key, "secret_key": secret_key, "bucket": bucket} elif provider_type == "local": if not path: return HTMLResponse('
Local storage requires a path
') config = {"path": path} else: return HTMLResponse(f'
Unknown provider: {provider_type}
') # Test provider = storage_providers.create_provider(provider_type, {**config, "capacity_gb": capacity_gb}) if provider: success, message = await provider.test_connection() if not success: return HTMLResponse(f'
Connection failed: {message}
') # Save storage_id = await db.add_user_storage( username=username, provider_type=provider_type, provider_name=provider_name, config=config, capacity_gb=capacity_gb, ) return HTMLResponse(f'''
Storage provider added!
''') @router.get("/{storage_id}") async def get_storage( storage_id: int, user: dict = Depends(require_auth), ): """Get storage details.""" import db storage = await db.get_storage_by_id(storage_id) if not storage: raise HTTPException(404, "Storage not found") if storage.get("username") != user["username"]: raise HTTPException(403, "Not authorized") return storage @router.delete("/{storage_id}") async def delete_storage( storage_id: int, request: Request, user: dict = Depends(require_auth), ): """Delete a storage provider.""" import db storage = await db.get_storage_by_id(storage_id) if not storage: raise HTTPException(404, "Storage not found") if storage.get("username") != user["username"]: raise HTTPException(403, "Not authorized") success = await db.remove_user_storage(storage_id) if wants_html(request): return HTMLResponse("") return {"deleted": True} @router.post("/{storage_id}/test") async def test_storage( storage_id: int, request: Request, user: dict = Depends(require_auth), ): """Test storage connectivity.""" import db import storage_providers import json storage = await db.get_storage_by_id(storage_id) if not storage: raise HTTPException(404, "Storage not found") if storage.get("username") != user["username"]: raise HTTPException(403, "Not authorized") config = storage["config"] if isinstance(config, str): config = json.loads(config) provider = storage_providers.create_provider(storage["provider_type"], { **config, "capacity_gb": storage.get("capacity_gb", 5), }) if not provider: if wants_html(request): return HTMLResponse('Failed to create provider') return {"success": False, "message": "Failed to create provider"} success, message = await provider.test_connection() if wants_html(request): color = "green" if success else "red" return HTMLResponse(f'{message}') return {"success": success, "message": message}