Skip to content

Conversation

@jameswhite-cmd
Copy link

What this PR does

  • Adds multi-tenant support: Organization and Membership tables and an org_id on Item. Existing items are backfilled into per-user default orgs in the DB migration.
  • Extends JWTs with an active_org_id claim and exposes an endpoint to switch active orgs (returns a new token).
  • Enforces org-scoped access and RBAC: only organization admins can invite or remove members; all reads/writes are scoped by the active organization.
  • Adds backend CRUD for organizations and memberships and updates items endpoints to be org-aware.
  • Adds frontend Org switcher in the navbar and an Organization Members management page.
  • Adds a seed script to create two example orgs and users and adds tests (pytest and Playwright) and docs for running the seed.

Key implementation notes

  • Database migration (backend/app/alembic/versions/fe12b3c4a567_add_organizations_memberships_and_item_org_id.py): creates organization and membership tables, adds org_id to item, backfills items by creating a default org per existing user, and makes item.org_id NOT NULL.

  • Models (backend/app/models.py): new Organization and Membership models, Role enum, membership relationships, and TokenPayload.active_org_id.

  • Security / tokens (backend/app/core/security.py): create_access_token accepts active_org_id and encodes it into the JWT.

  • Dependencies (backend/app/api/deps.py): new helpers to decode token payload, require_org_member (verifies membership and acceptance), and require_admin (enforces admin role). get_current_user now builds from the token payload. get_active_org_id helper is provided.

  • Orgs API (backend/app/api/routes/orgs.py): endpoints to list user orgs, create org (auto-adds creator as admin), switch active org (returns new token with active_org_id), list members, invite member (creates pending membership), accept invite, and remove member. All endpoints validate the active org against the token and membership.

  • Items API (backend/app/api/routes/items.py): all item endpoints (list, get, create, update, delete) are scoped to the active organization. Non-admins see only their own items within the org.

  • CRUD changes (backend/app/crud.py): creating users now creates a default org + admin membership; create_item uses a user’s accepted membership to assign the org (creates a default org if none exists).

  • Seed script (backend/scripts/seed.py): creates two orgs (Acme Inc, Umbrella Corp), users (superuser from settings, alice, bob), and memberships (superuser admin of both, Alice member of Acme, Bob member of Umbrella).

  • Frontend

    • Navbar org switcher (frontend/src/components/Common/Navbar.tsx): dropdown to list orgs and switch active org (calls /orgs/{id}/switch and stores returned token), plus a Manage button to go to members page.
    • Members page (frontend/src/routes/_layout/members.tsx): list members, invite form (posts to /orgs/{id}/invite).
  • Tests

    • Pytest (backend/tests/api/routes/test_orgs.py): verifies org creation, listing and invite permission checks.
    • Playwright e2e (frontend/tests/invite-accept-items.spec.ts): flows for invite → accept → items listing (basic smoke flow, expects seeded users).
  • Docs (development.md): added instructions for running the seed script in Docker Compose.

How to try locally

  1. Run migrations: alembic upgrade head (or start the stack and let migrations run).
  2. Seed example data: docker compose exec backend bash -lc "uv run scripts/seed.py" (see development.md).
  3. Start frontend/backend as usual. Use the Org switcher to change active org; /orgs/{id}/switch returns a new token with active_org_id.

Notes and considerations

  • The migration backfills existing items by creating a default org per user and marking that user admin of it; this ensures item.org_id can become NOT NULL safely.
  • The active organization is carried in the JWT as active_org_id. Clients should call the switch endpoint to rotate the token when changing organizations.
  • RBAC is enforced via require_admin and require_org_member helpers; endpoints use those to enforce membership and admin-only actions.

Files of interest (non-exhaustive)

  • backend/app/alembic/versions/fe12b3c4a567_*.py
  • backend/app/models.py
  • backend/app/api/deps.py
  • backend/app/api/routes/orgs.py
  • backend/app/api/routes/items.py
  • backend/app/core/security.py
  • backend/scripts/seed.py
  • frontend/src/components/Common/Navbar.tsx
  • frontend/src/routes/_layout/members.tsx
  • backend/tests/api/routes/test_orgs.py
  • frontend/tests/invite-accept-items.spec.ts

This PR implements the multi-tenant foundation (DB, models, API, frontend bits, seed data and tests). Follow-up work can include improving UX for selecting the active org, listing available orgs in the members page, stronger invite emails/accept flows, and more comprehensive e2e coverage.


This pull request was co-created with Cosine Genie

Original Task: full-stack-demo/q83jqyoy6fk9
Author: James White

…tive_org_id, migration, models, API routes, frontend org switcher, seed script & tests

Co-authored-by: Genie <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant