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:
parent
360a14b87b
commit
9bcf5ace5d
@ -184,7 +184,7 @@ async def send_connection_request(
|
|||||||
raise HTTPException(status_code=409, detail="A pending request already exists")
|
raise HTTPException(status_code=409, detail="A pending request already exists")
|
||||||
|
|
||||||
# Per-receiver cap: max 5 pending requests within 10 minutes
|
# 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(
|
pending_count = await db.scalar(
|
||||||
select(func.count())
|
select(func.count())
|
||||||
.select_from(ConnectionRequest)
|
.select_from(ConnectionRequest)
|
||||||
@ -375,7 +375,7 @@ async def _respond_to_request_inner(
|
|||||||
db: AsyncSession,
|
db: AsyncSession,
|
||||||
current_user: User,
|
current_user: User,
|
||||||
) -> RespondAcceptResponse | RespondRejectResponse:
|
) -> RespondAcceptResponse | RespondRejectResponse:
|
||||||
now = datetime.now()
|
now = datetime.now(timezone.utc)
|
||||||
|
|
||||||
# Atomic update — only succeeds if status is still 'pending' and receiver is current user
|
# Atomic update — only succeeds if status is still 'pending' and receiver is current user
|
||||||
result = await db.execute(
|
result = await db.execute(
|
||||||
@ -562,7 +562,7 @@ async def cancel_request(
|
|||||||
current_user: User = Depends(get_current_user),
|
current_user: User = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
"""Cancel an outgoing connection request. Atomic via UPDATE...WHERE status='pending'."""
|
"""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
|
# Atomic update — only succeeds if sender is current user and status is still pending
|
||||||
result = await db.execute(
|
result = await db.execute(
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { useConnections } from '@/hooks/useConnections';
|
import { useConnections } from '@/hooks/useConnections';
|
||||||
import { useSettings } from '@/hooks/useSettings';
|
import { useSettings } from '@/hooks/useSettings';
|
||||||
|
import axios from 'axios';
|
||||||
import { getErrorMessage } from '@/lib/api';
|
import { getErrorMessage } from '@/lib/api';
|
||||||
|
|
||||||
interface ConnectionSearchProps {
|
interface ConnectionSearchProps {
|
||||||
@ -24,7 +25,7 @@ interface ConnectionSearchProps {
|
|||||||
|
|
||||||
export default function ConnectionSearch({ open, onOpenChange, personId }: ConnectionSearchProps) {
|
export default function ConnectionSearch({ open, onOpenChange, personId }: ConnectionSearchProps) {
|
||||||
const { search, isSearching, sendRequest, isSending } = useConnections();
|
const { search, isSearching, sendRequest, isSending } = useConnections();
|
||||||
const { settings } = useSettings();
|
const { settings, isLoading: isLoadingSettings } = useSettings();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [umbralName, setUmbralName] = useState('');
|
const [umbralName, setUmbralName] = useState('');
|
||||||
const [found, setFound] = useState<boolean | null>(null);
|
const [found, setFound] = useState<boolean | null>(null);
|
||||||
@ -39,8 +40,12 @@ export default function ConnectionSearch({ open, onOpenChange, personId }: Conne
|
|||||||
try {
|
try {
|
||||||
const result = await search(umbralName.trim());
|
const result = await search(umbralName.trim());
|
||||||
setFound(result.found);
|
setFound(result.found);
|
||||||
} catch {
|
} catch (err) {
|
||||||
setFound(false);
|
if (axios.isAxiosError(err) && err.response?.status === 429) {
|
||||||
|
toast.error('Too many searches — please wait a moment and try again');
|
||||||
|
} else {
|
||||||
|
setFound(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,7 +81,9 @@ export default function ConnectionSearch({ open, onOpenChange, personId }: Conne
|
|||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<div className="space-y-4 pt-2">
|
<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">
|
<div className="flex flex-col items-center gap-3 py-4 text-center">
|
||||||
<AlertCircle className="h-8 w-8 text-amber-400" />
|
<AlertCircle className="h-8 w-8 text-amber-400" />
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user