@@ -262,3 +262,212 @@ pub async fn delete_role(
262262 }
263263 Ok ( ( ) )
264264}
265+
266+ // ─── Server management commands ─────────────────────────────────────────────
267+
268+ /// Update server settings (name, description, public flag, etc.).
269+ #[ tauri:: command]
270+ pub async fn update_server (
271+ state : State < ' _ , AppState > ,
272+ server_id : Uuid ,
273+ name : Option < String > ,
274+ description : Option < String > ,
275+ is_public : Option < bool > ,
276+ region : Option < String > ,
277+ require_2fa : Option < bool > ,
278+ spam_window_secs : Option < i32 > ,
279+ spam_max_messages : Option < i32 > ,
280+ ) -> Result < ServerClient , String > {
281+ let session = state. session_snapshot ( ) ;
282+ let ( client, base) = api_client ( & session) . map_err ( |e| e. to_string ( ) ) ?;
283+ let body = serde_json:: json!( {
284+ "name" : name,
285+ "description" : description,
286+ "is_public" : is_public,
287+ "region" : region,
288+ "require_2fa" : require_2fa,
289+ "spam_window_secs" : spam_window_secs,
290+ "spam_max_messages" : spam_max_messages,
291+ } ) ;
292+ let resp = client
293+ . patch ( format ! ( "{base}/api/v1/servers/{server_id}" ) )
294+ . json ( & body)
295+ . send ( )
296+ . await
297+ . map_err ( |e| e. to_string ( ) ) ?;
298+ if !resp. status ( ) . is_success ( ) {
299+ let text = resp. text ( ) . await . unwrap_or_default ( ) ;
300+ return Err ( text) ;
301+ }
302+ let raw: RawServer = resp. json ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
303+ Ok ( ServerClient :: from ( raw) )
304+ }
305+
306+ /// Delete a server permanently.
307+ #[ tauri:: command]
308+ pub async fn delete_server (
309+ state : State < ' _ , AppState > ,
310+ server_id : Uuid ,
311+ ) -> Result < ( ) , String > {
312+ let session = state. session_snapshot ( ) ;
313+ let ( client, base) = api_client ( & session) . map_err ( |e| e. to_string ( ) ) ?;
314+ let resp = client
315+ . delete ( format ! ( "{base}/api/v1/servers/{server_id}" ) )
316+ . send ( )
317+ . await
318+ . map_err ( |e| e. to_string ( ) ) ?;
319+ if !resp. status ( ) . is_success ( ) {
320+ let text = resp. text ( ) . await . unwrap_or_default ( ) ;
321+ return Err ( text) ;
322+ }
323+ Ok ( ( ) )
324+ }
325+
326+ /// Transfer server ownership to another member.
327+ #[ tauri:: command]
328+ pub async fn transfer_server_ownership (
329+ state : State < ' _ , AppState > ,
330+ server_id : Uuid ,
331+ new_owner_id : String ,
332+ ) -> Result < ( ) , String > {
333+ let session = state. session_snapshot ( ) ;
334+ let ( client, base) = api_client ( & session) . map_err ( |e| e. to_string ( ) ) ?;
335+ let body = serde_json:: json!( { "new_owner_id" : new_owner_id } ) ;
336+ let resp = client
337+ . post ( format ! ( "{base}/api/v1/servers/{server_id}/transfer-ownership" ) )
338+ . json ( & body)
339+ . send ( )
340+ . await
341+ . map_err ( |e| e. to_string ( ) ) ?;
342+ if !resp. status ( ) . is_success ( ) {
343+ let text = resp. text ( ) . await . unwrap_or_default ( ) ;
344+ return Err ( text) ;
345+ }
346+ Ok ( ( ) )
347+ }
348+
349+ /// Leave a server (non-owner members only).
350+ #[ tauri:: command]
351+ pub async fn leave_server (
352+ state : State < ' _ , AppState > ,
353+ server_id : Uuid ,
354+ ) -> Result < ( ) , String > {
355+ let session = state. session_snapshot ( ) ;
356+ let ( client, base) = api_client ( & session) . map_err ( |e| e. to_string ( ) ) ?;
357+ let resp = client
358+ . post ( format ! ( "{base}/api/v1/servers/{server_id}/leave" ) )
359+ . send ( )
360+ . await
361+ . map_err ( |e| e. to_string ( ) ) ?;
362+ if !resp. status ( ) . is_success ( ) {
363+ let text = resp. text ( ) . await . unwrap_or_default ( ) ;
364+ return Err ( text) ;
365+ }
366+ Ok ( ( ) )
367+ }
368+
369+ // ─── Invite commands ─────────────────────────────────────────────────────────
370+
371+ #[ derive( Serialize , Debug , Clone ) ]
372+ #[ serde( rename_all = "camelCase" ) ]
373+ pub struct InviteClient {
374+ pub code : String ,
375+ pub server_id : String ,
376+ pub max_uses : Option < i64 > ,
377+ pub uses : i64 ,
378+ pub expires_at : Option < String > ,
379+ pub created_at : String ,
380+ }
381+
382+ #[ derive( Deserialize , Debug , Clone ) ]
383+ struct RawInvite {
384+ pub code : String ,
385+ pub server_id : serde_json:: Value ,
386+ pub max_uses : Option < i64 > ,
387+ pub uses : i64 ,
388+ pub expires_at : Option < String > ,
389+ pub created_at : String ,
390+ }
391+
392+ impl From < RawInvite > for InviteClient {
393+ fn from ( r : RawInvite ) -> Self {
394+ Self {
395+ code : r. code ,
396+ server_id : r. server_id . to_string ( ) . trim_matches ( '"' ) . to_string ( ) ,
397+ max_uses : r. max_uses ,
398+ uses : r. uses ,
399+ expires_at : r. expires_at ,
400+ created_at : r. created_at ,
401+ }
402+ }
403+ }
404+
405+ /// List all active invites for a server.
406+ #[ tauri:: command]
407+ pub async fn list_server_invites (
408+ state : State < ' _ , AppState > ,
409+ server_id : Uuid ,
410+ ) -> Result < Vec < InviteClient > , String > {
411+ let session = state. session_snapshot ( ) ;
412+ let ( client, base) = api_client ( & session) . map_err ( |e| e. to_string ( ) ) ?;
413+ let resp = client
414+ . get ( format ! ( "{base}/api/v1/servers/{server_id}/invites" ) )
415+ . send ( )
416+ . await
417+ . map_err ( |e| e. to_string ( ) ) ?;
418+ if !resp. status ( ) . is_success ( ) {
419+ let text = resp. text ( ) . await . unwrap_or_default ( ) ;
420+ return Err ( text) ;
421+ }
422+ let raw: Vec < RawInvite > = resp. json ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
423+ Ok ( raw. into_iter ( ) . map ( InviteClient :: from) . collect ( ) )
424+ }
425+
426+ /// Create a new invite for a server.
427+ #[ tauri:: command]
428+ pub async fn create_invite (
429+ state : State < ' _ , AppState > ,
430+ server_id : Uuid ,
431+ max_uses : Option < i64 > ,
432+ max_age_secs : Option < i64 > ,
433+ ) -> Result < InviteClient , String > {
434+ let session = state. session_snapshot ( ) ;
435+ let ( client, base) = api_client ( & session) . map_err ( |e| e. to_string ( ) ) ?;
436+ let body = serde_json:: json!( {
437+ "max_uses" : max_uses,
438+ "max_age_secs" : max_age_secs,
439+ } ) ;
440+ let resp = client
441+ . post ( format ! ( "{base}/api/v1/servers/{server_id}/invites" ) )
442+ . json ( & body)
443+ . send ( )
444+ . await
445+ . map_err ( |e| e. to_string ( ) ) ?;
446+ if !resp. status ( ) . is_success ( ) {
447+ let text = resp. text ( ) . await . unwrap_or_default ( ) ;
448+ return Err ( text) ;
449+ }
450+ let raw: RawInvite = resp. json ( ) . await . map_err ( |e| e. to_string ( ) ) ?;
451+ Ok ( InviteClient :: from ( raw) )
452+ }
453+
454+ /// Delete (revoke) a server invite by code.
455+ #[ tauri:: command]
456+ pub async fn delete_invite (
457+ state : State < ' _ , AppState > ,
458+ server_id : Uuid ,
459+ code : String ,
460+ ) -> Result < ( ) , String > {
461+ let session = state. session_snapshot ( ) ;
462+ let ( client, base) = api_client ( & session) . map_err ( |e| e. to_string ( ) ) ?;
463+ let resp = client
464+ . delete ( format ! ( "{base}/api/v1/servers/{server_id}/invites/{code}" ) )
465+ . send ( )
466+ . await
467+ . map_err ( |e| e. to_string ( ) ) ?;
468+ if !resp. status ( ) . is_success ( ) {
469+ let text = resp. text ( ) . await . unwrap_or_default ( ) ;
470+ return Err ( text) ;
471+ }
472+ Ok ( ( ) )
473+ }
0 commit comments