Add multi-user authentication with JWT

- Users table with email/bcrypt-hashed password; register and login via /auth/ endpoints
- JWT tokens (30-day expiry) stored in localStorage; all API routes require Bearer auth
- All data (varieties, batches, settings, notification logs) scoped to the authenticated user
- Login/register screen overlays the app; sidebar shows user email and logout button
- Scheduler sends daily ntfy summaries for every configured user
- DB schema rewritten for multi-user; SECRET_KEY added to env

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-09 00:08:28 -07:00
parent 1bed02ebb5
commit 4db9988406
17 changed files with 470 additions and 115 deletions

View File

@@ -8,6 +8,53 @@
<link rel="stylesheet" href="/css/style.css" />
</head>
<body>
<!-- AUTH SCREEN -->
<div id="auth-screen" class="auth-overlay">
<div class="auth-card">
<div class="auth-brand">
<span class="brand-icon">&#127807;</span>
<span class="brand-name">Sproutly</span>
</div>
<div class="auth-tabs">
<button id="tab-login" class="auth-tab active" onclick="Auth.showTab('login')">Log In</button>
<button id="tab-register" class="auth-tab" onclick="Auth.showTab('register')">Create Account</button>
</div>
<div id="auth-login-panel">
<div class="form-group">
<label class="form-label">Email</label>
<input type="email" id="auth-email" class="form-input" placeholder="you@example.com"
onkeydown="if(event.key==='Enter') Auth.submit()" />
</div>
<div class="form-group">
<label class="form-label">Password</label>
<input type="password" id="auth-password" class="form-input" placeholder="Password"
onkeydown="if(event.key==='Enter') Auth.submit()" />
</div>
<div id="auth-error" class="auth-msg error hidden"></div>
<button class="btn btn-primary btn-full" onclick="Auth.submit()">Log In</button>
</div>
<div id="auth-register-panel" class="hidden">
<div class="form-group">
<label class="form-label">Email</label>
<input type="email" id="reg-email" class="form-input" placeholder="you@example.com"
onkeydown="if(event.key==='Enter') Auth.submitRegister()" />
</div>
<div class="form-group">
<label class="form-label">Password</label>
<input type="password" id="reg-password" class="form-input" placeholder="At least 8 characters"
onkeydown="if(event.key==='Enter') Auth.submitRegister()" />
</div>
<div id="reg-error" class="auth-msg error hidden"></div>
<button class="btn btn-primary btn-full" onclick="Auth.submitRegister()">Create Account</button>
</div>
</div>
</div>
<!-- APP SHELL -->
<div id="app-shell" class="hidden">
<aside class="sidebar">
<div class="sidebar-brand">
<span class="brand-icon">&#127807;</span>
@@ -28,7 +75,9 @@
</a>
</nav>
<div class="sidebar-footer">
<span id="sidebar-user" class="sidebar-user"></span>
<span id="sidebar-date"></span>
<button class="btn-logout" onclick="Auth.logout()">Log out</button>
</div>
</aside>
@@ -243,6 +292,8 @@
<div id="toast" class="toast hidden"></div>
</div><!-- /app-shell -->
<script src="/js/app.js"></script>
</body>
</html>