diff --git a/server.js b/server.js index e1ff08de..fcf61d62 100644 --- a/server.js +++ b/server.js @@ -2481,9 +2481,17 @@ ${excerpt} } }); - // GET /together/members/:groupId — メンバー一覧 + // GET /together/members/:groupId — メンバー一覧 ?u=username r.get('/together/members/:groupId', async (req, res) => { + const username = req.query.u; + if (!username) return res.status(400).json({ error: 'u (username) は必須です' }); try { + const memberCheck = await pool.query( + 'SELECT 1 FROM together_members WHERE group_id=$1 AND username=$2', + [req.params.groupId, username] + ); + if (memberCheck.rows.length === 0) return res.status(403).json({ error: 'グループのメンバーではありません' }); + const result = await pool.query( 'SELECT username, joined_at FROM together_members WHERE group_id=$1 ORDER BY joined_at', [req.params.groupId] @@ -2546,12 +2554,20 @@ ${excerpt} } }); - // GET /together/feed/:groupId — フィード(リアクション・コメント数付き) - // ?limit=N&cursor= でカーソルページネーション対応 + // GET /together/feed/:groupId — フィード(リアクション付き) + // ?u=username&limit=N&cursor= r.get('/together/feed/:groupId', async (req, res) => { + const username = req.query.u; + if (!username) return res.status(400).json({ error: 'u (username) は必須です' }); try { + const memberCheck = await pool.query( + 'SELECT 1 FROM together_members WHERE group_id=$1 AND username=$2', + [req.params.groupId, username] + ); + if (memberCheck.rows.length === 0) return res.status(403).json({ error: 'グループのメンバーではありません' }); + const limit = Math.min(parseInt(req.query.limit) || 20, 50); - const cursor = req.query.cursor; // shared_at の ISO タイムスタンプ + const cursor = req.query.cursor; const params = [req.params.groupId]; let cursorClause = ''; if (cursor) { @@ -2564,11 +2580,9 @@ ${excerpt} COALESCE( json_agg(DISTINCT jsonb_build_object('username', r.username, 'type', r.type)) FILTER (WHERE r.username IS NOT NULL), '[]' - ) AS reactions, - COUNT(DISTINCT c.id)::int AS comment_count + ) AS reactions 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 ${cursorClause} GROUP BY s.id ORDER BY s.shared_at DESC @@ -2585,9 +2599,17 @@ ${excerpt} } }); - // GET /together/article/:shareId — アーカイブ本文取得 + // GET /together/article/:shareId — アーカイブ本文取得 ?u=username r.get('/together/article/:shareId', async (req, res) => { + const username = req.query.u; + if (!username) return res.status(400).json({ error: 'u (username) は必須です' }); try { + const memberCheck = await pool.query( + 'SELECT 1 FROM together_members m JOIN together_shares s ON s.group_id=m.group_id WHERE s.id=$1 AND m.username=$2', + [req.params.shareId, username] + ); + if (memberCheck.rows.length === 0) return res.status(403).json({ error: 'グループのメンバーではありません' }); + const result = await pool.query( 'SELECT id, title, url, full_content, summary, archive_status, shared_at FROM together_shares WHERE id=$1', [req.params.shareId] @@ -2635,9 +2657,17 @@ ${excerpt} } }); - // GET /together/comments/:shareId — コメント一覧 + // GET /together/comments/:shareId — コメント一覧 ?u=username r.get('/together/comments/:shareId', async (req, res) => { + const username = req.query.u; + if (!username) return res.status(400).json({ error: 'u (username) は必須です' }); try { + const memberCheck = await pool.query( + 'SELECT 1 FROM together_members m JOIN together_shares s ON s.group_id=m.group_id WHERE s.id=$1 AND m.username=$2', + [req.params.shareId, username] + ); + if (memberCheck.rows.length === 0) return res.status(403).json({ error: 'グループのメンバーではありません' }); + const result = await pool.query( 'SELECT * FROM together_comments WHERE share_id=$1 ORDER BY created_at', [req.params.shareId] @@ -2673,9 +2703,15 @@ ${excerpt} } }); - // GET /together/search/:groupId — キーワード / タグ検索 + // GET /together/search/:groupId — キーワード / タグ検索 ?u=username r.get('/together/search/:groupId', async (req, res) => { - const { q = '', tag = '' } = req.query; + const { q = '', tag = '', u: username = '' } = req.query; + if (!username) return res.status(400).json({ error: 'u (username) は必須です' }); + const memberOk = await pool.query( + 'SELECT 1 FROM together_members WHERE group_id=$1 AND username=$2', + [req.params.groupId, username] + ).then(r => r.rows.length > 0).catch(() => false); + if (!memberOk) return res.status(403).json({ error: 'グループのメンバーではありません' }); if (!q && !tag) return res.status(400).json({ error: 'q または tag が必要です' }); try { const keyword = q ? `%${q}%` : '';