fedimovies/src/mastodon_api/accounts/types.rs

259 lines
7.1 KiB
Rust
Raw Normal View History

2021-11-13 17:37:31 +00:00
use std::path::Path;
2021-04-09 00:22:17 +00:00
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::errors::ValidationError;
2021-09-16 14:34:24 +00:00
use crate::models::profiles::types::{
DbActorProfile,
ExtraField,
ProfileUpdateData,
};
use crate::models::profiles::validators::validate_username;
use crate::models::users::types::{
validate_local_username,
validate_wallet_address,
User,
UserCreateData,
};
2021-04-09 00:22:17 +00:00
use crate::utils::files::{FileError, save_validated_b64_file, get_file_url};
#[derive(Serialize)]
pub struct AccountField {
pub name: String,
pub value: String,
}
2021-04-09 00:22:17 +00:00
/// https://docs.joinmastodon.org/entities/source/
#[derive(Serialize)]
pub struct Source {
pub note: Option<String>,
pub fields: Vec<AccountField>,
2021-04-09 00:22:17 +00:00
}
/// https://docs.joinmastodon.org/entities/account/
#[derive(Serialize)]
pub struct Account {
pub id: Uuid,
pub username: String,
pub acct: String,
2022-01-15 00:18:17 +00:00
pub url: String,
2021-04-09 00:22:17 +00:00
pub display_name: Option<String>,
pub created_at: DateTime<Utc>,
pub note: Option<String>,
pub avatar: Option<String>,
pub header: Option<String>,
pub fields: Vec<AccountField>,
2021-04-09 00:22:17 +00:00
pub followers_count: i32,
pub following_count: i32,
pub statuses_count: i32,
pub source: Option<Source>,
pub wallet_address: Option<String>,
2021-04-09 00:22:17 +00:00
}
impl Account {
pub fn from_profile(profile: DbActorProfile, instance_url: &str) -> Self {
2022-01-15 00:18:17 +00:00
let profile_url = profile.actor_url(instance_url);
let avatar_url = profile.avatar_file_name.as_ref()
2021-11-13 17:37:31 +00:00
.map(|name| get_file_url(instance_url, name));
let header_url = profile.banner_file_name.as_ref()
2021-11-13 17:37:31 +00:00
.map(|name| get_file_url(instance_url, name));
let fields = profile.extra_fields.unpack().into_iter()
.map(|field| AccountField { name: field.name, value: field.value })
.collect();
2021-04-09 00:22:17 +00:00
Self {
id: profile.id,
username: profile.username,
acct: profile.acct,
2022-01-15 00:18:17 +00:00
url: profile_url,
2021-04-09 00:22:17 +00:00
display_name: profile.display_name,
created_at: profile.created_at,
note: profile.bio,
avatar: avatar_url,
header: header_url,
fields,
2021-04-09 00:22:17 +00:00
followers_count: profile.follower_count,
following_count: profile.following_count,
statuses_count: profile.post_count,
source: None,
wallet_address: None,
2021-04-09 00:22:17 +00:00
}
}
pub fn from_user(user: User, instance_url: &str) -> Self {
let fields_sources = user.profile.extra_fields.clone()
.unpack().into_iter()
.map(|field| AccountField {
name: field.name,
value: field.value_source.unwrap_or(field.value),
})
.collect();
let source = Source {
note: user.profile.bio_source.clone(),
fields: fields_sources,
};
let mut account = Self::from_profile(user.profile, instance_url);
account.source = Some(source);
account.wallet_address = user.wallet_address;
account
}
2021-04-09 00:22:17 +00:00
}
/// https://docs.joinmastodon.org/methods/accounts/
2021-10-05 20:57:24 +00:00
#[derive(Deserialize)]
pub struct AccountCreateData {
2022-01-06 19:06:35 +00:00
pub username: String,
pub password: String,
2021-10-05 20:57:24 +00:00
2022-01-06 19:06:35 +00:00
pub wallet_address: Option<String>,
pub invite_code: Option<String>,
2021-10-05 20:57:24 +00:00
}
impl AccountCreateData {
pub fn clean(&self) -> Result<(), ValidationError> {
validate_username(&self.username)?;
validate_local_username(&self.username)?;
if let Some(wallet_address) = self.wallet_address.as_ref() {
validate_wallet_address(wallet_address)?;
};
Ok(())
}
2022-01-06 19:06:35 +00:00
pub fn into_user_data(
self,
password_hash: String,
private_key_pem: String,
) -> UserCreateData {
2021-10-05 20:57:24 +00:00
UserCreateData {
username: self.username,
2022-01-06 19:06:35 +00:00
password_hash,
private_key_pem,
2021-10-05 20:57:24 +00:00
wallet_address: self.wallet_address,
invite_code: self.invite_code,
}
}
}
2021-04-09 00:22:17 +00:00
#[derive(Deserialize)]
pub struct AccountUpdateData {
pub display_name: Option<String>,
pub note: Option<String>,
pub note_source: Option<String>,
pub avatar: Option<String>,
pub header: Option<String>,
2021-09-16 14:34:24 +00:00
pub fields_attributes: Option<Vec<ExtraField>>,
2021-04-09 00:22:17 +00:00
}
fn process_b64_image_field_value(
form_value: Option<String>,
db_value: Option<String>,
2021-11-13 17:37:31 +00:00
output_dir: &Path,
2021-04-09 00:22:17 +00:00
) -> Result<Option<String>, FileError> {
let maybe_file_name = match form_value {
Some(b64_data) => {
2021-11-13 17:37:31 +00:00
if b64_data.is_empty() {
2021-04-09 00:22:17 +00:00
// Remove file
None
} else {
// Decode and save file
let (file_name, _) = save_validated_b64_file(
2021-11-13 17:37:31 +00:00
&b64_data, output_dir, "image/",
2021-04-09 00:22:17 +00:00
)?;
Some(file_name)
}
},
// Keep current value
None => db_value,
};
Ok(maybe_file_name)
}
impl AccountUpdateData {
pub fn into_profile_data(
self,
current_avatar: &Option<String>,
current_banner: &Option<String>,
2021-11-13 17:37:31 +00:00
media_dir: &Path,
2021-04-09 00:22:17 +00:00
) -> Result<ProfileUpdateData, FileError> {
let avatar = process_b64_image_field_value(
self.avatar, current_avatar.clone(), media_dir,
)?;
let banner = process_b64_image_field_value(
self.header, current_banner.clone(), media_dir,
)?;
2021-09-16 14:34:24 +00:00
let extra_fields = self.fields_attributes.unwrap_or(vec![]);
2021-04-09 00:22:17 +00:00
let profile_data = ProfileUpdateData {
display_name: self.display_name,
bio: self.note,
bio_source: self.note_source,
avatar,
banner,
2021-09-16 14:34:24 +00:00
extra_fields,
actor_json: None,
2021-04-09 00:22:17 +00:00
};
Ok(profile_data)
}
}
fn default_page_size() -> i64 { 40 }
#[derive(Deserialize)]
pub struct FollowListQueryParams {
pub max_id: Option<i32>,
#[serde(default = "default_page_size")]
pub limit: i64,
}
#[cfg(test)]
mod tests {
use super::*;
const INSTANCE_URL: &str = "https://example.com";
#[test]
fn test_create_account_from_profile() {
let profile = DbActorProfile {
avatar_file_name: Some("test".to_string()),
..Default::default()
};
let account = Account::from_profile(profile, INSTANCE_URL);
assert_eq!(
account.avatar.unwrap(),
format!("{}/media/test", INSTANCE_URL),
);
assert!(account.source.is_none());
assert!(account.wallet_address.is_none());
}
#[test]
fn test_create_account_from_user() {
let bio_source = "test";
let wallet_address = "0x1234";
let profile = DbActorProfile {
bio_source: Some(bio_source.to_string()),
..Default::default()
};
let user = User {
wallet_address: Some(wallet_address.to_string()),
profile,
..Default::default()
};
let account = Account::from_user(user, INSTANCE_URL);
assert_eq!(
account.source.unwrap().note.unwrap(),
bio_source,
);
assert_eq!(
account.wallet_address.unwrap(),
wallet_address,
);
}
}