diff options
Diffstat (limited to 'src/server/admin.rs')
-rw-r--r-- | src/server/admin.rs | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/src/server/admin.rs b/src/server/admin.rs new file mode 100644 index 0000000..54bdf12 --- /dev/null +++ b/src/server/admin.rs @@ -0,0 +1,93 @@ +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<impl Future<Output = 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(()) +} |