feat: GEO/SEO structured data + search ranking tracker

This commit is contained in:
Yuzhiran Dev
2026-06-09 17:18:09 +08:00
parent 23ff63baa9
commit 8595bbc521
26 changed files with 961 additions and 51 deletions
+23 -5
View File
@@ -10,7 +10,7 @@ from typing import List, Optional
from ..database import get_db
from ..models import MediaAsset
from ..schemas import MediaAssetCreate, MediaAssetUpdate, MediaAssetResponse
from .auth import get_current_user
from .auth import get_current_user, org_filter
router = APIRouter(prefix="/api/assets", tags=["assets"])
@@ -33,6 +33,9 @@ def list_assets(
current_user=Depends(get_current_user)
):
query = db.query(MediaAsset)
of = org_filter(current_user, MediaAsset)
if of is not True:
query = query.filter(of)
if file_type:
query = query.filter(MediaAsset.file_type == file_type)
@@ -54,7 +57,11 @@ def list_tags(
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
):
assets = db.query(MediaAsset.tags).all()
of = org_filter(current_user, MediaAsset)
base = db.query(MediaAsset)
if of is not True:
base = base.filter(of)
assets = base.with_entities(MediaAsset.tags).all()
all_tags = set()
for a in assets:
if a[0]:
@@ -67,9 +74,13 @@ def get_counts(
db: Session = Depends(get_db),
current_user=Depends(get_current_user)
):
total = db.query(MediaAsset).count()
of = org_filter(current_user, MediaAsset)
base = db.query(MediaAsset)
if of is not True:
base = base.filter(of)
total = base.count()
by_type = {}
rows = db.query(MediaAsset.file_type, func.count(MediaAsset.id)).group_by(MediaAsset.file_type).all()
rows = base.with_entities(MediaAsset.file_type, func.count(MediaAsset.id)).group_by(MediaAsset.file_type).all()
for ftype, cnt in rows:
by_type[ftype] = cnt
return {"total": total, "by_type": by_type}
@@ -120,7 +131,8 @@ async def upload_asset(
alt_text=alt_text,
tags=parsed_tags,
topic_ids=parsed_topic_ids,
uploaded_by=current_user.username
uploaded_by=current_user.username,
org_id=current_user.org_id or "default",
)
db.add(asset)
db.commit()
@@ -138,6 +150,8 @@ def update_asset(
asset = db.query(MediaAsset).filter(MediaAsset.id == asset_id).first()
if not asset:
raise HTTPException(status_code=404, detail="素材不存在")
if current_user.role != "admin" and asset.org_id != current_user.org_id:
raise HTTPException(status_code=404, detail="素材不存在")
for k, v in data.model_dump(exclude_unset=True).items():
setattr(asset, k, v)
@@ -155,6 +169,8 @@ def delete_asset(
asset = db.query(MediaAsset).filter(MediaAsset.id == asset_id).first()
if not asset:
raise HTTPException(status_code=404, detail="素材不存在")
if current_user.role != "admin" and asset.org_id != current_user.org_id:
raise HTTPException(status_code=404, detail="素材不存在")
if os.path.exists(asset.file_path):
try:
@@ -176,6 +192,8 @@ def increment_usage(
asset = db.query(MediaAsset).filter(MediaAsset.id == asset_id).first()
if not asset:
raise HTTPException(status_code=404, detail="素材不存在")
if current_user.role != "admin" and asset.org_id != current_user.org_id:
raise HTTPException(status_code=404, detail="素材不存在")
asset.usage_count = (asset.usage_count or 0) + 1
db.commit()
return {"ok": True, "usage_count": asset.usage_count}