feat(api): implement rate limiting and SSRF protection across endpoints
- Added rate limiting to `reaction-users`, `search`, and `image-proxy` APIs to prevent abuse. - Introduced SSRF protection in `image-proxy` to block requests to private IP ranges. - Enhanced `link-preview` to use `linkedom` for HTML parsing and improved meta tag extraction. - Refactored authentication checks in various pages to utilize middleware for cleaner code. - Improved JWT key loading with error handling and security warnings for production. - Updated `authFetch` utility to handle token refresh more efficiently with deduplication. - Enhanced rate limiting utility to trust proxy headers from known sources. - Numerous layout / design changes
This commit is contained in:
@@ -17,7 +17,10 @@ const Memes = () => {
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
const [selectedImage, setSelectedImage] = useState(null);
|
||||
const [selectedIndex, setSelectedIndex] = useState(-1);
|
||||
const [imageLoading, setImageLoading] = useState({});
|
||||
const observerRef = useRef();
|
||||
const touchStartRef = useRef(null);
|
||||
const touchEndRef = useRef(null);
|
||||
const theme = document.documentElement.getAttribute("data-theme")
|
||||
const cacheRef = useRef({ pagesLoaded: new Set(), items: [] });
|
||||
|
||||
@@ -158,6 +161,43 @@ const Memes = () => {
|
||||
setSelectedIndex(-1);
|
||||
}, []);
|
||||
|
||||
// Touch swipe handlers for mobile navigation
|
||||
const handleTouchStart = useCallback((e) => {
|
||||
touchStartRef.current = e.touches[0].clientX;
|
||||
touchEndRef.current = null;
|
||||
}, []);
|
||||
|
||||
const handleTouchMove = useCallback((e) => {
|
||||
touchEndRef.current = e.touches[0].clientX;
|
||||
}, []);
|
||||
|
||||
const handleTouchEnd = useCallback(() => {
|
||||
if (!touchStartRef.current || !touchEndRef.current) return;
|
||||
const distance = touchStartRef.current - touchEndRef.current;
|
||||
const minSwipeDistance = 50;
|
||||
|
||||
if (Math.abs(distance) > minSwipeDistance) {
|
||||
if (distance > 0) {
|
||||
// Swiped left -> next
|
||||
handleNavigate(1);
|
||||
} else {
|
||||
// Swiped right -> prev
|
||||
handleNavigate(-1);
|
||||
}
|
||||
}
|
||||
touchStartRef.current = null;
|
||||
touchEndRef.current = null;
|
||||
}, [handleNavigate]);
|
||||
|
||||
// Track image loading state
|
||||
const handleImageLoad = useCallback((id) => {
|
||||
setImageLoading(prev => ({ ...prev, [id]: false }));
|
||||
}, []);
|
||||
|
||||
const handleImageLoadStart = useCallback((id) => {
|
||||
setImageLoading(prev => ({ ...prev, [id]: true }));
|
||||
}, []);
|
||||
|
||||
const handleCopyImage = useCallback(async () => {
|
||||
if (!selectedImage) return;
|
||||
try {
|
||||
@@ -181,6 +221,7 @@ const Memes = () => {
|
||||
<div className="grid-container">
|
||||
{images.map((img, i) => {
|
||||
const isLast = i === images.length - 1;
|
||||
const isLoading = imageLoading[img.id] !== false;
|
||||
return (
|
||||
<div
|
||||
key={img.id}
|
||||
@@ -191,13 +232,18 @@ const Memes = () => {
|
||||
setSelectedIndex(i);
|
||||
prefetchImage(images[i + 1]);
|
||||
}}
|
||||
style={{ cursor: 'pointer' }}
|
||||
style={{ cursor: 'pointer', position: 'relative' }}
|
||||
>
|
||||
{isLoading && (
|
||||
<div className="meme-skeleton" />
|
||||
)}
|
||||
<Image
|
||||
src={img.url}
|
||||
alt={`meme-${img.id}`}
|
||||
imageClassName="meme-img"
|
||||
imageClassName={`meme-img ${isLoading ? 'meme-img-loading' : ''}`}
|
||||
loading="lazy"
|
||||
onLoad={() => handleImageLoad(img.id)}
|
||||
onLoadStart={() => handleImageLoadStart(img.id)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -239,7 +285,12 @@ const Memes = () => {
|
||||
dismissableMask={true}
|
||||
>
|
||||
{selectedImage && (
|
||||
<div className="meme-dialog-body">
|
||||
<div
|
||||
className="meme-dialog-body"
|
||||
onTouchStart={handleTouchStart}
|
||||
onTouchMove={handleTouchMove}
|
||||
onTouchEnd={handleTouchEnd}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="meme-dialog-nav meme-dialog-nav-prev"
|
||||
|
||||
Reference in New Issue
Block a user