feat(web): add automated build version tracking and update notification banner
This commit is contained in:
@@ -8,7 +8,7 @@ build: build-web build-server
|
||||
|
||||
# Build the Go server
|
||||
build-server:
|
||||
CGO_ENABLED=0 go build -o dumpster-server ./cmd/server
|
||||
CGO_ENABLED=0 go build -ldflags "-X main.GitSHA=$(GIT_SHA)" -o dumpster-server ./cmd/server
|
||||
|
||||
# Build the TUI client
|
||||
build-tui:
|
||||
|
||||
+10
-3
@@ -47,9 +47,8 @@ import (
|
||||
// @host localhost:8080
|
||||
// @BasePath /api/v1
|
||||
// @schemes http https
|
||||
// @securityDefinitions.apikey SessionAuth
|
||||
// @in cookie
|
||||
// @name dumpster_session
|
||||
var GitSHA = "dev"
|
||||
|
||||
func main() {
|
||||
logger := slog.New(slog.NewTextHandler(os.Stdout, nil))
|
||||
|
||||
@@ -153,6 +152,14 @@ func main() {
|
||||
|
||||
// API routes
|
||||
r.Route("/api/v1", func(r chi.Router) {
|
||||
r.Get("/version", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
"version": GitSHA,
|
||||
})
|
||||
})
|
||||
|
||||
// Auth (public: register, login, logout) with strict rate limiting
|
||||
r.Route("/auth", func(r chi.Router) {
|
||||
r.Use(middleware.RateLimit(5, 10)) // 5 req/s, burst 10
|
||||
|
||||
@@ -15,6 +15,7 @@ import { MemberList } from './MemberList.tsx';
|
||||
import { VoicePanel } from './VoicePanel.tsx';
|
||||
import { ServerSettingsModal } from './ServerSettingsModal.tsx';
|
||||
import { ThemeToggle } from './ThemeToggle.tsx';
|
||||
import { VersionNotifier } from './VersionNotifier.tsx';
|
||||
|
||||
const STATUS_CYCLE: UserStatus[] = ['online', 'idle', 'dnd', 'offline'];
|
||||
function statusColor(status: UserStatus): string {
|
||||
@@ -99,6 +100,7 @@ export function Layout() {
|
||||
|
||||
return (
|
||||
<div className="h-full w-full bg-gb-bg text-gb-fg font-mono flex flex-col">
|
||||
<VersionNotifier />
|
||||
{/* Outer terminal frame with safe area */}
|
||||
<div className="flex-1 terminal-border bg-gb-bg-h flex flex-col min-h-0"
|
||||
style={{ padding: 'var(--safe-top, 0.25rem) var(--safe-right, 0) var(--safe-bottom, 0) var(--safe-left, 0)' }}>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { api } from "../lib/api.ts";
|
||||
import { useWebSocketStore } from "../stores/ws.ts";
|
||||
|
||||
export function VersionNotifier() {
|
||||
const [serverVersion, setServerVersion] = useState<string | null>(null);
|
||||
const [updateAvailable, setUpdateAvailable] = useState(false);
|
||||
const connected = useWebSocketStore((s) => s.connected);
|
||||
|
||||
const checkVersion = async () => {
|
||||
try {
|
||||
const data = await api.get<{ version: string }>("/version");
|
||||
if (data && data.version && data.version !== "dev") {
|
||||
setServerVersion((prev) => {
|
||||
if (!prev) {
|
||||
return data.version;
|
||||
}
|
||||
if (prev !== data.version) {
|
||||
setUpdateAvailable(true);
|
||||
}
|
||||
return data.version;
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// ignore check errors
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
checkVersion();
|
||||
const interval = setInterval(checkVersion, 2 * 60 * 1000); // Check every 2 mins
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (connected) {
|
||||
checkVersion();
|
||||
}
|
||||
}, [connected]);
|
||||
|
||||
const handleReload = () => {
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
if (!updateAvailable) return null;
|
||||
|
||||
return (
|
||||
<div className="bg-gb-accent text-gb-bg-h px-4 py-2 text-xs font-semibold flex items-center justify-between shadow-lg z-50 animate-pulse">
|
||||
<span>🚀 A new update is available! ({serverVersion})</span>
|
||||
<button
|
||||
onClick={handleReload}
|
||||
className="bg-gb-bg-h text-gb-accent px-3 py-1 rounded hover:opacity-90 font-bold transition-all"
|
||||
>
|
||||
Update Now
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user