feat(api): mesh user router — create, list, invite, archive, leave

New /my/* Hono router scoped by session.user.id. User can only see meshes
they own OR have a non-revoked meshMember row for. All 7 endpoints guard
authz at the query level (ownerUserId = userId OR EXISTS membership).

- GET /my/meshes — paginated list with myRole, isOwner, memberCount
- POST /my/meshes — create mesh (slug collision check, returns id + slug)
- GET /my/meshes/:id — detail (mesh + members + invites)
- POST /my/meshes/:id/invites — generate ic://join/<base64url(JSON)> link.
  Matches apps/cli/src/invite/parse.ts format exactly. mesh_root_key is a
  deterministic sha256(mesh.id:slug) placeholder until Step 18 ed25519
  signing lands.
- POST /my/meshes/:id/archive — owner-only
- POST /my/meshes/:id/leave — member self-removal (sets revokedAt)
- GET /my/invites — list invites this user has issued

Schemas live in packages/api/src/schema/mesh-user.ts. All enums mirror
the DB enums from packages/db/src/schema/mesh.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-04 22:56:29 +01:00
parent 9d3dbcecaf
commit a486ffd056
6 changed files with 641 additions and 0 deletions

View File

@@ -13,6 +13,7 @@ import { adminRouter } from "./modules/admin/router";
// import { aiRouter } from "./modules/ai/router"; // disabled: @turbostarter/ai package removed in claudemesh
import { authRouter } from "./modules/auth/router";
import { billingRouter } from "./modules/billing/router";
import { myRouter } from "./modules/mesh/router";
import { organizationRouter } from "./modules/organization/router";
import { storageRouter } from "./modules/storage/router";
import { onError } from "./utils/on-error";
@@ -48,6 +49,7 @@ const appRouter = new Hono()
// .route("/ai", aiRouter) // disabled: @turbostarter/ai package removed in claudemesh
.route("/auth", authRouter)
.route("/billing", billingRouter)
.route("/my", myRouter)
.route("/organizations", organizationRouter)
.route("/storage", storageRouter)
.onError(onError);