From 6bcc4f0ab09a36fc4750bcdd76110e5af895c6ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Wed, 22 May 2019 14:29:52 +0100 Subject: [PATCH 01/48] add custom_domain (default to NULL) to schema --- .../down.sql | 2 + .../up.sql | 2 + .../down.sql | 56 +++++++ .../up.sql | 57 +++++++ plume-models/src/blogs.rs | 3 + plume-models/src/schema.rs | 153 +++++++++--------- 6 files changed, 197 insertions(+), 76 deletions(-) create mode 100644 migrations/postgres/2019-05-22-124143_add_custom_domains/down.sql create mode 100644 migrations/postgres/2019-05-22-124143_add_custom_domains/up.sql create mode 100644 migrations/sqlite/2019-05-22-124153_add_custom_domains/down.sql create mode 100644 migrations/sqlite/2019-05-22-124153_add_custom_domains/up.sql diff --git a/migrations/postgres/2019-05-22-124143_add_custom_domains/down.sql b/migrations/postgres/2019-05-22-124143_add_custom_domains/down.sql new file mode 100644 index 00000000..7f181714 --- /dev/null +++ b/migrations/postgres/2019-05-22-124143_add_custom_domains/down.sql @@ -0,0 +1,2 @@ +-- undo the adding of custom_domain column to blogs table. +ALTER TABLE blogs DROP COLUMN custom_domain; diff --git a/migrations/postgres/2019-05-22-124143_add_custom_domains/up.sql b/migrations/postgres/2019-05-22-124143_add_custom_domains/up.sql new file mode 100644 index 00000000..d00f5a29 --- /dev/null +++ b/migrations/postgres/2019-05-22-124143_add_custom_domains/up.sql @@ -0,0 +1,2 @@ +--- Adding custom domain to Blog as an optional field +ALTER TABLE blogs ADD COLUMN custom_domain VARCHAR DEFAULT NULL UNIQUE; diff --git a/migrations/sqlite/2019-05-22-124153_add_custom_domains/down.sql b/migrations/sqlite/2019-05-22-124153_add_custom_domains/down.sql new file mode 100644 index 00000000..ed5d9231 --- /dev/null +++ b/migrations/sqlite/2019-05-22-124153_add_custom_domains/down.sql @@ -0,0 +1,56 @@ +-- undo the adding of "custom_domain" to blogs +CREATE TABLE IF NOT EXISTS "blogs_drop_custom_domain" ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + actor_id VARCHAR NOT NULL, + title VARCHAR NOT NULL, + summary TEXT NOT NULL DEFAULT '', + outbox_url VARCHAR NOT NULL UNIQUE, + inbox_url VARCHAR NOT NULL UNIQUE, + instance_id INTEGER REFERENCES instances(id) ON DELETE CASCADE NOT NULL, + creation_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + ap_url text not null default '' UNIQUE, + private_key TEXT, + public_key TEXT NOT NULL DEFAULT '', + fqn TEXT NOT NULL DEFAULT '', + summary_html TEXT NOT NULL DEFAULT '', + icon_id INTEGER REFERENCES medias(id) ON DELETE SET NULL DEFAULT NULL, + banner_id INTEGER REFERENCES medias(id) ON DELETE SET NULL DEFAULT NULL, + CONSTRAINT blog_unique UNIQUE (actor_id, instance_id) +); + +INSERT INTO blogs_drop_custom_domain ( + id, + actor_id, + title, + summary, + outbox_url, + inbox_url, + instance_id, + creation_date, + ap_url, + private_key, + public_key, + fqn, + summary_html, + icon_id, + banner_id +) SELECT + id, + actor_id, + title, + summary, + outbox_url, + inbox_url, + instance_id, + creation_date, + ap_url, + private_key, + public_key, + fqn, + summary_html, + icon_id, + banner_id +FROM blogs; + +DROP TABLE blogs; +ALTER TABLE "blogs_drop_custom_domain" RENAME to blogs; diff --git a/migrations/sqlite/2019-05-22-124153_add_custom_domains/up.sql b/migrations/sqlite/2019-05-22-124153_add_custom_domains/up.sql new file mode 100644 index 00000000..1cdffa7c --- /dev/null +++ b/migrations/sqlite/2019-05-22-124153_add_custom_domains/up.sql @@ -0,0 +1,57 @@ +-- add custom_domain to blogs +CREATE TABLE IF NOT EXISTS "blogs_add_custom_domain" ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + actor_id VARCHAR NOT NULL, + title VARCHAR NOT NULL, + summary TEXT NOT NULL DEFAULT '', + outbox_url VARCHAR NOT NULL UNIQUE, + inbox_url VARCHAR NOT NULL UNIQUE, + instance_id INTEGER REFERENCES instances(id) ON DELETE CASCADE NOT NULL, + creation_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + ap_url text not null default '' UNIQUE, + private_key TEXT, + public_key TEXT NOT NULL DEFAULT '', + fqn TEXT NOT NULL DEFAULT '', + summary_html TEXT NOT NULL DEFAULT '', + icon_id INTEGER REFERENCES medias(id) ON DELETE SET NULL DEFAULT NULL, + banner_id INTEGER REFERENCES medias(id) ON DELETE SET NULL DEFAULT NULL, + custom_domain text default NULL UNIQUE, + CONSTRAINT blog_unique UNIQUE (actor_id, instance_id) +); + +INSERT INTO blogs_add_custom_domain ( + id, + actor_id, + title, + summary, + outbox_url, + inbox_url, + instance_id, + creation_date, + ap_url, + private_key, + public_key, + fqn, + summary_html, + icon_id, + banner_id +) SELECT + id, + actor_id, + title, + summary, + outbox_url, + inbox_url, + instance_id, + creation_date, + ap_url, + private_key, + public_key, + fqn, + summary_html, + icon_id, + banner_id +FROM blogs; + +DROP TABLE blogs; +ALTER TABLE "blogs_add_custom_domain" RENAME to blogs; diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 8c55f402..7fd91aa3 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -44,6 +44,7 @@ pub struct Blog { pub summary_html: SafeString, pub icon_id: Option, pub banner_id: Option, + pub custom_domain: Option, } #[derive(Default, Insertable)] @@ -61,6 +62,7 @@ pub struct NewBlog { pub summary_html: SafeString, pub icon_id: Option, pub banner_id: Option, + pub custom_domain: Option, } const BLOG_PREFIX: &str = "~"; @@ -392,6 +394,7 @@ impl FromId for Blog { .summary_string() .unwrap_or_default(), ), + custom_domain: None, }, ) } diff --git a/plume-models/src/schema.rs b/plume-models/src/schema.rs index f2cc833c..01570bac 100644 --- a/plume-models/src/schema.rs +++ b/plume-models/src/schema.rs @@ -1,17 +1,17 @@ table! { api_tokens (id) { - id -> Int4, + id -> Integer, creation_date -> Timestamp, value -> Text, scopes -> Text, - app_id -> Int4, - user_id -> Int4, + app_id -> Integer, + user_id -> Integer, } } table! { apps (id) { - id -> Int4, + id -> Integer, name -> Text, client_id -> Text, client_secret -> Text, @@ -23,70 +23,71 @@ table! { table! { blog_authors (id) { - id -> Int4, - blog_id -> Int4, - author_id -> Int4, + id -> Integer, + blog_id -> Integer, + author_id -> Integer, is_owner -> Bool, } } table! { blogs (id) { - id -> Int4, - actor_id -> Varchar, - title -> Varchar, + id -> Integer, + actor_id -> Text, + title -> Text, summary -> Text, - outbox_url -> Varchar, - inbox_url -> Varchar, - instance_id -> Int4, + outbox_url -> Text, + inbox_url -> Text, + instance_id -> Integer, creation_date -> Timestamp, ap_url -> Text, private_key -> Nullable, public_key -> Text, fqn -> Text, summary_html -> Text, - icon_id -> Nullable, - banner_id -> Nullable, + icon_id -> Nullable, + banner_id -> Nullable, + custom_domain -> Nullable, + } +} + +table! { + comment_seers (id) { + id -> Integer, + comment_id -> Integer, + user_id -> Integer, } } table! { comments (id) { - id -> Int4, + id -> Integer, content -> Text, - in_response_to_id -> Nullable, - post_id -> Int4, - author_id -> Int4, + in_response_to_id -> Nullable, + post_id -> Integer, + author_id -> Integer, creation_date -> Timestamp, - ap_url -> Nullable, + ap_url -> Nullable, sensitive -> Bool, spoiler_text -> Text, public_visibility -> Bool, } } -table! { - comment_seers (id) { - id -> Int4, - comment_id -> Int4, - user_id -> Int4, - } -} - table! { follows (id) { - id -> Int4, - follower_id -> Int4, - following_id -> Int4, + id -> Integer, + follower_id -> Integer, + following_id -> Integer, ap_url -> Text, } } table! { instances (id) { - id -> Int4, - public_domain -> Varchar, - name -> Varchar, + id -> Integer, + public_domain -> Text, + name -> Text, local -> Bool, blocked -> Bool, creation_date -> Timestamp, @@ -94,50 +95,50 @@ table! { short_description -> Text, long_description -> Text, default_license -> Text, - long_description_html -> Varchar, - short_description_html -> Varchar, + long_description_html -> Text, + short_description_html -> Text, } } table! { likes (id) { - id -> Int4, - user_id -> Int4, - post_id -> Int4, + id -> Integer, + user_id -> Integer, + post_id -> Integer, creation_date -> Timestamp, - ap_url -> Varchar, + ap_url -> Text, } } table! { medias (id) { - id -> Int4, + id -> Integer, file_path -> Text, alt_text -> Text, is_remote -> Bool, remote_url -> Nullable, sensitive -> Bool, content_warning -> Nullable, - owner_id -> Int4, + owner_id -> Integer, } } table! { mentions (id) { - id -> Int4, - mentioned_id -> Int4, - post_id -> Nullable, - comment_id -> Nullable, + id -> Integer, + mentioned_id -> Integer, + post_id -> Nullable, + comment_id -> Nullable, } } table! { notifications (id) { - id -> Int4, - user_id -> Int4, + id -> Integer, + user_id -> Integer, creation_date -> Timestamp, - kind -> Varchar, - object_id -> Int4, + kind -> Text, + object_id -> Integer, } } @@ -152,67 +153,67 @@ table! { table! { post_authors (id) { - id -> Int4, - post_id -> Int4, - author_id -> Int4, + id -> Integer, + post_id -> Integer, + author_id -> Integer, } } table! { posts (id) { - id -> Int4, - blog_id -> Int4, - slug -> Varchar, - title -> Varchar, + id -> Integer, + blog_id -> Integer, + slug -> Text, + title -> Text, content -> Text, published -> Bool, - license -> Varchar, + license -> Text, creation_date -> Timestamp, - ap_url -> Varchar, + ap_url -> Text, subtitle -> Text, source -> Text, - cover_id -> Nullable, + cover_id -> Nullable, } } table! { reshares (id) { - id -> Int4, - user_id -> Int4, - post_id -> Int4, - ap_url -> Varchar, + id -> Integer, + user_id -> Integer, + post_id -> Integer, + ap_url -> Text, creation_date -> Timestamp, } } table! { tags (id) { - id -> Int4, + id -> Integer, tag -> Text, is_hashtag -> Bool, - post_id -> Int4, + post_id -> Integer, } } table! { users (id) { - id -> Int4, - username -> Varchar, - display_name -> Varchar, - outbox_url -> Varchar, - inbox_url -> Varchar, + id -> Integer, + username -> Text, + display_name -> Text, + outbox_url -> Text, + inbox_url -> Text, is_admin -> Bool, summary -> Text, email -> Nullable, hashed_password -> Nullable, - instance_id -> Int4, + instance_id -> Integer, creation_date -> Timestamp, ap_url -> Text, private_key -> Nullable, public_key -> Text, - shared_inbox_url -> Nullable, - followers_endpoint -> Varchar, - avatar_id -> Nullable, + shared_inbox_url -> Nullable, + followers_endpoint -> Text, + avatar_id -> Nullable, last_fetched_date -> Timestamp, fqn -> Text, summary_html -> Text, @@ -248,8 +249,8 @@ allow_tables_to_appear_in_same_query!( apps, blog_authors, blogs, - comments, comment_seers, + comments, follows, instances, likes, -- 2.38.5 From 92bbeeb45e28e17f2b24162b7e390dc517efe922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Fri, 24 May 2019 14:30:38 +0100 Subject: [PATCH 02/48] add Host(String) wrapper type we can use this to handle rocket Requests (from_request) but we still have to figure out how to automatically assign the type to custom_domain: Optional, and public_domain: Host. --- plume-models/src/blogs.rs | 56 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 7fd91aa3..20c63f18 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -7,6 +7,10 @@ use openssl::{ rsa::Rsa, sign::{Signer, Verifier}, }; +use rocket::{ + outcome::IntoOutcome, + request::{self, FromRequest, Request}, +}; use serde_json; use url::Url; use webfinger::*; @@ -21,11 +25,45 @@ use posts::Post; use safe_string::SafeString; use schema::blogs; use search::Searcher; +use std::fmt; use users::User; use {Connection, Error, PlumeRocket, Result}; pub type CustomGroup = CustomObject; +#[derive(Queryable, Clone)] +pub struct Host(String); + +impl Host { + pub fn new>(host: T) -> Host { + Host(host.into()) + } +} + +impl Into for Host { + fn into(self) -> String { + self.0.clone() + } +} + +impl From for Host { + fn from(s: String) -> Host { + Host::new(s) + } +} + +impl AsRef for Host { + fn as_ref(&self) -> &str { + &self.0 + } +} + +impl std::fmt::Display for Host { + fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + #[derive(Queryable, Identifiable, Clone, AsChangeset)] #[changeset_options(treat_none_as_null = "true")] pub struct Blog { @@ -294,6 +332,24 @@ impl Blog { } } +impl<'a, 'r> FromRequest<'a, 'r> for Host { + type Error = (); + + fn from_request(request: &'a Request<'r>) -> request::Outcome { + request + .headers() + .get_one("Host") + .and_then(|x| { + if x != Instance::get_local().ok()?.public_domain { + Some(Host(x.to_string())) + } else { + None + } + }) + .or_forward(()) + } +} + impl IntoId for Blog { fn into_id(self) -> Id { Id::new(self.ap_url) -- 2.38.5 From 65ae51b7e51e821a28643de57f007bfc9faca6e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Sat, 25 May 2019 21:03:06 +0200 Subject: [PATCH 03/48] implement Host more completely by doing less we now use DieselNewType and Shrinkwrap to automatically derive all the things we need. --- plume-models/src/blogs.rs | 29 +++++------------------------ plume-models/src/lib.rs | 4 ++++ 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 20c63f18..6c5a449d 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -25,30 +25,17 @@ use posts::Post; use safe_string::SafeString; use schema::blogs; use search::Searcher; -use std::fmt; use users::User; use {Connection, Error, PlumeRocket, Result}; pub type CustomGroup = CustomObject; -#[derive(Queryable, Clone)] +#[derive(Clone, Debug, DieselNewType, Shrinkwrap)] pub struct Host(String); impl Host { - pub fn new>(host: T) -> Host { - Host(host.into()) - } -} - -impl Into for Host { - fn into(self) -> String { - self.0.clone() - } -} - -impl From for Host { - fn from(s: String) -> Host { - Host::new(s) + pub fn new(host: impl ToString) -> Host { + Host(host.to_string()) } } @@ -58,12 +45,6 @@ impl AsRef for Host { } } -impl std::fmt::Display for Host { - fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - #[derive(Queryable, Identifiable, Clone, AsChangeset)] #[changeset_options(treat_none_as_null = "true")] pub struct Blog { @@ -82,7 +63,7 @@ pub struct Blog { pub summary_html: SafeString, pub icon_id: Option, pub banner_id: Option, - pub custom_domain: Option, + pub custom_domain: Option, } #[derive(Default, Insertable)] @@ -341,7 +322,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for Host { .get_one("Host") .and_then(|x| { if x != Instance::get_local().ok()?.public_domain { - Some(Host(x.to_string())) + Some(Host::new(x)) } else { None } diff --git a/plume-models/src/lib.rs b/plume-models/src/lib.rs index 2c30cb8b..5ed0cf3d 100644 --- a/plume-models/src/lib.rs +++ b/plume-models/src/lib.rs @@ -10,6 +10,8 @@ extern crate bcrypt; extern crate chrono; #[macro_use] extern crate diesel; +#[macro_use] +extern crate diesel_derive_newtype; extern crate guid_create; extern crate heck; extern crate itertools; @@ -31,6 +33,8 @@ extern crate serde_derive; #[macro_use] extern crate serde_json; #[macro_use] +extern crate shrinkwraprs; +#[macro_use] extern crate tantivy; extern crate url; extern crate webfinger; -- 2.38.5 From 351c01f71c384594625f9a34f4e1159e33aaf72e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Sat, 25 May 2019 23:35:00 +0200 Subject: [PATCH 04/48] list custom_domains thanks a lot to @fdb-hiroshima and @BaptisteGelez for helping with the code. --- plume-models/src/blogs.rs | 11 ++++++++++- src/main.rs | 3 +++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 6c5a449d..2a64d886 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -81,7 +81,7 @@ pub struct NewBlog { pub summary_html: SafeString, pub icon_id: Option, pub banner_id: Option, - pub custom_domain: Option, + pub custom_domain: Option, } const BLOG_PREFIX: &str = "~"; @@ -311,6 +311,15 @@ impl Blog { .map(|_| ()) .map_err(Error::from) } + + pub fn list_custom_domains(conn: &Connection) -> Result> { + blogs::table + .filter(blogs::custom_domain.is_not_null()) + .select(blogs::custom_domain) + .load::>(conn) + .map_err(Error::from) + .map(|res| res.into_iter().map(|s| s.unwrap()).collect::>()) + } } impl<'a, 'r> FromRequest<'a, 'r> for Host { diff --git a/src/main.rs b/src/main.rs index 0bdfaddc..6c466777 100644 --- a/src/main.rs +++ b/src/main.rs @@ -175,6 +175,9 @@ Then try to restart Plume println!("Please refer to the documentation to see how to configure it."); } + let custom_domains = plume_models::blogs::Blog::list_custom_domains(&dbpool.get().unwrap()).unwrap(); + dbg!(custom_domains); + let rocket = rocket::custom(CONFIG.rocket.clone().unwrap()) .mount( "/", -- 2.38.5 From 3a4c2f2cf9af2d8726c1209ff8f22c61933f2a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Sun, 26 May 2019 11:59:53 +0200 Subject: [PATCH 05/48] create & attach an AdHoc Fairing for dealing with Custom Domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit this rewrites the URL to /custom_domain/ (we have no route handlers for this path yet) Lots of help from @fdb-hiroshima & @BaptisteGelez in dealing with the borrow checker — thank you 💜️ --- plume-models/src/blogs.rs | 7 +++++++ src/main.rs | 14 +++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 2a64d886..69d3e129 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -25,6 +25,7 @@ use posts::Post; use safe_string::SafeString; use schema::blogs; use search::Searcher; +use std::fmt::{self, Display}; use users::User; use {Connection, Error, PlumeRocket, Result}; @@ -39,6 +40,12 @@ impl Host { } } +impl Display for Host { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + impl AsRef for Host { fn as_ref(&self) -> &str { &self.0 diff --git a/src/main.rs b/src/main.rs index 6c466777..86d0dfc6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,12 +42,14 @@ extern crate webfinger; use clap::App; use diesel::r2d2::ConnectionManager; use plume_models::{ + blogs::Host, db_conn::{DbPool, PragmaForeignKey}, instance::Instance, migrations::IMPORTED_MIGRATIONS, search::{Searcher as UnmanagedSearcher, SearcherError}, Connection, Error, CONFIG, }; +use rocket::{fairing::AdHoc, http::uri::Origin}; use rocket_csrf::CsrfFairingBuilder; use scheduled_thread_pool::ScheduledThreadPool; use std::process::exit; @@ -175,8 +177,13 @@ Then try to restart Plume println!("Please refer to the documentation to see how to configure it."); } - let custom_domains = plume_models::blogs::Blog::list_custom_domains(&dbpool.get().unwrap()).unwrap(); - dbg!(custom_domains); + let custom_domain_fairing = AdHoc::on_request("Custom Blog Domains", |req, _data| { + let host = req.guard::(); + if host.is_success() { + let rewrite_uri = format!("/custom_domains/{}/{}", host.unwrap(), req.uri()); + req.set_uri(Origin::parse_owned(rewrite_uri).unwrap()) + } + }); let rocket = rocket::custom(CONFIG.rocket.clone().unwrap()) .mount( @@ -317,7 +324,8 @@ Then try to restart Plume ]) .finalize() .expect("main: csrf fairing creation error"), - ); + ) + .attach(custom_domain_fairing); #[cfg(feature = "test")] let rocket = rocket.mount("/test", routes![test_routes::health,]); -- 2.38.5 From 2746e088aedb3b260396c0b57c6bb7bf45e4cf1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Sun, 26 May 2019 12:16:50 +0200 Subject: [PATCH 06/48] appease clippy --- plume-models/src/blogs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 69d3e129..2a7fa121 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -325,7 +325,7 @@ impl Blog { .select(blogs::custom_domain) .load::>(conn) .map_err(Error::from) - .map(|res| res.into_iter().map(|s| s.unwrap()).collect::>()) + .map(|res| res.into_iter().map(Option::unwrap).collect::>()) } } -- 2.38.5 From e6747de9982384c855de764c594d772604a54d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Sun, 26 May 2019 22:00:36 +0200 Subject: [PATCH 07/48] cache custom_domains list follow #572 and cache the list of custom domains. --- plume-models/src/blogs.rs | 17 +++++++++++++++-- src/main.rs | 2 ++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 2a7fa121..ebd8fab5 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -26,6 +26,7 @@ use safe_string::SafeString; use schema::blogs; use search::Searcher; use std::fmt::{self, Display}; +use std::sync::RwLock; use users::User; use {Connection, Error, PlumeRocket, Result}; @@ -93,6 +94,10 @@ pub struct NewBlog { const BLOG_PREFIX: &str = "~"; +lazy_static! { + static ref CUSTOM_DOMAINS: RwLock> = RwLock::new(vec![]); +} + impl Blog { insert!(blogs, NewBlog, |inserted, conn| { let instance = inserted.get_instance(conn)?; @@ -319,7 +324,15 @@ impl Blog { .map_err(Error::from) } - pub fn list_custom_domains(conn: &Connection) -> Result> { + pub fn list_custom_domains() -> Vec { + CUSTOM_DOMAINS.read().unwrap().clone() + } + + pub fn cache_custom_domains(conn: &Connection) { + *CUSTOM_DOMAINS.write().unwrap() = Blog::list_custom_domains_uncached(conn).unwrap(); + } + + pub fn list_custom_domains_uncached(conn: &Connection) -> Result> { blogs::table .filter(blogs::custom_domain.is_not_null()) .select(blogs::custom_domain) @@ -337,7 +350,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for Host { .headers() .get_one("Host") .and_then(|x| { - if x != Instance::get_local().ok()?.public_domain { + if Blog::list_custom_domains().contains(&x.to_string()) { Some(Host::new(x)) } else { None diff --git a/src/main.rs b/src/main.rs index 86d0dfc6..ec138744 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,6 +42,7 @@ extern crate webfinger; use clap::App; use diesel::r2d2::ConnectionManager; use plume_models::{ + blogs::Blog, blogs::Host, db_conn::{DbPool, PragmaForeignKey}, instance::Instance, @@ -89,6 +90,7 @@ fn init_pool() -> Option { .build(manager) .ok()?; Instance::cache_local(&pool.get().unwrap()); + Blog::cache_custom_domains(&pool.get().unwrap()); Some(pool) } -- 2.38.5 From b09b51c74bd4a26bb1ba4c2cb40a9643c558321d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Mon, 27 May 2019 13:34:38 +0200 Subject: [PATCH 08/48] normalize URLs before setting them --- src/main.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index ec138744..8bf93b2b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -50,7 +50,7 @@ use plume_models::{ search::{Searcher as UnmanagedSearcher, SearcherError}, Connection, Error, CONFIG, }; -use rocket::{fairing::AdHoc, http::uri::Origin}; +use rocket::{fairing::AdHoc, http::ext::IntoOwned, http::uri::Origin}; use rocket_csrf::CsrfFairingBuilder; use scheduled_thread_pool::ScheduledThreadPool; use std::process::exit; @@ -183,7 +183,9 @@ Then try to restart Plume let host = req.guard::(); if host.is_success() { let rewrite_uri = format!("/custom_domains/{}/{}", host.unwrap(), req.uri()); - req.set_uri(Origin::parse_owned(rewrite_uri).unwrap()) + let uri = Origin::parse_owned(rewrite_uri).unwrap(); + let uri = uri.to_normalized().into_owned(); + req.set_uri(uri); } }); -- 2.38.5 From f94b0c79c5c998fea58d788fa5952f363610c8f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Mon, 27 May 2019 17:53:10 +0200 Subject: [PATCH 09/48] move impl closer to mother struct --- plume-models/src/blogs.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index ebd8fab5..dab6bbcc 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -53,6 +53,24 @@ impl AsRef for Host { } } +impl<'a, 'r> FromRequest<'a, 'r> for Host { + type Error = (); + + fn from_request(request: &'a Request<'r>) -> request::Outcome { + request + .headers() + .get_one("Host") + .and_then(|x| { + if Blog::list_custom_domains().contains(&x.to_string()) { + Some(Host::new(x)) + } else { + None + } + }) + .or_forward(()) + } +} + #[derive(Queryable, Identifiable, Clone, AsChangeset)] #[changeset_options(treat_none_as_null = "true")] pub struct Blog { @@ -342,24 +360,6 @@ impl Blog { } } -impl<'a, 'r> FromRequest<'a, 'r> for Host { - type Error = (); - - fn from_request(request: &'a Request<'r>) -> request::Outcome { - request - .headers() - .get_one("Host") - .and_then(|x| { - if Blog::list_custom_domains().contains(&x.to_string()) { - Some(Host::new(x)) - } else { - None - } - }) - .or_forward(()) - } -} - impl IntoId for Blog { fn into_id(self) -> Id { Id::new(self.ap_url) -- 2.38.5 From 0645f7e2535f7912505e5fb3fcd2bbabca59031a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Mon, 27 May 2019 17:54:16 +0200 Subject: [PATCH 10/48] CustomDomainFairing must not match /static/ and /api/ thanks, again, @fdb-hiroshima for helping with this code, when i got stuck in lifetime-hecc. --- src/main.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 8bf93b2b..f44ed66e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -181,7 +181,14 @@ Then try to restart Plume let custom_domain_fairing = AdHoc::on_request("Custom Blog Domains", |req, _data| { let host = req.guard::(); - if host.is_success() { + if host.is_success() + && req + .uri() + .segments() + .next() + .map(|path| path != "static" && path != "api") + .unwrap_or(true) + { let rewrite_uri = format!("/custom_domains/{}/{}", host.unwrap(), req.uri()); let uri = Origin::parse_owned(rewrite_uri).unwrap(); let uri = uri.to_normalized().into_owned(); -- 2.38.5 From 468e6633440188307dbb89812f5b842199ba9489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Mon, 27 May 2019 17:55:09 +0200 Subject: [PATCH 11/48] Add custom_details and custom_activity_details as first routes how this works: we use find_by_host() to find the Host in question, the defer to the existing function! Caveat: Currently, we, in that function, we do another lookup DB lookup for the Blog, even thou we already know it. It might be wise, to have both of those another wrapper here?! --- plume-models/src/blogs.rs | 17 +++++++++++++++++ po/plume/plume.pot | 18 +++++++++--------- src/main.rs | 7 +++++++ src/routes/blogs.rs | 20 ++++++++++++++++++++ 4 files changed, 53 insertions(+), 9 deletions(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index dab6bbcc..f2ea8235 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -195,6 +195,23 @@ impl Blog { } } + pub fn find_by_host(c: &PlumeRocket, host: Host) -> Result { + let from_db = blogs::table + .filter(blogs::custom_domain.eq(host)) + .limit(1) + .load::(&*c.conn)? + .into_iter() + .next(); + if let Some(from_db) = from_db { + Ok(from_db) + } else { + // we should never get here, because + // a) load::()? should return early if it fails + // b) this function is only called after a Request::guard::() + Err(Error::NotFound) + } + } + fn fetch_from_webfinger(c: &PlumeRocket, acct: &str) -> Result { resolve_with_prefix(Prefix::Group, acct.to_owned(), true)? .links diff --git a/po/plume/plume.pot b/po/plume/plume.pot index 8e55c82e..751273b7 100644 --- a/po/plume/plume.pot +++ b/po/plume/plume.pot @@ -36,39 +36,39 @@ msgstr "" msgid "{0}'s avatar" msgstr "" -# src/routes/blogs.rs:64 +# src/routes/blogs.rs:84 msgid "To create a new blog, you need to be logged in" msgstr "" -# src/routes/blogs.rs:106 +# src/routes/blogs.rs:126 msgid "A blog with the same name already exists." msgstr "" -# src/routes/blogs.rs:141 +# src/routes/blogs.rs:161 msgid "Your blog was successfully created!" msgstr "" -# src/routes/blogs.rs:163 +# src/routes/blogs.rs:183 msgid "Your blog was deleted." msgstr "" -# src/routes/blogs.rs:170 +# src/routes/blogs.rs:190 msgid "You are not allowed to delete this blog." msgstr "" -# src/routes/blogs.rs:218 +# src/routes/blogs.rs:238 msgid "You are not allowed to edit this blog." msgstr "" -# src/routes/blogs.rs:263 +# src/routes/blogs.rs:283 msgid "You can't use this media as a blog icon." msgstr "" -# src/routes/blogs.rs:281 +# src/routes/blogs.rs:301 msgid "You can't use this media as a blog banner." msgstr "" -# src/routes/blogs.rs:314 +# src/routes/blogs.rs:334 msgid "Your blog information have been updated." msgstr "" diff --git a/src/main.rs b/src/main.rs index f44ed66e..c0fd2577 100644 --- a/src/main.rs +++ b/src/main.rs @@ -197,6 +197,13 @@ Then try to restart Plume }); let rocket = rocket::custom(CONFIG.rocket.clone().unwrap()) + .mount( + "/custom_domains/", + routes![ + routes::blogs::custom_details, + routes::blogs::custom_activity_details, + ], + ) .mount( "/", routes![ diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 7ab9e2b3..86f387ab 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -19,6 +19,16 @@ use plume_models::{ use routes::{errors::ErrorPage, Page, RespondOrRedirect}; use template_utils::{IntoContext, Ructe}; +#[get("/?", rank = 2)] +pub fn custom_details( + custom_domain: String, + page: Option, + rockets: PlumeRocket, +) -> Result { + let blog = Blog::find_by_host(&rockets, Host::new(custom_domain))?; + details(blog.fqn, page, rockets) +} + #[get("/~/?", rank = 2)] pub fn details(name: String, page: Option, rockets: PlumeRocket) -> Result { let page = page.unwrap_or_default(); @@ -38,6 +48,16 @@ pub fn details(name: String, page: Option, rockets: PlumeRocket) -> Result ))) } +#[get("/", rank = 1)] +pub fn custom_activity_details( + custom_domain: String, + rockets: PlumeRocket, + _ap: ApRequest, +) -> Option> { + let blog = Blog::find_by_host(&rockets, Host::new(custom_domain)).ok()?; + activity_details(blog.fqn, rockets, _ap) +} + #[get("/~/", rank = 1)] pub fn activity_details( name: String, -- 2.38.5 From 1c34ac38f7362be79b447de0171453ef27fbc79f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Mon, 27 May 2019 22:47:47 +0200 Subject: [PATCH 12/48] replace if / else with ok_or() --- plume-models/src/blogs.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index f2ea8235..f8fcffb3 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -202,14 +202,7 @@ impl Blog { .load::(&*c.conn)? .into_iter() .next(); - if let Some(from_db) = from_db { - Ok(from_db) - } else { - // we should never get here, because - // a) load::()? should return early if it fails - // b) this function is only called after a Request::guard::() - Err(Error::NotFound) - } + from_db.ok_or(Error::NotFound) } fn fetch_from_webfinger(c: &PlumeRocket, acct: &str) -> Result { -- 2.38.5 From 8e6b1ab86e2d5fe8fb63f52b5204922264f9e5e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Tue, 28 May 2019 11:14:41 +0200 Subject: [PATCH 13/48] simplify retrieval in find_by_host() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit we can use "first()" instead of limit(1).load().etc… since on a UNIQUE field, we only expect 1 result. first() returns QueryResult, which is Result, so we need to implement a converter for that error type. This commit addresses @fdb-hiroshima's review. --- plume-models/src/blogs.rs | 12 ++++-------- src/routes/errors.rs | 6 ++++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index f8fcffb3..5e36331c 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -1,6 +1,6 @@ use activitypub::{actor::Group, collection::OrderedCollection, object::Image, CustomObject}; use chrono::NaiveDateTime; -use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl}; +use diesel::{self, ExpressionMethods, QueryDsl, QueryResult, RunQueryDsl, SaveChangesDsl}; use openssl::{ hash::MessageDigest, pkey::{PKey, Private}, @@ -195,14 +195,10 @@ impl Blog { } } - pub fn find_by_host(c: &PlumeRocket, host: Host) -> Result { - let from_db = blogs::table + pub fn find_by_host(c: &PlumeRocket, host: Host) -> QueryResult { + blogs::table .filter(blogs::custom_domain.eq(host)) - .limit(1) - .load::(&*c.conn)? - .into_iter() - .next(); - from_db.ok_or(Error::NotFound) + .first::(&*c.conn) } fn fetch_from_webfinger(c: &PlumeRocket, acct: &str) -> Result { diff --git a/src/routes/errors.rs b/src/routes/errors.rs index 1320a068..23f38030 100644 --- a/src/routes/errors.rs +++ b/src/routes/errors.rs @@ -14,6 +14,12 @@ impl From for ErrorPage { } } +impl From for ErrorPage { + fn from(err: diesel::result::Error) -> ErrorPage { + ErrorPage(plume_models::Error::Db(err)) + } +} + impl<'r> Responder<'r> for ErrorPage { fn respond_to(self, req: &Request) -> response::Result<'r> { let rockets = req.guard::().unwrap(); -- 2.38.5 From a0aef506744db3385d9ab7d2d3398ab48a4aef50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Tue, 28 May 2019 12:37:27 +0200 Subject: [PATCH 14/48] extract common routing code into private "_guts()" functions --- po/plume/plume.pot | 18 +++++++++--------- src/routes/blogs.rs | 42 +++++++++++++++++++++++++++--------------- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/po/plume/plume.pot b/po/plume/plume.pot index 751273b7..b362a602 100644 --- a/po/plume/plume.pot +++ b/po/plume/plume.pot @@ -36,39 +36,39 @@ msgstr "" msgid "{0}'s avatar" msgstr "" -# src/routes/blogs.rs:84 +# src/routes/blogs.rs:96 msgid "To create a new blog, you need to be logged in" msgstr "" -# src/routes/blogs.rs:126 +# src/routes/blogs.rs:138 msgid "A blog with the same name already exists." msgstr "" -# src/routes/blogs.rs:161 +# src/routes/blogs.rs:173 msgid "Your blog was successfully created!" msgstr "" -# src/routes/blogs.rs:183 +# src/routes/blogs.rs:195 msgid "Your blog was deleted." msgstr "" -# src/routes/blogs.rs:190 +# src/routes/blogs.rs:202 msgid "You are not allowed to delete this blog." msgstr "" -# src/routes/blogs.rs:238 +# src/routes/blogs.rs:250 msgid "You are not allowed to edit this blog." msgstr "" -# src/routes/blogs.rs:283 +# src/routes/blogs.rs:295 msgid "You can't use this media as a blog icon." msgstr "" -# src/routes/blogs.rs:301 +# src/routes/blogs.rs:313 msgid "You can't use this media as a blog banner." msgstr "" -# src/routes/blogs.rs:334 +# src/routes/blogs.rs:346 msgid "Your blog information have been updated." msgstr "" diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 86f387ab..60c57783 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -19,21 +19,9 @@ use plume_models::{ use routes::{errors::ErrorPage, Page, RespondOrRedirect}; use template_utils::{IntoContext, Ructe}; -#[get("/?", rank = 2)] -pub fn custom_details( - custom_domain: String, - page: Option, - rockets: PlumeRocket, -) -> Result { - let blog = Blog::find_by_host(&rockets, Host::new(custom_domain))?; - details(blog.fqn, page, rockets) -} - -#[get("/~/?", rank = 2)] -pub fn details(name: String, page: Option, rockets: PlumeRocket) -> Result { +fn detail_guts(blog: Blog, page: Option, rockets: PlumeRocket) -> Result { let page = page.unwrap_or_default(); let conn = &*rockets.conn; - let blog = Blog::find_by_fqn(&rockets, &name)?; let posts = Post::blog_page(conn, &blog, page.limits())?; let articles_count = Post::count_for_blog(conn, &blog)?; let authors = &blog.list_authors(conn)?; @@ -48,6 +36,30 @@ pub fn details(name: String, page: Option, rockets: PlumeRocket) -> Result ))) } +#[get("/?", rank = 2)] +pub fn custom_details( + custom_domain: String, + page: Option, + rockets: PlumeRocket, +) -> Result { + let blog = Blog::find_by_host(&rockets, Host::new(custom_domain))?; + detail_guts(blog, page, rockets) +} + +#[get("/~/?", rank = 2)] +pub fn details(name: String, page: Option, rockets: PlumeRocket) -> Result { + let blog = Blog::find_by_fqn(&rockets, &name)?; + detail_guts(blog, page, rockets) +} + +pub fn activity_detail_guts( + blog: Blog, + rockets: PlumeRocket, + _ap: ApRequest, +) -> Option> { + Some(ActivityStream::new(blog.to_activity(&*rockets.conn).ok()?)) +} + #[get("/", rank = 1)] pub fn custom_activity_details( custom_domain: String, @@ -55,7 +67,7 @@ pub fn custom_activity_details( _ap: ApRequest, ) -> Option> { let blog = Blog::find_by_host(&rockets, Host::new(custom_domain)).ok()?; - activity_details(blog.fqn, rockets, _ap) + activity_detail_guts(blog, rockets, _ap) } #[get("/~/", rank = 1)] @@ -65,7 +77,7 @@ pub fn activity_details( _ap: ApRequest, ) -> Option> { let blog = Blog::find_by_fqn(&rockets, &name).ok()?; - Some(ActivityStream::new(blog.to_activity(&*rockets.conn).ok()?)) + activity_detail_guts(blog, rockets, _ap) } #[get("/blogs/new")] -- 2.38.5 From 6253adf768b0ac3cd6f325f27f372501fd4bdeaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Tue, 28 May 2019 17:54:01 +0200 Subject: [PATCH 15/48] simplify / unify error handling We want to return a Result, instead of a QueryResult. We can use map_err() to map it to the desired error type. thanks to review from @BaptisteGelez & @fdb-hiroshima. --- plume-models/src/blogs.rs | 5 +++-- src/routes/errors.rs | 6 ------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index 5e36331c..ad6e40a2 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -1,6 +1,6 @@ use activitypub::{actor::Group, collection::OrderedCollection, object::Image, CustomObject}; use chrono::NaiveDateTime; -use diesel::{self, ExpressionMethods, QueryDsl, QueryResult, RunQueryDsl, SaveChangesDsl}; +use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, SaveChangesDsl}; use openssl::{ hash::MessageDigest, pkey::{PKey, Private}, @@ -195,10 +195,11 @@ impl Blog { } } - pub fn find_by_host(c: &PlumeRocket, host: Host) -> QueryResult { + pub fn find_by_host(c: &PlumeRocket, host: Host) -> Result { blogs::table .filter(blogs::custom_domain.eq(host)) .first::(&*c.conn) + .map_err(|_| Error::NotFound) } fn fetch_from_webfinger(c: &PlumeRocket, acct: &str) -> Result { diff --git a/src/routes/errors.rs b/src/routes/errors.rs index 23f38030..1320a068 100644 --- a/src/routes/errors.rs +++ b/src/routes/errors.rs @@ -14,12 +14,6 @@ impl From for ErrorPage { } } -impl From for ErrorPage { - fn from(err: diesel::result::Error) -> ErrorPage { - ErrorPage(plume_models::Error::Db(err)) - } -} - impl<'r> Responder<'r> for ErrorPage { fn respond_to(self, req: &Request) -> response::Result<'r> { let rockets = req.guard::().unwrap(); -- 2.38.5 From 92fbd174eb344951d2ddc5e752e47e80e3f63594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Tue, 28 May 2019 23:02:13 +0200 Subject: [PATCH 16/48] extend 404 handler to handle all the requests our custom_ routes dont --- src/routes/errors.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/routes/errors.rs b/src/routes/errors.rs index 1320a068..626be8f5 100644 --- a/src/routes/errors.rs +++ b/src/routes/errors.rs @@ -1,6 +1,6 @@ -use plume_models::{Error, PlumeRocket}; +use plume_models::{instance::Instance, Error, PlumeRocket}; use rocket::{ - response::{self, Responder}, + response::{self, Redirect, Responder}, Request, }; use template_utils::{IntoContext, Ructe}; @@ -29,9 +29,26 @@ impl<'r> Responder<'r> for ErrorPage { } #[catch(404)] -pub fn not_found(req: &Request) -> Ructe { +pub fn not_found(req: &Request) -> Result { let rockets = req.guard::().unwrap(); - render!(errors::not_found(&rockets.to_context())) + if req + .uri() + .segments() + .next() + .map(|path| path == "custom_domains") + .unwrap_or(false) + { + let path = req + .uri() + .segments() + .skip(2) + .collect::>() + .join("/"); + let public_domain = Instance::get_local().unwrap().public_domain; + Err(Redirect::to(format!("https://{}/{}", public_domain, path))) + } else { + Ok(render!(errors::not_found(&rockets.to_context()))) + } } #[catch(422)] -- 2.38.5 From 8e7f7899693526a8a452f30a50f0845891c1538c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Thu, 30 May 2019 18:26:55 +0200 Subject: [PATCH 17/48] Allow searching from custom_domain --- src/main.rs | 1 + src/routes/search.rs | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index c0fd2577..9c30c7b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -202,6 +202,7 @@ Then try to restart Plume routes![ routes::blogs::custom_details, routes::blogs::custom_activity_details, + routes::search::custom_search, ], ) .mount( diff --git a/src/routes/search.rs b/src/routes/search.rs index b605d103..11d921d7 100644 --- a/src/routes/search.rs +++ b/src/routes/search.rs @@ -49,8 +49,7 @@ macro_rules! param_to_query { } } -#[get("/search?")] -pub fn search(query: Option>, rockets: PlumeRocket) -> Ructe { +fn search_guts(query: Option>, rockets: PlumeRocket) -> Ructe { let conn = &*rockets.conn; let query = query.map(Form::into_inner).unwrap_or_default(); let page = query.page.unwrap_or_default(); @@ -83,3 +82,17 @@ pub fn search(query: Option>, rockets: PlumeRocket) -> Ructe { )) } } + +#[get("/search?")] +pub fn search(query: Option>, rockets: PlumeRocket) -> Ructe { + search_guts(query, rockets) +} + +#[get("/<_custom_domain>/search?")] +pub fn custom_search( + _custom_domain: String, + query: Option>, + rockets: PlumeRocket, +) -> Ructe { + search_guts(query, rockets) +} -- 2.38.5 From 9cee38ae6a75f5aa3728c0ee531207e7fa163aa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Fri, 31 May 2019 23:18:29 +0200 Subject: [PATCH 18/48] add url to Blog, this seems useful. --- plume-models/src/blogs.rs | 13 +++++++++++++ templates/blogs/details.rs.html | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index ad6e40a2..a81b88f9 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -327,6 +327,19 @@ impl Blog { }) } + pub fn url(&self) -> String { + format!( + "https://{}", + self.custom_domain + .clone() + .unwrap_or_else(|| Host::new(format!( + "{}/~/{}", + Instance::get_local().unwrap().public_domain, + self.title + ))) + ) + } + pub fn icon_url(&self, conn: &Connection) -> String { self.icon_id .and_then(|id| Media::get(conn, id).and_then(|m| m.url()).ok()) diff --git a/templates/blogs/details.rs.html b/templates/blogs/details.rs.html index 71c87a71..571b1139 100644 --- a/templates/blogs/details.rs.html +++ b/templates/blogs/details.rs.html @@ -13,8 +13,8 @@ - - + + -- 2.38.5 From cc0df4ecb2ca6766d69260177feb758e32868e81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Fri, 14 Jun 2019 19:28:31 +0200 Subject: [PATCH 19/48] redirect blog urls to custom_domain if it exists --- plume-models/src/blogs.rs | 2 +- src/routes/blogs.rs | 36 +++++++++++++++++++++++++++++++----- src/routes/mod.rs | 9 ++++++++- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index a81b88f9..a52dad57 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -32,7 +32,7 @@ use {Connection, Error, PlumeRocket, Result}; pub type CustomGroup = CustomObject; -#[derive(Clone, Debug, DieselNewType, Shrinkwrap)] +#[derive(Clone, Debug, PartialEq, DieselNewType, Shrinkwrap)] pub struct Host(String); impl Host { diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 60c57783..bdcb1c55 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -19,7 +19,11 @@ use plume_models::{ use routes::{errors::ErrorPage, Page, RespondOrRedirect}; use template_utils::{IntoContext, Ructe}; -fn detail_guts(blog: Blog, page: Option, rockets: PlumeRocket) -> Result { +fn detail_guts( + blog: Blog, + page: Option, + rockets: PlumeRocket, +) -> Result { let page = page.unwrap_or_default(); let conn = &*rockets.conn; let posts = Post::blog_page(conn, &blog, page.limits())?; @@ -33,7 +37,8 @@ fn detail_guts(blog: Blog, page: Option, rockets: PlumeRocket) -> Result?", rank = 2)] @@ -41,15 +46,36 @@ pub fn custom_details( custom_domain: String, page: Option, rockets: PlumeRocket, -) -> Result { +) -> Result { let blog = Blog::find_by_host(&rockets, Host::new(custom_domain))?; detail_guts(blog, page, rockets) } #[get("/~/?", rank = 2)] -pub fn details(name: String, page: Option, rockets: PlumeRocket) -> Result { +pub fn details( + name: String, + page: Option, + rockets: PlumeRocket, +) -> Result { let blog = Blog::find_by_fqn(&rockets, &name)?; - detail_guts(blog, page, rockets) + + // check this first, and return early + // doing this prevents partially moving `blog` into the `match (tuple)`, + // which makes it impossible to reuse then. + if blog.custom_domain == None { + return detail_guts(blog, page, rockets); + } + + match (blog.custom_domain, page) { + (Some(ref custom_domain), Some(ref page)) => { + Ok(Redirect::to(format!("https://{}/?{}", custom_domain, page)).into()) + } + (Some(ref custom_domain), _) => { + Ok(Redirect::to(format!("https://{}/", custom_domain)).into()) + } + // we need this match arm, or the match won't compile + (None, _) => panic!("This code path should have already been handled!"), + } } pub fn activity_detail_guts( diff --git a/src/routes/mod.rs b/src/routes/mod.rs index c29ab10f..b3438a7f 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -10,6 +10,7 @@ use rocket::{ response::{Flash, NamedFile, Redirect}, Outcome, }; +use std::fmt; use std::path::{Path, PathBuf}; use template_utils::Ructe; @@ -52,9 +53,15 @@ impl From> for RespondOrRedirect { } } -#[derive(Shrinkwrap, Copy, Clone, UriDisplayQuery)] +#[derive(Debug, Shrinkwrap, Copy, Clone, UriDisplayQuery)] pub struct Page(i32); +impl fmt::Display for Page { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} + impl<'v> FromFormValue<'v> for Page { type Error = &'v RawStr; fn from_form_value(form_value: &'v RawStr) -> Result { -- 2.38.5 From fe110b5d8a4f9cdb4c7237760992ff7242844107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Sat, 15 Jun 2019 22:07:37 +0200 Subject: [PATCH 20/48] fix issues pointed out by @BaptisteGelez in review --- plume-models/src/blogs.rs | 2 +- src/routes/blogs.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plume-models/src/blogs.rs b/plume-models/src/blogs.rs index a52dad57..a4037ec2 100644 --- a/plume-models/src/blogs.rs +++ b/plume-models/src/blogs.rs @@ -335,7 +335,7 @@ impl Blog { .unwrap_or_else(|| Host::new(format!( "{}/~/{}", Instance::get_local().unwrap().public_domain, - self.title + self.fqn, ))) ) } diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index bdcb1c55..41251a3c 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -68,7 +68,7 @@ pub fn details( match (blog.custom_domain, page) { (Some(ref custom_domain), Some(ref page)) => { - Ok(Redirect::to(format!("https://{}/?{}", custom_domain, page)).into()) + Ok(Redirect::to(format!("https://{}/?page={}", custom_domain, page)).into()) } (Some(ref custom_domain), _) => { Ok(Redirect::to(format!("https://{}/", custom_domain)).into()) -- 2.38.5 From f73fba583aadab15d6719dd73c009e5b728237a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Wed, 10 Jul 2019 17:34:45 +0200 Subject: [PATCH 21/48] custom_domainify posts::details --- src/main.rs | 1 + src/routes/posts.rs | 100 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 80 insertions(+), 21 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9c30c7b8..d99a4e2f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -201,6 +201,7 @@ Then try to restart Plume "/custom_domains/", routes![ routes::blogs::custom_details, + routes::posts::custom_details, routes::blogs::custom_activity_details, routes::search::custom_search, ], diff --git a/src/routes/posts.rs b/src/routes/posts.rs index c78fe43c..45420785 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -31,28 +31,14 @@ use routes::{ }; use template_utils::{IntoContext, Ructe}; -#[get("/~//?", rank = 4)] -pub fn details( - blog: String, - slug: String, +fn detail_guts( + blog: &Blog, + post: &Post, responding_to: Option, - rockets: PlumeRocket, -) -> Result { + rockets: &PlumeRocket, +) -> Result { let conn = &*rockets.conn; let user = rockets.user.clone(); - let blog = Blog::find_by_fqn(&rockets, &blog)?; - let post = Post::find_by_slug(&*conn, &slug, blog.id)?; - if !(post.published - || post - .get_authors(&*conn)? - .into_iter() - .any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0))) - { - return Ok(render!(errors::not_authorized( - &rockets.to_context(), - i18n!(rockets.intl.catalog, "This post isn't published yet.") - ))); - } let comments = CommentTree::from_post(&*conn, &post, user.as_ref())?; @@ -61,7 +47,7 @@ pub fn details( Ok(render!(posts::details( &rockets.to_context(), post.clone(), - blog, + blog.clone(), &NewCommentForm { warning: previous.clone().map(|p| p.spoiler_text).unwrap_or_default(), content: previous.clone().and_then(|p| Some(format!( @@ -94,7 +80,79 @@ pub fn details( user.clone().and_then(|u| u.has_reshared(&*conn, &post).ok()).unwrap_or(false), user.and_then(|u| u.is_following(&*conn, post.get_authors(&*conn).ok()?[0].id).ok()).unwrap_or(false), post.get_authors(&*conn)?[0].clone() - ))) + )).into()) +} + +#[get("/custom_domains//?", rank = 4)] +pub fn custom_details( + custom_domain: String, + slug: String, + responding_to: Option, + rockets: PlumeRocket, +) -> Result { + let conn = &*rockets.conn; + let user = rockets.user.clone(); + let blog = Blog::find_by_host(&rockets, Host::new(custom_domain))?; + let post = Post::find_by_slug(&*conn, &slug, blog.id)?; + if !(post.published + || post + .get_authors(&*conn)? + .into_iter() + .any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0))) + { + return Ok(render!(errors::not_authorized( + &rockets.to_context(), + i18n!(rockets.intl.catalog, "This post isn't published yet.") + )) + .into()); + } + + detail_guts(&blog, &post, responding_to, &rockets) +} + +#[get("/~//?", rank = 4)] +pub fn details( + blog: String, + slug: String, + responding_to: Option, + rockets: PlumeRocket, +) -> Result { + let conn = &*rockets.conn; + let user = rockets.user.clone(); + let blog = Blog::find_by_fqn(&rockets, &blog)?; + let post = Post::find_by_slug(&*conn, &slug, blog.id)?; + + if !(post.published + || post + .get_authors(&*conn)? + .into_iter() + .any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0))) + { + return Ok(render!(errors::not_authorized( + &rockets.to_context(), + i18n!(rockets.intl.catalog, "This post isn't published yet.") + )) + .into()); + } + + // check this first, and return early + // doing this prevents partially moving `blog` into the `match (tuple)`, + // which makes it impossible to reuse then. + if blog.custom_domain == None { + return detail_guts(&blog, &post, responding_to, &rockets); + } + + match (blog.custom_domain, responding_to) { + (Some(ref custom_domain), Some(ref responding_to)) => Ok(Redirect::to(format!( + "https://{}/?responding_to={}", + custom_domain, responding_to + )) + .into()), + (Some(ref custom_domain), _) => { + Ok(Redirect::to(format!("https://{}/", custom_domain)).into()) + } + (None, _) => panic!("This code path should have already been handled!"), + } } #[get("/~//", rank = 3)] -- 2.38.5 From 203da23cf2237b58d2d3c87fa9e2af0c597898b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Fri, 12 Jul 2019 22:48:03 +0200 Subject: [PATCH 22/48] follow up on @fdb-hiroshima & @BaptisteGelez review --- src/routes/posts.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/routes/posts.rs b/src/routes/posts.rs index 45420785..f8ecc1c2 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -144,14 +144,14 @@ pub fn details( match (blog.custom_domain, responding_to) { (Some(ref custom_domain), Some(ref responding_to)) => Ok(Redirect::to(format!( - "https://{}/?responding_to={}", - custom_domain, responding_to + "https://{}/{}?responding_to={}", + custom_domain, slug, responding_to )) .into()), (Some(ref custom_domain), _) => { - Ok(Redirect::to(format!("https://{}/", custom_domain)).into()) + Ok(Redirect::to(format!("https://{}/{}", custom_domain, slug)).into()) } - (None, _) => panic!("This code path should have already been handled!"), + (None, _) => unreachable!("This code path should have already been handled!"), } } -- 2.38.5 From 7139119b8f54e3d71d9e988d615061691fd46bef Mon Sep 17 00:00:00 2001 From: Trinity Pointard Date: Sun, 14 Jul 2019 15:54:02 +0200 Subject: [PATCH 23/48] add url! macro for custom domain path uri creation see doc-comment for limitations --- src/template_utils.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/template_utils.rs b/src/template_utils.rs index 697084c1..577a47f0 100644 --- a/src/template_utils.rs +++ b/src/template_utils.rs @@ -342,3 +342,28 @@ macro_rules! input { )) }}; } + +/// This macro imitate rocket's uri!, but with support for custom domains +/// +/// It takes one more argument, domain, which must appear first, and must be an Option +/// sample call : +/// url!(domain=Some("something.tld".to_owned()), posts::details: blog = "blogname", slug = "title", responding_to = _) +/// routes used in this macro must have a proper custom-domain counterpart, for instance +/// for above call to compile, posts::custom::details must exist, and take the same parameters as +/// posts::details (plus the custom domain) +macro_rules! url { + (domain=$domain:expr, $module:ident::$route:ident: $($tt:tt)*) => {{ + let domain: Option = $domain; //for type inference with None + if let Some(domain) = domain { + let origin = uri!(crate::routes::$module::custom::$route: custom_domain=&domain, $($tt)*); + let path = origin.segments().skip(1).map(|seg| format!("/{}", seg)).collect::(); //first segment is domain, drop it + let query = origin.query().map(|q| format!("?{}", q)).unwrap_or_default(); + format!("https://{}{}{}", &domain, path, query) + } else { + url!($module::$route: $($tt)*) + } + }}; + ($module:ident::$route:ident: $($tt:tt)*) => {{ + uri!(crate::routes::$module::$route: $($tt)*).to_string() + }} +} -- 2.38.5 From f635dcf6c3dc7847e967a117d5f12a42bcb5c7fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Sun, 14 Jul 2019 17:14:08 +0200 Subject: [PATCH 24/48] move custom_ route functions into a custom namespace this way, we can actually use the url! macro --- src/main.rs | 8 +++--- src/routes/blogs.rs | 48 +++++++++++++++++++---------------- src/routes/posts.rs | 60 ++++++++++++++++++++++++-------------------- src/routes/search.rs | 20 +++++++++------ 4 files changed, 77 insertions(+), 59 deletions(-) diff --git a/src/main.rs b/src/main.rs index d99a4e2f..f1c903dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -200,10 +200,10 @@ Then try to restart Plume .mount( "/custom_domains/", routes![ - routes::blogs::custom_details, - routes::posts::custom_details, - routes::blogs::custom_activity_details, - routes::search::custom_search, + routes::blogs::custom::details, + routes::posts::custom::details, + routes::blogs::custom::activity_details, + routes::search::custom::search, ], ) .mount( diff --git a/src/routes/blogs.rs b/src/routes/blogs.rs index 41251a3c..b5edf6c5 100644 --- a/src/routes/blogs.rs +++ b/src/routes/blogs.rs @@ -41,16 +41,6 @@ fn detail_guts( .into()) } -#[get("/?", rank = 2)] -pub fn custom_details( - custom_domain: String, - page: Option, - rockets: PlumeRocket, -) -> Result { - let blog = Blog::find_by_host(&rockets, Host::new(custom_domain))?; - detail_guts(blog, page, rockets) -} - #[get("/~/?", rank = 2)] pub fn details( name: String, @@ -74,7 +64,7 @@ pub fn details( Ok(Redirect::to(format!("https://{}/", custom_domain)).into()) } // we need this match arm, or the match won't compile - (None, _) => panic!("This code path should have already been handled!"), + (None, _) => unreachable!("This code path should have already been handled!"), } } @@ -86,16 +76,6 @@ pub fn activity_detail_guts( Some(ActivityStream::new(blog.to_activity(&*rockets.conn).ok()?)) } -#[get("/", rank = 1)] -pub fn custom_activity_details( - custom_domain: String, - rockets: PlumeRocket, - _ap: ApRequest, -) -> Option> { - let blog = Blog::find_by_host(&rockets, Host::new(custom_domain)).ok()?; - activity_detail_guts(blog, rockets, _ap) -} - #[get("/~/", rank = 1)] pub fn activity_details( name: String, @@ -115,6 +95,32 @@ pub fn new(rockets: PlumeRocket, _user: User) -> Ructe { )) } +pub mod custom { + use plume_common::activity_pub::{ActivityStream, ApRequest}; + use plume_models::{blogs::Blog, blogs::CustomGroup, blogs::Host, PlumeRocket}; + use routes::{errors::ErrorPage, Page, RespondOrRedirect}; + + #[get("/?", rank = 2)] + pub fn details( + custom_domain: String, + page: Option, + rockets: PlumeRocket, + ) -> Result { + let blog = Blog::find_by_host(&rockets, Host::new(custom_domain))?; + super::detail_guts(blog, page, rockets) + } + + #[get("/", rank = 1)] + pub fn activity_details( + custom_domain: String, + rockets: PlumeRocket, + _ap: ApRequest, + ) -> Option> { + let blog = Blog::find_by_host(&rockets, Host::new(custom_domain)).ok()?; + super::activity_detail_guts(blog, rockets, _ap) + } +} + #[get("/blogs/new", rank = 2)] pub fn new_auth(i18n: I18n) -> Flash { utils::requires_login( diff --git a/src/routes/posts.rs b/src/routes/posts.rs index f8ecc1c2..0c1be74e 100644 --- a/src/routes/posts.rs +++ b/src/routes/posts.rs @@ -83,33 +83,6 @@ fn detail_guts( )).into()) } -#[get("/custom_domains//?", rank = 4)] -pub fn custom_details( - custom_domain: String, - slug: String, - responding_to: Option, - rockets: PlumeRocket, -) -> Result { - let conn = &*rockets.conn; - let user = rockets.user.clone(); - let blog = Blog::find_by_host(&rockets, Host::new(custom_domain))?; - let post = Post::find_by_slug(&*conn, &slug, blog.id)?; - if !(post.published - || post - .get_authors(&*conn)? - .into_iter() - .any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0))) - { - return Ok(render!(errors::not_authorized( - &rockets.to_context(), - i18n!(rockets.intl.catalog, "This post isn't published yet.") - )) - .into()); - } - - detail_guts(&blog, &post, responding_to, &rockets) -} - #[get("/~//?", rank = 4)] pub fn details( blog: String, @@ -155,6 +128,39 @@ pub fn details( } } +pub mod custom { + use plume_models::{blogs::Blog, blogs::Host, posts::Post, PlumeRocket}; + use routes::{errors::ErrorPage, RespondOrRedirect}; + use template_utils::{IntoContext, Ructe}; + + #[get("/custom_domains//?", rank = 4)] + pub fn details( + custom_domain: String, + slug: String, + responding_to: Option, + rockets: PlumeRocket, + ) -> Result { + let conn = &*rockets.conn; + let user = rockets.user.clone(); + let blog = Blog::find_by_host(&rockets, Host::new(custom_domain))?; + let post = Post::find_by_slug(&*conn, &slug, blog.id)?; + if !(post.published + || post + .get_authors(&*conn)? + .into_iter() + .any(|a| a.id == user.clone().map(|u| u.id).unwrap_or(0))) + { + return Ok(render!(errors::not_authorized( + &rockets.to_context(), + i18n!(rockets.intl.catalog, "This post isn't published yet.") + )) + .into()); + } + + super::detail_guts(&blog, &post, responding_to, &rockets) + } +} + #[get("/~//", rank = 3)] pub fn activity_details( blog: String, diff --git a/src/routes/search.rs b/src/routes/search.rs index 11d921d7..4df7ac77 100644 --- a/src/routes/search.rs +++ b/src/routes/search.rs @@ -88,11 +88,17 @@ pub fn search(query: Option>, rockets: PlumeRocket) -> Ructe { search_guts(query, rockets) } -#[get("/<_custom_domain>/search?")] -pub fn custom_search( - _custom_domain: String, - query: Option>, - rockets: PlumeRocket, -) -> Ructe { - search_guts(query, rockets) +pub mod custom { + use plume_models::PlumeRocket; + use rocket::request::Form; + use template_utils::Ructe; + + #[get("/<_custom_domain>/search?")] + pub fn search( + _custom_domain: String, + query: Option>, + rockets: PlumeRocket, + ) -> Ructe { + super::search_guts(query, rockets) + } } -- 2.38.5 From cdc919e308a919fd0edef6670b1a3dff423e8e70 Mon Sep 17 00:00:00 2001 From: Trinity Pointard Date: Sun, 14 Jul 2019 19:28:07 +0200 Subject: [PATCH 25/48] allow more syntaxes for url allow for params that are used only by one of normal/custom routes --- src/template_utils.rs | 71 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/src/template_utils.rs b/src/template_utils.rs index 577a47f0..534dff95 100644 --- a/src/template_utils.rs +++ b/src/template_utils.rs @@ -345,25 +345,72 @@ macro_rules! input { /// This macro imitate rocket's uri!, but with support for custom domains /// -/// It takes one more argument, domain, which must appear first, and must be an Option +/// It takes one more argument, domain, which must appear first, and must be an Option<&str> /// sample call : -/// url!(domain=Some("something.tld".to_owned()), posts::details: blog = "blogname", slug = "title", responding_to = _) -/// routes used in this macro must have a proper custom-domain counterpart, for instance -/// for above call to compile, posts::custom::details must exist, and take the same parameters as -/// posts::details (plus the custom domain) +/// assuming both take the same parameters +/// url!(custom_domain=Some("something.tld"), posts::details: slug = "title", responding_to = _, blog = "blogname")); +/// +/// assuming posts::details take one more parameter than posts::custom::details +/// url!(custom_domain=Some("something.tld"), posts::details: +/// common=[slug = "title", responding_to = _], +/// normal=[blog = "blogname"])); +/// +/// you can also provide custom=[] for custom-domain specific arguments +/// custom_domain can be changed to anything, indicating custom domain varname in the custom-domain +/// function (most likely custom_domain or _custom_domain) macro_rules! url { - (domain=$domain:expr, $module:ident::$route:ident: $($tt:tt)*) => {{ - let domain: Option = $domain; //for type inference with None + ($custom_domain:ident=$domain:expr, $module:ident::$route:ident: + common=[$($common_args:ident = $common_val:expr),*], + normal=[$($normal_args:ident = $normal_val:expr),*], + custom=[$($custom_args:ident = $custom_val:expr),*]) => {{ + let domain: Option<&str> = $domain; //for type inference with None if let Some(domain) = domain { - let origin = uri!(crate::routes::$module::custom::$route: custom_domain=&domain, $($tt)*); + let origin = uri!(crate::routes::$module::custom::$route: + $custom_domain=&domain, + $($common_args = $common_val,)* + $($custom_args = $custom_val,)* + ); let path = origin.segments().skip(1).map(|seg| format!("/{}", seg)).collect::(); //first segment is domain, drop it let query = origin.query().map(|q| format!("?{}", q)).unwrap_or_default(); format!("https://{}{}{}", &domain, path, query) } else { - url!($module::$route: $($tt)*) + url!($module::$route: + $($common_args = $common_val,)* + $($normal_args = $normal_val,)*) + .to_string() } }}; - ($module:ident::$route:ident: $($tt:tt)*) => {{ - uri!(crate::routes::$module::$route: $($tt)*).to_string() - }} + ($cd:ident=$d:expr, $m:ident::$r:ident: + common=[$($tt:tt)*]) => { + url!($cd=$d, $m::$r: common=[$($tt)*], normal=[], custom=[]) + }; + ($cd:ident=$d:expr, $m:ident::$r:ident: + normal=[$($tt:tt)*]) => { + url!($cd=$d, $m::$r: common=[], normal=[$($tt)*], custom=[]) + }; + ($cd:ident=$d:expr, $m:ident::$r:ident: + custom=[$($tt:tt)*]) => { + url!($cd=$d, $m::$r: common=[], normal=[], custom=[$($tt)*]) + }; + ($cd:ident=$d:expr, $m:ident::$r:ident: + common=[$($co:tt)*], + normal=[$($no:tt)*]) => { + url!($cd=$d, $m::$r: common=[$($co)*], normal=[$($no)*], custom=[]) + }; + ($cd:ident=$d:expr, $m:ident::$r:ident: + common=[$($co:tt)*], + custom=[$($cu:tt)*]) => { + url!($cd=$d, $m::$r: common=[$($co)*], normal=[], custom=[$($cu)*]) + }; + ($cd:ident=$d:expr, $m:ident::$r:ident: + normal=[$($no:tt)*], + custom=[$($cu:tt)*]) => { + url!($cd=$d, $m::$r: common=[], normal=[$($no)*], custom=[$($cu)*]) + }; + ($custom_domain:ident=$domain:expr, $module:ident::$route:ident: $($common_args:tt)*) => { + url!($custom_domain=$domain, $module::$route: common=[$($common_args)*]) + }; + ($module:ident::$route:ident: $($tt:tt)*) => { + uri!(crate::routes::$module::$route: $($tt)*) + }; } -- 2.38.5 From 6cd8bd89b2961df2d38185171b38859c93c3ca6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Gali=C4=87?= Date: Sun, 21 Jul 2019 15:32:07 +0200 Subject: [PATCH 26/48] fix syntax: we're now down to type errors --- templates/blogs/details.rs.html | 4 ++-- templates/partials/post_card.rs.html | 5 ++--- templates/posts/details.rs.html | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/templates/blogs/details.rs.html b/templates/blogs/details.rs.html index 571b1139..51a2a7f2 100644 --- a/templates/blogs/details.rs.html +++ b/templates/blogs/details.rs.html @@ -24,7 +24,7 @@ }, { - @blog.title + @blog.title }, { - +

@i18n!(ctx.1, "There's one author on this blog: ", "There are {0} authors on this blog: "; authors.len()) diff --git a/templates/partials/post_card.rs.html b/templates/partials/post_card.rs.html index e9e9f19a..296270b3 100644 --- a/templates/partials/post_card.rs.html +++ b/templates/partials/post_card.rs.html @@ -9,7 +9,7 @@

}

- + @article.title

@@ -25,10 +25,9 @@ @if article.published { ⋅ @article.creation_date.format("%B %e, %Y") } - ⋅ @article.get_blog(ctx.0).unwrap().title + ⋅ @article.get_blog(ctx.0).unwrap().title @if !article.published { ⋅ @i18n!(ctx.1, "Draft") } - diff --git a/templates/posts/details.rs.html b/templates/posts/details.rs.html index f8a0803f..f88d3f73 100644 --- a/templates/posts/details.rs.html +++ b/templates/posts/details.rs.html @@ -17,10 +17,10 @@ @if article.cover_id.is_some() { } - + }, { - @blog.title + @blog.title }, {
@for tag in tags { @if !tag.is_hashtag { -
  • @tag.tag
  • +
  • @tag.tag
  • } else { } -- 2.38.5 From c5f6b88b1d36670ff0ca4877f7bc79a578e9977d Mon Sep 17 00:00:00 2001 From: Trinity Pointard Date: Mon, 22 Jul 2019 22:15:25 +0200 Subject: [PATCH 27/48] make url! work better there is still some issue with `None`, and an error making no sense at some place --- src/template_utils.rs | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/template_utils.rs b/src/template_utils.rs index 534dff95..3d85ffd3 100644 --- a/src/template_utils.rs +++ b/src/template_utils.rs @@ -360,23 +360,32 @@ macro_rules! input { /// function (most likely custom_domain or _custom_domain) macro_rules! url { ($custom_domain:ident=$domain:expr, $module:ident::$route:ident: - common=[$($common_args:ident = $common_val:expr),*], - normal=[$($normal_args:ident = $normal_val:expr),*], - custom=[$($custom_args:ident = $custom_val:expr),*]) => {{ - let domain: Option<&str> = $domain; //for type inference with None + common=[$($common_args:tt = $common_val:expr),*], + normal=[$($normal_args:tt = $normal_val:expr),*], + custom=[$($custom_args:tt = $custom_val:expr),*]) => {{ + let domain: Option<&plume_models::blogs::Host> = $domain.as_ref(); //for type inference with None + $( + let $common_args = $common_val; + )* if let Some(domain) = domain { + $( + let $custom_args = $custom_val; + )* let origin = uri!(crate::routes::$module::custom::$route: - $custom_domain=&domain, - $($common_args = $common_val,)* - $($custom_args = $custom_val,)* + $custom_domain = domain.as_ref(), + $($common_args = $common_args,)* + $($custom_args = $custom_args,)* ); let path = origin.segments().skip(1).map(|seg| format!("/{}", seg)).collect::(); //first segment is domain, drop it let query = origin.query().map(|q| format!("?{}", q)).unwrap_or_default(); format!("https://{}{}{}", &domain, path, query) } else { + $( + let $normal_args = $normal_val; + )* url!($module::$route: - $($common_args = $common_val,)* - $($normal_args = $normal_val,)*) + $($common_args = $common_args,)* + $($normal_args = $normal_args,)*) .to_string() } }}; -- 2.38.5 From b172a80e35d66ae12851ca630a4fbd0932373d6b Mon Sep 17 00:00:00 2001 From: Trinity Pointard Date: Tue, 23 Jul 2019 00:07:23 +0200 Subject: [PATCH 28/48] finally fix url! also please a bit clippy --- src/template_utils.rs | 19 ++++++++++++++++--- templates/blogs/details.rs.html | 2 +- templates/partials/post_card.rs.html | 2 +- templates/posts/details.rs.html | 2 +- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/template_utils.rs b/src/template_utils.rs index 3d85ffd3..6c034d15 100644 --- a/src/template_utils.rs +++ b/src/template_utils.rs @@ -1,7 +1,10 @@ use plume_models::{notifications::*, users::User, Connection, PlumeRocket}; use rocket::http::hyper::header::{ETag, EntityTag}; -use rocket::http::{Method, Status}; +use rocket::http::{ + uri::{FromUriParam, Query}, + Method, Status, +}; use rocket::request::Request; use rocket::response::{self, content::Html as HtmlCt, Responder, Response}; use rocket_i18n::Catalog; @@ -13,6 +16,16 @@ pub use askama_escape::escape; pub static CACHE_NAME: &str = env!("CACHE_ID"); +pub struct NoValue; // workarround for missing FromUriParam implementation for Option + +impl FromUriParam for Option { + type Target = Option; + + fn from_uri_param(_: NoValue) -> Self::Target { + None + } +} + pub type BaseContext<'a> = &'a ( &'a Connection, &'a Catalog, @@ -363,7 +376,7 @@ macro_rules! url { common=[$($common_args:tt = $common_val:expr),*], normal=[$($normal_args:tt = $normal_val:expr),*], custom=[$($custom_args:tt = $custom_val:expr),*]) => {{ - let domain: Option<&plume_models::blogs::Host> = $domain.as_ref(); //for type inference with None + let domain: &Option = &$domain; //for type inference with None $( let $common_args = $common_val; )* @@ -372,7 +385,7 @@ macro_rules! url { let $custom_args = $custom_val; )* let origin = uri!(crate::routes::$module::custom::$route: - $custom_domain = domain.as_ref(), + $custom_domain = domain.to_string(), $($common_args = $common_args,)* $($custom_args = $custom_args,)* ); diff --git a/templates/blogs/details.rs.html b/templates/blogs/details.rs.html index 51a2a7f2..d4035348 100644 --- a/templates/blogs/details.rs.html +++ b/templates/blogs/details.rs.html @@ -24,7 +24,7 @@ }, { - @blog.title + @blog.title }, {