v3 : dixième relecture — mention « sur invitation », noms attendus protégés

- Tout auteur venu par invitation porte « sur invitation » à côté de son
  nom (propositions, révisions, signalements) : un nom pris avant l'arrivée
  de son titulaire Ğ1, ou une lettre sosie, ne passe pas pour un membre de
  la toile.
- Un lien d'équipe refuse aussi le nom qu'une invitation attend encore.

Boucle : 68 tests Rust, svelte-check, 9 vitest, e2e 13/13.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-10-21 09:06:15 +02:00
co-authored by Claude Opus 5.5
parent 753b52202b
commit 8a3216037c
13 changed files with 67 additions and 17 deletions
+10 -1
View File
@@ -97,7 +97,16 @@ pub struct JoinBody {
/// `name_key` settles two openings at the same instant.
async fn free_name(state: &AppState, raw: &str) -> AppResult<names::Name> {
let name = names::check(raw).ok_or(AppError::Invalid("name"))?;
let taken: Vec<String> = sqlx::query_scalar("SELECT display_name FROM identities").fetch_all(&state.db).await?;
// People already there, and those an invitation still waits for.
let mut taken: Vec<String> = sqlx::query_scalar("SELECT display_name FROM identities").fetch_all(&state.db).await?;
taken.extend(
sqlx::query_scalar::<_, String>(
"SELECT label FROM invitations WHERE used_at IS NULL AND identity_id IS NULL AND expires_at > ?",
)
.bind(util::now())
.fetch_all(&state.db)
.await?,
);
if taken.iter().any(|t| names::key(t) == name.key) {
return Err(AppError::Conflict("name_taken"));
}