Fix search cold-cache gate, 429 handling, and datetime.now() violations

ConnectionSearch.tsx:
- Add loading guard for useSettings() — prevents cold cache from showing
  "enable connections" gate when settings haven't loaded yet
- Add 429 rate limit handling in search catch block — shows user-friendly
  message instead of silently showing "User not found"
- Import axios for isAxiosError type guard

connections.py:
- Fix 3x datetime.now() → datetime.now(timezone.utc) per hard rule
  (lines 187, 378, 565)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Kyle 2026-03-05 20:02:52 +08:00
parent 360a14b87b
commit 9bcf5ace5d
2 changed files with 14 additions and 7 deletions

View File

@ -184,7 +184,7 @@ async def send_connection_request(
raise HTTPException(status_code=409, detail="A pending request already exists")
# Per-receiver cap: max 5 pending requests within 10 minutes
ten_min_ago = datetime.now() - timedelta(minutes=10)
ten_min_ago = datetime.now(timezone.utc) - timedelta(minutes=10)
pending_count = await db.scalar(
select(func.count())
.select_from(ConnectionRequest)
@ -375,7 +375,7 @@ async def _respond_to_request_inner(
db: AsyncSession,
current_user: User,
) -> RespondAcceptResponse | RespondRejectResponse:
now = datetime.now()
now = datetime.now(timezone.utc)
# Atomic update — only succeeds if status is still 'pending' and receiver is current user
result = await db.execute(
@ -562,7 +562,7 @@ async def cancel_request(
current_user: User = Depends(get_current_user),
):
"""Cancel an outgoing connection request. Atomic via UPDATE...WHERE status='pending'."""
now = datetime.now()
now = datetime.now(timezone.utc)
# Atomic update — only succeeds if sender is current user and status is still pending
result = await db.execute(

View File

@ -14,6 +14,7 @@ import { Button } from '@/components/ui/button';
import { Label } from '@/components/ui/label';
import { useConnections } from '@/hooks/useConnections';
import { useSettings } from '@/hooks/useSettings';
import axios from 'axios';
import { getErrorMessage } from '@/lib/api';
interface ConnectionSearchProps {
@ -24,7 +25,7 @@ interface ConnectionSearchProps {
export default function ConnectionSearch({ open, onOpenChange, personId }: ConnectionSearchProps) {
const { search, isSearching, sendRequest, isSending } = useConnections();
const { settings } = useSettings();
const { settings, isLoading: isLoadingSettings } = useSettings();
const navigate = useNavigate();
const [umbralName, setUmbralName] = useState('');
const [found, setFound] = useState<boolean | null>(null);
@ -39,9 +40,13 @@ export default function ConnectionSearch({ open, onOpenChange, personId }: Conne
try {
const result = await search(umbralName.trim());
setFound(result.found);
} catch {
} catch (err) {
if (axios.isAxiosError(err) && err.response?.status === 429) {
toast.error('Too many searches — please wait a moment and try again');
} else {
setFound(false);
}
}
};
const handleSend = async () => {
@ -76,7 +81,9 @@ export default function ConnectionSearch({ open, onOpenChange, personId }: Conne
</DialogDescription>
</DialogHeader>
<div className="space-y-4 pt-2">
{!acceptConnectionsEnabled ? (
{isLoadingSettings ? (
<div className="flex justify-center py-6"><Loader2 className="h-5 w-5 animate-spin text-muted-foreground" /></div>
) : !acceptConnectionsEnabled ? (
<div className="flex flex-col items-center gap-3 py-4 text-center">
<AlertCircle className="h-8 w-8 text-amber-400" />
<p className="text-sm text-muted-foreground">