Skip to content

Commit 9a6c637

Browse files
authored
Merge pull request #169 from Hirevo/refactor/axum
Move to `axum` and `tokio` stack
2 parents 09e497b + ae8941c commit 9a6c637

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+1781
-2524
lines changed

Cargo.lock

Lines changed: 316 additions & 1009 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,8 @@ members = [
66
"crates/alexandrie-rendering",
77
"helpers/syntect-dump",
88
]
9+
10+
[workspace.dependencies]
11+
tokio = "1.32.0"
12+
serde = "1.0.160"
13+
thiserror = "1.0.40"

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ RUN \
2020
fi && \
2121
if [ "${DATABASE}" = "postgres" ]; then \
2222
apt install -y libpq-dev; \
23-
cargo install diesel_cli --no-default-features --features "postgres"; \
23+
cargo install --locked diesel_cli --no-default-features --features "postgres"; \
2424
fi && \
2525
if [ "${DATABASE}" = "mysql" ]; then \
2626
apt install -y default-libmysqlclient-dev; \
27-
cargo install diesel_cli --no-default-features --features "mysql"; \
27+
cargo install --locked diesel_cli --no-default-features --features "mysql"; \
2828
fi
2929

3030
WORKDIR /alexandrie

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ Things yet to do
4242
How to build
4343
------------
4444

45-
Alexandrie is built using [**Tide**][Tide] and offers multiple options to be used as its database.
45+
Alexandrie is built using [**Axum**][Axum] and offers multiple options to be used as its database.
4646

4747
The current minimum supported Rust version for Alexandrie is `1.68` (on stable), so make sure to check if your local Rust version is adequate by running `rustc -V`.
4848

4949
To build, you can run `cargo build [--release]`.
5050

51-
[Tide]: https://github.com/http-rs/tide
51+
[Axum]: https://github.com/tokio-rs/axum
5252

5353
Before running it, you need to configure your instance in the `alexandrie.toml` file.
5454

alexandrie.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ login_required = false
1515

1616
[frontend.sessions]
1717
cookie_name = "alexandrie.sid"
18-
secret = "YOU_REALLY_SHOULD_CHANGE_THIS_BEFORE_DEPLOYING"
18+
secret = "YOU_REALLY_SHOULD_CHANGE_THIS_BEFORE_DEPLOYING_THIS_TO_PRODUCTION"
1919

2020
[frontend.assets]
2121
path = "assets"

crates/alexandrie-rendering/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ license = "MIT OR Apache-2.0"
1515

1616
[dependencies]
1717
# file formats
18-
serde = { version = "1.0.160", features = ["derive"] }
18+
serde = { workspace = true, features = ["derive"] }
1919

2020
# README rendering
2121
syntect = "5.0.0"

crates/alexandrie-storage/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,21 @@ license = "MIT OR Apache-2.0"
1515

1616
[dependencies]
1717
# async runtime
18-
async-std = { version = "1.12.0", features = ["tokio1"], optional = true }
18+
tokio = { workspace = true, optional = true }
1919

2020
# data types
2121
semver = { version = "1.0.17", features = ["serde"] }
2222

2323
# file formats
24-
serde = { version = "1.0.160", features = ["derive"] }
24+
serde = { workspace = true, features = ["derive"] }
2525

2626
# error handling
27-
thiserror = "1.0.40"
27+
thiserror = { workspace = true }
2828

2929
# S3 crate storage
3030
rusoto_core = { version = "0.48.0", optional = true }
3131
rusoto_s3 = { version = "0.48.0", optional = true }
3232

3333
[features]
3434
default = []
35-
s3 = ["dep:async-std", "dep:rusoto_core", "dep:rusoto_s3"]
35+
s3 = ["dep:tokio", "dep:rusoto_core", "dep:rusoto_s3"]

crates/alexandrie-storage/src/s3.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ use std::convert::TryFrom;
22
use std::fmt;
33
use std::io::{self, Read};
44

5-
use async_std::task;
6-
75
use rusoto_core::Region;
86
use rusoto_s3::{GetObjectOutput, GetObjectRequest, PutObjectRequest, S3Client, StreamingBody, S3};
97
use semver::Version;
@@ -59,7 +57,7 @@ impl S3Storage {
5957
key,
6058
..Default::default()
6159
};
62-
Ok(task::block_on(self.client.get_object(request))?)
60+
Ok(tokio::task::block_on(self.client.get_object(request))?)
6361
}
6462

6563
// NOTE: S3 requests can succeed but then give us a body of `None`. I'm not sure

crates/alexandrie/Cargo.toml

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,13 @@ alexandrie-storage = { path = "../alexandrie-storage", version = "0.1.0" }
2222
alexandrie-rendering = { path = "../alexandrie-rendering", version = "0.1.0" }
2323

2424
# core
25-
tide = { version = "0.16.0", default-features = false, features = ["h1-server", "sessions"] }
26-
clap = { version = "4.2.2", features = ["string"] }
25+
tokio = { workspace = true, features = ["rt-multi-thread", "fs", "macros"] }
26+
axum = { version = "0.6.19", features = ["http2", "headers"] }
27+
axum-extra = "0.7.5"
28+
axum-sessions = "0.5.0"
29+
30+
# command-line interface
31+
clap = { version = "4.2.2", features = ["string", "derive"] }
2732

2833
# session handling
2934
async-session = "3.0.0"
@@ -32,6 +37,7 @@ async-session = "3.0.0"
3237
url = "2.3.1"
3338
semver = { version = "1.0.17", features = ["serde"] }
3439
chrono = { version = "0.4.24", features = ["serde"] }
40+
bytes = "1.4.0"
3541

3642
# file formats
3743
serde = { version = "1.0.160", features = ["derive"] }
@@ -55,10 +61,13 @@ tantivy = "0.20"
5561
tantivy-analysis-contrib = { version = "0.9", default-features = false, features = ["commons"] }
5662

5763
# async primitives
58-
async-std = { version = "1.12.0", features = ["attributes", "tokio1"] }
64+
futures-util = "0.3.28"
65+
tower = "0.4.13"
66+
tower-http = { version = "0.4.1", features = ["trace", "fs"] }
5967

6068
# error handling
61-
thiserror = "1.0.40"
69+
thiserror = { workspace = true }
70+
anyhow = "1.0.72"
6271

6372
# README rendering
6473
flate2 = "1.0.25"
@@ -75,12 +84,8 @@ once_cell = { version = "1.17.1", optional = true }
7584
regex = { version = "1.7.3", optional = true }
7685

7786
# logs
78-
log = "0.4.17"
79-
slog = "2.7.0"
80-
slog-stdlog = "4.1.1"
81-
slog-scope = "4.4.0"
82-
slog-term = "2.9.0"
83-
slog-async = "2.7.0"
87+
tracing = "0.1.37"
88+
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
8489

8590
[features]
8691
default = ["frontend", "sqlite"]

crates/alexandrie/src/api/account/login.rs

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
use std::num::NonZeroU32;
2+
use std::sync::Arc;
23

4+
use axum::extract::State;
5+
use axum::Json;
36
use diesel::prelude::*;
47
use ring::digest as hasher;
58
use ring::pbkdf2;
69
use serde::{Deserialize, Serialize};
7-
use tide::{Request, StatusCode};
810

11+
use crate::config::AppState;
912
use crate::db::models::NewAuthorToken;
1013
use crate::db::schema::*;
14+
use crate::error::ApiError;
1115
use crate::utils;
12-
use crate::State;
16+
use crate::utils::auth::api::Auth;
1317

1418
/// Request body for this route.
1519
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -28,28 +32,20 @@ pub struct ResponseBody {
2832
}
2933

3034
/// Route to log in to an account.
31-
pub async fn post(mut req: Request<State>) -> tide::Result {
32-
let state = req.state().clone();
35+
pub async fn post(
36+
State(state): State<Arc<AppState>>,
37+
maybe_author: Option<Auth>,
38+
Json(body): Json<RequestBody>,
39+
) -> Result<Json<ResponseBody>, ApiError> {
3340
let db = &state.db;
3441

3542
//? Is the author logged in ?
36-
let author = if let Some(headers) = req.header(utils::auth::AUTHORIZATION_HEADER) {
37-
let header = headers.last().to_string();
38-
db.run(move |conn| utils::checks::get_author(conn, header))
39-
.await
40-
} else {
41-
None
42-
};
43-
if author.is_some() {
44-
return Ok(utils::response::error(
45-
StatusCode::Unauthorized,
46-
"please log out first to register as a new author",
43+
if maybe_author.is_some() {
44+
return Err(ApiError::msg(
45+
"please log out first to login as a new author",
4746
));
4847
}
4948

50-
//? Parse request body.
51-
let body: RequestBody = req.body_json().await?;
52-
5349
let transaction = db.transaction(move |conn| {
5450
//? Get the users' salt and expected hash.
5551
let results = salts::table
@@ -63,10 +59,7 @@ pub async fn post(mut req: Request<State>) -> tide::Result {
6359
let (author_id, encoded_salt, encoded_expected_hash) = match results {
6460
Some((author_id, salt, Some(passwd))) => (author_id, salt, passwd),
6561
_ => {
66-
return Ok(utils::response::error(
67-
StatusCode::Forbidden,
68-
"invalid email/password combination.",
69-
));
62+
return Err(ApiError::msg("invalid email/password combination."));
7063
}
7164
};
7265

@@ -77,10 +70,7 @@ pub async fn post(mut req: Request<State>) -> tide::Result {
7770
let (decoded_salt, decoded_expected_hash) = match decode_results {
7871
Ok(results) => results,
7972
Err(_) => {
80-
return Ok(utils::response::error(
81-
StatusCode::InternalServerError,
82-
"an author already exists for this email.",
83-
));
73+
return Err(ApiError::msg("an author already exists for this email."));
8474
}
8575
};
8676

@@ -112,10 +102,7 @@ pub async fn post(mut req: Request<State>) -> tide::Result {
112102
};
113103

114104
if !password_match {
115-
return Ok(utils::response::error(
116-
StatusCode::Forbidden,
117-
"invalid email/password combination.",
118-
));
105+
return Err(ApiError::msg("invalid email/password combination."));
119106
}
120107

121108
//? Generate new registry token.
@@ -143,9 +130,8 @@ pub async fn post(mut req: Request<State>) -> tide::Result {
143130
.select(author_tokens::token)
144131
.first(conn)?;
145132

146-
let response = ResponseBody { token };
147-
Ok(utils::response::json(&response))
133+
Ok(Json(ResponseBody { token }))
148134
});
149135

150-
transaction.await
136+
transaction.await.map_err(ApiError::from)
151137
}

0 commit comments

Comments
 (0)