feat: podpora high-availability a multi-replica nasazení
CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Successful in 20s
CI / Build server (push) Successful in 26s
CI / Build client (push) Successful in 35s
CI / Playwright E2E tests (push) Failing after 1m56s
CI / Build and push Docker image (push) Has been skipped
CI / Notify (push) Successful in 1s
CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Successful in 20s
CI / Build server (push) Successful in 26s
CI / Build client (push) Successful in 35s
CI / Playwright E2E tests (push) Failing after 1m56s
CI / Build and push Docker image (push) Has been skipped
CI / Notify (push) Successful in 1s
- Socket.io Redis adapter pro sdílený stav přes repliky - graceful shutdown serveru - WATCH/MULTI v updateData pro race-condition-safe aktualizace - lease mechanismus pro push reminder (zabrání duplicitnímu odesílání) - k8s/ manifesty pro testovací kind cluster - Dockerfile: opraven EXPOSE port na 3001 - .gitignore: ignorovány Claude pracovní soubory
This commit is contained in:
@@ -155,6 +155,21 @@ function App() {
|
||||
}
|
||||
}, [auth?.login, socket]);
|
||||
|
||||
// Po znovupřipojení socketu znovu vstoupit do místnosti a obnovit data
|
||||
useEffect(() => {
|
||||
const onReconnect = () => {
|
||||
if (auth?.login) socket.emit('join', auth.login);
|
||||
getData({ query: { dayIndex: dayIndexRef.current } }).then(response => {
|
||||
if (response.data) {
|
||||
setData(response.data);
|
||||
setFood(response.data.menus);
|
||||
}
|
||||
});
|
||||
};
|
||||
socket.io.on('reconnect', onReconnect);
|
||||
return () => { socket.io.off('reconnect', onReconnect); };
|
||||
}, [socket, auth?.login]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!auth?.login || !data?.choices) {
|
||||
return
|
||||
|
||||
@@ -8,12 +8,25 @@ if (process.env.NODE_ENV === 'development') {
|
||||
socketPath = undefined;
|
||||
} else {
|
||||
socketUrl = `${globalThis.location.host}`;
|
||||
socketPath = `${globalThis.location.pathname}socket.io`;
|
||||
socketPath = '/socket.io';
|
||||
}
|
||||
|
||||
export const socket = socketio.connect(socketUrl, { path: socketPath, transports: ["websocket"] });
|
||||
export const SocketContext = React.createContext();
|
||||
|
||||
// Prohlížeče throttlují setTimeout v neaktivních tabech, což zdržuje automatické
|
||||
// znovupřipojení socket.io. Po návratu do tabu nebo focusu okna se připojíme hned.
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible' && !socket.connected) {
|
||||
socket.connect();
|
||||
}
|
||||
});
|
||||
window.addEventListener('focus', () => {
|
||||
if (!socket.connected) {
|
||||
socket.connect();
|
||||
}
|
||||
});
|
||||
|
||||
// Konstanty websocket eventů, musí odpovídat těm na serveru!
|
||||
export const EVENT_CONNECT = 'connect';
|
||||
export const EVENT_DISCONNECT = 'disconnect';
|
||||
|
||||
@@ -73,6 +73,12 @@ export default function OrderGroupsPage() {
|
||||
return () => { socket.off(EVENT_MESSAGE); };
|
||||
}, [socket]);
|
||||
|
||||
useEffect(() => {
|
||||
const onReconnect = () => fetchData();
|
||||
socket.io.on('reconnect', onReconnect);
|
||||
return () => { socket.io.off('reconnect', onReconnect); };
|
||||
}, [socket]);
|
||||
|
||||
const refresh = async (fn: () => Promise<any>): Promise<boolean> => {
|
||||
setPageError(null);
|
||||
const result = await fn();
|
||||
@@ -85,7 +91,6 @@ export default function OrderGroupsPage() {
|
||||
setData(result.data);
|
||||
socket.emit?.('message', result.data as ClientData);
|
||||
}
|
||||
await fetchData();
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ export default defineConfig({
|
||||
plugins: [react(), viteTsconfigPaths()],
|
||||
server: {
|
||||
open: true,
|
||||
host: '0.0.0.0',
|
||||
port: 3000,
|
||||
proxy: {
|
||||
'/api': 'http://localhost:3001',
|
||||
|
||||
Reference in New Issue
Block a user