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:
parent
be1fdc4551
commit
a2c1058f9c
@ -22,6 +22,8 @@ class RecurrenceRule(BaseModel):
|
|||||||
"""Enforce required fields per rule type."""
|
"""Enforce required fields per rule type."""
|
||||||
if self.type == "every_n_days" and self.interval is None:
|
if self.type == "every_n_days" and self.interval is None:
|
||||||
raise ValueError("every_n_days rule requires 'interval'")
|
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.type == "monthly_nth_weekday":
|
||||||
if self.week is None or self.weekday is None:
|
if self.week is None or self.weekday is None:
|
||||||
raise ValueError("monthly_nth_weekday rule requires both 'week' and 'weekday'")
|
raise ValueError("monthly_nth_weekday rule requires both 'week' and 'weekday'")
|
||||||
|
|||||||
@ -22,14 +22,16 @@ LOCK_DURATION_MINUTES = 5
|
|||||||
|
|
||||||
async def get_accessible_calendar_ids(user_id: int, db: AsyncSession) -> list[int]:
|
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)."""
|
"""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))
|
result = await db.execute(
|
||||||
shared = await db.execute(
|
select(Calendar.id).where(Calendar.user_id == user_id)
|
||||||
select(CalendarMember.calendar_id).where(
|
.union(
|
||||||
CalendarMember.user_id == user_id,
|
select(CalendarMember.calendar_id).where(
|
||||||
CalendarMember.status == "accepted",
|
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:
|
async def get_user_permission(db: AsyncSession, calendar_id: int, user_id: int) -> str | None:
|
||||||
|
|||||||
@ -124,7 +124,9 @@ server {
|
|||||||
include /etc/nginx/proxy-params.conf;
|
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 {
|
location = /api/events {
|
||||||
limit_req zone=event_create_limit burst=10 nodelay;
|
limit_req zone=event_create_limit burst=10 nodelay;
|
||||||
limit_req_status 429;
|
limit_req_status 429;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user