summaryrefslogtreecommitdiffstats
path: root/src/entity/user.rs
diff options
context:
space:
mode:
authorrtkay123 <dev@kanjala.com>2025-07-17 14:00:40 +0200
committerrtkay123 <dev@kanjala.com>2025-07-17 14:00:40 +0200
commit69fe55ad54468948c13af520a498ed4aeac194ed (patch)
treec7db25aa7cf615480e9b386064f232d2bec6ccc3 /src/entity/user.rs
parent5fdb24b6a2cef7964a049e789ed90f883221d657 (diff)
downloadsellershut-69fe55ad54468948c13af520a498ed4aeac194ed.tar.bz2
sellershut-69fe55ad54468948c13af520a498ed4aeac194ed.zip
chore: convert to workspace
Diffstat (limited to 'src/entity/user.rs')
-rw-r--r--src/entity/user.rs316
1 files changed, 0 insertions, 316 deletions
diff --git a/src/entity/user.rs b/src/entity/user.rs
deleted file mode 100644
index 1abf50f..0000000
--- a/src/entity/user.rs
+++ /dev/null
@@ -1,316 +0,0 @@
-pub mod followers;
-
-use std::fmt::Display;
-
-use activitypub_federation::{
- activity_queue::queue_activity,
- activity_sending::SendActivityTask,
- config::Data,
- fetch::object_id::ObjectId,
- http_signatures::generate_actor_keypair,
- kinds::actor::{ApplicationType, GroupType, OrganizationType, PersonType, ServiceType},
- protocol::{context::WithContext, public_key::PublicKey},
- traits::{Activity, Actor, Object},
-};
-use async_trait::async_trait;
-use serde::{Deserialize, Serialize};
-use sqlx::types::time::OffsetDateTime;
-use stack_up::{Environment, Services};
-use tracing::trace;
-use url::Url;
-use uuid::Uuid;
-
-use crate::{error::AppError, state::AppHandle};
-
-#[derive(PartialEq, Clone, Debug)]
-pub struct User {
- pub id: String,
- pub username: String,
- pub ap_id: ObjectId<User>,
- pub private_key: Option<String>,
- pub description: Option<String>,
- pub avatar_url: Option<String>,
- pub public_key: String,
- pub inbox: Url,
- pub outbox: Option<Url>,
- pub user_type: UserType,
-}
-
-pub struct DbUser {
- pub id: String,
- pub description: Option<String>,
- pub username: String,
- pub ap_id: String,
- pub private_key: Option<String>,
- pub public_key: String,
- pub inbox: String,
- pub outbox: Option<String>,
- pub avatar_url: Option<String>,
- pub local: bool,
- pub updated_at: OffsetDateTime,
- pub created_at: OffsetDateTime,
- pub user_type: UserType,
-}
-
-#[derive(Deserialize, Serialize, Debug, Clone, PartialEq)]
-#[serde(rename_all = "PascalCase")]
-#[serde(untagged)]
-pub enum UserType {
- Person(PersonType),
- Application(ApplicationType),
- Group(GroupType),
- Organization(OrganizationType),
- Service(ServiceType),
-}
-
-impl Display for UserType {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(
- f,
- "{}",
- match self {
- UserType::Person(person) => person.to_string(),
- UserType::Application(application_type) => application_type.to_string(),
- UserType::Group(group_type) => group_type.to_string(),
- UserType::Organization(organization_type) => organization_type.to_string(),
- UserType::Service(service_type) => service_type.to_string(),
- }
- .to_uppercase()
- )
- }
-}
-
-impl From<String> for UserType {
- fn from(value: String) -> Self {
- match value.to_lowercase().as_str() {
- "person" => Self::Person(PersonType::Person),
- "application" => Self::Application(ApplicationType::Application),
- "group" => Self::Group(GroupType::Group),
- "organization" => Self::Organization(OrganizationType::Organization),
- "service" => Self::Service(ServiceType::Service),
- _ => unreachable!("{}", value),
- }
- }
-}
-
-impl TryFrom<DbUser> for User {
- type Error = AppError;
- fn try_from(value: DbUser) -> Result<Self, Self::Error> {
- Ok(Self {
- id: value.id,
- username: value.username,
- ap_id: Url::parse(&value.ap_id)?.into(),
- private_key: value.private_key,
- public_key: value.public_key,
- inbox: Url::parse(&value.inbox)?,
- outbox: match value.outbox {
- Some(ref url) => Some(Url::parse(url)?),
- None => None,
- },
- description: value.description,
- avatar_url: value.avatar_url,
- user_type: value.user_type,
- })
- }
-}
-
-impl User {
- pub async fn new(
- username: &str,
- hostname: &str,
- services: &Services,
- environment: Environment,
- ) -> Result<Self, AppError> {
- trace!(username = ?username, "checking for system user");
-
- let user = sqlx::query_as!(
- DbUser,
- "select * from account where username = $1 and local = $2",
- username,
- true
- )
- .fetch_optional(&services.postgres)
- .await?;
-
- if let Some(user) = user {
- trace!(username = ?username, "system user exists");
- return Self::try_from(user);
- } else {
- trace!(username = ?username, "system user does not exist. creating");
- }
-
- trace!("creating keypair for new user");
- let keys = generate_actor_keypair()?;
- let stub = &format!(
- "{}://{hostname}/users/{username}",
- match environment {
- Environment::Development => "http",
- Environment::Production => "https",
- }
- );
- let id = Uuid::now_v7();
-
- let kind = UserType::Service(ServiceType::Service);
-
- trace!(id = ?id, "creating a new user");
- let user = sqlx::query_as!(
- DbUser,
- "insert into account (id, username, ap_id, private_key, public_key, inbox, outbox, local, user_type) values ($1, $2, $3, $4, $5, $6, $7, $8, $9) returning *",
- id,
- username,
- stub,
- keys.private_key,
- keys.public_key,
- &format!("{stub}/inbox"),
- &format!("{stub}/outbox"),
- true,
- kind.to_string(),
- ).fetch_one(&services.postgres).await?;
- Self::try_from(user)
- }
-
- pub(crate) async fn send<A>(
- &self,
- activity: A,
- recipients: Vec<Url>,
- use_queue: bool,
- data: &Data<AppHandle>,
- ) -> Result<(), AppError>
- where
- A: Activity + Serialize + std::fmt::Debug + Send + Sync,
- <A as Activity>::Error: From<anyhow::Error> + From<serde_json::Error>,
- {
- let activity = WithContext::new_default(activity);
- // Send through queue in some cases and bypass it in others to test both code paths
- if use_queue {
- queue_activity(&activity, self, recipients, data).await?;
- } else {
- let sends = SendActivityTask::prepare(&activity, self, recipients, data).await?;
- for send in sends {
- send.sign_and_send(data).await?;
- }
- }
- Ok(())
- }
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct Person {
- #[serde(rename = "type")]
- kind: UserType,
- preferred_username: String,
- id: ObjectId<User>,
- inbox: Url,
- public_key: PublicKey,
- #[serde(skip_serializing_if = "Option::is_none")]
- outbox: Option<Url>,
- followers: Url,
- following: Url,
- #[serde(skip_serializing_if = "Option::is_none")]
- summary: Option<String>,
- #[serde(skip_serializing_if = "Option::is_none")]
- image: Option<Url>,
-}
-
-#[async_trait]
-impl Object for User {
- #[doc = " App data type passed to handlers. Must be identical to"]
- #[doc = " [crate::config::FederationConfigBuilder::app_data] type."]
- type DataType = AppHandle;
-
- #[doc = " The type of protocol struct which gets sent over network to federate this database struct."]
- type Kind = Person;
-
- #[doc = " Error type returned by handler methods"]
- type Error = AppError;
-
- #[doc = " `id` field of the object"]
- fn id(&self) -> &Url {
- self.ap_id.inner()
- }
-
- #[doc = " Try to read the object with given `id` from local database."]
- #[doc = " Should return `Ok(None)` if not found."]
- async fn read_from_id(
- object_id: Url,
- data: &Data<Self::DataType>,
- ) -> Result<Option<Self>, Self::Error> {
- let id = object_id.as_str();
- let result = sqlx::query_as!(DbUser, "select * from account where ap_id = $1", id)
- .fetch_optional(&data.services.postgres)
- .await?;
- let user = match result {
- Some(user) => Some(User::try_from(user)?),
- None => None,
- };
- Ok(user)
- }
-
- #[doc = " Convert database type to Activitypub type."]
- #[doc = " Called when a local object gets fetched by another instance over HTTP, or when an object"]
- #[doc = " gets sent in an activity."]
- async fn into_json(self, data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
- Ok(Person {
- preferred_username: self.username.clone(),
- kind: self.user_type.clone(),
- id: self.ap_id.clone(),
- inbox: self.inbox.clone(),
- public_key: self.public_key(),
- outbox: self.outbox.clone(),
- followers: Url::parse(&format!("{}/followers", self.ap_id))?,
- following: Url::parse(&format!("{}/following", self.ap_id))?,
- summary: self.description,
- image: match self.avatar_url {
- Some(ref v) => Some(Url::parse(v)?),
- None => None,
- },
- })
- }
-
- #[doc = " Verifies that the received object is valid."]
- #[doc = " You should check here that the domain of id matches `expected_domain`. Additionally you"]
- #[doc = " should perform any application specific checks."]
- #[doc = " It is necessary to use a separate method for this, because it might be used for activities"]
- #[doc = " like `Delete/Note`, which shouldn\'t perform any database write for the inner `Note`."]
- async fn verify(
- json: &Self::Kind,
- expected_domain: &Url,
- data: &Data<Self::DataType>,
- ) -> Result<(), Self::Error> {
- todo!()
- }
-
- #[doc = " Convert object from ActivityPub type to database type."]
- #[doc = " Called when an object is received from HTTP fetch or as part of an activity. This method"]
- #[doc = " should write the received object to database. Note that there is no distinction between"]
- #[doc = " create and update, so an `upsert` operation should be used."]
- async fn from_json(json: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, Self::Error> {
- Ok(Self {
- id: todo!(),
- username: todo!(),
- ap_id: todo!(),
- private_key: todo!(),
- description: todo!(),
- avatar_url: todo!(),
- public_key: todo!(),
- inbox: todo!(),
- outbox: todo!(),
- user_type: todo!(),
- })
- }
-}
-
-impl Actor for User {
- fn public_key_pem(&self) -> &str {
- &self.public_key
- }
-
- fn private_key_pem(&self) -> Option<String> {
- self.private_key.clone()
- }
-
- fn inbox(&self) -> Url {
- self.inbox.clone()
- }
-}