Phase 4 backend: teams data layer + membership management
CI / backend (push) Failing after 1m6s
CI / policy (push) Successful in 4s
CI / profile (push) Successful in 12s
CI / mobile (push) Successful in 17s

- Migration 0003: teams + team_memberships (owner/admin/member, seat_limit)
- POST /v1/teams (Team-tier gated), GET /v1/teams/{id},
  GET|POST /v1/teams/{id}/members, DELETE …/members/{userId}
- Add-by-email (existing accounts), seat-cap enforcement (402), role-based
  authorization (admin to mutate; non-members get 404; owner unremovable)
- Invite notification email; Tier::can_own_team()

100 backend tests; fmt + clippy clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-04 15:13:03 -05:00
co-authored by Claude Opus 4.8
parent 4ad3220917
commit 2b8e23dd3f
13 changed files with 647 additions and 2 deletions
@@ -4,7 +4,9 @@
use axum::routing::{get, post};
use axum::Router;
use crate::handlers::{analytics, assets, auth, cards, health, profile, share, wallet, webhooks};
use crate::handlers::{
analytics, assets, auth, cards, health, profile, share, teams, wallet, webhooks,
};
use crate::middleware::cors;
use crate::state::AppState;
@@ -42,6 +44,16 @@ pub fn build_router(state: AppState) -> Router {
.route("/s/:token", get(share::resolve_share))
.route("/profile/:handle/contact", post(profile::submit_contact))
.route("/webhooks/revenuecat", post(webhooks::revenuecat))
.route("/teams", post(teams::create_team))
.route("/teams/:id", get(teams::get_team))
.route(
"/teams/:id/members",
get(teams::list_members).post(teams::add_member),
)
.route(
"/teams/:id/members/:userId",
axum::routing::delete(teams::remove_member),
)
.route("/analytics/event", post(analytics::ingest_event))
.route("/assets/upload", post(assets::presign_upload))
.route("/assets/*key", axum::routing::delete(assets::delete_asset));