fix: code review fixes — date calc, input validation, rate limits, shared components

- Fix monthly_reset_at crash when billing anchor day exceeds next month's length
- Add environment_tags sanitization (max 20 tags, 100 chars each) to prevent prompt injection
- Add @limiter.limit("10/minute") rate limiting to all AI endpoints
- Use getTreeNavigatePath() routing helper instead of hardcoded paths
- Extract shared CreateFlowDropdown component from QuickStartPage and TreeLibraryPage
- Clear useCachedQuota on logout to prevent stale data across user sessions
- Add useRef guard to scaffold useEffect to prevent potential double-fire
- Use node.id as React key instead of array index in BranchDetailView
- Remove redundant dead logic in ai_tree_validator

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-02-21 01:32:38 -05:00
parent 4b9863f22d
commit 37e1202f46
11 changed files with 163 additions and 182 deletions

View File

@@ -4,6 +4,7 @@ Enforces monthly and daily limits on AI flow builder usage.
Monthly quota consumed only on successful tree assembly (counts_toward_quota=True).
Daily limit is an anti-abuse guard consumed on conversation start.
"""
import calendar
from datetime import datetime, timezone, timedelta
from typing import Optional
from uuid import UUID
@@ -127,9 +128,13 @@ async def check_ai_quota(
deny_reason = "daily"
# Calculate reset timestamps
next_month = month_start.month % 12 + 1
next_year = month_start.year + (1 if month_start.month == 12 else 0)
max_day = calendar.monthrange(next_year, next_month)[1]
monthly_reset_at = month_start.replace(
month=month_start.month % 12 + 1,
year=month_start.year + (1 if month_start.month == 12 else 0),
month=next_month,
year=next_year,
day=min(month_start.day, max_day),
)
daily_reset_at = day_start + timedelta(hours=24)

View File

@@ -159,7 +159,7 @@ def _check_branch_termination(node: dict[str, Any], errors: list[str]) -> None:
if node_type == "solution":
return # Solution is a valid terminus
if not children and node_type != "solution":
if not children:
errors.append(
f"Node '{node_id}' (type={node_type}) is a dead end — "
"it has no children and is not a solution node"