from .common import ( MAX_FEED_MESSAGES, current_user, jsonify, request, get_db, build_feed_payload, room_by_id, can_access_room, get_channel_by_id, can_access_channel, validate_dm_access, ) def register_feed_routes(app): @app.route("/api/feed") def get_feed(): user = current_user() after_raw = request.args.get("after_id", "").strip() after_id = 0 if after_raw: try: after_id = max(0, int(after_raw)) except ValueError: return jsonify({"error": "after_id must be a non-negative integer."}), 400 base_select = f""" SELECT m.id, m.room_id, m.channel_id, m.direct_conversation_id, m.user_id, m.username, m.text, m.created_at, m.attachment_id, m.parent_message_id, ( SELECT COUNT(*) FROM messages AS thread_reply WHERE thread_reply.parent_message_id = m.id ) AS thread_reply_count, r.slug AS room_slug, r.name AS room_name, r.visibility AS room_visibility, c.slug AS channel_slug, c.name AS channel_name, t.slug AS team_slug, t.name AS team_name, a.original_name AS attachment_original_name, a.mime_type AS attachment_mime_type, a.size_bytes AS attachment_size_bytes, u.role AS user_role, us.name_color AS user_name_color FROM messages AS m JOIN users AS u ON u.id = m.user_id LEFT JOIN user_settings AS us ON us.user_id = u.id LEFT JOIN rooms AS r ON r.id = m.room_id LEFT JOIN channels AS c ON c.id = m.channel_id AND c.is_active = 1 LEFT JOIN teams AS t ON t.id = c.team_id AND t.is_active = 1 LEFT JOIN attachments AS a ON a.id = m.attachment_id """ params = [] if user and user["role"] in {"moderator", "admin"}: access_clause = """ ( (m.direct_conversation_id IS NULL AND r.id IS NOT NULL AND r.is_active = 1) OR (m.direct_conversation_id IS NULL AND c.id IS NOT NULL AND c.is_active = 1 AND t.is_active = 1) OR ( m.direct_conversation_id IS NOT NULL AND EXISTS ( SELECT 1 FROM direct_conversation_members AS dcm WHERE dcm.direct_conversation_id = m.direct_conversation_id AND dcm.user_id = ? ) ) ) """ params.append(user["id"]) elif user: access_clause = """ ( ( m.direct_conversation_id IS NULL AND r.id IS NOT NULL AND r.is_active = 1 AND ( r.visibility = 'public' OR EXISTS ( SELECT 1 FROM room_members AS rm WHERE rm.room_id = r.id AND rm.user_id = ? ) ) ) OR ( m.direct_conversation_id IS NULL AND c.id IS NOT NULL AND c.is_active = 1 AND t.is_active = 1 AND c.legacy_room_id IS NULL AND EXISTS ( SELECT 1 FROM team_members AS tm WHERE tm.team_id = c.team_id AND tm.user_id = ? ) ) OR ( m.direct_conversation_id IS NOT NULL AND EXISTS ( SELECT 1 FROM direct_conversation_members AS dcm WHERE dcm.direct_conversation_id = m.direct_conversation_id AND dcm.user_id = ? ) ) ) """ params.extend([user["id"], user["id"], user["id"]]) else: access_clause = """ m.direct_conversation_id IS NULL AND r.id IS NOT NULL AND r.is_active = 1 AND r.visibility = 'public' """ if after_raw: query = f""" {base_select} WHERE m.parent_message_id IS NULL AND {access_clause} AND m.id > ? ORDER BY m.id ASC LIMIT ? """ query_params = [*params, after_id, MAX_FEED_MESSAGES] with get_db() as db: rows = db.execute(query, tuple(query_params)).fetchall() entries = [build_feed_payload(row, user) for row in rows] latest_id = entries[-1]["id"] if entries else after_id return jsonify( { "entries": entries, "latest_id": latest_id, "has_more": len(entries) == MAX_FEED_MESSAGES, } ) query = f""" SELECT * FROM ( {base_select} WHERE m.parent_message_id IS NULL AND {access_clause} ORDER BY m.id DESC LIMIT ? ) ORDER BY id ASC """ query_params = [*params, MAX_FEED_MESSAGES] with get_db() as db: rows = db.execute(query, tuple(query_params)).fetchall() entries = [build_feed_payload(row, user) for row in rows] latest_id = entries[-1]["id"] if entries else 0 return jsonify({"entries": entries, "latest_id": latest_id, "has_more": False})