diff --git a/.build.hash b/.build.hash index 04756d1..18cdd52 100644 --- a/.build.hash +++ b/.build.hash @@ -1 +1 @@ -2aa2de355546f6401f80a07e00066bd83f37fdc6-803b2c2ecad09a0fbca070296808a53489de891a-e9eccd451b778829eb2f2c9752c670b707e1268b +e43b26fea335b87cb7d2747d85540177b96c7847-803b2c2ecad09a0fbca070296808a53489de891a-e9eccd451b778829eb2f2c9752c670b707e1268b diff --git a/deploy_all.sh b/deploy_all.sh index 40422cc..5a73fe3 100755 --- a/deploy_all.sh +++ b/deploy_all.sh @@ -39,6 +39,7 @@ export FROM_SECRETS_MANAGER=true # React needs the prefixed var at BUILD time export REACT_APP_GOOGLE_MAPS_API_KEY="$GOOGLE_MAPS_API_KEY" +export REACT_APP_ENV_NAME="$ENV_NAME" # ───────────────────────── node + npm ci cache ───────────────────────── diff --git a/rotate_dev_secrets.sh b/rotate_dev_secrets.sh new file mode 100755 index 0000000..126e258 --- /dev/null +++ b/rotate_dev_secrets.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +set -euo pipefail + +PROJECT=aptivaai-dev +ENV=dev + +add_ver() { + local name="$1" ; shift + gcloud secrets versions add "${name}_${ENV}" --data-file=- --project="$PROJECT" >/dev/null + echo "✅ ${name}_${ENV} rotated" +} + +echo "🔐 Rotating DEV secrets in ${PROJECT}" + +# ── Generate fresh randoms +openssl rand -hex 32 | add_ver JWT_SECRET + +# ── Paste new third-party keys (press Enter to skip any you don't want to rotate) +read -s -p "OPENAI_API_KEY_${ENV}: " OPENAI && echo +[[ -n "${OPENAI}" ]] && printf "%s" "$OPENAI" | add_ver OPENAI_API_KEY + +read -p "ONET_USERNAME_${ENV}: " ONETU && echo +[[ -n "${ONETU}" ]] && printf "%s" "$ONETU" | add_ver ONET_USERNAME + +read -s -p "ONET_PASSWORD_${ENV}: " ONETP && echo +[[ -n "${ONETP}" ]] && printf "%s" "$ONETP" | add_ver ONET_PASSWORD + +read -s -p "STRIPE_SECRET_KEY_${ENV}: " SSK && echo +[[ -n "${SSK}" ]] && printf "%s" "$SSK" | add_ver STRIPE_SECRET_KEY + +read -p "STRIPE_PUBLISHABLE_KEY_${ENV}: " SPK && echo +[[ -n "${SPK}" ]] && printf "%s" "$SPK" | add_ver STRIPE_PUBLISHABLE_KEY + +read -s -p "STRIPE_WH_SECRET_${ENV}: " SWH && echo +[[ -n "${SWH}" ]] && printf "%s" "$SWH" | add_ver STRIPE_WH_SECRET + +read -s -p "SUPPORT_SENDGRID_API_KEY_${ENV}: " SG && echo +[[ -n "${SG}" ]] && printf "%s" "$SG" | add_ver SUPPORT_SENDGRID_API_KEY + +read -s -p "EMAIL_INDEX_SECRET_${ENV}: " EIDX && echo +[[ -n "${EIDX}" ]] && printf "%s" "$EIDX" | add_ver EMAIL_INDEX_SECRET + +read -p "TWILIO_ACCOUNT_SID_${ENV}: " TSID && echo +[[ -n "${TSID}" ]] && printf "%s" "$TSID" | add_ver TWILIO_ACCOUNT_SID + +read -s -p "TWILIO_AUTH_TOKEN_${ENV}: " TAUT && echo +[[ -n "${TAUT}" ]] && printf "%s" "$TAUT" | add_ver TWILIO_AUTH_TOKEN + +read -p "TWILIO_MESSAGING_SERVICE_SID_${ENV}: " TMSS && echo +[[ -n "${TMSS}" ]] && printf "%s" "$TMSS" | add_ver TWILIO_MESSAGING_SERVICE_SID + +# Optional: rotate Maps if it was in the leaked image +read -s -p "GOOGLE_MAPS_API_KEY_${ENV} (optional): " GMAPS && echo +[[ -n "${GMAPS}" ]] && printf "%s" "$GMAPS" | add_ver GOOGLE_MAPS_API_KEY + +echo "🔁 Rebuilding DEV with fresh secrets…" +ENV=dev ./deploy_all.sh + +echo "🧪 Verifying runtime env inside containers:" +docker compose exec -T server1 sh -lc 'printenv | egrep "JWT_SECRET|OPENAI|ONET|STRIPE_(SECRET|PUBLISH|WH)|SENDGRID|EMAIL_INDEX|TWILIO|TOKEN_MAX_AGE_MS|ACCESS_COOKIE_NAME|COOKIE_(SECURE|SAMESITE)"' +echo "✅ Done." diff --git a/src/App.js b/src/App.js index 19104e3..13779f1 100644 --- a/src/App.js +++ b/src/App.js @@ -69,6 +69,7 @@ function App() { const [retireProps, setRetireProps] = useState(null); const [supportOpen, setSupportOpen] = useState(false); const [userEmail, setUserEmail] = useState(''); + const [loggingOut, setLoggingOut] = useState(false); const AUTH_HOME = '/signin-landing'; @@ -167,9 +168,15 @@ const showPremiumCTA = !premiumPaths.some(p => // ============================== useEffect(() => { let cancelled = false; +if (loggingOut) return; - // Don’t do auth probe on reset-password - if (location.pathname.startsWith('/reset-password')) { + // Skip auth probe on all public auth routes + if ( + location.pathname.startsWith('/reset-password') || + location.pathname === '/signin' || + location.pathname === '/signup' || + location.pathname === '/forgot-password' + ) { try { localStorage.removeItem('id'); } catch {} setIsAuthenticated(false); setUser(null); @@ -177,6 +184,7 @@ useEffect(() => { return; } + (async () => { setIsLoading(true); try { @@ -208,7 +216,7 @@ useEffect(() => { // include isAuthScreen if you prefer, but this local check avoids a dep loop // eslint-disable-next-line react-hooks/exhaustive-deps -}, [location.pathname, navigate]); +}, [location.pathname, navigate, loggingOut]); /* ===================== Support Modal Email @@ -234,6 +242,7 @@ useEffect(() => { const confirmLogout = async () => { + setLoggingOut(true); // 1) Ask the server to clear the session cookie try { // If you created /logout (no /api prefix): @@ -268,6 +277,7 @@ const confirmLogout = async () => { // 4) Back to sign-in navigate('/signin', { replace: true }); + setLoggingOut(false); }; const cancelLogout = () => { @@ -297,9 +307,13 @@ const cancelLogout = () => { scenario, setScenario, user, setUser}} > - { setDrawerPane('support'); setDrawerOpen(true); }, - openRetire : (props) => { + { + if (!isAuthenticated) return; + setDrawerPane('support'); setDrawerOpen(true); + }, + openRetire : (props) => { + if (!isAuthenticated || !canShowRetireBot) return; if (!canShowRetireBot) { console.warn('Retirement bot disabled on this page'); return; @@ -549,10 +563,11 @@ const cancelLogout = () => { {/* LOGOUT BUTTON */} {/* SHOW WARNING MODAL IF needed */} @@ -699,6 +714,7 @@ const cancelLogout = () => { + {isAuthenticated && ( { uiToolHandlers={uiToolHandlers} canShowRetireBot={canShowRetireBot} /> + )} {/* Session Handler (Optional) */} diff --git a/src/components/InterestInventory.js b/src/components/InterestInventory.js index 272fd9d..764f4f5 100644 --- a/src/components/InterestInventory.js +++ b/src/components/InterestInventory.js @@ -23,6 +23,7 @@ const InterestInventory = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [userProfile, setUserProfile] = useState(null); + const isProd = (process.env.REACT_APP_ENV_NAME || '').toLowerCase() === 'prod'; const navigate = useNavigate(); @@ -290,14 +291,16 @@ const InterestInventory = () => { )} - + {!isProd && ( + + )}