Add multi-user auth, admin panel, and timezone support; rename to Yolkbook
- Rename app from Eggtracker to Yolkbook throughout - Add JWT-based authentication (python-jose, passlib/bcrypt) - Add users table; all data tables gain user_id FK for full data isolation - Super admin credentials sourced from ADMIN_USERNAME/ADMIN_PASSWORD env vars, synced on every startup; orphaned rows auto-assigned to admin post-migration - Login page with self-registration; JWT stored in localStorage (30-day expiry) - Admin panel (/admin): list users, reset passwords, disable/enable, delete, and impersonate (Login As) with Return to Admin banner - Settings modal (gear icon in nav): timezone selector and change password - Timezone stored per-user; stats date windows computed in user's timezone; date input setToday() respects user timezone via Intl API - migrate_v2.sql for existing single-user installs - Auto-migration adds timezone column to users on startup - Updated README with full setup, auth, admin, and migration docs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,10 +2,21 @@
|
||||
|
||||
const API = {
|
||||
async _fetch(url, options = {}) {
|
||||
const token = localStorage.getItem('token');
|
||||
const headers = { 'Content-Type': 'application/json' };
|
||||
if (token) headers['Authorization'] = `Bearer ${token}`;
|
||||
|
||||
const res = await fetch(url, {
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
headers,
|
||||
...options,
|
||||
});
|
||||
|
||||
if (res.status === 401) {
|
||||
localStorage.removeItem('token');
|
||||
window.location.href = '/login';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => ({ detail: res.statusText }));
|
||||
throw new Error(err.detail || `Request failed (${res.status})`);
|
||||
@@ -27,13 +38,13 @@ function showMessage(el, text, type = 'success') {
|
||||
setTimeout(() => { el.className = 'message'; }, 4000);
|
||||
}
|
||||
|
||||
// Set an input[type=date] to today's date (using local time, not UTC)
|
||||
// Set an input[type=date] to today's date in the user's configured timezone
|
||||
function setToday(inputEl) {
|
||||
const now = new Date();
|
||||
const y = now.getFullYear();
|
||||
const m = String(now.getMonth() + 1).padStart(2, '0');
|
||||
const d = String(now.getDate()).padStart(2, '0');
|
||||
inputEl.value = `${y}-${m}-${d}`;
|
||||
const tz = (typeof Auth !== 'undefined' && Auth.getUser()?.timezone)
|
||||
|| Intl.DateTimeFormat().resolvedOptions().timeZone
|
||||
|| 'UTC';
|
||||
// en-CA locale produces YYYY-MM-DD which is what date inputs expect
|
||||
inputEl.value = new Date().toLocaleDateString('en-CA', { timeZone: tz });
|
||||
}
|
||||
|
||||
// Format YYYY-MM-DD → MM/DD/YYYY for display
|
||||
|
||||
Reference in New Issue
Block a user