Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Upgrade] Users! #44

Merged
merged 8 commits into from
Jan 21, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
83 changes: 58 additions & 25 deletions backend/app.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from functools import wraps
import logging
import os
import threading
import time

from dotenv import load_dotenv
from flask import Flask, abort, after_this_request, jsonify, request, send_from_directory
from flask import Flask, abort, after_this_request, jsonify, make_response, request, send_from_directory
from flask_cors import CORS, cross_origin
from flask_apscheduler import APScheduler

Expand All @@ -22,13 +23,18 @@

# Initialize Logging
logging.basicConfig(level=logging.INFO)
log_level = logging.WARNING
app.logger.setLevel(log_level)
# log_level = logging.WARNING
# app.logger.setLevel(log_level)

# Import dot env variables
load_dotenv()


def is_user_logged_in():
user_session = request.cookies.get('user_session')
return user_session and bb_session_manager.retrieve_bb_session(user_session)


@scheduler.task('interval', id='clean_up', seconds=600)
def clean_up_and_upload_files_to_google_drive(file_path=None):

Expand All @@ -47,23 +53,13 @@ def clean_up_and_upload_files_to_google_drive(file_path=None):
bb_session_manager = BlackboardSessionManager()


@scheduler.task('interval', id='delete_sessions', seconds=60)
def delete_inactive_bb_sessions(inactivity_threshold_seconds=180):
current_time = time.time()

# Collect usernames with inactive sessions for deletion
usernames_to_delete = []
for username, session_id in bb_session_manager.user_session_map.items():
session = bb_session_manager.bb_sessions.get(session_id)
if session:
last_activity_time = session.last_activity_time
inactive_duration = current_time - last_activity_time
if inactive_duration > inactivity_threshold_seconds:
usernames_to_delete.append(username)

# Delete collected usernames' sessions
for username in usernames_to_delete:
bb_session_manager.delete_bb_session(username)
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not is_user_logged_in():
return jsonify({'error': 'Unauthorized access'}), 401
return f(*args, **kwargs)
return decorated_function


@app.route('/')
Expand All @@ -73,7 +69,7 @@ def index():


@app.route('/login', methods=['POST'])
@cross_origin()
@cross_origin(supports_credentials=True)
def login():
data = request.json
username = data.get('username')
Expand All @@ -83,7 +79,6 @@ def login():
return jsonify({'error': 'Missing username or password'}), 400

try:
# Retrieve or create a session for the user
bb_session = bb_session_manager.get_bb_session(username)
bb_session.username = username
bb_session.password = password
Expand All @@ -92,14 +87,47 @@ def login():
response = bb_session.get_response()
if response == 'Login successful.':
bb_session_manager.put_bb_session(username, bb_session)
return jsonify({'message': 'Logged in successfully'})

resp = make_response(
jsonify({'message': 'Logged in successfully'}))
resp.set_cookie('user_session', bb_session.session_id,
max_age=3600, secure=True, httponly=True)
return resp
else:
return jsonify({'error': response}), 401
except Exception as e:
return jsonify({'error': str(e)}), 500


@app.route('/logout', methods=['POST'])
@cross_origin(supports_credentials=True)
def logout():
user_session = request.cookies.get('user_session')
if user_session:
# Remove the session from BlackboardSessionManager
bb_session_manager.delete_bb_session(user_session)

# Clear the user's session cookie
resp = make_response(jsonify({'message': 'Logged out successfully'}))
resp.set_cookie('user_session', '', expires=0)
return resp
else:
return jsonify({'error': 'No active session'}), 400


@app.route('/is_logged_in', methods=['GET'])
@cross_origin(supports_credentials=True)
def is_logged_in():
user_session = request.cookies.get('user_session')
if user_session and bb_session_manager.retrieve_bb_session(user_session):
return jsonify({'logged_in': True}), 200
else:
return jsonify({'logged_in': False}), 401


@app.route('/scrape', methods=['GET'])
@cross_origin(supports_credentials=True)
@login_required
def scrape():
username = request.args.get('username')
if not username:
Expand All @@ -126,7 +154,8 @@ def scrape():


@app.route('/download/<file_key>', methods=['GET'])
@cross_origin()
@cross_origin(supports_credentials=True)
@login_required
def download(file_key):
"""
Download a file by its file key and then delete it from the server.
Expand All @@ -151,7 +180,8 @@ def trigger_post_download_operations(response):

@app.route('/browse/', defaults={'path': None})
@app.route('/browse/<path:path>')
@cross_origin()
@cross_origin(supports_credentials=True)
@login_required
def list_directory(path):

if path is None:
Expand All @@ -165,6 +195,7 @@ def list_directory(path):

return jsonify({'folders': folders, 'files': files})


def handle_single_file(file_id, file_name):
session_files_path = get_session_files_path()
if not os.path.exists(session_files_path):
Expand All @@ -183,7 +214,9 @@ def trigger_post_download_operations(response):

return send_from_directory(session_files_path, file_name, as_attachment=True)


@app.route('/browse')
@login_required
def list_root_directory():
return list_directory(None)

Expand Down
3 changes: 2 additions & 1 deletion backend/blackboard_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@


class BlackboardSession:
def __init__(self, username=None, password=None, max_threads=100):
def __init__(self, session_id=None, username=None, password=None, max_threads=100):
"""
Creates a blackboard session instance.

Expand All @@ -29,6 +29,7 @@ def __init__(self, username=None, password=None, max_threads=100):
self.username = username
self.password = password
self.max_threads = max_threads
self.session_id = session_id
self.courses = {}
self.download_tasks = []
self.is_logged_in = False
Expand Down
60 changes: 42 additions & 18 deletions backend/blackboard_session_manager.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,59 @@
import uuid
import threading
import time
from blackboard_session import BlackboardSession


class BlackboardSessionManager:
def __init__(self):
self.bb_sessions = {}
self.user_session_map = {}
self.lock = threading.Lock()

def get_bb_session(self, username):
if username not in self.user_session_map:
session_id = str(uuid.uuid4()) # Generate a unique session ID
self.user_session_map[username] = session_id
# Store the session object
self.bb_sessions[session_id] = BlackboardSession()
with self.lock:
if username not in self.user_session_map:
session_id = str(uuid.uuid4()) # Generate a unique session ID
self.user_session_map[username] = session_id
# Store the session object
self.bb_sessions[session_id] = BlackboardSession(
session_id, time.time())

return self.bb_sessions[self.user_session_map[username]]
return self.bb_sessions[self.user_session_map[username]]

def put_bb_session(self, username, bb_session):
session_id = self.user_session_map.get(username)
if session_id:
self.bb_sessions[session_id] = bb_session
with self.lock:
session_id = self.user_session_map.get(username)
if session_id:
self.bb_sessions[session_id] = bb_session

def retrieve_bb_session_by_username(self, username):
with self.lock:
session_id = self.user_session_map.get(username)
if session_id:
return self.bb_sessions.get(session_id)
return None

def retrieve_bb_session(self, username):
session_id = self.user_session_map.get(username)
if session_id:
def retrieve_bb_session_by_id(self, session_id):
with self.lock:
return self.bb_sessions.get(session_id)

return None
def retrieve_bb_session(self, identifier):
if isinstance(identifier, str) and '-' in identifier:
return self.retrieve_bb_session_by_id(identifier)
else:
return self.retrieve_bb_session_by_username(identifier)

def delete_bb_session(self, username):
session_id = self.user_session_map.get(username)
if session_id:
session_to_delete = self.bb_sessions.pop(session_id, None)
if session_to_delete:
del self.user_session_map[username]
with self.lock:
session_id = self.user_session_map.pop(username, None)
if session_id:
return self.bb_sessions.pop(session_id, None)

def clean_up_inactive_sessions(self, inactivity_threshold_seconds=3600):
with self.lock:
current_time = time.time()
inactive_sessions = [username for username, session_id in self.user_session_map.items()
if current_time - self.bb_sessions[session_id].last_activity_time > inactivity_threshold_seconds]
for username in inactive_sessions:
self.delete_bb_session(username)
24 changes: 17 additions & 7 deletions frontend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,35 @@
# Initialize Logging
logging.basicConfig(level=logging.INFO)


@app.route('/')
@cross_origin()
def index():
return render_template('index.html')

@app.route('/demo')
@cross_origin()
def demo():
return render_template('demo.html')

@app.route('/TOS')
@cross_origin()
def TOS():
return render_template('TOS.html')

@app.route('/directory/')

@app.route('/login')
@cross_origin()
def login():
return render_template('login.html')


@app.route('/logout')
@cross_origin()
def logout():
return render_template('logout.html')


@app.route('/userpage')
@cross_origin()
def directory():
return render_template('directory.html')
def userPage():
return render_template('userPage.html')


if __name__ == '__main__':
Expand Down
1 change: 0 additions & 1 deletion frontend/static/default.css
Original file line number Diff line number Diff line change
Expand Up @@ -2426,7 +2426,6 @@ select.form-control-lg:not([size]):not([multiple]),
line-height: 1.5;
border-radius: 0.3rem;
transition: all 0.2s;
margin-bottom: 5rem;
}
@media screen and (prefers-reduced-motion: reduce) {
.btn {
Expand Down
Loading
Loading