diff --git a/packages/api/index.ts b/packages/api/index.ts index 841e988..5797fd2 100644 --- a/packages/api/index.ts +++ b/packages/api/index.ts @@ -4,6 +4,7 @@ import InitializeWs from './services/socket.service'; import { NextFunction } from 'express'; import NirvanaResponse from '@nirvana/core/responses/nirvanaResponse'; import cors from 'cors'; +import getConversationRoutes from './routes/conversations'; import getSearchRoutes from './routes/search'; import getUserRoutes from './routes/user'; import morgan from 'morgan'; @@ -14,27 +15,25 @@ app.use(morgan('combined')); app.use(cors()); app.use(express.json()); -app.use((err: Error, req: Request, res: Response, next: NextFunction) => { - console.error(err); - next(err); -}); - -app.use((err: Error, req: Request, res: Response, next: NextFunction) => { - res.status(500); - res.json(new NirvanaResponse(undefined, err, err.message)); -}); - -app.get('/', (req: Request, res: Response) => { - res.send('hello world.'); -}); - app.use('/api/status', (req: Request, res: Response) => { res.json(new NirvanaResponse('wohoo, server is healthy')); }); app.use('/api/user', getUserRoutes()); app.use('/api/search', getSearchRoutes()); -// app.use('/api/conversations', getConversationRoutes()); +app.use('/api/conversations', getConversationRoutes()); + +app.use((err: Error, req: Request, res: Response, next: NextFunction) => { + // use logger or sentry + + if (!res.statusCode) res.status(500); + + return res.json(new NirvanaResponse(undefined, err, err.message)); +}); + +app.get('/', (req: Request, res: Response) => { + res.send('hello world.'); +}); const PORT = process.env.PORT || 8080; const server = app.listen(PORT, () => diff --git a/packages/api/middleware/auth.ts b/packages/api/middleware/auth.ts index 22ea14b..492848f 100644 --- a/packages/api/middleware/auth.ts +++ b/packages/api/middleware/auth.ts @@ -21,7 +21,8 @@ export const authCheck = async (req: Request, res: Response, next: NextFunction) next(); } catch (error) { - res.status(401).send('unauthorized'); + res.status(301); + next(error); } }; diff --git a/packages/api/routes/conversations.ts b/packages/api/routes/conversations.ts new file mode 100644 index 0000000..a96f686 --- /dev/null +++ b/packages/api/routes/conversations.ts @@ -0,0 +1,45 @@ +import express, { NextFunction, Request, Response } from 'express'; + +import Conversation from '@nirvana/core/models/conversation.model'; +import ConversationService from '../services/conversation.service'; +import CreateConversationRequest from '@nirvana/core/requests/CreateConversationRequest.request'; +import CreateConversationResponse from '@nirvana/core/responses/CreateConversationResponse.response'; +import NirvanaResponse from '@nirvana/core/responses/nirvanaResponse'; +import { authCheck } from '../middleware/auth'; + +export default function getConversationRoutes() { + const router = express.Router(); + + router.use(express.json()); + + // get a conversation + router.get('/:conversationId', authCheck); + + // get all conversations that I am in + router.get('/conversations', authCheck); + + // get a one on one conversation based on other user Id + + // get a conversation's content + + // create a conversation + router.post('/', authCheck, createConversation); + + return router; +} + +const createConversation = async (req: Request, res: Response, next: NextFunction) => { + const createRequest = req.body as CreateConversationRequest; + + next(Error('test this shit')); + + // console.log(createRequest.conversation); + + // const insertResult = await ConversationService.createConversation(createRequest.conversation); + + // if (!insertResult?.acknowledged) { + // throw Error('nothing inserted'); + // } + + // return res.json(new CreateConversationResponse(insertResult.insertedId)); +}; diff --git a/packages/api/routes/user.ts b/packages/api/routes/user.ts index 6ef2144..6dab422 100644 --- a/packages/api/routes/user.ts +++ b/packages/api/routes/user.ts @@ -1,6 +1,6 @@ import { JwtClaims, authCheck } from '../middleware/auth'; import User, { GoogleUserInfo, UserStatus } from '@nirvana/core/models/user.model'; -import express, { Request, Response } from 'express'; +import express, { NextFunction, Request, Response } from 'express'; import LoginResponse from '../../core/responses/login.response'; import { OAuth2Client } from 'google-auth-library'; @@ -29,116 +29,105 @@ export default function getUserRoutes() { } async function handleAuthCheck(req: Request, res: Response) { - try { - res.status(200).json('You are good to go!'); - } catch (error) { - res.status(401).json('Unauthorized'); - } + res.status(200).json('You are good to go!'); } -async function getUserDetails(req: Request, res: Response) { - try { - const userInfo = res.locals.userInfo as JwtClaims; +async function getUserDetails(req: Request, res: Response, next: NextFunction) { + const userInfo = res.locals.userInfo as JwtClaims; - const user = await UserService.getUserById(userInfo.userId); + const user = await UserService.getUserById(userInfo.userId); - user - ? res.status(200).json(new UserDetailsResponse(user)) - : res.status(404).json('No such user'); - } catch (error) { - console.log(error); - res.status(500).json(`Problem with signing user up or logging in`); + if (user) { + return res.status(200).json(new UserDetailsResponse(user)); } + + res.status(404); + next(Error('No user found')); } /** Create user if doesn't exist * Returns jwt token for client and user details */ -async function login(req: Request, res: Response) { +async function login(req: Request, res: Response, next: NextFunction) { // passed in accesstoken no matter what const { access_token, id_token } = req.query; - try { - const ticket = await client.verifyIdToken({ - idToken: (id_token as string) ?? '', - audience: environmentVariables.GOOGLE_AUTH_CLIENT_ID, // Specify the CLIENT_ID of the app that accesses the backend - }); - const googleUserId = ticket.getPayload()?.sub as string; - const email = ticket.getPayload()?.email as string; + const ticket = await client.verifyIdToken({ + idToken: (id_token as string) ?? '', + audience: environmentVariables.GOOGLE_AUTH_CLIENT_ID, // Specify the CLIENT_ID of the app that accesses the backend + }); + const googleUserId = ticket.getPayload()?.sub as string; + const email = ticket.getPayload()?.email as string; - if (!googleUserId || !email) { - res.status(401).json('no google account found'); - return; + if (!googleUserId || !email) { + res.status(401); + return next(Error('no google account found')); + } + + // return user details if it passed auth middleware + const user = await UserService.getUserByEmail(email); + + // if no user found, then go ahead and create user + if (!user) { + if (!access_token) { + res.status(401); + return next(Error('No access token provided')); } - // return user details if it passed auth middleware - const user = await UserService.getUserByEmail(email); + // get google user info from access token + const userInfo: GoogleUserInfo = await UserService.getGoogleUserInfoWithAccessToken( + access_token as string, + ); - // if no user found, then go ahead and create user - if (!user) { - if (!access_token) { - res.status(400).json('No access token provided'); - return; - } + // create initial user model object - // get google user info from access token - const userInfo: GoogleUserInfo = await UserService.getGoogleUserInfoWithAccessToken( - access_token as string, - ); + const newUser = new User( + googleUserId, + userInfo.email, + userInfo.name, + userInfo.given_name, + userInfo.family_name, + new Date(), + userInfo.picture, + userInfo.verifiedEmail, + userInfo.locale, + ); - // create initial user model object + // create user if not exists + const insertResult = await UserService.createUserIfNotExists(newUser); - const newUser = new User( - googleUserId, - userInfo.email, - userInfo.name, - userInfo.given_name, - userInfo.family_name, - new Date(), - userInfo.picture, - userInfo.verifiedEmail, - userInfo.locale, - ); + newUser._id = insertResult?.insertedId; - // create user if not exists - const insertResult = await UserService.createUserIfNotExists(newUser); - - newUser._id = insertResult?.insertedId; - - // create jwt token with new user info - const jwtToken = jwt.sign( - { - userId: newUser._id, - googleUserId: newUser.googleId, - picture: newUser.picture, - email: newUser.email, - name: newUser.name, - }, - environmentVariables.JWT_TOKEN_SECRET, - ); - - insertResult - ? res.status(200).json(new LoginResponse(jwtToken, newUser)) - : res.status(500).json('Failed to create account, already exists'); - - return; - } - - // create jwt token with existing user info - const existingUserJwtToken = jwt.sign( + // create jwt token with new user info + const jwtToken = jwt.sign( { - userId: user._id, - googleUserId: user.googleId, - picture: user.picture, - email: user.email, - name: user.name, + userId: newUser._id, + googleUserId: newUser.googleId, + picture: newUser.picture, + email: newUser.email, + name: newUser.name, }, environmentVariables.JWT_TOKEN_SECRET, ); - res.status(200).json(new LoginResponse(existingUserJwtToken, user)); - } catch (error) { - console.log(error); - res.status(500).json(`Problem with signing user up or logging in`); + if (insertResult) { + return res.status(200).json(new LoginResponse(jwtToken, newUser)); + } + + return next(Error('Failed to create account, already exists')); } + + // create jwt token with existing user info + const existingUserJwtToken = jwt.sign( + { + userId: user._id, + googleUserId: user.googleId, + picture: user.picture, + email: user.email, + name: user.name, + }, + environmentVariables.JWT_TOKEN_SECRET, + ); + + return res.status(200).json(new LoginResponse(existingUserJwtToken, user)); } diff --git a/packages/api/services/conversation.service.ts b/packages/api/services/conversation.service.ts new file mode 100644 index 0000000..bdc8f6e --- /dev/null +++ b/packages/api/services/conversation.service.ts @@ -0,0 +1,8 @@ +import Conversation from '@nirvana/core/models/conversation.model'; +import { collections } from './database.service'; + +export default class ConversationService { + static async createConversation(newConversation: Conversation) { + return await collections.conversations?.insertOne(newConversation); + } +} diff --git a/packages/api/services/database.service.ts b/packages/api/services/database.service.ts index 65be151..4cc63ee 100644 --- a/packages/api/services/database.service.ts +++ b/packages/api/services/database.service.ts @@ -6,6 +6,7 @@ import environmentVariables from '../config/config'; // Global Variables export const collections: { users?: mongoDB.Collection; + conversations?: mongoDB.Collection; } = {}; // Initialize Connection @@ -19,6 +20,7 @@ client.connect(); const db: mongoDB.Db = client.db(process.env.DB_NAME); const usersCollection: mongoDB.Collection = db.collection('users'); +const conversationsCollection: mongoDB.Collection = db.collection('conversations'); // client.on('connection', () => { // db.command({ @@ -27,5 +29,6 @@ const usersCollection: mongoDB.Collection = db.collection('users'); // }); collections.users = usersCollection; +collections.conversations = conversationsCollection; console.log(`Successfully connected to database: ${db.databaseName} and collections`); diff --git a/packages/core/models/conversation.model.ts b/packages/core/models/conversation.model.ts index 365d0f2..dc6a5ab 100644 --- a/packages/core/models/conversation.model.ts +++ b/packages/core/models/conversation.model.ts @@ -24,7 +24,7 @@ export default class Conversation { public lastUpdatedDate = new Date(), public createdDate = new Date(), - public id?: ObjectId, + public id: ObjectId = new ObjectId(), ) {} } diff --git a/packages/core/requests/CreateConversationRequest.request.ts b/packages/core/requests/CreateConversationRequest.request.ts new file mode 100644 index 0000000..de29875 --- /dev/null +++ b/packages/core/requests/CreateConversationRequest.request.ts @@ -0,0 +1,5 @@ +import Conversation from '@nirvana/core/models/conversation.model'; + +export default class CreateConversationRequest { + constructor(public conversation: Conversation) {} +} diff --git a/packages/core/responses/CreateConversationResponse.response.ts b/packages/core/responses/CreateConversationResponse.response.ts new file mode 100644 index 0000000..7d790fc --- /dev/null +++ b/packages/core/responses/CreateConversationResponse.response.ts @@ -0,0 +1,5 @@ +import { ObjectId } from 'mongodb'; + +export default class CreateConversationResponse { + constructor(public conversationId: ObjectId) {} +}