This commit is contained in:
2026-08-16 20:48:53 +02:00
parent c48faa4d6e
commit 07f4a76d74
10 changed files with 83 additions and 306 deletions
+21 -69
View File
@@ -1,7 +1,7 @@
use crate::http::error::HTTPError;
use crate::models::emoji;
use crate::services::ServicesContext;
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, Set, TransactionTrait};
use sea_orm::{ActiveModelTrait, Set, TransactionTrait};
use sha2::{Digest, Sha256};
use std::{
path::{Path, PathBuf},
@@ -19,32 +19,23 @@ impl EmojiService {
pub fn new(context: Arc<ServicesContext>) -> Self {
Self { context }
}
pub fn normalize_alias(alias: &str) -> Result<String, HTTPError> {
let alias = alias.trim().trim_matches(':').to_lowercase();
if alias.is_empty()
|| alias.len() > 64
|| !alias
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '+')
pub fn normalize_name(name: &str) -> Result<String, HTTPError> {
let name = name.trim().trim_matches(':').to_lowercase();
if name.is_empty()
|| name.len() > 64
|| !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
{
return Err(HTTPError::BadRequest("Invalid emoji alias".into()));
return Err(HTTPError::BadRequest("Invalid emoji name".into()));
}
Ok(alias)
Ok(name)
}
pub async fn aliases_available(
pub async fn name_available(
&self,
aliases: &[String],
name: &str,
server_id: Option<Uuid>,
except: Option<Uuid>,
) -> Result<(), HTTPError> {
let normalized = aliases
.iter()
.map(|a| Self::normalize_alias(a))
.collect::<Result<Vec<_>, _>>()?;
let mut unique = std::collections::HashSet::new();
if normalized.iter().any(|alias| !unique.insert(alias)) {
return Err(HTTPError::BadRequest("Duplicate emoji alias".into()));
}
let name = Self::normalize_name(name)?;
let scoped = self
.context
.repositories
@@ -55,74 +46,35 @@ impl EmojiService {
if Some(model.id) == except {
continue;
}
for alias in self.context.repositories.emoji.aliases(model.id).await? {
if normalized.iter().any(|candidate| candidate == &alias.alias) {
return Err(HTTPError::BadRequest(
"Emoji alias already exists in this scope".into(),
));
}
if model.name == name {
return Err(HTTPError::BadRequest(
"Emoji name already exists in this scope".into(),
));
}
}
Ok(())
}
pub async fn create(
&self,
model: emoji::ActiveModel,
aliases: Vec<String>,
mut model: emoji::ActiveModel,
name: String,
) -> Result<emoji::Model, HTTPError> {
let db = &self.context.repositories.emoji.context.db;
let aliases = aliases
.into_iter()
.map(|a| Self::normalize_alias(&a))
.collect::<Result<Vec<_>, _>>()?;
let name = Self::normalize_name(&name)?;
let server_id = match &model.server_id {
sea_orm::ActiveValue::Set(value) => *value,
_ => None,
};
self.aliases_available(&aliases, server_id, None).await?;
self.name_available(&name, server_id, None).await?;
model.name = Set(name);
let result = db
.transaction::<_, emoji::Model, anyhow::Error>(|txn| {
Box::pin(async move {
let model = model.insert(txn).await?;
for alias in aliases {
crate::models::emoji_alias::ActiveModel {
emoji_id: Set(model.id),
alias: Set(alias),
..Default::default()
}
.insert(txn)
.await?;
}
Ok(model)
})
Box::pin(async move { Ok(model.insert(txn).await?) })
})
.await
.map_err(|e| HTTPError::Internal(anyhow::anyhow!(e)))?;
Ok(result)
}
pub async fn replace_aliases(&self, id: Uuid, aliases: Vec<String>) -> Result<(), HTTPError> {
let aliases = aliases
.into_iter()
.map(|a| Self::normalize_alias(&a))
.collect::<Result<Vec<_>, _>>()?;
let db = &self.context.repositories.emoji.context.db;
let txn = db.begin().await?;
crate::models::emoji_alias::Entity::delete_many()
.filter(crate::models::emoji_alias::Column::EmojiId.eq(id))
.exec(&txn)
.await?;
for alias in aliases {
crate::models::emoji_alias::ActiveModel {
emoji_id: Set(id),
alias: Set(alias),
..Default::default()
}
.insert(&txn)
.await?;
}
txn.commit().await?;
Ok(())
}
pub fn hash(data: &[u8]) -> String {
Sha256::digest(data)
.iter()