@@ -482,16 +482,16 @@ pub async fn update_workspace(
482482 // Build dynamic update query
483483 let mut set_clauses = vec ! [ "updated_at = NOW()" . to_string( ) ] ;
484484
485- if request. name . is_some ( ) {
485+ if let Some ( name ) = & request. name {
486486 set_clauses. push ( format ! (
487487 "name = '{}'" ,
488- request . name. as_ref ( ) . unwrap ( ) . replace( '\'' , "''" )
488+ name. replace( '\'' , "''" )
489489 ) ) ;
490490 }
491- if request. description . is_some ( ) {
491+ if let Some ( description ) = & request. description {
492492 set_clauses. push ( format ! (
493493 "description = '{}'" ,
494- request . description. as_ref ( ) . unwrap ( ) . replace( '\'' , "''" )
494+ description. replace( '\'' , "''" )
495495 ) ) ;
496496 }
497497
@@ -568,6 +568,73 @@ pub async fn update_workspace(
568568 }
569569}
570570
571+ /// List ALL workspaces (admin endpoint — no user scoping)
572+ ///
573+ /// GET /api/v1/admin/workspaces
574+ pub async fn admin_list_workspaces (
575+ State ( state) : State < AppState > ,
576+ Query ( query) : Query < ListWorkspacesQuery > ,
577+ ) -> Result < Json < PaginatedResponse < WorkspaceResponse > > , ( StatusCode , Json < ApiResponse < ( ) > > ) > {
578+ let limit = query. limit . min ( 100 ) ;
579+ let offset = query. offset ;
580+
581+ let workspaces: Vec < ( uuid:: Uuid , String , String , Option < String > , String , Option < uuid:: Uuid > , Option < uuid:: Uuid > , chrono:: DateTime < chrono:: Utc > , chrono:: DateTime < chrono:: Utc > ) > =
582+ sqlx:: query_as (
583+ r#"
584+ SELECT id, name, slug, description, workspace_type, owner_user_id, tenant_id, created_at, updated_at
585+ FROM workspaces
586+ ORDER BY created_at DESC
587+ LIMIT $1 OFFSET $2
588+ "# ,
589+ )
590+ . bind ( limit as i64 )
591+ . bind ( offset as i64 )
592+ . fetch_all ( & state. db_pool )
593+ . await
594+ . map_err ( |e| {
595+ (
596+ StatusCode :: INTERNAL_SERVER_ERROR ,
597+ Json ( ApiResponse {
598+ success : false ,
599+ data : None ,
600+ error : Some ( ApiError {
601+ code : "LIST_FAILED" . to_string ( ) ,
602+ message : e. to_string ( ) ,
603+ } ) ,
604+ } ) ,
605+ )
606+ } ) ?;
607+
608+ let items: Vec < WorkspaceResponse > = workspaces
609+ . into_iter ( )
610+ . map (
611+ |( id, name, slug, description, workspace_type, owner_user_id, tenant_id, created_at, updated_at) | {
612+ WorkspaceResponse {
613+ id : id. to_string ( ) ,
614+ name,
615+ slug,
616+ description,
617+ workspace_type,
618+ owner_user_id : owner_user_id. map ( |u| u. to_string ( ) ) ,
619+ tenant_id : tenant_id. map ( |t| t. to_string ( ) ) ,
620+ created_at : created_at. to_rfc3339 ( ) ,
621+ updated_at : updated_at. to_rfc3339 ( ) ,
622+ }
623+ } ,
624+ )
625+ . collect ( ) ;
626+
627+ let has_more = items. len ( ) as u32 == limit;
628+
629+ Ok ( Json ( PaginatedResponse {
630+ total : items. len ( ) as u64 ,
631+ items,
632+ offset,
633+ limit,
634+ has_more,
635+ } ) )
636+ }
637+
571638/// Delete a workspace
572639///
573640/// DELETE /api/v1/workspaces/:id
0 commit comments