fix: make the badge look like badge
This commit is contained in:
parent
87d3fee588
commit
2374e3cd60
2 changed files with 38 additions and 10 deletions
|
|
@ -11,11 +11,24 @@ If the Pi stops responding to polls (configurable threshold, default 3 misses),
|
|||
| Method | Path | Auth | Description |
|
||||
|--------|------|------|-------------|
|
||||
| `GET` | `/status` | — | Current door status (`status`, `since`, `last_checked`) |
|
||||
| `GET` | `/badge.svg` | — | Live README badge with Noisebridge logo |
|
||||
| `POST` | `/webhook` | Bearer | Inbound webhook from the Pi |
|
||||
| `GET` | `/health` | — | Health check |
|
||||
|
||||
`since` is the Pi-reported time when the current state began. `last_checked` is when the cache most recently attempted a poll.
|
||||
|
||||
## Badge
|
||||
|
||||
`/badge.svg` serves a classic shields.io-style SVG badge with the Noisebridge logo and the current cache status (`open`, `closed`, or `offline`).
|
||||
|
||||
Use it in a GitHub README like this:
|
||||
|
||||
```md
|
||||
[](https://your-cache-domain.example.com/status)
|
||||
```
|
||||
|
||||
That keeps the badge clickable and sends readers to the live `/status` endpoint.
|
||||
|
||||
## Configuration
|
||||
|
||||
NixOS options under `services.noisebell-cache`:
|
||||
|
|
|
|||
|
|
@ -19,6 +19,12 @@ static CLOSED_PNG: &[u8] = include_bytes!("../assets/closed.png");
|
|||
static OFFLINE_PNG: &[u8] = include_bytes!("../assets/offline.png");
|
||||
|
||||
const BADGE_LABEL: &str = "space";
|
||||
const BADGE_HEIGHT: usize = 20;
|
||||
const BADGE_LOGO_WIDTH: usize = 21;
|
||||
const BADGE_LEFT_PADDING: usize = 6;
|
||||
const BADGE_RIGHT_PADDING: usize = 10;
|
||||
const BADGE_FONT_FAMILY: &str = "Verdana,Geneva,DejaVu Sans,sans-serif";
|
||||
const BADGE_LOGO_PATH: &str = "M215.863,155.875V65.776l-8.2,5.819l-22.357,15.869h-5.008V56.782c0.002-5.218-2.145-9.984-5.566-13.397c-3.412-3.421-8.177-5.567-13.396-5.565h-25.241c-1.08-6-3.964-11.391-8.092-15.517c-5.284-5.292-12.638-8.58-20.709-8.577c-8.072-0.003-15.427,3.286-20.71,8.579c-4.13,4.124-7.012,9.515-8.09,15.515H53.25c-5.218-0.001-9.983,2.144-13.396,5.565c-3.421,3.413-5.566,8.179-5.565,13.397v15.939L0.498,81.89l39.388,11.699L1.257,105.063l38.629,11.471L1.257,128.007L39.95,139.5l-5.661,1.694v23.675c-0.001,5.22,2.145,9.985,5.565,13.398c3.414,3.42,8.179,5.566,13.397,5.564h35.144v-5.194c0.001-5.234,2.105-9.927,5.533-13.366c3.437-3.429,8.129-5.533,13.365-5.533c5.234,0.004,9.927,2.107,13.362,5.533c3.429,3.439,5.533,8.132,5.536,13.366v5.194h35.143c5.221,0.002,9.985-2.145,13.397-5.564c3.421-3.416,5.566-8.181,5.566-13.398v-30.688h5.006L215.863,155.875z M192.152,126.306V95.344l13.321-9.455v49.872L192.152,126.306z M181.764,123.796h-21.126V97.854h21.126V123.796z M169.908,164.869c-0.002,2.356-0.954,4.476-2.523,6.053c-1.575,1.57-3.696,2.52-6.051,2.521h-25.241c-1.078-6.002-3.962-11.394-8.091-15.517c-5.287-5.29-12.641-8.58-20.71-8.576c-8.072-0.004-15.426,3.285-20.711,8.576c-4.128,4.123-7.012,9.515-8.09,15.517H53.25c-2.354-0.002-4.473-0.952-6.05-2.521c-1.57-1.577-2.521-3.699-2.523-6.053v-15.94l31.632-9.469l-38.564-11.453l38.628-11.473l-38.628-11.471l38.628-11.474L38.504,82.341l6.173-1.674V56.782c0.002-2.354,0.953-4.473,2.523-6.05c1.577-1.57,3.697-2.522,6.05-2.523h35.145v-5.194c0.001-5.236,2.105-9.927,5.533-13.364c3.437-3.428,8.129-5.532,13.364-5.535c5.235,0.001,9.925,2.107,13.361,5.535c3.431,3.438,5.535,8.13,5.535,13.364v5.194h35.145c2.354,0.001,4.474,0.953,6.051,2.523c1.57,1.578,2.522,3.696,2.522,6.05v30.684h-19.66v46.719h19.66v30.685H169.908z";
|
||||
|
||||
fn escape_xml(text: &str) -> String {
|
||||
text.replace('&', "&")
|
||||
|
|
@ -37,21 +43,22 @@ fn badge_color(status: DoorStatus) -> &'static str {
|
|||
}
|
||||
|
||||
fn badge_text_width(text: &str) -> usize {
|
||||
text.chars().count() * 7 + 10
|
||||
text.chars().count() * 7 + BADGE_RIGHT_PADDING
|
||||
}
|
||||
|
||||
fn render_badge_svg(status: DoorStatus, summary: &str) -> String {
|
||||
let message = status.as_str();
|
||||
let label_width = badge_text_width(BADGE_LABEL);
|
||||
let label_width = BADGE_LEFT_PADDING + BADGE_LOGO_WIDTH + badge_text_width(BADGE_LABEL);
|
||||
let message_width = badge_text_width(message);
|
||||
let total_width = label_width + message_width;
|
||||
let label_center = label_width / 2;
|
||||
let label_text_x = BADGE_LEFT_PADDING + BADGE_LOGO_WIDTH;
|
||||
let label_center = label_text_x + (badge_text_width(BADGE_LABEL) / 2);
|
||||
let message_center = label_width + (message_width / 2);
|
||||
let escaped_summary = escape_xml(summary);
|
||||
|
||||
format!(
|
||||
concat!(
|
||||
"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{total_width}\" height=\"20\" role=\"img\" aria-label=\"{label}: {message}\">",
|
||||
"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{total_width}\" height=\"{height}\" role=\"img\" aria-label=\"space status: {message}\">",
|
||||
"<title>{summary}</title>",
|
||||
"<linearGradient id=\"b\" x2=\"0\" y2=\"100%\">",
|
||||
"<stop offset=\"0\" stop-color=\"#fff\" stop-opacity=\".7\"/>",
|
||||
|
|
@ -59,13 +66,16 @@ fn render_badge_svg(status: DoorStatus, summary: &str) -> String {
|
|||
"<stop offset=\".9\" stop-opacity=\".3\"/>",
|
||||
"<stop offset=\"1\" stop-opacity=\".5\"/>",
|
||||
"</linearGradient>",
|
||||
"<mask id=\"a\"><rect width=\"{total_width}\" height=\"20\" rx=\"3\" fill=\"#fff\"/></mask>",
|
||||
"<mask id=\"a\"><rect width=\"{total_width}\" height=\"{height}\" rx=\"3\" fill=\"#fff\"/></mask>",
|
||||
"<g mask=\"url(#a)\">",
|
||||
"<rect width=\"{label_width}\" height=\"20\" fill=\"#555\"/>",
|
||||
"<rect x=\"{label_width}\" width=\"{message_width}\" height=\"20\" fill=\"{color}\"/>",
|
||||
"<rect width=\"{total_width}\" height=\"20\" fill=\"url(#b)\"/>",
|
||||
"<rect width=\"{label_width}\" height=\"{height}\" fill=\"#555\"/>",
|
||||
"<rect x=\"{label_width}\" width=\"{message_width}\" height=\"{height}\" fill=\"{color}\"/>",
|
||||
"<rect width=\"{total_width}\" height=\"{height}\" fill=\"url(#b)\"/>",
|
||||
"</g>",
|
||||
"<g fill=\"#fff\" text-anchor=\"middle\" font-family=\"{font_family}\" font-size=\"11\">",
|
||||
"<g transform=\"translate({logo_x} 3) scale(0.055)\">",
|
||||
"<path fill=\"#eb2026\" d=\"{logo_path}\"/>",
|
||||
"</g>",
|
||||
"<g fill=\"#fff\" text-anchor=\"middle\" font-family=\"Verdana,Geneva,DejaVu Sans,sans-serif\" font-size=\"11\">",
|
||||
"<text x=\"{label_center}\" y=\"15\" fill=\"#010101\" fill-opacity=\".3\">{label}</text>",
|
||||
"<text x=\"{label_center}\" y=\"14\">{label}</text>",
|
||||
"<text x=\"{message_center}\" y=\"15\" fill=\"#010101\" fill-opacity=\".3\">{message}</text>",
|
||||
|
|
@ -73,12 +83,16 @@ fn render_badge_svg(status: DoorStatus, summary: &str) -> String {
|
|||
"</g></svg>",
|
||||
),
|
||||
total_width = total_width,
|
||||
height = BADGE_HEIGHT,
|
||||
label = BADGE_LABEL,
|
||||
message = message,
|
||||
summary = escaped_summary,
|
||||
label_width = label_width,
|
||||
message_width = message_width,
|
||||
color = badge_color(status),
|
||||
font_family = BADGE_FONT_FAMILY,
|
||||
logo_x = BADGE_LEFT_PADDING,
|
||||
logo_path = BADGE_LOGO_PATH,
|
||||
label_center = label_center,
|
||||
message_center = message_center,
|
||||
)
|
||||
|
|
@ -364,10 +378,11 @@ mod tests {
|
|||
fn render_badge_svg_includes_status_and_summary() {
|
||||
let svg = render_badge_svg(DoorStatus::Offline, "Cache can't reach the space right now.");
|
||||
|
||||
assert!(svg.contains("aria-label=\"space: offline\""));
|
||||
assert!(svg.contains("aria-label=\"space status: offline\""));
|
||||
assert!(svg.contains(">space<"));
|
||||
assert!(svg.contains(">offline<"));
|
||||
assert!(svg.contains("Cache can't reach the space right now."));
|
||||
assert!(svg.contains("#9f9f9f"));
|
||||
assert!(svg.contains("#eb2026"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue