➕ add unregistered servers page
parent
9648a9853c
commit
699cb671ec
|
@ -52,10 +52,10 @@ pub mod file {
|
||||||
pub iso: Isomorphic,
|
pub iso: Isomorphic,
|
||||||
|
|
||||||
pub port: Option <u16>,
|
pub port: Option <u16>,
|
||||||
pub servers: Vec <Server>,
|
pub servers: Option <Vec <Server>>,
|
||||||
|
|
||||||
// Adding a DB will take a while, so I'm moving these out of dev mode.
|
// Adding a DB will take a while, so I'm moving these out of dev mode.
|
||||||
pub scraper_keys: Vec <ScraperKey <Valid30Days>>,
|
pub scraper_keys: Option <Vec <ScraperKey <Valid30Days>>>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,13 +73,14 @@ impl TryFrom <file::Config> for Config {
|
||||||
type Error = ConfigError;
|
type Error = ConfigError;
|
||||||
|
|
||||||
fn try_from (f: file::Config) -> Result <Self, Self::Error> {
|
fn try_from (f: file::Config) -> Result <Self, Self::Error> {
|
||||||
let servers = f.servers.into_iter ()
|
let servers = f.servers.unwrap_or_else (|| vec! []);
|
||||||
.map (|server| Ok::<_, ConfigError> ((server.name.clone (), server)));
|
let servers = servers.into_iter ().map (|server| Ok::<_, ConfigError> ((server.name.clone (), server)));
|
||||||
|
|
||||||
let servers = itertools::process_results (servers, |i| HashMap::from_iter (i))?;
|
let servers = itertools::process_results (servers, |i| HashMap::from_iter (i))?;
|
||||||
|
|
||||||
|
let scraper_keys = f.scraper_keys.unwrap_or_else (|| vec! []);
|
||||||
let scraper_keys = if f.iso.enable_scraper_api {
|
let scraper_keys = if f.iso.enable_scraper_api {
|
||||||
HashMap::from_iter (f.scraper_keys.into_iter ().map (|key| (key.hash.encode_base64 (), key)))
|
HashMap::from_iter (scraper_keys.into_iter ().map (|key| (key.hash.encode_base64 (), key)))
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Default::default ()
|
Default::default ()
|
||||||
|
|
|
@ -253,6 +253,18 @@ struct ServerListPage <'a> {
|
||||||
servers: Vec <ServerEntry <'a>>,
|
servers: Vec <ServerEntry <'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive (Serialize)]
|
||||||
|
struct UnregisteredServer {
|
||||||
|
name: String,
|
||||||
|
tripcode: String,
|
||||||
|
last_seen: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive (Serialize)]
|
||||||
|
struct UnregisteredServerListPage {
|
||||||
|
unregistered_servers: Vec <UnregisteredServer>,
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_server_list_internal (state: &Arc <RelayState>)
|
async fn handle_server_list_internal (state: &Arc <RelayState>)
|
||||||
-> ServerListPage <'static>
|
-> ServerListPage <'static>
|
||||||
{
|
{
|
||||||
|
@ -294,6 +306,14 @@ async fn handle_server_list_internal (state: &Arc <RelayState>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_unregistered_servers_internal (state: &Arc <RelayState>)
|
||||||
|
-> UnregisteredServerListPage
|
||||||
|
{
|
||||||
|
UnregisteredServerListPage {
|
||||||
|
unregistered_servers: vec! [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_server_list (
|
async fn handle_server_list (
|
||||||
state: Arc <RelayState>,
|
state: Arc <RelayState>,
|
||||||
handlebars: Arc <Handlebars <'static>>
|
handlebars: Arc <Handlebars <'static>>
|
||||||
|
@ -305,6 +325,17 @@ async fn handle_server_list (
|
||||||
Ok (ok_reply (s)?)
|
Ok (ok_reply (s)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_unregistered_servers (
|
||||||
|
state: Arc <RelayState>,
|
||||||
|
handlebars: Arc <Handlebars <'static>>
|
||||||
|
) -> Result <Response <Body>, RequestError>
|
||||||
|
{
|
||||||
|
let page = handle_unregistered_servers_internal (&state).await;
|
||||||
|
|
||||||
|
let s = handlebars.render ("unregistered_servers", &page)?;
|
||||||
|
Ok (ok_reply (s)?)
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_endless_sink (req: Request <Body>) -> Result <Response <Body>, http::Error>
|
async fn handle_endless_sink (req: Request <Body>) -> Result <Response <Body>, http::Error>
|
||||||
{
|
{
|
||||||
let (_parts, mut body) = req.into_parts ();
|
let (_parts, mut body) = req.into_parts ();
|
||||||
|
@ -431,6 +462,9 @@ async fn handle_all (
|
||||||
Ok (error_reply (StatusCode::BAD_REQUEST, "Bad URI format")?)
|
Ok (error_reply (StatusCode::BAD_REQUEST, "Bad URI format")?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if path == "/frontend/unregistered_servers" {
|
||||||
|
Ok (handle_unregistered_servers (state, handlebars).await?)
|
||||||
|
}
|
||||||
else if let Some (rest) = prefix_match ("/frontend/debug/", &path) {
|
else if let Some (rest) = prefix_match ("/frontend/debug/", &path) {
|
||||||
if rest == "" {
|
if rest == "" {
|
||||||
let s = handlebars.render ("debug", &())?;
|
let s = handlebars.render ("debug", &())?;
|
||||||
|
@ -479,6 +513,7 @@ pub fn load_templates (asset_root: &Path)
|
||||||
("debug", "debug.hbs"),
|
("debug", "debug.hbs"),
|
||||||
("root", "root.hbs"),
|
("root", "root.hbs"),
|
||||||
("server_list", "server_list.hbs"),
|
("server_list", "server_list.hbs"),
|
||||||
|
("unregistered_servers", "unregistered_servers.hbs"),
|
||||||
] {
|
] {
|
||||||
handlebars.register_template_file (k, &asset_root.join (v))?;
|
handlebars.register_template_file (k, &asset_root.join (v))?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,12 @@ impl Default for ServerStatus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RejectedServer {
|
||||||
|
pub name: String,
|
||||||
|
pub tripcode: blake3::Hash,
|
||||||
|
pub seen: DateTime <Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct RelayState {
|
pub struct RelayState {
|
||||||
pub config: RwLock <Config>,
|
pub config: RwLock <Config>,
|
||||||
|
|
||||||
|
@ -82,6 +88,9 @@ pub struct RelayState {
|
||||||
|
|
||||||
pub shutdown_watch_tx: watch::Sender <bool>,
|
pub shutdown_watch_tx: watch::Sender <bool>,
|
||||||
pub shutdown_watch_rx: watch::Receiver <bool>,
|
pub shutdown_watch_rx: watch::Receiver <bool>,
|
||||||
|
|
||||||
|
// List of recently rejected server names (Used to approve servers)
|
||||||
|
pub unregistered_servers: RwLock <Vec <RejectedServer>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom <Config> for RelayState {
|
impl TryFrom <Config> for RelayState {
|
||||||
|
@ -97,6 +106,7 @@ impl TryFrom <Config> for RelayState {
|
||||||
response_rendezvous: Default::default (),
|
response_rendezvous: Default::default (),
|
||||||
shutdown_watch_tx,
|
shutdown_watch_tx,
|
||||||
shutdown_watch_rx,
|
shutdown_watch_rx,
|
||||||
|
unregistered_servers: Default::default (),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,18 +54,31 @@ pub async fn handle_listen (
|
||||||
|
|
||||||
let trip_error = || Ok (error_reply (StatusCode::UNAUTHORIZED, "Bad X-ApiKey")?);
|
let trip_error = || Ok (error_reply (StatusCode::UNAUTHORIZED, "Bad X-ApiKey")?);
|
||||||
|
|
||||||
|
let now = Utc::now ();
|
||||||
|
let actual_tripcode = blake3::hash (api_key);
|
||||||
|
|
||||||
let expected_tripcode = {
|
let expected_tripcode = {
|
||||||
let config = state.config.read ().await;
|
let config = state.config.read ().await;
|
||||||
|
|
||||||
match config.servers.get (&watcher_code) {
|
match config.servers.get (&watcher_code) {
|
||||||
None => {
|
None => {
|
||||||
error! ("Denied http_listen for non-existent server name {}", watcher_code);
|
error! ("Denied http_listen for non-existent server name {}", watcher_code);
|
||||||
|
let mut guard = state.unregistered_servers.write ().await;
|
||||||
|
guard.push (crate::RejectedServer {
|
||||||
|
name: watcher_code,
|
||||||
|
tripcode: actual_tripcode,
|
||||||
|
seen: now,
|
||||||
|
});
|
||||||
|
|
||||||
|
while guard.len () > 20 {
|
||||||
|
guard.remove (0);
|
||||||
|
}
|
||||||
|
|
||||||
return trip_error ();
|
return trip_error ();
|
||||||
},
|
},
|
||||||
Some (x) => *(*x).tripcode,
|
Some (x) => *(*x).tripcode,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let actual_tripcode = blake3::hash (api_key);
|
|
||||||
|
|
||||||
if expected_tripcode != actual_tripcode {
|
if expected_tripcode != actual_tripcode {
|
||||||
error! ("Denied http_listen for bad tripcode {}", base64::encode (actual_tripcode.as_bytes ()));
|
error! ("Denied http_listen for bad tripcode {}", base64::encode (actual_tripcode.as_bytes ()));
|
||||||
|
@ -81,7 +94,7 @@ pub async fn handle_listen (
|
||||||
|
|
||||||
let mut status = server_status.entry (watcher_code.clone ()).or_insert_with (Default::default);
|
let mut status = server_status.entry (watcher_code.clone ()).or_insert_with (Default::default);
|
||||||
|
|
||||||
status.last_seen = Utc::now ();
|
status.last_seen = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (tx, rx) = oneshot::channel ();
|
let (tx, rx) = oneshot::channel ();
|
||||||
|
|
|
@ -44,6 +44,8 @@ AIABAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAA" rel="icon" type="image/x-icon" />
|
||||||
<div style="color: red;">Relay is in dev mode. This should never be seen in production!</div>
|
<div style="color: red;">Relay is in dev mode. This should never be seen in production!</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
<a href="../unregistered_servers">Unregistered servers</a>
|
||||||
|
|
||||||
<div style="padding-top: 1em;">
|
<div style="padding-top: 1em;">
|
||||||
{{#if servers}}
|
{{#if servers}}
|
||||||
<table class="entry_list">
|
<table class="entry_list">
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link href="data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAlJSUAAGIAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAIiIiIgAAAAAAAAAgAAAAAAAAAAAAADIiIiIi
|
||||||
|
IiIjMhERERERESMyERERERERIzIREREREREjMhERIRERESMyERIiIiERIzIRESEREREjMhERERER
|
||||||
|
ESMyERERERERIzIREREREREjMiIiIiIiIiMAAAAAAAAAAAAAAAAAAAAAAAAAAIABAACAAQAAgAEA
|
||||||
|
AIABAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAA" rel="icon" type="image/x-icon" />
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
td > * {
|
||||||
|
padding: 20px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.entry {
|
||||||
|
|
||||||
|
}
|
||||||
|
.grey {
|
||||||
|
color: #888;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.entry_list {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
tbody tr:nth-child(odd) {
|
||||||
|
background-color: #ddd;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title>Unregistered servers</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Unregistered servers</h1>
|
||||||
|
|
||||||
|
<div style="padding-top: 1em;">
|
||||||
|
{{#if unregistered_servers}}
|
||||||
|
<table class="entry_list">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Tripcode</th>
|
||||||
|
<th>Time seen</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
{{#each unregistered_servers}}
|
||||||
|
<tr>
|
||||||
|
<td>{{this.name}}</td>
|
||||||
|
<td>{{this.tripcode}}</td>
|
||||||
|
<td><span class="grey">{{this.last_seen}}</span></td>
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{else}}
|
||||||
|
(No unregistered servers have reported yet)
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue