summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cnfg.rs8
-rw-r--r--src/entity.rs1
-rw-r--r--src/entity/user.rs316
-rw-r--r--src/entity/user/followers.rs1
-rw-r--r--src/error.rs26
-rw-r--r--src/main.rs57
-rw-r--r--src/server.rs49
-rw-r--r--src/server/activities.rs2
-rw-r--r--src/server/activities/accept.rs52
-rw-r--r--src/server/activities/follow.rs91
-rw-r--r--src/server/routes.rs39
-rw-r--r--src/server/routes/users.rs30
-rw-r--r--src/server/routes/users/get_outbox.rs77
-rw-r--r--src/server/routes/users/get_user.rs100
-rw-r--r--src/server/routes/users/post_inbox.rs16
-rw-r--r--src/server/routes/users/webfinger.rs86
-rw-r--r--src/state.rs54
17 files changed, 0 insertions, 1005 deletions
diff --git a/src/cnfg.rs b/src/cnfg.rs
deleted file mode 100644
index 4ad7a06..0000000
--- a/src/cnfg.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-use serde::Deserialize;
-
-#[derive(Deserialize, Clone)]
-#[serde(rename_all = "kebab-case")]
-pub struct LocalConfig {
- pub hostname: String,
- pub instance_name: String,
-}
diff --git a/src/entity.rs b/src/entity.rs
deleted file mode 100644
index 22d12a3..0000000
--- a/src/entity.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod user;
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()
- }
-}
diff --git a/src/entity/user/followers.rs b/src/entity/user/followers.rs
deleted file mode 100644
index 8b13789..0000000
--- a/src/entity/user/followers.rs
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/error.rs b/src/error.rs
deleted file mode 100644
index 730f99a..0000000
--- a/src/error.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-use axum::{
- http::StatusCode,
- response::{IntoResponse, Response},
-};
-
-#[derive(Debug)]
-pub struct AppError(anyhow::Error);
-
-impl IntoResponse for AppError {
- fn into_response(self) -> Response {
- (
- StatusCode::INTERNAL_SERVER_ERROR,
- format!("Something went wrong: {}", self.0),
- )
- .into_response()
- }
-}
-
-impl<E> From<E> for AppError
-where
- E: Into<anyhow::Error>,
-{
- fn from(err: E) -> Self {
- Self(err.into())
- }
-}
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index f0540bf..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-mod cnfg;
-mod entity;
-mod error;
-mod server;
-mod state;
-
-use std::net::{Ipv6Addr, SocketAddr};
-
-use clap::Parser;
-use stack_up::{Configuration, Services, tracing::Tracing};
-
-use crate::{error::AppError, state::AppState};
-use tracing::{error, info};
-
-/// sellershut
-#[derive(Parser, Debug)]
-#[command(version, about, long_about = None)]
-struct Args {
- /// Path to config file
- #[arg(short, long)]
- config_file: Option<std::path::PathBuf>,
-}
-
-#[tokio::main]
-async fn main() -> Result<(), AppError> {
- let args = Args::parse();
- let config = include_str!("../sellershut.toml");
-
- let mut config = config::Config::builder()
- .add_source(config::File::from_str(config, config::FileFormat::Toml));
-
- if let Some(cf) = args.config_file.as_ref().and_then(|v| v.to_str()) {
- config = config.add_source(config::File::new(cf, config::FileFormat::Toml));
- };
-
- let mut config: Configuration = config.build()?.try_deserialize()?;
- config.application.name = env!("CARGO_CRATE_NAME").into();
- config.application.version = env!("CARGO_PKG_VERSION").into();
-
- let _tracing = Tracing::builder().build(&config.monitoring);
-
- let services = Services::builder()
- .postgres(&config.database)
- .await
- .inspect_err(|e| error!("database: {e}"))?
- .build();
-
- let state = AppState::create(services, &config).await?;
-
- let addr = SocketAddr::from((Ipv6Addr::UNSPECIFIED, config.application.port));
-
- let listener = tokio::net::TcpListener::bind(addr).await?;
- info!(port = addr.port(), "serving api");
-
- axum::serve(listener, server::router(state)).await?;
- Ok(())
-}
diff --git a/src/server.rs b/src/server.rs
deleted file mode 100644
index dd49a54..0000000
--- a/src/server.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-use activitypub_federation::config::{FederationConfig, FederationMiddleware};
-use axum::{Router, routing::get};
-use nanoid::nanoid;
-use stack_up::Environment;
-use tower_http::trace::TraceLayer;
-use url::Url;
-
-use crate::{error::AppError, server::routes::health_check, state::AppHandle};
-
-pub mod activities;
-pub mod routes;
-
-const ALPHABET: [char; 36] = [
- '2', '3', '4', '5', '6', '7', '8', '9', '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
- 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '-',
-];
-
-pub fn generate_object_id(domain: &str, env: Environment) -> Result<Url, AppError> {
- let id = nanoid!(21, &ALPHABET);
- Ok(Url::parse(&format!(
- "{}://{domain}/objects/{id}",
- match env {
- Environment::Development => "http",
- Environment::Production => "https",
- },
- ))?)
-}
-
-pub fn router(state: FederationConfig<AppHandle>) -> Router {
- Router::new()
- .merge(routes::users::users_router())
- .route("/", get(health_check))
- .layer(TraceLayer::new_for_http())
- .layer(FederationMiddleware::new(state))
-}
-
-#[cfg(test)]
-pub(crate) fn test_config() -> stack_up::Configuration {
- use stack_up::Configuration;
-
- let config_path = "sellershut.toml";
-
- let config = config::Config::builder()
- .add_source(config::File::new(config_path, config::FileFormat::Toml))
- .build()
- .unwrap();
-
- config.try_deserialize::<Configuration>().unwrap()
-}
diff --git a/src/server/activities.rs b/src/server/activities.rs
deleted file mode 100644
index 5e2ad4b..0000000
--- a/src/server/activities.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod accept;
-pub mod follow;
diff --git a/src/server/activities/accept.rs b/src/server/activities/accept.rs
deleted file mode 100644
index 44f26f6..0000000
--- a/src/server/activities/accept.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-use crate::{
- entity::user::User, error::AppError, server::activities::follow::Follow, state::AppHandle,
-};
-use activitypub_federation::{
- config::Data, fetch::object_id::ObjectId, kinds::activity::AcceptType, traits::Activity,
-};
-use async_trait::async_trait;
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Deserialize, Serialize, Debug)]
-#[serde(rename_all = "camelCase")]
-pub struct Accept {
- actor: ObjectId<User>,
- object: Follow,
- #[serde(rename = "type")]
- kind: AcceptType,
- id: Url,
-}
-
-impl Accept {
- pub fn new(actor: ObjectId<User>, object: Follow, id: Url) -> Accept {
- Accept {
- actor,
- object,
- kind: Default::default(),
- id,
- }
- }
-}
-
-#[async_trait]
-impl Activity for Accept {
- type DataType = AppHandle;
- type Error = AppError;
-
- fn id(&self) -> &Url {
- &self.id
- }
-
- fn actor(&self) -> &Url {
- self.actor.inner()
- }
-
- async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
- Ok(())
- }
-
- async fn receive(self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
- Ok(())
- }
-}
diff --git a/src/server/activities/follow.rs b/src/server/activities/follow.rs
deleted file mode 100644
index 466edb7..0000000
--- a/src/server/activities/follow.rs
+++ /dev/null
@@ -1,91 +0,0 @@
-use activitypub_federation::{
- config::Data,
- fetch::object_id::ObjectId,
- kinds::activity::FollowType,
- traits::{Activity, Actor},
-};
-use async_trait::async_trait;
-use serde::{Deserialize, Serialize};
-use url::Url;
-use uuid::Uuid;
-
-use crate::{
- entity::user::User,
- error::AppError,
- server::{activities::accept::Accept, generate_object_id},
- state::AppHandle,
-};
-
-#[derive(Deserialize, Serialize, Clone, Debug)]
-#[serde(rename_all = "camelCase")]
-pub struct Follow {
- pub actor: ObjectId<User>,
- pub object: ObjectId<User>,
- #[serde(rename = "type")]
- kind: FollowType,
- id: Url,
-}
-
-impl Follow {
- pub fn new(actor: ObjectId<User>, object: ObjectId<User>, id: Url) -> Follow {
- Follow {
- actor,
- object,
- kind: Default::default(),
- id,
- }
- }
-}
-
-#[async_trait]
-impl Activity for Follow {
- #[doc = " App data type passed to handlers. Must be identical to"]
- #[doc = " [crate::config::FederationConfigBuilder::app_data] type."]
- type DataType = AppHandle;
-
- #[doc = " Error type returned by handler methods"]
- type Error = AppError;
-
- #[doc = " `id` field of the activity"]
- fn id(&self) -> &Url {
- &self.id
- }
-
- #[doc = " `actor` field of activity"]
- fn actor(&self) -> &Url {
- self.actor.inner()
- }
-
- #[doc = " Verifies that the received activity is valid."]
- #[doc = ""]
- #[doc = " This needs to be a separate method, because it might be used for activities"]
- #[doc = " like `Undo/Follow`, which shouldn\'t perform any database write for the inner `Follow`."]
- async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
- Ok(())
- }
-
- #[doc = " Called when an activity is received."]
- #[doc = ""]
- #[doc = " Should perform validation and possibly write action to the database. In case the activity"]
- #[doc = " has a nested `object` field, must call `object.from_json` handler."]
- async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
- let id = Uuid::now_v7();
-
- sqlx::query!("insert into following (id, follower, followee) values ($1, $2, $3) on conflict (follower, followee) do nothing"
- ,id,
- self.actor.inner().as_str(),
- self.object.inner().as_str(),
- ).execute(&data.services.postgres).await?;
-
- let follower = self.actor.dereference(data).await?;
- let id = generate_object_id(data.domain(), data.environment)?;
-
- let local_user = self.object.dereference(data).await?;
- let accept = Accept::new(self.object.clone(), self, id.clone());
-
- local_user
- .send(accept, vec![follower.shared_inbox_or_inbox()], false, data)
- .await?;
- Ok(())
- }
-}
diff --git a/src/server/routes.rs b/src/server/routes.rs
deleted file mode 100644
index 9e1b9a9..0000000
--- a/src/server/routes.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-pub(super) mod users;
-use axum::response::IntoResponse;
-
-pub async fn health_check() -> impl IntoResponse {
- let name = env!("CARGO_PKG_NAME");
- let ver = env!("CARGO_PKG_VERSION");
-
- format!("{name} v{ver} is live")
-}
-
-#[cfg(test)]
-mod tests {
- use axum::{
- body::Body,
- http::{Request, StatusCode},
- };
- use sqlx::PgPool;
- use stack_up::Services;
- use tower::ServiceExt;
-
- use crate::{
- server::{self, test_config},
- state::AppState,
- };
-
- #[sqlx::test]
- async fn health_check(pool: PgPool) {
- let services = Services { postgres: pool };
- let state = AppState::create(services, &test_config()).await.unwrap();
- let app = server::router(state);
-
- let response = app
- .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
- .await
- .unwrap();
-
- assert_eq!(response.status(), StatusCode::OK);
- }
-}
diff --git a/src/server/routes/users.rs b/src/server/routes/users.rs
deleted file mode 100644
index 9c9a3bf..0000000
--- a/src/server/routes/users.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-pub mod get_outbox;
-pub mod post_inbox;
-pub mod get_user;
-pub mod webfinger;
-
-use activitypub_federation::traits::Activity;
-use axum::{routing::{get, post}, Router};
-use serde::{Deserialize, Serialize};
-
-use crate::server::activities::{accept::Accept, follow::Follow};
-use url::Url;
-use activitypub_federation::config::Data;
-
-/// List of all activities which this actor can receive.
-#[derive(Deserialize, Serialize, Debug)]
-#[serde(untagged)]
-#[enum_delegate::implement(Activity)]
-pub enum PersonAcceptedActivities {
- Follow(Follow),
- Accept(Accept),
-}
-
-
-pub fn users_router() -> Router {
- Router::new()
- .route("/users/{username}", get(get_user::http_get_user))
- .route("/users/{username}/outbox", get(get_outbox::http_get_outbox))
- .route("/users/{username}/inbox", post(post_inbox::http_post_user_inbox))
- .route("/.well-known/webfinger", get(webfinger::webfinger))
-}
diff --git a/src/server/routes/users/get_outbox.rs b/src/server/routes/users/get_outbox.rs
deleted file mode 100644
index 75467af..0000000
--- a/src/server/routes/users/get_outbox.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-use activitypub_federation::{
- axum::json::FederationJson, config::Data, protocol::context::WithContext, traits::Object,
-};
-use axum::{debug_handler, extract::Path, http::StatusCode, response::IntoResponse};
-
-use crate::{error::AppError, state::AppHandle};
-
-#[debug_handler]
-pub async fn http_get_outbox(
- Path(name): Path<String>,
- data: Data<AppHandle>,
-) -> Result<impl IntoResponse, AppError> {
- if let Some(a) = super::get_user::read_user(&name, &data).await? {
- let json_user = a.into_json(&data).await?;
- Ok((
- StatusCode::OK,
- FederationJson(WithContext::new_default(json_user)),
- )
- .into_response())
- } else {
- Ok((StatusCode::NOT_FOUND, "").into_response())
- }
-}
-
-#[cfg(test)]
-mod tests {
- use axum::{
- body::Body,
- http::{Request, StatusCode},
- };
- use sqlx::PgPool;
- use stack_up::Services;
- use tower::ServiceExt;
-
- use crate::{
- server::{self, test_config},
- state::AppState,
- };
-
- #[sqlx::test]
- async fn get_user(pool: PgPool) {
- let services = Services { postgres: pool };
- let state = AppState::create(services, &test_config()).await.unwrap();
- let app = server::router(state);
-
- let response = app
- .oneshot(
- Request::builder()
- .uri("/users/sellershut")
- .body(Body::empty())
- .unwrap(),
- )
- .await
- .unwrap();
-
- assert_eq!(response.status(), StatusCode::OK);
- }
-
- #[sqlx::test]
- async fn get_user_not_found(pool: PgPool) {
- let services = Services { postgres: pool };
- let state = AppState::create(services, &test_config()).await.unwrap();
- let app = server::router(state);
-
- let response = app
- .oneshot(
- Request::builder()
- .uri("/users/selut")
- .body(Body::empty())
- .unwrap(),
- )
- .await
- .unwrap();
-
- assert_eq!(response.status(), StatusCode::NOT_FOUND);
- }
-}
diff --git a/src/server/routes/users/get_user.rs b/src/server/routes/users/get_user.rs
deleted file mode 100644
index fc2803e..0000000
--- a/src/server/routes/users/get_user.rs
+++ /dev/null
@@ -1,100 +0,0 @@
-use activitypub_federation::{
- axum::json::FederationJson, config::Data, protocol::context::WithContext, traits::Object,
-};
-use axum::{debug_handler, extract::Path, http::StatusCode, response::IntoResponse};
-use tracing::trace;
-
-use crate::{error::AppError, state::AppHandle};
-
-#[debug_handler]
-pub async fn http_get_user(
- Path(name): Path<String>,
- data: Data<AppHandle>,
-) -> Result<impl IntoResponse, AppError> {
- if let Some(a) = read_user(&name, &data).await? {
- let json_user = a.into_json(&data).await?;
- Ok((
- StatusCode::OK,
- FederationJson(WithContext::new_default(json_user)),
- )
- .into_response())
- } else {
- Ok((StatusCode::NOT_FOUND, "").into_response())
- }
-}
-
-pub async fn read_user(
- name: &str,
- data: &Data<AppHandle>,
-) -> Result<Option<crate::entity::user::User>, AppError> {
- trace!(username = name, "getting user");
- let read = sqlx::query_as!(
- crate::entity::user::DbUser,
- "select * from account where username = $1 and local = $2",
- name,
- true
- )
- .fetch_optional(&data.services.postgres)
- .await?;
-
- let user = read.into_iter().find(|value| value.username.eq(&name));
- let user = match user {
- Some(user) => Some(crate::entity::user::User::try_from(user)?),
- None => None,
- };
- Ok(user)
-}
-
-#[cfg(test)]
-mod tests {
- use axum::{
- body::Body,
- http::{Request, StatusCode},
- };
- use sqlx::PgPool;
- use stack_up::Services;
- use tower::ServiceExt;
-
- use crate::{
- server::{self, test_config},
- state::AppState,
- };
-
- #[sqlx::test]
- async fn get_user(pool: PgPool) {
- let services = Services { postgres: pool };
- let state = AppState::create(services, &test_config()).await.unwrap();
- let app = server::router(state);
-
- let response = app
- .oneshot(
- Request::builder()
- .uri("/users/sellershut")
- .body(Body::empty())
- .unwrap(),
- )
- .await
- .unwrap();
-
- assert_eq!(response.status(), StatusCode::OK);
- }
-
- #[sqlx::test]
- async fn get_user_not_found(pool: PgPool) {
- let services = Services { postgres: pool };
- let state = AppState::create(services, &test_config()).await.unwrap();
- let app = server::router(state);
-
- let response = app
- .oneshot(
- Request::builder()
- .uri("/users/selut")
- .body(Body::empty())
- .unwrap(),
- )
- .await
- .unwrap();
-
- assert_eq!(response.status(), StatusCode::NOT_FOUND);
- }
-}
diff --git a/src/server/routes/users/post_inbox.rs b/src/server/routes/users/post_inbox.rs
deleted file mode 100644
index 5e3258b..0000000
--- a/src/server/routes/users/post_inbox.rs
+++ /dev/null
@@ -1,16 +0,0 @@
-use activitypub_federation::{axum::inbox::{receive_activity, ActivityData}, config::Data, protocol::context::WithContext};
-use axum::response::IntoResponse;
-
-use crate::{entity::user::User, server::routes::users::PersonAcceptedActivities, state::AppHandle};
-
-pub async fn http_post_user_inbox(
- data: Data<AppHandle>,
- activity_data: ActivityData,
-) -> impl IntoResponse {
- receive_activity::<WithContext<PersonAcceptedActivities>, User, AppHandle>(
- activity_data,
- &data,
- )
- .await
-}
-
diff --git a/src/server/routes/users/webfinger.rs b/src/server/routes/users/webfinger.rs
deleted file mode 100644
index c395d59..0000000
--- a/src/server/routes/users/webfinger.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-use activitypub_federation::{
- config::Data,
- fetch::webfinger::{build_webfinger_response, extract_webfinger_name},
-};
-use axum::{Json, extract::Query, http::StatusCode, response::IntoResponse};
-use serde::Deserialize;
-
-use crate::{error::AppError, server::routes::users::get_user::read_user, state::AppHandle};
-
-#[derive(Deserialize)]
-pub struct WebfingerQuery {
- resource: String,
-}
-
-pub async fn webfinger(
- Query(query): Query<WebfingerQuery>,
- data: Data<AppHandle>,
-) -> Result<impl IntoResponse, AppError> {
- let name = extract_webfinger_name(&query.resource, &data)?;
- if let Some(db_user) = read_user(name, &data).await? {
- Ok((
- StatusCode::OK,
- Json(build_webfinger_response(
- query.resource,
- db_user.ap_id.into_inner(),
- )),
- )
- .into_response())
- } else {
- Ok((StatusCode::NOT_FOUND, "").into_response())
- }
-}
-
-#[cfg(test)]
-mod tests {
- use axum::{
- body::Body,
- http::{Request, StatusCode},
- };
- use sqlx::PgPool;
- use stack_up::Services;
- use tower::ServiceExt;
-
- use crate::{
- server::{self, test_config},
- state::AppState,
- };
-
- #[sqlx::test]
- async fn webfinger_ok(pool: PgPool) {
- let services = Services { postgres: pool };
- let state = AppState::create(services, &test_config()).await.unwrap();
- let app = server::router(state);
-
- let response = app
- .oneshot(
- Request::builder()
- .uri("/.well-known/webfinger?resource=acct:sellershut@localhost")
- .body(Body::empty())
- .unwrap(),
- )
- .await
- .unwrap();
-
- assert_eq!(response.status(), StatusCode::OK);
- }
-
- #[sqlx::test]
- async fn webfinger_err(pool: PgPool) {
- let services = Services { postgres: pool };
- let state = AppState::create(services, &test_config()).await.unwrap();
- let app = server::router(state);
-
- let response = app
- .oneshot(
- Request::builder()
- .uri("/.well-known/webfinger?resource=acct:sst@localhost")
- .body(Body::empty())
- .unwrap(),
- )
- .await
- .unwrap();
-
- assert_eq!(response.status(), StatusCode::NOT_FOUND);
- }
-}
diff --git a/src/state.rs b/src/state.rs
deleted file mode 100644
index 9129030..0000000
--- a/src/state.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-use std::{ops::Deref, sync::Arc};
-
-use activitypub_federation::config::FederationConfig;
-use stack_up::{Configuration, Environment, Services};
-
-use crate::{cnfg::LocalConfig, entity::user::User, error::AppError};
-
-#[derive(Clone)]
-pub struct AppHandle(Arc<AppState>);
-
-impl Deref for AppHandle {
- type Target = Arc<AppState>;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-pub struct AppState {
- pub services: Services,
- pub environment: Environment,
-}
-
-impl AppState {
- pub async fn create(
- services: Services,
- configuration: &Configuration,
- ) -> Result<FederationConfig<AppHandle>, AppError> {
- let warden_config: LocalConfig = serde_json::from_value(configuration.misc.clone())?;
-
- let user = User::new(
- &warden_config.instance_name,
- &warden_config.hostname,
- &services,
- configuration.application.env,
- )
- .await?;
-
- let config = FederationConfig::builder()
- .domain(&warden_config.hostname)
- .signed_fetch_actor(&user)
- .app_data(AppHandle(Arc::new(Self {
- services,
- environment: configuration.application.env,
- })))
- // .url_verifier(Box::new(MyUrlVerifier()))
- // TODO: could change this to env variable?
- .debug(configuration.application.env == Environment::Development)
- .build()
- .await?;
-
- Ok(config)
- }
-}