mirror of
https://github.com/sergi0g/cup.git
synced 2025-11-08 05:03:49 -05:00
Add liquid again for static rendering, fix #21 and make some small frontend changes
This commit is contained in:
154
Cargo.lock
generated
154
Cargo.lock
generated
@@ -90,6 +90,12 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anymap2"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
@@ -344,7 +350,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cup"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
dependencies = [
|
||||
"bollard",
|
||||
"chrono",
|
||||
@@ -352,6 +358,7 @@ dependencies = [
|
||||
"http-auth",
|
||||
"indicatif",
|
||||
"json",
|
||||
"liquid",
|
||||
"once_cell",
|
||||
"rayon",
|
||||
"regex",
|
||||
@@ -381,6 +388,12 @@ dependencies = [
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.13.0"
|
||||
@@ -755,6 +768,15 @@ version = "1.70.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
@@ -776,6 +798,16 @@ version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
|
||||
|
||||
[[package]]
|
||||
name = "kstring"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
@@ -788,6 +820,63 @@ version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "liquid"
|
||||
version = "0.26.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cdcc72b82748f47c2933c172313f5a9aea5b2c4eb3fa4c66b4ea55bb60bb4b1"
|
||||
dependencies = [
|
||||
"doc-comment",
|
||||
"liquid-core",
|
||||
"liquid-derive",
|
||||
"liquid-lib",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "liquid-core"
|
||||
version = "0.26.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2752e978ffc53670f3f2e8b3ef09f348d6f7b5474a3be3f8a5befe5382e4effb"
|
||||
dependencies = [
|
||||
"anymap2",
|
||||
"itertools",
|
||||
"kstring",
|
||||
"liquid-derive",
|
||||
"num-traits",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"regex",
|
||||
"serde",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "liquid-derive"
|
||||
version = "0.26.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b51f1d220e3fa869e24cfd75915efe3164bd09bb11b3165db3f37f57bf673e3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "liquid-lib"
|
||||
version = "0.26.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59b1a298d3d2287ee5b1e43840d885b8fdfc37d3f4e90d82aacfd04d021618da"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"liquid-core",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"regex",
|
||||
"time",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.22"
|
||||
@@ -898,6 +987,51 @@ version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.7.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"thiserror",
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.7.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "664d22978e2815783adbdd2c588b455b1bd625299ce36b2a99881ac9627e6d8d"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.7.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2d5487022d5d33f4c30d91c22afa240ce2a644e87fe08caad974d4eab6badbe"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.7.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0091754bbd0ea592c4deb3a122ce8ecbb0753b738aa82bc055fcc2eccc8d8174"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"pest",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.5"
|
||||
@@ -1235,6 +1369,12 @@ version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
@@ -1483,6 +1623,12 @@ version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.15"
|
||||
@@ -1504,6 +1650,12 @@ dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.13"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "cup"
|
||||
version = "2.1.0"
|
||||
version = "2.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@@ -10,6 +10,7 @@ tokio = {version = "1.38.0", features = ["rt", "rt-multi-thread", "macros"]}
|
||||
ureq = { version = "2.9.7", features = ["tls"] }
|
||||
rayon = "1.10.0"
|
||||
xitca-web = { version = "0.5.0", optional = true, features = ["logger"] }
|
||||
liquid = { version = "0.26.6", optional = true }
|
||||
bollard = "0.16.1"
|
||||
once_cell = "1.19.0"
|
||||
http-auth = { version = "0.1.9", features = [] }
|
||||
@@ -20,7 +21,7 @@ json = "0.12.4"
|
||||
|
||||
[features]
|
||||
default = ["server", "cli"]
|
||||
server = ["dep:xitca-web", "dep:chrono"]
|
||||
server = ["dep:xitca-web", "dep:liquid", "dep:chrono"]
|
||||
cli = ["dep:indicatif", "dep:termsize"]
|
||||
|
||||
[profile.release]
|
||||
|
||||
@@ -34,7 +34,7 @@ pub async fn get_images_from_docker_daemon(socket: Option<String>) -> Vec<Image>
|
||||
};
|
||||
let mut result: Vec<Image> = Vec::new();
|
||||
for image in images {
|
||||
if !image.repo_tags.is_empty() && image.repo_digests.len() == 1 {
|
||||
if !image.repo_tags.is_empty() && !image.repo_digests.is_empty() {
|
||||
for t in &image.repo_tags {
|
||||
let (registry, repository, tag) = split_image(t);
|
||||
result.push(Image {
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::sync::Arc;
|
||||
|
||||
use chrono::Local;
|
||||
use json::JsonValue;
|
||||
use liquid::{object, Object};
|
||||
use tokio::sync::Mutex;
|
||||
use xitca_web::{
|
||||
body::ResponseBody,
|
||||
@@ -45,7 +46,7 @@ async fn _static(data: StateRef<'_, Arc<Mutex<ServerData>>>, path: PathRef<'_>)
|
||||
match path.0 {
|
||||
"/" => WebResponse::builder()
|
||||
.header("Content-Type", "text/html")
|
||||
.body(ResponseBody::from(HTML))
|
||||
.body(ResponseBody::from(data.lock().await.template.clone()))
|
||||
.unwrap(),
|
||||
"/assets/index.js" => WebResponse::builder()
|
||||
.header("Content-Type", "text/javascript")
|
||||
@@ -89,6 +90,7 @@ async fn refresh(data: StateRef<'_, Arc<Mutex<ServerData>>>) -> WebResponse {
|
||||
}
|
||||
|
||||
struct ServerData {
|
||||
template: String,
|
||||
raw_updates: Vec<(String, Option<bool>)>,
|
||||
json: JsonValue,
|
||||
socket: Option<String>,
|
||||
@@ -100,6 +102,7 @@ impl ServerData {
|
||||
async fn new(socket: Option<String>, config: JsonValue) -> Self {
|
||||
let mut s = Self {
|
||||
socket,
|
||||
template: String::new(),
|
||||
json: json::object! {
|
||||
metrics: json::object! {},
|
||||
images: json::object! {},
|
||||
@@ -116,9 +119,22 @@ impl ServerData {
|
||||
&get_all_updates(self.socket.clone(), &self.config["authentication"]).await,
|
||||
);
|
||||
self.raw_updates = updates;
|
||||
let template = liquid::ParserBuilder::with_stdlib()
|
||||
.build()
|
||||
.unwrap()
|
||||
.parse(HTML)
|
||||
.unwrap();
|
||||
let images = self
|
||||
.raw_updates
|
||||
.iter()
|
||||
.map(|(name, has_update)| match has_update {
|
||||
Some(v) => object!({"name": name, "has_update": v.to_string()}), // Liquid kinda thinks false == nil, so we'll be comparing strings from now on
|
||||
None => object!({"name": name, "has_update": "null"}),
|
||||
})
|
||||
.collect::<Vec<Object>>();
|
||||
self.json = to_json(&self.raw_updates);
|
||||
let last_updated = Local::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true);
|
||||
self.json["last_updated"] = last_updated.to_string().into();
|
||||
let last_updated = Local::now();
|
||||
self.json["last_updated"] = last_updated.to_rfc3339_opts(chrono::SecondsFormat::Secs, true).to_string().into();
|
||||
self.theme = match &self.config["theme"].as_str() {
|
||||
Some(t) => match *t {
|
||||
"default" => "neutral",
|
||||
@@ -130,5 +146,12 @@ impl ServerData {
|
||||
},
|
||||
None => "neutral",
|
||||
};
|
||||
let globals = object!({
|
||||
"metrics": [{"name": "Monitored images", "value": self.json["metrics"]["monitored_images"].as_usize()}, {"name": "Up to date", "value": self.json["metrics"]["up_to_date"].as_usize()}, {"name": "Updates available", "value": self.json["metrics"]["update_available"].as_usize()}, {"name": "Unknown", "value": self.json["metrics"]["unknown"].as_usize()}],
|
||||
"images": images,
|
||||
"last_updated": last_updated.format("%Y-%m-%d %H:%M:%S").to_string(),
|
||||
"theme": &self.theme
|
||||
});
|
||||
self.template = template.render(&globals).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
259
web/index.html
259
web/index.html
@@ -10,7 +10,264 @@
|
||||
<title>Cup</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<div id="root">
|
||||
<div
|
||||
class="flex justify-center items-center min-h-screen bg-{{ theme }}-50 dark:bg-{{ theme }}-950"
|
||||
>
|
||||
<div class="lg:px-8 sm:px-6 px-4 max-w-[80rem] mx-auto h-full w-full">
|
||||
<div class="max-w-[48rem] mx-auto h-full my-8">
|
||||
<div class="flex items-center gap-1">
|
||||
<h1 class="text-5xl lg:text-6xl font-bold dark:text-white">
|
||||
Cup
|
||||
</h1>
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Layer_2"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 128 128"
|
||||
style="enable-background: new 0 0 128 128"
|
||||
xml:space="preserve"
|
||||
class="size-14 lg:size-16"
|
||||
>
|
||||
<path
|
||||
style="fill: #a6cfd6"
|
||||
d="M65.12,17.55c-17.6-0.53-34.75,5.6-34.83,14.36c-0.04,5.2,1.37,18.6,3.62,48.68s2.25,33.58,3.5,34.95
|
||||
c1.25,1.37,10.02,8.8,25.75,8.8s25.93-6.43,26.93-8.05c0.48-0.78,1.83-17.89,3.5-37.07c1.81-20.84,3.91-43.9,3.99-45.06
|
||||
C97.82,30.66,94.2,18.43,65.12,17.55z"
|
||||
/>
|
||||
<path
|
||||
style="fill: #dcedf6"
|
||||
d="M41.4,45.29c-0.12,0.62,1.23,24.16,2.32,27.94c1.99,6.92,9.29,7.38,10.23,4.16
|
||||
c0.9-3.07-0.38-29.29-0.38-29.29s-3.66-0.3-6.43-0.84C44,46.63,41.4,45.29,41.4,45.29z"
|
||||
/>
|
||||
<path
|
||||
style="fill: #6ca4ae"
|
||||
d="M33.74,32.61c-0.26,8.83,20.02,12.28,30.19,12.22c13.56-0.09,29.48-4.29,29.8-11.7
|
||||
S79.53,21.1,63.35,21.1C49.6,21.1,33.96,25.19,33.74,32.61z"
|
||||
/>
|
||||
<path
|
||||
style="fill: #dc0d27"
|
||||
d="M84.85,13.1c-0.58,0.64-9.67,30.75-9.67,30.75s2.01-0.33,4-0.79c2.63-0.61,3.76-1.06,3.76-1.06
|
||||
s7.19-22.19,7.64-23.09c0.45-0.9,21.61-7.61,22.31-7.93c0.7-0.32,1.39-0.4,1.46-0.78c0.06-0.38-2.34-6.73-3.11-6.73
|
||||
C110.47,3.47,86.08,11.74,84.85,13.1z"
|
||||
/>
|
||||
<path
|
||||
style="fill: #8a1f0f"
|
||||
d="M110.55,7.79c1.04,2.73,2.8,3.09,3.55,2.77c0.45-0.19,1.25-1.84,0.01-4.47
|
||||
c-0.99-2.09-2.17-2.74-2.93-2.61C110.42,3.6,109.69,5.53,110.55,7.79z"
|
||||
/>
|
||||
<g>
|
||||
<path
|
||||
style="fill: #8a1f0f"
|
||||
d="M91.94,18.34c-0.22,0-0.44-0.11-0.58-0.3l-3.99-5.77c-0.22-0.32-0.14-0.75,0.18-0.97
|
||||
c0.32-0.22,0.76-0.14,0.97,0.18l3.99,5.77c0.22,0.32,0.14,0.75-0.18,0.97C92.21,18.3,92.07,18.34,91.94,18.34z"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
style="fill: #8a1f0f"
|
||||
d="M90.28,19.43c-0.18,0-0.35-0.07-0.49-0.2l-5.26-5.12c-0.28-0.27-0.28-0.71-0.01-0.99
|
||||
c0.27-0.28,0.71-0.28,0.99-0.01l5.26,5.12c0.28,0.27,0.28,0.71,0.01,0.99C90.64,19.36,90.46,19.43,90.28,19.43z"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path
|
||||
style="fill: #8a1f0f"
|
||||
d="M89.35,21.22c-0.12,0-0.25-0.03-0.36-0.1l-5.6-3.39c-0.33-0.2-0.44-0.63-0.24-0.96
|
||||
c0.2-0.33,0.63-0.44,0.96-0.24l5.6,3.39c0.33,0.2,0.44,0.63,0.24,0.96C89.82,21.1,89.59,21.22,89.35,21.22z"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
<div
|
||||
class="shadow-sm bg-white dark:bg-{{ theme }}-900 rounded-md my-8"
|
||||
>
|
||||
<dl
|
||||
class="lg:grid-cols-4 grid-cols-2 gap-1 grid overflow-hidden *:relative"
|
||||
>
|
||||
{% for metric in metrics %}
|
||||
<div class="gi">
|
||||
<div
|
||||
class="xl:px-8 px-6 py-4 gap-y-2 gap-x-4 justify-between align-baseline flex flex-col h-full"
|
||||
>
|
||||
<dt
|
||||
class="text-{{ theme }}-500 dark:text-{{ theme }}-400 leading-6 font-medium"
|
||||
>
|
||||
{{ metric.name }}
|
||||
</dt>
|
||||
<div class="flex gap-1 justify-between items-center">
|
||||
<dd
|
||||
class="text-black dark:text-white tracking-tight leading-10 font-medium text-3xl w-full"
|
||||
>
|
||||
{{ metric.value }}
|
||||
</dd>
|
||||
{% if metric.name == 'Monitored images' %}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-6 text-black dark:text-white shrink-0"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M12 4c4.29 0 7.863 2.429 10.665 7.154l.22 .379l.045 .1l.03 .083l.014 .055l.014 .082l.011 .1v.11l-.014 .111a.992 .992 0 0 1 -.026 .11l-.039 .108l-.036 .075l-.016 .03c-2.764 4.836 -6.3 7.38 -10.555 7.499l-.313 .004c-4.396 0 -8.037 -2.549 -10.868 -7.504a1 1 0 0 1 0 -.992c2.831 -4.955 6.472 -7.504 10.868 -7.504zm0 5a3 3 0 1 0 0 6a3 3 0 0 0 0 -6z"
|
||||
/>
|
||||
</svg>
|
||||
{% elsif metric.name == 'Up to date' %}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-6 text-green-500 shrink-0"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M17 3.34a10 10 0 1 1 -14.995 8.984l-.005 -.324l.005 -.324a10 10 0 0 1 14.995 -8.336zm-1.293 5.953a1 1 0 0 0 -1.32 -.083l-.094 .083l-3.293 3.292l-1.293 -1.292l-.094 -.083a1 1 0 0 0 -1.403 1.403l.083 .094l2 2l.094 .083a1 1 0 0 0 1.226 0l.094 -.083l4 -4l.083 -.094a1 1 0 0 0 -.083 -1.32z"
|
||||
/>
|
||||
</svg>
|
||||
{% elsif metric.name == 'Updates available' %}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-6 text-blue-500 shrink-0"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M17 3.34a10 10 0 1 1 -14.995 8.984l-.005 -.324l.005 -.324a10 10 0 0 1 14.995 -8.336zm-4.98 3.66l-.163 .01l-.086 .016l-.142 .045l-.113 .054l-.07 .043l-.095 .071l-.058 .054l-4 4l-.083 .094a1 1 0 0 0 1.497 1.32l2.293 -2.293v5.586l.007 .117a1 1 0 0 0 1.993 -.117v-5.585l2.293 2.292l.094 .083a1 1 0 0 0 1.32 -1.497l-4 -4l-.082 -.073l-.089 -.064l-.113 -.062l-.081 -.034l-.113 -.034l-.112 -.02l-.098 -.006z"
|
||||
/>
|
||||
</svg>
|
||||
{% elsif metric.name == 'Unknown' %}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-6 text-{{ theme }}-500 shrink-0"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M12 2c5.523 0 10 4.477 10 10a10 10 0 0 1 -19.995 .324l-.005 -.324l.004 -.28c.148 -5.393 4.566 -9.72 9.996 -9.72zm0 13a1 1 0 0 0 -.993 .883l-.007 .117l.007 .127a1 1 0 0 0 1.986 0l.007 -.117l-.007 -.127a1 1 0 0 0 -.993 -.883zm1.368 -6.673a2.98 2.98 0 0 0 -3.631 .728a1 1 0 0 0 1.44 1.383l.171 -.18a.98 .98 0 0 1 1.11 -.15a1 1 0 0 1 -.34 1.886l-.232 .012a1 1 0 0 0 .111 1.994a3 3 0 0 0 1.371 -5.673z"
|
||||
/>
|
||||
</svg>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
</div>
|
||||
<div
|
||||
class="shadow-sm bg-white dark:bg-{{ theme }}-900 rounded-md my-8"
|
||||
>
|
||||
<div
|
||||
class="flex justify-between items-center px-6 py-4 text-{{ theme }}-500"
|
||||
>
|
||||
<h3>Last checked: {{ last_updated }}</h3>
|
||||
<button class="group" onclick="refresh(event)">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="group-disabled:animate-spin"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M4,11A8.1,8.1 0 0 1 19.5,9M20,5v4h-4" />
|
||||
<path d="M20,13A8.1,8.1 0 0 1 4.5,15M4,19v-4h4" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<ul
|
||||
class="*:py-4 *:px-6 *:flex *:items-center *:gap-3 dark:divide-{{ theme }}-800 divide-y dark:text-white"
|
||||
>
|
||||
{% for image in images %}
|
||||
<li>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M21 16.008v-8.018a1.98 1.98 0 0 0 -1 -1.717l-7 -4.008a2.016 2.016 0 0 0 -2 0l-7 4.008c-.619 .355 -1 1.01 -1 1.718v8.018c0 .709 .381 1.363 1 1.717l7 4.008a2.016 2.016 0 0 0 2 0l7 -4.008c.619 -.355 1 -1.01 1 -1.718z"
|
||||
/>
|
||||
<path d="M12 22v-10" />
|
||||
<path d="M12 12l8.73 -5.04" />
|
||||
<path d="M3.27 6.96l8.73 5.04" />
|
||||
</svg>
|
||||
{{ image.name }} {% if image.has_update == 'false' %}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="text-green-500 ml-auto"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M17 3.34a10 10 0 1 1 -14.995 8.984l-.005 -.324l.005 -.324a10 10 0 0 1 14.995 -8.336zm-1.293 5.953a1 1 0 0 0 -1.32 -.083l-.094 .083l-3.293 3.292l-1.293 -1.292l-.094 -.083a1 1 0 0 0 -1.403 1.403l.083 .094l2 2l.094 .083a1 1 0 0 0 1.226 0l.094 -.083l4 -4l.083 -.094a1 1 0 0 0 -.083 -1.32z"
|
||||
/>
|
||||
</svg>
|
||||
{% elsif image.has_update == 'true' %}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="text-blue-500 ml-auto"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M17 3.34a10 10 0 1 1 -14.995 8.984l-.005 -.324l.005 -.324a10 10 0 0 1 14.995 -8.336zm-4.98 3.66l-.163 .01l-.086 .016l-.142 .045l-.113 .054l-.07 .043l-.095 .071l-.058 .054l-4 4l-.083 .094a1 1 0 0 0 1.497 1.32l2.293 -2.293v5.586l.007 .117a1 1 0 0 0 1.993 -.117v-5.585l2.293 2.292l.094 .083a1 1 0 0 0 1.32 -1.497l-4 -4l-.082 -.073l-.089 -.064l-.113 -.062l-.081 -.034l-.113 -.034l-.112 -.02l-.098 -.006z"
|
||||
/>
|
||||
</svg>
|
||||
{% elsif image.has_update == 'null' %}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="text-{{ theme }}-500 ml-auto"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path
|
||||
d="M12 2c5.523 0 10 4.477 10 10a10 10 0 0 1 -19.995 .324l-.005 -.324l.004 -.28c.148 -5.393 4.566 -9.72 9.996 -9.72zm0 13a1 1 0 0 0 -.993 .883l-.007 .117l.007 .127a1 1 0 0 0 1.986 0l.007 -.117l-.007 -.127a1 1 0 0 0 -.993 -.883zm1.368 -6.673a2.98 2.98 0 0 0 -3.631 .728a1 1 0 0 0 1.44 1.383l.171 -.18a.98 .98 0 0 1 1.11 -.15a1 1 0 0 1 -.34 1.886l-.232 .012a1 1 0 0 0 .111 1.994a3 3 0 0 0 1.371 -5.673z"
|
||||
/>
|
||||
</svg>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -2,7 +2,6 @@ import { MouseEvent, useState } from "react";
|
||||
import Logo from "./components/Logo";
|
||||
import Statistic from "./components/Statistic";
|
||||
import Image from "./components/Image";
|
||||
import { IconRefresh } from "@tabler/icons-react";
|
||||
import { LastChecked } from "./components/LastChecked";
|
||||
import Loading from "./components/Loading";
|
||||
import { Data } from "./types";
|
||||
@@ -16,7 +15,7 @@ function App() {
|
||||
btn.disabled = true;
|
||||
|
||||
let request = new XMLHttpRequest();
|
||||
request.onload = function () {
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
window.location.reload();
|
||||
}
|
||||
@@ -24,8 +23,8 @@ function App() {
|
||||
request.open(
|
||||
"GET",
|
||||
process.env.NODE_ENV === "production"
|
||||
? "/json"
|
||||
: `http://${window.location.hostname}:8000/json`,
|
||||
? "/refresh"
|
||||
: `http://${window.location.hostname}:8000/refresh`
|
||||
);
|
||||
request.send();
|
||||
};
|
||||
@@ -58,7 +57,22 @@ function App() {
|
||||
>
|
||||
<LastChecked datetime={data.last_updated} />
|
||||
<button className="group" onClick={refresh}>
|
||||
<IconRefresh className="-scale-x-100 group-disabled:animate-spin" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
className="group-disabled:animate-spin"
|
||||
>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M4,11A8.1,8.1 0 0 1 19.5,9M20,5v4h-4" />
|
||||
<path d="M20,13A8.1,8.1 0 0 1 4.5,15M4,19v-4h4" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<ul
|
||||
|
||||
Reference in New Issue
Block a user