Address QA review: model registry, NOT NULL constraint, variable naming, toggle defaults, lockout UX
- C3: Register User, UserSession, NtfySent, TOTPUsage, BackupCode in models/__init__.py - C4: Enforce settings.user_id NOT NULL after backfill in migration 023, update model - W4: Rename misleading current_user → current_settings in dashboard.py - W5: Match NtfySettingsSection initial state defaults to backend (true/1/2) - W8: Clear lockout banner on username/password input change in LockScreen Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f136a0820d
commit
15c99152d3
@ -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')
|
op.drop_column('settings', 'pin_hash')
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,11 @@ from app.models.project_task import ProjectTask
|
|||||||
from app.models.person import Person
|
from app.models.person import Person
|
||||||
from app.models.location import Location
|
from app.models.location import Location
|
||||||
from app.models.task_comment import TaskComment
|
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__ = [
|
__all__ = [
|
||||||
"Settings",
|
"Settings",
|
||||||
@ -20,4 +25,9 @@ __all__ = [
|
|||||||
"Person",
|
"Person",
|
||||||
"Location",
|
"Location",
|
||||||
"TaskComment",
|
"TaskComment",
|
||||||
|
"User",
|
||||||
|
"UserSession",
|
||||||
|
"NtfySent",
|
||||||
|
"TOTPUsage",
|
||||||
|
"BackupCode",
|
||||||
]
|
]
|
||||||
|
|||||||
@ -10,10 +10,10 @@ class Settings(Base):
|
|||||||
|
|
||||||
id: Mapped[int] = mapped_column(primary_key=True, index=True)
|
id: Mapped[int] = mapped_column(primary_key=True, index=True)
|
||||||
|
|
||||||
# FK to users table — nullable during migration, will be NOT NULL after data migration
|
# FK to users table — NOT NULL enforced by migration 023 after data backfill
|
||||||
user_id: Mapped[Optional[int]] = mapped_column(
|
user_id: Mapped[int] = mapped_column(
|
||||||
ForeignKey("users.id", ondelete="CASCADE"),
|
ForeignKey("users.id", ondelete="CASCADE"),
|
||||||
nullable=True,
|
nullable=False,
|
||||||
index=True,
|
index=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -26,11 +26,11 @@ _not_parent_template = or_(
|
|||||||
async def get_dashboard(
|
async def get_dashboard(
|
||||||
client_date: Optional[date] = Query(None),
|
client_date: Optional[date] = Query(None),
|
||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
current_user: Settings = Depends(get_current_settings)
|
current_settings: Settings = Depends(get_current_settings)
|
||||||
):
|
):
|
||||||
"""Get aggregated dashboard data."""
|
"""Get aggregated dashboard data."""
|
||||||
today = client_date or date.today()
|
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's events (exclude parent templates — they are hidden, children are shown)
|
||||||
today_start = datetime.combine(today, datetime.min.time())
|
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),
|
days: int = Query(default=7, ge=1, le=90),
|
||||||
client_date: Optional[date] = Query(None),
|
client_date: Optional[date] = Query(None),
|
||||||
db: AsyncSession = Depends(get_db),
|
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."""
|
"""Get unified list of upcoming items (todos, events, reminders) sorted by date."""
|
||||||
today = client_date or date.today()
|
today = client_date or date.today()
|
||||||
|
|||||||
@ -218,7 +218,7 @@ export default function LockScreen() {
|
|||||||
id="username"
|
id="username"
|
||||||
type="text"
|
type="text"
|
||||||
value={username}
|
value={username}
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onChange={(e) => { setUsername(e.target.value); setLockoutMessage(null); }}
|
||||||
placeholder="Enter username"
|
placeholder="Enter username"
|
||||||
required
|
required
|
||||||
autoFocus
|
autoFocus
|
||||||
@ -232,7 +232,7 @@ export default function LockScreen() {
|
|||||||
id="password"
|
id="password"
|
||||||
type="password"
|
type="password"
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => { setPassword(e.target.value); setLockoutMessage(null); }}
|
||||||
placeholder={isSetup ? 'Create a password' : 'Enter password'}
|
placeholder={isSetup ? 'Create a password' : 'Enter password'}
|
||||||
required
|
required
|
||||||
autoComplete={isSetup ? 'new-password' : 'current-password'}
|
autoComplete={isSetup ? 'new-password' : 'current-password'}
|
||||||
|
|||||||
@ -36,13 +36,13 @@ export default function NtfySettingsSection({ settings, updateSettings }: NtfySe
|
|||||||
const [showToken, setShowToken] = useState(false);
|
const [showToken, setShowToken] = useState(false);
|
||||||
|
|
||||||
// Per-type toggles
|
// Per-type toggles
|
||||||
const [eventsEnabled, setEventsEnabled] = useState(false);
|
const [eventsEnabled, setEventsEnabled] = useState(true);
|
||||||
const [eventLeadMinutes, setEventLeadMinutes] = useState(15);
|
const [eventLeadMinutes, setEventLeadMinutes] = useState(15);
|
||||||
const [remindersEnabled, setRemindersEnabled] = useState(false);
|
const [remindersEnabled, setRemindersEnabled] = useState(true);
|
||||||
const [todosEnabled, setTodosEnabled] = useState(false);
|
const [todosEnabled, setTodosEnabled] = useState(true);
|
||||||
const [todoLeadDays, setTodoLeadDays] = useState(0);
|
const [todoLeadDays, setTodoLeadDays] = useState(1);
|
||||||
const [projectsEnabled, setProjectsEnabled] = useState(false);
|
const [projectsEnabled, setProjectsEnabled] = useState(true);
|
||||||
const [projectLeadDays, setProjectLeadDays] = useState(0);
|
const [projectLeadDays, setProjectLeadDays] = useState(2);
|
||||||
|
|
||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
const [isTestingNtfy, setIsTestingNtfy] = useState(false);
|
const [isTestingNtfy, setIsTestingNtfy] = useState(false);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user