diff --git a/.tci b/.tci index 5cc5043..ed53713 100755 --- a/.tci +++ b/.tci @@ -12,7 +12,7 @@ echo "restarting service" systemctl --user start upub echo "rebuilding frontend" cd web -CARGO_BUILD_JOBS=4 /opt/bin/trunk build --release --public-url 'https://dev.upub.social/web' +CARGO_BUILD_JOBS=4 /opt/bin/trunk build --profile=wasm-release --public-url 'https://dev.upub.social/web' echo "deploying frontend" rm /srv/http/upub/dev/web/* mv ./dist/* /srv/http/upub/dev/web/ diff --git a/Cargo.lock b/Cargo.lock index c8b4da0..c736f71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -270,44 +270,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", - "axum-core 0.4.5", + "axum-core", "bytes", "futures-util", "http 1.2.0", "http-body", "http-body-util", - "itoa", - "matchit 0.7.3", - "memchr", - "mime", - "multer", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "axum" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" -dependencies = [ - "axum-core 0.5.0", - "bytes", - "form_urlencoded", - "futures-util", - "http 1.2.0", - "http-body", - "http-body-util", "hyper", "hyper-util", "itoa", - "matchit 0.8.4", + "matchit", "memchr", "mime", "multer", @@ -344,25 +316,6 @@ dependencies = [ "sync_wrapper", "tower-layer", "tower-service", -] - -[[package]] -name = "axum-core" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" -dependencies = [ - "bytes", - "futures-util", - "http 1.2.0", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper", - "tower-layer", - "tower-service", "tracing", ] @@ -1710,7 +1663,7 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" name = "httpsign" version = "0.1.0" dependencies = [ - "axum 0.8.1", + "axum", "base64", "openssl", "thiserror 2.0.11", @@ -2137,7 +2090,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43b613d5784037baee42a11d21bc263adfc1a55e416556a3d5bfe39c7b87fadf" dependencies = [ "any_spawner", - "axum 0.7.9", + "axum", "dashmap", "futures", "hydration_context", @@ -2452,12 +2405,6 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" -[[package]] -name = "matchit" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" - [[package]] name = "md-5" version = "0.10.6" @@ -3881,7 +3828,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5dd7fcccd3ef2081da086c1f8595b506627abbbbc9f64be0141d2251219570e" dependencies = [ - "axum 0.7.9", + "axum", "bytes", "const_format", "dashmap", @@ -5124,11 +5071,13 @@ dependencies = [ "apb", "async-recursion", "async-trait", + "axum", "base64", "chrono", "hmac", "httpsign", "jrd", + "leptos_config", "mdhtml", "nodeinfo", "openssl", @@ -5197,14 +5146,12 @@ name = "upub-routes" version = "0.3.0" dependencies = [ "apb", - "axum 0.8.1", + "axum", "chrono", "httpsign", "jrd", - "leptos", "leptos_axum", - "leptos_meta", - "leptos_router", + "leptos_config", "mastodon-async-entities", "nodeinfo", "rand", @@ -5219,6 +5166,7 @@ dependencies = [ "tower-http", "tracing", "upub", + "upub-web", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e1a094b..6805f36 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ repository = "https://git.alemi.dev/upub.git" readme = "README.md" [[bin]] -name = "upub" +name = "upub-bin" path = "main.rs" [dependencies] @@ -50,8 +50,14 @@ serve = ["dep:upub-routes"] migrate = ["dep:upub-migrations"] cli = ["dep:upub-cli"] worker = ["dep:upub-worker"] -web = [] -web-build-fe = [] +web = ["upub/web", "upub-routes?/web"] + +[[workspace.metadata.leptos]] +name = "upub" +bin-package = "upub-bin" +bin-features = ["serve", "migrate", "cli", "worker", "web"] +lib-package = "upub-web" +lib-features = ["leptos-hydrate"] [profile.wasm-release] inherits = "release" diff --git a/build.rs b/build.rs deleted file mode 100644 index 3511eff..0000000 --- a/build.rs +++ /dev/null @@ -1,12 +0,0 @@ -fn main() { - #[cfg(all(feature = "web", feature = "web-build-fe"))] - { - println!("cargo:warning=running sub-process to build frontend"); - let status = std::process::Command::new("cargo") - .current_dir("web") - .args(["build", "--profile=wasm-release", "--target=wasm32-unknown-unknown"]) - .status() - .unwrap(); - assert!(status.success(), "failed building wasm bundle"); - } -} diff --git a/core/Cargo.toml b/core/Cargo.toml index d1a5547..a935bae 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -36,3 +36,9 @@ reqwest = { version = "0.12", features = ["json"] } apb = { path = "../apb", features = ["unstructured", "orm", "did-core", "activitypub-miscellaneous-terms", "activitypub-fe", "activitypub-counters", "litepub", "ostatus", "toot"] } # nodeinfo = "0.0.2" # the version on crates.io doesn't re-export necessary types to build the struct!!! nodeinfo = { git = "https://codeberg.org/thefederationinfo/nodeinfo-rs", rev = "e865094804" } +leptos_config = { version = "0.7", optional = true } +axum = { version = "0.7", optional = true } + +[features] +default = [] +web = ["dep:leptos_config", "dep:axum"] diff --git a/core/src/context.rs b/core/src/context.rs index e066bcc..cd84d62 100644 --- a/core/src/context.rs +++ b/core/src/context.rs @@ -193,3 +193,30 @@ pub enum Internal { Activity(i64), Actor(i64), } + +#[cfg(feature = "web")] +mod leptos_state { + impl axum::extract::FromRef<super::Context> for leptos_config::LeptosOptions { + fn from_ref(_ctx: &super::Context) -> leptos_config::LeptosOptions { + static CONF: std::sync::OnceLock<leptos_config::LeptosOptions> = std::sync::OnceLock::new(); + CONF.get_or_init(|| + leptos_config::LeptosOptions { + env: { + #[cfg(debug_assertions)]{ leptos_config::Env::DEV } + #[cfg(not(debug_assertions))] { leptos_config::Env::PROD } + }, + output_name: "upub_web".into(), + site_root: "web/dist".into(), + site_pkg_dir: "pkg".into(), + site_addr: "127.0.0.1:3000/web".parse().expect("could not create socket addr"), // TODO we don't want to serve? what is this for?? + reload_port: 3001, + reload_external_port: None, + reload_ws_protocol: leptos_config::ReloadWSProtocol::WS, + not_found_path: "web/404.html".into(), + hash_file: "hash.txt".into(), + hash_files: true, + } + ).clone() + } + } +} diff --git a/routes/Cargo.toml b/routes/Cargo.toml index f2bd787..ccbefda 100644 --- a/routes/Cargo.toml +++ b/routes/Cargo.toml @@ -22,7 +22,7 @@ jrd = "0.1" tracing = "0.1" tokio = "1.43" reqwest = { version = "0.12", features = ["json"] } -axum = { version = "0.8", features = ["multipart"] } +axum = { version = "0.7", features = ["multipart"] } tower-http = { version = "0.6", features = ["cors", "trace"] } httpsign = { path = "../utils/httpsign/", features = ["axum"] } apb = { path = "../apb", features = ["unstructured", "orm", "activitypub-fe", "activitypub-counters", "litepub", "ostatus", "toot", "jsonld"] } @@ -33,18 +33,12 @@ nodeinfo = { git = "https://codeberg.org/thefederationinfo/nodeinfo-rs", rev = " mastodon-async-entities = { version = "1.1.0", optional = true } time = { version = "0.3", features = ["serde"], optional = true } # frontend -leptos = { version = "0.7", optional = true } -leptos_router = { version = "0.7", optional = true } leptos_axum = { version = "0.7", optional = true } -leptos_meta = { version = "0.7", optional = true } +leptos_config = { version = "0.7", optional = true } +upub-web = { path = "../web", default-features = false, optional = true } [features] -default = ["activitypub", "web"] +default = ["activitypub"] activitypub = [] mastodon = ["dep:mastodon-async-entities"] -web = [ - "dep:leptos", - "dep:leptos_router", - "dep:leptos_axum", - "dep:leptos_meta" -] +web = ["dep:leptos_axum", "dep:leptos_config", "dep:upub-web", "upub-web?/leptos-ssr", "upub/web"] diff --git a/routes/src/activitypub/mod.rs b/routes/src/activitypub/mod.rs index 523d855..4ff0414 100644 --- a/routes/src/activitypub/mod.rs +++ b/routes/src/activitypub/mod.rs @@ -22,7 +22,7 @@ impl super::ActivityPubRouter for Router<upub::Context> { // fetch route, to debug and retreive remote objects .route("/search", get(ap::application::search)) .route("/fetch", get(ap::application::ap_fetch)) - .route("/proxy/{hmac}/{uri}", get(ap::application::cloak_proxy)) + .route("/proxy/:hmac/:uri", get(ap::application::cloak_proxy)) .route("/inbox", post(ap::inbox::post)) .route("/inbox", get(ap::inbox::get)) .route("/inbox/page", get(ap::inbox::page)) @@ -39,49 +39,49 @@ impl super::ActivityPubRouter for Router<upub::Context> { .route("/.well-known/host-meta", get(ap::well_known::host_meta)) .route("/.well-known/nodeinfo", get(ap::well_known::nodeinfo_discovery)) .route("/.well-known/oauth-authorization-server", get(ap::well_known::oauth_authorization_server)) - .route("/nodeinfo/{version}", get(ap::well_known::nodeinfo)) + .route("/nodeinfo/:version", get(ap::well_known::nodeinfo)) // actor routes - .route("/actors/{id}", get(ap::actor::view)) - .route("/actors/{id}/inbox", post(ap::actor::inbox::post)) - .route("/actors/{id}/inbox", get(ap::actor::inbox::get)) - .route("/actors/{id}/inbox/page", get(ap::actor::inbox::page)) - .route("/actors/{id}/outbox", post(ap::actor::outbox::post)) - .route("/actors/{id}/outbox", get(ap::actor::outbox::get)) - .route("/actors/{id}/outbox/page", get(ap::actor::outbox::page)) - .route("/actors/{id}/notifications", get(ap::actor::notifications::get)) - .route("/actors/{id}/notifications/page", get(ap::actor::notifications::page)) - .route("/actors/{id}/followers", get(ap::actor::following::get::<false>)) - .route("/actors/{id}/followers/page", get(ap::actor::following::page::<false>)) - .route("/actors/{id}/following", get(ap::actor::following::get::<true>)) - .route("/actors/{id}/following/page", get(ap::actor::following::page::<true>)) - .route("/actors/{id}/likes", get(ap::actor::likes::get)) - .route("/actors/{id}/likes/page", get(ap::actor::likes::page)) + .route("/actors/:id", get(ap::actor::view)) + .route("/actors/:id/inbox", post(ap::actor::inbox::post)) + .route("/actors/:id/inbox", get(ap::actor::inbox::get)) + .route("/actors/:id/inbox/page", get(ap::actor::inbox::page)) + .route("/actors/:id/outbox", post(ap::actor::outbox::post)) + .route("/actors/:id/outbox", get(ap::actor::outbox::get)) + .route("/actors/:id/outbox/page", get(ap::actor::outbox::page)) + .route("/actors/:id/notifications", get(ap::actor::notifications::get)) + .route("/actors/:id/notifications/page", get(ap::actor::notifications::page)) + .route("/actors/:id/followers", get(ap::actor::following::get::<false>)) + .route("/actors/:id/followers/page", get(ap::actor::following::page::<false>)) + .route("/actors/:id/following", get(ap::actor::following::get::<true>)) + .route("/actors/:id/following/page", get(ap::actor::following::page::<true>)) + .route("/actors/:id/likes", get(ap::actor::likes::get)) + .route("/actors/:id/likes/page", get(ap::actor::likes::page)) .route("/groups", get(ap::groups::get)) .route("/groups/page", get(ap::groups::page)) - // .route("/actors/{id}/audience", get(ap::actor::audience::get)) - // .route("/actors/{id}/audience/page", get(ap::actor::audience::page)) + // .route("/actors/:id/audience", get(ap::actor::audience::get)) + // .route("/actors/:id/audience/page", get(ap::actor::audience::page)) // activities - .route("/activities/{id}", get(ap::activity::view)) + .route("/activities/:id", get(ap::activity::view)) // hashtags - .route("/tags/{id}", get(ap::tags::get)) - .route("/tags/{id}/page", get(ap::tags::page)) + .route("/tags/:id", get(ap::tags::get)) + .route("/tags/:id/page", get(ap::tags::page)) // specific object routes - .route("/objects/{id}", get(ap::object::view)) - .route("/objects/{id}/replies", get(ap::object::replies::get)) - .route("/objects/{id}/replies/page", get(ap::object::replies::page)) - .route("/objects/{id}/context", get(ap::object::context::get)) - .route("/objects/{id}/context/page", get(ap::object::context::page)) - .route("/objects/{id}/likes", get(ap::object::likes::get)) - .route("/objects/{id}/likes/page", get(ap::object::likes::page)) - .route("/objects/{id}/shares", get(ap::object::shares::get)) - .route("/objects/{id}/shares/page", get(ap::object::shares::page)) + .route("/objects/:id", get(ap::object::view)) + .route("/objects/:id/replies", get(ap::object::replies::get)) + .route("/objects/:id/replies/page", get(ap::object::replies::page)) + .route("/objects/:id/context", get(ap::object::context::get)) + .route("/objects/:id/context/page", get(ap::object::context::page)) + .route("/objects/:id/likes", get(ap::object::likes::get)) + .route("/objects/:id/likes/page", get(ap::object::likes::page)) + .route("/objects/:id/shares", get(ap::object::shares::get)) + .route("/objects/:id/shares/page", get(ap::object::shares::page)) // file routes .route("/file", post(ap::file::upload)) - .route("/file/{id}", get(ap::file::download)) - //.route("/objects/{id}/likes", get(ap::object::likes::get)) - //.route("/objects/{id}/likes/page", get(ap::object::likes::page)) - //.route("/objects/{id}/shares", get(ap::object::announces::get)) - //.route("/objects/{id}/shares/page", get(ap::object::announces::page)) + .route("/file/:id", get(ap::file::download)) + //.route("/objects/:id/likes", get(ap::object::likes::get)) + //.route("/objects/:id/likes/page", get(ap::object::likes::page)) + //.route("/objects/:id/shares", get(ap::object::announces::get)) + //.route("/objects/:id/shares/page", get(ap::object::announces::page)) } } diff --git a/routes/src/auth.rs b/routes/src/auth.rs index 67bef69..e0e8a93 100644 --- a/routes/src/auth.rs +++ b/routes/src/auth.rs @@ -82,6 +82,7 @@ impl Identity { pub struct AuthIdentity(pub Identity); +#[axum::async_trait] impl<S> FromRequestParts<S> for AuthIdentity where upub::Context: FromRef<S>, diff --git a/routes/src/lib.rs b/routes/src/lib.rs index da97e3a..ec5fad8 100644 --- a/routes/src/lib.rs +++ b/routes/src/lib.rs @@ -32,7 +32,7 @@ pub mod mastodon { impl super::MastodonRouter for axum::Router<upub::Context> {} pub trait WebRouter { - fn web_routes(self) -> Self where Self: Sized { self } + fn web_routes(self, _ctx: &upub::Context) -> Self where Self: Sized { self } } #[cfg(feature = "web")] @@ -43,7 +43,6 @@ pub mod web { impl super::WebRouter for axum::Router<upub::Context> {} } - pub async fn serve(ctx: upub::Context, bind: String, shutdown: impl ShutdownToken) -> Result<(), std::io::Error> { use tower_http::{cors::CorsLayer, trace::TraceLayer}; @@ -62,8 +61,8 @@ pub async fn serve(ctx: upub::Context, bind: String, shutdown: impl ShutdownToke }) ) .ap_routes() - .mastodon_routes() // no-op if mastodon feature is disabled - .web_routes() // no-op if web feature is disabled + .mastodon_routes() + .web_routes(&ctx) .layer(CorsLayer::permissive()) .with_state(ctx); diff --git a/routes/src/web.rs b/routes/src/web.rs deleted file mode 100644 index a7fff3d..0000000 --- a/routes/src/web.rs +++ /dev/null @@ -1,27 +0,0 @@ -use axum::{response::IntoResponse, routing::get, Router}; - - -impl super::WebRouter for Router<upub::Context> { - fn web_routes(self) -> Self { - self - .route("/web/assets/upub-web.wasm", get(upub_web_wasm)) - .route("/web/assets/style.css", get(upub_style_css)) - .route("/web", get(upub_web_index)) - .route("/web/", get(upub_web_index)) - .route("/web/{*any}", get(upub_web_index)) - } -} - - - -async fn upub_web_wasm() -> impl IntoResponse { - include_bytes!("../../target/wasm32-unknown-unknown/wasm-release/upub-web.wasm") -} - -async fn upub_style_css() -> impl IntoResponse { - include_str!("../../web/assets/style.css") -} - -async fn upub_web_index() -> impl IntoResponse { - include_str!("../../web/index.html") -} diff --git a/routes/src/web/mod.rs b/routes/src/web/mod.rs new file mode 100644 index 0000000..97753b4 --- /dev/null +++ b/routes/src/web/mod.rs @@ -0,0 +1,11 @@ +use leptos_axum::LeptosRoutes; + +impl super::WebRouter for axum::Router<upub::Context> { + fn web_routes(self, ctx: &upub::Context) -> Self where Self: Sized { + self.leptos_routes( + ctx, + leptos_axum::generate_route_list(upub_web::App), + move || "" + ) + } +} diff --git a/utils/httpsign/Cargo.toml b/utils/httpsign/Cargo.toml index 4ed58ae..205b9b3 100644 --- a/utils/httpsign/Cargo.toml +++ b/utils/httpsign/Cargo.toml @@ -19,7 +19,7 @@ thiserror = "2.0" tracing = "0.1" base64 = "0.22" openssl = "0.10" # TODO handle pubkeys with a smaller crate -axum = { version = "0.8", optional = true } +axum = { version = "0.7", optional = true } [features] default = [] diff --git a/web/Cargo.toml b/web/Cargo.toml index d75ede5..d35393c 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -9,6 +9,9 @@ keywords = ["activitypub", "upub", "json", "web", "wasm"] repository = "https://git.alemi.dev/upub.git" #readme = "README.md" +[lib] +crate-type = ["rlib", "cdylib"] + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] @@ -24,7 +27,7 @@ serde_json = "1.0" serde_default = "0.2" serde-inline-default = "0.2" dashmap = "6.1" -leptos = { version = "0.7", features = ["csr", "tracing"] } +leptos = { version = "0.7", features = ["tracing"] } leptos_router = { version = "0.7", features = ["tracing"] } leptos-use = "0.15" codee = { version = "0.2", features = ["json_serde"] } # WHYYY LEPTOS-USE AKSJFOASHGOAEG @@ -38,3 +41,9 @@ jrd = "0.1" tld = "2.36" web-sys = { version = "0.3", features = ["Screen"] } regex = "1.11" + +[features] +default = ["leptos-csr"] +leptos-ssr = ["leptos/ssr"] +leptos-csr = ["leptos/csr"] +leptos-hydrate = ["leptos/hydrate"] diff --git a/web/src/lib.rs b/web/src/lib.rs index 3ec9508..a35be0b 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -1,3 +1,5 @@ +#![recursion_limit = "256"] // oh nooo leptos... + mod auth; mod app; mod components;