From ba7097207c7f7790436cf458aa78ae2818d6b356 Mon Sep 17 00:00:00 2001 From: aronmal Date: Tue, 25 Apr 2023 22:27:23 +0200 Subject: [PATCH] SocketIO with auth working --- leaky-ships/components/SocketIO.tsx | 51 +++++++++++--------- leaky-ships/interfaces/NextApiSocket.ts | 39 ++++++++++++++- leaky-ships/lib/getObjectChecksum.ts | 7 +++ leaky-ships/pages/api/ws.ts | 50 ++++--------------- leaky-ships/pages/api/ws2.ts | 64 +++++++++++++++++++++++++ leaky-ships/pages/socket.tsx | 40 +++++++++------- 6 files changed, 167 insertions(+), 84 deletions(-) create mode 100644 leaky-ships/lib/getObjectChecksum.ts create mode 100644 leaky-ships/pages/api/ws2.ts diff --git a/leaky-ships/components/SocketIO.tsx b/leaky-ships/components/SocketIO.tsx index 52a0523..fdabef1 100644 --- a/leaky-ships/components/SocketIO.tsx +++ b/leaky-ships/components/SocketIO.tsx @@ -1,37 +1,42 @@ -import { ClientToServerEvents, ServerToClientEvents } from "../pages/api/ws" +import { cSocket } from "../interfaces/NextApiSocket" +import useGameState from "@lib/hooks/useGameState" import { useEffect } from "react" -import { io, Socket } from "socket.io-client" +import { io } from "socket.io-client" + +let socket: cSocket function SocketIO() { + const { session, status } = useGameState() + useEffect(() => { - socketInitializer() - }, []) - - const socketInitializer = async () => { - await fetch("/api/ws") - - const socket: Socket = io() - socket.on("test2", (warst) => { - console.log("Test2:", warst, socket.id) + if (status === "loading" || socket) return + socket = io({ + path: "/api/ws2", }) + socket.on("connect", () => { - console.log(socket.connected) // true - setTimeout(() => { - socket.emit("test", "warst") - socket.emit("test", "tsra") - socket.emit("test", "1234") - // socket.disconnect() - }, 1000) + console.log("connected") }) - socket.on("test", () => { - console.log("Got test1234") // false + socket.on("authenticated", () => { + console.log("Yay!") }) - socket.on("disconnect", () => { - console.log(socket.connected) // false + socket.on("unauthenticated", () => { + console.log("No... pls work") }) - } + + return () => { + socket.off("connect") + socket.off("authenticated") + socket.off("unauthenticated") + } + }, [status]) + + useEffect(() => { + console.log(4, socket) + socket?.emit("authenticate", { token: `hello from ${session?.user.email}` }) + }, [session?.user.email, status]) return
SocketIO
} diff --git a/leaky-ships/interfaces/NextApiSocket.ts b/leaky-ships/interfaces/NextApiSocket.ts index 9781e0a..9e31ec9 100644 --- a/leaky-ships/interfaces/NextApiSocket.ts +++ b/leaky-ships/interfaces/NextApiSocket.ts @@ -1,7 +1,9 @@ +import { User } from "@prisma/client" import type { Server as HTTPServer } from "http" import type { Socket as NetSocket } from "net" import type { NextApiResponse } from "next" -import type { Server as IOServer } from "socket.io" +import type { Server as IOServer, Server } from "socket.io" +import { Socket } from "socket.io-client" interface SocketServer extends HTTPServer { io?: IOServer @@ -14,3 +16,38 @@ interface SocketWithIO extends NetSocket { export interface NextApiResponseWithSocket extends NextApiResponse { socket: SocketWithIO } + +export interface ServerToClientEvents { + // noArg: () => void + // basicEmit: (a: number, b: string, c: Buffer) => void + // withAck: (d: string, callback: (e: number) => void) => void + "update-input": (msg: string) => void + unauthenticated: () => void + authenticated: () => void + test2: (test: string) => void + test: () => void +} + +export interface ClientToServerEvents { + // hello: () => void + "input-change": (msg: string) => void + authenticate: (payload: { token: string }) => void + test: (payload: any) => void +} + +interface InterServerEvents { + // ping: () => void +} + +interface SocketData { + user: User | null +} + +export type cServer = Server< + ClientToServerEvents, + ServerToClientEvents, + InterServerEvents, + SocketData +> + +export type cSocket = Socket diff --git a/leaky-ships/lib/getObjectChecksum.ts b/leaky-ships/lib/getObjectChecksum.ts new file mode 100644 index 0000000..1d85142 --- /dev/null +++ b/leaky-ships/lib/getObjectChecksum.ts @@ -0,0 +1,7 @@ +import crypto from "crypto" + +export function getObjectChecksum(obj: Object) { + const objString = JSON.stringify(obj) + const hash = crypto.createHash("md5").update(objString).digest("hex") + return hash +} diff --git a/leaky-ships/pages/api/ws.ts b/leaky-ships/pages/api/ws.ts index cc6e7c8..e2b9e96 100644 --- a/leaky-ships/pages/api/ws.ts +++ b/leaky-ships/pages/api/ws.ts @@ -1,58 +1,24 @@ -import type { NextApiResponseWithSocket } from "../../interfaces/NextApiSocket" -import prisma from "@lib/prisma" +import type { + NextApiResponseWithSocket, + cServer, +} from "../../interfaces/NextApiSocket" import type { NextApiRequest } from "next" import { Server } from "socket.io" -export interface ServerToClientEvents { - // noArg: () => void - // basicEmit: (a: number, b: string, c: Buffer) => void - // withAck: (d: string, callback: (e: number) => void) => void - "update-input": (msg: string) => void - unauthenticated: () => void - authenticated: () => void - test2: (test: string) => void - test: () => void -} - -export interface ClientToServerEvents { - // hello: () => void - "input-change": (msg: string) => void - authenticate: (payload: { token: string }) => void - test: (payload: any) => void -} - -interface InterServerEvents { - // ping: () => void -} - -interface SocketData { - // name: string - // age: number - isAuthenticated: boolean -} - -// const authenticate = (socket, next) => { -// socket.isAuthenticated = false -// -// } - const SocketHandler = (req: NextApiRequest, res: NextApiResponseWithSocket) => { if (res.socket.server.io) { console.log("Socket is already running " + req.url) } else { console.log("Socket is initializing " + req.url) - const io = new Server< - ClientToServerEvents, - ServerToClientEvents, - InterServerEvents, - SocketData - >(res.socket.server) + const io: cServer = new Server(res.socket.server, { + path: "/api/ws", + }) res.socket.server.io = io // io.use(authenticate) io.on("connection", (socket) => { socket.on("input-change", (msg) => { - if (!socket.data.isAuthenticated) socket.emit("unauthenticated") + if (!socket.data.user) socket.emit("unauthenticated") // socket.broadcast.emit("update-input", msg) io.emit("update-input", msg) }) diff --git a/leaky-ships/pages/api/ws2.ts b/leaky-ships/pages/api/ws2.ts new file mode 100644 index 0000000..06437ef --- /dev/null +++ b/leaky-ships/pages/api/ws2.ts @@ -0,0 +1,64 @@ +import { + NextApiResponseWithSocket, + cServer, +} from "../../interfaces/NextApiSocket" +import prisma from "@lib/prisma" +import colors from "colors" +import { NextApiRequest } from "next" +import { getSession } from "next-auth/react" +import { Server } from "socket.io" + +colors.enable() + +const SocketHandler = async ( + req: NextApiRequest, + res: NextApiResponseWithSocket +) => { + if (res.socket.server.io) { + console.log("Socket is already running " + req.url) + } else { + console.log("Socket is initializing " + req.url) + const io: cServer = new Server(res.socket.server, { path: "/api/ws2" }) + + res.socket.server.io = io + + // io.use(authenticate) + io.use(async (socket, next) => { + try { + const session = await getSession({ + req: socket.request, + }) + socket.data.user = await prisma.user.findUnique({ + where: { + id: session?.user.id, + }, + }) + next() + } catch (err) { + console.log("Unauthorized") + next(new Error("Unauthorized")) + } + }) + + io.on("connection", (socket) => { + console.log(`User connected <${socket.data.user?.email}>`.green) + + socket.on("authenticate", (msg) => { + // if (!socket.data.isAuthenticated) socket.emit("unauthenticated") + // socket.broadcast.emit("update-input", msg) + // io.emit("update-input", msg) + console.log(msg) + }) + + socket.on("test", (payload) => { + console.log("Got test:", payload) + // ... + }) + + socket.emit("test2", "lol") + }) + } + res.end() +} + +export default SocketHandler diff --git a/leaky-ships/pages/socket.tsx b/leaky-ships/pages/socket.tsx index 9759892..fc00e5f 100644 --- a/leaky-ships/pages/socket.tsx +++ b/leaky-ships/pages/socket.tsx @@ -1,27 +1,24 @@ -import { ClientToServerEvents, ServerToClientEvents } from "./api/ws" +import { cSocket } from "../interfaces/NextApiSocket" +import useGameState from "@lib/hooks/useGameState" import { ChangeEventHandler, useEffect, useState } from "react" -import { io, Socket } from "socket.io-client" +import { io } from "socket.io-client" -let socket: Socket +let socket: cSocket const Home = () => { const [input, setInput] = useState("") const [data, setData] = useState("") - useEffect(() => { - socketInitializer() - }, []) + const { status } = useGameState() - async function socketInitializer() { - await fetch("/api/ws") - socket = io() - await fetch("/api/login", { - method: "POST", - body: JSON.stringify({ username: "warst", password: "warst" }), - }) - // getAccessToken().then((token) => { - // socket.emit("authenticate", { token }) - // }) + useEffect(() => { + if (status === "loading") return + if (!socket) { + console.log("Socket init.") + socket = io({ + path: "/api/ws", + }) + } else console.log("Socket present.") socket.on("connect", () => { console.log("connected") @@ -38,11 +35,18 @@ const Home = () => { socket.on("update-input", (msg) => { setData(msg) }) - } + + return () => { + socket.off("connect") + socket.off("authenticated") + socket.off("unauthenticated") + socket.off("update-input") + } + }, [status, setData]) const onChangeHandler: ChangeEventHandler = (e) => { setInput(e.target.value) - socket.emit("input-change", e.target.value) + socket?.emit("input-change", e.target.value) } return (