diff --git a/backend/alembic/versions/023_auth_migration_users_sessions.py b/backend/alembic/versions/023_auth_migration_users_sessions.py index 5b7bb7a..5d3a217 100644 --- a/backend/alembic/versions/023_auth_migration_users_sessions.py +++ b/backend/alembic/versions/023_auth_migration_users_sessions.py @@ -119,7 +119,12 @@ def upgrade() -> None: ) # ------------------------------------------------------------------ - # 6. Drop pin_hash from settings — data now lives in users.password_hash + # 6. Enforce NOT NULL on user_id now that backfill is complete + # ------------------------------------------------------------------ + op.alter_column('settings', 'user_id', nullable=False) + + # ------------------------------------------------------------------ + # 7. Drop pin_hash from settings — data now lives in users.password_hash # ------------------------------------------------------------------ op.drop_column('settings', 'pin_hash') diff --git a/backend/app/models/__init__.py b/backend/app/models/__init__.py index 4c5854c..cca74b0 100644 --- a/backend/app/models/__init__.py +++ b/backend/app/models/__init__.py @@ -8,6 +8,11 @@ from app.models.project_task import ProjectTask from app.models.person import Person from app.models.location import Location from app.models.task_comment import TaskComment +from app.models.user import User +from app.models.session import UserSession +from app.models.ntfy_sent import NtfySent +from app.models.totp_usage import TOTPUsage +from app.models.backup_code import BackupCode __all__ = [ "Settings", @@ -20,4 +25,9 @@ __all__ = [ "Person", "Location", "TaskComment", + "User", + "UserSession", + "NtfySent", + "TOTPUsage", + "BackupCode", ] diff --git a/backend/app/models/settings.py b/backend/app/models/settings.py index 57d95fd..268c45f 100644 --- a/backend/app/models/settings.py +++ b/backend/app/models/settings.py @@ -10,10 +10,10 @@ class Settings(Base): id: Mapped[int] = mapped_column(primary_key=True, index=True) - # FK to users table — nullable during migration, will be NOT NULL after data migration - user_id: Mapped[Optional[int]] = mapped_column( + # FK to users table — NOT NULL enforced by migration 023 after data backfill + user_id: Mapped[int] = mapped_column( ForeignKey("users.id", ondelete="CASCADE"), - nullable=True, + nullable=False, index=True, ) diff --git a/backend/app/routers/dashboard.py b/backend/app/routers/dashboard.py index c6b874c..cd0b8b0 100644 --- a/backend/app/routers/dashboard.py +++ b/backend/app/routers/dashboard.py @@ -26,11 +26,11 @@ _not_parent_template = or_( async def get_dashboard( client_date: Optional[date] = Query(None), db: AsyncSession = Depends(get_db), - current_user: Settings = Depends(get_current_settings) + current_settings: Settings = Depends(get_current_settings) ): """Get aggregated dashboard data.""" today = client_date or date.today() - upcoming_cutoff = today + timedelta(days=current_user.upcoming_days) + upcoming_cutoff = today + timedelta(days=current_settings.upcoming_days) # Today's events (exclude parent templates — they are hidden, children are shown) today_start = datetime.combine(today, datetime.min.time()) @@ -143,7 +143,7 @@ async def get_upcoming( days: int = Query(default=7, ge=1, le=90), client_date: Optional[date] = Query(None), db: AsyncSession = Depends(get_db), - current_user: Settings = Depends(get_current_settings) + current_settings: Settings = Depends(get_current_settings) ): """Get unified list of upcoming items (todos, events, reminders) sorted by date.""" today = client_date or date.today() diff --git a/frontend/src/components/auth/LockScreen.tsx b/frontend/src/components/auth/LockScreen.tsx index 65f347d..57895bd 100644 --- a/frontend/src/components/auth/LockScreen.tsx +++ b/frontend/src/components/auth/LockScreen.tsx @@ -218,7 +218,7 @@ export default function LockScreen() { id="username" type="text" value={username} - onChange={(e) => setUsername(e.target.value)} + onChange={(e) => { setUsername(e.target.value); setLockoutMessage(null); }} placeholder="Enter username" required autoFocus @@ -232,7 +232,7 @@ export default function LockScreen() { id="password" type="password" value={password} - onChange={(e) => setPassword(e.target.value)} + onChange={(e) => { setPassword(e.target.value); setLockoutMessage(null); }} placeholder={isSetup ? 'Create a password' : 'Enter password'} required autoComplete={isSetup ? 'new-password' : 'current-password'} diff --git a/frontend/src/components/settings/NtfySettingsSection.tsx b/frontend/src/components/settings/NtfySettingsSection.tsx index f4053ce..2d99d5e 100644 --- a/frontend/src/components/settings/NtfySettingsSection.tsx +++ b/frontend/src/components/settings/NtfySettingsSection.tsx @@ -36,13 +36,13 @@ export default function NtfySettingsSection({ settings, updateSettings }: NtfySe const [showToken, setShowToken] = useState(false); // Per-type toggles - const [eventsEnabled, setEventsEnabled] = useState(false); + const [eventsEnabled, setEventsEnabled] = useState(true); const [eventLeadMinutes, setEventLeadMinutes] = useState(15); - const [remindersEnabled, setRemindersEnabled] = useState(false); - const [todosEnabled, setTodosEnabled] = useState(false); - const [todoLeadDays, setTodoLeadDays] = useState(0); - const [projectsEnabled, setProjectsEnabled] = useState(false); - const [projectLeadDays, setProjectLeadDays] = useState(0); + const [remindersEnabled, setRemindersEnabled] = useState(true); + const [todosEnabled, setTodosEnabled] = useState(true); + const [todoLeadDays, setTodoLeadDays] = useState(1); + const [projectsEnabled, setProjectsEnabled] = useState(true); + const [projectLeadDays, setProjectLeadDays] = useState(2); const [isSaving, setIsSaving] = useState(false); const [isTestingNtfy, setIsTestingNtfy] = useState(false);