Files
Munich-news/backend/routes/admin_routes.py
2025-11-11 16:58:03 +01:00

194 lines
6.5 KiB
Python

"""
Admin routes for testing and manual operations
"""
from flask import Blueprint, request, jsonify
import subprocess
import os
from pathlib import Path
admin_bp = Blueprint('admin', __name__)
@admin_bp.route('/api/admin/trigger-crawl', methods=['POST'])
def trigger_crawl():
"""
Manually trigger the news crawler
Request body (optional):
{
"max_articles": 10 // Number of articles per feed
}
"""
try:
data = request.get_json() or {}
max_articles = data.get('max_articles', 10)
# Validate max_articles
if not isinstance(max_articles, int) or max_articles < 1 or max_articles > 100:
return jsonify({
'success': False,
'error': 'max_articles must be an integer between 1 and 100'
}), 400
# Execute crawler in crawler container using docker exec
try:
result = subprocess.run(
['docker', 'exec', 'munich-news-crawler', 'python', 'crawler_service.py', str(max_articles)],
capture_output=True,
text=True,
timeout=300 # 5 minute timeout
)
# Check result
success = result.returncode == 0
return jsonify({
'success': success,
'message': f'Crawler {"executed successfully" if success else "failed"}',
'max_articles': max_articles,
'output': result.stdout[-1000:] if result.stdout else '', # Last 1000 chars
'errors': result.stderr[-500:] if result.stderr else ''
}), 200 if success else 500
except FileNotFoundError:
return jsonify({
'success': False,
'error': 'Docker command not found. Make sure Docker is installed and the socket is mounted.'
}), 500
except subprocess.TimeoutExpired:
return jsonify({
'success': False,
'error': 'Crawler timed out after 5 minutes'
}), 500
except Exception as e:
return jsonify({
'success': False,
'error': f'Failed to run crawler: {str(e)}'
}), 500
@admin_bp.route('/api/admin/send-test-email', methods=['POST'])
def send_test_email():
"""
Send a test newsletter to a specific email
Request body:
{
"email": "test@example.com",
"max_articles": 10 // Optional, defaults to 10
}
"""
try:
data = request.get_json()
if not data or 'email' not in data:
return jsonify({
'success': False,
'error': 'Email address is required'
}), 400
email = data.get('email', '').strip()
max_articles = data.get('max_articles', 10)
# Validate email
if not email or '@' not in email:
return jsonify({
'success': False,
'error': 'Invalid email address'
}), 400
# Validate max_articles (not used currently but validated for future use)
if not isinstance(max_articles, int) or max_articles < 1 or max_articles > 50:
return jsonify({
'success': False,
'error': 'max_articles must be an integer between 1 and 50'
}), 400
# Execute sender in sender container using docker exec
try:
result = subprocess.run(
['docker', 'exec', 'munich-news-sender', 'python', 'sender_service.py', 'test', email],
capture_output=True,
text=True,
timeout=60 # 1 minute timeout
)
# Check if successful
success = result.returncode == 0
return jsonify({
'success': success,
'message': f'Test email {"sent" if success else "failed"} to {email}',
'email': email,
'output': result.stdout[-1000:] if result.stdout else '', # Last 1000 chars
'errors': result.stderr[-500:] if result.stderr else ''
}), 200 if success else 500
except FileNotFoundError:
return jsonify({
'success': False,
'error': 'Docker command not found. Make sure Docker is installed and the socket is mounted.'
}), 500
except subprocess.TimeoutExpired:
return jsonify({
'success': False,
'error': 'Email sending timed out after 1 minute'
}), 500
except Exception as e:
return jsonify({
'success': False,
'error': f'Failed to send email: {str(e)}'
}), 500
@admin_bp.route('/api/admin/stats', methods=['GET'])
def get_stats():
"""Get system statistics"""
try:
from database import (
articles_collection,
subscribers_collection,
rss_feeds_collection,
newsletter_sends_collection,
link_clicks_collection
)
stats = {
'articles': {
'total': articles_collection.count_documents({}),
'with_summary': articles_collection.count_documents({'summary': {'$exists': True, '$ne': None}}),
'today': articles_collection.count_documents({
'crawled_at': {
'$gte': datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
}
})
},
'subscribers': {
'total': subscribers_collection.count_documents({}),
'active': subscribers_collection.count_documents({'active': True})
},
'rss_feeds': {
'total': rss_feeds_collection.count_documents({}),
'active': rss_feeds_collection.count_documents({'active': True})
},
'tracking': {
'total_sends': newsletter_sends_collection.count_documents({}),
'total_opens': newsletter_sends_collection.count_documents({'opened': True}),
'total_clicks': link_clicks_collection.count_documents({'clicked': True})
}
}
return jsonify(stats), 200
except Exception as e:
return jsonify({
'success': False,
'error': str(e)
}), 500
# Import datetime for stats endpoint
from datetime import datetime