import type { Socket } from 'socket.io-client'
import { io } from 'socket.io-client'
export default defineNuxtPlugin(async (nuxtApp) =>{
let socket: Socket
let listeners: Array<(payload: any) => void> = []
const config = useRuntimeConfig()
const baseURL = config.public.ncBackendUrl || BASE_FALLBACK_URL
const url = `${baseURL.replace('http', 'ws')}/table-update`
const connect = async () => {
try{
if (socket) socket.disconnect()
socket = io(url, { transports: ['websocket'] })
socket.on('table_update', (payload) => {
listeners.forEach((cb) => cb(payload))
})
socket.on('connect_error', () => {
socket.disconnect()
})
socket.on('connect', () => {
console.log('connected to table-update')
})
socket.on('disconnect', () => {
console.log('disconnected from table-update')
})
} catch (e) {}
}
console.log(nuxtApp)
if (nuxtApp.$state.signedIn.value) {
await connect()
}
watch((nuxtApp.$state as ReturnType<typeof useGlobal>).token, (newToken, oldToken) => {
try {
if (newToken && newToken !== oldToken) connect()
else if (!newToken) socket?.disconnect()
} catch (e) {
console.error(e)
}
})
// Provide a way to register listeners
function registerTableUpdateListener(cb: (payload: any) => void) {
listeners.push(cb)
return () => listeners = listeners.filter((l) => l !== cb)
}
nuxtApp.provide('registerTableUpdateListener', registerTableUpdateListener)
}
)
import { Injectable, Logger } from '@nestjs/common';
import { WebSocketGateway, WebSocketServer, OnGatewayConnection, OnGatewayDisconnect } from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { Client as PgClient } from 'pg';
function wait(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
@WebSocketGateway({
namespace: '/table-update',
cors: {
origin: '*',
// credentials: true,
},
})
@Injectable()
export class PgNotificationGateway implements OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer()
server: Server;
private logger = new Logger('PgNotificationGateway');
private pgClient: PgClient;
private connectedClients = new Set<Socket>();
private isConnected = false;
private retryDelay = 1000;
private maxRetryDelay = 30000;
private heartbeatInterval: NodeJS.Timeout | null = null;
constructor() {
this.connectToPostgres();
this.startHeartbeat();
}
async connectToPostgres() {
while (true) {
try {
this.pgClient = new PgClient({
host: 'db_host',
port: 5432,
user: 'noti_listener',
password: 'top_secret',
database: 'postgres',
});
await this.pgClient.connect();
await this.pgClient.query('LISTEN table_update');
this.logger.log('Heartbeat Postgres Client Notifcation successful');
this.pgClient.on('notification', (msg) => {
this.logger.log(`PG NOTIFY: ${msg.payload}`);
this.server.emit('table_update', msg.payload);
});
this.pgClient.on('error', (err) => {
this.logger.error('Postgres error:', err);
this.handlePgDisconnect();
});
this.isConnected = true;
this.logger.log('Connected to Postgres for table_update notifications');
this.retryDelay = 1000;
break;
} catch (err) {
this.logger.error('Failed to connect to Postgres, retrying...', err);
this.isConnected = false;
await wait(this.retryDelay);
this.retryDelay = Math.min(this.retryDelay * 2, this.maxRetryDelay);
}
}
}
async handlePgDisconnect() {
this.isConnected = false;
if (this.pgClient) {
try {
await this.pgClient.end();
} catch (error) {
this.logger.error('Failed to disconnect from Postgres', error);
}
}
await this.connectToPostgres();
}
startHeartbeat() {
this.heartbeatInterval = setInterval(async () => {
if (!this.pgClient) return;
try {
await this.pgClient.query('SELECT 1');
} catch {
this.logger.error('Heartbeat failed, reconnecting to Postgres Client Notifcation');
this.handlePgDisconnect();
}
}, 10000);
}
handleConnection(client: Socket) {
this.connectedClients.add(client);
this.logger.log(`Client connected: ${client.id}`);
}
handleDisconnect(client: Socket) {
this.connectedClients.delete(client);
this.logger.log(`Client disconnected: ${client.id}`);
}
}
When running 2 seperate server local with pnpm start:frontend and pnpm start:backend, it works fine. But when run build-local-docker-image.sh then run docker image nocodb-local. It can’t connect to websocket anymore, but telemetry socket seems fine. I don’t understand, help me.