Skip to content

Commit 3563d79

Browse files
Add oauth fix
1 parent 935ab5d commit 3563d79

1 file changed

Lines changed: 78 additions & 28 deletions

File tree

functions/auth/oauth_callback.py

Lines changed: 78 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from dataclasses import asdict
22
import json
33
import os
4+
import time
5+
import threading
46
import requests
57
from firebase_functions import https_fn
68
from firebase_functions.params import SecretParam, StringParam
@@ -92,6 +94,63 @@
9294
)
9395

9496

97+
# ---------------------------------------------------------------------------
98+
# In-memory spreadsheet cache – avoids hitting Google Sheets on every login.
99+
# Cloud Functions reuse warm instances, so the cache survives across
100+
# invocations on the same instance. A TTL (default 5 min) keeps it fresh.
101+
# ---------------------------------------------------------------------------
102+
103+
SHEET_CACHE_TTL_SECONDS = 300 # 5 minutes
104+
105+
_sheet_cache_lock = threading.Lock()
106+
_sheet_cache: dict | None = (
107+
None # {"ts": float, "reg_headers": [...], "reg_rows": [...], "checkin_headers": [...], "checkin_rows": [...]}
108+
)
109+
110+
111+
def _fetch_sheets_data() -> dict:
112+
"""Fetch both worksheets from Google Sheets and return them as a dict."""
113+
import gspread
114+
115+
creds, _ = google.auth.default(
116+
scopes=[
117+
"https://www.googleapis.com/auth/spreadsheets.readonly",
118+
"https://www.googleapis.com/auth/drive",
119+
]
120+
)
121+
gc = gspread.authorize(creds)
122+
spreadsheet = gc.open_by_url(SPREADSHEET_URL.value)
123+
124+
reg_sheet = spreadsheet.worksheet("Registration Submissions")
125+
checkin_sheet = spreadsheet.worksheet("Check-in")
126+
127+
# Batch-fetch all values in two calls (instead of per-row later)
128+
reg_all = reg_sheet.get_all_values()
129+
checkin_all = checkin_sheet.get_all_values()
130+
131+
return {
132+
"ts": time.monotonic(),
133+
"reg_headers": reg_all[0] if reg_all else [],
134+
"reg_rows": reg_all[1:] if len(reg_all) > 1 else [],
135+
"checkin_headers": checkin_all[0] if checkin_all else [],
136+
"checkin_rows": checkin_all[1:] if len(checkin_all) > 1 else [],
137+
}
138+
139+
140+
def _get_cached_sheets() -> dict:
141+
"""Return cached sheet data, refreshing if stale or missing."""
142+
global _sheet_cache
143+
with _sheet_cache_lock:
144+
now = time.monotonic()
145+
if _sheet_cache is None or (now - _sheet_cache["ts"]) > SHEET_CACHE_TTL_SECONDS:
146+
print("[sheet-cache] cache miss – fetching from Google Sheets")
147+
_sheet_cache = _fetch_sheets_data()
148+
else:
149+
age = round(now - _sheet_cache["ts"], 1)
150+
print(f"[sheet-cache] cache hit (age {age}s)")
151+
return _sheet_cache
152+
153+
95154
def _get_col_index_from_headers(headers: list[str], name: str) -> int:
96155
"""Return 1-based column index for a given header name.
97156
@@ -116,49 +175,40 @@ def _get_registration(uid: str, username: str) -> User:
116175
Organizers will not need to be present on the spreadsheet
117176
"""
118177

119-
import gspread
120-
121178
is_organizer = uid in [
122179
o.strip() for o in ORGANIZERS_LIST.value.split(",") if o.strip()
123180
]
124181

125182
if is_organizer:
126183
return User(id=uid, role="organizer", username=username)
127184
else:
128-
creds, _ = google.auth.default(
129-
scopes=[
130-
"https://www.googleapis.com/auth/spreadsheets.readonly",
131-
"https://www.googleapis.com/auth/drive",
132-
]
133-
)
134-
gc = gspread.authorize(creds)
135-
136-
spreadsheet = gc.open_by_url(
137-
SPREADSHEET_URL.value,
138-
)
139-
140-
print(spreadsheet.worksheets())
141-
142-
reg_sheet = spreadsheet.worksheet("Registration Submissions")
143-
checkin_sheet = spreadsheet.worksheet("Check-in")
144-
145-
reg_headers = reg_sheet.row_values(1)
146-
checkin_headers = checkin_sheet.row_values(1)
185+
sheets = _get_cached_sheets()
186+
reg_headers = sheets["reg_headers"]
187+
checkin_headers = sheets["checkin_headers"]
147188

148189
username_col_idx = _get_col_index_from_headers(
149190
reg_headers, USERNAME_COL_R.value
150191
)
151192

152-
cell = reg_sheet.find(
153-
username, in_column=username_col_idx, case_sensitive=False
154-
)
193+
# Search for the user in cached registration rows
194+
username_lower = username.lower()
195+
matched_row_idx: int | None = None
196+
for i, row in enumerate(sheets["reg_rows"]):
197+
cell_val = _get_cell_from_row(row, username_col_idx)
198+
if cell_val.strip().lower() == username_lower:
199+
matched_row_idx = i
200+
break
155201

156202
# participant not registered
157-
if not cell:
203+
if matched_row_idx is None:
158204
raise ValueError("participant_not_found")
159205

160-
reg_row = reg_sheet.row_values(cell.row)
161-
checkin_row = checkin_sheet.row_values(cell.row)
206+
reg_row = sheets["reg_rows"][matched_row_idx]
207+
checkin_row = (
208+
sheets["checkin_rows"][matched_row_idx]
209+
if matched_row_idx < len(sheets["checkin_rows"])
210+
else []
211+
)
162212

163213
checked_in_idx = _get_col_index_from_headers(
164214
checkin_headers, CHECKED_IN_COL_C.value
@@ -259,7 +309,7 @@ def _generate_login_token(uid: str, username: str) -> str:
259309
user = _get_registration(uid, username)
260310

261311
# If login is disabled for participants, reject them
262-
if user.role != "organizer" and os.getenv("FUNCTIONS_EMULATOR") == "false":
312+
if user.role != "organizer" and os.getenv("FUNCTIONS_EMULATOR") != "true":
263313
db = firestore.client()
264314
event_doc = db.document("event/main").get()
265315
if event_doc.exists:

0 commit comments

Comments
 (0)