diff --git a/client/src/components/CatCard.tsx b/client/src/components/CatCard.tsx index 418b7ba..35844d2 100644 --- a/client/src/components/CatCard.tsx +++ b/client/src/components/CatCard.tsx @@ -11,12 +11,18 @@ interface CatCardProps { cat: Cat; onOpen: (id: number) => void; likedIds?: Set; + priority?: boolean; } -export default function CatCard({ cat, onOpen, likedIds }: CatCardProps) { +export default function CatCard({ cat, onOpen, likedIds, priority }: CatCardProps) { const { user } = useAuth(); const [liked, setLiked] = useState(() => likedIds?.has(cat.id) ?? false); const [likeCount, setLikeCount] = useState(cat.likes_count); + const [imgLoaded, setImgLoaded] = useState(false); + + const thumbSrc = cat.image_url.endsWith('.gif') + ? cat.image_url + : cat.image_url.replace(/\.jpg$/, '_thumb.jpg'); const { toast } = useToast(); const likeMutation = useLikeCat(cat.id); const unlikeMutation = useUnlikeCat(cat.id); @@ -75,13 +81,16 @@ export default function CatCard({ cat, onOpen, likedIds }: CatCardProps) { {/* Изображение */} -
+
{cat.caption setImgLoaded(true)} + onError={e => { e.currentTarget.src = cat.image_url; }} />
diff --git a/client/src/pages/FeedPage.tsx b/client/src/pages/FeedPage.tsx index 1c97c21..925e5eb 100644 --- a/client/src/pages/FeedPage.tsx +++ b/client/src/pages/FeedPage.tsx @@ -103,8 +103,8 @@ export default function FeedPage() { {allCats.length > 0 && ( <>
- {allCats.map((cat) => ( - + {allCats.map((cat, index) => ( + ))}
diff --git a/server/src/index.ts b/server/src/index.ts index e29d39c..854c032 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -60,7 +60,10 @@ async function main() { return uploadLimiter(req, _res, next); }); - app.use('/uploads', express.static(path.join(__dirname, '..', 'uploads'))); + app.use('/uploads', express.static(path.join(__dirname, '..', 'uploads'), { + maxAge: '365d', + immutable: true, + })); app.use('/api/auth', authRoutes); app.use('/api/cats', catRoutes); diff --git a/server/src/routes/cats.ts b/server/src/routes/cats.ts index b6cd6c9..77fd292 100644 --- a/server/src/routes/cats.ts +++ b/server/src/routes/cats.ts @@ -127,8 +127,17 @@ router.post('/', authMiddleware, upload.single('image'), async (req: AuthRequest finalFilename = req.file.filename.replace(/\.[^.]+$/, '.jpg'); finalPath = path.join(uploadsDir, finalFilename); try { - await sharp(origPath).jpeg({ quality: 85 }).toFile(finalPath); + await sharp(origPath) + .resize({ width: 1200, withoutEnlargement: true }) + .jpeg({ quality: 85 }) + .toFile(finalPath); if (origPath !== finalPath) fs.unlinkSync(origPath); + + const thumbFilename = finalFilename.replace(/\.jpg$/, '_thumb.jpg'); + await sharp(finalPath) + .resize({ width: 600, withoutEnlargement: true }) + .jpeg({ quality: 80 }) + .toFile(path.join(uploadsDir, thumbFilename)); } catch { try { fs.unlinkSync(origPath); } catch {} res.status(400).json({ error: 'Не удалось обработать изображение. Попробуйте другой формат.' }); diff --git a/server/src/scripts/gen-thumbs.ts b/server/src/scripts/gen-thumbs.ts new file mode 100644 index 0000000..85b6ddc --- /dev/null +++ b/server/src/scripts/gen-thumbs.ts @@ -0,0 +1,43 @@ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import sharp from 'sharp'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const uploadsDir = path.join(__dirname, '..', '..', 'uploads'); + +async function main() { + const files = fs.readdirSync(uploadsDir); + const candidates = files.filter( + f => f.endsWith('.jpg') && !f.endsWith('_thumb.jpg') + ); + + console.log(`Found ${candidates.length} images to process`); + let created = 0; + let skipped = 0; + + for (const filename of candidates) { + const thumbFilename = filename.replace(/\.jpg$/, '_thumb.jpg'); + const thumbPath = path.join(uploadsDir, thumbFilename); + + if (fs.existsSync(thumbPath)) { + skipped++; + continue; + } + + try { + await sharp(path.join(uploadsDir, filename)) + .resize({ width: 600, withoutEnlargement: true }) + .jpeg({ quality: 80 }) + .toFile(thumbPath); + created++; + console.log(` ✓ ${thumbFilename}`); + } catch (err) { + console.error(` ✗ ${filename}: ${err}`); + } + } + + console.log(`\nDone: ${created} created, ${skipped} already existed`); +} + +main().catch(console.error);