Add IPFS CID display with public gateway links
- Cache detail page shows IPFS section with CID and links to ipfs.io, dweb.link, Cloudflare, and Pinata gateways - Media list cards show purple "IPFS" badge for items with CID - JSON API response includes ipfs_cid field Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
57
server.py
57
server.py
@@ -1714,6 +1714,10 @@ async def cache_detail(content_hash: str, request: Request):
|
|||||||
elif file_size > 1024:
|
elif file_size > 1024:
|
||||||
size_str = f"{file_size/1024:.1f} KB"
|
size_str = f"{file_size/1024:.1f} KB"
|
||||||
|
|
||||||
|
# Get IPFS CID from database
|
||||||
|
cache_item = await database.get_cache_item(content_hash)
|
||||||
|
ipfs_cid = cache_item.get("ipfs_cid") if cache_item else None
|
||||||
|
|
||||||
# Build media display HTML
|
# Build media display HTML
|
||||||
if media_type == "video":
|
if media_type == "video":
|
||||||
video_src = video_src_for_request(content_hash, request)
|
video_src = video_src_for_request(content_hash, request)
|
||||||
@@ -1770,7 +1774,47 @@ async def cache_detail(content_hash: str, request: Request):
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Add IPFS section if we have a CID
|
||||||
|
if ipfs_cid:
|
||||||
|
content += f'''
|
||||||
|
<div class="border-t border-dark-500 pt-6 mt-6">
|
||||||
|
<h2 class="text-lg font-semibold text-white mb-4">IPFS</h2>
|
||||||
|
<div class="bg-dark-600 rounded-lg p-4 mb-4">
|
||||||
|
<div class="text-sm text-gray-400 mb-1">Content Identifier (CID)</div>
|
||||||
|
<div class="font-mono text-xs text-gray-200 break-all">{ipfs_cid}</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-sm text-gray-400 mb-2">Public Gateways:</div>
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<a href="https://ipfs.io/ipfs/{ipfs_cid}" target="_blank" rel="noopener"
|
||||||
|
class="px-3 py-1 bg-purple-600 hover:bg-purple-700 text-white text-sm rounded-lg transition-colors">
|
||||||
|
ipfs.io
|
||||||
|
</a>
|
||||||
|
<a href="https://dweb.link/ipfs/{ipfs_cid}" target="_blank" rel="noopener"
|
||||||
|
class="px-3 py-1 bg-purple-600 hover:bg-purple-700 text-white text-sm rounded-lg transition-colors">
|
||||||
|
dweb.link
|
||||||
|
</a>
|
||||||
|
<a href="https://cloudflare-ipfs.com/ipfs/{ipfs_cid}" target="_blank" rel="noopener"
|
||||||
|
class="px-3 py-1 bg-purple-600 hover:bg-purple-700 text-white text-sm rounded-lg transition-colors">
|
||||||
|
Cloudflare
|
||||||
|
</a>
|
||||||
|
<a href="https://gateway.pinata.cloud/ipfs/{ipfs_cid}" target="_blank" rel="noopener"
|
||||||
|
class="px-3 py-1 bg-purple-600 hover:bg-purple-700 text-white text-sm rounded-lg transition-colors">
|
||||||
|
Pinata
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
'''
|
||||||
|
else:
|
||||||
|
content += '''
|
||||||
|
<div class="border-t border-dark-500 pt-6 mt-6">
|
||||||
|
<h2 class="text-lg font-semibold text-white mb-4">IPFS</h2>
|
||||||
|
<div class="text-gray-400 text-sm">Not yet uploaded to IPFS</div>
|
||||||
|
</div>
|
||||||
|
'''
|
||||||
|
|
||||||
|
content += f'''
|
||||||
<!-- Metadata Section -->
|
<!-- Metadata Section -->
|
||||||
<div class="border-t border-dark-500 pt-6 mt-6" id="metadata-section"
|
<div class="border-t border-dark-500 pt-6 mt-6" id="metadata-section"
|
||||||
hx-get="/cache/{content_hash}/meta-form" hx-trigger="load" hx-swap="innerHTML">
|
hx-get="/cache/{content_hash}/meta-form" hx-trigger="load" hx-swap="innerHTML">
|
||||||
@@ -1783,12 +1827,15 @@ async def cache_detail(content_hash: str, request: Request):
|
|||||||
|
|
||||||
# JSON response - return metadata
|
# JSON response - return metadata
|
||||||
meta = await database.load_item_metadata(content_hash, ctx.actor_id if ctx else None)
|
meta = await database.load_item_metadata(content_hash, ctx.actor_id if ctx else None)
|
||||||
|
cache_item = await database.get_cache_item(content_hash)
|
||||||
|
ipfs_cid = cache_item.get("ipfs_cid") if cache_item else None
|
||||||
file_size = cache_path.stat().st_size
|
file_size = cache_path.stat().st_size
|
||||||
media_type = detect_media_type(cache_path)
|
media_type = detect_media_type(cache_path)
|
||||||
return {
|
return {
|
||||||
"content_hash": content_hash,
|
"content_hash": content_hash,
|
||||||
"size": file_size,
|
"size": file_size,
|
||||||
"media_type": media_type,
|
"media_type": media_type,
|
||||||
|
"ipfs_cid": ipfs_cid,
|
||||||
"meta": meta
|
"meta": meta
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3510,6 +3557,11 @@ async def ui_media_list(
|
|||||||
cache_path = get_cache_path(content_hash)
|
cache_path = get_cache_path(content_hash)
|
||||||
media_type = detect_media_type(cache_path) if cache_path else "unknown"
|
media_type = detect_media_type(cache_path) if cache_path else "unknown"
|
||||||
|
|
||||||
|
# Check IPFS status
|
||||||
|
cache_item = await database.get_cache_item(content_hash)
|
||||||
|
ipfs_cid = cache_item.get("ipfs_cid") if cache_item else None
|
||||||
|
ipfs_badge = '<span class="px-2 py-1 bg-purple-600 text-white text-xs font-medium rounded-full" title="On IPFS">IPFS</span>' if ipfs_cid else ''
|
||||||
|
|
||||||
# Format size
|
# Format size
|
||||||
size = item["size"]
|
size = item["size"]
|
||||||
if size > 1024*1024:
|
if size > 1024*1024:
|
||||||
@@ -3523,7 +3575,10 @@ async def ui_media_list(
|
|||||||
<a href="/ui/cache/{content_hash}" class="block">
|
<a href="/ui/cache/{content_hash}" class="block">
|
||||||
<div class="bg-dark-700 rounded-lg p-4 hover:bg-dark-600 transition-colors">
|
<div class="bg-dark-700 rounded-lg p-4 hover:bg-dark-600 transition-colors">
|
||||||
<div class="flex items-center justify-between gap-2 mb-3">
|
<div class="flex items-center justify-between gap-2 mb-3">
|
||||||
<span class="px-2 py-1 bg-blue-600 text-white text-xs font-medium rounded-full">{media_type}</span>
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="px-2 py-1 bg-blue-600 text-white text-xs font-medium rounded-full">{media_type}</span>
|
||||||
|
{ipfs_badge}
|
||||||
|
</div>
|
||||||
<span class="text-xs text-gray-400">{size_str}</span>
|
<span class="text-xs text-gray-400">{size_str}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs text-gray-400 font-mono mb-3 truncate">{content_hash[:24]}...</div>
|
<div class="text-xs text-gray-400 font-mono mb-3 truncate">{content_hash[:24]}...</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user