4 use crate::db::comment_view::{ReplyQueryBuilder, ReplyView};
5 use crate::db::community::Community;
6 use crate::db::post_view::{PostQueryBuilder, PostView};
7 use crate::db::site_view::SiteView;
8 use crate::db::user::{Claims, User_};
9 use crate::db::user_mention_view::{UserMentionQueryBuilder, UserMentionView};
10 use crate::db::{ListingType, SortType};
12 use actix_web::{web, HttpResponse, Result};
13 use chrono::{DateTime, Utc};
14 use diesel::r2d2::{ConnectionManager, Pool};
15 use diesel::PgConnection;
17 use rss::{CategoryBuilder, ChannelBuilder, GuidBuilder, Item, ItemBuilder};
18 use serde::Deserialize;
19 use std::str::FromStr;
20 use strum::ParseError;
22 #[derive(Deserialize)]
34 pub fn config(cfg: &mut web::ServiceConfig) {
36 .route("/feeds/{type}/{name}.xml", web::get().to(feeds::get_feed))
37 .route("/feeds/all.xml", web::get().to(feeds::get_all_feed))
38 .route("/feeds/all.xml", web::get().to(feeds::get_all_feed));
41 async fn get_all_feed(
42 info: web::Query<Params>,
43 db: web::Data<Pool<ConnectionManager<PgConnection>>>,
44 ) -> Result<HttpResponse, actix_web::Error> {
45 let res = web::block(move || {
48 let sort_type = get_sort_type(info)?;
49 get_feed_all_data(&conn, &sort_type)
54 .content_type("application/rss+xml")
57 .map_err(|_| HttpResponse::InternalServerError())?;
62 path: web::Path<(String, String)>,
63 info: web::Query<Params>,
64 db: web::Data<Pool<ConnectionManager<PgConnection>>>,
65 ) -> Result<HttpResponse, actix_web::Error> {
66 let res = web::block(move || {
69 let sort_type = get_sort_type(info)?;
71 let request_type = match path.0.as_ref() {
72 "u" => RequestType::User,
73 "c" => RequestType::Community,
74 "front" => RequestType::Front,
75 "inbox" => RequestType::Inbox,
76 _ => return Err(format_err!("wrong_type")),
79 let param = path.1.to_owned();
82 RequestType::User => get_feed_user(&conn, &sort_type, param),
83 RequestType::Community => get_feed_community(&conn, &sort_type, param),
84 RequestType::Front => get_feed_front(&conn, &sort_type, param),
85 RequestType::Inbox => get_feed_inbox(&conn, param),
91 .content_type("application/rss+xml")
94 .map_err(|_| HttpResponse::InternalServerError())?;
98 fn get_sort_type(info: web::Query<Params>) -> Result<SortType, ParseError> {
102 .unwrap_or_else(|| SortType::Hot.to_string());
103 SortType::from_str(&sort_query)
106 fn get_feed_all_data(conn: &PgConnection, sort_type: &SortType) -> Result<String, failure::Error> {
107 let site_view = SiteView::read(&conn)?;
109 let posts = PostQueryBuilder::create(&conn)
110 .listing_type(ListingType::All)
114 let items = create_post_items(posts);
116 let mut channel_builder = ChannelBuilder::default();
118 .title(&format!("{} - All", site_view.name))
119 .link(format!("https://{}", Settings::get().hostname))
122 if let Some(site_desc) = site_view.description {
123 channel_builder.description(&site_desc);
126 Ok(channel_builder.build().unwrap().to_string())
131 sort_type: &SortType,
133 ) -> Result<String, Error> {
134 let site_view = SiteView::read(&conn)?;
135 let user = User_::find_by_username(&conn, &user_name)?;
136 let user_url = user.get_profile_url();
138 let posts = PostQueryBuilder::create(&conn)
139 .listing_type(ListingType::All)
141 .for_creator_id(user.id)
144 let items = create_post_items(posts);
146 let mut channel_builder = ChannelBuilder::default();
148 .title(&format!("{} - {}", site_view.name, user.name))
152 Ok(channel_builder.build().unwrap().to_string())
155 fn get_feed_community(
157 sort_type: &SortType,
158 community_name: String,
159 ) -> Result<String, Error> {
160 let site_view = SiteView::read(&conn)?;
161 let community = Community::read_from_name(&conn, community_name)?;
162 let community_url = community.get_url();
164 let posts = PostQueryBuilder::create(&conn)
165 .listing_type(ListingType::All)
167 .for_community_id(community.id)
170 let items = create_post_items(posts);
172 let mut channel_builder = ChannelBuilder::default();
174 .title(&format!("{} - {}", site_view.name, community.name))
178 if let Some(community_desc) = community.description {
179 channel_builder.description(&community_desc);
182 Ok(channel_builder.build().unwrap().to_string())
185 fn get_feed_front(conn: &PgConnection, sort_type: &SortType, jwt: String) -> Result<String, Error> {
186 let site_view = SiteView::read(&conn)?;
187 let user_id = Claims::decode(&jwt)?.claims.id;
189 let posts = PostQueryBuilder::create(&conn)
190 .listing_type(ListingType::Subscribed)
195 let items = create_post_items(posts);
197 let mut channel_builder = ChannelBuilder::default();
199 .title(&format!("{} - Subscribed", site_view.name))
200 .link(format!("https://{}", Settings::get().hostname))
203 if let Some(site_desc) = site_view.description {
204 channel_builder.description(&site_desc);
207 Ok(channel_builder.build().unwrap().to_string())
210 fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<String, Error> {
211 let site_view = SiteView::read(&conn)?;
212 let user_id = Claims::decode(&jwt)?.claims.id;
214 let sort = SortType::New;
216 let replies = ReplyQueryBuilder::create(&conn, user_id)
220 let mentions = UserMentionQueryBuilder::create(&conn, user_id)
224 let items = create_reply_and_mention_items(replies, mentions);
226 let mut channel_builder = ChannelBuilder::default();
228 .title(&format!("{} - Inbox", site_view.name))
229 .link(format!("https://{}/inbox", Settings::get().hostname))
232 if let Some(site_desc) = site_view.description {
233 channel_builder.description(&site_desc);
236 Ok(channel_builder.build().unwrap().to_string())
239 fn create_reply_and_mention_items(
240 replies: Vec<ReplyView>,
241 mentions: Vec<UserMentionView>,
243 let mut items: Vec<Item> = Vec::new();
246 let mut i = ItemBuilder::default();
248 i.title(format!("Reply from {}", r.creator_name));
250 let author_url = format!("https://{}/u/{}", Settings::get().hostname, r.creator_name);
252 "/u/{} <a href=\"{}\">(link)</a>",
253 r.creator_name, author_url
256 let dt = DateTime::<Utc>::from_utc(r.published, Utc);
257 i.pub_date(dt.to_rfc2822());
259 let reply_url = format!(
260 "https://{}/post/{}/comment/{}",
261 Settings::get().hostname,
265 i.comments(reply_url.to_owned());
266 let guid = GuidBuilder::default()
270 i.guid(guid.unwrap());
274 // TODO find a markdown to html parser here, do images, etc
275 i.description(r.content);
277 items.push(i.build().unwrap());
281 let mut i = ItemBuilder::default();
283 i.title(format!("Mention from {}", m.creator_name));
285 let author_url = format!("https://{}/u/{}", Settings::get().hostname, m.creator_name);
287 "/u/{} <a href=\"{}\">(link)</a>",
288 m.creator_name, author_url
291 let dt = DateTime::<Utc>::from_utc(m.published, Utc);
292 i.pub_date(dt.to_rfc2822());
294 let mention_url = format!(
295 "https://{}/post/{}/comment/{}",
296 Settings::get().hostname,
300 i.comments(mention_url.to_owned());
301 let guid = GuidBuilder::default()
305 i.guid(guid.unwrap());
309 // TODO find a markdown to html parser here, do images, etc
310 i.description(m.content);
312 items.push(i.build().unwrap());
318 fn create_post_items(posts: Vec<PostView>) -> Vec<Item> {
319 let mut items: Vec<Item> = Vec::new();
322 let mut i = ItemBuilder::default();
326 let author_url = format!("https://{}/u/{}", Settings::get().hostname, p.creator_name);
328 "/u/{} <a href=\"{}\">(link)</a>",
329 p.creator_name, author_url
332 let dt = DateTime::<Utc>::from_utc(p.published, Utc);
333 i.pub_date(dt.to_rfc2822());
335 let post_url = format!("https://{}/post/{}", Settings::get().hostname, p.id);
336 i.comments(post_url.to_owned());
337 let guid = GuidBuilder::default()
341 i.guid(guid.unwrap());
343 let community_url = format!(
345 Settings::get().hostname,
349 let category = CategoryBuilder::default()
351 "/c/{} <a href=\"{}\">(link)</a>",
352 p.community_name, community_url
354 .domain(Settings::get().hostname.to_owned())
356 i.categories(vec![category.unwrap()]);
358 if let Some(url) = p.url {
362 // TODO find a markdown to html parser here, do images, etc
363 let mut description = format!("
364 submitted by <a href=\"{}\">{}</a> to <a href=\"{}\">{}</a><br>{} points | <a href=\"{}\">{} comments</a>",
371 p.number_of_comments);
373 if let Some(body) = p.body {
374 description.push_str(&format!("<br><br>{}", body));
377 i.description(description);
379 items.push(i.build().unwrap());