239 lines
8.0 KiB
Python
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
|