diff --git a/backend/dist/app.js b/backend/dist/app.js index 604e049..c5596d2 100644 --- a/backend/dist/app.js +++ b/backend/dist/app.js @@ -6,13 +6,13 @@ import { conversationRouter } from "./routes/conversation.routes.js"; import { errorHandler } from "./utils/errorHandler.js"; import { app } from "./lib/socket.js"; const corsOptions = { - origin: "https://sect-chat.netlify.app", + origin: process.env.NODE_ENV === "production" ? ["https://sect-chat.netlify.app"] : ["http://localhost:5173"], credentials: true }; app.use(cors(corsOptions)); app.use(cookieParser()); app.use(express.json()); -app.options("/{*path}", cors(corsOptions)); +// app.options("/{*path}", cors(corsOptions)) app.use("/api/auth", authRouter); app.use("/api/conversations", conversationRouter); app.use(errorHandler); diff --git a/backend/dist/app.js.map b/backend/dist/app.js.map index be9e0d1..40fd35b 100644 --- a/backend/dist/app.js.map +++ b/backend/dist/app.js.map @@ -1 +1 @@ -{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AAErC,MAAM,WAAW,GAAG;IAChB,MAAM,EAAE,+BAA+B;IACvC,WAAW,EAAE,IAAI;CACpB,CAAC;AAEF,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;AAC3B,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;AACxB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACxB,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAA;AAC1C,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;AACjC,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,kBAAkB,CAAC,CAAC;AAClD,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAEtB,eAAe,GAAG,CAAC"} \ No newline at end of file +{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAA;AAErC,MAAM,WAAW,GAAG;IAChB,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAA,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,CAAC,CAAC,uBAAuB,CAAC;IAC5G,WAAW,EAAE,IAAI;CACpB,CAAC;AAEF,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;AAC3B,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;AACxB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AACxB,6CAA6C;AAC7C,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;AACjC,GAAG,CAAC,GAAG,CAAC,oBAAoB,EAAE,kBAAkB,CAAC,CAAC;AAClD,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;AAEtB,eAAe,GAAG,CAAC"} \ No newline at end of file diff --git a/backend/dist/config/env.d.ts b/backend/dist/config/env.d.ts index a1915cc..c58ed1d 100644 --- a/backend/dist/config/env.d.ts +++ b/backend/dist/config/env.d.ts @@ -1,7 +1,7 @@ export declare const ENV: { PORT: number; DB_URL: string; - NODE_ENV: "DEVELOPMENT" | "TEST" | "production"; + NODE_ENV: "development" | "TEST" | "production"; JWT_SECRET: string; }; //# sourceMappingURL=env.d.ts.map \ No newline at end of file diff --git a/backend/dist/config/env.js b/backend/dist/config/env.js index 62c3d17..9b2d39b 100644 --- a/backend/dist/config/env.js +++ b/backend/dist/config/env.js @@ -3,8 +3,8 @@ const EnvSchema = z.object({ PORT: z.coerce.number().int().positive(), DB_URL: z.string().min(1), NODE_ENV: z - .enum(["DEVELOPMENT", "TEST", "production"]) - .default("DEVELOPMENT"), + .enum(["development", "TEST", "production"]) + .default("development"), JWT_SECRET: z.string().min(1), }); export const ENV = EnvSchema.parse(process.env); diff --git a/backend/dist/lib/socket.d.ts.map b/backend/dist/lib/socket.d.ts.map index 52a8d3f..0620752 100644 --- a/backend/dist/lib/socket.d.ts.map +++ b/backend/dist/lib/socket.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../src/lib/socket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,QAAA,MAAM,GAAG,6CAAY,CAAC;AACtB,QAAA,MAAM,MAAM,sEAAyB,CAAC;AAEtC,QAAA,MAAM,EAAE,+HAKN,CAAC;AAyBH,OAAO,EAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../src/lib/socket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,QAAA,MAAM,GAAG,6CAAY,CAAC;AACtB,QAAA,MAAM,MAAM,sEAAyB,CAAC;AAEtC,QAAA,MAAM,EAAE,+HAKN,CAAC;AA6CH,OAAO,EAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAC,CAAC"} \ No newline at end of file diff --git a/backend/dist/lib/socket.js b/backend/dist/lib/socket.js index 3a79350..a58f2eb 100644 --- a/backend/dist/lib/socket.js +++ b/backend/dist/lib/socket.js @@ -5,7 +5,7 @@ const app = express(); const server = http.createServer(app); const io = new Server(server, { cors: { - origin: process.env.NODE_ENV === "production" ? ["https://your-frontend.onrender.com"] : ["http://localhost:5173"], + origin: process.env.NODE_ENV === "production" ? ["https://sect-chat.netlify.app"] : ["http://localhost:5173"], credentials: true } }); @@ -24,6 +24,21 @@ io.on("connection", (socket) => { }); socket.on("typing", (room, senderUsername) => socket.in(room).emit("is typing", senderUsername)); socket.on("not typing", (room, senderUsername) => socket.in(room).emit("is not typing", senderUsername)); + socket.on('call-user', ({ to, fromId, from, offer }) => { + socket.to(to).emit('incoming-call', { fromId, from, offer }); + }); + socket.on('answer-call', ({ to, answer }) => { + socket.to(to).emit('call-answered', answer); + }); + socket.on('decline-call', ({ to }) => { + socket.to(to).emit('call-declined'); + }); + socket.on('ice-candidate', ({ to, candidate }) => { + socket.to(to).emit('ice-candidate', candidate); + }); + socket.on('end-call', ({ to }) => { + socket.to(to).emit('call-ended'); + }); }); export { io, app, server }; //# sourceMappingURL=socket.js.map \ No newline at end of file diff --git a/backend/dist/lib/socket.js.map b/backend/dist/lib/socket.js.map index 77791f2..8ab4f9c 100644 --- a/backend/dist/lib/socket.js.map +++ b/backend/dist/lib/socket.js.map @@ -1 +1 @@ -{"version":3,"file":"socket.js","sourceRoot":"","sources":["../../src/lib/socket.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AACtB,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;AAEtC,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE;IAC5B,IAAI,EAAE;QACJ,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAA,CAAC,CAAC,CAAC,oCAAoC,CAAC,CAAC,CAAC,CAAC,CAAC,uBAAuB,CAAC;QACjH,WAAW,EAAE,IAAI;KAClB;CACF,CAAC,CAAC;AAEH,EAAE,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,EAAE;IAC7B,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE;QAC9B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE;QACvC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,IAAI,EAAE,EAAE;QACvC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,aAAa,EAAE,CAAC,UAAU,EAAE,YAAY,EAAE,EAAE;QACpD,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC,CAAC,uDAAuD;IACtI,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC;IAEjG,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC,CAAC;AAE3G,CAAC,CAAC,CAAA;AAEF,OAAO,EAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAC,CAAC"} \ No newline at end of file +{"version":3,"file":"socket.js","sourceRoot":"","sources":["../../src/lib/socket.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/server.js b/backend/dist/server.js index 98a4f7b..f392180 100644 --- a/backend/dist/server.js +++ b/backend/dist/server.js @@ -15,7 +15,6 @@ const startServer = async () => { await connectDB(); console.log("Mongo connected to:", mongoose.connection.name); server.listen(PORT, () => { console.log("Backend running on port", ENV.PORT); }); - console.log("NEW DEPLOY TEST"); } catch (error) { console.error("Error starting the server", error); diff --git a/backend/dist/server.js.map b/backend/dist/server.js.map index 32046ab..ff097ca 100644 --- a/backend/dist/server.js.map +++ b/backend/dist/server.js.map @@ -1 +1 @@ -{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,GAAG,MAAM,UAAU,CAAA;AAC1B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;AAEjC,IAAG,GAAG,CAAC,QAAQ,KAAK,YAAY;IAC9B,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;AAEvE,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,IAAI,CAAA;AAE7B,MAAM,WAAW,GAAG,KAAK,IAAI,EAAE;IAC7B,IAAG,CAAC;QACA,MAAM,SAAS,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC7D,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,GAAE,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA,CAAA,CAAC,CAAC,CAAC;QAC9E,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACjC,CAAC;IACH,OAAM,KAAK,EAAC,CAAC;QACT,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;IACpD,CAAC;AACH,CAAC,CAAA;AAEH,WAAW,EAAE,CAAC"} \ No newline at end of file +{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzC,OAAO,GAAG,MAAM,UAAU,CAAA;AAC1B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;AAEjC,IAAG,GAAG,CAAC,QAAQ,KAAK,YAAY;IAC9B,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,qBAAqB,CAAC,CAAC,CAAC,CAAC;AAEvE,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,IAAI,CAAA;AAE7B,MAAM,WAAW,GAAG,KAAK,IAAI,EAAE;IAC7B,IAAG,CAAC;QACA,MAAM,SAAS,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC7D,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE,GAAE,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA,CAAA,CAAC,CAAC,CAAC;IAChF,CAAC;IACH,OAAM,KAAK,EAAC,CAAC;QACT,OAAO,CAAC,KAAK,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;IACpD,CAAC;AACH,CAAC,CAAA;AAEH,WAAW,EAAE,CAAC"} \ No newline at end of file diff --git a/backend/dist/utils/generateToken.js b/backend/dist/utils/generateToken.js index dc38b38..d5b7d51 100644 --- a/backend/dist/utils/generateToken.js +++ b/backend/dist/utils/generateToken.js @@ -5,7 +5,7 @@ export const generateToken = (id, res) => { res.cookie("jwt", token, { maxAge: 7 * 24 * 60 * 60 * 1000, //the age for cookie's lifetime must be in ms httpOnly: true, - sameSite: "none", + sameSite: "lax", secure: ENV.NODE_ENV === "production", }); return token; diff --git a/backend/dist/utils/generateToken.js.map b/backend/dist/utils/generateToken.js.map index 69e7307..440c1c2 100644 --- a/backend/dist/utils/generateToken.js.map +++ b/backend/dist/utils/generateToken.js.map @@ -1 +1 @@ -{"version":3,"file":"generateToken.js","sourceRoot":"","sources":["../../src/utils/generateToken.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAA;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAGtC,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAAS,EAAE,GAAY,EAAE,EAAE;IACvD,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,EAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,EAAE,EAAC,EAAE,GAAG,CAAC,UAAU,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,CAAC,CAAC;IAE/E,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE;QACvB,MAAM,EAAE,CAAC,GAAC,EAAE,GAAC,EAAE,GAAC,EAAE,GAAC,IAAI,EAAG,6CAA6C;QACvE,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,GAAG,CAAC,QAAQ,KAAK,YAAY;KACtC,CAAC,CAAA;IAEF,OAAO,KAAK,CAAC;AACf,CAAC,CAAA"} \ No newline at end of file +{"version":3,"file":"generateToken.js","sourceRoot":"","sources":["../../src/utils/generateToken.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAA;AAC9B,OAAO,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAA;AAGtC,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,EAAS,EAAE,GAAY,EAAE,EAAE;IACvD,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,EAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,EAAE,EAAC,EAAE,GAAG,CAAC,UAAU,EAAE,EAAC,SAAS,EAAE,IAAI,EAAC,CAAC,CAAC;IAE/E,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE;QACvB,MAAM,EAAE,CAAC,GAAC,EAAE,GAAC,EAAE,GAAC,EAAE,GAAC,IAAI,EAAG,6CAA6C;QACvE,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,GAAG,CAAC,QAAQ,KAAK,YAAY;KACtC,CAAC,CAAA;IAEF,OAAO,KAAK,CAAC;AACf,CAAC,CAAA"} \ No newline at end of file diff --git a/backend/src/app.ts b/backend/src/app.ts index df4bbe9..f4012e2 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -7,14 +7,14 @@ import { errorHandler } from "./utils/errorHandler.js"; import { app } from "./lib/socket.js" const corsOptions = { - origin: "https://sect-chat.netlify.app", + origin: process.env.NODE_ENV === "production"? ["https://sect-chat.netlify.app"] : ["http://localhost:5173"], credentials: true }; app.use(cors(corsOptions)); app.use(cookieParser()); app.use(express.json()); -app.options("/{*path}", cors(corsOptions)) +// app.options("/{*path}", cors(corsOptions)) app.use("/api/auth", authRouter); app.use("/api/conversations", conversationRouter); app.use(errorHandler); diff --git a/backend/src/config/env.ts b/backend/src/config/env.ts index 3222c14..080a792 100644 --- a/backend/src/config/env.ts +++ b/backend/src/config/env.ts @@ -4,8 +4,8 @@ const EnvSchema = z.object({ PORT: z.coerce.number().int().positive(), DB_URL: z.string().min(1), NODE_ENV: z - .enum(["DEVELOPMENT", "TEST", "production"]) - .default("DEVELOPMENT"), + .enum(["development", "TEST", "production"]) + .default("development"), JWT_SECRET: z.string().min(1), }); diff --git a/backend/src/lib/socket.ts b/backend/src/lib/socket.ts index fc028ad..ffcdded 100644 --- a/backend/src/lib/socket.ts +++ b/backend/src/lib/socket.ts @@ -7,7 +7,7 @@ const server = http.createServer(app); const io = new Server(server, { cors: { - origin: process.env.NODE_ENV === "production"? ["https://your-frontend.onrender.com"] : ["http://localhost:5173"], + origin: process.env.NODE_ENV === "production"? ["https://sect-chat.netlify.app"] : ["http://localhost:5173"], credentials: true } }); @@ -33,6 +33,26 @@ io.on("connection", (socket) => { socket.on("not typing", (room, senderUsername) => socket.in(room).emit("is not typing", senderUsername)); + socket.on('call-user', ({ to, fromId, from, offer }) => { + socket.to(to).emit('incoming-call', { fromId, from, offer }); + }); + + socket.on('answer-call', ({ to, answer }) => { + socket.to(to).emit('call-answered', answer); + }); + + socket.on('decline-call', ({ to }) => { + socket.to(to).emit('call-declined'); + }); + + socket.on('ice-candidate', ({ to, candidate }) => { + socket.to(to).emit('ice-candidate', candidate); + }); + + socket.on('end-call', ({ to }) => { + socket.to(to).emit('call-ended'); + }); + }) export {io, app, server}; \ No newline at end of file diff --git a/backend/src/server.ts b/backend/src/server.ts index 47dd60a..e4021bb 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -19,7 +19,6 @@ const startServer = async () => { await connectDB(); console.log("Mongo connected to:", mongoose.connection.name); server.listen(PORT, () => {console.log("Backend running on port", ENV.PORT)}); - console.log("NEW DEPLOY TEST"); } catch(error){ console.error("Error starting the server", error); diff --git a/backend/src/utils/generateToken.ts b/backend/src/utils/generateToken.ts index 5fa7090..c046898 100644 --- a/backend/src/utils/generateToken.ts +++ b/backend/src/utils/generateToken.ts @@ -8,7 +8,7 @@ export const generateToken = (id:string, res:Response) => { res.cookie("jwt", token, { maxAge: 7*24*60*60*1000, //the age for cookie's lifetime must be in ms httpOnly: true, - sameSite: "none", + sameSite: "lax", secure: ENV.NODE_ENV === "production", }) diff --git a/frontend/src/components/AuthForm.tsx b/frontend/src/components/AuthForm.tsx index 0416c0b..54d63ed 100644 --- a/frontend/src/components/AuthForm.tsx +++ b/frontend/src/components/AuthForm.tsx @@ -1,6 +1,6 @@ import React from 'react' import {useForm} from "react-hook-form" -import type { SubmitHandler } from 'react-hook-form' +import type { SubmitHandler, UseFormRegister } from 'react-hook-form' import {zodResolver} from "@hookform/resolvers/zod" import { authStore } from '../store/useAuthStore'; import { useShallow } from 'zustand/react/shallow' @@ -76,7 +76,7 @@ const AuthForm: React.FC = () => { label={field.label} type={field.type} placeholder={field.placeholder} - register={register as any} + register={register as UseFormRegister} errors={errors} touched={!!touchedFields[field.name as keyof typeof touchedFields]} /> diff --git a/frontend/src/components/IncomingCallModal.tsx b/frontend/src/components/IncomingCallModal.tsx new file mode 100644 index 0000000..d88d35f --- /dev/null +++ b/frontend/src/components/IncomingCallModal.tsx @@ -0,0 +1,94 @@ +import { useEffect } from "react"; +import { PhoneOff, Lock } from "lucide-react"; +import { Button } from "./ui/button"; +import { useWebRTC } from "../hooks/useWebRTC"; +import { callStore } from "../store/useCallStore"; +import { useShallow } from "zustand/shallow"; + + interface IncomingCallModalProps { + isOpen: boolean; + closeCall: () => void; + } + + const IncomingCallModal = ({ + isOpen, + closeCall, + }: IncomingCallModalProps) => { + const { acceptCall, endCall } = useWebRTC(); + const { + remoteId, + remoteUsername, + incomingOffer + } = callStore( + useShallow((state) => ({ + remoteId: state.remoteId, + remoteUsername: state.remoteUsername, + incomingOffer: state.incomingOffer + }))); + + useEffect(() => { + if (!isOpen) return; + const timeout = setTimeout(() => {if (remoteId) endCall(remoteId)}, 30000); + return () => clearTimeout(timeout); + }, [isOpen, endCall, remoteId]); + + if (!isOpen) return null; + + return ( +
+
+ {/* Header */} +
+ + + Incoming encrypted video call + +
+ + {/* Avatar */} +
+
+ {/* Pulse rings */} +
+
+
+ + {remoteUsername?.toUpperCase()} + +
+
+ +

{remoteUsername}

+

is calling you...

+
+ + {/* Actions */} +
+
+ + Decline +
+ +
+ + Accept +
+
+
+
+ ); +}; + +export default IncomingCallModal; diff --git a/frontend/src/components/MessageArea.tsx b/frontend/src/components/MessageArea.tsx index c9c3527..6b4995b 100644 --- a/frontend/src/components/MessageArea.tsx +++ b/frontend/src/components/MessageArea.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { ArrowLeft, Lock, User } from 'lucide-react' +import { ArrowLeft, Lock, User, VideoIcon } from 'lucide-react' import TextArea from './TextArea' import { chatStore, type Message } from '../store/useChatStore' import { useShallow } from 'zustand/shallow' @@ -7,6 +7,8 @@ import { formatMessageTime } from '../lib/utils' import { socket } from '../lib/socket' import { authStore } from '../store/useAuthStore' import MessageSkeleton from './MessageSkeleton' +import { Button } from './ui/button' +import { useWebRTC } from '../hooks/useWebRTC' type MessageAreaProps = { onBack?: () => void; @@ -28,6 +30,8 @@ const MessageArea = ({onBack}: MessageAreaProps) => { isMessagesLoading: state.isMessagesLoading, }))); + const { initiateCall } = useWebRTC(); + React.useEffect(() => { if (messageEndRef.current && messages) setTimeout(() => { @@ -91,10 +95,16 @@ const MessageArea = ({onBack}: MessageAreaProps) => { End-to-end encrypted
+ - {/* Messages Area */}
@@ -108,7 +118,6 @@ const MessageArea = ({onBack}: MessageAreaProps) => { }> {messages && (messages.map((message: Message) => { return( - void; + } + + const VideoCallModal = ({ isOpen, closeCall }: VideoCallModalProps) => { + const { + remoteUsername, + callStatus, + isVideoOff, + isMuted, + toggleAudio, + toggleVideo + } = callStore(useShallow((state) => ({ + remoteUsername: state.remoteUsername, + callStatus: state.callStatus, + isVideoOff: state.isVideoOff, + isMuted: state.isMuted, + toggleAudio: state.toggleAudio, + toggleVideo: state.toggleVideo, + }))) + + const localVideoRef = useRef(null); + const streamRef = useRef(null); + const { endCall } = useWebRTC(); + const { remoteId } = callStore((useShallow((state) => ({ remoteId: state.remoteId})))) + + // Acquire local media when call opens + useEffect(() => { + if (!isOpen) return; + + return () => { + if (streamRef.current) { + streamRef.current.getTracks().forEach(t => t.stop()); + streamRef.current = null; + } + }; + }, [isOpen]); + + // Auto-close after declined + useEffect(() => { + if (callStatus !== "declined") return; + const t = setTimeout(() => {if (remoteId) endCall(remoteId); closeCall();}, 2500); + return () => clearTimeout(t); + }, [callStatus, endCall, remoteId, closeCall]); + + if (!isOpen) return null; + + return ( +
+
+ {/* Header */} +
+
+
+
+ + {remoteUsername?.toUpperCase()} + +
+
+
+
+

{remoteUsername}

+
+ + + {callStatus === "calling" && "Ringing..."} + {callStatus === "declined" && "Call ended"} + +
+
+
+ +
+ + + {callStatus === "calling" && "Calling"} + {callStatus === "connected" && "Live"} + {callStatus === "declined" && "Declined"} + +
+
+ + {/* Video stage */} +
+ {/* Remote video placeholder (mock) */} +
+
+
+ {callStatus === "calling" && ( + <> +
+
+ + )} + + {remoteUsername?.toUpperCase()} + +
+

{remoteUsername}

+ + {callStatus === "calling" && ( +

+ Ringing @{remoteUsername}... +

+ )} + {callStatus === "connected" && ( +

+ Remote video stream is simulated in this preview. +

+ )} + {callStatus === "declined" && ( +
+ + + {remoteUsername} declined the call + +
+ )} +
+
+ + {/* Local video PiP */} +
+ {!isVideoOff ? ( +
+
+ + {/* Controls */} +
+
+ + + + + + +
+ + +
+

+ 🔒 Call protected by sect end-to-end encryption +

+
+
+
+ ); +}; + +export default VideoCallModal; diff --git a/frontend/src/components/ui/button.tsx b/frontend/src/components/ui/button.tsx index dcfee0c..d20dee9 100644 --- a/frontend/src/components/ui/button.tsx +++ b/frontend/src/components/ui/button.tsx @@ -54,4 +54,4 @@ const Button = React.forwardRef( ) Button.displayName = "Button" -export { Button, buttonVariants } +export { Button } diff --git a/frontend/src/hooks/useCallSocket.tsx b/frontend/src/hooks/useCallSocket.tsx new file mode 100644 index 0000000..db48762 --- /dev/null +++ b/frontend/src/hooks/useCallSocket.tsx @@ -0,0 +1,54 @@ +import { useShallow } from "zustand/shallow"; +import { callStore } from "../store/useCallStore"; +import { useWebRTC } from "./useWebRTC"; +import { useEffect } from "react"; +import { socket } from "../lib/socket"; + +const useCallSocket = () => { + + const { + setCallStatus, + setIncomingOffer, + setRemote, + resetCall, + + } = callStore(useShallow((state) => ({ + setCallStatus: state.setCallStatus, + setIncomingOffer: state.setIncomingOffer, + setRemote: state.setRemote, + resetCall: state.resetCall + }))); + + const { handleRemoteAnswer, addIceCandidate, cleanupConnection } = useWebRTC(); + + useEffect(() => { + socket.on('incoming-call', ({ fromId, from, offer }) => { + setRemote(fromId, from); + setIncomingOffer(offer); + setCallStatus('receiving'); + }); + + socket.on('call-answered', handleRemoteAnswer); + + socket.on('call-declined', () => { + cleanupConnection(); + resetCall(); + }); + + socket.on('ice-candidate', addIceCandidate); + + socket.on('call-ended', () => { + cleanupConnection(); + resetCall(); + }); + + return () => { socket.disconnect(); }; + }, []) + + return ( +
+
+ ) +} + +export default useCallSocket diff --git a/frontend/src/hooks/useWebRTC.tsx b/frontend/src/hooks/useWebRTC.tsx new file mode 100644 index 0000000..004ad75 --- /dev/null +++ b/frontend/src/hooks/useWebRTC.tsx @@ -0,0 +1,123 @@ +import { useRef, useCallback } from 'react'; +import { callStore } from '../store/useCallStore'; +import { useShallow } from 'zustand/shallow'; +import { socket } from '../lib/socket'; +import { authStore } from '../store/useAuthStore'; + +const ICE_SERVERS = { + iceServers: [{ urls: 'stun:stun.l.google.com:19302' }], + }; + + export function useWebRTC() { + const pcRef = useRef(null); + const { + setLocalStream, + setRemoteStream, + setCallStatus, + setRemote, + resetCall, + localStream + } = callStore(useShallow((state) => ({ + setLocalStream: state.setLocalStream, + setRemoteStream: state.setRemoteStream, + setCallStatus: state.setCallStatus, + setRemote: state.setRemote, + resetCall: state.resetCall, + localStream: state.localStream + }))); + + const authUser = authStore((state) => state.authUser); + + const cleanupTracks = useCallback(() => { + // Stop all local tracks to release the camera and mic indicator + localStream?.getTracks().forEach((t) => t.stop()); + setLocalStream(null); + }, [localStream, setLocalStream]); + + const getLocalMedia = useCallback(async () => { + const stream = await navigator.mediaDevices.getUserMedia({ + video: true, audio: true, + }); + setLocalStream(stream); + return stream; + }, [setLocalStream]); + + const createPeerConnection = useCallback((stream: MediaStream, targetId: string) => { + const pc = new RTCPeerConnection(ICE_SERVERS); + pcRef.current = pc; + + // Add our tracks so the remote gets our stream + stream.getTracks().forEach((track) => pc.addTrack(track, stream)); + + // When we receive remote tracks, store them + pc.ontrack = (e) => setRemoteStream(e.streams[0]); + + // Relay ICE candidates through the server + pc.onicecandidate = (e) => { + if (e.candidate) { + socket?.emit('ice-candidate', { + to: targetId, candidate: e.candidate, + }); + } + }; + + return pc; + }, [socket, setRemoteStream]); + + const initiateCall = useCallback(async (targetId: string, targetUsername: string) => { + const stream = await getLocalMedia(); + const pc = createPeerConnection(stream, targetId); + + const offer = await pc.createOffer(); + await pc.setLocalDescription(offer); + + socket?.emit('call-user', { to: targetId, fromId: authUser?._id, from: authUser?.username, offer }); + setRemote(targetId, targetUsername); + setCallStatus('calling'); + }, [getLocalMedia, createPeerConnection, socket, setRemote, setCallStatus]); + + const acceptCall = useCallback(async (fromId: string, from: string, offer: RTCSessionDescriptionInit) => { + const stream = await getLocalMedia(); + const pc = createPeerConnection(stream, fromId); + + await pc.setRemoteDescription(new RTCSessionDescription(offer)); + const answer = await pc.createAnswer(); + await pc.setLocalDescription(answer); + + socket?.emit('answer-call', { to: fromId, answer }); + setRemote(fromId, from); + setCallStatus('in-call'); + }, [getLocalMedia, createPeerConnection, socket, setRemote, setCallStatus]); + + const handleRemoteAnswer = useCallback(async (answer: RTCSessionDescriptionInit) => { + await pcRef.current?.setRemoteDescription(new RTCSessionDescription(answer)); + setCallStatus('in-call'); + }, [setCallStatus]); + + const addIceCandidate = useCallback(async (candidate: RTCIceCandidateInit) => { + await pcRef.current?.addIceCandidate(new RTCIceCandidate(candidate)); + }, []); + + const cleanupConnection = useCallback(() => { + pcRef.current?.close(); + pcRef.current = null; + }, []); + + const declineCall = useCallback((fromId: string) => { + // Emit to server → server relays 'call-declined' to the caller + socket?.emit('decline-call', { to: fromId }); + // Reset our own state — we were only in 'receiving' status, no PC yet + resetCall(); + }, [socket, resetCall]); + + const endCall = useCallback((remoteId: string) => { + // Notify the other peer so their screen resets too + socket?.emit('end-call', { to: remoteId }); + // Tear down in order: tracks first, then peer connection, then state + cleanupTracks(); + cleanupConnection(); + resetCall(); + }, [socket, cleanupTracks, cleanupConnection, resetCall]); + + return { initiateCall, acceptCall, handleRemoteAnswer, addIceCandidate, cleanupConnection, declineCall, endCall }; +} \ No newline at end of file diff --git a/frontend/src/lib/socketEvents.ts b/frontend/src/lib/socketEvents.ts index 19c1bc7..e71b0da 100644 --- a/frontend/src/lib/socketEvents.ts +++ b/frontend/src/lib/socketEvents.ts @@ -10,9 +10,9 @@ export const registerSocketListeners = () => { socket.connect(); socket.on("message received", (newMessage) => { - chatStore.setState((prevState) => ({messages: [...prevState.messages, newMessage]}))}); + chatStore.setState((prevState) => ({ messages: [...prevState.messages, newMessage] }))}); - socket.on("is typing", () => {chatStore.setState({isTyping: true})}); - socket.on("is not typing", () => {chatStore.setState({isTyping: false})}); + socket.on("is typing", () => {chatStore.setState({ isTyping: true })}); + socket.on("is not typing", () => {chatStore.setState({ isTyping: false })}); } \ No newline at end of file diff --git a/frontend/src/pages/Chats.tsx b/frontend/src/pages/Chats.tsx index c8f024d..040c239 100644 --- a/frontend/src/pages/Chats.tsx +++ b/frontend/src/pages/Chats.tsx @@ -2,14 +2,16 @@ import React from 'react' import Sidebar from '../components/Sidebar' import ChatArea from '../components/ChatArea' import { chatStore } from '../store/useChatStore' +import { useShallow } from 'zustand/shallow'; const SearchModalComponewnt = React.lazy(() => import("../components/SearchModal")); const MessageAreaComponent = React.lazy(() => import("../components/MessageArea")); +const VideoCallComponent = React.lazy(() => import("../components/VideoCallModal")); const Chats = () => { - const selectedUser = chatStore((state) => state.selectedUser) - const setSelectedUser = chatStore((state) => state.setSelectedUser) + const {setSelectedUser, selectedUser} = chatStore(useShallow((state) => ({selectedUser: state.selectedUser, setSelectedUser: state.setSelectedUser}))); const [showModal, setShowModal] = React.useState(false); + const [videoCallModal, setvideoCallModal] = React.useState(false); const handleBack = () => { setSelectedUser(null); @@ -45,6 +47,11 @@ const Chats = () => { setShowModal(false)} /> )} + {videoCallModal && ( + + setvideoCallModal(false)} /> + + )} ) } diff --git a/frontend/src/store/useCallStore.tsx b/frontend/src/store/useCallStore.tsx new file mode 100644 index 0000000..ca4aba2 --- /dev/null +++ b/frontend/src/store/useCallStore.tsx @@ -0,0 +1,62 @@ +import { create } from "zustand"; + +export type CallStatus = "idle" | "calling" | "receiving" | "in-call" | "declined" | "connected"; + +export type CallStore = { + remoteId: string | null, + remoteUsername: string | null, + + localStream: MediaStream | null, + remoteStream: MediaStream | null, + incomingOffer: RTCSessionDescriptionInit| null, + + isMuted: boolean, + isVideoOff: boolean + callStatus: CallStatus, + + setCallStatus: (s: CallStatus) => void; + setLocalStream: (s: MediaStream | null) => void; + setRemoteStream: (s: MediaStream | null) => void; + setRemote: (id: string | null, username: string | null) => void; + setIncomingOffer: (offer: RTCSessionDescriptionInit | null) => void; + + resetCall: () => void; + toggleAudio: () => void; + toggleVideo: () => void; +}; + +export const callStore = create((set, get) => ({ + remoteId: null, + remoteUsername: null, + localStream: null, + remoteStream: null, + incomingOffer: null, + + isMuted: false, + isVideoOff: false, + callStatus: "idle", + + setLocalStream: (localStream) => set({ localStream }), + setRemoteStream: (remoteStream) => set({ remoteStream }), + setRemote: (remoteId, remoteUsername) => set({ remoteId, remoteUsername }), + setCallStatus: (callStatus) => set({ callStatus }), + setIncomingOffer: (incomingOffer) => set({ incomingOffer }), + + toggleAudio: () => { + const { localStream, isMuted } = get(); + localStream?.getAudioTracks().forEach((t) => (t.enabled = isMuted)); + set({ isMuted: !isMuted }); + }, + + toggleVideo: () => { + const { localStream, isVideoOff } = get(); + localStream?.getVideoTracks().forEach((t) => (t.enabled = isVideoOff)); + set({ isVideoOff: !isVideoOff }); + }, + + resetCall: () => set({ + remoteStream: null, localStream: null, callStatus: 'idle', + remoteId: null, incomingOffer: null, + isMuted: false, isVideoOff: false, + }), +})); \ No newline at end of file