Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 64 additions & 52 deletions Cargo.lock

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions entity/src/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ pub enum Relation {
ContentAudit,
#[sea_orm(has_many = "super::execution_metadata::Entity")]
ExecutionMetadata,
#[sea_orm(has_many = "super::content_gossip::Entity")]
ContentGossip,
}

impl Related<super::content_audit::Entity> for Entity {
Expand All @@ -56,6 +58,12 @@ impl Related<super::execution_metadata::Entity> for Entity {
}
}

impl Related<super::content_gossip::Entity> for Entity {
fn to() -> RelationDef {
Relation::ContentGossip.def()
}
}

impl ActiveModelBehavior for ActiveModel {}

pub async fn get_or_create<T: OverlayContentKey>(
Expand Down
1 change: 1 addition & 0 deletions entity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod prelude;

pub mod content;
pub mod content_audit;
pub mod content_gossip;
pub mod execution_metadata;
pub mod key_value;
pub mod node;
Expand Down
1 change: 1 addition & 0 deletions glados-web/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ glados-core = { path = "../glados-core" }
hex = "0.4.3"
migration = { path = "../migration" }
sea-orm = "0.10.2"
serde = "1.0.158"
tokio = "1.22.0"
tower-http = { version = "0.3.5", features = ["fs"] }
tracing = "0.1.37"
Expand Down
6 changes: 6 additions & 0 deletions glados-web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::sync::Arc;
use std::{net::SocketAddr, path::Path};

use anyhow::{bail, Result};
use axum::routing::post;
use axum::{
extract::Extension,
routing::{get, get_service},
Expand Down Expand Up @@ -38,6 +39,11 @@ pub async fn run_glados_web(config: Arc<State>) -> Result<()> {
"/content/key/:content_key_hex",
get(routes::contentkey_detail),
)
.route("/gossip", post(routes::create_gossip_record))
.route(
"/gossip/:content_id_hex",
get(routes::get_gossip_records_for_content),
)
.nest_service("/static/", serve_dir.clone())
.fallback_service(serve_dir)
.layer(Extension(config));
Expand Down
33 changes: 31 additions & 2 deletions glados-web/src/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@ use std::io;
use std::sync::Arc;

use axum::{
extract::{Extension, Path},
extract::{Extension, Json, Path},
http::StatusCode,
response::IntoResponse,
};
use ethportal_api::{types::content_key::OverlayContentKey, HistoryContentKey};
use sea_orm::{ColumnTrait, EntityTrait, ModelTrait, QueryFilter, QueryOrder, QuerySelect};
use serde::Deserialize;

use entity::{
content,
content_audit::{self, AuditResult},
execution_metadata, node,
content_gossip, execution_metadata, node,
};

use crate::state::State;
Expand Down Expand Up @@ -221,6 +222,12 @@ pub async fn contentkey_detail(
.expect("No content found");
let block_number = metadata_model.map(|m| m.block_number);

let gossip_records = content_key_model
.find_related(content_gossip::Entity)
.all(&state.database_connection)
.await
.expect("Failed to look up gossip records");

let content_id = format!("0x{}", hex::encode(content_key.content_id()));
let content_kind = content_key.to_string();
let template = ContentKeyDetailTemplate {
Expand All @@ -230,6 +237,28 @@ pub async fn contentkey_detail(
content_id,
content_kind,
block_number,
gossip_records,
};
HtmlTemplate(template)
}

pub async fn create_gossip_record(
Json(gossip_data): Json<NewGossipData>,
Extension(state): Extension<Arc<State>>,
) -> impl IntoResponse {
let gossip = content_gossip::create(
gossip_data.sending_node,
gossip_data.receiving_node,
gossip_data.content_key,
&state.database_connection,
)
.await
.expect("Failed to create gossip record");
}

#[derive(Deserialize)]
pub struct NewGossipData {
receiving_node: String,
sending_node: String,
content_key: String,
}
3 changes: 2 additions & 1 deletion glados-web/src/templates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use axum::{
response::{Html, IntoResponse, Response},
};

use entity::{content, content_audit, node};
use entity::{content, content_audit, content_gossip, node};

#[derive(Template)]
#[template(path = "index.html")]
Expand Down Expand Up @@ -54,6 +54,7 @@ pub struct ContentKeyDetailTemplate {
pub content_kind: String,
pub block_number: Option<i32>,
pub contentaudit_list: Vec<content_audit::Model>,
pub gossip_records: Vec<content_gossip::Model>,
}

pub struct HtmlTemplate<T>(pub T);
Expand Down
152 changes: 79 additions & 73 deletions glados-web/templates/content_dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,28 @@ <h1>Content Dashboard</h1>
<div class="col">
<ul>
<h2> Recent content with audits</h2>
<table>
<table class="table">
<tr>
<td>| Result </td>
<td>| Sub-protocol </td>
<td>| Strategy</td>
<td>| Content Key</td>
<td>| Content ID</td>
<td>| Content first available</td>
<td>| Audited at</td>
<thead>
<th scope="col">Result </td>
<th scope="col">Sub-protocol </td>
<th scope="col">Strategy</td>
<th scope="col">Content Key</td>
<th scope="col">Content ID</td>
<th scope="col">Content first available</td>
<th scope="col">Audited at</td>
</thead>
</tr>
{% for (content, audit) in recent_content %}
<tr>
<td>| <span class="badge text-bg-{% if audit.is_success() %}success{% else %}danger{% endif %}">{% if audit.is_success() %}Success{% else %}Fail{% endif %}</span></td>
<td>| {{ content.protocol_id.as_text() }} </td>
<td>| {{ audit.strategy_as_text() }}</td>
<td>| <a href="/content/key/{{content.key_as_hex()}}">{{ content.key_as_hex_short() }}</a></td>
<td>| <a href="/content/id/{{content.id_as_hex()}}">{{ content.id_as_hex_short() }}</a></td>
<td>| {{ content.available_at_local_time() }}</td>
<td>| {{ audit.created_at_local_time() }}</td>
<td><span class="badge text-bg-{% if audit.is_success() %}success{% else %}danger{% endif %}">{%
if audit.is_success() %}Success{% else %}Fail{% endif %}</span></td>
<td>{{ content.protocol_id.as_text() }} </td>
<td>{{ audit.strategy_as_text() }}</td>
<td><a href="/content/key/{{content.key_as_hex()}}">{{ content.key_as_hex_short() }}</a></td>
<td><a href="/content/id/{{content.id_as_hex()}}">{{ content.id_as_hex_short() }}</a></td>
<td>{{ content.available_at_local_time() }}</td>
<td>{{ audit.created_at_local_time() }}</td>
</tr>
{% endfor %}
</table>
Expand All @@ -44,22 +47,22 @@ <h2> Recent content with audits</h2>
<div class="row">
<div class="col">
<ul>
<h2>Recent Content</h2>
<table>
<tr>
<td>| Sub-protocol </td>
<td>| Content Key</td>
<td>| Content ID</td>
<td>| Content first available</td>
</tr>
{% for content in contentid_list %}
<tr>
<td>| {{ content.protocol_id.as_text() }} </td>
<td>| <a href="/content/key/{{content.key_as_hex() }}">{{ content.key_as_hex_short() }}</a></td>
<td>| <a href="/content/id/{{content.id_as_hex() }}">{{ content.id_as_hex_short() }}</a></td>
<td>| {{ content.available_at_local_time() }}</td>
</tr>
{% endfor %}
<h2>Recent Content</h2>
<table class="table-sm">
<tr>
<td>| Sub-protocol </td>
<td>| Content Key</td>
<td>| Content ID</td>
<td>| Content first available</td>
</tr>
{% for content in contentid_list %}
<tr>
<td>| {{ content.protocol_id.as_text() }} </td>
<td>| <a href="/content/key/{{content.key_as_hex() }}">{{ content.key_as_hex_short() }}</a></td>
<td>| <a href="/content/id/{{content.id_as_hex() }}">{{ content.id_as_hex_short() }}</a></td>
<td>| {{ content.available_at_local_time() }}</td>
</tr>
{% endfor %}
</table>
</ul>
</div>
Expand All @@ -70,23 +73,24 @@ <h2>Recent Content</h2>
<h2> Recent audits</h2>
<table>
<tr>
<td>| Result </td>
<td>| Sub-protocol </td>
<td>| Strategy</td>
<td>| Content Key</td>
<td>| Content ID</td>
<td>| Content first available</td>
<td>| Audited at</td>
<td>| Result </td>
<td>| Sub-protocol </td>
<td>| Strategy</td>
<td>| Content Key</td>
<td>| Content ID</td>
<td>| Content first available</td>
<td>| Audited at</td>
</tr>
{% for (content, audit) in recent_audits %}
<tr>
<td>| <span class="badge text-bg-{% if audit.is_success() %}success{% else %}danger{% endif %}">{% if audit.is_success() %}Success{% else %}Fail{% endif %}</span></td>
<td>| {{ content.protocol_id.as_text() }} </td>
<td>| {{ audit.strategy_as_text() }}</td>
<td>| <a href="/content/key/{{content.key_as_hex()}}">{{ content.key_as_hex_short() }}</a></td>
<td>| <a href="/content/id/{{content.id_as_hex()}}">{{ content.id_as_hex_short() }}</a></td>
<td>| {{ content.available_at_local_time() }}</td>
<td>| {{ audit.created_at_local_time() }}</td>
<td>| <span class="badge text-bg-{% if audit.is_success() %}success{% else %}danger{% endif %}">{%
if audit.is_success() %}Success{% else %}Fail{% endif %}</span></td>
<td>| {{ content.protocol_id.as_text() }} </td>
<td>| {{ audit.strategy_as_text() }}</td>
<td>| <a href="/content/key/{{content.key_as_hex()}}">{{ content.key_as_hex_short() }}</a></td>
<td>| <a href="/content/id/{{content.id_as_hex()}}">{{ content.id_as_hex_short() }}</a></td>
<td>| {{ content.available_at_local_time() }}</td>
<td>| {{ audit.created_at_local_time() }}</td>
</tr>
{% endfor %}
</table>
Expand All @@ -99,23 +103,24 @@ <h2> Recent audits</h2>
<h2> Recent audit successes</h2>
<table>
<tr>
<td>| Result </td>
<td>| Sub-protocol </td>
<td>| Strategy</td>
<td>| Content Key</td>
<td>| Content ID</td>
<td>| Content first available</td>
<td>| Audited at</td>
<td>| Result </td>
<td>| Sub-protocol </td>
<td>| Strategy</td>
<td>| Content Key</td>
<td>| Content ID</td>
<td>| Content first available</td>
<td>| Audited at</td>
</tr>
{% for (content, audit) in recent_audit_successes %}
<tr>
<td>| <span class="badge text-bg-{% if audit.is_success() %}success{% else %}danger{% endif %}">{% if audit.is_success() %}Success{% else %}Fail{% endif %}</span></td>
<td>| {{ content.protocol_id.as_text() }} </td>
<td>| {{ audit.strategy_as_text() }}</td>
<td>| <a href="/content/key/{{content.key_as_hex()}}">{{ content.key_as_hex_short() }}</a></td>
<td>| <a href="/content/id/{{content.id_as_hex()}}">{{ content.id_as_hex_short() }}</a></td>
<td>| {{ content.available_at_local_time() }}</td>
<td>| {{ audit.created_at_local_time() }}</td>
<td>| <span class="badge text-bg-{% if audit.is_success() %}success{% else %}danger{% endif %}">{%
if audit.is_success() %}Success{% else %}Fail{% endif %}</span></td>
<td>| {{ content.protocol_id.as_text() }} </td>
<td>| {{ audit.strategy_as_text() }}</td>
<td>| <a href="/content/key/{{content.key_as_hex()}}">{{ content.key_as_hex_short() }}</a></td>
<td>| <a href="/content/id/{{content.id_as_hex()}}">{{ content.id_as_hex_short() }}</a></td>
<td>| {{ content.available_at_local_time() }}</td>
<td>| {{ audit.created_at_local_time() }}</td>
</tr>
{% endfor %}
</table>
Expand All @@ -128,23 +133,24 @@ <h2> Recent audit successes</h2>
<h2> Recent audit failures</h2>
<table>
<tr>
<td>| Result </td>
<td>| Sub-protocol </td>
<td>| Strategy</td>
<td>| Content Key</td>
<td>| Content ID</td>
<td>| Content first available</td>
<td>| Audited at</td>
<td>| Result </td>
<td>| Sub-protocol </td>
<td>| Strategy</td>
<td>| Content Key</td>
<td>| Content ID</td>
<td>| Content first available</td>
<td>| Audited at</td>
</tr>
{% for (content, audit) in recent_audit_failures %}
<tr>
<td>| <span class="badge text-bg-{% if audit.is_success() %}success{% else %}danger{% endif %}">{% if audit.is_success() %}Success{% else %}Fail{% endif %}</span></td>
<td>| {{ content.protocol_id.as_text() }} </td>
<td>| {{ audit.strategy_as_text() }}</td>
<td>| <a href="/content/key/{{content.key_as_hex()}}">{{ content.key_as_hex_short() }}</a></td>
<td>| <a href="/content/id/{{content.id_as_hex()}}">{{ content.id_as_hex_short() }}</a></td>
<td>| {{ content.available_at_local_time() }}</td>
<td>| {{ audit.created_at_local_time() }}</td>
<td>| <span class="badge text-bg-{% if audit.is_success() %}success{% else %}danger{% endif %}">{%
if audit.is_success() %}Success{% else %}Fail{% endif %}</span></td>
<td>| {{ content.protocol_id.as_text() }} </td>
<td>| {{ audit.strategy_as_text() }}</td>
<td>| <a href="/content/key/{{content.key_as_hex()}}">{{ content.key_as_hex_short() }}</a></td>
<td>| <a href="/content/id/{{content.id_as_hex()}}">{{ content.id_as_hex_short() }}</a></td>
<td>| {{ content.available_at_local_time() }}</td>
<td>| {{ audit.created_at_local_time() }}</td>
</tr>
{% endfor %}
</table>
Expand Down
47 changes: 28 additions & 19 deletions glados-web/templates/contentkey_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,40 @@
{% block title %}Content Key: {{ content_key_model.key_as_hex() }}{% endblock %}

{% block content %}
<div class="row">
<div class="row">
<h1>Content Key: {{ content_key_model.key_as_hex() }}</h1>
</div>
<div class="row">
</div>
<div class="row">
<div class="col">
<ul>
<li>Database ID: {{ content_key_model.id }}</li>
<li>Content Key: {{ content_key }}</li>
<li>Content Id: {{ content_id }}</li>
<li>Kind: {{ content_kind }}</li>
{% if block_number.is_some() %}
<ul>
<li>Database ID: {{ content_key_model.id }}</li>
<li>Content Key: {{ content_key }}</li>
<li>Content Id: {{ content_id }}</li>
<li>Kind: {{ content_kind }}</li>
{% if block_number.is_some() %}
<li>Block number: {{ block_number.unwrap() }}</li>
{% else %}{% endif %}
</ul>
{% else %}{% endif %}
</ul>
</div>
<div class="col">
<h3>Content Audits</h3>
<ul>
{% for content_audit in contentaudit_list %}
<li>Audit#: {{ content_audit.id }} <span
class="badge text-bg-{% if content_audit.is_success() %}success{% else %}danger{% endif %}">{% if
content_audit.is_success() %}Success{% else %}Fail{% endif %}</span></li>
{% else %}
<li>No audits</li>
{% endfor %}
</ul>
</div>
</div>
<div class="row">
<h1>Nodes received via gossip</h1>
<div class="col">
<h3>Content Audits</h3>
<ul>
{% for content_audit in contentaudit_list %}
<li>Audit#: {{ content_audit.id }} <span class="badge text-bg-{% if content_audit.is_success() %}success{% else %}danger{% endif %}">{% if content_audit.is_success() %}Success{% else %}Fail{% endif %}</span></li>
{% else %}
<li>No audits</li>
{% for gossip_record in gossip_records %}
<p>{{ gossip_record.receiving_node }}</p>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endblock %}