Compare commits
3 Commits
claude/sle
...
e853bca9c9
| Author | SHA1 | Date | |
|---|---|---|---|
| e853bca9c9 | |||
| 99efe831b0 | |||
| 7141d40826 |
@@ -11,18 +11,12 @@ interface CatCardProps {
|
|||||||
cat: Cat;
|
cat: Cat;
|
||||||
onOpen: (id: number) => void;
|
onOpen: (id: number) => void;
|
||||||
likedIds?: Set<number>;
|
likedIds?: Set<number>;
|
||||||
priority?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CatCard({ cat, onOpen, likedIds, priority }: CatCardProps) {
|
export default function CatCard({ cat, onOpen, likedIds }: CatCardProps) {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const [liked, setLiked] = useState(() => likedIds?.has(cat.id) ?? false);
|
const [liked, setLiked] = useState(() => likedIds?.has(cat.id) ?? false);
|
||||||
const [likeCount, setLikeCount] = useState(cat.likes_count);
|
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 { toast } = useToast();
|
||||||
const likeMutation = useLikeCat(cat.id);
|
const likeMutation = useLikeCat(cat.id);
|
||||||
const unlikeMutation = useUnlikeCat(cat.id);
|
const unlikeMutation = useUnlikeCat(cat.id);
|
||||||
@@ -81,16 +75,13 @@ export default function CatCard({ cat, onOpen, likedIds, priority }: CatCardProp
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Изображение */}
|
{/* Изображение */}
|
||||||
<figure className="cat-card-image relative min-h-[200px] bg-[var(--border-light)]">
|
<figure className="cat-card-image">
|
||||||
<img
|
<img
|
||||||
src={thumbSrc}
|
src={cat.image_url}
|
||||||
alt={cat.caption || 'Фото кота'}
|
alt={cat.caption || 'Фото кота'}
|
||||||
className={`w-full max-h-[540px] object-contain select-none mx-auto cat-card-img transition-opacity duration-300 ${imgLoaded ? 'opacity-100' : 'opacity-0'}`}
|
className="w-full max-h-[540px] object-contain select-none mx-auto cat-card-img"
|
||||||
draggable={false}
|
draggable={false}
|
||||||
loading={priority ? 'eager' : 'lazy'}
|
loading="lazy"
|
||||||
decoding="async"
|
|
||||||
onLoad={() => setImgLoaded(true)}
|
|
||||||
onError={e => { e.currentTarget.src = cat.image_url; }}
|
|
||||||
/>
|
/>
|
||||||
</figure>
|
</figure>
|
||||||
|
|
||||||
|
|||||||
@@ -103,8 +103,8 @@ export default function FeedPage() {
|
|||||||
{allCats.length > 0 && (
|
{allCats.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
{allCats.map((cat, index) => (
|
{allCats.map((cat) => (
|
||||||
<CatCard key={cat.id} cat={cat} onOpen={setModalCatId} likedIds={likedIds} priority={index < 3} />
|
<CatCard key={cat.id} cat={cat} onOpen={setModalCatId} likedIds={likedIds} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -60,10 +60,7 @@ async function main() {
|
|||||||
return uploadLimiter(req, _res, next);
|
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/auth', authRoutes);
|
||||||
app.use('/api/cats', catRoutes);
|
app.use('/api/cats', catRoutes);
|
||||||
|
|||||||
@@ -127,17 +127,8 @@ router.post('/', authMiddleware, upload.single('image'), async (req: AuthRequest
|
|||||||
finalFilename = req.file.filename.replace(/\.[^.]+$/, '.jpg');
|
finalFilename = req.file.filename.replace(/\.[^.]+$/, '.jpg');
|
||||||
finalPath = path.join(uploadsDir, finalFilename);
|
finalPath = path.join(uploadsDir, finalFilename);
|
||||||
try {
|
try {
|
||||||
await sharp(origPath)
|
await sharp(origPath).jpeg({ quality: 85 }).toFile(finalPath);
|
||||||
.resize({ width: 1200, withoutEnlargement: true })
|
|
||||||
.jpeg({ quality: 85 })
|
|
||||||
.toFile(finalPath);
|
|
||||||
if (origPath !== finalPath) fs.unlinkSync(origPath);
|
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 {
|
} catch {
|
||||||
try { fs.unlinkSync(origPath); } catch {}
|
try { fs.unlinkSync(origPath); } catch {}
|
||||||
res.status(400).json({ error: 'Не удалось обработать изображение. Попробуйте другой формат.' });
|
res.status(400).json({ error: 'Не удалось обработать изображение. Попробуйте другой формат.' });
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
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);
|
|
||||||
Reference in New Issue
Block a user