mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-04-14 13:47:00 +00:00
fix: first user invite code
This commit is contained in:
@@ -548,28 +548,38 @@ pub async fn create_account(
|
||||
return ApiError::HandleTaken.into_response();
|
||||
}
|
||||
|
||||
let invite_code_required = tranquil_config::get().server.invite_code_required;
|
||||
if invite_code_required
|
||||
&& input
|
||||
.invite_code
|
||||
.as_ref()
|
||||
.map(|c| c.trim().is_empty())
|
||||
.unwrap_or(true)
|
||||
{
|
||||
return ApiError::InviteCodeRequired.into_response();
|
||||
}
|
||||
if let Some(code) = &input.invite_code
|
||||
&& !code.trim().is_empty()
|
||||
{
|
||||
let valid = match state.user_repo.check_and_consume_invite_code(code).await {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!("Error checking invite code: {:?}", e);
|
||||
return ApiError::InternalError(None).into_response();
|
||||
let is_bootstrap = state.bootstrap_invite_code.is_some()
|
||||
&& state.user_repo.count_users().await.unwrap_or(1) == 0;
|
||||
|
||||
if is_bootstrap {
|
||||
match input.invite_code.as_deref() {
|
||||
Some(code) if Some(code) == state.bootstrap_invite_code.as_deref() => {}
|
||||
_ => return ApiError::InvalidInviteCode.into_response(),
|
||||
}
|
||||
} else {
|
||||
let invite_code_required = tranquil_config::get().server.invite_code_required;
|
||||
if invite_code_required
|
||||
&& input
|
||||
.invite_code
|
||||
.as_ref()
|
||||
.map(|c| c.trim().is_empty())
|
||||
.unwrap_or(true)
|
||||
{
|
||||
return ApiError::InviteCodeRequired.into_response();
|
||||
}
|
||||
if let Some(code) = &input.invite_code
|
||||
&& !code.trim().is_empty()
|
||||
{
|
||||
let valid = match state.user_repo.check_and_consume_invite_code(code).await {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
error!("Error checking invite code: {:?}", e);
|
||||
return ApiError::InternalError(None).into_response();
|
||||
}
|
||||
};
|
||||
if !valid {
|
||||
return ApiError::InvalidInviteCode.into_response();
|
||||
}
|
||||
};
|
||||
if !valid {
|
||||
return ApiError::InvalidInviteCode.into_response();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -678,7 +688,11 @@ pub async fn create_account(
|
||||
commit_cid: commit_cid_str.clone(),
|
||||
repo_rev: rev_str.clone(),
|
||||
genesis_block_cids,
|
||||
invite_code: input.invite_code.clone(),
|
||||
invite_code: if is_bootstrap {
|
||||
None
|
||||
} else {
|
||||
input.invite_code.clone()
|
||||
},
|
||||
birthdate_pref,
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ use tracing::error;
|
||||
|
||||
const BASE32_ALPHABET: &[u8] = b"abcdefghijklmnopqrstuvwxyz234567";
|
||||
|
||||
fn gen_random_token() -> String {
|
||||
pub(crate) fn gen_random_token() -> String {
|
||||
let mut rng = rand::thread_rng();
|
||||
let gen_segment = |rng: &mut rand::rngs::ThreadRng, len: usize| -> String {
|
||||
(0..len)
|
||||
@@ -24,7 +24,7 @@ fn gen_random_token() -> String {
|
||||
format!("{}-{}", gen_segment(&mut rng, 5), gen_segment(&mut rng, 5))
|
||||
}
|
||||
|
||||
fn gen_invite_code() -> String {
|
||||
pub fn gen_invite_code() -> String {
|
||||
let hostname = &tranquil_config::get().server.hostname;
|
||||
let hostname_prefix = hostname.replace('.', "-");
|
||||
format!("{}-{}", hostname_prefix, gen_random_token())
|
||||
|
||||
@@ -146,7 +146,15 @@ pub async fn create_passkey_account(
|
||||
return ApiError::InvalidEmail.into_response();
|
||||
}
|
||||
|
||||
let _validated_invite_code = if let Some(ref code) = input.invite_code {
|
||||
let is_bootstrap = state.bootstrap_invite_code.is_some()
|
||||
&& state.user_repo.count_users().await.unwrap_or(1) == 0;
|
||||
|
||||
let _validated_invite_code = if is_bootstrap {
|
||||
match input.invite_code.as_deref() {
|
||||
Some(code) if Some(code) == state.bootstrap_invite_code.as_deref() => None,
|
||||
_ => return ApiError::InvalidInviteCode.into_response(),
|
||||
}
|
||||
} else if let Some(ref code) = input.invite_code {
|
||||
match state.infra_repo.validate_invite_code(code).await {
|
||||
Ok(validated) => Some(validated),
|
||||
Err(_) => return ApiError::InvalidInviteCode.into_response(),
|
||||
@@ -447,7 +455,11 @@ pub async fn create_passkey_account(
|
||||
commit_cid: commit_cid.to_string(),
|
||||
repo_rev: rev.as_ref().to_string(),
|
||||
genesis_block_cids,
|
||||
invite_code: input.invite_code.clone(),
|
||||
invite_code: if is_bootstrap {
|
||||
None
|
||||
} else {
|
||||
input.invite_code.clone()
|
||||
},
|
||||
birthdate_pref,
|
||||
};
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ pub struct AppState {
|
||||
pub sso_manager: SsoManager,
|
||||
pub webauthn_config: Arc<WebAuthnConfig>,
|
||||
pub shutdown: CancellationToken,
|
||||
pub bootstrap_invite_code: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -232,7 +233,26 @@ impl AppState {
|
||||
.await
|
||||
.map_err(|e| format!("Failed to run migrations: {}", e))?;
|
||||
|
||||
Ok(Self::from_db(db, shutdown).await)
|
||||
let bootstrap_invite_code = match (
|
||||
cfg.server.invite_code_required,
|
||||
sqlx::query_scalar!("SELECT COUNT(*) FROM users")
|
||||
.fetch_one(&db)
|
||||
.await,
|
||||
) {
|
||||
(true, Ok(Some(0))) => {
|
||||
let code = crate::api::server::invite::gen_invite_code();
|
||||
tracing::info!(
|
||||
"No users exist and invite codes are required. Bootstrap invite code: {}",
|
||||
code
|
||||
);
|
||||
Some(code)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let mut state = Self::from_db(db, shutdown).await;
|
||||
state.bootstrap_invite_code = bootstrap_invite_code;
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub async fn from_db(db: PgPool, shutdown: CancellationToken) -> Self {
|
||||
@@ -285,6 +305,7 @@ impl AppState {
|
||||
sso_manager,
|
||||
webauthn_config,
|
||||
shutdown,
|
||||
bootstrap_invite_code: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user