diff --git a/server.js b/server.js index 6fdcdf38..73c4cb8d 100644 --- a/server.js +++ b/server.js @@ -2547,8 +2547,17 @@ ${excerpt} }); // GET /together/feed/:groupId — フィード(リアクション・コメント数付き) + // ?limit=N&cursor= でカーソルページネーション対応 r.get('/together/feed/:groupId', async (req, res) => { try { + const limit = Math.min(parseInt(req.query.limit) || 20, 50); + const cursor = req.query.cursor; // shared_at の ISO タイムスタンプ + const params = [req.params.groupId]; + let cursorClause = ''; + if (cursor) { + params.push(cursor); + cursorClause = 'AND s.shared_at < $2'; + } const result = await pool.query(` SELECT s.*, @@ -2560,12 +2569,16 @@ ${excerpt} FROM together_shares s LEFT JOIN together_reactions r ON r.share_id = s.id LEFT JOIN together_comments c ON c.share_id = s.id - WHERE s.group_id = $1 + WHERE s.group_id = $1 ${cursorClause} GROUP BY s.id ORDER BY s.shared_at DESC - LIMIT 50 - `, [req.params.groupId]); - res.json(result.rows); + LIMIT ${limit + 1} + `, params); + const rows = result.rows; + const hasMore = rows.length > limit; + const items = hasMore ? rows.slice(0, limit) : rows; + const nextCursor = items.length > 0 ? items[items.length - 1].shared_at : null; + res.json({ items, next_cursor: nextCursor, has_more: hasMore }); } catch (e) { console.error('[together/feed]', e.message); res.status(500).json({ error: 'Internal server error' });