Fix stale DB connections, add Why Bourbonacci section, harden auth

- Add pool_pre_ping and pool_recycle to prevent lost connection errors on idle pool
- Add Why Bourbonacci card to about page
- Redirect to login on 401 in API layer
- Check JWT expiry in isLoggedIn instead of just token presence

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-09 21:37:04 -07:00
parent a9de081a8a
commit 866c2e0bed
4 changed files with 26 additions and 2 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ from sqlalchemy.orm import DeclarativeBase
from app.config import settings from app.config import settings
engine = create_async_engine(settings.database_url, echo=False) engine = create_async_engine(settings.database_url, echo=False, pool_pre_ping=True, pool_recycle=1800)
AsyncSessionLocal = async_sessionmaker(engine, expire_on_commit=False) AsyncSessionLocal = async_sessionmaker(engine, expire_on_commit=False)
+13
View File
@@ -21,6 +21,19 @@
<h1 class="page-title">What is an Infinity Bottle?</h1> <h1 class="page-title">What is an Infinity Bottle?</h1>
<div class="card" style="margin-bottom:1.5rem">
<div class="card-title">Why Bourbonacci?</div>
<p style="color:var(--cream);line-height:1.8;margin-bottom:1rem">
The name is a blend of two things: <strong style="color:var(--amber-light)">Bourbon</strong> and the <strong style="color:var(--amber-light)">Fibonacci sequence</strong>.
</p>
<p style="color:var(--cream);line-height:1.8;margin-bottom:1rem">
The Fibonacci sequence — 1, 1, 2, 3, 5, 8, 13… — is a series where each number is the sum of the two before it. It goes on forever, building endlessly on everything that came before it. An infinity bottle works the same way: every addition builds on the blend that already exists, and the result is something that compounds in complexity over time with no defined end.
</p>
<p style="color:var(--cream);line-height:1.8">
Just as no two numbers in the sequence are the same, no two infinity bottles are alike — and just like the sequence itself, yours will never truly be finished.
</p>
</div>
<div class="card" style="margin-bottom:1.5rem"> <div class="card" style="margin-bottom:1.5rem">
<div class="card-title">The Concept</div> <div class="card-title">The Concept</div>
<p style="color:var(--cream);line-height:1.8;margin-bottom:1rem"> <p style="color:var(--cream);line-height:1.8;margin-bottom:1rem">
+7
View File
@@ -26,6 +26,13 @@ const API = (() => {
const data = await res.json().catch(() => null); const data = await res.json().catch(() => null);
if (res.status === 401) {
localStorage.removeItem('bb_token');
localStorage.removeItem('bb_user');
window.location.href = '/login.html';
return;
}
if (!res.ok) { if (!res.ok) {
const msg = data?.detail || `HTTP ${res.status}`; const msg = data?.detail || `HTTP ${res.status}`;
throw new Error(Array.isArray(msg) ? msg.map(e => e.msg).join(', ') : msg); throw new Error(Array.isArray(msg) ? msg.map(e => e.msg).join(', ') : msg);
+5 -1
View File
@@ -26,7 +26,11 @@ const Auth = (() => {
window.location.href = '/login.html'; window.location.href = '/login.html';
} }
function isLoggedIn() { return !!getToken(); } function isLoggedIn() {
const payload = _decodePayload();
if (!payload) return false;
return payload.exp * 1000 > Date.now();
}
function requireAuth() { function requireAuth() {
if (!isLoggedIn()) { if (!isLoggedIn()) {