diff --git a/blueprints/admin/routes_social_publisher.py b/blueprints/admin/routes_social_publisher.py index 71c824c..5ba0c1c 100644 --- a/blueprints/admin/routes_social_publisher.py +++ b/blueprints/admin/routes_social_publisher.py @@ -388,7 +388,8 @@ def social_publisher_fb_posts(company_id): finally: db.close() - result = social_publisher.get_page_recent_posts(company_id) + after = request.args.get('after') + result = social_publisher.get_page_recent_posts(company_id, after=after) return jsonify(result) diff --git a/facebook_graph_service.py b/facebook_graph_service.py index cd08435..bc4ceaf 100644 --- a/facebook_graph_service.py +++ b/facebook_graph_service.py @@ -273,22 +273,28 @@ class FacebookGraphService: 'reactions_total': result.get('reactions', {}).get('summary', {}).get('total_count', 0), } - def get_page_posts(self, page_id: str, limit: int = 10) -> Optional[List[Dict]]: + def get_page_posts(self, page_id: str, limit: int = 10, + after: str = None) -> Optional[Dict]: """Get recent posts from a Facebook Page with engagement metrics. Args: page_id: Facebook Page ID limit: Number of posts to fetch (max 100) + after: Pagination cursor for next page Returns: - List of post dicts or None on failure + Dict with 'posts' list and 'next_cursor' (or None), or None on failure """ fields = ( 'id,message,created_time,full_picture,permalink_url,status_type,' 'likes.summary(true).limit(0),comments.summary(true).limit(0),' 'shares,reactions.summary(true).limit(0)' ) - result = self._get(f'{page_id}/posts', {'fields': fields, 'limit': limit}) + params = {'fields': fields, 'limit': limit} + if after: + params['after'] = after + + result = self._get(f'{page_id}/posts', params) if not result: return None @@ -306,7 +312,14 @@ class FacebookGraphService: 'shares': item.get('shares', {}).get('count', 0) if item.get('shares') else 0, 'reactions_total': item.get('reactions', {}).get('summary', {}).get('total_count', 0), }) - return posts + + # Extract next page cursor + next_cursor = None + paging = result.get('paging', {}) + if paging.get('next'): + next_cursor = paging.get('cursors', {}).get('after') + + return {'posts': posts, 'next_cursor': next_cursor} def get_post_insights_metrics(self, post_id: str) -> Optional[Dict]: """Get detailed insights metrics for a specific post. diff --git a/services/social_publisher_service.py b/services/social_publisher_service.py index 5e39ad1..0d17a56 100644 --- a/services/social_publisher_service.py +++ b/services/social_publisher_service.py @@ -603,10 +603,11 @@ class SocialPublisherService: # ---- Facebook Page Posts (read from API) ---- - def get_page_recent_posts(self, company_id: int, limit: int = 10) -> Dict: + def get_page_recent_posts(self, company_id: int, limit: int = 10, + after: str = None) -> Dict: """Fetch recent posts from company's Facebook page with engagement metrics. - Uses in-memory cache with 5-minute TTL. + Uses in-memory cache with 5-minute TTL (first page only). """ db = SessionLocal() try: @@ -615,31 +616,41 @@ class SocialPublisherService: return {'success': False, 'error': 'Brak konfiguracji Facebook dla tej firmy.'} page_id = config.page_id - cache_key = (company_id, page_id) - # Check cache - cached = _posts_cache.get(cache_key) - if cached and (time.time() - cached['ts']) < _CACHE_TTL: - return { - 'success': True, - 'posts': cached['data'], - 'page_name': config.page_name or '', - 'cached': True, - } + # Cache only first page (no cursor) + if not after: + cache_key = (company_id, page_id) + cached = _posts_cache.get(cache_key) + if cached and (time.time() - cached['ts']) < _CACHE_TTL: + return { + 'success': True, + 'posts': cached['data'], + 'next_cursor': cached.get('next_cursor'), + 'page_name': config.page_name or '', + 'cached': True, + } from facebook_graph_service import FacebookGraphService fb = FacebookGraphService(access_token) - posts = fb.get_page_posts(page_id, limit) + result = fb.get_page_posts(page_id, limit, after=after) - if posts is None: + if result is None: return {'success': False, 'error': 'Nie udało się pobrać postów z Facebook API.'} - # Update cache - _posts_cache[cache_key] = {'data': posts, 'ts': time.time()} + posts = result['posts'] + next_cursor = result.get('next_cursor') + + # Update cache for first page only + if not after: + cache_key = (company_id, page_id) + _posts_cache[cache_key] = { + 'data': posts, 'next_cursor': next_cursor, 'ts': time.time() + } return { 'success': True, 'posts': posts, + 'next_cursor': next_cursor, 'page_name': config.page_name or '', 'cached': False, }