diff --git a/app/routers/effects.py b/app/routers/effects.py index fd85a8a..6bb4f0a 100644 --- a/app/routers/effects.py +++ b/app/routers/effects.py @@ -200,6 +200,15 @@ async def upload_effect( # Also store metadata in IPFS for discoverability meta_cid = ipfs_client.add_json(full_meta) + # Track ownership in item_types + import database + await database.save_item_metadata( + cid=cid, + actor_id=ctx.actor_id, + item_type="effect", + filename=file.filename, + ) + # Assign friendly name from ..services.naming_service import get_naming_service naming = get_naming_service() @@ -314,31 +323,33 @@ async def list_effects( limit: int = 20, ctx: UserContext = Depends(require_auth), ): - """List uploaded effects with pagination.""" + """List user's effects with pagination.""" + import database effects_dir = get_effects_dir() effects = [] + # Get user's effect CIDs from item_types + user_items = await database.get_user_items(ctx.actor_id, item_type="effect", limit=1000) + effect_cids = [item["cid"] for item in user_items] + # Get naming service for friendly name lookup from ..services.naming_service import get_naming_service naming = get_naming_service() - if effects_dir.exists(): - for effect_dir in effects_dir.iterdir(): - if effect_dir.is_dir(): - metadata_path = effect_dir / "metadata.json" - if metadata_path.exists(): - try: - meta = json.loads(metadata_path.read_text()) - # Add friendly name if available - cid = meta.get("cid") - if cid: - friendly = await naming.get_by_cid(ctx.actor_id, cid) - if friendly: - meta["friendly_name"] = friendly["friendly_name"] - meta["base_name"] = friendly["base_name"] - effects.append(meta) - except json.JSONDecodeError: - pass + for cid in effect_cids: + effect_dir = effects_dir / cid + metadata_path = effect_dir / "metadata.json" + if metadata_path.exists(): + try: + meta = json.loads(metadata_path.read_text()) + # Add friendly name if available + friendly = await naming.get_by_cid(ctx.actor_id, cid) + if friendly: + meta["friendly_name"] = friendly["friendly_name"] + meta["base_name"] = friendly["base_name"] + effects.append(meta) + except json.JSONDecodeError: + pass # Sort by upload time (newest first) effects.sort(key=lambda e: e.get("uploaded_at", ""), reverse=True) @@ -412,25 +423,29 @@ async def delete_effect( cid: str, ctx: UserContext = Depends(require_auth), ): - """Delete an effect from local cache (IPFS content is immutable).""" - effects_dir = get_effects_dir() - effect_dir = effects_dir / cid + """Remove user's ownership link to an effect.""" + import database - if not effect_dir.exists(): - raise HTTPException(404, f"Effect {cid[:16]}... not found in local cache") + # Remove user's ownership link from item_types + await database.delete_item_type(cid, ctx.actor_id, "effect") - # Check ownership - metadata_path = effect_dir / "metadata.json" - if metadata_path.exists(): - meta = json.loads(metadata_path.read_text()) - if meta.get("uploader") != ctx.actor_id: - raise HTTPException(403, "Can only delete your own effects") + # Remove friendly name + await database.delete_friendly_name(ctx.actor_id, cid) - import shutil - shutil.rmtree(effect_dir) + # Check if anyone still owns this effect + remaining_owners = await database.get_item_types(cid) - # Unpin from IPFS (content remains available if pinned elsewhere) - ipfs_client.unpin(cid) + # Only delete local files if no one owns it anymore + if not remaining_owners: + effects_dir = get_effects_dir() + effect_dir = effects_dir / cid + if effect_dir.exists(): + import shutil + shutil.rmtree(effect_dir) - logger.info(f"Deleted effect {cid[:16]}... by {ctx.actor_id}") - return {"deleted": True, "note": "Unpinned from local IPFS; content may still exist on other nodes"} + # Unpin from IPFS + ipfs_client.unpin(cid) + logger.info(f"Garbage collected effect {cid[:16]}... (no remaining owners)") + + logger.info(f"Removed effect {cid[:16]}... ownership for {ctx.actor_id}") + return {"deleted": True}