add: image proxy
add: mentions in posts TODO: audit log, reports, user mod panel
This commit is contained in:
parent
e183a01887
commit
3a8af17154
14 changed files with 308 additions and 33 deletions
|
@ -293,7 +293,7 @@ pub async fn update_membership_role(
|
|||
match data.update_membership_role(membership.id, req.role).await {
|
||||
Ok(_) => {
|
||||
// check if the user was just banned/unbanned (and send notifs)
|
||||
if (req.role & CommunityPermission::BANNED) == CommunityPermission::BANNED {
|
||||
if req.role.check_banned() {
|
||||
// user was banned
|
||||
if let Err(e) = data
|
||||
.create_notification(Notification::new(
|
||||
|
@ -313,8 +313,7 @@ pub async fn update_membership_role(
|
|||
// banned members do not count towards member count
|
||||
return Json(e.into());
|
||||
}
|
||||
} else if (membership.role & CommunityPermission::BANNED) == CommunityPermission::BANNED
|
||||
{
|
||||
} else if membership.role.check_banned() {
|
||||
// user was unbanned
|
||||
if let Err(e) = data
|
||||
.create_notification(Notification::new(
|
||||
|
|
|
@ -2,6 +2,7 @@ pub mod auth;
|
|||
pub mod communities;
|
||||
pub mod notifications;
|
||||
pub mod reactions;
|
||||
pub mod util;
|
||||
|
||||
use axum::{
|
||||
Router,
|
||||
|
@ -16,6 +17,9 @@ use tetratto_core::model::{
|
|||
|
||||
pub fn routes() -> Router {
|
||||
Router::new()
|
||||
// misc
|
||||
.route("/util/proxy", get(util::proxy_request))
|
||||
.route("/util/lang", get(util::set_langfile_request))
|
||||
// reactions
|
||||
.route("/reactions", post(reactions::create_request))
|
||||
.route("/reactions/{id}", get(reactions::get_request))
|
||||
|
|
|
@ -40,7 +40,7 @@ pub async fn delete_all_request(
|
|||
match data.delete_all_notifications(&user).await {
|
||||
Ok(_) => Json(ApiReturn {
|
||||
ok: true,
|
||||
message: "Notifications deleted".to_string(),
|
||||
message: "Notifications cleared".to_string(),
|
||||
payload: (),
|
||||
}),
|
||||
Err(e) => Json(e.into()),
|
||||
|
|
133
crates/app/src/routes/api/v1/util.rs
Normal file
133
crates/app/src/routes/api/v1/util.rs
Normal file
|
@ -0,0 +1,133 @@
|
|||
use super::auth::images::read_image;
|
||||
use crate::State;
|
||||
use axum::{Extension, body::Body, extract::Query, http::HeaderMap, response::IntoResponse};
|
||||
use pathbufd::PathBufD;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct ProxyQuery {
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
/// Proxy an external url
|
||||
pub async fn proxy_request(
|
||||
Query(props): Query<ProxyQuery>,
|
||||
Extension(data): Extension<State>,
|
||||
) -> impl IntoResponse {
|
||||
let data = &(data.read().await);
|
||||
let http = &data.2;
|
||||
let data = &data.0;
|
||||
|
||||
let image_url = &props.url;
|
||||
|
||||
for host in &data.0.banned_hosts {
|
||||
if image_url.starts_with(host) {
|
||||
return (
|
||||
[("Content-Type", "image/svg+xml")],
|
||||
Body::from(read_image(PathBufD::current().extend(&[
|
||||
data.0.dirs.media.as_str(),
|
||||
"images",
|
||||
"default-banner.svg",
|
||||
]))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// get proxied image
|
||||
if image_url.is_empty() {
|
||||
return (
|
||||
[("Content-Type", "image/svg+xml")],
|
||||
Body::from(read_image(PathBufD::current().extend(&[
|
||||
data.0.dirs.media.as_str(),
|
||||
"images",
|
||||
"default-banner.svg",
|
||||
]))),
|
||||
);
|
||||
}
|
||||
|
||||
let guessed_mime = mime_guess::from_path(image_url)
|
||||
.first_raw()
|
||||
.unwrap_or("application/octet-stream");
|
||||
|
||||
match http.get(image_url).send().await {
|
||||
Ok(stream) => {
|
||||
let size = stream.content_length();
|
||||
if size.unwrap_or_default() > 10485760 {
|
||||
// return defualt image (content too big)
|
||||
return (
|
||||
[("Content-Type", "image/svg+xml")],
|
||||
Body::from(read_image(PathBufD::current().extend(&[
|
||||
data.0.dirs.media.as_str(),
|
||||
"images",
|
||||
"default-banner.svg",
|
||||
]))),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(ct) = stream.headers().get("Content-Type") {
|
||||
let ct = ct.to_str().unwrap();
|
||||
let bad_ct = vec!["text/html", "text/plain"];
|
||||
if (!ct.starts_with("image/") && !ct.starts_with("font/")) | bad_ct.contains(&ct) {
|
||||
// if we got html, return default banner (likely an error page)
|
||||
return (
|
||||
[("Content-Type", "image/svg+xml")],
|
||||
Body::from(read_image(PathBufD::current().extend(&[
|
||||
data.0.dirs.media.as_str(),
|
||||
"images",
|
||||
"default-banner.svg",
|
||||
]))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
[(
|
||||
"Content-Type",
|
||||
if guessed_mime == "text/html" {
|
||||
"text/plain"
|
||||
} else {
|
||||
guessed_mime
|
||||
},
|
||||
)],
|
||||
Body::from_stream(stream.bytes_stream()),
|
||||
)
|
||||
}
|
||||
Err(_) => (
|
||||
[("Content-Type", "image/svg+xml")],
|
||||
Body::from(read_image(PathBufD::current().extend(&[
|
||||
data.0.dirs.media.as_str(),
|
||||
"images",
|
||||
"default-banner.svg",
|
||||
]))),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LangFileQuery {
|
||||
#[serde(default)]
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
/// Set the current language
|
||||
pub async fn set_langfile_request(Query(props): Query<LangFileQuery>) -> impl IntoResponse {
|
||||
(
|
||||
{
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
headers.insert(
|
||||
"Set-Cookie",
|
||||
format!(
|
||||
"__Secure-atto-lang={}; SameSite=Lax; Secure; Path=/; HostOnly=true; HttpOnly=true; Max-Age={}",
|
||||
props.id,
|
||||
60* 60 * 24 * 365
|
||||
)
|
||||
.parse()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
headers
|
||||
},
|
||||
"Language changed",
|
||||
)
|
||||
}
|
|
@ -31,6 +31,20 @@ macro_rules! check_permissions {
|
|||
}
|
||||
_ => (),
|
||||
};
|
||||
|
||||
if let Some(ref ua) = $user {
|
||||
if let Ok(membership) = $data
|
||||
.0
|
||||
.get_membership_by_owner_community(ua.id, $community.id)
|
||||
.await
|
||||
{
|
||||
if membership.role.check_banned() {
|
||||
return Err(Html(
|
||||
render_error(Error::NotAllowed, &$jar, &$data, &$user).await,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ pub fn routes() -> Router {
|
|||
pub async fn render_error(
|
||||
e: Error,
|
||||
jar: &CookieJar,
|
||||
data: &(DataManager, tera::Tera),
|
||||
data: &(DataManager, tera::Tera, reqwest::Client),
|
||||
user: &Option<User>,
|
||||
) -> String {
|
||||
let lang = get_lang!(jar, data.0);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue