Server: paginate servers on admin page
This commit is contained in:
parent
7fc61d0e26
commit
5940264ffd
|
@ -38,6 +38,7 @@ rsa-pem = "0.2.0"
|
||||||
rust-embed = "5"
|
rust-embed = "5"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
serde_urlencoded = "0.7"
|
||||||
sha2 = "0.9"
|
sha2 = "0.9"
|
||||||
sled = { version = "0.34.6", features = ["compression"] }
|
sled = { version = "0.34.6", features = ["compression"] }
|
||||||
structopt = "0.3"
|
structopt = "0.3"
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::{
|
||||||
error::{Error, OptionExt},
|
error::{Error, OptionExt},
|
||||||
extensions::{CommentExt, ProfileExt, SubmissionExt},
|
extensions::{CommentExt, ProfileExt, SubmissionExt},
|
||||||
nav::NavState,
|
nav::NavState,
|
||||||
|
pagination::{PageNum, SearchPage},
|
||||||
views::{OwnedProfileView, OwnedSubmissionView},
|
views::{OwnedProfileView, OwnedSubmissionView},
|
||||||
ActixLoader, State,
|
ActixLoader, State,
|
||||||
};
|
};
|
||||||
|
@ -20,6 +21,11 @@ use uuid::Uuid;
|
||||||
|
|
||||||
pub use hyaenidae_profiles::store::Report;
|
pub use hyaenidae_profiles::store::Report;
|
||||||
|
|
||||||
|
mod pagination;
|
||||||
|
use pagination::{
|
||||||
|
BlockedPager, FederatedPager, InboundPager, KnownPager, OutboundPager, ServerPager,
|
||||||
|
};
|
||||||
|
|
||||||
pub(super) fn scope() -> Scope {
|
pub(super) fn scope() -> Scope {
|
||||||
web::scope("/admin")
|
web::scope("/admin")
|
||||||
.service(web::resource("").route(web::get().to(admin_page)))
|
.service(web::resource("").route(web::get().to(admin_page)))
|
||||||
|
@ -87,6 +93,7 @@ async fn discover_server(
|
||||||
loader: ActixLoader,
|
loader: ActixLoader,
|
||||||
_: Admin,
|
_: Admin,
|
||||||
form: web::Form<DiscoverForm>,
|
form: web::Form<DiscoverForm>,
|
||||||
|
query: web::Query<HashMap<String, String>>,
|
||||||
nav_state: NavState,
|
nav_state: NavState,
|
||||||
client: web::Data<Client>,
|
client: web::Data<Client>,
|
||||||
state: web::Data<State>,
|
state: web::Data<State>,
|
||||||
|
@ -117,7 +124,7 @@ async fn discover_server(
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = (fallible)().await {
|
if let Err(e) = (fallible)().await {
|
||||||
let mut federation_view = FederationView::build(&state2).await?;
|
let mut federation_view = FederationView::build(query.into_inner(), &state2).await?;
|
||||||
federation_view
|
federation_view
|
||||||
.discover_error(e.to_string())
|
.discover_error(e.to_string())
|
||||||
.discover_value(url2);
|
.discover_value(url2);
|
||||||
|
@ -384,13 +391,14 @@ async fn defederate(
|
||||||
|
|
||||||
pub struct FederationView {
|
pub struct FederationView {
|
||||||
servers: HashMap<Uuid, Server>,
|
servers: HashMap<Uuid, Server>,
|
||||||
blocked: Vec<Uuid>,
|
blocked: SearchPage,
|
||||||
federated: Vec<Uuid>,
|
federated: SearchPage,
|
||||||
inbound_requests: Vec<Uuid>,
|
inbound_requests: SearchPage,
|
||||||
outbound_requests: Vec<Uuid>,
|
outbound_requests: SearchPage,
|
||||||
known: Vec<Uuid>,
|
known: SearchPage,
|
||||||
discover_value: Option<String>,
|
discover_value: Option<String>,
|
||||||
discover_error: Option<String>,
|
discover_error: Option<String>,
|
||||||
|
query: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct BlockView<'a> {
|
pub(crate) struct BlockView<'a> {
|
||||||
|
@ -514,13 +522,6 @@ impl FederationView {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn blocked<'a>(&'a self) -> impl Iterator<Item = BlockView<'a>> + 'a {
|
|
||||||
self.blocked
|
|
||||||
.iter()
|
|
||||||
.filter_map(move |id| self.servers.get(id))
|
|
||||||
.map(|server| BlockView { server })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn discover_input(&self, loader: &ActixLoader) -> TextInput {
|
pub(crate) fn discover_input(&self, loader: &ActixLoader) -> TextInput {
|
||||||
let input = TextInput::new("url")
|
let input = TextInput::new("url")
|
||||||
.title(&fl!(loader, "admin-discover-input"))
|
.title(&fl!(loader, "admin-discover-input"))
|
||||||
|
@ -538,107 +539,326 @@ impl FederationView {
|
||||||
"/admin/discover"
|
"/admin/discover"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn blocked<'a>(&'a self) -> impl Iterator<Item = BlockView<'a>> + 'a {
|
||||||
|
self.blocked
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.filter_map(move |id| self.servers.get(id))
|
||||||
|
.map(|server| BlockView { server })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn has_blocked_nav(&self) -> bool {
|
||||||
|
self.blocked.next.is_some() || self.blocked.prev.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blocked_next(&self, loader: &ActixLoader) -> Option<Button> {
|
||||||
|
if let Some(next) = self.blocked.next {
|
||||||
|
let mut query = self.query.clone();
|
||||||
|
query.insert("blocked_page".to_owned(), next.to_string());
|
||||||
|
let href = if let Ok(query) = serde_urlencoded::to_string(query) {
|
||||||
|
format!("/admin?{}", query)
|
||||||
|
} else {
|
||||||
|
"/admin".to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Button::secondary(&fl!(loader, "server-next-page")).href(&href))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn blocked_previous(&self, loader: &ActixLoader) -> Option<Button> {
|
||||||
|
if let Some(prev) = self.blocked.prev {
|
||||||
|
let mut query = self.query.clone();
|
||||||
|
query.insert("blocked_page".to_owned(), prev.to_string());
|
||||||
|
let href = if let Ok(query) = serde_urlencoded::to_string(query) {
|
||||||
|
format!("/admin?{}", query)
|
||||||
|
} else {
|
||||||
|
"/admin".to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Button::secondary(&fl!(loader, "server-previous-page")).href(&href))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn blocked_buttons(&self, loader: &ActixLoader) -> Vec<Button> {
|
||||||
|
self.blocked_previous(loader)
|
||||||
|
.into_iter()
|
||||||
|
.chain(self.blocked_next(loader))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn known<'a>(&'a self) -> impl Iterator<Item = KnownView<'a>> + 'a {
|
pub(crate) fn known<'a>(&'a self) -> impl Iterator<Item = KnownView<'a>> + 'a {
|
||||||
self.known
|
self.known
|
||||||
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(move |id| self.servers.get(id))
|
.filter_map(move |id| self.servers.get(id))
|
||||||
.map(|server| KnownView { server })
|
.map(|server| KnownView { server })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn has_known_nav(&self) -> bool {
|
||||||
|
self.known.next.is_some() || self.known.prev.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn known_next(&self, loader: &ActixLoader) -> Option<Button> {
|
||||||
|
if let Some(next) = self.known.next {
|
||||||
|
let mut query = self.query.clone();
|
||||||
|
query.insert("known_page".to_owned(), next.to_string());
|
||||||
|
let href = if let Ok(query) = serde_urlencoded::to_string(query) {
|
||||||
|
format!("/admin?{}", query)
|
||||||
|
} else {
|
||||||
|
"/admin".to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Button::secondary(&fl!(loader, "server-next-page")).href(&href))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn known_previous(&self, loader: &ActixLoader) -> Option<Button> {
|
||||||
|
if let Some(prev) = self.known.prev {
|
||||||
|
let mut query = self.query.clone();
|
||||||
|
query.insert("known_page".to_owned(), prev.to_string());
|
||||||
|
let href = if let Ok(query) = serde_urlencoded::to_string(query) {
|
||||||
|
format!("/admin?{}", query)
|
||||||
|
} else {
|
||||||
|
"/admin".to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Button::secondary(&fl!(loader, "server-previous-page")).href(&href))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn known_buttons(&self, loader: &ActixLoader) -> Vec<Button> {
|
||||||
|
self.known_previous(loader)
|
||||||
|
.into_iter()
|
||||||
|
.chain(self.known_next(loader))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn federated<'a>(&'a self) -> impl Iterator<Item = FederatedView<'a>> + 'a {
|
pub(crate) fn federated<'a>(&'a self) -> impl Iterator<Item = FederatedView<'a>> + 'a {
|
||||||
self.federated
|
self.federated
|
||||||
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(move |id| self.servers.get(id))
|
.filter_map(move |id| self.servers.get(id))
|
||||||
.map(|server| FederatedView { server })
|
.map(|server| FederatedView { server })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn has_federated_nav(&self) -> bool {
|
||||||
|
self.federated.next.is_some() || self.federated.prev.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn federated_next(&self, loader: &ActixLoader) -> Option<Button> {
|
||||||
|
if let Some(next) = self.federated.next {
|
||||||
|
let mut query = self.query.clone();
|
||||||
|
query.insert("federated_page".to_owned(), next.to_string());
|
||||||
|
let href = if let Ok(query) = serde_urlencoded::to_string(query) {
|
||||||
|
format!("/admin?{}", query)
|
||||||
|
} else {
|
||||||
|
"/admin".to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Button::secondary(&fl!(loader, "server-next-page")).href(&href))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn federated_previous(&self, loader: &ActixLoader) -> Option<Button> {
|
||||||
|
if let Some(prev) = self.federated.prev {
|
||||||
|
let mut query = self.query.clone();
|
||||||
|
query.insert("federated_page".to_owned(), prev.to_string());
|
||||||
|
let href = if let Ok(query) = serde_urlencoded::to_string(query) {
|
||||||
|
format!("/admin?{}", query)
|
||||||
|
} else {
|
||||||
|
"/admin".to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Button::secondary(&fl!(loader, "server-previous-page")).href(&href))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn federated_buttons(&self, loader: &ActixLoader) -> Vec<Button> {
|
||||||
|
self.federated_previous(loader)
|
||||||
|
.into_iter()
|
||||||
|
.chain(self.federated_next(loader))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn inbound<'a>(&'a self) -> impl Iterator<Item = InboundRequestView<'a>> + 'a {
|
pub(crate) fn inbound<'a>(&'a self) -> impl Iterator<Item = InboundRequestView<'a>> + 'a {
|
||||||
self.inbound_requests
|
self.inbound_requests
|
||||||
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(move |id| self.servers.get(id))
|
.filter_map(move |id| self.servers.get(id))
|
||||||
.map(|server| InboundRequestView { server })
|
.map(|server| InboundRequestView { server })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn has_inbound_nav(&self) -> bool {
|
||||||
|
self.inbound_requests.next.is_some() || self.inbound_requests.prev.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inbound_next(&self, loader: &ActixLoader) -> Option<Button> {
|
||||||
|
if let Some(next) = self.inbound_requests.next {
|
||||||
|
let mut query = self.query.clone();
|
||||||
|
query.insert("inbound_page".to_owned(), next.to_string());
|
||||||
|
let href = if let Ok(query) = serde_urlencoded::to_string(query) {
|
||||||
|
format!("/admin?{}", query)
|
||||||
|
} else {
|
||||||
|
"/admin".to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Button::secondary(&fl!(loader, "server-next-page")).href(&href))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inbound_previous(&self, loader: &ActixLoader) -> Option<Button> {
|
||||||
|
if let Some(prev) = self.inbound_requests.prev {
|
||||||
|
let mut query = self.query.clone();
|
||||||
|
query.insert("inbound_page".to_owned(), prev.to_string());
|
||||||
|
let href = if let Ok(query) = serde_urlencoded::to_string(query) {
|
||||||
|
format!("/admin?{}", query)
|
||||||
|
} else {
|
||||||
|
"/admin".to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Button::secondary(&fl!(loader, "server-previous-page")).href(&href))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn inbound_buttons(&self, loader: &ActixLoader) -> Vec<Button> {
|
||||||
|
self.inbound_previous(loader)
|
||||||
|
.into_iter()
|
||||||
|
.chain(self.inbound_next(loader))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn outbound<'a>(&'a self) -> impl Iterator<Item = OutboundRequestView<'a>> + 'a {
|
pub(crate) fn outbound<'a>(&'a self) -> impl Iterator<Item = OutboundRequestView<'a>> + 'a {
|
||||||
self.outbound_requests
|
self.outbound_requests
|
||||||
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(move |id| self.servers.get(id))
|
.filter_map(move |id| self.servers.get(id))
|
||||||
.map(|server| OutboundRequestView { server })
|
.map(|server| OutboundRequestView { server })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn build(state: &State) -> Result<Self, Error> {
|
pub(crate) fn has_outbound_nav(&self) -> bool {
|
||||||
let server_store = state.profiles.store.servers.clone();
|
self.outbound_requests.next.is_some() || self.outbound_requests.prev.is_some()
|
||||||
let server_blocks = state.profiles.store.view.server_blocks.clone();
|
}
|
||||||
let server_requests = state.profiles.store.view.server_follow_requests.clone();
|
|
||||||
let server_follows = state.profiles.store.view.server_follows.clone();
|
fn outbound_next(&self, loader: &ActixLoader) -> Option<Button> {
|
||||||
|
if let Some(next) = self.outbound_requests.next {
|
||||||
|
let mut query = self.query.clone();
|
||||||
|
query.insert("outbound_page".to_owned(), next.to_string());
|
||||||
|
let href = if let Ok(query) = serde_urlencoded::to_string(query) {
|
||||||
|
format!("/admin?{}", query)
|
||||||
|
} else {
|
||||||
|
"/admin".to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Button::secondary(&fl!(loader, "server-next-page")).href(&href))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outbound_previous(&self, loader: &ActixLoader) -> Option<Button> {
|
||||||
|
if let Some(prev) = self.outbound_requests.prev {
|
||||||
|
let mut query = self.query.clone();
|
||||||
|
query.insert("outbound_page".to_owned(), prev.to_string());
|
||||||
|
let href = if let Ok(query) = serde_urlencoded::to_string(query) {
|
||||||
|
format!("/admin?{}", query)
|
||||||
|
} else {
|
||||||
|
"/admin".to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Button::secondary(&fl!(loader, "server-previous-page")).href(&href))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn outbound_buttons(&self, loader: &ActixLoader) -> Vec<Button> {
|
||||||
|
self.outbound_previous(loader)
|
||||||
|
.into_iter()
|
||||||
|
.chain(self.outbound_next(loader))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn build(query: HashMap<String, String>, state: &State) -> Result<Self, Error> {
|
||||||
|
let profiles = state.profiles.clone();
|
||||||
|
|
||||||
let view = web::block(move || {
|
let view = web::block(move || {
|
||||||
let mut servers = HashMap::new();
|
let mut servers = HashMap::new();
|
||||||
|
|
||||||
let self_id = server_store.get_self()?.req()?;
|
let self_id = profiles.store.servers.get_self()?.req()?;
|
||||||
|
|
||||||
let self_server = server_store.by_id(self_id)?.req()?;
|
let self_server = profiles.store.servers.by_id(self_id)?.req()?;
|
||||||
servers.insert(self_server.id(), self_server);
|
servers.insert(self_server.id(), self_server);
|
||||||
|
|
||||||
let federated = server_follows
|
let federated = SearchPage::from_pagination(
|
||||||
.forward_iter(self_id)
|
FederatedPager(ServerPager {
|
||||||
.chain(server_follows.backward_iter(self_id))
|
self_id,
|
||||||
.filter_map(|server_id| {
|
store: &profiles.store,
|
||||||
if !servers.contains_key(&server_id) {
|
servers: &mut servers,
|
||||||
let server = server_store.by_id(server_id).ok()??;
|
}),
|
||||||
servers.insert(server.id(), server);
|
10,
|
||||||
Some(server_id)
|
"".to_owned(),
|
||||||
} else {
|
page_num(&query, "federated_page"),
|
||||||
None
|
);
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let inbound_requests = server_requests
|
let inbound_requests = SearchPage::from_pagination(
|
||||||
.forward_iter(self_id)
|
InboundPager(ServerPager {
|
||||||
.filter_map(|server_id| {
|
self_id,
|
||||||
if !servers.contains_key(&server_id) {
|
store: &profiles.store,
|
||||||
let server = server_store.by_id(server_id).ok()??;
|
servers: &mut servers,
|
||||||
servers.insert(server.id(), server);
|
}),
|
||||||
}
|
10,
|
||||||
Some(server_id)
|
"".to_owned(),
|
||||||
})
|
page_num(&query, "inbound_page"),
|
||||||
.collect();
|
);
|
||||||
|
|
||||||
let outbound_requests = server_requests
|
let outbound_requests = SearchPage::from_pagination(
|
||||||
.backward_iter(self_id)
|
OutboundPager(ServerPager {
|
||||||
.filter_map(|server_id| {
|
self_id,
|
||||||
if !servers.contains_key(&server_id) {
|
store: &profiles.store,
|
||||||
let server = server_store.by_id(server_id).ok()??;
|
servers: &mut servers,
|
||||||
servers.insert(server.id(), server);
|
}),
|
||||||
}
|
10,
|
||||||
Some(server_id)
|
"".to_owned(),
|
||||||
})
|
page_num(&query, "outbound_page"),
|
||||||
.collect();
|
);
|
||||||
|
|
||||||
let blocked = server_blocks
|
let blocked = SearchPage::from_pagination(
|
||||||
.backward_iter(self_id)
|
BlockedPager(ServerPager {
|
||||||
.filter_map(|server_id| {
|
self_id,
|
||||||
if !servers.contains_key(&server_id) {
|
store: &profiles.store,
|
||||||
let server = server_store.by_id(server_id).ok()??;
|
servers: &mut servers,
|
||||||
servers.insert(server.id(), server);
|
}),
|
||||||
}
|
10,
|
||||||
Some(server_id)
|
"".to_owned(),
|
||||||
})
|
page_num(&query, "blocked_page"),
|
||||||
.collect();
|
);
|
||||||
|
|
||||||
let known = server_store
|
let known = SearchPage::from_pagination(
|
||||||
.known()
|
KnownPager(ServerPager {
|
||||||
.filter_map(|server_id| {
|
self_id,
|
||||||
if !servers.contains_key(&server_id) {
|
store: &profiles.store,
|
||||||
let server = server_store.by_id(server_id).ok()??;
|
servers: &mut servers,
|
||||||
servers.insert(server.id(), server);
|
}),
|
||||||
Some(server_id)
|
10,
|
||||||
} else {
|
"".to_owned(),
|
||||||
None
|
page_num(&query, "known_page"),
|
||||||
}
|
);
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(FederationView {
|
Ok(FederationView {
|
||||||
servers,
|
servers,
|
||||||
|
@ -649,6 +869,7 @@ impl FederationView {
|
||||||
known,
|
known,
|
||||||
discover_value: None,
|
discover_value: None,
|
||||||
discover_error: None,
|
discover_error: None,
|
||||||
|
query,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -657,6 +878,14 @@ impl FederationView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn page_num(query: &HashMap<String, String>, name: &str) -> Option<PageNum> {
|
||||||
|
query.get(name).and_then(|page_str| {
|
||||||
|
Some(PageNum {
|
||||||
|
page: page_str.parse().ok()?,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, serde::Deserialize)]
|
#[derive(Clone, Debug, serde::Deserialize)]
|
||||||
struct ConfigForm {
|
struct ConfigForm {
|
||||||
title: String,
|
title: String,
|
||||||
|
@ -876,11 +1105,12 @@ fn to_admin() -> HttpResponse {
|
||||||
|
|
||||||
async fn admin_page(
|
async fn admin_page(
|
||||||
loader: ActixLoader,
|
loader: ActixLoader,
|
||||||
|
query: web::Query<HashMap<String, String>>,
|
||||||
_: Admin,
|
_: Admin,
|
||||||
nav_state: NavState,
|
nav_state: NavState,
|
||||||
state: web::Data<State>,
|
state: web::Data<State>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let federation_view = FederationView::build(&state).await?;
|
let federation_view = FederationView::build(query.into_inner(), &state).await?;
|
||||||
let server_view = ServerView::build(&state).await?;
|
let server_view = ServerView::build(&state).await?;
|
||||||
let open_reports = ReportsView::new(state).await?;
|
let open_reports = ReportsView::new(state).await?;
|
||||||
|
|
104
src/admin/pagination.rs
Normal file
104
src/admin/pagination.rs
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
use crate::pagination::SearchPagination;
|
||||||
|
use hyaenidae_profiles::store::Server;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
pub(super) struct ServerPager<'b> {
|
||||||
|
pub(super) self_id: Uuid,
|
||||||
|
pub(super) store: &'b hyaenidae_profiles::store::Store,
|
||||||
|
pub(super) servers: &'b mut HashMap<Uuid, Server>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct FederatedPager<'a>(pub(super) ServerPager<'a>);
|
||||||
|
pub(super) struct InboundPager<'a>(pub(super) ServerPager<'a>);
|
||||||
|
pub(super) struct OutboundPager<'a>(pub(super) ServerPager<'a>);
|
||||||
|
pub(super) struct BlockedPager<'a>(pub(super) ServerPager<'a>);
|
||||||
|
pub(super) struct KnownPager<'a>(pub(super) ServerPager<'a>);
|
||||||
|
|
||||||
|
impl<'b> SearchPagination for FederatedPager<'b> {
|
||||||
|
fn from_term<'a>(&'a mut self, _: &'a str) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||||||
|
Box::new(
|
||||||
|
self.0
|
||||||
|
.store
|
||||||
|
.view
|
||||||
|
.server_follows
|
||||||
|
.forward_iter(self.0.self_id)
|
||||||
|
.chain(
|
||||||
|
self.0
|
||||||
|
.store
|
||||||
|
.view
|
||||||
|
.server_follows
|
||||||
|
.backward_iter(self.0.self_id),
|
||||||
|
)
|
||||||
|
.filter_map(move |server_id| self.0.filter_server(server_id))
|
||||||
|
.rev(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b> SearchPagination for InboundPager<'b> {
|
||||||
|
fn from_term<'a>(&'a mut self, _: &'a str) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||||||
|
Box::new(
|
||||||
|
self.0
|
||||||
|
.store
|
||||||
|
.view
|
||||||
|
.server_follow_requests
|
||||||
|
.forward_iter(self.0.self_id)
|
||||||
|
.filter_map(move |server_id| self.0.filter_server(server_id))
|
||||||
|
.rev(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b> SearchPagination for OutboundPager<'b> {
|
||||||
|
fn from_term<'a>(&'a mut self, _: &'a str) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||||||
|
Box::new(
|
||||||
|
self.0
|
||||||
|
.store
|
||||||
|
.view
|
||||||
|
.server_follow_requests
|
||||||
|
.backward_iter(self.0.self_id)
|
||||||
|
.filter_map(move |server_id| self.0.filter_server(server_id))
|
||||||
|
.rev(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b> SearchPagination for BlockedPager<'b> {
|
||||||
|
fn from_term<'a>(&'a mut self, _: &'a str) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||||||
|
Box::new(
|
||||||
|
self.0
|
||||||
|
.store
|
||||||
|
.view
|
||||||
|
.server_blocks
|
||||||
|
.backward_iter(self.0.self_id)
|
||||||
|
.filter_map(move |server_id| self.0.filter_server(server_id))
|
||||||
|
.rev(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b> SearchPagination for KnownPager<'b> {
|
||||||
|
fn from_term<'a>(&'a mut self, _: &'a str) -> Box<dyn DoubleEndedIterator<Item = Uuid> + 'a> {
|
||||||
|
Box::new(
|
||||||
|
self.0
|
||||||
|
.store
|
||||||
|
.servers
|
||||||
|
.known()
|
||||||
|
.filter_map(move |server_id| self.0.filter_server(server_id))
|
||||||
|
.rev(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'b> ServerPager<'b> {
|
||||||
|
fn filter_server(&mut self, server_id: Uuid) -> Option<Uuid> {
|
||||||
|
if !self.servers.contains_key(&server_id) {
|
||||||
|
let server = self.store.servers.by_id(server_id).ok()??;
|
||||||
|
self.servers.insert(server.id(), server);
|
||||||
|
Some(server_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -113,24 +113,25 @@ impl SearchPage {
|
||||||
|
|
||||||
match page {
|
match page {
|
||||||
Some(PageNum { page }) => {
|
Some(PageNum { page }) => {
|
||||||
|
let page = page.saturating_sub(1);
|
||||||
items = pagination
|
items = pagination
|
||||||
.from_term(&term)
|
.from_term(&term)
|
||||||
.skip(page * per_page)
|
.skip(page * per_page)
|
||||||
.take(per_page + 1)
|
.take(per_page + 1)
|
||||||
.collect();
|
.collect();
|
||||||
if page > 0 {
|
if page > 0 {
|
||||||
prev = Some(page - 1);
|
prev = Some(page);
|
||||||
}
|
}
|
||||||
if items.len() == per_page + 1 {
|
if items.len() == per_page + 1 {
|
||||||
items.pop();
|
items.pop();
|
||||||
next = Some(page + 1);
|
next = Some(page + 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
items = pagination.from_term(&term).take(per_page + 1).collect();
|
items = pagination.from_term(&term).take(per_page + 1).collect();
|
||||||
if items.len() == per_page + 1 {
|
if items.len() == per_page + 1 {
|
||||||
items.pop();
|
items.pop();
|
||||||
next = Some(1);
|
next = Some(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
@use crate::admin::{FederationView, ReportsView, ServerView};
|
@use crate::admin::{FederationView, ReportsView, ServerView};
|
||||||
@use crate::extensions::ProfileExt;
|
@use crate::extensions::ProfileExt;
|
||||||
@use crate::nav::NavState;
|
@use crate::nav::NavState;
|
||||||
|
@use crate::templates::button_js;
|
||||||
@use crate::templates::layouts::home;
|
@use crate::templates::layouts::home;
|
||||||
@use crate::templates::admin::{reporter, server_box};
|
@use crate::templates::admin::{reporter, server_box};
|
||||||
@use hyaenidae_toolkit::{templates::button_group, Button};
|
@use hyaenidae_toolkit::{templates::button_group, Button};
|
||||||
|
@ -12,7 +13,9 @@
|
||||||
|
|
||||||
@(loader: &ActixLoader, reports_view: &ReportsView, server_view: &ServerView, federation_view: &FederationView, nav_state: &NavState)
|
@(loader: &ActixLoader, reports_view: &ReportsView, server_view: &ServerView, federation_view: &FederationView, nav_state: &NavState)
|
||||||
|
|
||||||
@:home(loader, &fl!(loader, "admin-settings-title"), &fl!(loader, "admin-settings-subtitle"), nav_state, {}, {
|
@:home(loader, &fl!(loader, "admin-settings-title"), &fl!(loader, "admin-settings-subtitle"), nav_state, {
|
||||||
|
@:button_js()
|
||||||
|
}, {
|
||||||
@:card(&Card::full_width().dark(nav_state.dark()), {
|
@:card(&Card::full_width().dark(nav_state.dark()), {
|
||||||
<form method="POST" action="@server_view.update_path()">
|
<form method="POST" action="@server_view.update_path()">
|
||||||
@:card_title({
|
@:card_title({
|
||||||
|
@ -123,6 +126,11 @@
|
||||||
</div>
|
</div>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@if federation_view.has_inbound_nav() {
|
||||||
|
@:card_body({
|
||||||
|
@:button_group(&federation_view.inbound_buttons(loader))
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
@:card(&Card::full_width().dark(nav_state.dark()), {
|
@:card(&Card::full_width().dark(nav_state.dark()), {
|
||||||
@:card_title({
|
@:card_title({
|
||||||
|
@ -139,6 +147,11 @@
|
||||||
</div>
|
</div>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@if federation_view.has_outbound_nav() {
|
||||||
|
@:card_body({
|
||||||
|
@:button_group(&federation_view.outbound_buttons(loader))
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
@:card(&Card::full_width().dark(nav_state.dark()), {
|
@:card(&Card::full_width().dark(nav_state.dark()), {
|
||||||
@:card_title({
|
@:card_title({
|
||||||
|
@ -155,6 +168,11 @@
|
||||||
</div>
|
</div>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@if federation_view.has_federated_nav() {
|
||||||
|
@:card_body({
|
||||||
|
@:button_group(&federation_view.federated_buttons(loader))
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
@:card(&Card::full_width().dark(nav_state.dark()), {
|
@:card(&Card::full_width().dark(nav_state.dark()), {
|
||||||
@:card_title({
|
@:card_title({
|
||||||
|
@ -170,6 +188,11 @@
|
||||||
</div>
|
</div>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@if federation_view.has_blocked_nav() {
|
||||||
|
@:card_body({
|
||||||
|
@:button_group(&federation_view.blocked_buttons(loader))
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
@:card(&Card::full_width().dark(nav_state.dark()), {
|
@:card(&Card::full_width().dark(nav_state.dark()), {
|
||||||
@:card_title({
|
@:card_title({
|
||||||
|
@ -186,5 +209,10 @@
|
||||||
</div>
|
</div>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@if federation_view.has_known_nav() {
|
||||||
|
@:card_body({
|
||||||
|
@:button_group(&federation_view.known_buttons(loader))
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -326,6 +326,9 @@ admin-federation-defederate = Defederate
|
||||||
admin-federation-block = Block
|
admin-federation-block = Block
|
||||||
admin-federation-unblock = Unblock
|
admin-federation-unblock = Unblock
|
||||||
|
|
||||||
|
server-previous-page = Previous
|
||||||
|
server-next-page = Next
|
||||||
|
|
||||||
update-comment-heading = Update Comment
|
update-comment-heading = Update Comment
|
||||||
update-comment-button = Save
|
update-comment-button = Save
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue