Fix QA findings: single UNION query, weekly validation, nginx docs

W-01: Consolidate get_accessible_calendar_ids to single UNION query
instead of two separate DB round-trips.
W-02: Document that nginx rate limit on /api/events applies to all
methods (30r/m generous enough for GET polling at 2r/m).
W-03: Add weekly rule validation for consistency with other rule types.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Kyle 2026-03-15 01:46:11 +08:00
parent be1fdc4551
commit a2c1058f9c
3 changed files with 13 additions and 7 deletions

View File

@ -22,6 +22,8 @@ class RecurrenceRule(BaseModel):
"""Enforce required fields per rule type."""
if self.type == "every_n_days" and self.interval is None:
raise ValueError("every_n_days rule requires 'interval'")
if self.type == "weekly" and self.weekday is None:
raise ValueError("weekly rule requires 'weekday'")
if self.type == "monthly_nth_weekday":
if self.week is None or self.weekday is None:
raise ValueError("monthly_nth_weekday rule requires both 'week' and 'weekday'")

View File

@ -22,14 +22,16 @@ LOCK_DURATION_MINUTES = 5
async def get_accessible_calendar_ids(user_id: int, db: AsyncSession) -> list[int]:
"""Return all calendar IDs the user can access (owned + accepted shared memberships)."""
owned = await db.execute(select(Calendar.id).where(Calendar.user_id == user_id))
shared = await db.execute(
select(CalendarMember.calendar_id).where(
CalendarMember.user_id == user_id,
CalendarMember.status == "accepted",
result = await db.execute(
select(Calendar.id).where(Calendar.user_id == user_id)
.union(
select(CalendarMember.calendar_id).where(
CalendarMember.user_id == user_id,
CalendarMember.status == "accepted",
)
)
)
return [r[0] for r in owned.all()] + [r[0] for r in shared.all()]
return [r[0] for r in result.all()]
async def get_user_permission(db: AsyncSession, calendar_id: int, user_id: int) -> str | None:

View File

@ -124,7 +124,9 @@ server {
include /etc/nginx/proxy-params.conf;
}
# Event creation rate-limited to prevent DB flooding via recurrence amplification
# Event creation rate-limited to prevent DB flooding via recurrence amplification.
# Note: exact match applies to GET+POST; 30r/m with burst=10 is generous enough
# for polling (2r/m) and won't affect reads even with multiple tabs.
location = /api/events {
limit_req zone=event_create_limit burst=10 nodelay;
limit_req_status 429;