Files
Munich-news/backend/routes/subscription_routes.py
2025-11-12 22:33:56 +01:00

239 lines
8.0 KiB
Python

from flask import Blueprint, request, jsonify
from datetime import datetime
from pymongo.errors import DuplicateKeyError
from database import subscribers_collection
subscription_bp = Blueprint('subscription', __name__)
@subscription_bp.route('/api/subscribe', methods=['POST'])
def subscribe():
"""Subscribe a user to the newsletter with category preferences"""
data = request.json
email = data.get('email', '').strip().lower()
# Get valid categories from RSS feeds
from database import rss_feeds_collection
valid_categories = rss_feeds_collection.distinct('category')
categories = data.get('categories', valid_categories) # Default: all categories
if not email or '@' not in email:
return jsonify({'error': 'Invalid email address'}), 400
# Validate categories
if not isinstance(categories, list) or not categories:
categories = valid_categories # Default to all if invalid
else:
# Filter to only valid categories (categories that exist in RSS feeds)
categories = [c for c in categories if c in valid_categories]
if not categories:
categories = valid_categories # Default to all if none valid
try:
subscriber_doc = {
'email': email,
'subscribed_at': datetime.utcnow(),
'status': 'active',
'categories': categories
}
# Try to insert, if duplicate key error, subscriber already exists
try:
subscribers_collection.insert_one(subscriber_doc)
return jsonify({
'message': 'Successfully subscribed!',
'categories': categories
}), 201
except DuplicateKeyError:
# Check if subscriber is active
existing = subscribers_collection.find_one({'email': email})
if existing and existing.get('status') == 'active':
# Update categories even if already subscribed
subscribers_collection.update_one(
{'email': email},
{'$set': {'categories': categories}}
)
return jsonify({
'message': 'Email already subscribed. Preferences updated!',
'categories': categories
}), 200
else:
# Reactivate if previously unsubscribed
subscribers_collection.update_one(
{'email': email},
{'$set': {
'status': 'active',
'subscribed_at': datetime.utcnow(),
'categories': categories
}}
)
return jsonify({
'message': 'Successfully re-subscribed!',
'categories': categories
}), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
@subscription_bp.route('/api/unsubscribe', methods=['POST'])
def unsubscribe():
"""Unsubscribe a user from the newsletter"""
data = request.json
email = data.get('email', '').strip().lower()
try:
result = subscribers_collection.update_one(
{'email': email},
{'$set': {'status': 'inactive'}}
)
if result.matched_count > 0:
return jsonify({'message': 'Successfully unsubscribed'}), 200
else:
return jsonify({'error': 'Email not found in subscribers'}), 404
except Exception as e:
return jsonify({'error': str(e)}), 500
@subscription_bp.route('/api/subscribers', methods=['GET'])
def list_subscribers():
"""List all subscribers with optional status filter"""
try:
# Get status filter from query params (default: all)
status = request.args.get('status', None)
# Build query
query = {}
if status:
query['status'] = status
# Fetch subscribers
subscribers = list(subscribers_collection.find(
query,
{'_id': 0, 'email': 1, 'subscribed_at': 1, 'status': 1}
).sort('subscribed_at', -1))
# Convert datetime to ISO format
for sub in subscribers:
if 'subscribed_at' in sub:
sub['subscribed_at'] = sub['subscribed_at'].isoformat()
return jsonify({
'subscribers': subscribers,
'total': len(subscribers)
}), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
@subscription_bp.route('/api/subscribers/<email>', methods=['GET'])
def get_subscriber(email):
"""Get a single subscriber's information"""
try:
email = email.strip().lower()
subscriber = subscribers_collection.find_one(
{'email': email},
{'_id': 0, 'email': 1, 'categories': 1, 'status': 1, 'subscribed_at': 1}
)
if subscriber:
# Convert datetime to ISO format
if 'subscribed_at' in subscriber:
subscriber['subscribed_at'] = subscriber['subscribed_at'].isoformat()
# Ensure categories field exists
if 'categories' not in subscriber:
subscriber['categories'] = ['general', 'local', 'sports']
return jsonify(subscriber), 200
else:
return jsonify({'error': 'Email not found in subscribers'}), 404
except Exception as e:
return jsonify({'error': str(e)}), 500
@subscription_bp.route('/api/subscribers/<email>', methods=['DELETE'])
def remove_subscriber(email):
"""Permanently remove a subscriber from the database"""
try:
email = email.strip().lower()
result = subscribers_collection.delete_one({'email': email})
if result.deleted_count > 0:
return jsonify({'message': f'Subscriber {email} permanently removed'}), 200
else:
return jsonify({'error': 'Email not found in subscribers'}), 404
except Exception as e:
return jsonify({'error': str(e)}), 500
@subscription_bp.route('/api/admin/subscribers/delete-all', methods=['DELETE'])
def delete_all_subscribers():
"""Delete all subscribers (admin only)"""
try:
result = subscribers_collection.delete_many({})
return jsonify({
'message': f'Successfully deleted {result.deleted_count} subscribers',
'deleted_count': result.deleted_count
}), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
@subscription_bp.route('/api/categories', methods=['GET'])
def get_categories():
"""Get available newsletter categories from RSS feeds"""
from database import rss_feeds_collection
# Get unique categories from RSS feeds
categories_from_db = rss_feeds_collection.distinct('category')
# Category metadata
category_info = {
'general': {
'name': 'Top Trending',
'description': 'Top trending news and updates',
'icon': '🔥'
},
'local': {
'name': 'Local Events',
'description': 'Local events, culture, and community news',
'icon': '🏛️'
},
'sports': {
'name': 'Sports',
'description': 'Sports news and updates',
'icon': ''
},
'science': {
'name': 'Science & Tech',
'description': 'Science and technology news',
'icon': '🔬'
}
}
# Build category list
categories = []
for cat_id in sorted(categories_from_db):
info = category_info.get(cat_id, {
'name': cat_id.title(),
'description': f'{cat_id.title()} news',
'icon': '📄'
})
categories.append({
'id': cat_id,
'name': info['name'],
'description': info['description'],
'icon': info['icon']
})
return jsonify({'categories': categories}), 200