//! The methods whoever runs the server calls, which `pdsadmin` is the client //! for. use std::sync::Arc; use axum::{Json, extract::State}; use serde::{Deserialize, Serialize}; use crate::account::Manager; use crate::xrpc::{self, Input, auth::Admin}; /// Who a code belongs to when the caller does not say, which is nobody: a /// code the server handed out itself. const ADMINISTRATOR: &str = "admin"; /// One code, for one account. #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub(crate) struct One { /// How many signups it is good for. use_count: u32, /// Whose it is, which decides nothing but who is credited. for_account: Option, } /// The code that was written. #[derive(Debug, Serialize)] pub(crate) struct Code { code: String, } /// Several codes, for several accounts. #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub(crate) struct Many { /// How many codes each account gets. The lexicon gives this a default, so /// leaving it out asks for one rather than being a malformed request. #[serde(default = "one")] code_count: u32, /// How many signups each one is good for. use_count: u32, /// Whose they are. for_accounts: Option>, } /// What `codeCount` means when it is not sent. fn one() -> u32 { 1 } /// The codes that were written, by whose they are. #[derive(Debug, Serialize)] pub(crate) struct Codes { codes: Vec, } /// One account's share of them. #[derive(Debug, Serialize)] pub(crate) struct Held { account: String, codes: Vec, } /// `com.atproto.server.createInviteCode` /// /// # Errors /// /// If the caller is not the administrator, or the write fails. pub(crate) async fn create_invite_code( _: Admin, State(accounts): State>, Input(input): Input, ) -> xrpc::Result> { let account = input .for_account .unwrap_or_else(|| ADMINISTRATOR.to_owned()); let code = accounts .mint_invites(std::slice::from_ref(&account), 1, input.use_count) .await? .pop() .and_then(|mut codes| codes.pop()) .ok_or_else(|| xrpc::Error::internal("asked for one invite code and was given none"))?; Ok(Json(Code { code })) } /// `com.atproto.server.createInviteCodes` /// /// # Errors /// /// If the caller is not the administrator, or the write fails. pub(crate) async fn create_invite_codes( _: Admin, State(accounts): State>, Input(input): Input, ) -> xrpc::Result> { let for_accounts = input .for_accounts .unwrap_or_else(|| vec![ADMINISTRATOR.to_owned()]); let minted = accounts .mint_invites(&for_accounts, input.code_count, input.use_count) .await?; let codes = for_accounts .into_iter() .zip(minted) .map(|(account, codes)| Held { account, codes }) .collect(); Ok(Json(Codes { codes })) }