feat: add share calendar API

This commit is contained in:
qzl
2026-02-28 12:15:59 +08:00
parent 709ae5ab73
commit 7a49783156
5 changed files with 204 additions and 1 deletions
+59 -1
View File
@@ -1,6 +1,7 @@
from __future__ import annotations
from typing import TYPE_CHECKING
import json
from typing import TYPE_CHECKING, Protocol
from uuid import UUID
from fastapi import HTTPException
@@ -9,13 +10,17 @@ from sqlalchemy.exc import SQLAlchemyError
from core.auth.models import CurrentUser
from core.db.base_service import BaseService
from core.logging import get_logger
from models.inbox_messages import InboxMessage, InboxMessageType
from models.schedule_items import ScheduleItem
from v1.auth.gateway import SupabaseAuthGateway
from v1.schedule_items.repository import ScheduleItemRepository
from v1.schedule_items.schemas import (
ScheduleItemCreateRequest,
ScheduleItemListRequest,
ScheduleItemMetadata,
ScheduleItemResponse,
ScheduleItemShareRequest,
ScheduleItemShareResponse,
ScheduleItemUpdateRequest,
ScheduleItemSourceType,
ScheduleItemStatus,
@@ -24,22 +29,31 @@ from v1.schedule_items.schemas import (
if TYPE_CHECKING:
from sqlalchemy.ext.asyncio import AsyncSession
from v1.auth.schemas import UserByEmailResponse
logger = get_logger("v1.schedule_items.service")
class AuthByEmailGateway(Protocol):
async def get_user_by_email(self, email: str) -> "UserByEmailResponse": ...
class ScheduleItemService(BaseService):
_repository: ScheduleItemRepository
_session: AsyncSession
_auth_gateway: AuthByEmailGateway
def __init__(
self,
repository: ScheduleItemRepository,
session: AsyncSession,
current_user: CurrentUser | None,
auth_gateway: AuthByEmailGateway | None = None,
) -> None:
super().__init__(current_user=current_user)
self._repository = repository
self._session = session
self._auth_gateway = auth_gateway or SupabaseAuthGateway()
async def create(self, request: ScheduleItemCreateRequest) -> ScheduleItemResponse:
user_id = self.require_user_id()
@@ -179,6 +193,50 @@ class ScheduleItemService(BaseService):
return [self._to_response(item) for item in items]
async def share(
self, item_id: UUID, request: ScheduleItemShareRequest
) -> ScheduleItemShareResponse:
user_id = self.require_user_id()
try:
item = await self._repository.get_by_id(item_id)
if item is None:
raise HTTPException(status_code=404, detail="Schedule item not found")
if item.owner_id != user_id:
raise HTTPException(
status_code=403,
detail="Only owner can share this schedule item",
)
target_user = await self._auth_gateway.get_user_by_email(request.email)
recipient_id = UUID(target_user.id)
message = InboxMessage(
recipient_id=recipient_id,
sender_id=user_id,
message_type=InboxMessageType.CALENDAR,
schedule_item_id=item.id,
content=json.dumps({"permission": request._permission_value()}),
created_by=user_id,
)
self._session.add(message)
await self._session.commit()
except HTTPException:
raise
except SQLAlchemyError:
await self._session.rollback()
logger.exception("Failed to share schedule item", item_id=str(item_id))
raise HTTPException(
status_code=503, detail="Schedule item store unavailable"
)
except ValueError:
await self._session.rollback()
logger.exception(
"Auth lookup returned invalid user id", email=request.email
)
raise HTTPException(status_code=503, detail="Auth lookup unavailable")
return ScheduleItemShareResponse(message="Calendar invitation sent")
def _to_response(self, item: ScheduleItem) -> ScheduleItemResponse:
return ScheduleItemResponse(
id=item.id,