feat: Setup websockets with socket.io
This commit is contained in:
parent
861418db70
commit
618afe9840
@ -37,6 +37,8 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"drizzle-orm": "^0.33.0",
|
"drizzle-orm": "^0.33.0",
|
||||||
"postgres": "^3.4.4"
|
"postgres": "^3.4.4",
|
||||||
|
"socket.io": "^4.8.1",
|
||||||
|
"socket.io-client": "^4.8.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
prodServer.ts
Normal file
11
prodServer.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import * as path from 'path';
|
||||||
|
import * as url from 'url';
|
||||||
|
import { createWSSGlobalInstance, onHttpServerUpgrade } from './src/lib/server/webSocketUtils';
|
||||||
|
|
||||||
|
const __filename = url.fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
createWSSGlobalInstance();
|
||||||
|
|
||||||
|
const { server } = await import(path.resolve(__dirname, './build/index.js'));
|
||||||
|
server.server.on('upgrade', onHttpServerUpgrade);
|
29
src/lib/hooks.server.ts
Normal file
29
src/lib/hooks.server.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { building } from '$app/environment';
|
||||||
|
import type { Handle } from '@sveltejs/kit';
|
||||||
|
import { Server as SocketIOServer } from 'socket.io';
|
||||||
|
|
||||||
|
let io: SocketIOServer | undefined;
|
||||||
|
|
||||||
|
const startupSocketIOServer = (httpServer: never) => {
|
||||||
|
if (io) return;
|
||||||
|
console.log('[socket.io:kit] setup');
|
||||||
|
io = new SocketIOServer(httpServer);
|
||||||
|
io.on('connection', (socket) => {
|
||||||
|
console.log(`[socket.io:kit] client connected (${socket.id})`);
|
||||||
|
socket.emit('message', `Hello from SvelteKit ${new Date().toLocaleString()} (${socket.id})`);
|
||||||
|
|
||||||
|
socket.on('disconnect', () => {
|
||||||
|
console.log(`[socket.io:kit] client disconnected (${socket.id})`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handle = (async ({ event, resolve }) => {
|
||||||
|
if (!building) {
|
||||||
|
startupSocketIOServer(event.locals.httpServer);
|
||||||
|
event.locals.io = io;
|
||||||
|
}
|
||||||
|
return resolve(event, {
|
||||||
|
filterSerializedResponseHeaders: (name) => name === 'content-type'
|
||||||
|
});
|
||||||
|
}) satisfies Handle;
|
51
src/lib/server/webSocketUtils.ts
Normal file
51
src/lib/server/webSocketUtils.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { parse } from 'url';
|
||||||
|
import { WebSocketServer } from 'ws';
|
||||||
|
import { nanoid } from 'nanoid';
|
||||||
|
import type { Server, WebSocket as WebSocketBase } from 'ws';
|
||||||
|
import type { IncomingMessage } from 'http';
|
||||||
|
import type { Duplex } from 'stream';
|
||||||
|
|
||||||
|
export const GlobalThisWSS = Symbol.for('sveltekit.wss');
|
||||||
|
|
||||||
|
export interface ExtendedWebSocket extends WebSocketBase {
|
||||||
|
socketId: string;
|
||||||
|
// userId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// You can define server-wide functions or class instances here
|
||||||
|
// export interface ExtendedServer extends Server<ExtendedWebSocket> {};
|
||||||
|
|
||||||
|
export type ExtendedWebSocketServer = Server<ExtendedWebSocket>;
|
||||||
|
|
||||||
|
export type ExtendedGlobal = typeof globalThis & {
|
||||||
|
[GlobalThisWSS]: ExtendedWebSocketServer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const onHttpServerUpgrade = (req: IncomingMessage, sock: Duplex, head: Buffer) => {
|
||||||
|
const pathname = req.url ? parse(req.url).pathname : null;
|
||||||
|
if (pathname !== '/websocket') return;
|
||||||
|
|
||||||
|
const wss = (globalThis as ExtendedGlobal)[GlobalThisWSS];
|
||||||
|
|
||||||
|
wss.handleUpgrade(req, sock, head, (ws) => {
|
||||||
|
console.log('[handleUpgrade] creating new connecttion');
|
||||||
|
wss.emit('connection', ws, req);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createWSSGlobalInstance = () => {
|
||||||
|
const wss = new WebSocketServer({ noServer: true }) as ExtendedWebSocketServer;
|
||||||
|
|
||||||
|
(globalThis as ExtendedGlobal)[GlobalThisWSS] = wss;
|
||||||
|
|
||||||
|
wss.on('connection', (ws) => {
|
||||||
|
ws.socketId = nanoid();
|
||||||
|
console.log(`[wss:global] client connected (${ws.socketId})`);
|
||||||
|
|
||||||
|
ws.on('close', () => {
|
||||||
|
console.log(`[wss:global] client disconnected (${ws.socketId})`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return wss;
|
||||||
|
};
|
@ -1,2 +1,44 @@
|
|||||||
<h1>Welcome to SvelteKit</h1>
|
<script lang="ts">
|
||||||
<p>Visit <a href="https://svelte.dev/docs/kit">svelte.dev/docs/kit</a> to read the documentation</p>
|
import { io } from 'socket.io-client';
|
||||||
|
|
||||||
|
let socket: ReturnType<typeof io> | null = null;
|
||||||
|
let log: string[] = [];
|
||||||
|
|
||||||
|
function logEvent(str: string) {
|
||||||
|
log = [...log, str];
|
||||||
|
}
|
||||||
|
|
||||||
|
function establishSocketIOConnection() {
|
||||||
|
if (socket) return;
|
||||||
|
socket = io();
|
||||||
|
|
||||||
|
socket.on('connect', () => {
|
||||||
|
console.log('[ws] connection open');
|
||||||
|
logEvent('[ws] connection open');
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('disconnect', () => {
|
||||||
|
console.log('[ws] connection closed');
|
||||||
|
logEvent('[ws] connection closed');
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('message', (data: string) => {
|
||||||
|
console.log('[ws] message received', data);
|
||||||
|
logEvent(`[ws] message received: ${data}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1 class="text-lg"># SvelteKit with Socket.IO Integration</h1>
|
||||||
|
|
||||||
|
<button class="button" on:click={() => establishSocketIOConnection()}>
|
||||||
|
Establish Socket.IO connection
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{#each log as event}
|
||||||
|
<li>{event}</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</main>
|
@ -2,10 +2,5 @@ import type { Config } from 'tailwindcss';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
content: ['./src/**/*.{html,js,svelte,ts}'],
|
content: ['./src/**/*.{html,js,svelte,ts}'],
|
||||||
|
|
||||||
theme: {
|
|
||||||
extend: {}
|
|
||||||
},
|
|
||||||
|
|
||||||
plugins: []
|
plugins: []
|
||||||
} satisfies Config;
|
} satisfies Config;
|
||||||
|
@ -1,6 +1,33 @@
|
|||||||
import { sveltekit } from '@sveltejs/kit/vite';
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
|
import { Server as SocketIOServer } from 'socket.io';
|
||||||
|
|
||||||
|
function setupSocketIOServer(httpServer: never) {
|
||||||
|
if (!httpServer) {
|
||||||
|
throw new Error('HTTP server is not available');
|
||||||
|
}
|
||||||
|
const io = new SocketIOServer(httpServer);
|
||||||
|
io.on('connection', (socket) => {
|
||||||
|
console.log(`[socket.io] client connected (${socket.id})`);
|
||||||
|
io.emit('message', `Hello from SvelteKit ${new Date().toLocaleString()} (${socket.id})`);
|
||||||
|
|
||||||
|
socket.on('disconnect', () => {
|
||||||
|
console.log(`[socket.io] client disconnected (${socket.id})`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [sveltekit()]
|
plugins: [
|
||||||
|
sveltekit(),
|
||||||
|
{
|
||||||
|
name: 'integratedSocketIOServer',
|
||||||
|
configureServer(server) {
|
||||||
|
setupSocketIOServer(server.httpServer);
|
||||||
|
},
|
||||||
|
configurePreviewServer(server) {
|
||||||
|
setupSocketIOServer(server.httpServer);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
});
|
});
|
Loading…
Reference in New Issue
Block a user