Changelog

This changelog is generated automatically from GitHub Releases.

v1.1.7

2026-07-06 · GitHub

What’s Changed

  • add frontend tests by @rafizamankhan in https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/pull/225

Full Changelog: https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/compare/v1.1.6…v1.1.7

v1.1.6

2026-07-02 · GitHub

What’s Changed

  • fix: add Postgres-backed rate limiter for multi-worker deployments by @chriscarrollsmith in https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/pull/222

Full Changelog: https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/compare/v1.1.5…v1.1.6

v1.1.5

2026-07-02 · GitHub

What’s Changed

  • fix: enforce avatar upload size limit before buffering body by @chriscarrollsmith in https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/pull/220

Full Changelog: https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/compare/v1.1.4…v1.1.5

v1.1.4

2026-07-02 · GitHub

What’s Changed

  • fix: honor X-Forwarded-For for rate limits behind trusted proxies by @chriscarrollsmith in https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/pull/221

Full Changelog: https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/compare/v1.1.3…v1.1.4

v1.1.3

2026-07-02 · GitHub

What’s Changed

  • fix: add CSRF tokens and convert account recovery to POST by @chriscarrollsmith in https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/pull/223

Full Changelog: https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/compare/v1.1.2…v1.1.3

v1.1.2

2026-07-02 · GitHub

What’s Changed

  • fix: revoke sessions on password reset by @chriscarrollsmith in https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/pull/215

Full Changelog: https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/compare/v1.1.1…v1.1.2

v1.1.1

2026-07-01 · GitHub

What’s Changed

  • Fix broken screenshot link in docs by @chriscarrollsmith in https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/pull/213

Full Changelog: https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/compare/v1.1.0…v1.1.1

v1.1.0

2026-07-01 · GitHub

What’s Changed

  • docs: migrate documentation from raw Quarto to Great Docs by @chriscarrollsmith in https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/pull/211
  • New frontend theme by @chriscarrollsmith in https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/pull/212

Full Changelog: https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/compare/v1.0.2…v1.1.0

v1.0.2

2026-06-30 · GitHub

What’s Changed

  • Fix ownership cascades during account deletion by @chriscarrollsmith in https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/pull/210

Full Changelog: https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/compare/v1.0.1…v1.0.2

v1.0.1

2026-06-30 · GitHub

What’s Changed

  • Fix mobile layout by @rafizamankhan in https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/pull/207
  • Fix invalid token for logged-in users by @rafizamankhan in https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/pull/208

Full Changelog: https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/compare/v1.0.0…v1.0.1

v1.0.0

2026-06-23 · GitHub

What’s Changed

  • ci: add semantic-release workflow and conventional commit enforcement by @chriscarrollsmith in https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/pull/203

Full Changelog: https://github.com/Promptly-Technologies-LLC/fastapi-jinja2-postgres-webapp/compare/v0.1.30…v1.0.0

v0.1.30

2026-06-23 · GitHub

Summary

  • Add InstrumentedAttribute[Any] casts on all selectinload chains in utils/core/organizations.py per the SQLModel typing rule.
  • Introduce clear_all_rate_limiters() and a session-wide autouse fixture in tests/conftest.py that resets limiters before and after every test.
  • Remove duplicate module-local rate limiter reset fixtures from tests/test_htmx.py and tests/routers/core/test_account.py.

Closes #201

Test plan

Made with Cursor

v0.1.29

2026-06-23 · GitHub

Summary

Resolves #196 by upgrading the full dependency lockfile to latest resolved versions, addressing Dependabot security alerts across runtime, dev, and transitive packages.

Key runtime upgrades: - fastapi 0.136.1 → 0.138.0 (starlette 1.0.0 → 1.3.1) - pyjwt 2.12.1 → 2.13.0 - python-multipart 0.0.29 → 0.0.32

Key dev/tooling upgrades: - pytest 8.4.2 → 9.1.1 (widened constraint from <9.0.0 to >=8.3.3) - pytest-jinja-check 1.0.2 → 1.1.0 (Starlette 1.x _IncludedRouter route discovery) - jupyter-server 2.18.2 → 2.20.0 - tornado 6.5.5 → 6.5.7 - bleach 6.3.0 → 6.4.0

Test plan

Made with Cursor

v0.1.28

2026-06-23 · GitHub

Summary

Two small, related cleanups to the communication-preferences feature added in #195:

  1. Restore the confirm-password autocomplete regression test. #195 accidentally deleted the def line for test_register_page_confirm_password_has_autocomplete, collapsing its body into test_register_page_shows_password_requirements and demoting its docstring to a no-op string statement. The issue #156 autocomplete check is restored as its own test. The comm-preference assertions remain in test_register_page_shows_password_requirements, where they correctly verify the register page renders the new fields.

  2. Drop the syncSubPreferences helper. In communication_preferences_fields.html the helper was only ever called from the master checkbox’s change handler (disable branch) and never on init — initial visibility is already set server-side via the Jinja style="display: none;" guard. Inlining it removes the indirection and the enable/disable asymmetry. Behavior is preserved exactly: enabling reveals the sub-preferences and checks comm_updates; disabling hides them and clears both sub-preferences.

Test plan

v0.1.27

2026-06-23 · GitHub

Invalidates old invitation when prompted to send one to the same member within or after expiry without requiring cron jobs supporting cleanup.

Resolves #192

v0.1.26

2026-06-23 · GitHub

Added a cancel button to delete pending invitations.

Resolves #190

v0.1.25

2026-06-07 · GitHub

Wires the login “Remember Me” checkbox into auth cookie lifetime: unchecked logins get session cookies with a 12-hour refresh token, while checked logins get persistent cookies aligned to the existing 30-day refresh TTL. Cookie handling is centralized in set_auth_cookies() / clear_auth_cookies(), and the persistent flag is preserved through token rotation on /refresh and silent refresh via NeedsNewTokens. All auth paths keep httponly, secure, and samesite settings unchanged.

Resolves #187

v0.1.24

2026-06-04 · GitHub

Summary

  • Adds a modal-only conflict resolver to the propagate workflow for pyproject.toml and uv.lock conflicts.
  • Preserves the Modal branch dependency overlay, regenerates uv.lock, and pushes the merge when only those known files conflict.
  • Keeps the fallback PR path for all other conflicts.

Test plan

  • Simulated the current origin/main into origin/modal merge locally.
  • Verified the resolver clears pyproject.toml and uv.lock conflicts.
  • Verified the resolved pyproject.toml keeps modal>=0.73.162 and uv.lock still contains the modal package.

Made with Cursor

v0.1.23

2026-06-04 · GitHub

Summary

  • Disables matrix fail-fast for the propagate workflow so a conflict or fallback failure on one deployment branch does not cancel the other branch’s propagation job.
  • Leaves the existing GitHub App token flow in place; the app now has PR read/write permissions for fallback PR creation.

Test plan

  • Not run; workflow-only change.

Made with Cursor

v0.1.22

2026-06-04 · GitHub

Confirm field re-validates against the current password; error clears when they match.

Resolves #182

v0.1.21

2026-05-20 · GitHub

Create Role Modal used to retain information from input and after role creation. Now the modal information is reset on cancel and after role creation.

Resolves #180

v0.1.20

2026-05-20 · GitHub

Fixed the modalDismiss handler in app.js to call bootstrap.Modal.hide() on any open modals before cleaning up backdrops, so the dialog properly closes after HTMX swaps the roles table.

Resolves #178

v0.1.19

2026-03-18 · GitHub

Summary

  • Toast migration: Replaced the custom showToast() JS function and client-side flash cookie reader with server-side rendering via htmx-ext-remove-me for auto-dismiss, HTMX responseHandling config for error-status OOB swaps, and a flash cookie middleware for full-page-load toasts.
  • Avatar update fix: Replaced HX-Refresh: true on avatar upload with OOB swaps for both the profile display and navbar avatar. This eliminates the visible flicker from full page reloads and ensures the “Profile updated successfully” toast always appears (previously lost during refresh).
  • Navbar avatar partial: Extracted the navbar avatar into a reusable partial (navbar_avatar.html) with an OOB variant (navbar_avatar_oob.html) for HTMX responses.

Test plan

    • test_htmx_success_toast_appears — name-only profile update shows toast
    • test_avatar_update_toast_appears — avatar update shows toast
    • test_avatar_update_no_full_reload — avatar update uses OOB swaps, no page reload
    • test_htmx_error_toast_appears — error responses show toast via OOB
    • test_flash_cookie_toast_appears — flash cookies render server-side on load
    • test_toast_auto_dismisses — toasts auto-dismiss after 5s via remove-me
    • test_toast_close_button — manual close button works

🤖 Generated with Claude Code

v0.1.18

2026-03-18 · GitHub

Summary

Closes #175

  • Clear the “Add email” form field after successful submission using HTMX’s hx-on::after-request event, with an HX-Trigger header from the server to signal success
  • Order email addresses on the profile page with primary first via ORDER BY is_primary DESC, so the newly-promoted email always moves to the top row

Test plan

🤖 Generated with Claude Code

v0.1.17

2026-03-18 · GitHub

Summary

  • Multi-email system: Users can add a secondary email (max 2), verify it via an emailed link, promote it to primary, or remove it. The old single-email update flow (EmailUpdateToken, /request_email_update, /confirm_email_update) has been removed.
  • Account recovery: When a sensitive email change occurs (primary swap or email removal), the affected email receives a notification with a tokenized recovery link. Clicking it restores the victim’s email as primary, purges attacker emails, revokes all sessions, and forces a password reset.
  • Password reset auto-login: After resetting a password, the user is automatically logged in and redirected to the dashboard — no redundant login step.
  • Flash cookie encoding fix: URL-encode the JSON cookie value before setting it, preventing Python’s http.cookies from mangling commas (\054) and breaking client-side JSON.parse().
  • NeedsNewTokens query string preservation: The token-refresh redirect now uses str(request.url) instead of request.url.path, preserving query parameters for GET routes.

QA checklist

Add and verify a secondary email

    • Note: “Add email” field/button should clear/disable after submission

Promote a secondary email to primary

    • Note: Primary email should move to top row rather than just swapping labels

Remove a secondary email

Account recovery (after primary swap)

Account recovery (after email removal)

Recovery edge cases

Standard password reset (regression check)

Flash toast visibility (regression check)

🤖 Generated with Claude Code

v0.1.16

2026-03-14 · GitHub

Summary

  • Add RefreshToken model with JTI tracking in the private schema for server-side refresh token validation
  • Implement token rotation: each refresh token use revokes the old token and issues a new one
  • Add reuse detection: replaying a revoked token revokes ALL tokens for that account (signals theft)
  • Logout now revokes the refresh token server-side, not just deleting the cookie
  • Email update flow revokes all old refresh tokens before issuing new ones
  • Legacy tokens without JTI (pre-migration) are gracefully rejected, forcing re-login
  • Fix misleading “new email address” text in email update form to correctly say “current email address”

Test plan

Closes #81

🤖 Generated with Claude Code

v0.1.15

2026-03-14 · GitHub

  • The dashboard now has a dropdown that lets you select an organization (so you can interact only with the resources for that organization)
  • utils/app/models.py now has an example/illustrative OrganizationResource data model, and this data model is automatically imported and set up with the rest of the DB

v0.1.14

2026-03-14 · GitHub

Summary

  • Adds a GitHub Actions workflow that automatically merges main into modal and hetzner deployment branches on every push
  • If merge conflicts arise, opens a PR instead so they can be resolved manually
  • Uses the existing GitHub App token for authentication

Test plan

Closes #169

🤖 Generated with Claude Code

v0.1.13

2026-03-14 · GitHub

Enforces that:

  • Templates contain no syntax errors
  • Templates use url_for instead of hardcoded routes for all onsite links
  • Routes pass all required context variables when rendering templates
  • url_for calls all point at valid FastAPI routes

v0.1.12

2026-03-14 · GitHub

Summary

  • Add require_unauthenticated_client dependency that wraps get_optional_user and redirects authenticated users to the dashboard via a new AlreadyAuthenticatedError exception handler
  • Add get_verified_account dependency that wraps get_authenticated_account with email/password re-verification for sensitive operations like account deletion
  • Update routes (read_home, read_login, read_register, read_forgot_password, delete_account) to use the new dependencies, removing duplicated check-and-redirect logic from route bodies

Closes #108

Test plan

🤖 Generated with Claude Code

v0.1.11

2026-03-14 · GitHub

Release v0.1.11

v0.1.10

2026-03-14 · GitHub

Summary

  • Enable Open Graph and Twitter Card meta tags via _quarto.yml for rich link previews on social media
  • Add site-url for canonical URLs and automatic sitemap generation
  • Add description frontmatter to all .qmd pages for <meta name="description"> tags
  • Improve alt text on homepage screenshot image

Closes #37

Test plan

🤖 Generated with Claude Code

v0.1.9

2026-03-13 · GitHub

v0.1.8

2026-03-13 · GitHub

Resequenced deployment documentation to clarify that Modal requires separate Postgres deployment, but Hetzner does not.

  • Digital Ocean PostgreSQL deployment is nested under Modal
  • Removed a Supabase-related example since Supabase is not an explicit deployment target (yet)

v0.1.7

2026-03-13 · GitHub

Template changes (templates/base/partials/header.html): 1. Added a mobile-only

v0.1.6

2026-03-13 · GitHub

  1. The Register page now clearly states the requirements for passwords
  2. The error toast, if the user submits an invalid password, restates the requirements
  3. Using a suggested password on Chrome now auto-populates both password fields, not just one.

v0.1.5

2026-03-12 · GitHub

  • Add Hetzner Cloud deployment docs and deployment branch note
  • Add hcloud CLI installation to devcontainer setup
  • Update all dependencies and pin dev postgres to v17
  • Replace mypy with ty type checker and fix ruff lint issues
  • chore: release v0.1.2 [skip ci]
  • Error messaging standardization
  • Type checks and lints
  • Bugfixes for modal cleanup

v0.1.4

2026-03-12 · GitHub

OOB Toast (HTMX error handlers)

These all fire through the exception handlers in main.py.

    1. Go to /account/login
    2. Enter a valid email format but wrong credentials
    3. Submit — toast renders: “Invalid email or password”
    1. Go to /account/login
    2. Leave both fields blank, submit
    3. Toast is rendered with validation messages
    1. Go to /account/register
    2. Fill in name/email, enter a weak password like abc, different confirm password
    3. Submit — toast renders: “The passwords you entered do not match”
    1. Go to /account/login
    2. Submit wrong credentials rapidly ~6 times (depends on your limiter config)
    3. Eventually toast renders: “Too many attempts…” with Retry-After
    1. Log in, go to an org page
    2. Try to create a role with a name that already exists (e.g. “Owner”)
    3. Toast: “A role with that name already exists”
    • Harder to trigger naturally. You can use browser devtools to fire an HTMX request to a non-existent route: htmx.ajax('GET', '/nonexistent', {target: 'body', headers: {'HX-Request': 'true'}})

OOB Toasts (HTMX success — append_toast)

These return the partial content + an appended toast in the same response.

    1. Log in, go to /user/profile
    2. Click Edit, change your name, Save
    3. Toast: “Profile updated successfully.”
    1. Go to an org page where you’re Owner
    2. Create a new role — toast: “Role created successfully.”
    3. Edit that role’s name — toast: “Role updated successfully.”
    4. Delete that role — toast: “Role deleted successfully.”
  • [] User role update

    1. On the org page, edit a member’s roles (check/uncheck roles)
    2. Save — toast: “User role updated successfully.”
  • [] Remove member

    1. On the org page, remove a non-owner member
    2. Toast: “User removed from organization.”
  • [] Send invitation

    1. On the org page, invite a new email address
    2. Toast: “Invitation sent successfully.”
    1. On /user/profile, enter a new email in the Update Email form
    2. Submit — toast: “Confirmation email sent. Check your inbox.”

    Flash Cookie (PRG redirects — full page load)

    These set a flash_message cookie that the JS in base.html reads on the next page load.

    1. Trigger an email update from profile
    2. Click the confirmation link from the email
    3. You’ll be redirected to /user/profile — toast: “Your email address has been successfully updated.”
  • [] Forgot password

    1. Go to /account/forgot_password (or use the Change Password section on profile)
    2. Submit your email
    3. Redirected to ?show_form=false — toast: “If an account exists with this email, a password reset link will be sent.”
    1. On the org page, edit the org name and save
    2. Page redirects via HX-Redirect — toast: “Organization updated successfully.”
    1. On the org page, delete the organization
    2. Redirected to /user/profile — toast: “Organization deleted successfully.”

JS showToast() (client-side validation)

    1. Go to /user/profile, click Edit
    2. Try uploading a file that’s too large — toast: “File size must be less than X MB”
    3. Try uploading a .txt file — toast: “File format must be one of: …”
    4. Try a very small image (e.g. 1x1 px) — toast: “Image dimensions must be at least …”

    Quick checklist

    ┌────────────────┬─────────────────────────────────────────────┬────────────────────────────────┐ │ Mechanism │ Trigger │ Where to look │ ├────────────────┼─────────────────────────────────────────────┼────────────────────────────────┤ │ toast_response │ Any HTMX error (login, register, │ Bottom-right toast │ │ │ validation) │ │ ├────────────────┼─────────────────────────────────────────────┼────────────────────────────────┤ │ append_toast │ Any HTMX success mutation (profile, roles, │ Bottom-right toast alongside │ │ │ members, invitations) │ updated partial │ ├────────────────┼─────────────────────────────────────────────┼────────────────────────────────┤ │ Flash cookie │ PRG redirects (email confirm, forgot │ Bottom-right toast after page │ │ │ password, org update/delete) │ load │ ├────────────────┼─────────────────────────────────────────────┼────────────────────────────────┤ │ showToast() JS │ Avatar file picker validation │ Bottom-right toast, no network │ │ │ │ request │ └────────────────┴─────────────────────────────────────────────┴────────────────────────────────┘

v0.1.3

2026-03-12 · GitHub

Summary

  • Set follow_redirects=False on all TestClient instances in conftest.py instead of passing it per API call
  • Removed ~90 redundant follow_redirects=False arguments from individual test calls
  • Added redirect location assertions (response.headers["location"]) to all tests that check 3xx status codes but previously didn’t verify the redirect target
  • Fixed InsecureKeyLengthWarning by using a test SECRET_KEY longer than 32 bytes

Closes #101

Test plan

🤖 Generated with Claude Code

v0.1.2

2026-03-12 · GitHub

Summary

  • Add Hetzner Cloud deployment documentation to deployment.qmd with instructions for provisioning, DNS setup, configuration, and management
  • Add callout note explaining that deployment artifacts live on per-target branches (modal, hetzner)
  • Add DOMAIN env var to .env.example for Caddy TLS configuration
  • Install hcloud CLI in devcontainer setup

Closes #151

🤖 Generated with Claude Code

v0.1.1

2026-03-11 · GitHub

Summary

  • Adds a new .github/workflows/release.yml that runs on pushes to main
  • Automatically bumps the patch version in pyproject.toml, syncs uv.lock, creates a GitHub release with PR body as release notes, and commits the version bump back to main
  • Uses [skip ci] in the version bump commit to prevent infinite workflow loops

Closes #111

Test plan

🤖 Generated with Claude Code