Skip to content

Commit

Permalink
Merge pull request #44 from TheManWhoLikesToCode/TheManWhoLikesToCode…
Browse files Browse the repository at this point in the history
…-patch-1

[Upgrade] Users!
  • Loading branch information
TheManWhoLikesToCode authored Jan 21, 2024
2 parents a735430 + f6294e2 commit 09b3782
Show file tree
Hide file tree
Showing 13 changed files with 458 additions and 268 deletions.
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
9 changes: 6 additions & 3 deletions frontend/static/helpers.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
function getConfig() {
const config = {
apiUrl: {
dev: "http://localhost:5003",
prod: "https://api.archive-me.net"
dev: "https://devapi.archive-me.net",
prod: "https://api.archive-me.net",
local: "http://localhost:5003"
}
};
return config;
}

function getEnv() {
const hostname = window.location.hostname;
return hostname.includes('localhost') ? 'dev' : 'prod';
if (hostname.includes('localhost')) return 'local';
if (hostname.includes('dev')) return 'dev';
return 'prod';
}

function getApiUrl() {
Expand Down
Loading

0 comments on commit 09b3782

Please sign in to comment.