feat: add accounts and address watchlist #3

Merged
rssnyder merged 1 commit from feat/user-accounts-pinned-addresses into main 2026-08-04 21:26:08 -05:00
Owner

Users can register (username + password, bcrypt) and add an address to a
watchlist to be notified when it records a new sale or changes ownership.
Notifications are in-app only, so there is no mail or SMS infrastructure to
run. Sessions are a random token in an httpOnly SameSite=Lax cookie backed
by a sessions table; expired rows are pruned daily.

The auth endpoints are rate limited per client IP, in memory. Login counts
only failed attempts (a success clears the counter) so ordinary use is never
limited; registration counts accounts actually created.

Notifications are generated by the daily ETL rather than polled for by the
web app. The sales feed only ever changes when update_polk_county_db.py
runs, so a background poller could only ever re-derive deltas the loader
already knows. notify_watchlist() compares each watched parcel against the
baseline stored on its row, files any messages, and advances the baseline.
It runs during the PostgreSQL sync, where the accounts live, and no-ops
cleanly when the web app's tables do not exist yet.

Schema is created at startup and works on both SQLite and PostgreSQL.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

Users can register (username + password, bcrypt) and add an address to a watchlist to be notified when it records a new sale or changes ownership. Notifications are in-app only, so there is no mail or SMS infrastructure to run. Sessions are a random token in an httpOnly SameSite=Lax cookie backed by a sessions table; expired rows are pruned daily. The auth endpoints are rate limited per client IP, in memory. Login counts only failed attempts (a success clears the counter) so ordinary use is never limited; registration counts accounts actually created. Notifications are generated by the daily ETL rather than polled for by the web app. The sales feed only ever changes when update_polk_county_db.py runs, so a background poller could only ever re-derive deltas the loader already knows. notify_watchlist() compares each watched parcel against the baseline stored on its row, files any messages, and advances the baseline. It runs during the PostgreSQL sync, where the accounts live, and no-ops cleanly when the web app's tables do not exist yet. Schema is created at startup and works on both SQLite and PostgreSQL. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Users can register (username + password, bcrypt) and add an address to a
watchlist to be notified when it records a new sale or changes ownership.
Notifications are in-app only, so there is no mail or SMS infrastructure to
run. Sessions are a random token in an httpOnly SameSite=Lax cookie backed
by a sessions table; expired rows are pruned daily.

The auth endpoints are rate limited per client IP, in memory. Login counts
only failed attempts (a success clears the counter) so ordinary use is never
limited; registration counts accounts actually created.

Notifications are generated by the daily ETL rather than polled for by the
web app. The sales feed only ever changes when update_polk_county_db.py
runs, so a background poller could only ever re-derive deltas the loader
already knows. notify_watchlist() compares each watched parcel against the
baseline stored on its row, files any messages, and advances the baseline.
It runs during the PostgreSQL sync, where the accounts live, and no-ops
cleanly when the web app's tables do not exist yet.

Schema is created at startup and works on both SQLite and PostgreSQL.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
rssnyder deleted branch feat/user-accounts-pinned-addresses 2026-08-04 21:26:08 -05:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
rssnyder/polk-county-iowa-property-search!3
No description provided.