diff --git a/web/lib/client/helpers/socket-context.jsx b/web/lib/client/helpers/socket-context.jsx
deleted file mode 100644
index 6a3851bf3..000000000
--- a/web/lib/client/helpers/socket-context.jsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import { createContext, useContext, useEffect, useMemo } from 'react';
-import { io } from 'socket.io-client';
-import { v4 as uuid } from 'uuid';
-import { socketUrl } from '~/root/lib/base-url';
-import logger from './log';
-import { useReload } from './reloader';
-
-const createSocketContext = () => {
- const callbacks = {};
- const socket = io(socketUrl);
-
- socket.on('connect', () => {
- logger.log('socket connected');
- });
-
- socket.on('disconnect', () => {
- logger.log('socket disconnected');
- });
-
- socket.on('error', (error) => {
- logger.error(error);
- });
-
- return {
- subscribe: (topic, callback) => {
- if (!callbacks[topic]) {
- callbacks[topic] = {};
- // socket.emit('subscribe', topic);
- socket.on(`pubsub_${topic}`, (msg = '{}') => {
- // logger.log('message', topic);
- const { message } = JSON.parse(msg);
- logger.log('socket: ', topic);
- if (callbacks[topic]) {
- Object.values(callbacks[topic]).forEach((cb) =>
- cb({ topic, message })
- );
- }
- });
- }
-
- const id = uuid();
- callbacks[topic][id] = callback;
- return id;
- },
-
- unsubscribe: (topic, id) => {
- delete callbacks[topic][id];
- if (Object.keys(callbacks[topic]).length === 0) {
- // socket.emit('unsubscribe', topic);
- socket.off(`pubsub_${topic}`).removeAllListeners();
- }
- },
- };
-};
-
-const SocketContext = createContext(null);
-
-const SocketProvider = ({ children }) => {
- const socket = useMemo(() => {
- // if (typeof window !== 'undefined') {
- // return createSocketContext();
- // }
- //
- // return {
- // subscribe: () => {},
- // unsubscribe: () => {},
- // };
- }, [typeof window]);
-
- return (
- {children}
- );
-};
-
-export const useSubscribe = (topics, callback, dependencies = []) => {
- const t = typeof topics === 'string' ? [topics] : topics;
-
- const { subscribe, unsubscribe } = useContext(SocketContext);
-
- useEffect(() => {
- const subscriptions = t.map((topic) => ({
- topic,
- id: subscribe(topic, callback),
- }));
- return () => {
- subscriptions.forEach(({ topic, id }) => unsubscribe(topic, id));
- };
- }, [...dependencies, ...t]);
-};
-
-export const useWatch = (...topics) => {
- const reloadPage = useReload();
- useSubscribe(
- topics,
- () => {
- reloadPage();
- },
- topics
- );
-};
-
-export default SocketProvider;
diff --git a/web/lib/client/helpers/socket-context.tsx b/web/lib/client/helpers/socket-context.tsx
new file mode 100644
index 000000000..aecf938b8
--- /dev/null
+++ b/web/lib/client/helpers/socket-context.tsx
@@ -0,0 +1,194 @@
+import { createContext, useContext, useEffect, useMemo } from 'react';
+import * as sock from 'websocket';
+import { v4 as uuid } from 'uuid';
+import { socketUrl } from '~/lib/configs/base-url.cjs';
+import { ChildrenProps } from '~/components/types';
+import logger from './log';
+import { NonNullableString } from '../../types/common';
+import { useReload } from './reloader';
+
+type Ievent = 'subscribe' | 'unsubscribe' | NonNullableString;
+
+type IMessage = {
+ data: string;
+ event: Ievent;
+};
+
+const message = ({ event, data }: IMessage): string => {
+ return JSON.stringify({ event, data });
+};
+
+const socketContextDefaultValue: {
+ subscribe: (
+ topic: string,
+ callback: (arg: { topic: string; message: string }) => void
+ ) => string;
+ unsubscribe: (topic: string, id: string) => void;
+} = {
+ subscribe: (): string => '',
+ unsubscribe: (): void => {},
+};
+
+const createSocketContext = () => {
+ if (typeof window === 'undefined') {
+ return socketContextDefaultValue;
+ }
+
+ const callbacks: {
+ [key: string]: {
+ [key: string]: (arg: { topic: string; message: string }) => void;
+ };
+ } = {};
+
+ const wsclient = new Promise((res, rej) => {
+ try {
+ // eslint-disable-next-line new-cap
+ const w = new sock.w3cwebsocket(socketUrl, '', '', {});
+
+ w.onmessage = (msg) => {
+ try {
+ const m: {
+ topic: string;
+ message: string;
+ type: 'update' | 'error' | 'info';
+ } = JSON.parse(msg.data as string);
+
+ if (m.type === 'error') {
+ console.error(m.message);
+ return;
+ }
+
+ if (m.type === 'info') {
+ console.log(m.message);
+ return;
+ }
+
+ Object.values(callbacks[m.topic]).forEach((cb) => {
+ cb(m);
+ });
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ w.onopen = () => {
+ res(w);
+ };
+
+ w.onerror = (e) => {
+ rej(e);
+ };
+
+ w.onclose = () => {
+ // wsclient.send(newMessage({ event: 'unsubscribe', data: 'test' }));
+ logger.log('socket disconnected');
+ };
+ } catch (e) {
+ rej(e);
+ }
+ });
+
+ return {
+ subscribe: (
+ topic: string,
+ callback: (arg: { topic: string; message: string }) => void
+ ): string => {
+ (async () => {
+ if (!callbacks[topic]) {
+ callbacks[topic] = {};
+
+ try {
+ const w = await wsclient;
+
+ w.send(
+ message({
+ event: 'subscribe',
+ data: topic,
+ })
+ );
+
+ logger.log('subscribed to', topic);
+ } catch (err) {
+ logger.warn(err);
+ }
+ }
+ })();
+
+ const id = uuid();
+ callbacks[topic][id] = callback;
+ return id;
+ },
+
+ unsubscribe: (topic: string, id: string) => {
+ (async () => {
+ delete callbacks[topic][id];
+
+ try {
+ const w = await wsclient;
+
+ if (Object.keys(callbacks[topic]).length === 0) {
+ w.send(
+ message({
+ event: 'unsubscribe',
+ data: topic,
+ })
+ );
+ }
+ } catch (err) {
+ logger.warn(err);
+ }
+ })();
+ },
+ };
+};
+
+const SocketContext = createContext(socketContextDefaultValue);
+
+const SocketProvider = ({ children }: ChildrenProps) => {
+ const socket = useMemo(() => {
+ if (typeof window !== 'undefined') {
+ return createSocketContext();
+ }
+
+ return socketContextDefaultValue;
+ }, [typeof window]);
+
+ return (
+ {children}
+ );
+};
+
+export const useSubscribe = (
+ topics: string[],
+ callback: (arg: { topic: string; message: string }) => void,
+ dependencies: any[] = []
+) => {
+ const t = typeof topics === 'string' ? [topics] : topics;
+
+ const { subscribe, unsubscribe } = useContext(SocketContext);
+
+ useEffect(() => {
+ const subscriptions = t.map((topic) => ({
+ topic,
+ id: subscribe(topic, callback),
+ }));
+
+ return () => {
+ subscriptions.forEach(({ topic, id }) => unsubscribe(topic, id));
+ };
+ }, [...dependencies, ...t]);
+};
+
+export const useWatch = (...topics: string[]) => {
+ const reloadPage = useReload();
+ useSubscribe(
+ topics,
+ () => {
+ console.log('hi');
+ reloadPage();
+ },
+ topics
+ );
+};
+
+export default SocketProvider;
diff --git a/web/lib/configs/base-url.cjs b/web/lib/configs/base-url.cjs
index c80fda6f8..296f99a5a 100644
--- a/web/lib/configs/base-url.cjs
+++ b/web/lib/configs/base-url.cjs
@@ -42,6 +42,7 @@ const baseUrls = () => {
cookieDomain,
baseUrl: bUrl,
githubAppName: 'kloudlite-dev',
+ socketUrl: `wss://socket${postFix}.${bUrl}/ws`,
};
};
@@ -52,6 +53,7 @@ const defaultConfig = {
cookieDomain: baseUrls().cookieDomain,
baseUrl: baseUrls().baseUrl,
githubAppName: baseUrls().githubAppName,
+ socketUrl: baseUrls().socketUrl,
};
module.exports = defaultConfig;
diff --git a/web/package.json b/web/package.json
index af083b7b2..d7b773bc8 100644
--- a/web/package.json
+++ b/web/package.json
@@ -90,6 +90,7 @@
"react-viewport-list": "^7.1.1",
"remix": "^1.19.2",
"search-in-json": "^1.0.5",
+ "socket.io-client": "^4.7.2",
"swr": "^2.2.4",
"use-immer": "^0.9.0",
"uuid": "^9.0.0",
diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml
index a82834b3d..dfabbb9a0 100644
--- a/web/pnpm-lock.yaml
+++ b/web/pnpm-lock.yaml
@@ -209,6 +209,9 @@ dependencies:
search-in-json:
specifier: ^1.0.5
version: 1.0.67
+ socket.io-client:
+ specifier: ^4.7.2
+ version: 4.7.2
swr:
specifier: ^2.2.4
version: 2.2.4(react@18.2.0)
@@ -3874,6 +3877,10 @@ packages:
engines: {node: '>=10'}
dev: true
+ /@socket.io/component-emitter@3.1.0:
+ resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==}
+ dev: false
+
/@swc/helpers@0.5.3:
resolution: {integrity: sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==}
dependencies:
@@ -5192,7 +5199,6 @@ packages:
optional: true
dependencies:
ms: 2.1.2
- dev: true
/decode-named-character-reference@1.0.2:
resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
@@ -5382,6 +5388,25 @@ packages:
once: 1.4.0
dev: true
+ /engine.io-client@6.5.3:
+ resolution: {integrity: sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==}
+ dependencies:
+ '@socket.io/component-emitter': 3.1.0
+ debug: 4.3.4
+ engine.io-parser: 5.2.1
+ ws: 8.11.0
+ xmlhttprequest-ssl: 2.0.0
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+ dev: false
+
+ /engine.io-parser@5.2.1:
+ resolution: {integrity: sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==}
+ engines: {node: '>=10.0.0'}
+ dev: false
+
/enhanced-resolve@5.15.0:
resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==}
engines: {node: '>=10.13.0'}
@@ -7946,7 +7971,6 @@ packages:
/ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
- dev: true
/ms@2.1.3:
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
@@ -9471,6 +9495,30 @@ packages:
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
dev: true
+ /socket.io-client@4.7.2:
+ resolution: {integrity: sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==}
+ engines: {node: '>=10.0.0'}
+ dependencies:
+ '@socket.io/component-emitter': 3.1.0
+ debug: 4.3.4
+ engine.io-client: 6.5.3
+ socket.io-parser: 4.2.4
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+ dev: false
+
+ /socket.io-parser@4.2.4:
+ resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==}
+ engines: {node: '>=10.0.0'}
+ dependencies:
+ '@socket.io/component-emitter': 3.1.0
+ debug: 4.3.4
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
/socks-proxy-agent@8.0.2:
resolution: {integrity: sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==}
engines: {node: '>= 14'}
@@ -10499,6 +10547,19 @@ packages:
optional: true
dev: true
+ /ws@8.11.0:
+ resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ^5.0.2
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+ dev: false
+
/xdm@2.1.0:
resolution: {integrity: sha512-3LxxbxKcRogYY7cQSMy1tUuU1zKNK9YPqMT7/S0r7Cz2QpyF8O9yFySGD7caOZt+LWUOQioOIX+6ZzCoBCpcAA==}
dependencies:
@@ -10529,6 +10590,11 @@ packages:
- supports-color
dev: true
+ /xmlhttprequest-ssl@2.0.0:
+ resolution: {integrity: sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==}
+ engines: {node: '>=0.4.0'}
+ dev: false
+
/xtend@4.0.2:
resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=0.4'}
diff --git a/web/src/apps/console/root.tsx b/web/src/apps/console/root.tsx
index ff343fb7c..93548e154 100644
--- a/web/src/apps/console/root.tsx
+++ b/web/src/apps/console/root.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable react/jsx-no-useless-fragment */
import Root, { links as baseLinks } from '~/lib/app-setup/root';
import { ChildrenProps } from '~/components/types';
import authStylesUrl from './styles/index.css';
@@ -17,12 +18,8 @@ export const links = () => {
export { ErrorBoundary } from '~/lib/app-setup/root';
const Layout = ({ children }: ChildrenProps) => {
- return (
- //
- // eslint-disable-next-line react/jsx-no-useless-fragment
- <>{children}>
- //
- );
+ // return {children};
+ return <>{children}>;
};
const _Root = ({ ...props }) => {
diff --git a/web/src/apps/console/routes/test.tsx b/web/src/apps/console/routes/test.tsx
new file mode 100644
index 000000000..6c408999a
--- /dev/null
+++ b/web/src/apps/console/routes/test.tsx
@@ -0,0 +1,38 @@
+import { useLoaderData } from '@remix-run/react';
+import SocketProvider, {
+ useSubscribe,
+ // useSubscribe,
+ useWatch,
+} from '~/root/lib/client/helpers/socket-context';
+
+export const loader = () => {
+ return {
+ data: Math.random(),
+ };
+};
+
+const App = () => {
+ useSubscribe(
+ ['account:newteam.cluster'],
+ () => {
+ console.log('hi');
+ },
+ []
+ );
+
+ // useWatch('account:newteam.cluster');
+ // res-updates.account.acc-ruwibp-pf5jvcsew2rnl54kriv59.cluster.*
+ // res-updates.account.accid.project.projid.env.envid.app.*
+
+ const { data } = useLoaderData();
+
+ return {data}
;
+};
+
+export default () => {
+ return (
+
+
+
+ );
+};