added static page

This commit is contained in:
Dejan Rožič 2026-04-17 09:57:32 +02:00
parent 45b565a672
commit c7057d7853

37
main.go
View file

@ -2,6 +2,7 @@ package main
import (
"database/sql"
"embed"
"encoding/json"
"errors"
"fmt"
@ -21,6 +22,9 @@ import (
"gopkg.in/yaml.v3"
)
//go:embed static
var staticFiles embed.FS
type Config struct {
Server ServerConfig `yaml:"server"`
PLC PLCConfig `yaml:"plc"`
@ -849,6 +853,8 @@ func main() {
go startDBCleanup(db)
go startPLCPoller()
// Serve embedded static assets (tailwind.min.js, chart.umd.min.js)
http.Handle("/static/", http.FileServer(http.FS(staticFiles)))
http.HandleFunc("/", serveUI)
http.HandleFunc("/api/data", apiData)
http.HandleFunc("/api/history", apiHistory)
@ -865,11 +871,9 @@ const uiHTML = `<!DOCTYPE html>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.Title}}</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<script src="/static/tailwind.min.js"></script>
<script src="/static/chart.umd.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Space+Grotesk:wght@500;600;700&display=swap');
:root {
--bg1: #050816;
--bg2: #0b1224;
@ -879,7 +883,7 @@ const uiHTML = `<!DOCTYPE html>
* { box-sizing: border-box; }
body {
font-family: 'Inter', system-ui, sans-serif;
font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
background:
radial-gradient(circle at 10% 10%, rgba(34,211,238,0.12), transparent 18%),
radial-gradient(circle at 90% 10%, rgba(168,85,247,0.14), transparent 18%),
@ -887,8 +891,6 @@ const uiHTML = `<!DOCTYPE html>
color: #f4f4f5;
}
.title { font-family: 'Space Grotesk', sans-serif; }
.glass {
background: var(--panel);
backdrop-filter: blur(14px);
@ -993,14 +995,14 @@ const uiHTML = `<!DOCTYPE html>
<div class="w-[92vw] max-w-[1800px] mx-auto p-4 md:p-8 min-h-screen">
<div id="alarm-banner" class="hidden mb-6 bg-red-600/90 border border-red-500 text-white px-8 py-4 rounded-2xl flex items-center justify-between text-lg font-medium">
<div class="flex items-center gap-3">
<span class="text-2xl"></span>
<span class="text-2xl">&#9888;&#65039;</span>
<span id="alarm-text">CRITICAL ALARM ACTIVE</span>
</div>
</div>
<div class="flex flex-col gap-6 xl:flex-row xl:items-center xl:justify-between mb-8">
<div>
<h1 class="title text-4xl md:text-5xl xl:text-6xl font-semibold tracking-tighter bg-gradient-to-r from-sky-300 to-violet-300 bg-clip-text text-transparent">{{.Title}}</h1>
<h1 class="text-4xl md:text-5xl xl:text-6xl font-semibold tracking-tighter bg-gradient-to-r from-sky-300 to-violet-300 bg-clip-text text-transparent">{{.Title}}</h1>
<p class="text-zinc-400 mt-2 text-base md:text-lg">{{.Subtitle}}</p>
<p class="text-zinc-500 mt-1 text-sm font-mono">MAX_TONNAGE = {{printf "%.1f" .MaxTonnage}} {{.UnitForce}}</p>
</div>
@ -1195,7 +1197,8 @@ const uiHTML = `<!DOCTYPE html>
};
}
function colorToCss(c, a = 1) {
function colorToCss(c, a) {
a = a === undefined ? 1 : a;
return 'rgba(' + c.r + ',' + c.g + ',' + c.b + ',' + a + ')';
}
@ -1241,7 +1244,8 @@ const uiHTML = `<!DOCTYPE html>
return colorMix(yellow, red, t);
}
function drawArc(ctx, cx, cy, r, a1, a2, stroke, width, shadowBlur = 0) {
function drawArc(ctx, cx, cy, r, a1, a2, stroke, width, shadowBlur) {
shadowBlur = shadowBlur || 0;
ctx.save();
ctx.beginPath();
ctx.arc(cx, cy, r, a1, a2, false);
@ -1292,7 +1296,6 @@ const uiHTML = `<!DOCTYPE html>
drawArc(ctx, cx, cy, radius, START_ANGLE, END_ANGLE, 'rgba(255,255,255,0.06)', trackWidth + 10, 0);
drawColoredBand(ctx, cx, cy, radius, trackWidth);
drawArc(ctx, cx, cy, radius, valueAngle, END_ANGLE, 'rgba(9,9,11,0.60)', trackWidth - 1, 0);
drawArc(ctx, cx, cy, radius, START_ANGLE, valueAngle, 'rgba(255,255,255,0.04)', trackWidth - 1, 10);
@ -1328,7 +1331,7 @@ const uiHTML = `<!DOCTYPE html>
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillStyle = 'rgba(244,244,245,0.96)';
ctx.font = '700 18px Inter, sans-serif';
ctx.font = '700 18px system-ui, sans-serif';
for (const v of labels) {
const a = valueToAngle(v);
@ -1386,15 +1389,15 @@ const uiHTML = `<!DOCTYPE html>
ctx.textBaseline = 'middle';
ctx.fillStyle = '#ffffff';
ctx.font = '700 ' + valueFontPx + 'px Space Grotesk, Inter, sans-serif';
ctx.font = '700 ' + valueFontPx + 'px system-ui, sans-serif';
ctx.fillText(valueText, cx, cy - 6);
ctx.fillStyle = sideAccent;
ctx.font = '700 18px Inter, sans-serif';
ctx.font = '700 18px system-ui, sans-serif';
ctx.fillText(UNIT_PCT, cx, cy + 28);
ctx.fillStyle = '#a1a1aa';
ctx.font = '600 16px Inter, sans-serif';
ctx.font = '600 16px system-ui, sans-serif';
ctx.fillText((Number(knValue) || 0).toFixed(1) + ' ' + UNIT_FORCE, cx, cy + 54);
}
@ -1529,7 +1532,7 @@ const uiHTML = `<!DOCTYPE html>
parts.push('IMBALANCE');
}
text.textContent = 'CRITICAL ALARM ACTIVE ' + parts.join(' ');
text.textContent = 'CRITICAL ALARM ACTIVE \u2022 ' + parts.join(' \u2022 ');
banner.classList.remove('hidden');
}