Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 95 additions & 9 deletions backend/api_gateway/api_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@
from backend.core.config import Config
from backend.core.utils import setup_logger, log_exception
from backend.microservices.auth_service import load_users
from backend.microservices.news_storage import store_article_in_supabase, log_user_search
from backend.microservices.news_storage import store_article_in_supabase, log_user_search, add_bookmark, get_user_bookmarks, delete_bookmark
# Initialize logger
logger = setup_logger(__name__)

# Initialize Flask app with CORS support
app = Flask(__name__)
app.config['SECRET_KEY'] = os.getenv('JWT_SECRET_KEY', 'your-secret-key') # Change this in production
CORS(app, origins=Config.CORS_ORIGINS, supports_credentials=True, allow_headers=['Content-Type', 'Authorization'])
CORS(app, origins=Config.CORS_ORIGINS, supports_credentials=True, allow_headers=['Content-Type', 'Authorization', 'Access-Control-Allow-Origin'], methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], expose_headers=['Access-Control-Allow-Origin'])

# Initialize Flask-RestX
api = Api(app, version='1.0', title='News Aggregator API',
Expand All @@ -45,6 +45,7 @@
summarize_ns = api.namespace('summarize', description='Text summarization operations')
user_ns = api.namespace('api/user', description='User operations')
auth_ns = api.namespace('api/auth', description='Authentication operations')
bookmark_ns = api.namespace('api/bookmarks', description='Bookmark operations')

def token_required(f):
@wraps(f)
Expand All @@ -54,7 +55,8 @@ def decorated(*args, **kwargs):
return {'error': 'Authorization header missing'}, 401
try:
token = auth_header.split()[1]
payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'],audience='authenticated')

return f(*args, **kwargs)
except Exception as e:
return {'error': 'Invalid token', 'message': str(e)}, 401
Expand Down Expand Up @@ -139,8 +141,6 @@ def get(self):
'message': str(e)
}), 500)




# News processing endpoint
@news_ns.route('/process')
Expand Down Expand Up @@ -169,7 +169,6 @@ def post(self):
class Signup(Resource):
@auth_ns.expect(signup_model)
def post(self):
print('signup')
"""Register a new user"""
data = request.get_json()
username = data.get('username')
Expand All @@ -196,8 +195,6 @@ def post(self):
'firstName': firstName,
'lastName': lastName
}

print(new_user)

users.append(new_user)

Expand All @@ -223,7 +220,6 @@ def post(self):
class Login(Resource):
def post(self):
"""Login and get authentication token"""
print('login in')
data = request.get_json()
username = data.get('username')
password = data.get('password')
Expand Down Expand Up @@ -263,6 +259,96 @@ def get(self):

return {k: user[k] for k in user if k != 'password'}, 200

@bookmark_ns.route('/')
class Bookmark(Resource):
@token_required
def get(self):
"""Get all bookmarked articles for the authenticated user"""
try:
# Get the user ID from the token
auth_header = request.headers.get('Authorization')
token = auth_header.split()[1]
payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'],audience='authenticated')
user_id = payload.get('sub')

# Get bookmarks using the news_storage service
bookmarks = get_user_bookmarks(user_id)

return {
'status': 'success',
'data': bookmarks
}, 200

except Exception as e:
logger.error(f"Error fetching bookmarks: {str(e)}")
return {
'status': 'error',
'message': str(e)
}, 500

@token_required
def post(self):
"""Add a bookmark for a news article"""
try:
# Get the user ID from the token
auth_header = request.headers.get('Authorization')
token = auth_header.split()[1]
payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'],audience='authenticated')
user_id = payload.get('sub')

# Get the news article ID from the request body
data = request.get_json()
news_id = data.get('news_id')

if not news_id:
return {'error': 'News article ID is required'}, 400

# Add the bookmark using the news_storage service
# bookmark = add_bookmark(user_id, '054c021a-f6f3-44b2-a43f-1ca0d211eb15')
bookmark = add_bookmark(user_id, news_id)

return {
'status': 'success',
'message': 'Bookmark added successfully',
'data': {
'bookmark_id': bookmark['id'] if isinstance(bookmark, dict) else bookmark
}
}, 201

except Exception as e:
logger.error(f"Error adding bookmark: {str(e)}")
return {
'status': 'error',
'message': str(e)
}, 500

@bookmark_ns.route('/<string:bookmark_id>')
class BookmarkDelete(Resource):
@token_required
def delete(self, bookmark_id):
"""Remove a bookmark for a news article"""
try:
# Get the user ID from the token
auth_header = request.headers.get('Authorization')
token = auth_header.split()[1]
payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'],audience='authenticated')
user_id = payload.get('sub')

# Delete the bookmark using the news_storage service
result = delete_bookmark(user_id, bookmark_id)

return {
'status': 'success',
'message': 'Bookmark removed successfully'
}, 200

except Exception as e:
logger.error(f"Error removing bookmark: {str(e)}")
return {
'status': 'error',
'message': str(e)
}, 500

if __name__ == '__main__':
port = int(sys.argv[1]) if len(sys.argv) > 1 else Config.API_PORT
app.run(host=Config.API_HOST, port=port, debug=True)
51 changes: 0 additions & 51 deletions backend/microservices/auth_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,54 +42,3 @@ def load_users():
except Exception as e:
print(f"Error loading users: {e}")
return []

# @app.route('/api/auth/login', methods=['POST'])
# def login():
# data = request.get_json()
# username = data.get('username')
# password = data.get('password')

# if not username or not password:
# return jsonify({'error': 'Username and password are required'}), 400

# users = load_users()
# user = next((u for u in users if u.get('username') == username and u.get('password') == password), None)

# if not user:
# return jsonify({'error': 'Invalid credentials'}), 401

# token = jwt.encode({
# 'id': user['id'],
# 'username': user['username'],
# 'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
# }, app.config['SECRET_KEY'], algorithm='HS256')

# user_data = {k: user[k] for k in user if k != 'password'}
# return jsonify({'token': token, 'user': user_data})

# @app.route('/api/user/profile', methods=['GET'])
# def profile():
# auth_header = request.headers.get('Authorization')
# if not auth_header:
# return jsonify({'error': 'Authorization header missing'}), 401

# try:
# token = auth_header.split()[1]
# payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256'])
# except Exception as e:
# return jsonify({'error': 'Invalid token', 'message': str(e)}), 401

# users = load_users()
# user = next((u for u in users if u.get('id') == payload.get('id')), None)
# if not user:
# return jsonify({'error': 'User not found'}), 404

# user_data = {k: user[k] for k in user if k != 'password'}
# return jsonify(user_data)

# @app.route('/health', methods=['GET'])
# def health():
# return jsonify({'status': 'Authentication service is healthy'}), 200

# if __name__ == '__main__':
# app.run(host='0.0.0.0', port=5003, debug=True)
7 changes: 6 additions & 1 deletion backend/microservices/news_fetcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,9 @@ def write_to_file(articles, session_id=None):
print(f"Error writing to file: {e}")

if __name__ == '__main__':
fetch_news()
fetch_news()





56 changes: 55 additions & 1 deletion backend/microservices/news_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,58 @@ def log_user_search(user_id, news_id, session_id):
"searched_at": datetime.datetime.utcnow().isoformat(),
"session_id": session_id,
}).execute()
return result
return result

def add_bookmark(user_id, news_id):
"""
Adds a bookmark by inserting a record into the user_bookmarks table.
Returns the created bookmark record if successful.
"""
try:
result = supabase.table("user_bookmarks").insert({
"user_id": user_id,
"news_id": news_id,
}).execute()
return result.data[0] if result.data else None
except Exception as e:
print(f"Error adding bookmark: {str(e)}")
raise e

def get_user_bookmarks(user_id):
"""
Retrieves all bookmarked articles for a user with full article details.
Returns a list of bookmarked articles with their details.
"""
try:
# Query user_bookmarks and join with news_articles to get full article details
result = supabase.table("user_bookmarks") \
.select(
"id,"
"news_articles(id,title,summary,content,source,published_at,url,image)"
) \
.eq("user_id", user_id) \
.execute()

# Transform the result to a more friendly format
bookmarks = []
for item in result.data:
article = item["news_articles"]
article["bookmark_id"] = item["id"]
bookmarks.append(article)

return bookmarks
except Exception as e:
print(f"Error fetching bookmarks: {str(e)}")
raise e

def delete_bookmark(user_id, bookmark_id):
"""
Deletes a bookmark from the user_bookmarks table.
Returns True if successful, False otherwise.
"""
try:
result = supabase.table("user_bookmarks").delete().eq("id", bookmark_id).eq("user_id", user_id).execute()
return len(result.data) > 0
except Exception as e:
print(f"Error deleting bookmark: {str(e)}")
raise e
1 change: 1 addition & 0 deletions backend/microservices/summarization_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ def process_articles(session_id):
summary = run_summarization(article.get('content', ''))

summarized_articles.append({
'id': article['id'],
'title': article['title'],
'author': article.get('author', 'Unknown Author'),
'source': article.get('source'),
Expand Down
Loading