diff --git a/Cargo.toml b/Cargo.toml index 00f3635..9d9c6fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,7 +64,7 @@ axum-extra = { version = "0.9.4", features = ["cookie"], optional = true } leptos = "0.6.15" leptos_meta = "0.6.15" leptos_router = "0.6.15" -leptos_axum = { version = "0.6.15", optional = true } +leptos_axum = { version = "0.6.15", features = ["wasm"], optional = true } bcrypt = "0.15.1" chrono = { version = "0.4.38", features = ["serde"] } diesel = { version = "2.2.4", features = [ diff --git a/src/backend/api/article.rs b/src/backend/api/article.rs index e6b1fd9..06193eb 100644 --- a/src/backend/api/article.rs +++ b/src/backend/api/article.rs @@ -68,7 +68,7 @@ pub(in crate::backend::api) async fn create_article( instance_id: local_instance.id, local: true, protected: false, - approved: !data.config.article_approval, + approved: !data.config.config.article_approval, }; let article = DbArticle::create(form, &data)?; @@ -214,7 +214,7 @@ pub(in crate::backend::api) async fn fork_article( instance_id: local_instance.id, local: true, protected: false, - approved: !data.config.article_approval, + approved: !data.config.config.article_approval, }; let article = DbArticle::create(form, &data)?; diff --git a/src/backend/api/mod.rs b/src/backend/api/mod.rs index c0e216c..92eb93d 100644 --- a/src/backend/api/mod.rs +++ b/src/backend/api/mod.rs @@ -12,20 +12,12 @@ use crate::{ search_article, }, instance::{follow_instance, get_instance, resolve_instance}, - user::{ - get_user, - login_user, - logout_user, - my_profile, - register_user, - validate, - AUTH_COOKIE, - }, + user::{get_user, login_user, logout_user, register_user, validate, AUTH_COOKIE}, }, database::IbisData, error::MyResult, }, - common::LocalUserView, + common::{LocalUserView, SiteView}, }; use activitypub_federation::config::Data; use anyhow::anyhow; @@ -36,9 +28,11 @@ use axum::{ middleware::{self, Next}, response::Response, routing::{get, post}, + Json, Router, }; use axum_extra::extract::CookieJar; +use axum_macros::debug_handler; use instance::list_remote_instances; use user::{count_notifications, list_notifications}; @@ -67,8 +61,8 @@ pub fn api_routes() -> Router<()> { .route("/user/notifications/count", get(count_notifications)) .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("/site", get(site_view)) .route_layer(middleware::from_fn(auth)) } @@ -93,3 +87,20 @@ fn check_is_admin(user: &LocalUserView) -> MyResult<()> { } Ok(()) } + +#[debug_handler] +pub(in crate::backend::api) async fn site_view( + data: Data, + jar: CookieJar, +) -> MyResult> { + let jwt = jar.get(AUTH_COOKIE).map(|c| c.value()); + let my_profile = if let Some(jwt) = jwt { + Some(validate(jwt, &data).await?) + } else { + None + }; + Ok(Json(SiteView { + my_profile, + config: data.config.config.clone(), + })) +} diff --git a/src/backend/api/user.rs b/src/backend/api/user.rs index 452242a..63415bc 100644 --- a/src/backend/api/user.rs +++ b/src/backend/api/user.rs @@ -77,7 +77,7 @@ pub(in crate::backend::api) async fn register_user( jar: CookieJar, Form(form): Form, ) -> MyResult<(CookieJar, Json)> { - if !data.config.registration_open { + if !data.config.config.registration_open { return Err(anyhow!("Registration is closed").into()); } let user = DbPerson::create_local(form.username, form.password, false, &data)?; @@ -122,19 +122,6 @@ fn create_cookie(jwt: String, data: &Data) -> Cookie<'static> { .build() } -#[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( data: Data, diff --git a/src/backend/config.rs b/src/backend/config.rs index 5015e3a..a2c2514 100644 --- a/src/backend/config.rs +++ b/src/backend/config.rs @@ -1,4 +1,4 @@ -use crate::backend::error::MyResult; +use crate::{backend::error::MyResult, common::SharedConfig}; use config::Config; use doku::Document; use serde::Deserialize; @@ -10,17 +10,10 @@ use smart_default::SmartDefault; pub struct IbisConfig { /// Details about the PostgreSQL database connection pub database: IbisConfigDatabase, - /// Whether users can create new accounts - #[default = true] - #[doku(example = "true")] - pub registration_open: bool, - /// Whether admins need to approve new articles - #[default = false] - #[doku(example = "false")] - pub article_approval: bool, /// Details of the initial admin account pub setup: IbisConfigSetup, pub federation: IbisConfigFederation, + pub config: SharedConfig, } impl IbisConfig { diff --git a/src/backend/database/instance.rs b/src/backend/database/instance.rs index 44f7cf9..be1d31f 100644 --- a/src/backend/database/instance.rs +++ b/src/backend/database/instance.rs @@ -86,7 +86,6 @@ impl DbInstance { Ok(InstanceView { instance, followers, - registration_open: data.config.registration_open, }) } diff --git a/src/backend/nodeinfo.rs b/src/backend/nodeinfo.rs index a20a419..6e35127 100644 --- a/src/backend/nodeinfo.rs +++ b/src/backend/nodeinfo.rs @@ -34,7 +34,7 @@ async fn node_info(data: Data) -> MyResult> { version: env!("CARGO_PKG_VERSION").to_string(), }, protocols: vec!["activitypub".to_string()], - open_registrations: data.config.registration_open, + open_registrations: data.config.config.registration_open, })) } diff --git a/src/common/mod.rs b/src/common/mod.rs index 44bc823..f6f2fdf 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -6,6 +6,7 @@ use chrono::{DateTime, Utc}; use newtypes::{ArticleId, ConflictId, EditId, InstanceId, PersonId}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; +use smart_default::SmartDefault; use url::Url; use uuid::Uuid; #[cfg(feature = "ssr")] @@ -17,6 +18,7 @@ use { }, activitypub_federation::fetch::{collection_id::CollectionId, object_id::ObjectId}, diesel::{Identifiable, Queryable, Selectable}, + doku::Document, }; pub const MAIN_PAGE_NAME: &str = "Main_Page"; @@ -133,6 +135,30 @@ pub struct LoginUserForm { pub password: String, } +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, SmartDefault)] +#[serde(default)] +#[serde(deny_unknown_fields)] +#[cfg_attr(feature = "ssr", derive(Queryable, Document))] +#[cfg_attr(feature = "ssr", diesel(check_for_backend(diesel::pg::Pg)))] +pub struct SharedConfig { + /// Whether users can create new accounts + #[default = true] + #[cfg_attr(feature = "ssr", doku(example = "true"))] + pub registration_open: bool, + /// Whether admins need to approve new articles + #[default = false] + #[cfg_attr(feature = "ssr", doku(example = "false"))] + pub article_approval: bool, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[cfg_attr(feature = "ssr", derive(Queryable))] +#[cfg_attr(feature = "ssr", diesel(check_for_backend(diesel::pg::Pg)))] +pub struct SiteView { + pub my_profile: Option, + pub config: SharedConfig, +} + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[cfg_attr(feature = "ssr", derive(Queryable))] #[cfg_attr(feature = "ssr", diesel(check_for_backend(diesel::pg::Pg)))] @@ -306,7 +332,6 @@ impl DbInstance { pub struct InstanceView { pub instance: DbInstance, pub followers: Vec, - pub registration_open: bool, } #[derive(Deserialize, Serialize, Clone, Debug)]