Skip to content

Commit 65edd65

Browse files
committed
add env variable for frontend url to remove hardcoded references to it in the backend, update welcome page so that it the learn more link actually takes us somehwere based on the user's language
1 parent 0c57f7c commit 65edd65

File tree

11 files changed

+121
-48
lines changed

11 files changed

+121
-48
lines changed

.env.sample

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1-
POSTGRES_DATABASE=
2-
POSTGRES_USER=
3-
POSTGRES_PASSWORD=
4-
POSTGRES_DATABASE_URL=
5-
AWS_ACCESS_KEY_ID=
6-
AWS_SECRET_ACCESS_KEY=
7-
AWS_REGION=
8-
SES_EMAIL_FROM=
1+
POSTGRES_DATABASE=llsc
2+
POSTGRES_USER=postgres
3+
POSTGRES_PASSWORD=postgres
4+
POSTGRES_DATABASE_URL=postgresql+psycopg2://postgres:postgres@db:5432/llsc
5+
POSTGRES_TEST_DATABASE_URL=postgresql+psycopg2://postgres:postgres@db:5432/llsc_test
6+
7+
FIREBASE_WEB_API_KEY=
8+
9+
AWS_ACCESS_KEY=
10+
AWS_SECRET_KEY=
11+
AWS_REGION=ca-central-1
12+
SES_SOURCE_EMAIL=
13+
SES_SOURCE_EMAIL_FR=
14+
15+
FRONTEND_URL=http://localhost:3000

backend/.env.sample

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
POSTGRES_DATABASE=llsc
2+
POSTGRES_USER=postgres
3+
POSTGRES_PASSWORD=postgres
4+
POSTGRES_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/llsc
5+
POSTGRES_TEST_DATABASE_URL=postgresql+psycopg2://postgres:postgres@localhost:5432/llsc_test
6+
7+
FIREBASE_WEB_API_KEY=
8+
9+
AWS_ACCESS_KEY=
10+
AWS_SECRET_KEY=
11+
AWS_REGION=ca-central-1
12+
SES_SOURCE_EMAIL=
13+
SES_SOURCE_EMAIL_FR=
14+
15+
FRONTEND_URL=http://localhost:3000

backend/app/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def create_app():
3333
app.add_middleware(
3434
CORSMiddleware,
3535
allow_origins=[
36-
"http://localhost:3000",
36+
os.getenv("FRONTEND_URL", "http://localhost:3000"),
3737
"https://uw-blueprint-starter-code.firebaseapp.com",
3838
"https://uw-blueprint-starter-code.web.app",
3939
# TODO: create a separate middleware function to dynamically

backend/app/server.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import os
23
from contextlib import asynccontextmanager
34
from typing import Union
45

@@ -87,7 +88,7 @@ async def lifespan(_: FastAPI):
8788

8889
# Shutdown scheduler gracefully
8990
log.info("Shutting down scheduler...")
90-
scheduler.shutdown()
91+
scheduler.shutdown(wait=False) # Don't wait for running jobs to prevent interpreter shutdown race condition
9192

9293
# Dispose database engine to close all connection pools
9394
# This prevents async generator cleanup errors during shutdown
@@ -104,8 +105,7 @@ async def lifespan(_: FastAPI):
104105
app.add_middleware(
105106
CORSMiddleware,
106107
allow_origins=[
107-
"http://localhost:3000",
108-
"http://localhost:3002",
108+
os.getenv("FRONTEND_URL", "http://localhost:3000"),
109109
"https://uw-blueprint-starter-code.firebaseapp.com",
110110
"https://uw-blueprint-starter-code.web.app",
111111
# TODO: create a separate middleware function to dynamically

backend/app/services/implementations/auth_service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def reset_password(self, email: str) -> None:
7979

8080
# Use Firebase Admin SDK to generate password reset link
8181
action_code_settings = firebase_admin.auth.ActionCodeSettings(
82-
url="http://localhost:3000/set-new-password",
82+
url=f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/set-new-password",
8383
handle_code_in_app=True,
8484
)
8585

@@ -152,7 +152,7 @@ def send_email_verification_link(self, email: str, language: str = None) -> None
152152

153153
# Use Firebase Admin SDK to generate email verification link
154154
action_code_settings = firebase_admin.auth.ActionCodeSettings(
155-
url="http://localhost:3000/action", # URL to redirect after verification
155+
url=f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/action", # URL to redirect after verification
156156
handle_code_in_app=True,
157157
)
158158

backend/app/services/implementations/match_service.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import os
23
from datetime import date, datetime, timedelta, timezone
34
from typing import List, Optional
45
from uuid import UUID
@@ -106,7 +107,7 @@ async def create_matches(self, req: MatchCreateRequest) -> MatchCreateResponse:
106107
language = volunteer.language.value if volunteer.language else "en"
107108

108109
first_name = volunteer.first_name if volunteer.first_name else None
109-
matches_url = "http://localhost:3000/volunteer/dashboard"
110+
matches_url = f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/volunteer/dashboard"
110111

111112
ses_service.send_matches_available_email(
112113
to_email=volunteer.email,
@@ -342,7 +343,7 @@ async def schedule_match(
342343
time=participant_time_str,
343344
timezone=participant_tz_abbr,
344345
first_name=participant.first_name,
345-
scheduled_calls_url="http://localhost:3000/participant/dashboard",
346+
scheduled_calls_url=f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/participant/dashboard",
346347
language=participant_language,
347348
)
348349

@@ -357,7 +358,7 @@ async def schedule_match(
357358
time=volunteer_time_str,
358359
timezone=volunteer_tz_abbr,
359360
first_name=volunteer.first_name,
360-
scheduled_calls_url="http://localhost:3000/volunteer/dashboard",
361+
scheduled_calls_url=f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/volunteer/dashboard",
361362
language=volunteer_language,
362363
)
363364

@@ -446,7 +447,7 @@ async def request_new_times(
446447
to_email=volunteer.email,
447448
participant_name=participant_name,
448449
first_name=volunteer.first_name,
449-
matches_url="http://localhost:3000/volunteer/dashboard",
450+
matches_url=f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/volunteer/dashboard",
450451
language=volunteer_language,
451452
)
452453
except Exception as e:
@@ -532,7 +533,7 @@ async def cancel_match_by_participant(
532533
time=volunteer_time_str,
533534
timezone=volunteer_tz_abbr,
534535
first_name=volunteer.first_name,
535-
dashboard_url="http://localhost:3000/volunteer/dashboard",
536+
dashboard_url=f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/volunteer/dashboard",
536537
language=volunteer_language,
537538
)
538539
except Exception as e:
@@ -623,7 +624,7 @@ async def cancel_match_by_volunteer(
623624
time=participant_time_str,
624625
timezone=participant_tz_abbr,
625626
first_name=participant.first_name,
626-
request_matches_url="http://localhost:3000/participant/dashboard",
627+
request_matches_url=f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/participant/dashboard",
627628
language=participant_language,
628629
)
629630
except Exception as e:
@@ -829,7 +830,7 @@ async def volunteer_accept_match(
829830
language = participant.language.value if participant.language else "en"
830831

831832
first_name = participant.first_name if participant.first_name else None
832-
matches_url = "http://localhost:3000/participant/dashboard"
833+
matches_url = f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/participant/dashboard"
833834

834835
ses_service = SESEmailService()
835836
ses_service.send_matches_available_email(

backend/app/utilities/ses_email_service.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ def send_matches_available_email(
217217

218218
# Default to dashboard if no specific URL provided
219219
if not matches_url:
220-
matches_url = "http://localhost:3000/participant/dashboard"
220+
matches_url = f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/participant/dashboard"
221221

222222
template_data = {"first_name": first_name if first_name else "there", "matches_url": matches_url}
223223

@@ -260,7 +260,7 @@ def send_call_scheduled_email(
260260

261261
# Default to dashboard if no specific URL provided
262262
if not scheduled_calls_url:
263-
scheduled_calls_url = "http://localhost:3000/participant/dashboard"
263+
scheduled_calls_url = f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/participant/dashboard"
264264

265265
template_data = {
266266
"first_name": first_name if first_name else "there",
@@ -304,7 +304,7 @@ def send_participant_requested_new_times_email(
304304

305305
# Default to dashboard if no specific URL provided
306306
if not matches_url:
307-
matches_url = "http://localhost:3000/volunteer/dashboard"
307+
matches_url = f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/volunteer/dashboard"
308308

309309
template_data = {
310310
"first_name": first_name if first_name else "there",
@@ -351,7 +351,7 @@ def send_volunteer_accepted_new_times_email(
351351

352352
# Default to dashboard if no specific URL provided
353353
if not scheduled_calls_url:
354-
scheduled_calls_url = "http://localhost:3000/participant/dashboard"
354+
scheduled_calls_url = f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/participant/dashboard"
355355

356356
template_data = {
357357
"first_name": first_name if first_name else "there",
@@ -401,7 +401,7 @@ def send_participant_cancelled_email(
401401

402402
# Default to dashboard if no specific URL provided
403403
if not dashboard_url:
404-
dashboard_url = "http://localhost:3000/volunteer/dashboard"
404+
dashboard_url = f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/volunteer/dashboard"
405405

406406
template_data = {
407407
"first_name": first_name if first_name else "there",
@@ -451,7 +451,7 @@ def send_volunteer_cancelled_email(
451451

452452
# Default to dashboard if no specific URL provided
453453
if not request_matches_url:
454-
request_matches_url = "http://localhost:3000/participant/dashboard"
454+
request_matches_url = f"{os.getenv('FRONTEND_URL', 'http://localhost:3000')}/participant/dashboard"
455455

456456
template_data = {
457457
"first_name": first_name if first_name else "there",

frontend/.env.sample

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
POSTGRES_DATABASE=llsc
2+
POSTGRES_USER=postgres
3+
POSTGRES_PASSWORD=postgres
4+
POSTGRES_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/llsc
5+
6+
NEXT_PUBLIC_FIREBASE_WEB_API_KEY=
7+
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=
8+
NEXT_PUBLIC_FIREBASE_PROJECT_ID=
9+
NEXT_PUBLIC_FIREBASE_APP_ID=
10+
11+
REACT_APP_BACKEND_URL=http://localhost:8080

frontend/src/hooks/useEmailVerification.ts

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useState } from 'react';
22
import baseAPIClient from '@/APIClients/baseAPIClient';
3+
import { detectUserLanguage } from '@/utils/languageDetection';
34

45
export const useEmailVerification = () => {
56
const [isLoading, setIsLoading] = useState(false);
@@ -12,26 +13,8 @@ export const useEmailVerification = () => {
1213
setSuccess(false);
1314

1415
try {
15-
// Get browser language from navigator - check all languages in preference order
16-
let detectedLang = 'en';
17-
18-
// Check navigator.languages array first (user's preferred languages in order)
19-
if (navigator.languages && navigator.languages.length > 0) {
20-
for (const lang of navigator.languages) {
21-
const langCode = lang.split('-')[0].toLowerCase();
22-
if (langCode === 'fr') {
23-
detectedLang = 'fr';
24-
break;
25-
} else if (langCode === 'en') {
26-
detectedLang = 'en';
27-
// Continue checking in case French comes later
28-
}
29-
}
30-
} else if (navigator.language) {
31-
// Fallback to navigator.language
32-
const langCode = navigator.language.split('-')[0].toLowerCase();
33-
detectedLang = langCode === 'fr' ? 'fr' : 'en';
34-
}
16+
// Detect user's preferred language from browser settings
17+
const detectedLang = detectUserLanguage();
3518

3619
await baseAPIClient.post(
3720
`/auth/send-email-verification/${encodeURIComponent(email)}?language=${detectedLang}`,

frontend/src/pages/welcome.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,18 @@ import { AuthenticatedUser, FormStatus, UserRole } from '@/types/authTypes';
77
import { roleIdToUserRole } from '@/utils/roleUtils';
88
import { getRedirectRoute } from '@/constants/formStatusRoutes';
99
import { AuthPageLayout } from '@/components/layout';
10+
import { detectUserLanguage, getProgramInfoUrl } from '@/utils/languageDetection';
1011

1112
export default function WelcomePage() {
1213
const router = useRouter();
1314
const [currentUser, setCurrentUser] = useState<AuthenticatedUser>(null);
1415
const [loading, setLoading] = useState(true);
16+
const [userLanguage, setUserLanguage] = useState<'en' | 'fr'>('en');
17+
18+
// Detect user's preferred language on client-side only
19+
useEffect(() => {
20+
setUserLanguage(detectUserLanguage());
21+
}, []);
1522

1623
useEffect(() => {
1724
const evaluate = async () => {
@@ -107,7 +114,9 @@ export default function WelcomePage() {
107114
<Text color="brand.navy" fontSize="md">
108115
You can learn more about the program{' '}
109116
<a
110-
href="#"
117+
href={getProgramInfoUrl(userLanguage)}
118+
target="_blank"
119+
rel="noopener noreferrer"
111120
style={{ color: 'var(--chakra-colors-brand-primary)', textDecoration: 'underline' }}
112121
>
113122
here

0 commit comments

Comments
 (0)