add: ability to ip block users from their profile
This commit is contained in:
parent
a799c777ea
commit
0163391380
12 changed files with 241 additions and 20 deletions
|
@ -169,6 +169,7 @@ version = "1.0.0"
|
||||||
"settings:label.export" = "Export"
|
"settings:label.export" = "Export"
|
||||||
"settings:label.manage_blocks" = "Manage blocks"
|
"settings:label.manage_blocks" = "Manage blocks"
|
||||||
"settings:label.users" = "Users"
|
"settings:label.users" = "Users"
|
||||||
|
"settings:label.ips" = "IPs"
|
||||||
"settings:label.generate_invites" = "Generate invites"
|
"settings:label.generate_invites" = "Generate invites"
|
||||||
"settings:label.add_to_stack" = "Add to stack"
|
"settings:label.add_to_stack" = "Add to stack"
|
||||||
"settings:tab.security" = "Security"
|
"settings:tab.security" = "Security"
|
||||||
|
|
|
@ -38,6 +38,10 @@
|
||||||
--pad-2: 0.5rem;
|
--pad-2: 0.5rem;
|
||||||
--pad-3: 0.75rem;
|
--pad-3: 0.75rem;
|
||||||
--pad-4: 1rem;
|
--pad-4: 1rem;
|
||||||
|
|
||||||
|
--online: var(--color-green);
|
||||||
|
--idle: var(--color-yellow);
|
||||||
|
--offline: hsl(0, 0%, 50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark,
|
.dark,
|
||||||
|
|
|
@ -528,7 +528,7 @@
|
||||||
("width" "24")
|
("width" "24")
|
||||||
("height" "24")
|
("height" "24")
|
||||||
("viewBox" "0 0 24 24")
|
("viewBox" "0 0 24 24")
|
||||||
("style" "fill: var(--color-green)")
|
("style" "fill: var(--online)")
|
||||||
(circle
|
(circle
|
||||||
("cx" "12")
|
("cx" "12")
|
||||||
("cy" "12")
|
("cy" "12")
|
||||||
|
@ -541,7 +541,7 @@
|
||||||
("width" "24")
|
("width" "24")
|
||||||
("height" "24")
|
("height" "24")
|
||||||
("viewBox" "0 0 24 24")
|
("viewBox" "0 0 24 24")
|
||||||
("style" "fill: var(--color-yellow)")
|
("style" "fill: var(--idle)")
|
||||||
(circle
|
(circle
|
||||||
("cx" "12")
|
("cx" "12")
|
||||||
("cy" "12")
|
("cy" "12")
|
||||||
|
@ -554,7 +554,7 @@
|
||||||
("width" "24")
|
("width" "24")
|
||||||
("height" "24")
|
("height" "24")
|
||||||
("viewBox" "0 0 24 24")
|
("viewBox" "0 0 24 24")
|
||||||
("style" "fill: hsl(0, 0%, 50%)")
|
("style" "fill: var(--offline)")
|
||||||
(circle
|
(circle
|
||||||
("cx" "12")
|
("cx" "12")
|
||||||
("cy" "12")
|
("cy" "12")
|
||||||
|
@ -611,7 +611,8 @@
|
||||||
(text "{%- endif %}")
|
(text "{%- endif %}")
|
||||||
(div
|
(div
|
||||||
("style" "display: none;")
|
("style" "display: none;")
|
||||||
(text "{{ self::theme_color(color=user.settings.theme_color_surface, css=\"color-surface\") }} {{ self::theme_color(color=user.settings.theme_color_text, css=\"color-text\") }} {{ self::theme_color(color=user.settings.theme_color_text_link, css=\"color-link\") }} {{ self::theme_color(color=user.settings.theme_color_lowered, css=\"color-lowered\") }} {{ self::theme_color(color=user.settings.theme_color_text_lowered, css=\"color-text-lowered\") }} {{ self::theme_color(color=user.settings.theme_color_super_lowered, css=\"color-super-lowered\") }} {{ self::theme_color(color=user.settings.theme_color_raised, css=\"color-raised\") }} {{ self::theme_color(color=user.settings.theme_color_text_raised, css=\"color-text-raised\") }} {{ self::theme_color(color=user.settings.theme_color_super_raised, css=\"color-super-raised\") }} {{ self::theme_color(color=user.settings.theme_color_primary, css=\"color-primary\") }} {{ self::theme_color(color=user.settings.theme_color_text_primary, css=\"color-text-primary\") }} {{ self::theme_color(color=user.settings.theme_color_primary_lowered, css=\"color-primary-lowered\") }} {{ self::theme_color(color=user.settings.theme_color_secondary, css=\"color-secondary\") }} {{ self::theme_color(color=user.settings.theme_color_text_secondary, css=\"color-text-secondary\") }} {{ self::theme_color(color=user.settings.theme_color_secondary_lowered, css=\"color-secondary-lowered\") }} {% if user.permissions|has_supporter -%}")
|
(text "{{ self::theme_color(color=user.settings.theme_color_surface, css=\"color-surface\") }} {{ self::theme_color(color=user.settings.theme_color_text, css=\"color-text\") }} {{ self::theme_color(color=user.settings.theme_color_text_link, css=\"color-link\") }} {{ self::theme_color(color=user.settings.theme_color_lowered, css=\"color-lowered\") }} {{ self::theme_color(color=user.settings.theme_color_text_lowered, css=\"color-text-lowered\") }} {{ self::theme_color(color=user.settings.theme_color_super_lowered, css=\"color-super-lowered\") }} {{ self::theme_color(color=user.settings.theme_color_raised, css=\"color-raised\") }} {{ self::theme_color(color=user.settings.theme_color_text_raised, css=\"color-text-raised\") }} {{ self::theme_color(color=user.settings.theme_color_super_raised, css=\"color-super-raised\") }} {{ self::theme_color(color=user.settings.theme_color_primary, css=\"color-primary\") }} {{ self::theme_color(color=user.settings.theme_color_text_primary, css=\"color-text-primary\") }} {{ self::theme_color(color=user.settings.theme_color_primary_lowered, css=\"color-primary-lowered\") }} {{ self::theme_color(color=user.settings.theme_color_secondary, css=\"color-secondary\") }} {{ self::theme_color(color=user.settings.theme_color_text_secondary, css=\"color-text-secondary\") }} {{ self::theme_color(color=user.settings.theme_color_secondary_lowered, css=\"color-secondary-lowered\") }}
|
||||||
|
{{ self::theme_color(color=user.settings.theme_color_online, css=\"online\") }} {{ self::theme_color(color=user.settings.theme_color_idle, css=\"idle\") }} {{ self::theme_color(color=user.settings.theme_color_offline, css=\"offline\") }} {% if user.permissions|has_supporter -%}")
|
||||||
(style
|
(style
|
||||||
(text "{{ user.settings.theme_custom_css|remove_script_tags|safe }}"))
|
(text "{{ user.settings.theme_custom_css|remove_script_tags|safe }}"))
|
||||||
(text "{%- endif %}"))
|
(text "{%- endif %}"))
|
||||||
|
|
|
@ -219,12 +219,24 @@
|
||||||
(text "{{ icon \"user-minus\" }}")
|
(text "{{ icon \"user-minus\" }}")
|
||||||
(span
|
(span
|
||||||
(text "{{ text \"auth:action.unfollow\" }}")))
|
(text "{{ text \"auth:action.unfollow\" }}")))
|
||||||
(button
|
(div
|
||||||
("onclick" "toggle_block_user()")
|
("class" "dropdown")
|
||||||
("class" "lowered red")
|
(button
|
||||||
(text "{{ icon \"shield\" }}")
|
("onclick" "trigger('atto::hooks::dropdown', [event])")
|
||||||
(span
|
("exclude" "dropdown")
|
||||||
(text "{{ text \"auth:action.block\" }}")))
|
("class" "lowered red")
|
||||||
|
(icon_class (text "chevron-down") (text "dropdown-arrow"))
|
||||||
|
(str (text "auth:action.block")))
|
||||||
|
(div
|
||||||
|
("class" "inner")
|
||||||
|
(button
|
||||||
|
("onclick" "toggle_block_user()")
|
||||||
|
(icon (text "shield"))
|
||||||
|
(str (text "auth:action.block")))
|
||||||
|
(button
|
||||||
|
("onclick" "ip_block_user()")
|
||||||
|
(icon (text "wifi"))
|
||||||
|
(str (text "auth:action.ip_block")))))
|
||||||
(text "{% else %}")
|
(text "{% else %}")
|
||||||
(button
|
(button
|
||||||
("onclick" "toggle_block_user()")
|
("onclick" "toggle_block_user()")
|
||||||
|
@ -342,6 +354,30 @@
|
||||||
res.message,
|
res.message,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
globalThis.ip_block_user = async () => {
|
||||||
|
if (
|
||||||
|
!(await trigger(\"atto::confirm\", [
|
||||||
|
\"Are you sure you would like to do this?\",
|
||||||
|
]))
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(
|
||||||
|
\"/api/v1/auth/user/{{ profile.id }}/block_ip\",
|
||||||
|
{
|
||||||
|
method: \"POST\",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((res) => {
|
||||||
|
trigger(\"atto::toast\", [
|
||||||
|
res.ok ? \"success\" : \"error\",
|
||||||
|
res.message,
|
||||||
|
]);
|
||||||
|
});
|
||||||
};"))))
|
};"))))
|
||||||
(text "{%- endif %} {% if not profile.settings.private_communities or is_self or is_helper %}")
|
(text "{%- endif %} {% if not profile.settings.private_communities or is_self or is_helper %}")
|
||||||
(div
|
(div
|
||||||
|
|
|
@ -446,6 +446,30 @@
|
||||||
("class" "button lowered small")
|
("class" "button lowered small")
|
||||||
(icon (text "external-link"))
|
(icon (text "external-link"))
|
||||||
(span (str (text "requests:action.view_profile"))))))
|
(span (str (text "requests:action.view_profile"))))))
|
||||||
|
(text "{% endfor %}")))
|
||||||
|
|
||||||
|
; ip blocks
|
||||||
|
(div
|
||||||
|
("class" "card-nest")
|
||||||
|
(div
|
||||||
|
("class" "card flex items-center gap-2 small")
|
||||||
|
(text "{{ icon \"wifi\" }}")
|
||||||
|
(span
|
||||||
|
(text "{{ text \"settings:label.ips\" }}")))
|
||||||
|
(div
|
||||||
|
("class" "card flex flex-col gap-2")
|
||||||
|
(text "{% for ip in ipblocks %}")
|
||||||
|
(div
|
||||||
|
("class" "card secondary flex flex-wrap gap-2 items-center justify-between")
|
||||||
|
(span
|
||||||
|
(text "Block from: ") (span ("class" "date") (text "{{ ip.created }}")))
|
||||||
|
(div
|
||||||
|
("class" "flex gap-2")
|
||||||
|
(button
|
||||||
|
("onclick" "trigger('me::remove_ip_block', ['{{ ip.id }}'])")
|
||||||
|
("class" "lowered small red")
|
||||||
|
(icon (text "x"))
|
||||||
|
(span (str (text "auth:action.unblock"))))))
|
||||||
(text "{% endfor %}")))))
|
(text "{% endfor %}")))))
|
||||||
(div
|
(div
|
||||||
("class" "w-full flex flex-col gap-2 hidden")
|
("class" "w-full flex flex-col gap-2 hidden")
|
||||||
|
@ -1734,6 +1758,35 @@
|
||||||
description: \"Hover state for secondary buttons.\",
|
description: \"Hover state for secondary buttons.\",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
// online indicator
|
||||||
|
[[], \"\", \"divider\"],
|
||||||
|
[
|
||||||
|
[\"theme_color_online\", \"Online indicator (online)\"],
|
||||||
|
\"{{ profile.settings.theme_color_online }}\",
|
||||||
|
\"color\",
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
\"The green dot next to the name of online users.\",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[\"theme_color_idle\", \"Online indicator (idle)\"],
|
||||||
|
\"{{ profile.settings.theme_color_idle }}\",
|
||||||
|
\"color\",
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
\"The yellow dot next to the name of online users.\",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
[\"theme_color_offline\", \"Online indicator (offline)\"],
|
||||||
|
\"{{ profile.settings.theme_color_offline }}\",
|
||||||
|
\"color\",
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
\"The grey next to the name of online users.\",
|
||||||
|
},
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
if (can_use_custom_css) {
|
if (can_use_custom_css) {
|
||||||
|
|
|
@ -505,7 +505,7 @@ media_theme_pref();
|
||||||
return now - last_seen <= maximum_time_to_be_considered_idle;
|
return now - last_seen <= maximum_time_to_be_considered_idle;
|
||||||
});
|
});
|
||||||
|
|
||||||
self.define("hooks::online_indicator", ({ $ }) => {
|
self.define("hooks::online_indicator", async ({ $ }) => {
|
||||||
for (const element of Array.from(
|
for (const element of Array.from(
|
||||||
document.querySelectorAll("[hook=online_indicator]") || [],
|
document.querySelectorAll("[hook=online_indicator]") || [],
|
||||||
)) {
|
)) {
|
||||||
|
@ -513,8 +513,8 @@ media_theme_pref();
|
||||||
element.getAttribute("hook-arg:last_seen"),
|
element.getAttribute("hook-arg:last_seen"),
|
||||||
);
|
);
|
||||||
|
|
||||||
const is_online = $.last_seen_just_now(last_seen);
|
const is_online = await $.last_seen_just_now(last_seen);
|
||||||
const is_idle = $.last_seen_recently(last_seen);
|
const is_idle = await $.last_seen_recently(last_seen);
|
||||||
|
|
||||||
const offline = element.querySelector("[hook_ui_ident=offline]");
|
const offline = element.querySelector("[hook_ui_ident=offline]");
|
||||||
const online = element.querySelector("[hook_ui_ident=online]");
|
const online = element.querySelector("[hook_ui_ident=online]");
|
||||||
|
|
|
@ -402,6 +402,27 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.define("remove_ip_block", async (_, id) => {
|
||||||
|
if (
|
||||||
|
!(await trigger("atto::confirm", [
|
||||||
|
"Are you sure you want to do this?",
|
||||||
|
]))
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(`/api/v1/auth/ip/${id}/unblock_ip`, {
|
||||||
|
method: "POST",
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((res) => {
|
||||||
|
trigger("atto::toast", [
|
||||||
|
res.ok ? "success" : "error",
|
||||||
|
res.message,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
self.define("notifications_stream", ({ _, streams }) => {
|
self.define("notifications_stream", ({ _, streams }) => {
|
||||||
const element = document.getElementById("notifications_span");
|
const element = document.getElementById("notifications_span");
|
||||||
|
|
||||||
|
|
|
@ -314,3 +314,64 @@ pub async fn following_request(
|
||||||
Err(e) => Json(e.into()),
|
Err(e) => Json(e.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn ip_block_profile_request(
|
||||||
|
jar: CookieJar,
|
||||||
|
Extension(data): Extension<State>,
|
||||||
|
Path(id): Path<usize>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let data = &(data.read().await).0;
|
||||||
|
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserCreateIpBlock) {
|
||||||
|
Some(ua) => ua,
|
||||||
|
None => return Json(Error::NotAllowed.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// get other user
|
||||||
|
let other_user = match data.get_user_by_id(id).await {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(e) => return Json(e.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (ip, _, _) in other_user.tokens {
|
||||||
|
// check for an existing ip block
|
||||||
|
if data
|
||||||
|
.get_ipblock_by_initiator_receiver(user.id, &ip)
|
||||||
|
.await
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create ip block
|
||||||
|
if let Err(e) = data.create_ipblock(IpBlock::new(user.id, ip)).await {
|
||||||
|
return Json(e.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Json(ApiReturn {
|
||||||
|
ok: true,
|
||||||
|
message: "IP(s) blocked".to_string(),
|
||||||
|
payload: (),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn remove_ip_block_request(
|
||||||
|
jar: CookieJar,
|
||||||
|
Extension(data): Extension<State>,
|
||||||
|
Path(id): Path<usize>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let data = &(data.read().await).0;
|
||||||
|
let user = match get_user_from_token!(jar, data, oauth::AppScope::UserManageBlocks) {
|
||||||
|
Some(ua) => ua,
|
||||||
|
None => return Json(Error::NotAllowed.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
match data.delete_ipblock(id, user).await {
|
||||||
|
Ok(_) => Json(ApiReturn {
|
||||||
|
ok: true,
|
||||||
|
message: "IP unblocked".to_string(),
|
||||||
|
payload: (),
|
||||||
|
}),
|
||||||
|
Err(e) => Json(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -288,6 +288,14 @@ pub fn routes() -> Router {
|
||||||
post(auth::social::accept_follow_request),
|
post(auth::social::accept_follow_request),
|
||||||
)
|
)
|
||||||
.route("/auth/user/{id}/block", post(auth::social::block_request))
|
.route("/auth/user/{id}/block", post(auth::social::block_request))
|
||||||
|
.route(
|
||||||
|
"/auth/user/{id}/block_ip",
|
||||||
|
post(auth::social::ip_block_profile_request),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/auth/ip/{id}/unblock_ip",
|
||||||
|
post(auth::social::remove_ip_block_request),
|
||||||
|
)
|
||||||
.route(
|
.route(
|
||||||
"/auth/user/{id}/settings",
|
"/auth/user/{id}/settings",
|
||||||
post(auth::profile::update_user_settings_request),
|
post(auth::profile::update_user_settings_request),
|
||||||
|
|
|
@ -94,6 +94,11 @@ pub async fn settings_request(
|
||||||
out
|
out
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let ipblocks = match data.0.get_ipblocks_by_initiator(profile.id).await {
|
||||||
|
Ok(l) => l,
|
||||||
|
Err(e) => return Err(Html(render_error(e, &jar, &data, &None).await)),
|
||||||
|
};
|
||||||
|
|
||||||
let uploads = match data.0.get_uploads_by_owner(profile.id, 12, req.page).await {
|
let uploads = match data.0.get_uploads_by_owner(profile.id, 12, req.page).await {
|
||||||
Ok(ua) => ua,
|
Ok(ua) => ua,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -129,6 +134,7 @@ pub async fn settings_request(
|
||||||
context.insert("following", &following);
|
context.insert("following", &following);
|
||||||
context.insert("blocks", &blocks);
|
context.insert("blocks", &blocks);
|
||||||
context.insert("stackblocks", &stackblocks);
|
context.insert("stackblocks", &stackblocks);
|
||||||
|
context.insert("ipblocks", &ipblocks);
|
||||||
context.insert("invites", &invites);
|
context.insert("invites", &invites);
|
||||||
context.insert(
|
context.insert(
|
||||||
"user_tokens_serde",
|
"user_tokens_serde",
|
||||||
|
|
|
@ -2,7 +2,7 @@ use oiseau::cache::Cache;
|
||||||
use crate::model::{Error, Result, auth::User, auth::IpBlock, permissions::FinePermission};
|
use crate::model::{Error, Result, auth::User, auth::IpBlock, permissions::FinePermission};
|
||||||
use crate::{auto_method, DataManager};
|
use crate::{auto_method, DataManager};
|
||||||
|
|
||||||
use oiseau::PostgresRow;
|
use oiseau::{query_rows, PostgresRow};
|
||||||
|
|
||||||
use oiseau::{execute, get, query_row, params};
|
use oiseau::{execute, get, query_row, params};
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ impl DataManager {
|
||||||
|
|
||||||
auto_method!(get_ipblock_by_id()@get_ipblock_from_row -> "SELECT * FROM ipblocks WHERE id = $1" --name="ip block" --returns=IpBlock --cache-key-tmpl="atto.ipblock:{}");
|
auto_method!(get_ipblock_by_id()@get_ipblock_from_row -> "SELECT * FROM ipblocks WHERE id = $1" --name="ip block" --returns=IpBlock --cache-key-tmpl="atto.ipblock:{}");
|
||||||
|
|
||||||
/// Get a user block by `initiator` and `receiver` (in that order).
|
/// Get a ip block by `initiator` and `receiver` (in that order).
|
||||||
pub async fn get_ipblock_by_initiator_receiver(
|
pub async fn get_ipblock_by_initiator_receiver(
|
||||||
&self,
|
&self,
|
||||||
initiator: usize,
|
initiator: usize,
|
||||||
|
@ -38,13 +38,13 @@ impl DataManager {
|
||||||
);
|
);
|
||||||
|
|
||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
return Err(Error::GeneralNotFound("user block".to_string()));
|
return Err(Error::GeneralNotFound("ip block".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(res.unwrap())
|
Ok(res.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a user block by `receiver` and `initiator` (in that order).
|
/// Get a ip block by `receiver` and `initiator` (in that order).
|
||||||
pub async fn get_ipblock_by_receiver_initiator(
|
pub async fn get_ipblock_by_receiver_initiator(
|
||||||
&self,
|
&self,
|
||||||
receiver: &str,
|
receiver: &str,
|
||||||
|
@ -63,13 +63,34 @@ impl DataManager {
|
||||||
);
|
);
|
||||||
|
|
||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
return Err(Error::GeneralNotFound("user block".to_string()));
|
return Err(Error::GeneralNotFound("ip block".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(res.unwrap())
|
Ok(res.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new user block in the database.
|
/// Get all ip blocks by `initiator`.
|
||||||
|
pub async fn get_ipblocks_by_initiator(&self, initiator: usize) -> Result<Vec<IpBlock>> {
|
||||||
|
let conn = match self.0.connect().await {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
|
||||||
|
};
|
||||||
|
|
||||||
|
let res = query_rows!(
|
||||||
|
&conn,
|
||||||
|
"SELECT * FROM ipblocks WHERE initiator = $1",
|
||||||
|
params![&(initiator as i64)],
|
||||||
|
|x| { Self::get_ipblock_from_row(x) }
|
||||||
|
);
|
||||||
|
|
||||||
|
if res.is_err() {
|
||||||
|
return Err(Error::GeneralNotFound("ip block".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(res.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new ip block in the database.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `data` - a mock [`IpBlock`] object to insert
|
/// * `data` - a mock [`IpBlock`] object to insert
|
||||||
|
@ -102,7 +123,7 @@ impl DataManager {
|
||||||
let block = self.get_ipblock_by_id(id).await?;
|
let block = self.get_ipblock_by_id(id).await?;
|
||||||
|
|
||||||
if user.id != block.initiator {
|
if user.id != block.initiator {
|
||||||
// only the initiator (or moderators) can delete user blocks!
|
// only the initiator (or moderators) can delete ip blocks!
|
||||||
if !user.permissions.check(FinePermission::MANAGE_FOLLOWS) {
|
if !user.permissions.check(FinePermission::MANAGE_FOLLOWS) {
|
||||||
return Err(Error::NotAllowed);
|
return Err(Error::NotAllowed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,6 +192,15 @@ pub struct UserSettings {
|
||||||
/// Custom CSS input.
|
/// Custom CSS input.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub theme_custom_css: String,
|
pub theme_custom_css: String,
|
||||||
|
/// The color of an online online indicator.
|
||||||
|
#[serde(default)]
|
||||||
|
pub theme_color_online: String,
|
||||||
|
/// The color of an idle online indicator.
|
||||||
|
#[serde(default)]
|
||||||
|
pub theme_color_idle: String,
|
||||||
|
/// The color of an offline online indicator.
|
||||||
|
#[serde(default)]
|
||||||
|
pub theme_color_offline: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub disable_other_themes: bool,
|
pub disable_other_themes: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue