Compare commits

..

No commits in common. "db081869113e9b5b1b37675e49e9c75305f3f5f0" and "4a04612393cc391718e6ac441194e471d83e3416" have entirely different histories.

2 changed files with 39 additions and 73 deletions

View file

@ -7,37 +7,27 @@
<title>uppe.rs</title>
<style>
span.cell {
position: relative;
display: inline-block;
width: .5rem;
height: 1.2rem;
width: 1rem;
height: 1rem;
border: 1px solid var(--secondary);
font-size: 8pt;
line-height: 1.2rem;
font-size: 6pt;
line-height: 1rem;
background-color: rgba(var(--secondary-rgb), 0.4);
margin-top: .3rem;
margin-bottom: .3rem;
margin-top: .2rem;
margin-bottom: .2rem;
padding-top: 0;
padding-bottom: 0;
transition: .1s;
text-align: center;
cursor: default;
border-radius: .3rem;
color: #ffffff00;
}
span.cell:hover {
padding-top: .3rem;
padding-bottom: .3rem;
width: 2rem;
font-size: 8pt;
padding-top: .2rem;
padding-bottom: .2rem;
margin-top: 0;
margin-bottom: 0;
margin-right: -0.75rem;
margin-left: -0.75rem;
color: var(--background);
background-color: rgba(var(--secondary-rgb), 1);
z-index: 1;
font-weight: bold;
line-height: 1rem;
}
span.cell.warning {
border-color: var(--accent);
@ -46,9 +36,6 @@
border-color: var(--accent);
background-color: rgba(var(--accent-rgb), 0.4);
}
span.cell.error:hover {
background-color: rgba(var(--accent-rgb), 1);
}
hr.color {
color: var(--accent);
border-color: var(--accent);
@ -56,20 +43,9 @@
hr.separator {
margin: 2em;
}
div.card {
display: inline-block;
span.nobr {
white-space: nowrap;
overflow-x: scroll;
max-width: 100%;
margin-top: 2em;
border-radius: 1em;
border: 1px solid var(--background-secondary);
padding: 1em;
box-sizing: border-box;
transition: .3s;
}
div.card:hover {
background-color: var(--background-dim);
margin-right: 1em;
}
</style>
</head>
@ -77,7 +53,6 @@
<h1>uppe.rs</h1>
<p>keeping track of your infra's up status</p>
<hr class="color"/>
<small style="display: block" class="rev">now --&gt;</small>
<main id="uppe-rs-content">
@ -91,7 +66,7 @@ function cell(timestamp, rtt) {
warning = " warning";
}
if (rtt === null) {
return `<span class="cell error" title="${d}"></span>`;
return `<span class="cell error" title="${d}"></span>`;
} else {
return `<span class="cell${warning}" title="${rtt}ms -- ${d}">${rtt}</span>`;
}
@ -103,11 +78,12 @@ function card(key, history, last_rtt) {
bar += cell(el[0], el[1]);
}
return `<div class="card">
<h3 class="mt-0">${key} <code class="color">${last_rtt ? last_rtt + 'ms' : 'DOWN'}</code></h3>
<h3>${key} (${last_rtt}ms)</h3>
<div class="box">
${bar}
<span class="nobr">${bar}</span>
</div>
</div>`;
</div>
<hr class="separator"/>`;
}
let main = document.getElementById("uppe-rs-content");
@ -115,21 +91,15 @@ let main = document.getElementById("uppe-rs-content");
async function updateStatus() {
let res = await fetch("/api/status")
let status = await res.json()
if (status.error) {
console.error("server error:", status);
return;
}
let keys = Object.keys(status);
keys.sort();
let out = "";
for (let key of keys) {
let res = await fetch(`/api/status/${key}?limit=120`);
let res = await fetch(`/api/status/${key}`);
let history = await res.json();
out += card(key, history, status[key]);
out += "\n";
}
main.innerHTML = out;

View file

@ -65,11 +65,11 @@ async fn entry(cli: Cli, config: Config, db: Database) -> Result<(), Box<dyn std
for (key, service) in config.service {
let interval = service.interval_s.unwrap_or(default_interval);
let db = db.clone();
let sid = db.sid(&key, true).await?;
let sid = db.sid(&key).await?;
tokio::spawn(async move {
loop {
let res = test_route(&service.endpoint).await;
let res = test(&service.endpoint).await;
let value = match res {
Ok(rtt) => Some(rtt),
Err(e) => {
@ -98,15 +98,6 @@ async fn entry(cli: Cli, config: Config, db: Database) -> Result<(), Box<dyn std
Ok(())
}
async fn test_route(url: &str) -> reqwest::Result<i64> {
let before = chrono::Utc::now();
reqwest::get(url)
.await?
.error_for_status()?;
let delta = chrono::Utc::now() - before;
Ok(delta.num_milliseconds())
}
// ============= APIs
@ -138,6 +129,15 @@ async fn root() -> Html<&'static str> {
Html(include_str!("../index.html"))
}
async fn test(url: &str) -> reqwest::Result<i64> {
let before = chrono::Utc::now();
reqwest::get(url)
.await?
.error_for_status()?;
let delta = chrono::Utc::now() - before;
Ok(delta.num_milliseconds())
}
use axum::{extract::{Path, Query, State}, response::{Html, IntoResponse}, Json};
#[derive(serde::Deserialize)]
@ -172,8 +172,8 @@ async fn api_status_service(
Query(q): Query<ServiceStatusQuery>,
) -> ApiResult<Vec<(i64, Option<i64>)>> {
let limit = q.limit.unwrap_or(50).min(250);
let sid = db.sid(&service, false).await?;
Ok(Json(db.get(sid, limit).await?))
let sid = db.sid(&service).await?;
Ok(Json(db.get(sid, Some(limit)).await?))
}
@ -195,8 +195,8 @@ impl Database {
"CREATE TABLE IF NOT EXISTS events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
service INTEGER NOT NULL,
time BIGINT NOT NULL,
value BIGINT NULL
time BIG INTEGER NOT NULL,
value BIG INTEGER NULL
)", params![]
)?;
@ -224,7 +224,7 @@ impl Database {
async fn services(&self) -> rusqlite::Result<Vec<(i64, String)>> {
let db = self.0.lock().await;
let mut stmt = db.prepare("SELECT id, name FROM services")?;
let mut stmt = db.prepare("SELECT * FROM services")?;
let res = stmt.query_map(
params![],
|row| Ok((row.get(0)?, row.get(1)?))
@ -241,12 +241,12 @@ impl Database {
Ok(())
}
async fn get(&self, sid: i64, limit: i64) -> rusqlite::Result<Vec<Event>> {
async fn get(&self, sid: i64, limit: Option<i64>) -> rusqlite::Result<Vec<Event>> {
let db = self.0.lock().await;
let mut stmt = db.prepare("SELECT time, value FROM events WHERE service = :sid LIMIT :limit")?;
let results = stmt.query_map(
named_params! { ":sid": sid, ":limit": limit },
|row| Ok((row.get(0)?, row.get(1)?)),
|row| Ok((row.get(0)?, row.get(1).optional()?)),
)?;
Ok(
@ -257,7 +257,7 @@ impl Database {
}
#[async_recursion::async_recursion]
async fn sid(&self, service: &str, upsert: bool) -> rusqlite::Result<i64> {
async fn sid(&self, service: &str) -> rusqlite::Result<i64> {
let res = {
let db = self.0.lock().await;
let mut stmt = db.prepare("SELECT id FROM services WHERE name = ?")?;
@ -267,12 +267,8 @@ impl Database {
match res {
Some(sid) => Ok(sid),
None => {
if upsert {
self.0.lock().await.execute("INSERT INTO services(name) VALUES (?)", params![service])?;
self.sid(service, upsert).await
} else {
Err(rusqlite::Error::QueryReturnedNoRows)
}
self.sid(service).await
}
}
}
@ -282,7 +278,7 @@ impl Database {
let mut stmt = db.prepare("SELECT value FROM events WHERE service = :sid AND time > :time")?;
stmt.query_row(
named_params! { ":sid": sid, ":time": since },
|row| row.get::<usize, Option<i64>>(0)
|row| row.get(0).optional()
)
}
}