@@ -87,65 +87,73 @@ pub async fn create_clip(
8787 . into_response ( ) ;
8888 }
8989
90- // Check org membership
91- let org_id = OrgId :: new ( payload. org_id ) ;
92- let membership = match state
93- . org_repo
94- . get_membership ( & org_id, & current_user. user . id )
95- . await
96- {
97- Ok ( Some ( m) ) => m,
98- Ok ( None ) => {
99- return (
100- StatusCode :: FORBIDDEN ,
101- Json ( ClipsErrorResponse {
102- error : "forbidden" . to_string ( ) ,
103- message : t ( locale, "server.api.error.forbidden" ) . to_string ( ) ,
104- } ) ,
105- )
106- . into_response ( ) ;
107- }
108- Err ( e) => {
109- tracing:: error!( error = %e, "Failed to check org membership" ) ;
110- return (
111- StatusCode :: INTERNAL_SERVER_ERROR ,
112- Json ( ClipsErrorResponse {
113- error : "internal_error" . to_string ( ) ,
114- message : t ( locale, "server.api.error.internal" ) . to_string ( ) ,
115- } ) ,
116- )
117- . into_response ( ) ;
118- }
119- } ;
90+ // Determine owner: org slug if org_id provided, otherwise user's display_name
91+ let ( owner_slug, org_id_for_record) = if let Some ( org_uuid) = payload. org_id {
92+ // Check org membership
93+ let org_id = OrgId :: new ( org_uuid) ;
94+ let _membership = match state
95+ . org_repo
96+ . get_membership ( & org_id, & current_user. user . id )
97+ . await
98+ {
99+ Ok ( Some ( m) ) => m,
100+ Ok ( None ) => {
101+ return (
102+ StatusCode :: FORBIDDEN ,
103+ Json ( ClipsErrorResponse {
104+ error : "forbidden" . to_string ( ) ,
105+ message : t ( locale, "server.api.error.forbidden" ) . to_string ( ) ,
106+ } ) ,
107+ )
108+ . into_response ( ) ;
109+ }
110+ Err ( e) => {
111+ tracing:: error!( error = %e, "Failed to check org membership" ) ;
112+ return (
113+ StatusCode :: INTERNAL_SERVER_ERROR ,
114+ Json ( ClipsErrorResponse {
115+ error : "internal_error" . to_string ( ) ,
116+ message : t ( locale, "server.api.error.internal" ) . to_string ( ) ,
117+ } ) ,
118+ )
119+ . into_response ( ) ;
120+ }
121+ } ;
120122
121- // Get org for the owner name
122- let org = match state. org_repo . get_org_by_id ( & org_id) . await {
123- Ok ( Some ( o) ) => o,
124- Ok ( None ) => {
125- return (
126- StatusCode :: NOT_FOUND ,
127- Json ( ClipsErrorResponse {
128- error : "not_found" . to_string ( ) ,
129- message : t ( locale, "server.api.clips.org_not_found" ) . to_string ( ) ,
130- } ) ,
131- )
132- . into_response ( ) ;
133- }
134- Err ( e) => {
135- tracing:: error!( error = %e, "Failed to get organization" ) ;
136- return (
137- StatusCode :: INTERNAL_SERVER_ERROR ,
138- Json ( ClipsErrorResponse {
139- error : "internal_error" . to_string ( ) ,
140- message : t ( locale, "server.api.error.internal" ) . to_string ( ) ,
141- } ) ,
142- )
143- . into_response ( ) ;
144- }
123+ // Get org for the owner name
124+ let org = match state. org_repo . get_org_by_id ( & org_id) . await {
125+ Ok ( Some ( o) ) => o,
126+ Ok ( None ) => {
127+ return (
128+ StatusCode :: NOT_FOUND ,
129+ Json ( ClipsErrorResponse {
130+ error : "not_found" . to_string ( ) ,
131+ message : t ( locale, "server.api.clips.org_not_found" ) . to_string ( ) ,
132+ } ) ,
133+ )
134+ . into_response ( ) ;
135+ }
136+ Err ( e) => {
137+ tracing:: error!( error = %e, "Failed to get organization" ) ;
138+ return (
139+ StatusCode :: INTERNAL_SERVER_ERROR ,
140+ Json ( ClipsErrorResponse {
141+ error : "internal_error" . to_string ( ) ,
142+ message : t ( locale, "server.api.error.internal" ) . to_string ( ) ,
143+ } ) ,
144+ )
145+ . into_response ( ) ;
146+ }
147+ } ;
148+
149+ ( org. slug , Some ( org_uuid) )
150+ } else {
151+ // Personal clip - use user's display name as owner
152+ ( current_user. user . display_name . clone ( ) , None )
145153 } ;
146154
147- // Check if clip name already exists
148- if let Ok ( true ) = clips_repo. clip_name_exists ( & org . slug , & payload. name ) . await {
155+ // Check if clip name already exists for this owner
156+ if let Ok ( true ) = clips_repo. clip_name_exists ( & owner_slug , & payload. name ) . await {
149157 return (
150158 StatusCode :: CONFLICT ,
151159 Json ( ClipsErrorResponse {
@@ -162,12 +170,12 @@ pub async fn create_clip(
162170
163171 let params = CreateClipParams {
164172 id : clip_id,
165- owner : org . slug . clone ( ) ,
173+ owner : owner_slug . clone ( ) ,
166174 name : payload. name . clone ( ) ,
167175 description : payload. description . clone ( ) ,
168176 visibility,
169177 created_by : current_user. user . id . into_inner ( ) ,
170- org_id : Some ( payload . org_id ) ,
178+ org_id : org_id_for_record ,
171179 is_fork : false ,
172180 forked_from : None ,
173181 } ;
@@ -245,7 +253,8 @@ pub async fn create_clip(
245253 tracing:: info!(
246254 clip_id = %clip_id,
247255 name = %payload. name,
248- org_id = %payload. org_id,
256+ org_id = ?org_id_for_record,
257+ owner = %owner_slug,
249258 created_by = %current_user. user. id,
250259 "Clip created"
251260 ) ;
@@ -256,15 +265,14 @@ pub async fn create_clip(
256265 . actor ( AuditUserId :: new ( current_user. user . id . into_inner ( ) ) )
257266 . resource ( "clip" , clip_id. to_string ( ) )
258267 . details ( serde_json:: json!( {
259- "org_id" : payload. org_id. to_string( ) ,
268+ "org_id" : org_id_for_record. map( |id| id. to_string( ) ) ,
269+ "owner" : owner_slug,
260270 "name" : payload. name,
261271 "visibility" : format!( "{:?}" , visibility) ,
262272 "file_count" : payload. files. len( ) ,
263273 } ) )
264274 . build ( ) ,
265275 ) ;
266-
267- let _ = membership;
268276 (
269277 StatusCode :: CREATED ,
270278 Json ( clip_record_to_response ( clip, & state. base_url ) ) ,
0 commit comments