fixing things regarding bson error with node and such...now working?
This commit is contained in:
@@ -4,7 +4,6 @@ import { NextFunction } from "express";
|
|||||||
import SocketChannels from "@nirvana/core/sockets/channels";
|
import SocketChannels from "@nirvana/core/sockets/channels";
|
||||||
import { UserService } from "./services/user.service";
|
import { UserService } from "./services/user.service";
|
||||||
import { UserStatus } from "@nirvana/core/models";
|
import { UserStatus } from "@nirvana/core/models";
|
||||||
import { connectToDatabase } from "./services/database.service";
|
|
||||||
import cors from "cors";
|
import cors from "cors";
|
||||||
import getConversationRoutes from "./routes/conversation";
|
import getConversationRoutes from "./routes/conversation";
|
||||||
import getSearchRoutes from "./routes/search";
|
import getSearchRoutes from "./routes/search";
|
||||||
@@ -29,7 +28,6 @@ app.use("/api/conversations", getConversationRoutes());
|
|||||||
|
|
||||||
const PORT = 5000;
|
const PORT = 5000;
|
||||||
var server = app.listen(PORT, () => console.log("express running"));
|
var server = app.listen(PORT, () => console.log("express running"));
|
||||||
connectToDatabase();
|
|
||||||
|
|
||||||
// socket IO stuff
|
// socket IO stuff
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
|
import {
|
||||||
|
Conversation,
|
||||||
|
ConversationMember,
|
||||||
|
ConversationMemberState,
|
||||||
|
} from "../../core/models/conversation.model";
|
||||||
|
import { JwtClaims, authCheck } from "../middleware/auth";
|
||||||
import express, { Application, Request, Response } from "express";
|
import express, { Application, Request, Response } from "express";
|
||||||
|
|
||||||
import Content from "@nirvana/core/models/content.model";
|
import Content from "@nirvana/core/models/content.model";
|
||||||
|
import { ConversationService } from "../services/conversation.service";
|
||||||
|
import CreateConvoRequest from "../../core/requests/createConvo.request";
|
||||||
import GetConversationDetailsResponse from "@nirvana/core/responses/getConversationDetails.response";
|
import GetConversationDetailsResponse from "@nirvana/core/responses/getConversationDetails.response";
|
||||||
|
import GetDmConversationByOtherUserIdResponse from "../../core/responses/getDmConversationByOtherUserId.response";
|
||||||
|
import MasterConversation from "../../core/models/masterConversation.model";
|
||||||
import { ObjectId } from "mongodb";
|
import { ObjectId } from "mongodb";
|
||||||
import Relationship from "@nirvana/core/models/relationship.model";
|
import Relationship from "@nirvana/core/models/relationship.model";
|
||||||
import { UserService } from "../services/user.service";
|
import { UserService } from "../services/user.service";
|
||||||
import { authCheck } from "../middleware/auth";
|
|
||||||
import { collections } from "../services/database.service";
|
import { collections } from "../services/database.service";
|
||||||
|
|
||||||
export default function getConversationRoutes() {
|
export default function getConversationRoutes() {
|
||||||
@@ -16,5 +25,76 @@ export default function getConversationRoutes() {
|
|||||||
// get data for a one on one conversation
|
// get data for a one on one conversation
|
||||||
// router.get("/:otherUserGoogleUserId", authCheck, getConversationDetails);
|
// router.get("/:otherUserGoogleUserId", authCheck, getConversationDetails);
|
||||||
|
|
||||||
|
// create a convo
|
||||||
|
router.post("/", authCheck, createConversation);
|
||||||
|
|
||||||
|
// get conversation between user and other user
|
||||||
|
router.get("/dm/:otherUserId", authCheck, getDmByOtherUserId);
|
||||||
|
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getDmByOtherUserId(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
const { otherUserId } = req.params;
|
||||||
|
const userInfo = res.locals.userInfo as JwtClaims;
|
||||||
|
|
||||||
|
console.log(otherUserId);
|
||||||
|
|
||||||
|
// check db for convos between me and this other person
|
||||||
|
// if there is one, then return it with 200 status
|
||||||
|
// else return it with custom status that frontend will read
|
||||||
|
|
||||||
|
res.status(200).json();
|
||||||
|
return;
|
||||||
|
|
||||||
|
res.status(205).json("no such conversation between you two");
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createConversation(req: Request, res: Response) {
|
||||||
|
try {
|
||||||
|
const reqObj: CreateConvoRequest = req.body as CreateConvoRequest;
|
||||||
|
|
||||||
|
if (!reqObj?.otherMemberIds.length) {
|
||||||
|
res.status(400).json("must provide member Ids");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userInfo = res.locals.userInfo as JwtClaims;
|
||||||
|
|
||||||
|
const newConvo = new Conversation();
|
||||||
|
const convoMembers: ConversationMember[] =
|
||||||
|
reqObj.otherMemberIds.map((memId) => {
|
||||||
|
const newConvoMember = new ConversationMember(
|
||||||
|
newConvo._id,
|
||||||
|
new ObjectId(memId),
|
||||||
|
ConversationMemberState.INVITED
|
||||||
|
);
|
||||||
|
|
||||||
|
return newConvoMember;
|
||||||
|
}) ?? [];
|
||||||
|
|
||||||
|
convoMembers.push(
|
||||||
|
new ConversationMember(
|
||||||
|
newConvo._id,
|
||||||
|
new ObjectId(userInfo.userId),
|
||||||
|
ConversationMemberState.INBOX
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const transactionResult = await ConversationService.createConversation(
|
||||||
|
newConvo,
|
||||||
|
convoMembers
|
||||||
|
);
|
||||||
|
|
||||||
|
transactionResult
|
||||||
|
? res.status(200).json(newConvo)
|
||||||
|
: res.status(400).json("unable to create convo");
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
res.status(500).json(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,77 @@
|
|||||||
|
import {
|
||||||
|
Conversation,
|
||||||
|
ConversationMember,
|
||||||
|
} from "../../core/models/conversation.model";
|
||||||
|
import { client, collections } from "./database.service";
|
||||||
|
|
||||||
|
import { ObjectId } from "mongodb";
|
||||||
|
|
||||||
export class ConversationService {
|
export class ConversationService {
|
||||||
// static async getUserById(userId: string) {
|
static async getConversationByOtherUserId(otherUserId: ObjectId) {
|
||||||
// const query = { googleId: userId };
|
// get all of the conversations for this user that have exactly two conversation members
|
||||||
// const res = await collections.users?.findOne(query);
|
// get all of the conversationMembers for this user
|
||||||
// // exists
|
// get all of the conversations for this user
|
||||||
// if (res?._id) {
|
// get all of the conversations
|
||||||
// return res as User;
|
// const query = { googleId: userId };
|
||||||
// }
|
// const res = await collections.users?.findOne(query);
|
||||||
// return null;
|
// // exists
|
||||||
// }
|
// if (res?._id) {
|
||||||
|
// return res as User;
|
||||||
|
// }
|
||||||
|
// return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async createConversation(
|
||||||
|
convo: Conversation,
|
||||||
|
convoMembers: ConversationMember[]
|
||||||
|
) {
|
||||||
|
const session = client.startSession();
|
||||||
|
try {
|
||||||
|
const transactionResults = await session.withTransaction(async () => {
|
||||||
|
// todo: check if convoMembers userId's actually exist
|
||||||
|
|
||||||
|
const insertConvoRes = await collections.conversations?.insertOne(
|
||||||
|
convo
|
||||||
|
);
|
||||||
|
if (!insertConvoRes?.insertedId) {
|
||||||
|
await session.abortTransaction();
|
||||||
|
console.error("failed to create convo");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const insertConvoMembersRes =
|
||||||
|
await collections.conversationMembers?.insertMany(convoMembers);
|
||||||
|
if (!insertConvoMembersRes?.insertedCount) {
|
||||||
|
await session.abortTransaction();
|
||||||
|
console.error("failed to create conversation members");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("success");
|
||||||
|
return insertConvoMembersRes;
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(transactionResults);
|
||||||
|
|
||||||
|
return "success";
|
||||||
|
|
||||||
|
// if (transactionResults) {
|
||||||
|
// console.log("The convo was successfully created.");
|
||||||
|
// return transactionResults;
|
||||||
|
// } else {
|
||||||
|
// console.log("The convo was intentionally aborted.");
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
} catch (e) {
|
||||||
|
console.log(
|
||||||
|
"The transaction was aborted due to an unexpected error: " + e
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
await session.endSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,25 +6,34 @@ import { loadConfig } from "../config";
|
|||||||
// Global Variables
|
// Global Variables
|
||||||
export const collections: {
|
export const collections: {
|
||||||
users?: mongoDB.Collection;
|
users?: mongoDB.Collection;
|
||||||
|
conversations?: mongoDB.Collection;
|
||||||
|
conversationMembers?: mongoDB.Collection;
|
||||||
|
audioClips?: mongoDB.Collection;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
// Initialize Connection
|
// Initialize Connection
|
||||||
export async function connectToDatabase() {
|
const config = loadConfig();
|
||||||
const config = loadConfig();
|
|
||||||
|
|
||||||
const client: mongoDB.MongoClient = new mongoDB.MongoClient(
|
export const client: mongoDB.MongoClient = new mongoDB.MongoClient(
|
||||||
config.MONGO_CONNECTION_STRING
|
config.MONGO_CONNECTION_STRING
|
||||||
);
|
);
|
||||||
|
|
||||||
await client.connect();
|
client.connect();
|
||||||
|
|
||||||
const db: mongoDB.Db = client.db(process.env.DB_NAME);
|
const db: mongoDB.Db = client.db(process.env.DB_NAME);
|
||||||
|
|
||||||
const usersCollection: mongoDB.Collection = db.collection("users");
|
const usersCollection: mongoDB.Collection = db.collection("users");
|
||||||
|
const convoCollection: mongoDB.Collection = db.collection("conversations");
|
||||||
|
const convoMembersCollection: mongoDB.Collection = db.collection(
|
||||||
|
"conversationMembers"
|
||||||
|
);
|
||||||
|
const audioClipsCollection: mongoDB.Collection = db.collection("audioClips");
|
||||||
|
|
||||||
collections.users = usersCollection;
|
collections.users = usersCollection;
|
||||||
|
collections.conversations = convoCollection;
|
||||||
|
collections.conversationMembers = convoMembersCollection;
|
||||||
|
collections.audioClips = audioClipsCollection;
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`Successfully connected to database: ${db.databaseName} and collections`
|
`Successfully connected to database: ${db.databaseName} and collections`
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
import { ObjectId } from "mongodb";
|
import { ObjectId } from "mongodb";
|
||||||
|
|
||||||
export class Conversation {
|
export class Conversation {
|
||||||
_id: ObjectId;
|
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|
||||||
createdDate: Date;
|
constructor(
|
||||||
lastUpdatedDate: Date;
|
public _id?: ObjectId,
|
||||||
|
public createdDate: Date = new Date(),
|
||||||
constructor() {}
|
public lastUpdatedDate: Date = new Date()
|
||||||
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ConversationMember {
|
export class ConversationMember {
|
||||||
_id: ObjectId;
|
constructor(
|
||||||
|
// unique constraint
|
||||||
|
public conversationId: ObjectId,
|
||||||
|
public userId: ObjectId,
|
||||||
|
|
||||||
// unique constraint
|
public state: ConversationMemberState = ConversationMemberState.INVITED,
|
||||||
conversationId: ObjectId;
|
|
||||||
userId: ObjectId;
|
|
||||||
|
|
||||||
state: ConversationMemberState;
|
public createdDate: Date = new Date(),
|
||||||
|
public _id?: ObjectId
|
||||||
createdDate: Date;
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ConversationMemberState {
|
export enum ConversationMemberState {
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default class CreateConvoRequest {
|
||||||
|
constructor(public otherMemberIds: string[]) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Conversation } from "../models/conversation.model";
|
||||||
|
|
||||||
|
export default class GetDmConversationByOtherUserIdResponse {
|
||||||
|
constructor(public conversation: Conversation) {}
|
||||||
|
}
|
||||||
@@ -105,6 +105,7 @@
|
|||||||
"typescript": "~4.5.4"
|
"typescript": "~4.5.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@nirvana/core": "*",
|
||||||
"@emotion/react": "^11.8.2",
|
"@emotion/react": "^11.8.2",
|
||||||
"@emotion/styled": "^11.8.1",
|
"@emotion/styled": "^11.8.1",
|
||||||
"@getstation/electron-google-oauth2": "^2.1.0",
|
"@getstation/electron-google-oauth2": "^2.1.0",
|
||||||
|
|||||||
@@ -31,3 +31,6 @@ export function useUserSearch(searchQuery: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// =========== MUTATIONS
|
// =========== MUTATIONS
|
||||||
|
export function useGetDmByUserId() {
|
||||||
|
return useMutation(ApiCalls.getDmByUserId);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import axios, { AxiosRequestConfig, AxiosResponse, Method } from "axios";
|
import axios, { AxiosRequestConfig, AxiosResponse, Method } from "axios";
|
||||||
|
|
||||||
|
import { Conversation } from "../../../core/models/conversation.model";
|
||||||
import LoginResponse from "../../../core/responses/login.response";
|
import LoginResponse from "../../../core/responses/login.response";
|
||||||
import { User } from "@nirvana/core/models";
|
import { User } from "@nirvana/core/models";
|
||||||
import UserDetailsResponse from "../../../core/responses/userDetails.response";
|
import UserDetailsResponse from "../../../core/responses/userDetails.response";
|
||||||
@@ -69,9 +70,18 @@ async function userSearch(searchQuery: string): Promise<UserSearchResponse> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getDmByUserId(otherUserId: string): Promise<Conversation> {
|
||||||
|
return await NirvanaApi.fetch(
|
||||||
|
`/conversations/dm/${otherUserId}`,
|
||||||
|
"GET",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export const ApiCalls = {
|
export const ApiCalls = {
|
||||||
login,
|
login,
|
||||||
authCheck,
|
authCheck,
|
||||||
getUserDetails,
|
getUserDetails,
|
||||||
userSearch,
|
userSearch,
|
||||||
|
getDmByUserId,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,12 +8,18 @@ import {
|
|||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import { Check, Search } from "@mui/icons-material";
|
import { Check, Search } from "@mui/icons-material";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
useGetDmByUserId,
|
||||||
|
useGetUserDetails,
|
||||||
|
useUserSearch,
|
||||||
|
} from "../../../controller/index";
|
||||||
|
|
||||||
import { $newConvoPage } from "../../../controller/recoil";
|
import { $newConvoPage } from "../../../controller/recoil";
|
||||||
|
import { ApiCalls } from "../../../controller/nirvanaApi";
|
||||||
import { User } from "@nirvana/core/models/user.model";
|
import { User } from "@nirvana/core/models/user.model";
|
||||||
import UserRow from "../../../components/User/basicUserDetailsRow";
|
import UserRow from "../../../components/User/basicUserDetailsRow";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
import { useSetRecoilState } from "recoil";
|
import { useSetRecoilState } from "recoil";
|
||||||
import { useUserSearch } from "../../../controller/index";
|
|
||||||
|
|
||||||
export default function NewConvo() {
|
export default function NewConvo() {
|
||||||
const setNewPageConvo = useSetRecoilState($newConvoPage);
|
const setNewPageConvo = useSetRecoilState($newConvoPage);
|
||||||
@@ -21,6 +27,8 @@ export default function NewConvo() {
|
|||||||
const [debUserSearchQ, setDebUserSearchQ] = useState<string>("");
|
const [debUserSearchQ, setDebUserSearchQ] = useState<string>("");
|
||||||
const [selectedUsers, setSelectedUsers] = useState<User[]>([]);
|
const [selectedUsers, setSelectedUsers] = useState<User[]>([]);
|
||||||
|
|
||||||
|
const { data: userDetails } = useGetUserDetails();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
isLoading: isSearching,
|
isLoading: isSearching,
|
||||||
@@ -101,13 +109,36 @@ export default function NewConvo() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const createConvo = () => {
|
const { mutateAsync } = useGetDmByUserId();
|
||||||
|
|
||||||
|
const createConvo = async () => {
|
||||||
|
if (!selectedUsers?.length) {
|
||||||
|
toast.error("Must select at least one person");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// consolidate the user Ids into an array
|
// consolidate the user Ids into an array
|
||||||
// make sure that there are no duplicates
|
// make sure that there are no duplicates
|
||||||
console;
|
// const userIds = selectedUsers.map((selUser) => selUser._id.toString());
|
||||||
|
// userIds.push(userDetails.user._id.toString());
|
||||||
|
|
||||||
// IF it's a one on one chat
|
// IF it's a one on one chat
|
||||||
// check backend with one route if there is an existing conversation
|
// check backend with one route if there is an existing conversation
|
||||||
console;
|
if (selectedUsers?.length === 1) {
|
||||||
|
let existingConvo;
|
||||||
|
try {
|
||||||
|
existingConvo = await mutateAsync(selectedUsers[0]._id.toString());
|
||||||
|
|
||||||
|
console.log("there is an existing convo!");
|
||||||
|
console.log(existingConvo);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
} finally {
|
||||||
|
console.log("done");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// create a conversation object in db with two members, me and this other person
|
// create a conversation object in db with two members, me and this other person
|
||||||
// IF it's a group convo/room/channel, create a channel with these people
|
// IF it's a group convo/room/channel, create a channel with these people
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user