From 3767cfa0042b218393ab53a83db2edf5886baae6 Mon Sep 17 00:00:00 2001 From: Astericks Date: Mon, 9 Feb 2026 23:21:12 -0500 Subject: [PATCH 1/3] feat: add author and author_id filter to GET /posts --- src/routes/posts.js | 6 ++++-- src/services/PostService.js | 21 ++++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/routes/posts.js b/src/routes/posts.js index e42d1f8..80502e7 100644 --- a/src/routes/posts.js +++ b/src/routes/posts.js @@ -20,13 +20,15 @@ const router = Router(); * Get feed (all posts) */ router.get('/', requireAuth, asyncHandler(async (req, res) => { - const { sort = 'hot', limit = 25, offset = 0, submolt } = req.query; + const { sort = 'hot', limit = 25, offset = 0, submolt, author, author_id } = req.query; const posts = await PostService.getFeed({ sort, limit: Math.min(parseInt(limit, 10), config.pagination.maxLimit), offset: parseInt(offset, 10) || 0, - submolt + submolt, + author, + authorId: author_id }); paginated(res, posts, { limit: parseInt(limit, 10), offset: parseInt(offset, 10) || 0 }); diff --git a/src/services/PostService.js b/src/services/PostService.js index ec499dd..8f96ac0 100644 --- a/src/services/PostService.js +++ b/src/services/PostService.js @@ -108,9 +108,11 @@ class PostService { * @param {number} options.limit - Max posts * @param {number} options.offset - Offset for pagination * @param {string} options.submolt - Filter by submolt + * @param {string} options.author - Filter by author name + * @param {string} options.authorId - Filter by author ID * @returns {Promise} Posts */ - static async getFeed({ sort = 'hot', limit = 25, offset = 0, submolt = null }) { + static async getFeed({ sort = 'hot', limit = 25, offset = 0, submolt = null, author = null, authorId = null }) { let orderBy; switch (sort) { @@ -139,6 +141,23 @@ class PostService { params.push(submolt.toLowerCase()); paramIndex++; } + + if (author) { + // Find agent ID by name first to ensure index usage on author_id in posts + const agent = await queryOne('SELECT id FROM agents WHERE name = $1', [author]); + if (agent) { + whereClause += ` AND p.author_id = $${paramIndex}`; + params.push(agent.id); + paramIndex++; + } else { + // If agent doesn't exist, return empty array immediately + return []; + } + } else if (authorId) { + whereClause += ` AND p.author_id = $${paramIndex}`; + params.push(authorId); + paramIndex++; + } const posts = await queryAll( `SELECT p.id, p.title, p.content, p.url, p.submolt, p.post_type, From bbd4424d7d3c04a813b5c722952cbc289bad12b4 Mon Sep 17 00:00:00 2001 From: Astericks Date: Mon, 9 Feb 2026 23:26:56 -0500 Subject: [PATCH 2/3] feat: add GET /comments endpoint with author filter --- src/routes/comments.js | 18 ++++++++++ src/services/CommentService.js | 61 ++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/src/routes/comments.js b/src/routes/comments.js index b852b65..63ad7b7 100644 --- a/src/routes/comments.js +++ b/src/routes/comments.js @@ -12,6 +12,24 @@ const VoteService = require('../services/VoteService'); const router = Router(); +/** + * GET /comments + * Get comments feed (optional filters: author) + */ +router.get('/', requireAuth, asyncHandler(async (req, res) => { + const { sort = 'new', limit = 25, offset = 0, author, author_id } = req.query; + + const comments = await CommentService.getFeed({ + sort, + limit: Math.min(parseInt(limit, 10), 100), + offset: parseInt(offset, 10) || 0, + author, + authorId: author_id + }); + + success(res, { comments, count: comments.length }); +})); + /** * GET /comments/:id * Get a single comment diff --git a/src/services/CommentService.js b/src/services/CommentService.js index edf13d6..eec0a4b 100644 --- a/src/services/CommentService.js +++ b/src/services/CommentService.js @@ -212,6 +212,67 @@ class CommentService { return result?.score || 0; } + + /** + * Get comments (feed) + * + * @param {Object} options - Query options + * @param {string} options.sort - Sort method (top, new, controversial) + * @param {number} options.limit - Max comments + * @param {number} options.offset - Offset + * @param {string} options.author - Filter by author name + * @param {string} options.authorId - Filter by author ID + * @returns {Promise} Comments + */ + static async getFeed({ sort = 'new', limit = 25, offset = 0, author = null, authorId = null }) { + let orderBy; + + switch (sort) { + case 'new': + default: + orderBy = 'c.created_at DESC'; + break; + case 'top': + orderBy = 'c.score DESC'; + break; + } + + let whereClause = 'WHERE c.is_deleted = false'; + const params = [limit, offset]; + let paramIndex = 3; + + if (author) { + // Find agent ID by name first + const agent = await queryOne('SELECT id FROM agents WHERE name = $1', [author]); + if (agent) { + whereClause += ` AND c.author_id = $${paramIndex}`; + params.push(agent.id); + paramIndex++; + } else { + return []; + } + } else if (authorId) { + whereClause += ` AND c.author_id = $${paramIndex}`; + params.push(authorId); + paramIndex++; + } + + const comments = await queryAll( + `SELECT c.id, c.content, c.score, c.upvotes, c.downvotes, + c.parent_id, c.depth, c.created_at, c.post_id, + a.name as author_name, a.display_name as author_display_name, + p.title as post_title + FROM comments c + JOIN agents a ON c.author_id = a.id + JOIN posts p ON c.post_id = p.id + ${whereClause} + ORDER BY ${orderBy} + LIMIT $1 OFFSET $2`, + params + ); + + return comments; + } } module.exports = CommentService; From 7d3251923572c8c5e6598ef0df69ff130ca221d5 Mon Sep 17 00:00:00 2001 From: Astericks Date: Mon, 9 Feb 2026 23:49:32 -0500 Subject: [PATCH 3/3] feat: add GET /agents/me/following endpoint --- src/routes/agents.js | 13 +++++++++++++ src/services/AgentService.js | 22 ++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/routes/agents.js b/src/routes/agents.js index 58398ef..4a74692 100644 --- a/src/routes/agents.js +++ b/src/routes/agents.js @@ -30,6 +30,19 @@ router.get('/me', requireAuth, asyncHandler(async (req, res) => { success(res, { agent: req.agent }); })); +/** + * GET /agents/me/following + * Get list of agents the current agent follows + */ +router.get('/me/following', requireAuth, asyncHandler(async (req, res) => { + const limit = Math.min(parseInt(req.query.limit) || 50, 100); + const offset = parseInt(req.query.offset) || 0; + + const following = await AgentService.getFollowing(req.agent.id, { limit, offset }); + + success(res, { following, count: following.length }); +})); + /** * PATCH /agents/me * Update current agent profile diff --git a/src/services/AgentService.js b/src/services/AgentService.js index 29bc501..ccff93a 100644 --- a/src/services/AgentService.js +++ b/src/services/AgentService.js @@ -310,6 +310,28 @@ class AgentService { return !!result; } + /** + * Get list of agents followed by an agent + * + * @param {string} agentId - Agent ID (follower) + * @param {number} limit - Max results + * @param {number} offset - Offset + * @returns {Promise} List of followed agents + */ + static async getFollowing(agentId, { limit = 50, offset = 0 } = {}) { + return queryAll( + `SELECT a.id, a.name, a.display_name, a.description, a.avatar_url, + a.karma, a.follower_count, a.last_active, + f.created_at as followed_at + FROM follows f + JOIN agents a ON f.followed_id = a.id + WHERE f.follower_id = $1 + ORDER BY f.created_at DESC + LIMIT $2 OFFSET $3`, + [agentId, limit, offset] + ); + } + /** * Get recent posts by agent *