Merge pull request #7: Stop caching index.html and speed up socket reconnect
CI/CD / backend (push) Successful in 5m42s
CI/CD / frontend (push) Successful in 5m15s
CI/CD / build (push) Successful in 17s
CI/CD / cleanup (push) Successful in 4s
CI/CD / deploy (push) Successful in 16s

This commit is contained in:
matitos committed 2026-09-20 00:10:52 +00:00
commit 07c17fbd2c
3 files changed
+33 -6

No files matched your search

+15
View File
@@ -4,7 +4,22 @@ server {
root /usr/share/nginx/html; root /usr/share/nginx/html;
index index.html; index index.html;
# Hashed Vite assets are immutable; cache them hard.
location /assets/ {
add_header Cache-Control "public, max-age=31536000, immutable";
try_files $uri =404;
}
# The HTML entry point must never be cached, or a phone (which offers no
# easy hard-refresh) keeps loading an old bundle after a deploy. The
# catch-all serves index.html for client-side routes, so it gets the same
# no-store header.
location = /index.html {
add_header Cache-Control "no-store";
}
location / { location / {
add_header Cache-Control "no-store";
try_files $uri $uri/ /index.html; try_files $uri $uri/ /index.html;
} }
+10 -1
View File
@@ -36,6 +36,7 @@ const events = ref([])
const equityPoints = ref([]) const equityPoints = ref([])
const busy = ref(false) const busy = ref(false)
const connected = ref(false) const connected = ref(false)
const socketError = ref('')
let socket let socket
let pollTimer let pollTimer
@@ -132,8 +133,15 @@ onMounted(async () => {
]) ])
socket = api.openSocket({ socket = api.openSocket({
onMessage: handleMessage, onMessage: handleMessage,
onOpen: () => (connected.value = true), onOpen: () => {
connected.value = true
socketError.value = ''
},
onClose: () => (connected.value = false), onClose: () => (connected.value = false),
onError: (code) => {
if (code === 1008) socketError.value = 'rejected by the server (token/auth)'
else socketError.value = `socket closed (code ${code})`
},
}) })
pollTimer = setInterval(refreshAll, 5000) pollTimer = setInterval(refreshAll, 5000)
}) })
@@ -161,6 +169,7 @@ onUnmounted(() => {
:class="connected ? 'bg-emerald-400' : 'bg-slate-500'" :class="connected ? 'bg-emerald-400' : 'bg-slate-500'"
></span> ></span>
{{ connected ? 'live' : 'reconnecting' }} {{ connected ? 'live' : 'reconnecting' }}
<span v-if="!connected && socketError" class="text-amber-300">· {{ socketError }}</span>
</span> </span>
</div> </div>
<div class="text-xs text-slate-400"> <div class="text-xs text-slate-400">
+8 -5
View File
@@ -26,7 +26,7 @@ export const getPresets = () => http.get('/presets').then((r) => r.data)
export const createPreset = (payload) => http.post('/presets', payload).then((r) => r.data) export const createPreset = (payload) => http.post('/presets', payload).then((r) => r.data)
export const deletePreset = (id) => http.delete(`/presets/${id}`).then((r) => r.data) export const deletePreset = (id) => http.delete(`/presets/${id}`).then((r) => r.data)
export function openSocket({ onMessage, onOpen, onClose }) { export function openSocket({ onMessage, onOpen, onClose, onError }) {
const proto = window.location.protocol === 'https:' ? 'wss' : 'ws' const proto = window.location.protocol === 'https:' ? 'wss' : 'ws'
const url = `${proto}://${window.location.host}/api/ws` const url = `${proto}://${window.location.host}/api/ws`
let socket let socket
@@ -36,9 +36,9 @@ export function openSocket({ onMessage, onOpen, onClose }) {
const schedule = () => { const schedule = () => {
if (closed) return if (closed) return
// Back off from 2s up to 30s so a persistently failing socket (e.g. auth) // Retry at 1s, then back off up to 30s so a persistently failing socket
// does not hammer the server, while a brief blip still reconnects fast. // does not hammer the server while a brief blip recovers immediately.
const delay = Math.min(2000 * 2 ** attempt, 30000) const delay = Math.min(1000 * 2 ** attempt, 30000)
attempt += 1 attempt += 1
retryTimer = setTimeout(connect, delay) retryTimer = setTimeout(connect, delay)
} }
@@ -51,8 +51,11 @@ export function openSocket({ onMessage, onOpen, onClose }) {
onOpen?.() onOpen?.()
} }
socket.onmessage = (event) => onMessage?.(JSON.parse(event.data)) socket.onmessage = (event) => onMessage?.(JSON.parse(event.data))
socket.onclose = () => { socket.onclose = (event) => {
onClose?.() onClose?.()
// 1008 = policy violation (token rejected); 1006 = abnormal close, which
// is what an auth/proxy rejection looks like to the browser.
onError?.(event.code, event.reason)
schedule() schedule()
} }
socket.onerror = () => socket.close() socket.onerror = () => socket.close()