Migrate to actix 4.0 and tokio 1
This commit is contained in:
parent
ed68b728be
commit
30bd3d6a37
11 changed files with 786 additions and 1085 deletions
1732
Cargo.lock
generated
1732
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
45
Cargo.toml
45
Cargo.toml
|
@ -5,18 +5,18 @@ description = "Mitra backend"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
rust-version = "1.53"
|
rust-version = "1.54"
|
||||||
publish = false
|
publish = false
|
||||||
default-run = "mitra"
|
default-run = "mitra"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Used to handle incoming HTTP requests
|
# Used to handle incoming HTTP requests
|
||||||
actix-cors = "0.5.4"
|
actix-cors = "0.6.1"
|
||||||
actix-files = "0.5.0"
|
actix-files = "0.6.0"
|
||||||
actix-web = "3.3.3"
|
actix-web = "4.0.1"
|
||||||
actix-web-httpauth = "0.5.1"
|
actix-web-httpauth = "0.6.0"
|
||||||
# Used for managing async tasks
|
# Used for managing async tasks
|
||||||
actix-rt = "1.1.1"
|
actix-rt = "2.7.0"
|
||||||
# Used for HTML sanitization
|
# Used for HTML sanitization
|
||||||
ammonia = "3.1.2"
|
ammonia = "3.1.2"
|
||||||
# Used for working with RSA keys, HTTP signatures and file uploads
|
# Used for working with RSA keys, HTTP signatures and file uploads
|
||||||
|
@ -26,9 +26,9 @@ chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
# Used to build admin CLI tool
|
# Used to build admin CLI tool
|
||||||
# Versions greater than beta.2 require Rust 1.54
|
# Versions greater than beta.2 require Rust 1.54
|
||||||
clap = { version = "3.0.0-beta.2", default-features = false, features = ["std", "derive"] }
|
clap = { version = "3.0.0-beta.2", default-features = false, features = ["std", "derive"] }
|
||||||
# Used for pooling database connections (compatible with tokio 0.2)
|
# Used for pooling database connections
|
||||||
deadpool = "0.5.2"
|
deadpool = "0.9.2"
|
||||||
deadpool-postgres = { version = "0.5.6", default-features = false }
|
deadpool-postgres = { version = "0.10.2", default-features = false }
|
||||||
# Used to read .env files
|
# Used to read .env files
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
# Used to work with hexadecimal strings
|
# Used to work with hexadecimal strings
|
||||||
|
@ -46,9 +46,9 @@ regex = "1.5.4"
|
||||||
# Used to generate random numbers
|
# Used to generate random numbers
|
||||||
rand = "0.8.4"
|
rand = "0.8.4"
|
||||||
# Used for managing database migrations
|
# Used for managing database migrations
|
||||||
refinery = { version = "0.4.0", features = ["tokio-postgres"] }
|
refinery = { version = "0.8.4", features = ["tokio-postgres"] }
|
||||||
# Used for making async HTTP requests
|
# Used for making async HTTP requests
|
||||||
reqwest = { version = "0.10.10", features = ["json"] }
|
reqwest = { version = "0.11.10", features = ["json", "multipart"] }
|
||||||
# Used for working with RSA keys
|
# Used for working with RSA keys
|
||||||
rsa = "0.5.0"
|
rsa = "0.5.0"
|
||||||
pem = "1.0.2"
|
pem = "1.0.2"
|
||||||
|
@ -57,8 +57,7 @@ rust-argon2 = "0.8.3"
|
||||||
# Used for working with ethereum keys
|
# Used for working with ethereum keys
|
||||||
secp256k1 = { version = "0.20.3", features = ["rand", "rand-std"] }
|
secp256k1 = { version = "0.20.3", features = ["rand", "rand-std"] }
|
||||||
# Used for serialization/deserialization
|
# Used for serialization/deserialization
|
||||||
# https://github.com/rust-db/refinery/issues/160
|
serde = { version = "1.0.136", features = ["derive"] }
|
||||||
serde = { version = "=1.0.117", features = ["derive"] }
|
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
# Used to parse config file
|
# Used to parse config file
|
||||||
serde_yaml = "0.8.17"
|
serde_yaml = "0.8.17"
|
||||||
|
@ -68,15 +67,15 @@ sha2 = "0.9.5"
|
||||||
siwe = { git = "https://github.com/silverpill/siwe-rs", rev = "6589eb6fdcffbfb9ee2880906014f5cf71f0f441" }
|
siwe = { git = "https://github.com/silverpill/siwe-rs", rev = "6589eb6fdcffbfb9ee2880906014f5cf71f0f441" }
|
||||||
# Used for creating error types
|
# Used for creating error types
|
||||||
thiserror = "1.0.24"
|
thiserror = "1.0.24"
|
||||||
# Async runtime ( required for #[tokio::main] )
|
# Async runtime
|
||||||
tokio = { version = "0.2.25", features = ["macros"] }
|
tokio = { version = "1.17.0", features = ["macros"] }
|
||||||
# Used for working with Postgresql database (compatible with tokio 0.2)
|
# Used for working with Postgresql database
|
||||||
tokio-postgres = { version = "0.5.5", features = ["with-chrono-0_4", "with-uuid-0_8", "with-serde_json-1"] }
|
tokio-postgres = { version = "0.7.5", features = ["with-chrono-0_4", "with-uuid-0_8", "with-serde_json-1"] }
|
||||||
postgres-types = { version = "0.1.2", features = ["derive", "with-chrono-0_4", "with-uuid-0_8", "with-serde_json-1"] }
|
postgres-types = { version = "0.2.2", features = ["derive", "with-chrono-0_4", "with-uuid-0_8", "with-serde_json-1"] }
|
||||||
postgres-protocol = "0.5.3"
|
postgres-protocol = "0.6.1"
|
||||||
# Used to construct PostgreSQL queries
|
# Used to construct PostgreSQL queries
|
||||||
postgres_query = "0.3.3"
|
postgres_query = { git = "https://github.com/nolanderc/rust-postgres-query", rev = "b4422051c8a31fbba4a35f88004c1cefb1878dd5" }
|
||||||
postgres_query_macro = "0.3.1"
|
postgres_query_macro = { git = "https://github.com/nolanderc/rust-postgres-query", rev = "b4422051c8a31fbba4a35f88004c1cefb1878dd5" }
|
||||||
# Used to work with URLs
|
# Used to work with URLs
|
||||||
url = "2.2.2"
|
url = "2.2.2"
|
||||||
# Used to generate lexicographically sortable IDs
|
# Used to generate lexicographically sortable IDs
|
||||||
|
@ -84,7 +83,9 @@ ulid = { version = "0.4.1", features = ["uuid"] }
|
||||||
# Used to work with UUIDs
|
# Used to work with UUIDs
|
||||||
uuid = { version = "0.8.2", features = ["serde", "v4"] }
|
uuid = { version = "0.8.2", features = ["serde", "v4"] }
|
||||||
# Used to query ethereum node
|
# Used to query ethereum node
|
||||||
web3 = { version = "0.15.0", default-features = false, features = ["http", "http-tls", "signing"] }
|
web3 = { version = "0.16.0", default-features = false, features = ["http", "http-tls", "signing"] }
|
||||||
|
# Dependency of web3; version 0.2.2 requires edition 2021
|
||||||
|
impl-trait-for-tuples = "=0.2.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serial_test = "0.5.1"
|
serial_test = "0.5.1"
|
||||||
|
|
|
@ -21,7 +21,7 @@ Demo instance: https://mitra.social/ (invite-only)
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Rust 1.53+
|
- Rust 1.54+
|
||||||
- PostgreSQL 10.2+
|
- PostgreSQL 10.2+
|
||||||
- IPFS node (optional, see [guide](./docs/ipfs.md))
|
- IPFS node (optional, see [guide](./docs/ipfs.md))
|
||||||
- Ethereum node (optional)
|
- Ethereum node (optional)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
get, post, web,
|
get, post, web,
|
||||||
HttpRequest, HttpResponse, Scope,
|
HttpRequest, HttpResponse, Scope,
|
||||||
http::HeaderMap,
|
http::header::HeaderMap,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -84,7 +84,7 @@ async fn actor_view(
|
||||||
if !is_activitypub_request(request.headers()) {
|
if !is_activitypub_request(request.headers()) {
|
||||||
let page_url = get_profile_page_url(&config.instance_url(), &user.id);
|
let page_url = get_profile_page_url(&config.instance_url(), &user.id);
|
||||||
let response = HttpResponse::Found()
|
let response = HttpResponse::Found()
|
||||||
.header("Location", page_url)
|
.append_header(("Location", page_url))
|
||||||
.finish();
|
.finish();
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
};
|
};
|
||||||
|
@ -293,7 +293,7 @@ pub async fn object_view(
|
||||||
if !is_activitypub_request(request.headers()) {
|
if !is_activitypub_request(request.headers()) {
|
||||||
let page_url = get_post_page_url(&config.instance_url(), &post.id);
|
let page_url = get_post_page_url(&config.instance_url(), &post.id);
|
||||||
let response = HttpResponse::Found()
|
let response = HttpResponse::Found()
|
||||||
.header("Location", page_url)
|
.append_header(("Location", page_url))
|
||||||
.finish();
|
.finish();
|
||||||
return Ok(response);
|
return Ok(response);
|
||||||
};
|
};
|
||||||
|
@ -321,7 +321,10 @@ pub async fn object_view(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_web::http::{header, HeaderMap, HeaderValue};
|
use actix_web::http::{
|
||||||
|
header,
|
||||||
|
header::{HeaderMap, HeaderValue},
|
||||||
|
};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use tokio_postgres::config::{Config as DbConfig};
|
use tokio_postgres::config::{Config as DbConfig};
|
||||||
|
use tokio_postgres::error::{Error as PgError, SqlState};
|
||||||
|
use crate::errors::DatabaseError;
|
||||||
|
|
||||||
pub mod int_enum;
|
pub mod int_enum;
|
||||||
pub mod migrate;
|
pub mod migrate;
|
||||||
|
@ -21,19 +23,15 @@ pub async fn create_database_client(db_config: &DbConfig) -> tokio_postgres::Cli
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_pool(database_url: &str) -> Pool {
|
pub fn create_pool(database_url: &str) -> Pool {
|
||||||
deadpool_postgres::Pool::new(
|
let manager = deadpool_postgres::Manager::new(
|
||||||
deadpool_postgres::Manager::new(
|
database_url.parse().expect("invalid database URL"),
|
||||||
database_url.parse().expect("invalid database URL"),
|
tokio_postgres::NoTls,
|
||||||
tokio_postgres::NoTls,
|
);
|
||||||
),
|
// https://wiki.postgresql.org/wiki/Number_Of_Database_Connections
|
||||||
// https://wiki.postgresql.org/wiki/Number_Of_Database_Connections
|
let pool_size = num_cpus::get() * 2;
|
||||||
num_cpus::get() * 2,
|
Pool::builder(manager).max_size(pool_size).build().unwrap()
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
use tokio_postgres::error::{Error as PgError, SqlState};
|
|
||||||
use crate::errors::DatabaseError;
|
|
||||||
|
|
||||||
pub async fn get_database_client(pool: &Pool)
|
pub async fn get_database_client(pool: &Pool)
|
||||||
-> Result<deadpool_postgres::Client, DatabaseError>
|
-> Result<deadpool_postgres::Client, DatabaseError>
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
dev::HttpResponseBuilder,
|
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
HttpResponse,
|
HttpResponse, HttpResponseBuilder,
|
||||||
error::ResponseError,
|
error::ResponseError,
|
||||||
};
|
};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
HttpRequest,
|
HttpRequest,
|
||||||
http::{HeaderMap, Method, Uri},
|
http::{Method, Uri, header::HeaderMap},
|
||||||
};
|
};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use tokio_postgres::GenericClient;
|
use tokio_postgres::GenericClient;
|
||||||
|
@ -154,7 +154,11 @@ pub async fn verify_http_signature(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_web::http::{header, HeaderMap, HeaderName, HeaderValue, Uri};
|
use actix_web::http::{
|
||||||
|
header,
|
||||||
|
header::{HeaderMap, HeaderName, HeaderValue},
|
||||||
|
Uri,
|
||||||
|
};
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -77,10 +77,10 @@ async fn main() -> std::io::Result<()> {
|
||||||
.wrap(ActixLogger::new("%r : %s : %{r}a"))
|
.wrap(ActixLogger::new("%r : %s : %{r}a"))
|
||||||
.wrap(cors_config)
|
.wrap(cors_config)
|
||||||
.wrap(create_auth_error_handler())
|
.wrap(create_auth_error_handler())
|
||||||
.data(web::PayloadConfig::default().limit(MAX_UPLOAD_SIZE))
|
.app_data(web::PayloadConfig::default().limit(MAX_UPLOAD_SIZE))
|
||||||
.data(web::JsonConfig::default().limit(MAX_UPLOAD_SIZE))
|
.app_data(web::JsonConfig::default().limit(MAX_UPLOAD_SIZE))
|
||||||
.data(config.clone())
|
.app_data(web::Data::new(config.clone()))
|
||||||
.data(db_pool.clone())
|
.app_data(web::Data::new(db_pool.clone()))
|
||||||
.service(actix_files::Files::new(
|
.service(actix_files::Files::new(
|
||||||
"/media",
|
"/media",
|
||||||
config.media_dir(),
|
config.media_dir(),
|
||||||
|
|
|
@ -112,8 +112,8 @@ pub async fn create_account(
|
||||||
}
|
}
|
||||||
// Generate RSA private key for actor
|
// Generate RSA private key for actor
|
||||||
let private_key = match web::block(generate_private_key).await {
|
let private_key = match web::block(generate_private_key).await {
|
||||||
Ok(private_key) => private_key,
|
Ok(Ok(private_key)) => private_key,
|
||||||
Err(_) => return Err(HttpError::InternalError),
|
_ => return Err(HttpError::InternalError),
|
||||||
};
|
};
|
||||||
let private_key_pem = serialize_private_key(private_key)
|
let private_key_pem = serialize_private_key(private_key)
|
||||||
.map_err(|_| HttpError::InternalError)?;
|
.map_err(|_| HttpError::InternalError)?;
|
||||||
|
|
|
@ -44,10 +44,10 @@ async fn get_notifications_view(
|
||||||
let response = if let Some(item) = notifications.get(max_index) {
|
let response = if let Some(item) = notifications.get(max_index) {
|
||||||
let pagination_header = get_pagination_header(&config.instance_url(), &item.id);
|
let pagination_header = get_pagination_header(&config.instance_url(), &item.id);
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.header("Link", pagination_header)
|
.append_header(("Link", pagination_header))
|
||||||
// Link header needs to be exposed
|
// Link header needs to be exposed
|
||||||
// https://github.com/actix/actix-extras/issues/192
|
// https://github.com/actix/actix-extras/issues/192
|
||||||
.header("Access-Control-Expose-Headers", "Link")
|
.append_header(("Access-Control-Expose-Headers", "Link"))
|
||||||
.json(notifications)
|
.json(notifications)
|
||||||
} else {
|
} else {
|
||||||
HttpResponse::Ok().json(notifications)
|
HttpResponse::Ok().json(notifications)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
body::{Body, BodySize, MessageBody, ResponseBody},
|
body::{BodySize, BoxBody, MessageBody},
|
||||||
|
dev::ServiceResponse,
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
middleware::errhandlers::{ErrorHandlerResponse, ErrorHandlers},
|
middleware::{ErrorHandlerResponse, ErrorHandlers},
|
||||||
};
|
};
|
||||||
use actix_web::dev::ServiceResponse;
|
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use tokio_postgres::GenericClient;
|
use tokio_postgres::GenericClient;
|
||||||
|
|
||||||
|
@ -27,22 +27,20 @@ pub async fn get_current_user(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Error handler for 401 Unauthorized
|
/// Error handler for 401 Unauthorized
|
||||||
pub fn create_auth_error_handler<B: MessageBody>() -> ErrorHandlers<B> {
|
pub fn create_auth_error_handler<B: MessageBody + 'static>() -> ErrorHandlers<B> {
|
||||||
ErrorHandlers::new()
|
ErrorHandlers::new()
|
||||||
.handler(StatusCode::UNAUTHORIZED, |mut response: ServiceResponse<B>| {
|
.handler(StatusCode::UNAUTHORIZED, |response: ServiceResponse<B>| {
|
||||||
response = response.map_body(|_, body| {
|
let response_new = response.map_body(|_, body| {
|
||||||
if let ResponseBody::Body(data) = &body {
|
if let BodySize::None | BodySize::Sized(0) = body.size() {
|
||||||
if let BodySize::Empty = data.size() {
|
// Insert error description if response body is empty
|
||||||
// Insert error description if response body is empty
|
// https://github.com/actix/actix-extras/issues/156
|
||||||
// https://github.com/actix/actix-extras/issues/156
|
let error_data = json!({
|
||||||
let error_data = json!({
|
"message": "auth header is not present",
|
||||||
"message": "auth header is not present",
|
});
|
||||||
});
|
return BoxBody::new(error_data.to_string());
|
||||||
return ResponseBody::Body(Body::from(error_data)).into_body();
|
};
|
||||||
}
|
body.boxed()
|
||||||
}
|
|
||||||
body
|
|
||||||
});
|
});
|
||||||
Ok(ErrorHandlerResponse::Response(response))
|
Ok(ErrorHandlerResponse::Response(response_new.map_into_right_body()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue