- consent-sdk/src/types/index.ts: extracted 438 LOC into core.ts, config.ts, vendor.ts, api.ts, events.ts, storage.ts, translations.ts; index.ts is now a 21-LOC barrel re-exporter - consent-sdk/src/core/ConsentManager.ts: extracted normalizeConsentInput, isConsentExpired, needsConsent, ALL_CATEGORIES, MINIMAL_CATEGORIES into consent-manager-helpers.ts; reduced from 467 to 345 LOC - dsms-gateway/main.py: extracted models → models.py, config → config.py, IPFS helpers + verify_token → dependencies.py, route handlers → routers/documents.py and routers/node.py; main.py is now a 41-LOC app factory; test mock paths updated accordingly (27/27 tests pass) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
110 lines
3.4 KiB
Python
110 lines
3.4 KiB
Python
"""
|
|
Node router — handles /health, /api/v1/verify/{cid}, and /api/v1/node/info endpoints.
|
|
"""
|
|
|
|
import hashlib
|
|
import json
|
|
from datetime import datetime
|
|
|
|
import httpx
|
|
from fastapi import APIRouter
|
|
|
|
from dependencies import ipfs_cat
|
|
from config import IPFS_API_URL
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/health")
|
|
async def health_check():
|
|
"""Health Check für DSMS Gateway"""
|
|
try:
|
|
async with httpx.AsyncClient(timeout=5.0) as client:
|
|
response = await client.post(f"{IPFS_API_URL}/api/v0/id")
|
|
ipfs_status = response.status_code == 200
|
|
except Exception:
|
|
ipfs_status = False
|
|
|
|
return {
|
|
"status": "healthy" if ipfs_status else "degraded",
|
|
"ipfs_connected": ipfs_status,
|
|
"timestamp": datetime.utcnow().isoformat()
|
|
}
|
|
|
|
|
|
@router.get("/api/v1/verify/{cid}")
|
|
async def verify_document(cid: str):
|
|
"""
|
|
Verifiziert die Integrität eines Dokuments.
|
|
Öffentlich zugänglich für Audit-Zwecke.
|
|
|
|
- **cid**: Content Identifier (IPFS Hash)
|
|
"""
|
|
try:
|
|
content = await ipfs_cat(cid)
|
|
package = json.loads(content)
|
|
|
|
# Checksum verifizieren
|
|
stored_checksum = package.get("metadata", {}).get("checksum")
|
|
|
|
if "content_base64" in package:
|
|
original_content = bytes.fromhex(package["content_base64"])
|
|
calculated_checksum = hashlib.sha256(original_content).hexdigest()
|
|
elif "content" in package:
|
|
calculated_checksum = hashlib.sha256(
|
|
package["content"].encode('utf-8')
|
|
).hexdigest()
|
|
else:
|
|
calculated_checksum = None
|
|
|
|
integrity_valid = (
|
|
stored_checksum == calculated_checksum
|
|
if stored_checksum and calculated_checksum
|
|
else None
|
|
)
|
|
|
|
return {
|
|
"cid": cid,
|
|
"exists": True,
|
|
"integrity_valid": integrity_valid,
|
|
"metadata": package.get("metadata", {}),
|
|
"stored_checksum": stored_checksum,
|
|
"calculated_checksum": calculated_checksum,
|
|
"verified_at": datetime.utcnow().isoformat()
|
|
}
|
|
except Exception as e:
|
|
return {
|
|
"cid": cid,
|
|
"exists": False,
|
|
"error": str(e),
|
|
"verified_at": datetime.utcnow().isoformat()
|
|
}
|
|
|
|
|
|
@router.get("/api/v1/node/info")
|
|
async def get_node_info():
|
|
"""
|
|
Gibt Informationen über den DSMS Node zurück.
|
|
"""
|
|
try:
|
|
async with httpx.AsyncClient(timeout=10.0) as client:
|
|
# Node ID
|
|
id_response = await client.post(f"{IPFS_API_URL}/api/v0/id")
|
|
node_info = id_response.json() if id_response.status_code == 200 else {}
|
|
|
|
# Repo Stats
|
|
stat_response = await client.post(f"{IPFS_API_URL}/api/v0/repo/stat")
|
|
repo_stats = stat_response.json() if stat_response.status_code == 200 else {}
|
|
|
|
return {
|
|
"node_id": node_info.get("ID"),
|
|
"protocol_version": node_info.get("ProtocolVersion"),
|
|
"agent_version": node_info.get("AgentVersion"),
|
|
"repo_size": repo_stats.get("RepoSize"),
|
|
"storage_max": repo_stats.get("StorageMax"),
|
|
"num_objects": repo_stats.get("NumObjects"),
|
|
"addresses": node_info.get("Addresses", [])[:5] # Erste 5
|
|
}
|
|
except Exception as e:
|
|
return {"error": str(e)}
|