gabriel / musehub public
orgs.py python
139 lines 4.7 KB
Raw
sha256:34035d72cef530c1ab9d6a6f53be18d803dde705fc3157617d70352a96d0747b Merge branch 'fix/wire-push-external-parent-manifest' into dev Human 8 days ago
1 """REST API — org endpoints.
2
3 Mounted at /api/orgs/...
4
5 Endpoint surface:
6 POST /api/orgs — create org identity + repo (MSign)
7 POST /api/orgs/{org}/members/{handle} — add member (MSign)
8 GET /api/orgs/{org}/members — list members from identity repo HEAD
9 DELETE /api/orgs/{org}/members/{handle} — remove member (MSign)
10 """
11
12 import logging
13
14 from fastapi import APIRouter, Depends, HTTPException, status
15 from fastapi.responses import JSONResponse, Response
16 from pydantic import BaseModel, Field
17 from sqlalchemy.ext.asyncio import AsyncSession
18
19 from musehub.auth.dependencies import require_valid_token, TokenClaims
20 from musehub.db.database import get_db as get_session
21 from musehub.services.musehub_auth import AuthError
22 from musehub.services.musehub_orgs import (
23 add_org_member,
24 create_org,
25 list_org_members,
26 remove_org_member,
27 )
28
29 logger = logging.getLogger(__name__)
30
31 router = APIRouter(tags=["Orgs"])
32
33 class CreateOrgRequest(BaseModel):
34 handle: str = Field(..., min_length=1, max_length=64)
35 display_name: str = Field(..., min_length=1, max_length=255)
36 quorum: int = Field(1, ge=1)
37
38 class AddMemberRequest(BaseModel):
39 weight: str = Field("write", pattern="^(admin|write|read)$")
40
41 # ---------------------------------------------------------------------------
42 # POST /api/orgs
43 # ---------------------------------------------------------------------------
44
45 @router.post("/api/orgs", status_code=status.HTTP_201_CREATED, summary="Create org")
46 async def create_org_route(
47 body: CreateOrgRequest,
48 claims: TokenClaims = Depends(require_valid_token),
49 session: AsyncSession = Depends(get_session),
50 ) -> Response:
51 try:
52 identity = await create_org(
53 session,
54 handle=body.handle,
55 display_name=body.display_name,
56 quorum=body.quorum,
57 creator_identity_id=claims.identity_id,
58 )
59 except AuthError as exc:
60 raise HTTPException(status_code=exc.status_code, detail=str(exc))
61
62 return JSONResponse(content={
63 "identity_id": identity.identity_id,
64 "handle": identity.handle,
65 "identity_type": identity.identity_type,
66 "display_name": identity.display_name,
67 "quorum": identity.org_quorum,
68 }, status_code=status.HTTP_201_CREATED)
69
70 # ---------------------------------------------------------------------------
71 # POST /api/orgs/{org}/members/{handle}
72 # ---------------------------------------------------------------------------
73
74 @router.post(
75 "/api/orgs/{org}/members/{handle}",
76 status_code=status.HTTP_201_CREATED,
77 summary="Add org member",
78 )
79 async def add_member_route(
80 org: str,
81 handle: str,
82 body: AddMemberRequest,
83 claims: TokenClaims = Depends(require_valid_token),
84 session: AsyncSession = Depends(get_session),
85 ) -> Response:
86 try:
87 await add_org_member(
88 session,
89 org_handle=org,
90 member_handle=handle,
91 weight=body.weight,
92 actor_identity_id=claims.identity_id,
93 )
94 except AuthError as exc:
95 raise HTTPException(status_code=exc.status_code, detail=str(exc))
96
97 return JSONResponse(content={"org": org, "member": handle, "weight": body.weight}, status_code=status.HTTP_201_CREATED)
98
99 # ---------------------------------------------------------------------------
100 # GET /api/orgs/{org}/members
101 # ---------------------------------------------------------------------------
102
103 @router.get("/api/orgs/{org}/members", summary="List org members")
104 async def list_members_route(
105 org: str,
106 claims: TokenClaims = Depends(require_valid_token),
107 session: AsyncSession = Depends(get_session),
108 ) -> Response:
109 try:
110 members = await list_org_members(session, org_handle=org)
111 except AuthError as exc:
112 raise HTTPException(status_code=exc.status_code, detail=str(exc))
113
114 return JSONResponse(content={"org": org, "members": members})
115
116 # ---------------------------------------------------------------------------
117 # DELETE /api/orgs/{org}/members/{handle}
118 # ---------------------------------------------------------------------------
119
120 @router.delete(
121 "/api/orgs/{org}/members/{handle}",
122 status_code=status.HTTP_204_NO_CONTENT,
123 summary="Remove org member",
124 )
125 async def remove_member_route(
126 org: str,
127 handle: str,
128 claims: TokenClaims = Depends(require_valid_token),
129 session: AsyncSession = Depends(get_session),
130 ) -> None:
131 try:
132 await remove_org_member(
133 session,
134 org_handle=org,
135 member_handle=handle,
136 actor_identity_id=claims.identity_id,
137 )
138 except AuthError as exc:
139 raise HTTPException(status_code=exc.status_code, detail=str(exc))
File History 14 commits
sha256:34035d72cef530c1ab9d6a6f53be18d803dde705fc3157617d70352a96d0747b Merge branch 'fix/wire-push-external-parent-manifest' into dev Human 8 days ago
sha256:fc04e4cae9e1774d6a21b65c45daeed0e6787eb581d13aa1b03bfe9384a34226 Merge branch 'fix/two-column-scroll-layout' into dev Human 8 days ago
sha256:408916fc5973ba59c6e4eebaa80ebdcc801c0a63205651e25009d11548f79454 chore: bump version to 0.2.0.dev2 — nightly.2, matching muse Sonnet 4.6 patch 11 days ago
sha256:d035733f21ccff27735fddebfbbe0ed24565a32a22db8de5885402262671ecd2 chore: bump version to 0.2.0rc15 for musehub#113 fix release Sonnet 4.6 patch 14 days ago
sha256:0032d6cfa33bc3c8367436ad768e7dd0e339b4332153160247da8266cb5fa352 Merge branch 'task/version-tags-phase3-server' into dev Human 17 days ago
sha256:4669620efda9ff41c55bdefd1f7bfe1c239d468428744c84ead9957e5a003a53 merge: rescue snapshot-recovery hardening (c00aa21d) into d… Opus 4.8 minor 29 days ago
sha256:a59da49c4611b970fc4b6ae48678ce4943261c213a07ddbd73ce9201df869b4a fix: remove false-positive proposal_comments index drop fro… Sonnet 4.6 patch 33 days ago
sha256:0a240d6dbff234f07d98a28a4a9a68db702f3f9ff9260196f24219bdb1c0b6f3 feat: render markdown mists as HTML with heading anchor links Sonnet 4.6 patch 34 days ago
sha256:24a7d47486ebc4ebd1832830580e177ec6f877b48dced8c000e198cdec4ce9d6 Merge 'task/bump-version-rc12' into 'dev' — proposal: Bump … Human 35 days ago
sha256:b9ff931d147e0114a1f17060f415b89ed551c170a91ff226c70437aa5c85f9ee Merge 'task/bump-version-rc12' into 'dev' — proposal: Bump … Human 35 days ago
sha256:d1122d21e73471879b460037b22c0b50fded7c423444a176f248428f75dac39c Merge 'task/fix-issue-pagination-cursor' into 'dev' — propo… Human 35 days ago
sha256:01e18975e73d2b3cd5b6db7929c895bef9aa6e0d4391dc5b2adfc548b41318dd Merge 'feat/adding-debug-logs-to-staging' into 'dev' — prop… Human 36 days ago
sha256:6b1949fc2797ca4c1936a637a4cbfec828ef56cf52398a2e74ca3c4f494e728f fix: use wire_bytes not mpack_bytes_raw in compute_object_b… Sonnet 4.6 patch 48 days ago
sha256:b99f2455dc346966d040133f5203297e6e3ef5803a93728a2c30568d0a0f7583 rename: delta_add → delta_upsert across wire format, models… Sonnet 4.6 patch 50 days ago