Reduce media bandwidth: compress uploads, add long-lived cache headers, gzip API
- Resize/re-encode every uploaded image server-side (posts, avatars, stories, shop products/logo, chat messages) via a shared sharp-based optimizer — caps dimensions to what's actually displayed and re-compresses in place, cutting a typical raw camera photo by ~90% with no visible quality loss - Cap uploaded video resolution/bitrate (re-encode only when above 1280px/ ~2.5Mbps) alongside the existing faststart step; apply faststart+cap to story and chat videos too (previously stored completely raw) - Add long-lived Cache-Control to all /storage static routes so clients stop re-downloading unchanged media on every request - Add gzip/brotli compression for JSON API responses (feed/explore/chat) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
64
index.js
64
index.js
@@ -1,8 +1,16 @@
|
||||
require('dotenv').config();
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
const compression = require('compression');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const { optimizeImageFile } = require('./utils/imageOptimize');
|
||||
let optimizeVideoFaststart = async () => {};
|
||||
try {
|
||||
({ optimizeVideoFaststart } = require('./utils/postVideoProcessing'));
|
||||
} catch (error) {
|
||||
console.warn('[index] postVideoProcessing unavailable:', error.message);
|
||||
}
|
||||
const http = require('http').createServer(app);
|
||||
const cors = require('cors');
|
||||
const jwt = require('jsonwebtoken');
|
||||
@@ -106,30 +114,41 @@ const io = require('socket.io')(http, {
|
||||
|
||||
app.set('io', io);
|
||||
|
||||
// gzip/brotli فقط برای پاسخهای متنی (JSON فید/اکسپلور/چت) — فیلترِ پیشفرضِ compression
|
||||
// خودش مدیا (image/*، video/*) رو compressible نمیدونه و رد میکنه، پس نیازی به فیلترِ
|
||||
// دستی نیست؛ فقط باید قبل از هر res.json/res.send دیگهای بیاد
|
||||
app.use(compression());
|
||||
|
||||
// افزایش limit برای Base64 (1000mb کافیه)
|
||||
app.use(express.json({ limit: '1000mb' }));
|
||||
app.use(express.urlencoded({ limit: '1000mb', extended: true }));
|
||||
|
||||
// کش طولانی برای مدیا — بدون این، مرورگر/اپ هر بار همون عکس/ویدیوی قبلی رو دوباره
|
||||
// دانلود میکرد. immutable نمیذاریم چون مسیر آپلود تازه (optimizeImageFile/
|
||||
// optimizeVideoFaststart) گاهی چند ثانیه بعدِ همون ریسپانس، بایتهای همون فایل رو در جا
|
||||
// عوض میکنه؛ بدونِ immutable مرورگر با ETag/Last-Modified همچنان رواعتبارسنجی میکنه
|
||||
const STATIC_MEDIA_OPTIONS = { maxAge: '365d' };
|
||||
|
||||
// مسیرهای مشخص قبل از /storage عمومی — پروفایل از هر دو محل (قدیم و جدید) سرو میشود
|
||||
app.use('/storage/profiles', express.static(path.join(__dirname, '../storage/profiles')));
|
||||
app.use('/storage/profiles', express.static(path.join(__dirname, 'storage/profiles')));
|
||||
app.use('/storage/carts', express.static(path.join(__dirname, '../storage/carts')));
|
||||
app.use('/storage/carts', express.static(path.join(__dirname, 'storage/carts')));
|
||||
app.use('/storage/posts', express.static(path.join(__dirname, 'storage/posts')));
|
||||
app.use('/storage/posts', express.static(path.join(__dirname, '../storage/posts')));
|
||||
app.use('/storage/messages', express.static(path.join(__dirname, '../storage/messages')));
|
||||
app.use('/storage/messages', express.static(path.join(__dirname, 'storage/messages')));
|
||||
app.use('/storage/shop-chat', express.static(path.join(__dirname, '../storage/shop-chat')));
|
||||
app.use('/storage/shop-chat', express.static(path.join(__dirname, 'storage/shop-chat')));
|
||||
app.use('/storage/chat-rooms', express.static(path.join(__dirname, '../storage/chat-rooms')));
|
||||
app.use('/storage/chat-rooms', express.static(path.join(__dirname, 'storage/chat-rooms')));
|
||||
app.use('/storage/tickets', express.static(path.join(__dirname, '../storage/tickets')));
|
||||
app.use('/storage/tickets', express.static(path.join(__dirname, 'storage/tickets')));
|
||||
app.use('/storage/advertising', express.static(path.join(__dirname, '../storage/advertising')));
|
||||
app.use('/storage/advertising', express.static(path.join(__dirname, 'storage/advertising')));
|
||||
app.use('/storage/services', express.static(path.join(__dirname, '../storage/services')));
|
||||
app.use('/storage/services', express.static(path.join(__dirname, 'storage/services')));
|
||||
app.use('/storage', express.static(path.join(__dirname, 'storage')));
|
||||
app.use('/storage/profiles', express.static(path.join(__dirname, '../storage/profiles'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/profiles', express.static(path.join(__dirname, 'storage/profiles'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/carts', express.static(path.join(__dirname, '../storage/carts'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/carts', express.static(path.join(__dirname, 'storage/carts'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/posts', express.static(path.join(__dirname, 'storage/posts'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/posts', express.static(path.join(__dirname, '../storage/posts'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/messages', express.static(path.join(__dirname, '../storage/messages'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/messages', express.static(path.join(__dirname, 'storage/messages'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/shop-chat', express.static(path.join(__dirname, '../storage/shop-chat'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/shop-chat', express.static(path.join(__dirname, 'storage/shop-chat'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/chat-rooms', express.static(path.join(__dirname, '../storage/chat-rooms'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/chat-rooms', express.static(path.join(__dirname, 'storage/chat-rooms'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/tickets', express.static(path.join(__dirname, '../storage/tickets'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/tickets', express.static(path.join(__dirname, 'storage/tickets'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/advertising', express.static(path.join(__dirname, '../storage/advertising'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/advertising', express.static(path.join(__dirname, 'storage/advertising'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/services', express.static(path.join(__dirname, '../storage/services'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage/services', express.static(path.join(__dirname, 'storage/services'), STATIC_MEDIA_OPTIONS));
|
||||
app.use('/storage', express.static(path.join(__dirname, 'storage'), STATIC_MEDIA_OPTIONS));
|
||||
|
||||
require('./boot');
|
||||
|
||||
@@ -394,6 +413,13 @@ app.post('/api/v1/chat/file', [blockCheck, chatPeerBlock], async (req, res) => {
|
||||
const uniqueFileName = `${Date.now()}-${crypto.randomUUID()}${path.extname(file.name || '')}`
|
||||
const filePath = path.join(uploadDir, uniqueFileName)
|
||||
await fs.move(file.path, filePath)
|
||||
if (resolvedFileType === 'image' || (file.type || '').startsWith('image/')) {
|
||||
await optimizeImageFile(filePath)
|
||||
} else if (resolvedFileType === 'video' || (file.type || '').startsWith('video/')) {
|
||||
optimizeVideoFaststart(filePath).catch((err) => {
|
||||
console.error('[chat/file] video optimize background error:', err.message)
|
||||
})
|
||||
}
|
||||
fileUrl = `/messages/${uniqueFileName}`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user