1
0
card/api/app/services/card_sync.py
2025-06-02 18:24:43 +09:00

184 lines
6.0 KiB
Python

"""Card synchronization service for atproto"""
from typing import List, Dict, Any, Optional
from datetime import datetime
from sqlalchemy.ext.asyncio import AsyncSession
# from app.services.atproto import AtprotoService, CardLexicon
from app.repositories.card import CardRepository
from app.repositories.user import UserRepository
from app.models.card import Card as CardModel
from app.db.models import UserCard
from app.core.config import settings
class CardSyncService:
"""Service for syncing cards between database and atproto PDS"""
def __init__(self, session: AsyncSession, atproto_session: Optional[str] = None):
self.db_session = session
self.card_repo = CardRepository(session)
self.user_repo = UserRepository(session)
self.atproto_service = AtprotoService()
# Restore atproto session if provided
if atproto_session:
self.atproto_service.restore_session(atproto_session)
async def sync_card_to_pds(self, user_card: UserCard, user_did: str) -> Optional[str]:
"""
Sync a single card to user's PDS
Args:
user_card: Card from database
user_did: User's DID
Returns:
Record URI if successful
"""
if not settings.atproto_handle or not settings.atproto_password:
# Skip if atproto credentials not configured
return None
try:
# Login if not already
if not self.atproto_service.client:
await self.atproto_service.login(
settings.atproto_handle,
settings.atproto_password
)
# Convert to API model
card_model = CardModel(
id=user_card.card_id,
cp=user_card.cp,
status=user_card.status,
skill=user_card.skill,
owner_did=user_did,
obtained_at=user_card.obtained_at,
is_unique=user_card.is_unique,
unique_id=str(user_card.unique_id) if user_card.unique_id else None
)
# Create record in PDS
uri = await self.atproto_service.create_card_record(
did=user_did,
card=card_model,
collection=CardLexicon.LEXICON_ID
)
# Store URI in database for future reference
# (You might want to add a field to UserCard model for this)
return uri
except Exception as e:
print(f"Failed to sync card to PDS: {e}")
return None
async def sync_all_user_cards(self, user_id: int, user_did: str) -> int:
"""
Sync all user's cards to PDS
Args:
user_id: Database user ID
user_did: User's DID
Returns:
Number of cards synced
"""
# Get all user cards from database
user_cards = await self.card_repo.get_user_cards(user_id)
synced_count = 0
for card in user_cards:
uri = await self.sync_card_to_pds(card, user_did)
if uri:
synced_count += 1
return synced_count
async def import_cards_from_pds(self, user_did: str) -> int:
"""
Import cards from user's PDS to database
Args:
user_did: User's DID
Returns:
Number of cards imported
"""
if not self.atproto_service.client:
return 0
# Get user from database
user = await self.user_repo.get_by_did(user_did)
if not user:
return 0
# Get cards from PDS
pds_cards = await self.atproto_service.get_user_cards(
did=user_did,
collection=CardLexicon.LEXICON_ID
)
imported_count = 0
for pds_card in pds_cards:
# Check if card already exists
existing_count = await self.card_repo.count_user_cards(
user.id,
pds_card.get("cardId")
)
if existing_count == 0:
# Import card
await self.card_repo.create_user_card(
user_id=user.id,
card_id=pds_card.get("cardId"),
cp=pds_card.get("cp"),
status=pds_card.get("status"),
skill=pds_card.get("skill"),
is_unique=pds_card.get("isUnique", False)
)
imported_count += 1
await self.db_session.commit()
return imported_count
async def verify_card_ownership(
self,
user_did: str,
card_id: int,
unique_id: Optional[str] = None
) -> bool:
"""
Verify user owns a card by checking both database and PDS
Args:
user_did: User's DID
card_id: Card type ID
unique_id: Unique ID for unique cards
Returns:
True if user owns the card
"""
# Check database first
user = await self.user_repo.get_by_did(user_did)
if user:
user_cards = await self.card_repo.get_user_cards(user.id)
for card in user_cards:
if card.card_id == card_id:
if not unique_id or str(card.unique_id) == unique_id:
return True
# Check PDS if configured
if self.atproto_service.client:
try:
pds_cards = await self.atproto_service.get_user_cards(user_did)
for card in pds_cards:
if card.get("cardId") == card_id:
if not unique_id or card.get("uniqueId") == unique_id:
return True
except Exception:
pass
return False