2019-03-20 17:56:17 +01:00
|
|
|
use rocket::{
|
|
|
|
request::LenientForm,
|
2019-04-30 12:04:25 +02:00
|
|
|
response::{status, Flash, Redirect},
|
2019-03-20 17:56:17 +01:00
|
|
|
};
|
2018-12-06 18:54:16 +01:00
|
|
|
use rocket_contrib::json::Json;
|
|
|
|
use rocket_i18n::I18n;
|
2018-05-12 14:56:38 +02:00
|
|
|
use serde_json;
|
2018-12-06 18:54:16 +01:00
|
|
|
use validator::{Validate, ValidationErrors};
|
2018-04-22 15:35:37 +02:00
|
|
|
|
2019-04-17 19:31:47 +02:00
|
|
|
use inbox;
|
2019-04-28 19:01:41 +02:00
|
|
|
use plume_common::activity_pub::{broadcast, inbox::FromId};
|
2018-06-23 18:36:11 +02:00
|
|
|
use plume_models::{
|
2019-03-20 17:56:17 +01:00
|
|
|
admin::Admin, comments::Comment, db_conn::DbConn, headers::Headers, instance::*, posts::Post,
|
2019-04-17 19:31:47 +02:00
|
|
|
safe_string::SafeString, users::User, Error, PlumeRocket, CONFIG,
|
2018-05-19 09:39:59 +02:00
|
|
|
};
|
2019-06-14 09:33:30 +02:00
|
|
|
use routes::{errors::ErrorPage, rocket_uri_macro_static_files, Page, RespondOrRedirect};
|
2019-04-30 12:04:25 +02:00
|
|
|
use template_utils::{IntoContext, Ructe};
|
2018-04-22 15:35:37 +02:00
|
|
|
|
2018-09-05 19:03:02 +02:00
|
|
|
#[get("/")]
|
2019-04-30 12:04:25 +02:00
|
|
|
pub fn index(rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
|
|
|
let conn = &*rockets.conn;
|
2019-05-10 22:59:34 +02:00
|
|
|
let inst = Instance::get_local()?;
|
2019-04-30 12:04:25 +02:00
|
|
|
let federated = Post::get_recents_page(conn, Page::default().limits())?;
|
|
|
|
let local = Post::get_instance_page(conn, inst.id, Page::default().limits())?;
|
|
|
|
let user_feed = rockets.user.clone().and_then(|user| {
|
|
|
|
let followed = user.get_followed(conn).ok()?;
|
2018-12-29 09:36:07 +01:00
|
|
|
let mut in_feed = followed.into_iter().map(|u| u.id).collect::<Vec<i32>>();
|
|
|
|
in_feed.push(user.id);
|
2019-04-30 12:04:25 +02:00
|
|
|
Post::user_feed_page(conn, in_feed, Page::default().limits()).ok()
|
2018-12-29 09:36:07 +01:00
|
|
|
});
|
2018-05-12 14:56:38 +02:00
|
|
|
|
2018-12-29 09:36:07 +01:00
|
|
|
Ok(render!(instance::index(
|
2019-04-30 12:04:25 +02:00
|
|
|
&rockets.to_context(),
|
2018-12-29 09:36:07 +01:00
|
|
|
inst,
|
2019-04-30 12:04:25 +02:00
|
|
|
User::count_local(conn)?,
|
|
|
|
Post::count_local(conn)?,
|
2018-12-29 09:36:07 +01:00
|
|
|
local,
|
|
|
|
federated,
|
|
|
|
user_feed
|
|
|
|
)))
|
2018-04-29 19:50:46 +02:00
|
|
|
}
|
|
|
|
|
2018-09-04 21:56:27 +02:00
|
|
|
#[get("/local?<page>")]
|
2019-04-30 12:04:25 +02:00
|
|
|
pub fn local(page: Option<Page>, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
2018-12-13 22:20:19 +01:00
|
|
|
let page = page.unwrap_or_default();
|
2019-05-10 22:59:34 +02:00
|
|
|
let instance = Instance::get_local()?;
|
2019-04-30 12:04:25 +02:00
|
|
|
let articles = Post::get_instance_page(&*rockets.conn, instance.id, page.limits())?;
|
2018-12-29 09:36:07 +01:00
|
|
|
Ok(render!(instance::local(
|
2019-04-30 12:04:25 +02:00
|
|
|
&rockets.to_context(),
|
2018-12-06 18:54:16 +01:00
|
|
|
instance,
|
|
|
|
articles,
|
|
|
|
page.0,
|
2019-04-30 12:04:25 +02:00
|
|
|
Page::total(Post::count_local(&*rockets.conn)? as i32)
|
2018-12-29 09:36:07 +01:00
|
|
|
)))
|
2018-09-04 21:56:27 +02:00
|
|
|
}
|
|
|
|
|
2018-09-05 16:21:50 +02:00
|
|
|
#[get("/feed?<page>")]
|
2019-04-30 12:04:25 +02:00
|
|
|
pub fn feed(user: User, page: Option<Page>, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
2018-12-13 22:20:19 +01:00
|
|
|
let page = page.unwrap_or_default();
|
2019-04-30 12:04:25 +02:00
|
|
|
let followed = user.get_followed(&*rockets.conn)?;
|
2018-10-06 14:41:23 +02:00
|
|
|
let mut in_feed = followed.into_iter().map(|u| u.id).collect::<Vec<i32>>();
|
|
|
|
in_feed.push(user.id);
|
2019-04-30 12:04:25 +02:00
|
|
|
let articles = Post::user_feed_page(&*rockets.conn, in_feed, page.limits())?;
|
2018-12-29 09:36:07 +01:00
|
|
|
Ok(render!(instance::feed(
|
2019-04-30 12:04:25 +02:00
|
|
|
&rockets.to_context(),
|
2018-12-06 18:54:16 +01:00
|
|
|
articles,
|
|
|
|
page.0,
|
2019-04-30 12:04:25 +02:00
|
|
|
Page::total(Post::count_local(&*rockets.conn)? as i32)
|
2018-12-29 09:36:07 +01:00
|
|
|
)))
|
2018-09-05 16:37:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/federated?<page>")]
|
2019-04-30 12:04:25 +02:00
|
|
|
pub fn federated(page: Option<Page>, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
2018-12-13 22:20:19 +01:00
|
|
|
let page = page.unwrap_or_default();
|
2019-04-30 12:04:25 +02:00
|
|
|
let articles = Post::get_recents_page(&*rockets.conn, page.limits())?;
|
2018-12-29 09:36:07 +01:00
|
|
|
Ok(render!(instance::federated(
|
2019-04-30 12:04:25 +02:00
|
|
|
&rockets.to_context(),
|
2018-12-06 18:54:16 +01:00
|
|
|
articles,
|
|
|
|
page.0,
|
2019-04-30 12:04:25 +02:00
|
|
|
Page::total(Post::count_local(&*rockets.conn)? as i32)
|
2018-12-29 09:36:07 +01:00
|
|
|
)))
|
2018-09-05 16:21:50 +02:00
|
|
|
}
|
|
|
|
|
2018-07-27 19:05:36 +02:00
|
|
|
#[get("/admin")]
|
2019-04-30 12:04:25 +02:00
|
|
|
pub fn admin(_admin: Admin, rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
2019-05-10 22:59:34 +02:00
|
|
|
let local_inst = Instance::get_local()?;
|
2018-12-29 09:36:07 +01:00
|
|
|
Ok(render!(instance::admin(
|
2019-04-30 12:04:25 +02:00
|
|
|
&rockets.to_context(),
|
2018-12-06 18:54:16 +01:00
|
|
|
local_inst.clone(),
|
|
|
|
InstanceSettingsForm {
|
|
|
|
name: local_inst.name.clone(),
|
|
|
|
open_registrations: local_inst.open_registrations,
|
|
|
|
short_description: local_inst.short_description,
|
|
|
|
long_description: local_inst.long_description,
|
|
|
|
default_license: local_inst.default_license,
|
|
|
|
},
|
|
|
|
ValidationErrors::default()
|
2018-12-29 09:36:07 +01:00
|
|
|
)))
|
2018-07-27 19:05:36 +02:00
|
|
|
}
|
|
|
|
|
2019-03-12 19:40:54 +01:00
|
|
|
#[derive(Clone, FromForm, Validate)]
|
2018-12-06 18:54:16 +01:00
|
|
|
pub struct InstanceSettingsForm {
|
2018-07-27 19:05:36 +02:00
|
|
|
#[validate(length(min = "1"))]
|
2018-12-06 18:54:16 +01:00
|
|
|
pub name: String,
|
|
|
|
pub open_registrations: bool,
|
|
|
|
pub short_description: SafeString,
|
|
|
|
pub long_description: SafeString,
|
2018-07-27 19:05:36 +02:00
|
|
|
#[validate(length(min = "1"))]
|
2019-03-20 17:56:17 +01:00
|
|
|
pub default_license: String,
|
2018-07-27 19:05:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[post("/admin", data = "<form>")]
|
2019-03-20 17:56:17 +01:00
|
|
|
pub fn update_settings(
|
2019-04-30 12:04:25 +02:00
|
|
|
_admin: Admin,
|
2019-03-20 17:56:17 +01:00
|
|
|
form: LenientForm<InstanceSettingsForm>,
|
2019-04-30 12:04:25 +02:00
|
|
|
rockets: PlumeRocket,
|
2019-06-14 09:33:30 +02:00
|
|
|
) -> RespondOrRedirect {
|
2019-04-30 12:04:25 +02:00
|
|
|
let conn = &*rockets.conn;
|
2019-06-14 09:33:30 +02:00
|
|
|
if let Err(e) = form.validate() {
|
|
|
|
let local_inst =
|
|
|
|
Instance::get_local().expect("instance::update_settings: local instance error");
|
|
|
|
render!(instance::admin(
|
|
|
|
&rockets.to_context(),
|
|
|
|
local_inst,
|
|
|
|
form.clone(),
|
|
|
|
e
|
|
|
|
))
|
|
|
|
.into()
|
|
|
|
} else {
|
|
|
|
let instance =
|
|
|
|
Instance::get_local().expect("instance::update_settings: local instance error");
|
|
|
|
instance
|
|
|
|
.update(
|
|
|
|
conn,
|
|
|
|
form.name.clone(),
|
|
|
|
form.open_registrations,
|
|
|
|
form.short_description.clone(),
|
|
|
|
form.long_description.clone(),
|
|
|
|
)
|
|
|
|
.expect("instance::update_settings: save error");
|
|
|
|
Flash::success(
|
|
|
|
Redirect::to(uri!(admin)),
|
|
|
|
i18n!(rockets.intl.catalog, "Instance settings have been saved."),
|
|
|
|
)
|
|
|
|
.into()
|
|
|
|
}
|
2018-07-27 19:05:36 +02:00
|
|
|
}
|
|
|
|
|
2018-09-08 20:54:09 +02:00
|
|
|
#[get("/admin/instances?<page>")]
|
2019-03-20 17:56:17 +01:00
|
|
|
pub fn admin_instances(
|
2019-04-30 12:04:25 +02:00
|
|
|
_admin: Admin,
|
2019-03-20 17:56:17 +01:00
|
|
|
page: Option<Page>,
|
2019-04-30 12:04:25 +02:00
|
|
|
rockets: PlumeRocket,
|
2019-03-20 17:56:17 +01:00
|
|
|
) -> Result<Ructe, ErrorPage> {
|
2018-12-13 22:20:19 +01:00
|
|
|
let page = page.unwrap_or_default();
|
2019-04-30 12:04:25 +02:00
|
|
|
let instances = Instance::page(&*rockets.conn, page.limits())?;
|
2018-12-29 09:36:07 +01:00
|
|
|
Ok(render!(instance::list(
|
2019-04-30 12:04:25 +02:00
|
|
|
&rockets.to_context(),
|
2019-05-10 22:59:34 +02:00
|
|
|
Instance::get_local()?,
|
2018-12-06 18:54:16 +01:00
|
|
|
instances,
|
|
|
|
page.0,
|
2019-04-30 12:04:25 +02:00
|
|
|
Page::total(Instance::count(&*rockets.conn)? as i32)
|
2018-12-29 09:36:07 +01:00
|
|
|
)))
|
2018-09-08 20:54:09 +02:00
|
|
|
}
|
|
|
|
|
2018-09-19 19:13:07 +02:00
|
|
|
#[post("/admin/instances/<id>/block")]
|
2019-04-30 12:04:25 +02:00
|
|
|
pub fn toggle_block(
|
|
|
|
_admin: Admin,
|
|
|
|
conn: DbConn,
|
|
|
|
id: i32,
|
|
|
|
intl: I18n,
|
|
|
|
) -> Result<Flash<Redirect>, ErrorPage> {
|
|
|
|
let inst = Instance::get(&*conn, id)?;
|
|
|
|
let message = if inst.blocked {
|
2019-05-29 13:26:37 +02:00
|
|
|
i18n!(intl.catalog, "{} has been unblocked."; &inst.name)
|
2019-04-30 12:04:25 +02:00
|
|
|
} else {
|
2019-05-29 13:26:37 +02:00
|
|
|
i18n!(intl.catalog, "{} has been blocked."; &inst.name)
|
2019-04-30 12:04:25 +02:00
|
|
|
};
|
2018-09-08 21:07:55 +02:00
|
|
|
|
2019-04-30 12:04:25 +02:00
|
|
|
inst.toggle_block(&*conn)?;
|
|
|
|
Ok(Flash::success(
|
|
|
|
Redirect::to(uri!(admin_instances: page = _)),
|
|
|
|
message,
|
|
|
|
))
|
2018-09-09 12:25:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/admin/users?<page>")]
|
2019-03-20 17:56:17 +01:00
|
|
|
pub fn admin_users(
|
2019-04-30 12:04:25 +02:00
|
|
|
_admin: Admin,
|
2019-03-20 17:56:17 +01:00
|
|
|
page: Option<Page>,
|
2019-04-30 12:04:25 +02:00
|
|
|
rockets: PlumeRocket,
|
2019-03-20 17:56:17 +01:00
|
|
|
) -> Result<Ructe, ErrorPage> {
|
2018-12-13 22:20:19 +01:00
|
|
|
let page = page.unwrap_or_default();
|
2018-12-29 09:36:07 +01:00
|
|
|
Ok(render!(instance::users(
|
2019-04-30 12:04:25 +02:00
|
|
|
&rockets.to_context(),
|
|
|
|
User::get_local_page(&*rockets.conn, page.limits())?,
|
2018-12-06 18:54:16 +01:00
|
|
|
page.0,
|
2019-04-30 12:04:25 +02:00
|
|
|
Page::total(User::count_local(&*rockets.conn)? as i32)
|
2018-12-29 09:36:07 +01:00
|
|
|
)))
|
2018-09-09 12:25:55 +02:00
|
|
|
}
|
|
|
|
|
2018-09-19 19:13:07 +02:00
|
|
|
#[post("/admin/users/<id>/ban")]
|
2019-04-30 12:04:25 +02:00
|
|
|
pub fn ban(_admin: Admin, id: i32, rockets: PlumeRocket) -> Result<Flash<Redirect>, ErrorPage> {
|
|
|
|
let u = User::get(&*rockets.conn, id)?;
|
|
|
|
u.delete(&*rockets.conn, &rockets.searcher)?;
|
2019-04-28 19:01:41 +02:00
|
|
|
|
2019-05-10 22:59:34 +02:00
|
|
|
if Instance::get_local()
|
2019-04-30 12:04:25 +02:00
|
|
|
.map(|i| u.instance_id == i.id)
|
|
|
|
.unwrap_or(false)
|
|
|
|
{
|
|
|
|
let target = User::one_by_instance(&*rockets.conn)?;
|
|
|
|
let delete_act = u.delete_activity(&*rockets.conn)?;
|
|
|
|
let u_clone = u.clone();
|
|
|
|
rockets
|
|
|
|
.worker
|
|
|
|
.execute(move || broadcast(&u_clone, delete_act, target));
|
2018-11-26 10:21:52 +01:00
|
|
|
}
|
2019-04-30 12:04:25 +02:00
|
|
|
|
|
|
|
Ok(Flash::success(
|
|
|
|
Redirect::to(uri!(admin_users: page = _)),
|
2019-05-29 13:26:37 +02:00
|
|
|
i18n!(rockets.intl.catalog, "{} has been banned."; u.name()),
|
2019-04-30 12:04:25 +02:00
|
|
|
))
|
2018-09-09 12:25:55 +02:00
|
|
|
}
|
|
|
|
|
2018-05-13 19:39:18 +02:00
|
|
|
#[post("/inbox", data = "<data>")]
|
2019-03-20 17:56:17 +01:00
|
|
|
pub fn shared_inbox(
|
2019-04-17 19:31:47 +02:00
|
|
|
rockets: PlumeRocket,
|
|
|
|
data: inbox::SignedJson<serde_json::Value>,
|
2019-03-20 17:56:17 +01:00
|
|
|
headers: Headers,
|
|
|
|
) -> Result<String, status::BadRequest<&'static str>> {
|
2019-04-17 19:31:47 +02:00
|
|
|
inbox::handle_incoming(rockets, data, headers)
|
2018-05-13 19:39:18 +02:00
|
|
|
}
|
2018-06-10 21:33:42 +02:00
|
|
|
|
2019-04-17 22:09:07 +02:00
|
|
|
#[get("/remote_interact?<target>")]
|
|
|
|
pub fn interact(rockets: PlumeRocket, user: Option<User>, target: String) -> Option<Redirect> {
|
|
|
|
if User::find_by_fqn(&rockets, &target).is_ok() {
|
|
|
|
return Some(Redirect::to(uri!(super::user::details: name = target)));
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Ok(post) = Post::from_id(&rockets, &target, None) {
|
|
|
|
return Some(Redirect::to(
|
|
|
|
uri!(super::posts::details: blog = post.get_blog(&rockets.conn).expect("Can't retrieve blog").fqn, slug = &post.slug, responding_to = _),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Ok(comment) = Comment::from_id(&rockets, &target, None) {
|
|
|
|
if comment.can_see(&rockets.conn, user.as_ref()) {
|
|
|
|
let post = comment
|
|
|
|
.get_post(&rockets.conn)
|
|
|
|
.expect("Can't retrieve post");
|
|
|
|
return Some(Redirect::to(uri!(
|
|
|
|
super::posts::details: blog = post
|
|
|
|
.get_blog(&rockets.conn)
|
|
|
|
.expect("Can't retrieve blog")
|
|
|
|
.fqn,
|
|
|
|
slug = &post.slug,
|
|
|
|
responding_to = comment.id
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2019-02-17 13:42:59 +01:00
|
|
|
#[get("/nodeinfo/<version>")]
|
|
|
|
pub fn nodeinfo(conn: DbConn, version: String) -> Result<Json<serde_json::Value>, ErrorPage> {
|
2019-03-12 17:00:23 +01:00
|
|
|
if version != "2.0" && version != "2.1" {
|
2019-02-17 13:42:59 +01:00
|
|
|
return Err(ErrorPage::from(Error::NotFound));
|
|
|
|
}
|
|
|
|
|
2019-05-10 22:59:34 +02:00
|
|
|
let local_inst = Instance::get_local()?;
|
2019-02-17 13:42:59 +01:00
|
|
|
let mut doc = json!({
|
|
|
|
"version": version,
|
2018-06-10 21:33:42 +02:00
|
|
|
"software": {
|
2019-02-17 13:42:59 +01:00
|
|
|
"name": env!("CARGO_PKG_NAME"),
|
|
|
|
"version": env!("CARGO_PKG_VERSION"),
|
2018-06-10 21:33:42 +02:00
|
|
|
},
|
|
|
|
"protocols": ["activitypub"],
|
|
|
|
"services": {
|
|
|
|
"inbound": [],
|
|
|
|
"outbound": []
|
|
|
|
},
|
2019-02-17 13:42:59 +01:00
|
|
|
"openRegistrations": local_inst.open_registrations,
|
2018-06-10 21:33:42 +02:00
|
|
|
"usage": {
|
|
|
|
"users": {
|
2018-12-29 09:36:07 +01:00
|
|
|
"total": User::count_local(&*conn)?
|
2018-06-10 21:33:42 +02:00
|
|
|
},
|
2018-12-29 09:36:07 +01:00
|
|
|
"localPosts": Post::count_local(&*conn)?,
|
|
|
|
"localComments": Comment::count_local(&*conn)?
|
2018-06-10 21:33:42 +02:00
|
|
|
},
|
2019-02-17 13:42:59 +01:00
|
|
|
"metadata": {
|
|
|
|
"nodeName": local_inst.name,
|
|
|
|
"nodeDescription": local_inst.short_description
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
if version == "2.1" {
|
|
|
|
doc["software"]["repository"] = json!(env!("CARGO_PKG_REPOSITORY"));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Json(doc))
|
2018-06-10 21:33:42 +02:00
|
|
|
}
|
2018-09-01 18:39:40 +02:00
|
|
|
|
|
|
|
#[get("/about")]
|
2019-04-30 12:04:25 +02:00
|
|
|
pub fn about(rockets: PlumeRocket) -> Result<Ructe, ErrorPage> {
|
|
|
|
let conn = &*rockets.conn;
|
2018-12-29 09:36:07 +01:00
|
|
|
Ok(render!(instance::about(
|
2019-04-30 12:04:25 +02:00
|
|
|
&rockets.to_context(),
|
2019-05-10 22:59:34 +02:00
|
|
|
Instance::get_local()?,
|
|
|
|
Instance::get_local()?.main_admin(conn)?,
|
2019-04-30 12:04:25 +02:00
|
|
|
User::count_local(conn)?,
|
|
|
|
Post::count_local(conn)?,
|
|
|
|
Instance::count(conn)? - 1
|
2018-12-29 09:36:07 +01:00
|
|
|
)))
|
2018-09-01 18:39:40 +02:00
|
|
|
}
|
2018-09-10 16:08:22 +02:00
|
|
|
|
2019-05-18 14:09:51 +02:00
|
|
|
#[get("/privacy")]
|
|
|
|
pub fn privacy(rockets: PlumeRocket) -> Ructe {
|
|
|
|
render!(instance::privacy(&rockets.to_context()))
|
|
|
|
}
|
|
|
|
|
2018-09-10 16:08:22 +02:00
|
|
|
#[get("/manifest.json")]
|
2019-05-10 22:59:34 +02:00
|
|
|
pub fn web_manifest() -> Result<Json<serde_json::Value>, ErrorPage> {
|
|
|
|
let instance = Instance::get_local()?;
|
2018-12-29 09:36:07 +01:00
|
|
|
Ok(Json(json!({
|
2018-09-10 16:08:22 +02:00
|
|
|
"name": &instance.name,
|
|
|
|
"description": &instance.short_description,
|
|
|
|
"start_url": String::from("/"),
|
|
|
|
"scope": String::from("/"),
|
|
|
|
"display": String::from("standalone"),
|
|
|
|
"background_color": String::from("#f4f4f4"),
|
2018-10-09 20:38:01 +02:00
|
|
|
"theme_color": String::from("#7765e3"),
|
2019-01-24 13:16:48 +01:00
|
|
|
"categories": [String::from("social")],
|
2019-03-21 11:51:41 +01:00
|
|
|
"icons": CONFIG.logo.other.iter()
|
|
|
|
.map(|i| i.with_prefix(&uri!(static_files: file = "").to_string()))
|
|
|
|
.collect::<Vec<_>>()
|
2018-12-29 09:36:07 +01:00
|
|
|
})))
|
2018-09-10 16:08:22 +02:00
|
|
|
}
|