Skip to content

Commit

Permalink
trying to solve 401 error for gcal api
Browse files Browse the repository at this point in the history
  • Loading branch information
qianxuege committed Jan 6, 2025
1 parent 8810868 commit 1d7db95
Show file tree
Hide file tree
Showing 18 changed files with 344 additions and 134 deletions.
258 changes: 195 additions & 63 deletions backend/api/app.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,52 @@

import json
from flask import Flask, redirect, request, jsonify, session, url_for
from flask_pymongo import PyMongo
from flask_cors import CORS
from flask_session import Session
from google_auth_oauthlib.flow import Flow
from google.oauth2.credentials import Credentials
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from dotenv import load_dotenv
import os
import configparser
from datetime import timedelta


config = configparser.ConfigParser()
config.read(os.path.abspath(os.path.join("config.ini")))

app = Flask(__name__)
app.secret_key = 'YOUR_SECRET_KEY' # Replace with a secure secret key
'''commented out the two lines below so the app won't crash due to missing DB_URI'''
app.secret_key = "YOUR_SECRET_KEY" # Replace with a secure secret key

# Configure server-side session
app.config['SESSION_TYPE'] = 'filesystem' # Store sessions in files
app.config['SESSION_COOKIE_DOMAIN'] = 'localhost' # Allow cookies for all localhost subdomains
# app.config['SESSION_COOKIE_PATH'] = '/' # Make cookie available for all routes
app.config['SESSION_PERMANENT'] = True # Make the session permanent
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=1) # Session lifetime
app.config['SESSION_COOKIE_SAMESITE'] = 'None' # Allow cross-site cookies
app.config['SESSION_COOKIE_SECURE'] = False # Set to True if using HTTPS
app.config['SESSION_COOKIE_HTTPONLY'] = True # Prevent client-side JavaScript access
Session(app)

"""commented out the two lines below so the app won't crash due to missing DB_URI"""
# app.config["MONGO_URI"] = config['PROD']['DB_URI']
# mongo = PyMongo(app)

# CORS(app, supports_credentials=True, resources={r"/*": {"origins": "http://127.0.0.1:5173"}})
CORS(app, supports_credentials=True, resources={r"/*": {"origins": "*"}})
CORS(app, supports_credentials=True, resources={r"/*": {"origins": "http://localhost:5173"}})
# CORS(app, supports_credentials=True, resources={r"/*": {"origins": "*"}})


# Load environment variables from .env file
load_dotenv()
# Access Google API credentials from .env
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' # For development only
CLIENT_ID = os.getenv('GOOGLE_CLIENT_ID') # Google client ID
CLIENT_SECRET = os.getenv('GOOGLE_CLIENT_SECRET') # Google client secret
REDIRECT_URI = os.getenv('GOOGLE_REDIRECT_URI') # Google redirect URI
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1" # For development only
CLIENT_ID = os.getenv("GOOGLE_CLIENT_ID") # Google client ID
CLIENT_SECRET = os.getenv("GOOGLE_CLIENT_SECRET") # Google client secret
REDIRECT_URI = os.getenv("GOOGLE_REDIRECT_URI") # Google redirect URI
SCOPES = ["https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/calendar.events",
"https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/userinfo.profile"]

# Initialize OAuth flow
flow = Flow.from_client_config(
Expand All @@ -43,15 +60,20 @@
}
},
scopes=SCOPES,
redirect_uri=REDIRECT_URI # Explicitly set the redirect URI
redirect_uri=REDIRECT_URI, # Explicitly set the redirect URI
)


# Define the mapping between category and MongoDB collections
category_collections = {
'academic': ['drop-in-tutoring', 'office-hours', 'peer-tutoring', 'supplemental-instruction'],
'career': ['handshake'],
'club': ['tartanconnect']
"academic": [
"drop-in-tutoring",
"office-hours",
"peer-tutoring",
"supplemental-instruction",
],
"career": ["handshake"],
"club": ["tartanconnect"],
}

"""
Expand All @@ -66,90 +88,200 @@ def create_text_indexes():
create_text_indexes()
"""

@app.route('/login')

@app.before_request
def log_request_path():
print(f"Request path: {request.path}")



@app.route("/login")
def login():
origin = request.headers.get('Origin')
origin = request.headers.get("Origin")
if not origin:
return jsonify({"error": "Origin header missing"}), 400
auth_url, _ = flow.authorization_url(prompt='consent', state=origin)
auth_url, _ = flow.authorization_url(prompt="consent", state=origin)
print("Generated authorization URL:", auth_url)

# authorization_url, state = flow.authorization_url(
# access_type='offline',
# include_granted_scopes='true'
# )
# session['state'] = state
# print('auth_url', auth_url)
return jsonify({"auth_url": auth_url})

@app.route('/callback')

@app.route("/callback")
def callback():
# Retrieve the origin from the state parameter
origin = request.args.get('state')
redirect_uri = origin + '/home'
print(redirect_uri)
flow.fetch_token(authorization_response=request.url)
credentials = flow.credentials
session['credentials'] = {
'token': credentials.token,
'refresh_token': credentials.refresh_token,
'token_uri': credentials.token_uri,
'client_id': credentials.client_id,
'client_secret': credentials.client_secret,
'scopes': credentials.scopes,
}
return redirect(redirect_uri) # Redirect to React app

@app.route('/get_user_calendar')
def get_user_calendar():
flow.redirect_uri = 'http://localhost:8000/callback'

# Log the redirect_uri and the full request URL
print("Expected redirect_uri:", flow.redirect_uri)
print("Authorization response URL:", request.url)

try:
# Retrieve the origin from the state parameter
origin = request.args.get("state")
redirect_uri = origin + "/home"
print('redirect_uri in /callback', redirect_uri)
flow.fetch_token(authorization_response=request.url)
credentials = flow.credentials
session["credentials"] = {
"token": credentials.token,
"refresh_token": credentials.refresh_token,
"token_uri": credentials.token_uri,
"client_id": credentials.client_id,
"client_secret": credentials.client_secret,
"scopes": credentials.scopes,
'granted_scopes': credentials.granted_scopes
}
print("Credentials stored in session:", session['credentials'])
# Redirect to frontend after setting session
return redirect('http://localhost:8000/set_session')
# return redirect(redirect_uri) # Redirect to React app
except Exception as e:
print(f"Error during token exchange: {e}")
return jsonify({"error": "Failed to authenticate"}), 500

@app.route('/set_session')
def set_session():
# Redirect to React app after confirming session is set
return redirect('http://localhost:5173/home')

@app.route('/get_user_info')
def get_user_info():
print("Session contents during get_user_info:", dict(session))
if 'credentials' not in session:
return jsonify({"error": "User not authenticated"}), 401

credentials = session['credentials']
service = build('calendar', 'v3', credentials=credentials)
service = build('oauth2', 'v2', credentials=credentials)
user_info = service.userinfo().get().execute()
return jsonify(user_info)

@app.route('/check_credentials', methods=['GET'])
def check_credentials():
print("Session contents:", session) # Debug the session
if 'credentials' in session:
return jsonify({"authenticated": True, "message": "Credentials are stored"})
else:
return jsonify({"authenticated": False, "message": "No credentials found"}), 401


# @app.route("/refresh_token")
# def refresh_token():
# if "credentials" not in session:
# return jsonify({"error": "User not authenticated"}), 401

# credentials = google.oauth2.credentials.Credentials(**session["credentials"])

# if credentials.expired and credentials.refresh_token:
# try:
# credentials.refresh(Request())
# # Update the session with new tokens
# session["credentials"] = {
# "token": credentials.token,
# "refresh_token": credentials.refresh_token,
# "token_uri": credentials.token_uri,
# "client_id": credentials.client_id,
# "client_secret": credentials.client_secret,
# "scopes": credentials.scopes,
# }
# return jsonify({"message": "Token refreshed successfully"})
# except Exception as e:
# print(f"Error refreshing token: {e}")
# return jsonify({"error": "Failed to refresh token"}), 500

# return jsonify({"message": "Token is still valid"})


@app.route("/get_user_calendar")
def get_user_calendar():
print('session in get_user_calendar', session);

if "credentials" not in session:
return jsonify({"error": "User not authenticated"}), 401

# print("Session contents in /get_user_calendar:", session.get('credentials'))

# credentials = session['credentials']

# Reconstruct the credentials object
credentials = Credentials(**session["credentials"])

# Check if the access token is expired and refresh it
if credentials.expired and credentials.refresh_token:
credentials.refresh(Request())
# Update session with refreshed credentials
session["credentials"] = {
"token": credentials.token,
"refresh_token": credentials.refresh_token,
"token_uri": credentials.token_uri,
"client_id": credentials.client_id,
"client_secret": credentials.client_secret,
"scopes": credentials.scopes,
}

service = build("calendar", "v3", credentials=credentials)

try:
events_result = service.events().list(
calendarId='primary',
timeMin='2024-12-01T00:00:00Z', # Replace with your desired time range
maxResults=10,
singleEvents=True,
orderBy='startTime'
).execute()

events = events_result.get('items', [])
events_result = (
service.events()
.list(
calendarId="primary",
timeMin="2024-12-01T00:00:00Z", # Replace with your desired time range
maxResults=10,
singleEvents=True,
orderBy="startTime",
)
.execute()
)

events = events_result.get("items", [])
return jsonify(events)
except Exception as e:
print(e)
return jsonify({"error": "Failed to fetch events"}), 500

@app.route('/events', methods=['POST'])

@app.route("/events", methods=["POST"])
def search_events():
data = request.json

query = {}
if 'category' in data and data['category'] in category_collections:
collections = category_collections[data['category']]
if "category" in data and data["category"] in category_collections:
collections = category_collections[data["category"]]
else:
collections = sum(category_collections.values(), []) # All collections
if 'name' in data:
query['$text'] = {
'$search': data['name'],
'$caseSensitive': False,
'$diacriticSensitive': False

if "name" in data:
query["$text"] = {
"$search": data["name"],
"$caseSensitive": False,
"$diacriticSensitive": False,
}

results = []
for collection in collections:
if 'startDate' in data or 'endDate' in data:
if "startDate" in data or "endDate" in data:
date_query = {}
if 'startDate' in data:
date_query['events.start_time'] = {'$gte': data['startDate']}
if 'endDate' in data:
date_query['events.end_time'] = {'$lte': data['endDate']}
if "startDate" in data:
date_query["events.start_time"] = {"$gte": data["startDate"]}
if "endDate" in data:
date_query["events.end_time"] = {"$lte": data["endDate"]}
query.update(date_query)

print(f"Checking {collection}, query:")
print(query)
found_events = mongo.db[collection].find(query)
for event in found_events:
event['_id'] = str(event['_id'])
event["_id"] = str(event["_id"])
results.append(event)

return jsonify(results)


if __name__ == "__main__":
app.run()
# app.run()
app.run(host="localhost", port=8000) # , debug=True
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading

0 comments on commit 1d7db95

Please sign in to comment.