From 8ca01dee078fe1a1a5f1e2d0f3554eba5d28de83 Mon Sep 17 00:00:00 2001
From: Felix Ableitner
Date: Tue, 16 Jan 2024 16:07:01 +0100
Subject: [PATCH] registration and login working
---
Cargo.lock | 118 +++++++++++++++++++-
Cargo.toml | 5 +-
README.md | 8 +-
src/backend/api/article.rs | 2 +-
src/backend/api/instance.rs | 2 +-
src/backend/api/mod.rs | 11 +-
src/backend/api/user.rs | 61 ++++++++--
src/backend/database/conflict.rs | 7 +-
src/backend/database/instance.rs | 2 +-
src/backend/database/user.rs | 40 +------
src/backend/federation/activities/follow.rs | 2 +-
src/backend/federation/activities/mod.rs | 2 +-
src/backend/federation/objects/article.rs | 2 +-
src/backend/federation/objects/edit.rs | 6 +-
src/backend/federation/objects/user.rs | 3 +-
src/backend/federation/routes.rs | 2 +-
src/common/mod.rs | 63 +++++++++--
src/frontend/api.rs | 32 ++++--
src/frontend/app.rs | 50 +++++++--
src/frontend/components/nav.rs | 37 +++++-
src/frontend/pages/login.rs | 91 ++++++++-------
src/frontend/pages/register.rs | 15 ++-
src/main.rs | 1 -
tests/common.rs | 38 ++++---
tests/test.rs | 5 +-
25 files changed, 424 insertions(+), 181 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
index c2332e3..0f5c273 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -233,6 +233,28 @@ dependencies = [
"tower-service",
]
+[[package]]
+name = "axum-extra"
+version = "0.7.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a93e433be9382c737320af3924f7d5fc6f89c155cf2bf88949d8f5126fab283f"
+dependencies = [
+ "axum",
+ "axum-core",
+ "bytes",
+ "cookie 0.17.0",
+ "futures-util",
+ "http 0.2.11",
+ "http-body 0.4.5",
+ "mime",
+ "pin-project-lite",
+ "serde",
+ "tokio",
+ "tower",
+ "tower-layer",
+ "tower-service",
+]
+
[[package]]
name = "axum-macros"
version = "0.3.8"
@@ -539,6 +561,45 @@ dependencies = [
"unicode-segmentation",
]
+[[package]]
+name = "cookie"
+version = "0.16.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
+dependencies = [
+ "percent-encoding",
+ "time",
+ "version_check",
+]
+
+[[package]]
+name = "cookie"
+version = "0.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24"
+dependencies = [
+ "percent-encoding",
+ "time",
+ "version_check",
+]
+
+[[package]]
+name = "cookie_store"
+version = "0.16.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d606d0fba62e13cf04db20536c05cb7f13673c161cb47a47a82b9b9e7d3f1daa"
+dependencies = [
+ "cookie 0.16.2",
+ "idna 0.2.3",
+ "log",
+ "publicsuffix",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "time",
+ "url",
+]
+
[[package]]
name = "core-foundation"
version = "0.9.3"
@@ -1325,6 +1386,7 @@ dependencies = [
"anyhow",
"async-trait",
"axum",
+ "axum-extra",
"axum-macros",
"bcrypt",
"chrono",
@@ -1351,6 +1413,7 @@ dependencies = [
"serde",
"serde_json",
"sha2",
+ "time",
"tokio",
"tower-http",
"tracing",
@@ -1365,6 +1428,27 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+[[package]]
+name = "idna"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
+dependencies = [
+ "matches",
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
+[[package]]
+name = "idna"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
+
[[package]]
name = "idna"
version = "0.5.0"
@@ -1802,6 +1886,12 @@ dependencies = [
"quote",
]
+[[package]]
+name = "matches"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
+
[[package]]
name = "matchit"
version = "0.7.3"
@@ -2245,6 +2335,22 @@ dependencies = [
"yansi 1.0.0-rc.1",
]
+[[package]]
+name = "psl-types"
+version = "2.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
+
+[[package]]
+name = "publicsuffix"
+version = "2.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457"
+dependencies = [
+ "idna 0.3.0",
+ "psl-types",
+]
+
[[package]]
name = "pulldown-cmark"
version = "0.9.3"
@@ -2389,6 +2495,8 @@ checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b"
dependencies = [
"base64 0.21.5",
"bytes",
+ "cookie 0.16.2",
+ "cookie_store",
"encoding_rs",
"futures-core",
"futures-util",
@@ -2949,9 +3057,9 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.30"
+version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
+checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e"
dependencies = [
"deranged",
"itoa",
@@ -2969,9 +3077,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "time-macros"
-version = "0.2.15"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20"
+checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f"
dependencies = [
"time-core",
]
@@ -3268,7 +3376,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
dependencies = [
"form_urlencoded",
- "idna",
+ "idna 0.5.0",
"percent-encoding",
"serde",
]
diff --git a/Cargo.toml b/Cargo.toml
index c9569b5..4aa2058 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,6 +8,7 @@ default = ["ssr"]
ssr = [
"axum",
"axum-macros",
+ "axum-extra",
"tower-http",
"diesel",
"diesel-derive-newtype",
@@ -28,6 +29,7 @@ anyhow = "1.0.75"
async-trait = "0.1.74"
axum = { version = "0.6.20", optional = true }
axum-macros = { version = "0.3.8", optional = true }
+axum-extra = { version = "0.7.7", features = ["cookie"], optional = true }
leptos = "0.5.4"
leptos_meta = "0.5.4"
leptos_router = "0.5.4"
@@ -62,10 +64,11 @@ once_cell = "1.18.0"
wasm-bindgen = "0.2.89"
console_error_panic_hook = "0.1.7"
console_log = "1.0.0"
+time = "0.3.31"
[dev-dependencies]
pretty_assertions = "1.4.0"
-reqwest = "0.11.22"
+reqwest = { version = "0.11.22", features = ["cookies"] }
[package.metadata.leptos]
output-name = "ibis"
diff --git a/README.md b/README.md
index 02a7581..3f4fb2d 100644
--- a/README.md
+++ b/README.md
@@ -11,11 +11,11 @@ The Ibis is a [bird which is related to the Egyptian god of knowledge and scienc
You need to install [cargo](https://rustup.rs/) and [trunk](https://trunkrs.dev). Then run the following commands in separate terminals:
```
-# start backend
-cargo run
+# start backend, with separate target folder to avoid rebuilds from arch change
+cargo run --target-dir target/backend
-# start frontend
-trunk serve
+# start frontend, automatic rebuild on changes
+trunk serve -w src/frontend/
```
## License
diff --git a/src/backend/api/article.rs b/src/backend/api/article.rs
index e140641..ee21c45 100644
--- a/src/backend/api/article.rs
+++ b/src/backend/api/article.rs
@@ -2,7 +2,6 @@ use crate::backend::database::article::DbArticleForm;
use crate::backend::database::conflict::{ApiConflict, DbConflict, DbConflictForm};
use crate::backend::database::edit::DbEditForm;
use crate::backend::database::instance::DbInstance;
-use crate::backend::database::user::LocalUserView;
use crate::backend::database::MyDataHandle;
use crate::backend::error::MyResult;
use crate::backend::federation::activities::create_article::CreateArticle;
@@ -10,6 +9,7 @@ use crate::backend::federation::activities::submit_article_update;
use crate::backend::utils::generate_article_version;
use crate::common::EditVersion;
use crate::common::GetArticleData;
+use crate::common::LocalUserView;
use crate::common::{ArticleView, DbArticle, DbEdit};
use activitypub_federation::config::Data;
use activitypub_federation::fetch::object_id::ObjectId;
diff --git a/src/backend/api/instance.rs b/src/backend/api/instance.rs
index cf16fa2..ee9718f 100644
--- a/src/backend/api/instance.rs
+++ b/src/backend/api/instance.rs
@@ -1,8 +1,8 @@
use crate::backend::database::instance::{DbInstance, InstanceView};
-use crate::backend::database::user::LocalUserView;
use crate::backend::database::MyDataHandle;
use crate::backend::error::MyResult;
use crate::backend::federation::activities::follow::Follow;
+use crate::common::LocalUserView;
use activitypub_federation::config::Data;
use axum::Extension;
use axum::{Form, Json};
diff --git a/src/backend/api/mod.rs b/src/backend/api/mod.rs
index 236e7df..60b768b 100644
--- a/src/backend/api/mod.rs
+++ b/src/backend/api/mod.rs
@@ -2,15 +2,16 @@ use crate::backend::api::article::create_article;
use crate::backend::api::article::{edit_article, fork_article, get_article};
use crate::backend::api::instance::follow_instance;
use crate::backend::api::instance::get_local_instance;
-use crate::backend::api::user::login_user;
+use crate::backend::api::user::my_profile;
use crate::backend::api::user::register_user;
use crate::backend::api::user::validate;
+use crate::backend::api::user::{login_user, logout_user};
use crate::backend::database::conflict::{ApiConflict, DbConflict};
use crate::backend::database::instance::DbInstance;
-use crate::backend::database::user::LocalUserView;
use crate::backend::database::MyDataHandle;
use crate::backend::error::MyResult;
use crate::common::DbEdit;
+use crate::common::LocalUserView;
use crate::common::{ArticleView, DbArticle};
use activitypub_federation::config::Data;
use activitypub_federation::fetch::object_id::ObjectId;
@@ -49,8 +50,10 @@ pub fn api_routes() -> Router {
.route("/instance", get(get_local_instance))
.route("/instance/follow", post(follow_instance))
.route("/search", get(search_article))
- .route("/user/register", post(register_user))
- .route("/user/login", post(login_user))
+ .route("/account/register", post(register_user))
+ .route("/account/login", post(login_user))
+ .route("/account/my_profile", get(my_profile))
+ .route("/account/logout", get(logout_user))
.route_layer(middleware::from_fn(auth))
}
diff --git a/src/backend/api/user.rs b/src/backend/api/user.rs
index 1fdf50a..6fc5a4f 100644
--- a/src/backend/api/user.rs
+++ b/src/backend/api/user.rs
@@ -1,10 +1,10 @@
-use crate::backend::database::user::{DbLocalUser, DbPerson, LocalUserView};
use crate::backend::database::{read_jwt_secret, MyDataHandle};
use crate::backend::error::MyResult;
-use crate::common::{LoginResponse, LoginUserData, RegisterUserData};
+use crate::common::{DbLocalUser, DbPerson, LocalUserView, LoginUserData, RegisterUserData};
use activitypub_federation::config::Data;
use anyhow::anyhow;
use axum::{Form, Json};
+use axum_extra::extract::cookie::{Cookie, CookieJar, Expiration, SameSite};
use axum_macros::debug_handler;
use bcrypt::verify;
use chrono::Utc;
@@ -13,6 +13,9 @@ use jsonwebtoken::Validation;
use jsonwebtoken::{decode, get_current_timestamp};
use jsonwebtoken::{encode, EncodingKey, Header};
use serde::{Deserialize, Serialize};
+use time::{Duration, OffsetDateTime};
+
+pub static AUTH_COOKIE: &str = "auth";
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
@@ -26,22 +29,19 @@ struct Claims {
pub exp: u64,
}
-fn generate_login_token(
- local_user: DbLocalUser,
- data: &Data,
-) -> MyResult {
+fn generate_login_token(local_user: &DbLocalUser, data: &Data) -> MyResult {
let hostname = data.domain().to_string();
let claims = Claims {
sub: local_user.id.to_string(),
iss: hostname,
iat: Utc::now().timestamp(),
- exp: get_current_timestamp(),
+ exp: get_current_timestamp() + 60 * 60 * 24 * 365,
};
let secret = read_jwt_secret(data)?;
let key = EncodingKey::from_secret(secret.as_bytes());
let jwt = encode(&Header::default(), &claims, &key)?;
- Ok(LoginResponse { jwt })
+ Ok(jwt)
}
pub async fn validate(jwt: &str, data: &Data) -> MyResult {
@@ -55,21 +55,58 @@ pub async fn validate(jwt: &str, data: &Data) -> MyResult,
+ jar: CookieJar,
Form(form): Form,
-) -> MyResult> {
+) -> MyResult<(CookieJar, Json)> {
let user = DbPerson::create_local(form.username, form.password, &data)?;
- Ok(Json(generate_login_token(user.local_user, &data)?))
+ let token = generate_login_token(&user.local_user, &data)?;
+ let jar = jar.add(create_cookie(token));
+ Ok((jar, Json(user)))
}
#[debug_handler]
pub(in crate::backend::api) async fn login_user(
data: Data,
+ jar: CookieJar,
Form(form): Form,
-) -> MyResult> {
+) -> MyResult<(CookieJar, Json)> {
let user = DbPerson::read_local_from_name(&form.username, &data)?;
let valid = verify(&form.password, &user.local_user.password_encrypted)?;
if !valid {
return Err(anyhow!("Invalid login").into());
}
- Ok(Json(generate_login_token(user.local_user, &data)?))
+ let token = generate_login_token(&user.local_user, &data)?;
+ let jar = jar.add(create_cookie(token));
+ Ok((jar, Json(user)))
+}
+
+fn create_cookie(jwt: String) -> Cookie<'static> {
+ Cookie::build(AUTH_COOKIE, jwt)
+ .domain("localhost")
+ .same_site(SameSite::Strict)
+ .path("/")
+ .http_only(true)
+ .expires(Expiration::DateTime(
+ OffsetDateTime::now_utc() + Duration::weeks(52),
+ ))
+ .finish()
+}
+
+#[debug_handler]
+pub(in crate::backend::api) async fn my_profile(
+ data: Data,
+ jar: CookieJar,
+) -> MyResult> {
+ let jwt = jar.get(AUTH_COOKIE).map(|c| c.value());
+ if let Some(jwt) = jwt {
+ Ok(Json(validate(jwt, &data).await?))
+ } else {
+ Err(anyhow!("invalid/missing auth").into())
+ }
+}
+
+#[debug_handler]
+pub(in crate::backend::api) async fn logout_user(jar: CookieJar) -> MyResult {
+ let jar = jar.remove(Cookie::named(AUTH_COOKIE));
+ Ok(jar)
}
diff --git a/src/backend/database/conflict.rs b/src/backend/database/conflict.rs
index 55f4023..2f6f2d5 100644
--- a/src/backend/database/conflict.rs
+++ b/src/backend/database/conflict.rs
@@ -1,14 +1,13 @@
use crate::backend::database::schema::conflict;
-use crate::backend::database::user::DbLocalUser;
use crate::backend::database::MyDataHandle;
use crate::backend::error::MyResult;
use crate::backend::federation::activities::submit_article_update;
use crate::backend::utils::generate_article_version;
use crate::common::DbArticle;
use crate::common::DbEdit;
+use crate::common::DbLocalUser;
use crate::common::EditVersion;
use activitypub_federation::config::Data;
-use activitypub_federation::fetch::object_id::ObjectId;
use diesel::ExpressionMethods;
use diesel::{
delete, insert_into, Identifiable, Insertable, PgConnection, QueryDsl, Queryable, RunQueryDsl,
@@ -76,9 +75,7 @@ impl DbConflict {
) -> MyResult
+ }
+ }
+ >
"Login"
"Register"
+
}
}
+
+fn do_logout() {
+ dbg!("do logout");
+ create_action(move |()| async move {
+ dbg!("run logout action");
+ logout(&GlobalState::read_hostname()).await.unwrap();
+ expect_context::>()
+ .get()
+ .update_my_profile();
+ });
+}
diff --git a/src/frontend/pages/login.rs b/src/frontend/pages/login.rs
index 1e9d3e3..e8df78c 100644
--- a/src/frontend/pages/login.rs
+++ b/src/frontend/pages/login.rs
@@ -1,51 +1,58 @@
+use crate::common::LoginUserData;
use crate::frontend::api::login;
-use leptos::ev::SubmitEvent;
+use crate::frontend::app::GlobalState;
+use crate::frontend::components::credentials::*;
use leptos::*;
-use log::info;
-
-// TODO: this seems to be working, but need to implement registration also
-// TODO: use leptos_form if possible
-// https://github.com/leptos-form/leptos_form/issues/18
-fn do_login(ev: SubmitEvent, username: String, password: String) {
- ev.prevent_default();
- spawn_local(async move {
- let res = login("localhost:8080", &username, &password).await;
- info!("{}", res.unwrap().jwt);
- });
-}
+use leptos_router::Redirect;
#[component]
pub fn Login() -> impl IntoView {
- let name = RwSignal::new(String::new());
- let password = RwSignal::new(String::new());
+ let (login_response, set_login_response) = create_signal(None::<()>);
+ let (login_error, set_login_error) = create_signal(None::);
+ let (wait_for_response, set_wait_for_response) = create_signal(false);
+
+ let login_action = create_action(move |(email, password): &(String, String)| {
+ let username = email.to_string();
+ let password = password.to_string();
+ let credentials = LoginUserData { username, password };
+ async move {
+ set_wait_for_response.update(|w| *w = true);
+ let result = login(&GlobalState::read_hostname(), credentials).await;
+ set_wait_for_response.update(|w| *w = false);
+ match result {
+ Ok(res) => {
+ expect_context::>()
+ .update(|state| state.my_profile = Some(res));
+ set_login_response.update(|v| *v = Some(()));
+ set_login_error.update(|e| *e = None);
+ }
+ Err(err) => {
+ let msg = err.0.to_string();
+ log::warn!("Unable to login: {msg}");
+ set_login_error.update(|e| *e = Some(msg));
+ }
+ }
+ }
+ });
+
+ let disabled = Signal::derive(move || wait_for_response.get());
view! {
-
+
+ }
+ }
+ >
+
+
}
}
diff --git a/src/frontend/pages/register.rs b/src/frontend/pages/register.rs
index 2aed240..847b41f 100644
--- a/src/frontend/pages/register.rs
+++ b/src/frontend/pages/register.rs
@@ -1,14 +1,12 @@
-use crate::common::{LoginResponse, RegisterUserData};
+use crate::common::RegisterUserData;
use crate::frontend::api::register;
-use crate::frontend::app::BackendHostname;
+use crate::frontend::app::GlobalState;
use crate::frontend::components::credentials::*;
-use crate::frontend::pages::Page;
use leptos::{logging::log, *};
-use leptos_router::*;
#[component]
pub fn Register() -> impl IntoView {
- let (register_response, set_register_response) = create_signal(None::);
+ let (register_response, set_register_response) = create_signal(None::<()>);
let (register_error, set_register_error) = create_signal(None::);
let (wait_for_response, set_wait_for_response) = create_signal(false);
@@ -19,11 +17,13 @@ pub fn Register() -> impl IntoView {
log!("Try to register new account for {}", credentials.username);
async move {
set_wait_for_response.update(|w| *w = true);
- let result = register(&BackendHostname::read(), credentials).await;
+ let result = register(&GlobalState::read_hostname(), credentials).await;
set_wait_for_response.update(|w| *w = false);
match result {
Ok(res) => {
- set_register_response.update(|v| *v = Some(res));
+ expect_context::>()
+ .update(|state| state.my_profile = Some(res));
+ set_register_response.update(|v| *v = Some(()));
set_register_error.update(|e| *e = None);
}
Err(err) => {
@@ -53,7 +53,6 @@ pub fn Register() -> impl IntoView {
}
>
"You have successfully registered."
- "You can now " "login" " with your new account."
}
}
diff --git a/src/main.rs b/src/main.rs
index 11cd734..40089c1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -23,5 +23,4 @@ fn main() {
mount_to_body(|| {
view! { }
});
- log::info!("test 2");
}
diff --git a/tests/common.rs b/tests/common.rs
index 20562a9..734fb3a 100644
--- a/tests/common.rs
+++ b/tests/common.rs
@@ -1,19 +1,18 @@
use anyhow::anyhow;
use ibis::backend::api::article::{CreateArticleData, EditArticleData, ForkArticleData};
use ibis::backend::api::instance::FollowInstance;
-use ibis::backend::api::user::RegisterUserData;
-use ibis::backend::api::user::{LoginResponse, LoginUserData};
+use ibis::backend::api::user::AUTH_COOKIE;
use ibis::backend::api::ResolveObject;
use ibis::backend::database::conflict::ApiConflict;
use ibis::backend::database::instance::DbInstance;
use ibis::backend::error::MyResult;
use ibis::backend::start;
use ibis::common::ArticleView;
-use ibis::frontend::api;
-use ibis::frontend::api::get_query;
-use ibis_lib::frontend::api;
+use ibis::common::LoginUserData;
+use ibis::common::RegisterUserData;
+use ibis::frontend::api::{get_article, get_query, handle_json_res, register};
use once_cell::sync::Lazy;
-use reqwest::{Client, StatusCode};
+use reqwest::{Client, ClientBuilder, StatusCode};
use serde::de::Deserialize;
use std::env::current_dir;
use std::fs::create_dir_all;
@@ -97,6 +96,7 @@ fn generate_db_path(name: &'static str, port: i32) -> String {
pub struct IbisInstance {
pub hostname: String,
+ pub client: Client,
pub jwt: String,
db_path: String,
db_handle: JoinHandle<()>,
@@ -123,11 +123,18 @@ impl IbisInstance {
});
// wait a moment for the backend to start
tokio::time::sleep(Duration::from_millis(100)).await;
- let register_res = api::register(&hostname, username, "hunter2").await.unwrap();
- assert!(!register_res.jwt.is_empty());
+ let form = RegisterUserData {
+ username: username.to_string(),
+ password: "hunter2".to_string(),
+ };
+ // TODO: use a separate http client for each backend instance, with cookie store for auth
+ // TODO: how to pass the client/hostname to api client methods?
+ // probably create a struct ApiClient(hostname, client) with all api methods in impl
+ let client = ClientBuilder::new().cookie_store(true).build();
+ let register_res = register(&hostname, form).await.unwrap();
Self {
- jwt: register_res.jwt,
hostname,
+ client,
db_path,
db_handle: handle,
}
@@ -156,7 +163,7 @@ pub async fn create_article(instance: &IbisInstance, title: String) -> MyResult<
.post(format!("http://{}/api/v1/article", &instance.hostname))
.form(&create_form)
.bearer_auth(&instance.jwt);
- let article: ArticleView = api::handle_json_res(req).await?;
+ let article: ArticleView = handle_json_res(req).await?;
// create initial edit to ensure that conflicts are generated (there are no conflicts on empty file)
let edit_form = EditArticleData {
@@ -176,7 +183,7 @@ pub async fn edit_article_with_conflict(
.patch(format!("http://{}/api/v1/article", instance.hostname))
.form(edit_form)
.bearer_auth(&instance.jwt);
- api::handle_json_res(req).await
+ handle_json_res(req).await?
}
pub async fn get_conflicts(instance: &IbisInstance) -> MyResult> {
@@ -186,7 +193,7 @@ pub async fn get_conflicts(instance: &IbisInstance) -> MyResult
&instance.hostname
))
.bearer_auth(&instance.jwt);
- api::handle_json_res(req).await
+ handle_json_res(req).await?
}
pub async fn edit_article(
@@ -195,7 +202,8 @@ pub async fn edit_article(
) -> MyResult {
let edit_res = edit_article_with_conflict(instance, edit_form).await?;
assert!(edit_res.is_none());
- api::get_article(&instance.hostname, edit_form.article_id).await
+
+ get_article(&instance.hostname, todo!("{}", edit_form.article_id)).await
}
pub async fn get(hostname: &str, endpoint: &str) -> MyResult
@@ -213,7 +221,7 @@ pub async fn fork_article(
.post(format!("http://{}/api/v1/article/fork", instance.hostname))
.form(form)
.bearer_auth(&instance.jwt);
- api::handle_json_res(req).await
+ handle_json_res(req).await?
}
pub async fn follow_instance(instance: &IbisInstance, follow_instance: &str) -> MyResult<()> {
@@ -222,7 +230,7 @@ pub async fn follow_instance(instance: &IbisInstance, follow_instance: &str) ->
id: Url::parse(&format!("http://{}", follow_instance))?,
};
let instance_resolved: DbInstance =
- api::get_query(&instance.hostname, "resolve_instance", Some(resolve_form)).await?;
+ get_query(&instance.hostname, "resolve_instance", Some(resolve_form)).await?;
// send follow
let follow_form = FollowInstance {
diff --git a/tests/test.rs b/tests/test.rs
index 071e1b6..3f2d86b 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -2,12 +2,12 @@ extern crate ibis;
mod common;
+use crate::common::fork_article;
+use crate::common::get_conflicts;
use crate::common::{
create_article, edit_article, edit_article_with_conflict, follow_instance, get, TestData,
CLIENT, TEST_ARTICLE_DEFAULT_TEXT,
};
-use crate::common::{fork_article, login};
-use crate::common::{get_conflicts, register};
use ibis::backend::api::article::{CreateArticleData, EditArticleData, ForkArticleData};
use ibis::backend::api::{ResolveObject, SearchArticleData};
use ibis::backend::database::instance::{DbInstance, InstanceView};
@@ -17,6 +17,7 @@ use ibis::common::DbArticle;
use ibis::frontend::api::get_article;
use ibis::frontend::api::get_query;
use ibis::frontend::api::handle_json_res;
+use ibis::frontend::api::login;
use pretty_assertions::{assert_eq, assert_ne};
use url::Url;