diff --git a/packages/api/.env.development b/packages/api/.env.development index 6b35865..95d58c9 100644 --- a/packages/api/.env.development +++ b/packages/api/.env.development @@ -1 +1,3 @@ -MONGO_CONNECTION_STRING=mongodb+srv://default:M9iZXokJlZpN4KLX@cluster0.mkuqa.mongodb.net/default?retryWrites=true&w=majority \ No newline at end of file +MONGO_CONNECTION_STRING=mongodb+srv://default:M9iZXokJlZpN4KLX@cluster0.mkuqa.mongodb.net/default?retryWrites=true&w=majority + +JWT_TOKEN_SECRET=afajdslfwk1@lkkasdfl21ASDF!2 \ No newline at end of file diff --git a/packages/api/config.ts b/packages/api/config.ts new file mode 100644 index 0000000..06f1d5e --- /dev/null +++ b/packages/api/config.ts @@ -0,0 +1,14 @@ +import dotenv from "dotenv"; + +if (process.env && process.env.NODE_ENV === "development") { + dotenv.config({ path: ".env.development" }); +} else { + dotenv.config({ path: ".env" }); +} + +export const loadConfig = () => { + return { + MONGO_CONNECTION_STRING: process.env.MONGO_CONNECTION_STRING!, + JWT_TOKEN_SECRET: process.env.JWT_TOKEN_SECRET!, + }; +}; diff --git a/packages/api/index.ts b/packages/api/index.ts index 38ea2fb..1ed2a38 100644 --- a/packages/api/index.ts +++ b/packages/api/index.ts @@ -6,11 +6,9 @@ import { UserService } from "./services/user.service"; import { UserStatus } from "@nirvana/core/models"; import { connectToDatabase } from "./services/database.service"; import cors from "cors"; -import getContactsRoutes from "./routes/contacts"; import getConversationRoutes from "./routes/conversation"; import getSearchRoutes from "./routes/search"; import getUserRoutes from "./routes/user"; -import http from "http"; const app = express(); @@ -28,7 +26,6 @@ app.get("/", (req: Request, res: Response) => { app.use("/api/user", getUserRoutes()); app.use("/api/search", getSearchRoutes()); app.use("/api/conversations", getConversationRoutes()); -app.use("/api/contacts", getContactsRoutes()); const PORT = 5000; var server = app.listen(PORT, () => console.log("express running")); diff --git a/packages/api/package.json b/packages/api/package.json index c6e3c6e..8599526 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -7,15 +7,17 @@ "@nirvana/core": "*", "axios": "^0.26.1", "cors": "^2.8.5", + "dotenv": "^16.0.0", "express": "^4.17.3", "google-auth-library": "^7.14.0", + "jsonwebtoken": "^8.5.1", "mongodb": "^4.4.1", "socket.io": "^4.4.1" }, "scripts": { - "dev": "NODE_ENV=development && nodemon", + "dev": "NODE_ENV=development nodemon", "start": "ts-node index.ts", - "production": "NODE_ENV=production && nodemon" + "production": "NODE_ENV=production nodemon" }, "devDependencies": { "@types/express": "^4.17.13", diff --git a/packages/api/routes/contacts.ts b/packages/api/routes/contacts.ts deleted file mode 100644 index 61b00da..0000000 --- a/packages/api/routes/contacts.ts +++ /dev/null @@ -1,127 +0,0 @@ -import GetContactsResponse, { - ContactDetails, -} from "../../core/responses/getContacts.response"; -import Relationship, { - RelationshipState, -} from "@nirvana/core/models/relationship.model"; -import express, { Application, Request, Response } from "express"; - -import { ContactService } from "../services/contact.service"; -import { ObjectId } from "mongodb"; -import UpdateRelationshipStateRequest from "../../core/requests/updateRelationshipState.request"; -import { UserService } from "../services/user.service"; -import { authCheck } from "../middleware/auth"; -import { collections } from "../services/database.service"; - -export default function getContactsRoutes() { - const router = express.Router(); - - router.use(express.json()); - - // see all of my contacts - router.get("/", authCheck, getAllContacts); - - // send a friend request - router.post("/:otherUserGoogleId", authCheck, addContact); - - router.put("/", authCheck, updateRelationshipState); - - return router; -} - -// todo optimization with mongo lookups and such -async function getAllContacts(req: Request, res: Response) { - try { - const { userId } = res.locals; - - const resultRelationships = await ContactService.getAllUserRelationships( - userId - ); - - const responseObj = new GetContactsResponse(); - - // get all of the other users based on these relationships and join them - await Promise.all( - resultRelationships.map(async (relationship) => { - const otherUserId = - relationship.receiverUserId === userId - ? relationship.senderUserId - : relationship.receiverUserId; - - const otherUser = await UserService.getUserByGoogleId(otherUserId); - - if (otherUser) - responseObj.contactsDetails.push( - new ContactDetails(otherUser, relationship) - ); - }) - ); - - res.status(200).send(responseObj); - } catch (error) { - console.log(error); - res.status(500).send(`something went wrong`); - } -} - -async function addContact(req: Request, res: Response) { - try { - // get the userId of the person to add as a friend/contact - const { otherUserGoogleId } = req.params; - const { userId } = res.locals; - - if (!otherUserGoogleId) { - res.status(400).send("No contact id provided!"); - return; - } - - if (otherUserGoogleId === userId) { - res.status(400).send("Can't add yourself!"); - return; - } - - // validations - // make sure that we are not adding a friend that already has added us - - const newRelationship = new Relationship( - userId, - otherUserGoogleId, - RelationshipState.PENDING - ); - - const insertResult = await ContactService.createRelationship( - newRelationship - ); - - newRelationship._id = insertResult?.insertedId; - - insertResult - ? res.status(200).send(newRelationship) - : res.status(500).send("Failed to create relationship, already exists"); - - return; - } catch (error) { - console.log(error); - res.status(500).send(`something went wrong`); - } -} - -async function updateRelationshipState(req: Request, res: Response) { - try { - // get the userId of the person to add as a friend/contact - const requestObj = req.body as UpdateRelationshipStateRequest; - - const updateResult = await ContactService.updateRelationshipState( - requestObj.relationshipId, - requestObj.newState - ); - - updateResult?.modifiedCount - ? res.status(200).send("Updated state of relationship") - : res.status(500).send("Failed to update relationship"); - - return; - } catch (error) { - res.status(500).send(`something went wrong`); - } -} diff --git a/packages/api/routes/conversation.ts b/packages/api/routes/conversation.ts index 14d8773..267615b 100644 --- a/packages/api/routes/conversation.ts +++ b/packages/api/routes/conversation.ts @@ -1,6 +1,5 @@ import express, { Application, Request, Response } from "express"; -import { ContactService } from "../services/contact.service"; import Content from "@nirvana/core/models/content.model"; import GetConversationDetailsResponse from "@nirvana/core/responses/getConversationDetails.response"; import { ObjectId } from "mongodb"; @@ -15,47 +14,7 @@ export default function getConversationRoutes() { router.use(express.json()); // get data for a one on one conversation - router.get("/:otherUserGoogleUserId", authCheck, getConversationDetails); + // router.get("/:otherUserGoogleUserId", authCheck, getConversationDetails); return router; } - -async function getConversationDetails(req: Request, res: Response) { - try { - // google user Id of current user - const { userId } = res.locals; - const { otherUserGoogleUserId } = req.params; - - // get the "other" user's details - - const otherUser = await UserService.getUserByGoogleId( - otherUserGoogleUserId - ); - - if (!otherUser) { - res.status(404).send("No such user found"); - return; - } - - // find the relationship between us...if not there just null - const ourRelationship = await ContactService.getRelationship( - userId, - otherUserGoogleUserId - ); - - // todo: get latest content based on limit count - const latestContent: Content[] = []; - - const resObj = new GetConversationDetailsResponse( - otherUser, - false, - ourRelationship ?? undefined, - latestContent - ); - - res.status(200).send(resObj); - } catch (error) { - console.log(error); - res.status(500).send(`something went wrong`); - } -} diff --git a/packages/api/services/contact.service.ts b/packages/api/services/contact.service.ts deleted file mode 100644 index c7151a5..0000000 --- a/packages/api/services/contact.service.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Db, ObjectId } from "mongodb"; -import Relationship, { - RelationshipState, -} from "@nirvana/core/models/relationship.model"; - -import { collections } from "./database.service"; - -export class ContactService { - static async getAllUserRelationships(userId: string) { - const query = { - $or: [{ senderUserId: userId }, { receiverUserId: userId }], - }; - const res = (await collections.relationships - ?.find(query) - .toArray()) as Relationship[]; - - return res; - } - - static async getRelationship( - myGoogleUserId: string, - otherGoogleUserId: string - ) { - // i am the sender and they are the receiver - const clauseOne = { - $and: [ - { senderUserId: myGoogleUserId }, - { receiverUserId: otherGoogleUserId }, - ], - }; - // i could also be the receiver and them the sender - const clauseTwo = { - $and: [ - { senderUserId: otherGoogleUserId }, - { receiverUserId: myGoogleUserId }, - ], - }; - - const query = { $or: [clauseOne, clauseTwo] }; - const res = await collections.relationships?.findOne(query); - - // exists - if (res?._id) { - return res as Relationship; - } - - return null; - } - - static async createRelationship(newRelationship: Relationship) { - // i am the sender and they are the receiver - const relationshipRes = await this.getRelationship( - newRelationship.senderUserId, - newRelationship.receiverUserId - ); - - if (relationshipRes) { - throw new Error("Already exists"); - } - - const insertResult = await collections.relationships?.insertOne( - newRelationship - ); - - return insertResult; - } - - static async updateRelationshipState( - relationshipId: ObjectId, - newState: RelationshipState - ) { - const query = { _id: new ObjectId(relationshipId) }; - const updateDoc = { - $set: { state: newState, lastUpdatedDate: new Date() }, - }; - const result = await collections.relationships?.updateOne(query, updateDoc); - - return result; - } -} diff --git a/packages/api/services/database.service.ts b/packages/api/services/database.service.ts index 09f3c11..8ea500c 100644 --- a/packages/api/services/database.service.ts +++ b/packages/api/services/database.service.ts @@ -1,18 +1,19 @@ // External Dependencies import * as mongoDB from "mongodb"; -// import * as dotenv from "dotenv"; + +import { loadConfig } from "../config"; // Global Variables - export const collections: { users?: mongoDB.Collection; - relationships?: mongoDB.Collection; } = {}; // Initialize Connection export async function connectToDatabase() { + const config = loadConfig(); + const client: mongoDB.MongoClient = new mongoDB.MongoClient( - "mongodb+srv://default:M9iZXokJlZpN4KLX@cluster0.mkuqa.mongodb.net/default?retryWrites=true&w=majority" + config.MONGO_CONNECTION_STRING ); await client.connect(); @@ -20,11 +21,8 @@ export async function connectToDatabase() { const db: mongoDB.Db = client.db(process.env.DB_NAME); const usersCollection: mongoDB.Collection = db.collection("users"); - const relationshipCollection: mongoDB.Collection = - db.collection("relationships"); collections.users = usersCollection; - collections.relationships = relationshipCollection; console.log( `Successfully connected to database: ${db.databaseName} and collections` diff --git a/yarn.lock b/yarn.lock index 3c40d7f..1d32b91 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3013,6 +3013,11 @@ dot-prop@^6.0.1: dependencies: is-obj "^2.0.0" +dotenv@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411" + integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q== + duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz" @@ -5222,6 +5227,22 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + jsprim@^1.2.2: version "1.4.2" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" @@ -5245,6 +5266,15 @@ junk@^3.1.0: resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + jwa@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz" @@ -5254,6 +5284,14 @@ jwa@^2.0.0: ecdsa-sig-formatter "1.0.11" safe-buffer "^5.0.1" +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + jws@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz" @@ -5387,11 +5425,46 @@ lodash.get@^4.0.0: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + lodash.template@^4.2.2: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" @@ -7612,7 +7685,7 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==