Skip to content

Commit

Permalink
merged websocket_fix.
Browse files Browse the repository at this point in the history
- fixes connection issues in production mode
- disables vite minifying
  • Loading branch information
pSpitzner committed Oct 7, 2024
1 parent 3370514 commit 66d13ff
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 43 deletions.
5 changes: 4 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,13 @@ FROM deps AS prod

WORKDIR /repo
COPY --from=deps /repo /repo
COPY --chown=beetle:beetle . .
COPY --chown=beetle:beetle . .
RUN chmod +x ./entrypoint.sh

WORKDIR /repo/frontend
RUN rm -rf node_modules
RUN rm -rf dist
RUN rm -rf .pnpm-store
RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store \
pnpm install
RUN pnpm run build
Expand Down
6 changes: 5 additions & 1 deletion backend/beets_flask/inbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@
from beets_flask.logger import log
from beets_flask.config import config
from beets_flask.routes.sse import update_client_view
from beets_flask.disk import all_album_folders, album_folders_from_track_paths
from beets_flask.disk import (
all_album_folders,
album_folders_from_track_paths,
path_to_dict,
)


# ------------------------------------------------------------------------------------ #
Expand Down
2 changes: 1 addition & 1 deletion entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ done
redis-cli FLUSHALL

# we need to run with one worker for socketio to work (but need at lesat threads for SSEs)
gunicorn --worker-class eventlet -w 1 --threads 32 --bind 0.0.0.0:5001 'main:create_app()'
gunicorn --worker-class eventlet -w 1 --threads 32 --timeout 300 --bind 0.0.0.0:5001 'main:create_app()'
6 changes: 4 additions & 2 deletions frontend/src/components/common/hooks/useSocket.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const useSocket = (
const url: string =
import.meta.env.MODE === "development"
? `ws://localhost:5001/${namespace}`
: namespace;
: `/${namespace}`;

const [socket, setSocket] = useState<Socket | null>(null);
const [isConnected, setIsConnected] = useState(false);
Expand Down Expand Up @@ -94,7 +94,9 @@ const STATUS_URL =
import.meta.env.MODE === "development" ? "ws://localhost:5001/status" : "/status";

const statusSocket = io(STATUS_URL, {
autoConnect: true,
// Setting autoConnect to true causes issues in production mode.
// Seems the connection is attempted before dependencies are ready.
autoConnect: false,
transports: ["websocket"],
});

Expand Down
82 changes: 44 additions & 38 deletions frontend/src/components/frontpage/terminal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,17 @@ const xTermTheme = {

export function Terminal(props: HtmlHTMLAttributes<HTMLDivElement>) {
const ref = useRef<HTMLDivElement>(null);
const { term, resetTerm } = useTerminalContext();
const { term, resetTerm, socket } = useTerminalContext();

// we have to recreate the terminal on every mount of the component.
// not sure why we cannot restore.
useEffect(resetTerm, [resetTerm]);

// resetting term also retriggers this guy:
// resetting term also retriggers this guy.
// having socket as a dependencies should make sure we retrigger when
// the if socket had connection issues.
useEffect(() => {
if (!ref.current || !term) return;
if (!ref.current || !term || !socket) return;
const ele = ref.current;
function copyPasteHandler(e: KeyboardEvent) {
if (!term) return false;
Expand Down Expand Up @@ -83,23 +85,19 @@ export function Terminal(props: HtmlHTMLAttributes<HTMLDivElement>) {
const fitAddon = new xTermFitAddon();
term.loadAddon(fitAddon);
term.open(ele);
term.focus();
fitAddon.fit();
console.log(term.rows);

// Resize on window resize
const resizeObserver = new ResizeObserver(() => {
fitAddon.fit();
});
resizeObserver.observe(ele);

// On visibility change rerender terminal
console.log("Term mounted");
return () => {
// term.dispose();
term.dispose();
if (ele) resizeObserver.unobserve(ele);
console.log("Term unmounted");
};
}, [term]);
}, [term, ref, socket]);

return <div ref={ref} {...props} />;
}
Expand Down Expand Up @@ -139,12 +137,45 @@ export function TerminalContextProvider({ children }: { children: React.ReactNod
});
}, []);

useEffect(resetTerm, [resetTerm]);
// useEffect(resetTerm, [resetTerm]);

const onCursorUpdate = useCallback(
(data: { x: number; y: number }) => {
if (!term) return;
// xterm uses 1-based indexing
// console.log("Cursor update", data);
term.write(`\x1b[${data.y + 1};${data.x + 1}H`);
},
[term]
);

const onOutput = useCallback(
(data: { output: string[] }) => {
if (!term) return;
// term!.clear(); seems to be preferred from the documentation,
// but it leaves the prompt on the first line in place - which we here do not want
// ideally we would directly access the buffer.
// console.log("ptyOutput", data);
term.reset();
data.output.forEach((line, index) => {
if (index < data.output.length - 1) {
term.writeln(line);
} else {
// Workaround: strip all trailing whitespaces except for one
// not a perfect fix (one wrong space remains when backspacing)
const stripped_line = line.replace(/\s+$/, " ");
term.write(stripped_line);
}
});
},
[term]
);

// Attach socket handler
useEffect(() => {
if (!term || !isConnected || !socket) return;

// spaces are needed to clear out the longer "Connecting..."
term.writeln("\rConnected! ");

const onInput = term.onData((data) => {
Expand All @@ -156,33 +187,10 @@ export function TerminalContextProvider({ children }: { children: React.ReactNod
});

const onResize = term.onResize(({ cols, rows }) => {
// console.log(`Terminal was resized to ${cols} cols and ${rows} rows.`);
console.log(`Terminal was resized to ${cols} cols and ${rows} rows.`);
socket.emit("ptyResize", { cols, rows: rows });
});

function onOutput(data: { output: string[] }) {
// term!.clear(); seems to be preferred from the documentation,
// but it leaves the prompt on the first line in place - which we here do not want
// ideally we would directly access the buffer.
// console.log("ptyOutput", data);
term!.reset();
data.output.forEach((line, index) => {
if (index < data.output.length - 1) {
term!.writeln(line);
} else {
// Workaround: strip all trailing whitespaces except for one
// not a perfect fix (one wrong space remains when backspacing)
const stripped_line = line.replace(/\s+$/, " ");
term!.write(stripped_line);
}
});
}

function onCursorUpdate(data: { x: number; y: number }) {
// xterm uses 1-based indexing
term!.write(`\x1b[${data.y + 1};${data.x + 1}H`);
}

socket.on("ptyOutput", onOutput);
socket.on("ptyCursorPosition", onCursorUpdate);

Expand All @@ -197,7 +205,7 @@ export function TerminalContextProvider({ children }: { children: React.ReactNod
socket.off("ptyOutput", onOutput);
socket.off("ptyCursorPosition", onCursorUpdate);
};
}, [isConnected, term, socket]);
}, [isConnected, term, socket, onOutput, onCursorUpdate]);

// make first responder directly after opening
useEffect(() => {
Expand All @@ -211,7 +219,6 @@ export function TerminalContextProvider({ children }: { children: React.ReactNod
console.error("No socket available");
return;
}

socket.emit("ptyInput", { input: t });
}

Expand All @@ -220,7 +227,6 @@ export function TerminalContextProvider({ children }: { children: React.ReactNod
console.error("No socket available");
return;
}

socket.emit("ptyInput", { input: "\x15" });
}

Expand Down
5 changes: 5 additions & 0 deletions frontend/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ import svgr from "vite-plugin-svgr";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [tsconfigPaths(), react(), TanStackRouterVite(), svgr()],
// not minifying helped when debugging in production mode
// we can enable this again when the code base is a bit more mature.
build: {
minify: false,
},
});

0 comments on commit 66d13ff

Please sign in to comment.