RSS for posts
This commit is contained in:
parent
bb118f6144
commit
8fac396d7e
13 changed files with 233 additions and 14 deletions
83
Cargo.lock
generated
83
Cargo.lock
generated
|
|
@ -370,6 +370,19 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atom_syndication"
|
||||||
|
version = "0.12.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2f68d23e2cb4fd958c705b91a6b4c80ceeaf27a9e11651272a8389d5ce1a4a3"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"derive_builder",
|
||||||
|
"diligent-date-parser",
|
||||||
|
"never",
|
||||||
|
"quick-xml",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic-waker"
|
name = "atomic-waker"
|
||||||
version = "1.1.2"
|
version = "1.1.2"
|
||||||
|
|
@ -953,6 +966,7 @@ dependencies = [
|
||||||
"ident_case",
|
"ident_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
"strsim",
|
||||||
"syn 2.0.100",
|
"syn 2.0.100",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -988,6 +1002,37 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_builder"
|
||||||
|
version = "0.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
|
||||||
|
dependencies = [
|
||||||
|
"derive_builder_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_builder_core"
|
||||||
|
version = "0.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.100",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_builder_macro"
|
||||||
|
version = "0.20.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||||
|
dependencies = [
|
||||||
|
"derive_builder_core",
|
||||||
|
"syn 2.0.100",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
|
|
@ -1000,6 +1045,15 @@ dependencies = [
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "diligent-date-parser"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8ede7d79366f419921e2e2f67889c12125726692a313bffb474bd5f37a581e9"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "displaydoc"
|
name = "displaydoc"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
|
|
@ -2062,6 +2116,12 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "never"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "new_debug_unreachable"
|
name = "new_debug_unreachable"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
|
@ -2518,6 +2578,16 @@ version = "2.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-xml"
|
||||||
|
version = "0.37.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4ce8c88de324ff838700f36fb6ab86c96df0e3c4ab6ef3a9b2044465cce1369"
|
||||||
|
dependencies = [
|
||||||
|
"encoding_rs",
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.40"
|
version = "1.0.40"
|
||||||
|
|
@ -2800,6 +2870,18 @@ dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rss"
|
||||||
|
version = "2.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2107738f003660f0a91f56fd3e3bd3ab5d918b2ddaf1e1ec2136fb1c46f71bf"
|
||||||
|
dependencies = [
|
||||||
|
"atom_syndication",
|
||||||
|
"derive_builder",
|
||||||
|
"never",
|
||||||
|
"quick-xml",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rust-embed"
|
name = "rust-embed"
|
||||||
version = "8.7.0"
|
version = "8.7.0"
|
||||||
|
|
@ -2969,6 +3051,7 @@ dependencies = [
|
||||||
"password-auth",
|
"password-auth",
|
||||||
"pulldown-cmark",
|
"pulldown-cmark",
|
||||||
"rand 0.9.0",
|
"rand 0.9.0",
|
||||||
|
"rss",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"sea-orm",
|
"sea-orm",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ mime_guess = "2.0.5"
|
||||||
password-auth = "1.0.0"
|
password-auth = "1.0.0"
|
||||||
pulldown-cmark = "0.13.0"
|
pulldown-cmark = "0.13.0"
|
||||||
rand = "0.9.0"
|
rand = "0.9.0"
|
||||||
|
rss = "2.0.12"
|
||||||
rust-embed = { version = "8.7.0", features = ["axum", "debug-embed"] }
|
rust-embed = { version = "8.7.0", features = ["axum", "debug-embed"] }
|
||||||
sea-orm = { version = "1.1.8", features = [
|
sea-orm = { version = "1.1.8", features = [
|
||||||
"sqlx-sqlite",
|
"sqlx-sqlite",
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,10 @@ Still very much an early WIP.
|
||||||
### Roadmap
|
### Roadmap
|
||||||
|
|
||||||
- [ ] Favicon from post
|
- [ ] Favicon from post
|
||||||
|
- [ ] Bulk edit tag
|
||||||
|
- [ ] RSS feed
|
||||||
- [ ] Logging
|
- [ ] Logging
|
||||||
- [ ] Improved error handling
|
- [ ] Improved error handling
|
||||||
- [ ] Bulk edit tag
|
|
||||||
- [ ] Caching
|
- [ ] Caching
|
||||||
- [ ] Lossless compression
|
- [ ] Lossless compression
|
||||||
- [ ] User management
|
- [ ] User management
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,13 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) const APPLICATION_NAME_KEY: &str = "APPLICATION_NAME";
|
pub(crate) const APPLICATION_NAME_KEY: &str = "APPLICATION_NAME";
|
||||||
|
pub(crate) const BASE_URL_KEY: &str = "BASE_URL";
|
||||||
pub(crate) const AGE_CONFIRMATION_KEY: &str = "AGE_CONFIRMATION";
|
pub(crate) const AGE_CONFIRMATION_KEY: &str = "AGE_CONFIRMATION";
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct AppConfig {
|
pub(crate) struct AppConfig {
|
||||||
pub(crate) application_name: String,
|
pub(crate) application_name: String,
|
||||||
|
pub(crate) base_url: String,
|
||||||
pub(crate) age_confirmation: bool,
|
pub(crate) age_confirmation: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -24,6 +26,14 @@ impl AppConfig {
|
||||||
Some(row) => row.data.as_str().unwrap_or("Samey").to_owned(),
|
Some(row) => row.data.as_str().unwrap_or("Samey").to_owned(),
|
||||||
None => "Samey".to_owned(),
|
None => "Samey".to_owned(),
|
||||||
};
|
};
|
||||||
|
let base_url = match SameyConfig::find()
|
||||||
|
.filter(samey_config::Column::Key.eq(BASE_URL_KEY))
|
||||||
|
.one(db)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
Some(row) => row.data.as_str().unwrap_or("").to_owned(),
|
||||||
|
None => "".to_owned(),
|
||||||
|
};
|
||||||
let age_confirmation = match SameyConfig::find()
|
let age_confirmation = match SameyConfig::find()
|
||||||
.filter(samey_config::Column::Key.eq(AGE_CONFIRMATION_KEY))
|
.filter(samey_config::Column::Key.eq(AGE_CONFIRMATION_KEY))
|
||||||
.one(db)
|
.one(db)
|
||||||
|
|
@ -34,6 +44,7 @@ impl AppConfig {
|
||||||
};
|
};
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
application_name,
|
application_name,
|
||||||
|
base_url,
|
||||||
age_confirmation,
|
age_confirmation,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,9 @@ pub async fn get_router(
|
||||||
fs::create_dir_all(files_dir.as_ref()).await?;
|
fs::create_dir_all(files_dir.as_ref()).await?;
|
||||||
|
|
||||||
let session_store = SessionStorage::new(db.clone());
|
let session_store = SessionStorage::new(db.clone());
|
||||||
let session_layer = SessionManagerLayer::new(session_store);
|
let session_layer = SessionManagerLayer::new(session_store).with_expiry(
|
||||||
|
tower_sessions::Expiry::OnInactivity(time::Duration::weeks(1)),
|
||||||
|
);
|
||||||
let auth_layer = AuthManagerLayerBuilder::new(Backend::new(db), session_layer).build();
|
let auth_layer = AuthManagerLayerBuilder::new(Backend::new(db), session_layer).build();
|
||||||
|
|
||||||
Ok(Router::new()
|
Ok(Router::new()
|
||||||
|
|
@ -134,6 +136,7 @@ pub async fn get_router(
|
||||||
.route_with_tsr("/posts/{page}", get(posts_page))
|
.route_with_tsr("/posts/{page}", get(posts_page))
|
||||||
// Other routes
|
// Other routes
|
||||||
.route_with_tsr("/remove", delete(remove_field))
|
.route_with_tsr("/remove", delete(remove_field))
|
||||||
|
.route("/posts.xml", get(rss_page))
|
||||||
.route("/", get(index))
|
.route("/", get(index))
|
||||||
.with_state(state)
|
.with_state(state)
|
||||||
.nest_service("/files", ServeDir::new(files_dir))
|
.nest_service("/files", ServeDir::new(files_dir))
|
||||||
|
|
|
||||||
13
src/query.rs
13
src/query.rs
|
|
@ -1,5 +1,6 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
use migration::{Expr, Query};
|
use migration::{Expr, Query};
|
||||||
use sea_orm::{
|
use sea_orm::{
|
||||||
ColumnTrait, Condition, DatabaseConnection, EntityTrait, FromQueryResult, IntoSimpleExpr,
|
ColumnTrait, Condition, DatabaseConnection, EntityTrait, FromQueryResult, IntoSimpleExpr,
|
||||||
|
|
@ -20,6 +21,10 @@ use crate::{
|
||||||
pub(crate) struct PostOverview {
|
pub(crate) struct PostOverview {
|
||||||
pub(crate) id: i32,
|
pub(crate) id: i32,
|
||||||
pub(crate) thumbnail: String,
|
pub(crate) thumbnail: String,
|
||||||
|
pub(crate) media: String,
|
||||||
|
pub(crate) title: Option<String>,
|
||||||
|
pub(crate) description: Option<String>,
|
||||||
|
pub(crate) uploaded_at: NaiveDateTime,
|
||||||
pub(crate) tags: Option<String>,
|
pub(crate) tags: Option<String>,
|
||||||
pub(crate) media_type: String,
|
pub(crate) media_type: String,
|
||||||
pub(crate) rating: String,
|
pub(crate) rating: String,
|
||||||
|
|
@ -54,6 +59,10 @@ pub(crate) fn search_posts(
|
||||||
let mut query = SameyPost::find()
|
let mut query = SameyPost::find()
|
||||||
.select_only()
|
.select_only()
|
||||||
.column(samey_post::Column::Id)
|
.column(samey_post::Column::Id)
|
||||||
|
.column(samey_post::Column::Media)
|
||||||
|
.column(samey_post::Column::Title)
|
||||||
|
.column(samey_post::Column::Description)
|
||||||
|
.column(samey_post::Column::UploadedAt)
|
||||||
.column(samey_post::Column::Thumbnail)
|
.column(samey_post::Column::Thumbnail)
|
||||||
.column(samey_post::Column::Rating)
|
.column(samey_post::Column::Rating)
|
||||||
.column(samey_post::Column::MediaType)
|
.column(samey_post::Column::MediaType)
|
||||||
|
|
@ -77,6 +86,10 @@ pub(crate) fn search_posts(
|
||||||
let mut query = SameyPost::find()
|
let mut query = SameyPost::find()
|
||||||
.select_only()
|
.select_only()
|
||||||
.column(samey_post::Column::Id)
|
.column(samey_post::Column::Id)
|
||||||
|
.column(samey_post::Column::Media)
|
||||||
|
.column(samey_post::Column::Title)
|
||||||
|
.column(samey_post::Column::Description)
|
||||||
|
.column(samey_post::Column::UploadedAt)
|
||||||
.column(samey_post::Column::Thumbnail)
|
.column(samey_post::Column::Thumbnail)
|
||||||
.column(samey_post::Column::Rating)
|
.column(samey_post::Column::Rating)
|
||||||
.column(samey_post::Column::MediaType)
|
.column(samey_post::Column::MediaType)
|
||||||
|
|
|
||||||
85
src/views.rs
85
src/views.rs
|
|
@ -29,7 +29,7 @@ use tokio::{task::spawn_blocking, try_join};
|
||||||
use crate::{
|
use crate::{
|
||||||
AppState, NEGATIVE_PREFIX, RATING_PREFIX,
|
AppState, NEGATIVE_PREFIX, RATING_PREFIX,
|
||||||
auth::{AuthSession, Credentials, User},
|
auth::{AuthSession, Credentials, User},
|
||||||
config::{AGE_CONFIRMATION_KEY, APPLICATION_NAME_KEY},
|
config::{AGE_CONFIRMATION_KEY, APPLICATION_NAME_KEY, BASE_URL_KEY},
|
||||||
entities::{
|
entities::{
|
||||||
prelude::{
|
prelude::{
|
||||||
SameyConfig, SameyPool, SameyPoolPost, SameyPost, SameyPostSource, SameyTag,
|
SameyConfig, SameyPool, SameyPoolPost, SameyPost, SameyPostSource, SameyTag,
|
||||||
|
|
@ -90,6 +90,63 @@ pub(crate) async fn index(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RSS view
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "fragments/rss_entry.html")]
|
||||||
|
struct RssEntryTemplate<'a> {
|
||||||
|
post: PostOverview,
|
||||||
|
base_url: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[axum::debug_handler]
|
||||||
|
pub(crate) async fn rss_page(
|
||||||
|
State(AppState { app_config, db, .. }): State<AppState>,
|
||||||
|
Query(query): Query<PostsQuery>,
|
||||||
|
) -> Result<impl IntoResponse, SameyError> {
|
||||||
|
let app_config = app_config.read().await;
|
||||||
|
let application_name = app_config.application_name.clone();
|
||||||
|
let base_url = app_config.base_url.clone();
|
||||||
|
drop(app_config);
|
||||||
|
|
||||||
|
let tags = query
|
||||||
|
.tags
|
||||||
|
.as_ref()
|
||||||
|
.map(|tags| tags.split_whitespace().collect::<Vec<_>>());
|
||||||
|
|
||||||
|
let posts = search_posts(tags.as_ref(), None)
|
||||||
|
.paginate(&db, 20)
|
||||||
|
.fetch_page(0)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let channel = rss::ChannelBuilder::default()
|
||||||
|
.title(&application_name)
|
||||||
|
.link(&base_url)
|
||||||
|
.items(
|
||||||
|
posts
|
||||||
|
.into_iter()
|
||||||
|
.map(|post| {
|
||||||
|
rss::ItemBuilder::default()
|
||||||
|
.title(post.tags.clone())
|
||||||
|
.pub_date(post.uploaded_at.and_utc().to_rfc2822())
|
||||||
|
.link(format!("{}/post/{}", &base_url, post.id))
|
||||||
|
.content(
|
||||||
|
RssEntryTemplate {
|
||||||
|
post,
|
||||||
|
base_url: &base_url,
|
||||||
|
}
|
||||||
|
.render()
|
||||||
|
.ok(),
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
})
|
||||||
|
.collect_vec(),
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Ok(channel.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
// Auth views
|
// Auth views
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
|
|
@ -1097,6 +1154,7 @@ pub(crate) async fn sort_pool(
|
||||||
#[template(path = "pages/settings.html")]
|
#[template(path = "pages/settings.html")]
|
||||||
struct SettingsTemplate {
|
struct SettingsTemplate {
|
||||||
application_name: String,
|
application_name: String,
|
||||||
|
base_url: String,
|
||||||
age_confirmation: bool,
|
age_confirmation: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1110,6 +1168,7 @@ pub(crate) async fn settings(
|
||||||
|
|
||||||
let app_config = app_config.read().await;
|
let app_config = app_config.read().await;
|
||||||
let application_name = app_config.application_name.clone();
|
let application_name = app_config.application_name.clone();
|
||||||
|
let base_url = app_config.base_url.clone();
|
||||||
let age_confirmation = app_config.age_confirmation;
|
let age_confirmation = app_config.age_confirmation;
|
||||||
drop(app_config);
|
drop(app_config);
|
||||||
|
|
||||||
|
|
@ -1129,6 +1188,7 @@ pub(crate) async fn settings(
|
||||||
Ok(Html(
|
Ok(Html(
|
||||||
SettingsTemplate {
|
SettingsTemplate {
|
||||||
application_name,
|
application_name,
|
||||||
|
base_url,
|
||||||
age_confirmation,
|
age_confirmation,
|
||||||
}
|
}
|
||||||
.render_with_values(&values)?,
|
.render_with_values(&values)?,
|
||||||
|
|
@ -1138,6 +1198,7 @@ pub(crate) async fn settings(
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub(crate) struct UpdateSettingsForm {
|
pub(crate) struct UpdateSettingsForm {
|
||||||
application_name: String,
|
application_name: String,
|
||||||
|
base_url: String,
|
||||||
age_confirmation: Option<bool>,
|
age_confirmation: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1164,6 +1225,16 @@ pub(crate) async fn update_settings(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _ = mem::replace(
|
||||||
|
&mut app_config.write().await.base_url,
|
||||||
|
body.base_url.clone(),
|
||||||
|
);
|
||||||
|
configs.push(samey_config::ActiveModel {
|
||||||
|
key: Set(BASE_URL_KEY.into()),
|
||||||
|
data: Set(body.base_url.into()),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
let age_confirmation = body.age_confirmation.is_some();
|
let age_confirmation = body.age_confirmation.is_some();
|
||||||
let _ = mem::replace(
|
let _ = mem::replace(
|
||||||
&mut app_config.write().await.age_confirmation,
|
&mut app_config.write().await.age_confirmation,
|
||||||
|
|
@ -1248,6 +1319,10 @@ pub(crate) async fn view_post_page(
|
||||||
Some(parent_post) => Some(PostOverview {
|
Some(parent_post) => Some(PostOverview {
|
||||||
id: parent_id,
|
id: parent_id,
|
||||||
thumbnail: parent_post.thumbnail,
|
thumbnail: parent_post.thumbnail,
|
||||||
|
title: parent_post.title,
|
||||||
|
description: parent_post.description,
|
||||||
|
uploaded_at: parent_post.uploaded_at,
|
||||||
|
media: parent_post.media,
|
||||||
tags: Some(
|
tags: Some(
|
||||||
get_tags_for_post(post_id)
|
get_tags_for_post(post_id)
|
||||||
.all(&db)
|
.all(&db)
|
||||||
|
|
@ -1277,6 +1352,10 @@ pub(crate) async fn view_post_page(
|
||||||
children_posts.push(PostOverview {
|
children_posts.push(PostOverview {
|
||||||
id: child_post.id,
|
id: child_post.id,
|
||||||
thumbnail: child_post.thumbnail,
|
thumbnail: child_post.thumbnail,
|
||||||
|
title: child_post.title,
|
||||||
|
description: child_post.description,
|
||||||
|
uploaded_at: child_post.uploaded_at,
|
||||||
|
media: child_post.media,
|
||||||
tags: Some(
|
tags: Some(
|
||||||
get_tags_for_post(child_post.id)
|
get_tags_for_post(child_post.id)
|
||||||
.all(&db)
|
.all(&db)
|
||||||
|
|
@ -1410,6 +1489,10 @@ pub(crate) async fn submit_post_details(
|
||||||
Some(parent_post) => Some(PostOverview {
|
Some(parent_post) => Some(PostOverview {
|
||||||
id: parent_id,
|
id: parent_id,
|
||||||
thumbnail: parent_post.thumbnail,
|
thumbnail: parent_post.thumbnail,
|
||||||
|
title: parent_post.title,
|
||||||
|
description: parent_post.description,
|
||||||
|
uploaded_at: parent_post.uploaded_at,
|
||||||
|
media: parent_post.media,
|
||||||
tags: Some(
|
tags: Some(
|
||||||
get_tags_for_post(post_id)
|
get_tags_for_post(post_id)
|
||||||
.all(&db)
|
.all(&db)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<video
|
<video
|
||||||
id="media"
|
id="media"
|
||||||
src="/files/{{ post.media }}"
|
src="/files/{{ post.media }}"
|
||||||
controls="controls"
|
controls="true"
|
||||||
:style="{ width: '100%', height: '100%', 'max-width': width + 'px', 'max-height': height + 'px', 'aspect-ratio': width + ' / ' + height }"
|
style="width: 100%; height: 100%"
|
||||||
|
:style="{ 'max-width': width + 'px', 'max-height': height + 'px', 'aspect-ratio': width + ' / ' + height }"
|
||||||
></video>
|
></video>
|
||||||
|
|
|
||||||
11
templates/fragments/rss_entry.html
Normal file
11
templates/fragments/rss_entry.html
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<h1>
|
||||||
|
{% if let Some(title) = post.title %}{{ title }}{% else %}Details{% endif %}
|
||||||
|
</h1>
|
||||||
|
{% match post.media_type.as_ref() %}{% when "image" %}
|
||||||
|
<img src="{{ base_url }}/files/{{ post.media }}" />
|
||||||
|
{% when "video" %}
|
||||||
|
<video src="{{ base_url }}/files/{{ post.media }}" controls="true"></video>
|
||||||
|
{% else %}{% endmatch %}{% if let Some(description) = post.description %}
|
||||||
|
<h2>Description</h2>
|
||||||
|
<div>{{ description | markdown }}</div>
|
||||||
|
{% endif %}
|
||||||
|
|
@ -18,10 +18,17 @@ parent_post %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<article id="parent-post" hx-swap-oob="outerHTML" hidden></article>
|
<article id="parent-post" hx-swap-oob="outerHTML" hidden></article>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<ul id="tags-list" hx-swap-oob="outerHTML">
|
<article id="tags-list" hx-swap-oob="outerHTML">
|
||||||
{% for tag in tags %}
|
<h2>Tags</h2>
|
||||||
<li>
|
{% if tags.is_empty() %}
|
||||||
<a href="/posts?tags={{ tag.name }}">{{ tag.name }}</a>
|
<p>No tags in post. Consider adding some!</p>
|
||||||
</li>
|
{% else %}
|
||||||
{% endfor %}
|
<ul>
|
||||||
</ul>
|
{% for tag in tags %}
|
||||||
|
<li>
|
||||||
|
<a href="/posts?tags={{ tag.name }}">{{ tag.name }}</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</article>
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
{% if age_confirmation %}{% include "fragments/age_restricted_check.html"
|
{% if age_confirmation %}{% include "fragments/age_restricted_check.html"
|
||||||
%}{% endif %}
|
%}{% endif %}
|
||||||
<div><a href="/">< To home</a></div>
|
<div><a href="/">< To home</a></div>
|
||||||
|
<div><a href="{% if let Some(tags_text) = tags_text %}/posts.xml?tags={{ tags_text.replace(' ', "+") }}{% else %}/posts.xml{% endif %}">RSS feed</a></div>
|
||||||
<article>
|
<article>
|
||||||
<h2>Search</h2>
|
<h2>Search</h2>
|
||||||
<form method="get" action="/posts">
|
<form method="get" action="/posts">
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,10 @@
|
||||||
value="{{ application_name }}"
|
value="{{ application_name }}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Base URL</label>
|
||||||
|
<input name="base_url" type="text" value="{{ base_url }}" />
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label>Ask for age confirmation?</label>
|
<label>Ask for age confirmation?</label>
|
||||||
<input
|
<input
|
||||||
|
|
|
||||||
|
|
@ -92,12 +92,12 @@
|
||||||
</ul>
|
</ul>
|
||||||
</article>
|
</article>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<article>
|
<article id="tags-list">
|
||||||
<h2>Tags</h2>
|
<h2>Tags</h2>
|
||||||
{% if tags.is_empty() %}
|
{% if tags.is_empty() %}
|
||||||
<p>No tags in post. Consider adding some!</p>
|
<p>No tags in post. Consider adding some!</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<ul id="tags-list">
|
<ul>
|
||||||
{% for tag in tags %}
|
{% for tag in tags %}
|
||||||
<li>
|
<li>
|
||||||
<a href="/posts?tags={{ tag.name }}">{{ tag.name }}</a>
|
<a href="/posts?tags={{ tag.name }}">{{ tag.name }}</a>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue