Phase 2: share system + analytics feed (backend)
CI / policy (push) Successful in 4s
CI / backend (push) Failing after 44s
CI / mobile (push) Successful in 49s
CI / profile (push) Successful in 2m19s

- share_links model/queries; opaque 12-char base62 tokens (PRD §17.1)
- POST /v1/cards/{id}/share — create share link (owner-scoped, modality-validated)
- GET /v1/s/{token} — record modality-attributed event (qr→qr_scan, nfc→nfc_tap)
  and 302-redirect to the profile; unknown/expired → 404
- GET /v1/cards/{id}/share-links — list a card's links
- analytics_events now carry the share_token correlation
- GET /v1/cards/{id}/analytics/feed — chronological event feed (§6.7.2)

6 new integration tests; 73 backend tests total. fmt + clippy clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-06-04 11:19:08 -05:00
co-authored by Claude Opus 4.8
parent c30d3afeec
commit e66813ef58
17 changed files with 527 additions and 10 deletions
@@ -4,7 +4,7 @@
use axum::routing::{get, post};
use axum::Router;
use crate::handlers::{analytics, assets, auth, cards, health, wallet};
use crate::handlers::{analytics, assets, auth, cards, health, share, wallet};
use crate::middleware::cors;
use crate::state::AppState;
@@ -34,6 +34,10 @@ pub fn build_router(state: AppState) -> Router {
.route("/cards/:id/wallet/apple", post(wallet::apple_pass))
.route("/cards/handle/:handle", get(cards::get_card_by_handle))
.route("/cards/:id/analytics", get(analytics::summary))
.route("/cards/:id/analytics/feed", get(analytics::feed))
.route("/cards/:id/share", post(share::create_share))
.route("/cards/:id/share-links", get(share::list_shares))
.route("/s/:token", get(share::resolve_share))
.route("/analytics/event", post(analytics::ingest_event))
.route("/assets/upload", post(assets::presign_upload))
.route("/assets/*key", axum::routing::delete(assets::delete_asset));