use std::{future::Future, path::Path}; use tap::Pipe; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, net::{UnixListener, UnixStream}, }; use crate::ipc::*; use super::store::Store; pub fn serve( bind: &Path, web_base: String, store: Store, ) -> eyre::Result>> { if bind.exists() { std::fs::remove_file(bind)?; } let listener = UnixListener::bind(bind)?; Ok(async move { loop { match listener.accept().await { Ok((stream, _)) => tokio::spawn({ let store = store.clone(); let web_base = web_base.clone(); async move { if let Err(err) = handle(stream, store, &web_base).await { eprintln!("error handling admin connection: {err:?}"); } } }), Err(err) => { eprintln!("error accepting admin connection: {err:?}"); continue; } }; } }) } async fn handle(mut stream: UnixStream, store: Store, web_base: &str) -> eyre::Result<()> { while let Ok(request_length) = stream.read_u16().await { let mut request_buffer = vec![0u8; request_length as usize]; stream.read_exact(&mut request_buffer).await?; let (request, _): (IPCRequest, _) = bincode::decode_from_slice(&request_buffer, bincode::config::standard())?; let response = match request { IPCRequest::ListAccounts(_) => ListAccountsResponse { names: store.list_accounts().await, } .into_response(), IPCRequest::GetAccount(GetAccountRequest { name }) => { IPCResponse::GetAccount(match store.get_account(&name).await { Some(account) => Ok(GetAccountResponse { scopes: account.scopes.clone(), }), None => Err(GetAccountError::NotFound), }) } IPCRequest::DeleteAccount(DeleteAccountRequest { name }) => { store.delete_account(&name).await?; DeleteAccountResponse {}.into_response() } IPCRequest::CreateInvite(_) => store .create_invite() .await? .pipe(|invite| CreateInviteResponse { link: format!("{web_base}/invite/{invite}"), }) .into_response(), IPCRequest::UpdateScopes(UpdateScopesRequest { account, scopes }) => { IPCResponse::UpdateScopes( match store .update_account(&account, |account| { account.scopes = scopes; }) .await? { true => Ok(UpdateScopesResponse {}), false => Err(UpdateScopesError::NotFound), }, ) } }; let response_buffer = bincode::encode_to_vec(response, bincode::config::standard())?; stream.write_u16(response_buffer.len() as u16).await?; stream.write_all(&response_buffer).await?; } Ok(()) }