1
0
Fork 0
mirror of https://github.com/Nutomic/ibis.git synced 2024-12-24 04:51:22 +00:00

finally compiling

This commit is contained in:
Felix Ableitner 2024-11-20 10:21:26 +01:00
parent 5847e99128
commit 688ead9217
5 changed files with 136 additions and 132 deletions

2
Cargo.lock generated
View file

@ -1781,6 +1781,7 @@ dependencies = [
"getrandom", "getrandom",
"gloo-net", "gloo-net",
"hex", "hex",
"http",
"jsonwebtoken", "jsonwebtoken",
"katex", "katex",
"leptos", "leptos",
@ -1803,6 +1804,7 @@ dependencies = [
"send_wrapper", "send_wrapper",
"serde", "serde",
"serde_json", "serde_json",
"serde_urlencoded",
"sha2", "sha2",
"smart-default", "smart-default",
"time", "time",

View file

@ -29,12 +29,9 @@ ssr = [
"config", "config",
"tower", "tower",
"tower-layer", "tower-layer",
"reqwest","diesel-derive-newtype"
] ]
hydrate = [ hydrate = ["leptos/hydrate", "katex/wasm-js", "gloo-net"]
"leptos/hydrate",
"katex/wasm-js",
]
diesel-derive-newtype = ["dep:diesel-derive-newtype"]
# This profile significantly speeds up build time. If debug info is needed you can comment the line # This profile significantly speeds up build time. If debug info is needed you can comment the line
# out temporarily, but make sure to leave this in the main branch. # out temporarily, but make sure to leave this in the main branch.
@ -73,7 +70,10 @@ sha2 = "0.10.8"
uuid = { version = "1.11.0", features = ["serde"] } uuid = { version = "1.11.0", features = ["serde"] }
serde = { version = "1.0.215", features = ["derive"] } serde = { version = "1.0.215", features = ["derive"] }
url = { version = "2.5.3", features = ["serde"] } url = { version = "2.5.3", features = ["serde"] }
reqwest = { version = "0.12.9", features = ["json", "cookies"] } reqwest = { version = "0.12.9", features = [
"json",
"cookies",
], optional = true }
log = "0.4" log = "0.4"
tracing = "0.1.40" tracing = "0.1.40"
once_cell = "1.20.2" once_cell = "1.20.2"
@ -121,8 +121,10 @@ tower = { version = "0.5.1", optional = true }
tower-layer = { version = "0.3.3", optional = true } tower-layer = { version = "0.3.3", optional = true }
console_log = "1.0.0" console_log = "1.0.0"
send_wrapper = "0.6.0" send_wrapper = "0.6.0"
gloo-net = "0.6.0" gloo-net = { version = "0.6.0", optional = true }
web-sys = "0.3.72" web-sys = "0.3.72"
http = "1.1.0"
serde_urlencoded = "0.7.1"
[dev-dependencies] [dev-dependencies]
pretty_assertions = "1.4.1" pretty_assertions = "1.4.1"

View file

@ -36,7 +36,7 @@ pub struct GetArticleForm {
pub id: Option<ArticleId>, pub id: Option<ArticleId>,
} }
#[derive(Deserialize, Serialize, Clone, Default)] #[derive(Deserialize, Serialize, Clone, Default, Debug)]
pub struct ListArticlesForm { pub struct ListArticlesForm {
pub only_local: Option<bool>, pub only_local: Option<bool>,
pub instance_id: Option<InstanceId>, pub instance_id: Option<InstanceId>,
@ -128,13 +128,13 @@ impl Default for EditVersion {
} }
} }
#[derive(Deserialize, Serialize, Clone)] #[derive(Deserialize, Serialize, Clone, Debug)]
pub struct RegisterUserForm { pub struct RegisterUserForm {
pub username: String, pub username: String,
pub password: String, pub password: String,
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize, Debug)]
pub struct LoginUserForm { pub struct LoginUserForm {
pub username: String, pub username: String,
pub password: String, pub password: String,
@ -188,7 +188,7 @@ impl DbPerson {
} }
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize, Debug)]
pub struct CreateArticleForm { pub struct CreateArticleForm {
pub title: String, pub title: String,
pub text: String, pub text: String,
@ -217,19 +217,19 @@ pub struct ProtectArticleForm {
pub protected: bool, pub protected: bool,
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize, Debug)]
pub struct ForkArticleForm { pub struct ForkArticleForm {
pub article_id: ArticleId, pub article_id: ArticleId,
pub new_title: String, pub new_title: String,
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize, Debug)]
pub struct ApproveArticleForm { pub struct ApproveArticleForm {
pub article_id: ArticleId, pub article_id: ArticleId,
pub approve: bool, pub approve: bool,
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize, Debug)]
pub struct DeleteConflictForm { pub struct DeleteConflictForm {
pub conflict_id: ConflictId, pub conflict_id: ConflictId,
} }
@ -244,12 +244,12 @@ pub struct FollowInstance {
pub id: InstanceId, pub id: InstanceId,
} }
#[derive(Deserialize, Serialize, Clone)] #[derive(Deserialize, Serialize, Clone, Debug)]
pub struct SearchArticleForm { pub struct SearchArticleForm {
pub query: String, pub query: String,
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize, Debug)]
pub struct ResolveObject { pub struct ResolveObject {
pub id: Url, pub id: Url,
} }

View file

@ -11,37 +11,40 @@ use crate::{
frontend::error::MyResult, frontend::error::MyResult,
}; };
use anyhow::anyhow; use anyhow::anyhow;
use reqwest::{Client, RequestBuilder, StatusCode}; use http::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::sync::LazyLock; use std::sync::LazyLock;
use url::Url; use url::Url;
pub static CLIENT: LazyLock<ApiClient> = LazyLock::new(|| ApiClient::new(Client::new(), None)); pub static CLIENT: LazyLock<ApiClient> = LazyLock::new(|| {
#[cfg(feature = "ssr")]
{
ApiClient::new(reqwest::Client::new(), None)
}
#[cfg(not(feature = "ssr"))]
{
ApiClient::new()
}
});
#[derive(Clone)] #[derive(Clone)]
pub struct ApiClient { pub struct ApiClient {
client: Client, #[cfg(feature = "ssr")]
client: reqwest::Client,
pub hostname: String, pub hostname: String,
ssl: bool, ssl: bool,
} }
impl ApiClient { impl ApiClient {
pub fn new(client: Client, hostname_: Option<String>) -> Self { #[cfg(feature = "ssr")]
pub fn new(client: reqwest::Client, hostname_: Option<String>) -> Self {
let mut hostname; let mut hostname;
let ssl; let ssl;
#[cfg(not(feature = "ssr"))] use leptos::config::get_config_from_str;
{ let leptos_options = get_config_from_str(include_str!("../../Cargo.toml")).unwrap();
use leptos_use::use_document; hostname = leptos_options.site_addr.to_string();
hostname = use_document().location().unwrap().host().unwrap(); ssl = false;
ssl = !cfg!(debug_assertions);
}
#[cfg(feature = "ssr")]
{
use leptos::config::get_config_from_str;
let leptos_options = get_config_from_str(include_str!("../../Cargo.toml")).unwrap();
hostname = leptos_options.site_addr.to_string();
ssl = false;
}
// required for tests // required for tests
if let Some(hostname_) = hostname_ { if let Some(hostname_) = hostname_ {
hostname = hostname_; hostname = hostname_;
@ -52,17 +55,12 @@ impl ApiClient {
ssl, ssl,
} }
} }
#[cfg(not(feature = "ssr"))]
async fn get<T, R>(&self, endpoint: &str, query: Option<R>) -> MyResult<T> pub fn new() -> Self {
where use leptos_use::use_document;
T: for<'de> Deserialize<'de>, let hostname = use_document().location().unwrap().host().unwrap();
R: Serialize, let ssl = !cfg!(debug_assertions);
{ Self { hostname, ssl }
let mut req = self.client.get(self.request_endpoint(endpoint));
if let Some(query) = query {
req = req.query(&query);
}
self.send::<T>(req).await
} }
pub async fn get_article(&self, data: GetArticleForm) -> MyResult<ArticleView> { pub async fn get_article(&self, data: GetArticleForm) -> MyResult<ArticleView> {
@ -74,38 +72,24 @@ impl ApiClient {
} }
pub async fn register(&self, register_form: RegisterUserForm) -> MyResult<LocalUserView> { pub async fn register(&self, register_form: RegisterUserForm) -> MyResult<LocalUserView> {
let req = self self.post("/api/v1/account/register", Some(&register_form))
.client .await
.post(self.request_endpoint("/api/v1/account/register"))
.form(&register_form);
self.send::<LocalUserView>(req).await
} }
pub async fn login(&self, login_form: LoginUserForm) -> MyResult<LocalUserView> { pub async fn login(&self, login_form: LoginUserForm) -> MyResult<LocalUserView> {
let req = self self.post("/api/v1/account/login", Some(&login_form)).await
.client
.post(self.request_endpoint("/api/v1/account/login"))
.form(&login_form);
self.send::<LocalUserView>(req).await
} }
pub async fn create_article(&self, data: &CreateArticleForm) -> MyResult<ArticleView> { pub async fn create_article(&self, data: &CreateArticleForm) -> MyResult<ArticleView> {
let req = self self.send(Method::POST, "/api/v1/article", Some(&data))
.client .await
.post(self.request_endpoint("/api/v1/article"))
.form(data);
self.send(req).await
} }
pub async fn edit_article_with_conflict( pub async fn edit_article_with_conflict(
&self, &self,
edit_form: &EditArticleForm, edit_form: &EditArticleForm,
) -> MyResult<Option<ApiConflict>> { ) -> MyResult<Option<ApiConflict>> {
let req = self self.get("/api/v1/article", Some(&edit_form)).await
.client
.patch(self.request_endpoint("/api/v1/article"))
.form(edit_form);
self.send(req).await
} }
pub async fn edit_article(&self, edit_form: &EditArticleForm) -> MyResult<ArticleView> { pub async fn edit_article(&self, edit_form: &EditArticleForm) -> MyResult<ArticleView> {
@ -121,17 +105,13 @@ impl ApiClient {
} }
pub async fn notifications_list(&self) -> MyResult<Vec<Notification>> { pub async fn notifications_list(&self) -> MyResult<Vec<Notification>> {
let req = self self.get("/api/v1/user/notifications/list", None::<()>)
.client .await
.get(self.request_endpoint("/api/v1/user/notifications/list"));
self.send(req).await
} }
pub async fn notifications_count(&self) -> MyResult<usize> { pub async fn notifications_count(&self) -> MyResult<usize> {
let req = self self.get("/api/v1/user/notifications/count", None::<()>)
.client .await
.get(self.request_endpoint("/api/v1/user/notifications/count"));
self.send(req).await
} }
pub async fn approve_article(&self, article_id: ArticleId, approve: bool) -> MyResult<()> { pub async fn approve_article(&self, article_id: ArticleId, approve: bool) -> MyResult<()> {
@ -139,20 +119,13 @@ impl ApiClient {
article_id, article_id,
approve, approve,
}; };
let req = self self.post("/api/v1/article/approve", Some(&form)).await
.client
.post(self.request_endpoint("/api/v1/article/approve"))
.form(&form);
self.send(req).await
} }
pub async fn delete_conflict(&self, conflict_id: ConflictId) -> MyResult<()> { pub async fn delete_conflict(&self, conflict_id: ConflictId) -> MyResult<()> {
let form = DeleteConflictForm { conflict_id }; let form = DeleteConflictForm { conflict_id };
let req = self self.send(Method::DELETE, "/api/v1/conflict", Some(form))
.client .await
.delete(self.request_endpoint("/api/v1/conflict"))
.form(&form);
self.send(req).await
} }
pub async fn search(&self, search_form: &SearchArticleForm) -> MyResult<Vec<DbArticle>> { pub async fn search(&self, search_form: &SearchArticleForm) -> MyResult<Vec<DbArticle>> {
@ -164,7 +137,7 @@ impl ApiClient {
} }
pub async fn get_instance(&self, get_form: &GetInstance) -> MyResult<InstanceView> { pub async fn get_instance(&self, get_form: &GetInstance) -> MyResult<InstanceView> {
self.get("/api/v1/instance", Some(get_form)).await self.get("/api/v1/instance", Some(&get_form)).await
} }
pub async fn list_instances(&self) -> MyResult<Vec<DbInstance>> { pub async fn list_instances(&self) -> MyResult<Vec<DbInstance>> {
@ -210,31 +183,22 @@ impl ApiClient {
} }
pub async fn site(&self) -> MyResult<SiteView> { pub async fn site(&self) -> MyResult<SiteView> {
let req = self.client.get(self.request_endpoint("/api/v1/site")); self.get("/api/v1/site", None::<()>).await
self.send(req).await
} }
pub async fn logout(&self) -> MyResult<()> { pub async fn logout(&self) -> MyResult<()> {
let req = self Ok(self
.client .get("/api/v1/account/logout", None::<()>)
.get(self.request_endpoint("/api/v1/account/logout")); .await
Ok(self.send(req).await.unwrap()) .unwrap())
} }
pub async fn fork_article(&self, form: &ForkArticleForm) -> MyResult<ArticleView> { pub async fn fork_article(&self, form: &ForkArticleForm) -> MyResult<ArticleView> {
let req = self Ok(self.post("/api/v1/article/fork", Some(form)).await.unwrap())
.client
.post(self.request_endpoint("/api/v1/article/fork"))
.form(form);
Ok(self.send(req).await.unwrap())
} }
pub async fn protect_article(&self, params: &ProtectArticleForm) -> MyResult<DbArticle> { pub async fn protect_article(&self, params: &ProtectArticleForm) -> MyResult<DbArticle> {
let req = self self.post("/api/v1/article/protect", Some(params)).await
.client
.post(self.request_endpoint("/api/v1/article/protect"))
.form(params);
self.send(req).await
} }
pub async fn resolve_article(&self, id: Url) -> MyResult<ArticleView> { pub async fn resolve_article(&self, id: Url) -> MyResult<ArticleView> {
@ -245,47 +209,67 @@ impl ApiClient {
pub async fn resolve_instance(&self, id: Url) -> MyResult<DbInstance> { pub async fn resolve_instance(&self, id: Url) -> MyResult<DbInstance> {
let resolve_object = ResolveObject { id }; let resolve_object = ResolveObject { id };
self.get("/api/v1/instance/resolve", Some(resolve_object)) self.get("/api/v1/user", Some(resolve_object)).await
.await
} }
pub async fn get_user(&self, data: GetUserForm) -> MyResult<DbPerson> { pub async fn get_user(&self, data: GetUserForm) -> MyResult<DbPerson> {
self.get("/api/v1/user", Some(data)).await self.get("/api/v1/user", Some(data)).await
} }
#[cfg(feature = "ssr")]
async fn send<T>(&self, mut req: RequestBuilder) -> MyResult<T> async fn get<T, R>(&self, endpoint: &str, query: Option<R>) -> MyResult<T>
where where
T: for<'de> Deserialize<'de>, T: for<'de> Deserialize<'de>,
R: Serialize + Debug,
{
self.send(Method::GET, endpoint, query).await
}
async fn post<T, R>(&self, endpoint: &str, query: Option<R>) -> MyResult<T>
where
T: for<'de> Deserialize<'de>,
R: Serialize + Debug,
{
self.send(Method::POST, endpoint, query).await
}
#[cfg(feature = "ssr")]
async fn send<P, T>(&self, method: Method, path: &str, params: Option<P>) -> MyResult<T>
where
P: Serialize + Debug,
T: for<'de> Deserialize<'de>,
{ {
use crate::common::{Auth, AUTH_COOKIE}; use crate::common::{Auth, AUTH_COOKIE};
use leptos::prelude::use_context; use leptos::prelude::use_context;
use reqwest::header::HeaderName; use reqwest::header::HeaderName;
let mut req = self
.client
.request(method, self.request_endpoint(path))
.query(&params);
let auth = use_context::<Auth>(); let auth = use_context::<Auth>();
if let Some(Auth(Some(auth))) = auth { if let Some(Auth(Some(auth))) = auth {
req = req.header(HeaderName::from_static(AUTH_COOKIE), auth); req = req.header(HeaderName::from_static(AUTH_COOKIE), auth);
} }
let res = req.send().await?; let res = req.send().await?;
let status = res.status(); let status = res.status();
let text = res.text().await?; let text = res.text().await?.to_string();
if status == StatusCode::OK { Self::response(status.into(), text)
Ok(serde_json::from_str(&text).map_err(|e| anyhow!("Json error on {text}: {e}"))?)
} else {
Err(anyhow!("API error: {text}").into())
}
} }
#[cfg(feature = "hydrate")] #[cfg(not(feature = "ssr"))]
fn send<T>( fn send<'a, P, T>(
&self, &'a self,
mut req: RequestBuilder, method: Method,
) -> impl std::future::Future<Output = MyResult<T>> + Send path: &'a str,
params: Option<P>,
) -> impl std::future::Future<Output = MyResult<T>> + Send + 'a
where where
P: Serialize + Debug + 'a,
T: for<'de> Deserialize<'de>, T: for<'de> Deserialize<'de>,
{ {
use gloo_net::http::*; use gloo_net::http::*;
use leptos::prelude::on_cleanup; use leptos::prelude::on_cleanup;
use send_wrapper::SendWrapper; use send_wrapper::SendWrapper;
use web_sys::RequestCredentials; use std::collections::HashMap;
use web_sys::{RequestCredentials, UrlSearchParams};
SendWrapper::new(async move { SendWrapper::new(async move {
let abort_controller = SendWrapper::new(web_sys::AbortController::new().ok()); let abort_controller = SendWrapper::new(web_sys::AbortController::new().ok());
@ -298,26 +282,42 @@ impl ApiClient {
} }
}); });
/* let path_with_endpoint = self.request_endpoint(path);
if status == StatusCode::OK { let path = if method == Method::GET {
Ok(serde_json::from_str(&text).map_err(|e| anyhow!("Json error on {text}: {e}"))?) let query = serde_urlencoded::to_string(&params).unwrap();
format!("{path_with_endpoint}?{query}")
} else { } else {
Err(anyhow!("API error: {text}").into()) path_with_endpoint
} };
*/ log::info!("{path}");
Ok(RequestBuilder::new("/api/v1/site") let builder = RequestBuilder::new(&path)
.method(Method::GET) .method(method.clone())
.abort_signal(abort_signal.as_ref()) .abort_signal(abort_signal.as_ref())
.credentials(RequestCredentials::Include) .credentials(RequestCredentials::Include);
.send() let req = if method == Method::POST {
.await builder.json(&params)
.unwrap() } else {
.json() builder.build()
.await }
.unwrap()) .unwrap();
let res = req.send().await.unwrap();
let status = res.status();
let text = res.text().await.unwrap();
Self::response(status, text)
}) })
} }
fn response<T>(status: u16, text: String) -> MyResult<T>
where
T: for<'de> Deserialize<'de>,
{
if status == StatusCode::OK {
Ok(serde_json::from_str(&text).map_err(|e| anyhow!("Json error on {text}: {e}"))?)
} else {
Err(anyhow!("API error: {text}").into())
}
}
fn request_endpoint(&self, path: &str) -> String { fn request_endpoint(&self, path: &str) -> String {
let protocol = if self.ssl { "https" } else { "http" }; let protocol = if self.ssl { "https" } else { "http" };
format!("{protocol}://{}{path}", &self.hostname) format!("{protocol}://{}{path}", &self.hostname)

View file

@ -16,7 +16,7 @@ pub fn hydrate() {
use crate::frontend::app::App; use crate::frontend::app::App;
console_log::init_with_level(log::Level::Debug).expect("error initializing logger"); console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
console_error_panic_hook::set_once(); console_error_panic_hook::set_once();
mount_to_body(App); leptos::mount::hydrate_body(App);
} }
fn article_link(article: &DbArticle) -> String { fn article_link(article: &DbArticle) -> String {