Added build ID to footer. Added check for client-server mismatch.
This commit is contained in:
parent
f9c4facad6
commit
ec5b5192da
5 changed files with 77 additions and 2 deletions
4
Makefile
4
Makefile
|
|
@ -1,10 +1,10 @@
|
||||||
.PHONY: build frontend-build dev clean test
|
.PHONY: build frontend-build dev clean test
|
||||||
|
|
||||||
build: frontend-build
|
build: frontend-build
|
||||||
CGO_ENABLED=0 go build -o turnpike .
|
CGO_ENABLED=0 go build -ldflags "-X main.buildID=$$(git rev-parse --short HEAD)" -o turnpike .
|
||||||
|
|
||||||
frontend-build:
|
frontend-build:
|
||||||
cd frontend && npm ci && npm run build
|
cd frontend && npm ci && BUILD_ID=$$(git rev-parse --short HEAD) npm run build
|
||||||
|
|
||||||
dev:
|
dev:
|
||||||
@echo "Run in two terminals:"
|
@echo "Run in two terminals:"
|
||||||
|
|
|
||||||
1
frontend/.gitignore
vendored
1
frontend/.gitignore
vendored
|
|
@ -8,6 +8,7 @@ pnpm-debug.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
|
.vite
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
*.local
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,27 @@
|
||||||
import Nav from './components/Nav.svelte'
|
import Nav from './components/Nav.svelte'
|
||||||
import SyncStatus from './components/SyncStatus.svelte'
|
import SyncStatus from './components/SyncStatus.svelte'
|
||||||
|
|
||||||
|
const clientBuild = __BUILD_ID__
|
||||||
|
|
||||||
let session = $state(null)
|
let session = $state(null)
|
||||||
let loading = $state(true)
|
let loading = $state(true)
|
||||||
let route = $state(window.location.hash || '#/')
|
let route = $state(window.location.hash || '#/')
|
||||||
|
let updateAvailable = $state(false)
|
||||||
|
|
||||||
// Check if this is a kiosk token URL before doing anything else
|
// Check if this is a kiosk token URL before doing anything else
|
||||||
const kioskToken = $derived(window.location.hash.match(/^#\/v\/([A-Z0-9]+)/i)?.[1] ?? '')
|
const kioskToken = $derived(window.location.hash.match(/^#\/v\/([A-Z0-9]+)/i)?.[1] ?? '')
|
||||||
|
|
||||||
|
async function checkVersion() {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/version')
|
||||||
|
const { build } = await res.json()
|
||||||
|
if (build && build !== clientBuild) updateAvailable = true
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
|
checkVersion()
|
||||||
|
|
||||||
// Kiosk pages don't need auth
|
// Kiosk pages don't need auth
|
||||||
if (kioskToken) {
|
if (kioskToken) {
|
||||||
loading = false
|
loading = false
|
||||||
|
|
@ -40,6 +53,9 @@
|
||||||
window.addEventListener('hashchange', () => {
|
window.addEventListener('hashchange', () => {
|
||||||
route = window.location.hash || '#/'
|
route = window.location.hash || '#/'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Periodically check for updates
|
||||||
|
setInterval(checkVersion, 60000)
|
||||||
})
|
})
|
||||||
|
|
||||||
function onLogin(s) {
|
function onLogin(s) {
|
||||||
|
|
@ -58,6 +74,13 @@
|
||||||
const role = $derived(session?.user?.role ?? '')
|
const role = $derived(session?.user?.role ?? '')
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
{#if updateAvailable}
|
||||||
|
<div class="update-banner">
|
||||||
|
A new version is available.
|
||||||
|
<button onclick={() => location.reload()}>Refresh</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<!-- checking session -->
|
<!-- checking session -->
|
||||||
{:else if kioskToken}
|
{:else if kioskToken}
|
||||||
|
|
@ -100,3 +123,45 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<footer class="build-footer">{clientBuild}</footer>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.build-footer {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 4px;
|
||||||
|
right: 8px;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--c-muted);
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-banner {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 9999;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: var(--c-accent);
|
||||||
|
color: white;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-banner button {
|
||||||
|
margin-left: 12px;
|
||||||
|
padding: 2px 12px;
|
||||||
|
border: 1px solid rgba(255,255,255,0.5);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
background: transparent;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-banner button:hover {
|
||||||
|
background: rgba(255,255,255,0.15);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,9 @@ import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [svelte()],
|
plugins: [svelte()],
|
||||||
|
define: {
|
||||||
|
__BUILD_ID__: JSON.stringify(process.env.BUILD_ID || 'dev'),
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': 'http://localhost:8180',
|
'/api': 'http://localhost:8180',
|
||||||
|
|
|
||||||
6
main.go
6
main.go
|
|
@ -12,6 +12,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var buildID = "dev"
|
||||||
|
|
||||||
//go:embed frontend/dist
|
//go:embed frontend/dist
|
||||||
var frontendFS embed.FS
|
var frontendFS embed.FS
|
||||||
|
|
||||||
|
|
@ -145,6 +147,10 @@ func (app *App) registerRoutes(mux *http.ServeMux) {
|
||||||
mux.HandleFunc("GET /api/sync/pull", auth(app.handleSyncPull))
|
mux.HandleFunc("GET /api/sync/pull", auth(app.handleSyncPull))
|
||||||
mux.HandleFunc("GET /api/sync/stream", auth(app.handleSyncStream))
|
mux.HandleFunc("GET /api/sync/stream", auth(app.handleSyncStream))
|
||||||
|
|
||||||
|
mux.HandleFunc("GET /api/version", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
writeJSON(w, map[string]string{"build": buildID})
|
||||||
|
})
|
||||||
|
|
||||||
// Kiosk — authenticated by volunteer token, no JWT required.
|
// Kiosk — authenticated by volunteer token, no JWT required.
|
||||||
mux.HandleFunc("GET /api/v/{token}", app.handleKioskGet)
|
mux.HandleFunc("GET /api/v/{token}", app.handleKioskGet)
|
||||||
mux.HandleFunc("POST /api/v/{token}/shifts/{id}", app.handleKioskClaim)
|
mux.HandleFunc("POST /api/v/{token}/shifts/{id}", app.handleKioskClaim)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue