This commit is contained in:
payacom
2026-07-11 20:52:56 +03:30
parent 3c90976f23
commit 54971578b0
6 changed files with 103 additions and 10 deletions

View File

@@ -21,6 +21,29 @@ function storyMediaUrl (storagePath) {
return normalized.replace('/root/modstagram-back/storage', '/storage')
}
function clamp01 (value, fallback) {
const num = Number(value)
if (Number.isNaN(num)) return fallback
return Math.min(1, Math.max(0, num))
}
function sanitizeOverlays (raw) {
if (!Array.isArray(raw)) return []
return raw
.filter((o) => o && typeof o.text === 'string' && o.text.trim().length)
.slice(0, 20)
.map((o) => ({
text: String(o.text).slice(0, 300),
x: clamp01(o.x, 0.5),
y: clamp01(o.y, 0.5),
font_size: clamp01(o.font_size, 0.06) || 0.06,
color: typeof o.color === 'string' ? o.color.slice(0, 32) : '#ffffff',
background: typeof o.background === 'string' ? o.background.slice(0, 32) : '',
rotation: Number.isFinite(Number(o.rotation)) ? Number(o.rotation) : 0,
align: ['left', 'center', 'right'].includes(o.align) ? o.align : 'center'
}))
}
async function resolveViewerId (req) {
const token = req.header('Authorization')?.split(' ')[1]
if (!token) return null
@@ -35,7 +58,7 @@ async function resolveViewerId (req) {
const createStoryBase64 = async (req, res) => {
try {
const userId = req.user._id
const { file } = req.body
const { file, overlays } = req.body
if (!file?.data || !file?.type) {
return res.status(400).json({ error: true, message: 'فایل استوری الزامی است' })
@@ -52,6 +75,7 @@ const createStoryBase64 = async (req, res) => {
user_id: userId,
media_path: filePath,
media_type: mediaType,
overlays: sanitizeOverlays(overlays),
expires_at: new Date(Date.now() + STORY_TTL_MS),
viewers: []
})
@@ -96,6 +120,7 @@ const getStoriesFeed = async (req, res, next) => {
_id: story._id,
media_path: storyMediaUrl(story.media_path),
media_type: story.media_type,
overlays: story.overlays || [],
createdAt: story.createdAt,
expires_at: story.expires_at,
viewed: viewerId
@@ -174,6 +199,30 @@ const markStoryViewed = async (req, res) => {
}
}
const updateStory = async (req, res) => {
try {
const userId = req.user._id
const { storyId } = req.params
const { overlays } = req.body
const story = await StoryModel.findOne({ _id: storyId, user_id: userId })
if (!story || story.expires_at <= new Date()) {
return res.status(404).json({ message: 'استوری یافت نشد' })
}
story.overlays = sanitizeOverlays(overlays)
await story.save()
return res.status(200).json({
message: 'استوری ویرایش شد',
overlays: story.overlays
})
} catch (err) {
console.error('updateStory:', err)
return res.status(500).json({ message: 'خطا در ویرایش استوری' })
}
}
const deleteStory = async (req, res) => {
try {
const userId = req.user._id
@@ -193,5 +242,6 @@ module.exports = {
createStoryBase64,
getStoriesFeed,
markStoryViewed,
updateStory,
deleteStory
}

View File

@@ -970,10 +970,13 @@ const getPostsWeb = async (req, res, next) => {
lng,
feedMode,
seedPostId,
sort,
} = req.query;
const reelsMode = feedMode === "reels" || type === "video";
const gridMode = feedMode === "grid";
// فید اصلی صفحه پست‌ها: فقط جدیدترین‌ها (بدون شخصی‌سازی)
const latestMode = sort === "latest" && !reelsMode;
const postFilter = { status: { $ne: null } };
if (type === 'video' || type === 'image') {
@@ -1090,6 +1093,24 @@ const getPostsWeb = async (req, res, next) => {
explorePosts = await mapPostsWithUsers(explorePosts, usersMap, decodedToken);
// فید اصلی: فقط جدیدترین‌ها بر اساس تاریخ ساخت
if (latestMode) {
explorePosts.sort(
(a, b) => new Date(b.createdAt) - new Date(a.createdAt)
);
const totalItems = explorePosts.length;
const startIndex = (parseInt(page, 10) - 1) * parseInt(limit, 10);
const paginatedPosts = explorePosts.slice(
startIndex,
startIndex + parseInt(limit, 10)
);
return res.status(200).json({
posts: paginatedPosts,
totalPages: Math.ceil(totalItems / limit) || 1,
totalItems,
});
}
const { posts: paginatedPosts, totalItems } = await paginatePersonalizedExplore({
posts: explorePosts,
usersById,

View File

@@ -1,3 +1,4 @@
require('dotenv').config();
const express = require('express');
const app = express();
const path = require('path');
@@ -486,8 +487,7 @@ cron.schedule('0 0 1 * *', async () => {
scheduled: true,
timezone: 'Asia/Tehran'
})
module.exports = (port) => {
http.listen(port, () => {
console.log(`HTTP server is running on port ${port}`)
})
}
const port = process.env.APP_PORT || 3002;
http.listen(port, () => {
console.log(`HTTP server is running on port ${port}`);
});

View File

@@ -1,6 +1,18 @@
const mongoose = require('mongoose')
const timestamp = require('mongoose-timestamp')
// متن‌های روی استوری با مختصات نسبی (۰ تا ۱) تا در هر ابعادی درست رندر شوند
const overlaySchema = new mongoose.Schema({
text: { type: String, default: '' },
x: { type: Number, default: 0.5 },
y: { type: Number, default: 0.5 },
font_size: { type: Number, default: 0.06 },
color: { type: String, default: '#ffffff' },
background: { type: String, default: '' },
rotation: { type: Number, default: 0 },
align: { type: String, default: 'center' }
}, { _id: false })
const storySchema = new mongoose.Schema({
user_id: {
type: mongoose.Schema.Types.ObjectId,
@@ -17,6 +29,10 @@ const storySchema = new mongoose.Schema({
enum: ['image', 'video'],
required: true
},
overlays: {
type: [overlaySchema],
default: []
},
expires_at: {
type: Date,
required: true,

View File

@@ -6,6 +6,7 @@ const {
createStoryBase64,
getStoriesFeed,
markStoryViewed,
updateStory,
deleteStory
} = require('../../../controllers/application/stories/storyController')
@@ -14,6 +15,7 @@ const router = express.Router()
router.get('/feed', getStoriesFeed)
router.post('/create-base64', auth, blockCheck, isHalfRegister, createStoryBase64)
router.post('/view', auth, blockCheck, markStoryViewed)
router.patch('/:storyId', auth, blockCheck, updateStory)
router.delete('/:storyId', auth, blockCheck, deleteStory)
module.exports = router

View File

@@ -1,9 +1,13 @@
require('dotenv').config()
console.log('APP_PORT:', process.env.APP_PORT)
const bootApplication = require('./index')
// index.js خودش روی پورت listen می‌کند (اجرای مستقیم روی سرور: node index.js).
// اینجا فقط require می‌کنیم تا سرور بالا بیاید. اگر نسخه‌ای از index.js تابع
// export کند، آن را هم صدا می‌زنیم تا با هر دو حالت سازگار باشد.
const app = require('./index')
console.log('Index loaded, booting...')
const port = process.env.APP_PORT || 3002
bootApplication(port)
console.log(`✅ Server should be running on port ${port}`)
if (typeof app === 'function') {
const port = process.env.APP_PORT || 3002
app(port)
}