V1.12.3: live updates behind any proxy, fewer requests
- Live updates start on plain requests and move up to a websocket when one gets through. The site used to ask for the websocket first, with no way back: behind a proxy that does not pass the upgrade, nothing live arrived at all. - A browser asks the API's leave before a request, the site and the API being on different hosts. It now remembers the answer for two hours, where it used to ask before nearly every request. - A built file is kept by the browser for good: its name changes when its contents do. Each used to be checked again on every page load. - For a real websocket through nginx, the blocks that pass requests to the API need "proxy_http_version 1.1", "proxy_set_header Upgrade $http_upgrade" and "proxy_set_header Connection "upgrade"". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"private": true,
|
||||
"version": "1.12.2",
|
||||
"version": "1.12.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -8,7 +8,10 @@ let sharedSocket: Socket | null = null;
|
||||
|
||||
function getSocket(token: string): Socket {
|
||||
if (!sharedSocket) {
|
||||
sharedSocket = io(API_URL, { auth: { token }, transports: ['websocket', 'polling'] });
|
||||
// Starts on plain requests and moves up to a websocket when one gets
|
||||
// through. Asking for the websocket first leaves no way back: behind a
|
||||
// proxy that does not pass the upgrade, nothing live arrives at all.
|
||||
sharedSocket = io(API_URL, { auth: { token } });
|
||||
}
|
||||
return sharedSocket;
|
||||
}
|
||||
|
||||
+18
-5
@@ -1,20 +1,33 @@
|
||||
import { existsSync } from 'node:fs'
|
||||
import { join } from 'node:path'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import compression from 'compression'
|
||||
import { defineConfig, type Connect, type Plugin } from 'vite'
|
||||
|
||||
// `vite preview` is what serves the built site, and nothing in front of it
|
||||
// compresses (frp only tunnels), so the files are gzipped here.
|
||||
const gzipPreview: Plugin = {
|
||||
name: 'gzip-preview',
|
||||
// `vite preview` is what serves the built site, so the two things a web
|
||||
// server would be set up to do are done here.
|
||||
const servePreview: Plugin = {
|
||||
name: 'serve-preview',
|
||||
configurePreviewServer(server) {
|
||||
const assets = join(server.config.root, server.config.build.outDir)
|
||||
// Typed for Express, and at home in any Node server.
|
||||
server.middlewares.use(compression() as Connect.NextHandleFunction)
|
||||
// A built file carries a hash of its contents in its name, so a browser
|
||||
// can keep it for good and never ask again. Only for files that are
|
||||
// there: a name from an older build must not be remembered as missing.
|
||||
server.middlewares.use((req, res, next) => {
|
||||
const path = (req.url ?? '').split('?')[0]
|
||||
if (path.startsWith('/assets/') && !path.includes('..') && existsSync(join(assets, path))) {
|
||||
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable')
|
||||
}
|
||||
next()
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), gzipPreview],
|
||||
plugins: [react(), servePreview],
|
||||
|
||||
preview: {
|
||||
host: true,
|
||||
|
||||
Reference in New Issue
Block a user