From 2008f729c96dca00b17f91ff760d07375df1b158 Mon Sep 17 00:00:00 2001 From: Michael Nesbitt Date: Wed, 4 Aug 2021 20:56:57 -0500 Subject: [PATCH 1/5] Added transaction middleware --- src/middleware/useTransaction.ts | 22 ++++++++++++++++++++++ src/routes/user.ts | 5 +++++ src/typings/express/index.d.ts | 2 ++ 3 files changed, 29 insertions(+) create mode 100644 src/middleware/useTransaction.ts diff --git a/src/middleware/useTransaction.ts b/src/middleware/useTransaction.ts new file mode 100644 index 0000000..5b9c737 --- /dev/null +++ b/src/middleware/useTransaction.ts @@ -0,0 +1,22 @@ +import { NextFunction, Request, Response } from "express"; +import mongoose from "mongoose"; + +export const useTransaction = async ( + req: Request, + res: Response, + next: NextFunction +) => { + const session = await mongoose.startSession(); + + req.session = session; + + try { + next(); + } catch (error) { + console.log(error); + await session.abortTransaction(); + next(error); + } finally { + session.endSession(); + } +}; diff --git a/src/routes/user.ts b/src/routes/user.ts index 3a99d4e..347501f 100644 --- a/src/routes/user.ts +++ b/src/routes/user.ts @@ -35,6 +35,7 @@ import { hasPermissionToPutUserRequest, validatePutUserRequest } from "../middleware/user"; +import { useTransaction } from "../middleware/useTransaction"; import { isMongoDuplicateKeyError, projection } from "../utilities"; const router = Router(); @@ -84,8 +85,10 @@ router.post( (req, res, next) => { isValidBody(keysOf(), req, res, next); }, + useTransaction, async (req, res, next) => { try { + req.session!.startTransaction(); const body = req.body as PostUserCompleteSignupRequest; // Check if signinKey exists @@ -127,6 +130,8 @@ router.post( res .status(StatusCode.CREATED) .json({ token } as PostAuthCompleteSignUpResponse); + + await req.session!.commitTransaction(); } catch (error) { next(error); } diff --git a/src/typings/express/index.d.ts b/src/typings/express/index.d.ts index 1617461..87c2f20 100644 --- a/src/typings/express/index.d.ts +++ b/src/typings/express/index.d.ts @@ -1,10 +1,12 @@ import { IPFSContent, Token } from "@gatsby-tv/types"; +import { ClientSession } from "mongoose"; declare global { declare namespace Express { interface Request { decodedToken?: Token; ipfsContent?: IPFSContent; + session?: ClientSession; } } } From c0e69f68ebf784aebc3f6c60ae5d3ac483d539ae Mon Sep 17 00:00:00 2001 From: Michael Nesbitt Date: Wed, 11 Aug 2021 08:11:39 -0500 Subject: [PATCH 2/5] Added transactions to all relevant routes --- src/index.ts | 5 ++++ src/middleware/useTransaction.ts | 14 +++------- src/routes/auth.ts | 44 ++++++++++++++++++-------------- src/routes/channel.ts | 6 +++++ src/routes/user.ts | 8 +++--- src/routes/video.ts | 5 ++++ 6 files changed, 50 insertions(+), 32 deletions(-) diff --git a/src/index.ts b/src/index.ts index af8733c..3b2a79f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -61,6 +61,11 @@ app.use( return; } + if (req.session) { + req.session.abortTransaction(); + req.session.endSession(); + } + // Check if error is specific with response code if (error instanceof WestEggError) { return res.status(error.statusCode).json({ error } as ErrorResponse); diff --git a/src/middleware/useTransaction.ts b/src/middleware/useTransaction.ts index 5b9c737..0f79c11 100644 --- a/src/middleware/useTransaction.ts +++ b/src/middleware/useTransaction.ts @@ -9,14 +9,8 @@ export const useTransaction = async ( const session = await mongoose.startSession(); req.session = session; - - try { - next(); - } catch (error) { - console.log(error); - await session.abortTransaction(); - next(error); - } finally { - session.endSession(); - } + session.startTransaction(); + next(); + await session.commitTransaction(); + session.endSession(); }; diff --git a/src/routes/auth.ts b/src/routes/auth.ts index bb79011..6fb8bf7 100644 --- a/src/routes/auth.ts +++ b/src/routes/auth.ts @@ -1,7 +1,3 @@ -import { Router } from "express"; -import { createHmac } from "crypto"; -import jwt from "jsonwebtoken"; -import { keys as keysOf } from "ts-transformer-keys"; import { ErrorMessage, GetAuthSignInKeyRequest, @@ -13,7 +9,6 @@ import { PostAuthSignInResponse, StatusCode } from "@gatsby-tv/types"; - import { InvalidToken } from "@src/entities/InvalidToken"; import { PersistSignInKey } from "@src/entities/PersistSignInKey"; import { SignInKey } from "@src/entities/SignInKey"; @@ -22,7 +17,12 @@ import logger from "@src/logger"; import mail from "@src/mail"; import { isValidBody } from "@src/middleware"; import { isAuthenticated, validateSignin } from "@src/middleware/auth"; +import { useTransaction } from "@src/middleware/useTransaction"; import { randomString } from "@src/utilities"; +import { createHmac } from "crypto"; +import { Router } from "express"; +import jwt from "jsonwebtoken"; +import { keys as keysOf } from "ts-transformer-keys"; const router = Router(); @@ -32,6 +32,7 @@ const router = Router(); router.post( "/signin", validateSignin, + useTransaction, (req, res, next) => { isValidBody(keysOf(), req, res, next); }, @@ -115,7 +116,7 @@ router.get("/signin/:key", async (req, res, next) => { /** * POST /auth/signin/:key/persist */ -router.post("/signin/:key/persist", async (req, res, next) => { +router.post("/signin/:key/persist", useTransaction, async (req, res, next) => { try { const params = req.params as PostAuthPersistSignInKeyRequestParams; @@ -158,20 +159,25 @@ router.get("/token/refresh", isAuthenticated, async (req, res, next) => { /** * POST /auth/token/invalidate */ -router.post("/token/invalidate", isAuthenticated, async (req, res, next) => { - try { - const invalid = new InvalidToken({ - expire: Date.now() - }); - await InvalidToken.findByIdAndUpdate(req.decodedToken!._id, invalid, { - upsert: true, - setDefaultsOnInsert: true - }); +router.post( + "/token/invalidate", + isAuthenticated, + useTransaction, + async (req, res, next) => { + try { + const invalid = new InvalidToken({ + expire: Date.now() + }); + await InvalidToken.findByIdAndUpdate(req.decodedToken!._id, invalid, { + upsert: true, + setDefaultsOnInsert: true + }); - res.sendStatus(StatusCode.CREATED); - } catch (error) { - next(error); + res.sendStatus(StatusCode.CREATED); + } catch (error) { + next(error); + } } -}); +); export default router; diff --git a/src/routes/channel.ts b/src/routes/channel.ts index a456621..5b7f25b 100644 --- a/src/routes/channel.ts +++ b/src/routes/channel.ts @@ -39,6 +39,7 @@ import { } from "@src/middleware/channel"; import { upload } from "@src/middleware/multipart"; import { isMongoDuplicateKeyError, projection } from "@src/utilities"; +import { useTransaction } from "@src/middleware/useTransaction"; const router = Router(); @@ -86,6 +87,7 @@ router.get( router.post( "/", isAuthenticated, + useTransaction, (req, res, next) => { isValidBody(keysOf(), req, res, next); }, @@ -198,6 +200,7 @@ router.put( isAuthenticated, hasPermissionToPutChannelRequest, validatePutChannelHandleRequest, + useTransaction, (req, res, next) => { isValidBody(keysOf(), req, res, next); }, @@ -239,6 +242,7 @@ router.put( "/:id/avatar", isAuthenticated, hasPermissionToPutChannelRequest, + useTransaction, (req, res, next) => { upload(req, res, next, 2); }, @@ -273,6 +277,7 @@ router.put( "/:id/banner", isAuthenticated, hasPermissionToPutChannelRequest, + useTransaction, (req, res, next) => { upload(req, res, next, 2); }, @@ -307,6 +312,7 @@ router.put( "/:id/poster", isAuthenticated, hasPermissionToPutChannelRequest, + useTransaction, (req, res, next) => { upload(req, res, next, 2); }, diff --git a/src/routes/user.ts b/src/routes/user.ts index e0ec263..2191518 100644 --- a/src/routes/user.ts +++ b/src/routes/user.ts @@ -37,6 +37,7 @@ import { validatePutUserRequest } from "@src/middleware/user"; import { isMongoDuplicateKeyError, projection } from "@src/utilities"; +import { useTransaction } from "@src/middleware/useTransaction"; const router = Router(); @@ -88,7 +89,6 @@ router.post( useTransaction, async (req, res, next) => { try { - req.session!.startTransaction(); const body = req.body as PostUserCompleteSignupRequest; // Check if signinKey exists @@ -130,8 +130,6 @@ router.post( res .status(StatusCode.CREATED) .json({ token } as PostAuthCompleteSignUpResponse); - - await req.session!.commitTransaction(); } catch (error) { next(error); } @@ -200,6 +198,7 @@ router.put( isValidBody(keysOf(), req, res, next); }, hasPermissionToPutUserRequest, + useTransaction, async (req, res, next) => { try { const body = req.body as PutUserRequest; @@ -234,6 +233,7 @@ router.put( isAuthenticated, hasPermissionToPutUserRequest, validatePutUserRequest, + useTransaction, (req, res, next) => { upload(req, res, next, 2); }, @@ -271,6 +271,7 @@ router.put( "/:id/banner", isAuthenticated, hasPermissionToPutUserRequest, + useTransaction, (req, res, next) => { upload(req, res, next, 2); }, @@ -309,6 +310,7 @@ router.put( isValidBody(keysOf(), req, res, next); }, hasPermissionToPutUserRequest, + useTransaction, async (req, res, next) => { try { const body = req.body as PutUserSubscriptionRequest; diff --git a/src/routes/video.ts b/src/routes/video.ts index 3116202..414c83b 100644 --- a/src/routes/video.ts +++ b/src/routes/video.ts @@ -24,6 +24,7 @@ import { validateVideoExists } from "@src/middleware/video"; import { projection } from "@src/utilities"; +import { useTransaction } from "@src/middleware/useTransaction"; const router = Router(); @@ -56,6 +57,7 @@ router.post( "/", isAuthenticated, validatePostVideo, + useTransaction, (req, res, next) => { isValidBody(keysOf(), req, res, next); }, @@ -103,6 +105,7 @@ router.post( router.put( "/:id", isAuthenticated, + useTransaction, (req, res, next) => { isValidBody(keysOf(), req, res, next); }, @@ -128,6 +131,7 @@ router.put( "/:id/view", isAuthenticated, validateVideoExists, + useTransaction, async (req, res, next) => { try { const params = req.params as PutVideoViewRequestParams; @@ -155,6 +159,7 @@ router.delete( "/:id", isAuthenticated, validateVideoExists, + useTransaction, async (req, res, next) => { try { const request = req.params as DeleteVideoRequest; From 06842051efe471dfda05be05604b72f29e790c48 Mon Sep 17 00:00:00 2001 From: Michael Nesbitt Date: Wed, 11 Aug 2021 08:14:25 -0500 Subject: [PATCH 3/5] Added todo --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index 3b2a79f..09e5eac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -62,6 +62,7 @@ app.use( } if (req.session) { + // TODO: Abort transaction is async req.session.abortTransaction(); req.session.endSession(); } From 90a5457599b563bf503d82630bf72fdd65c8dc4d Mon Sep 17 00:00:00 2001 From: Michael Nesbitt Date: Wed, 11 Aug 2021 08:18:00 -0500 Subject: [PATCH 4/5] Rename middleware file --- src/middleware/{useTransaction.ts => transaction.ts} | 0 src/routes/auth.ts | 2 +- src/routes/channel.ts | 2 +- src/routes/user.ts | 2 +- src/routes/video.ts | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename src/middleware/{useTransaction.ts => transaction.ts} (100%) diff --git a/src/middleware/useTransaction.ts b/src/middleware/transaction.ts similarity index 100% rename from src/middleware/useTransaction.ts rename to src/middleware/transaction.ts diff --git a/src/routes/auth.ts b/src/routes/auth.ts index 6fb8bf7..33e11b1 100644 --- a/src/routes/auth.ts +++ b/src/routes/auth.ts @@ -17,7 +17,7 @@ import logger from "@src/logger"; import mail from "@src/mail"; import { isValidBody } from "@src/middleware"; import { isAuthenticated, validateSignin } from "@src/middleware/auth"; -import { useTransaction } from "@src/middleware/useTransaction"; +import { useTransaction } from "@src/middleware/transaction"; import { randomString } from "@src/utilities"; import { createHmac } from "crypto"; import { Router } from "express"; diff --git a/src/routes/channel.ts b/src/routes/channel.ts index 5b7f25b..8fa7b3b 100644 --- a/src/routes/channel.ts +++ b/src/routes/channel.ts @@ -39,7 +39,7 @@ import { } from "@src/middleware/channel"; import { upload } from "@src/middleware/multipart"; import { isMongoDuplicateKeyError, projection } from "@src/utilities"; -import { useTransaction } from "@src/middleware/useTransaction"; +import { useTransaction } from "@src/middleware/transaction"; const router = Router(); diff --git a/src/routes/user.ts b/src/routes/user.ts index 2191518..db15bab 100644 --- a/src/routes/user.ts +++ b/src/routes/user.ts @@ -37,7 +37,7 @@ import { validatePutUserRequest } from "@src/middleware/user"; import { isMongoDuplicateKeyError, projection } from "@src/utilities"; -import { useTransaction } from "@src/middleware/useTransaction"; +import { useTransaction } from "@src/middleware/transaction"; const router = Router(); diff --git a/src/routes/video.ts b/src/routes/video.ts index 414c83b..16c2d6d 100644 --- a/src/routes/video.ts +++ b/src/routes/video.ts @@ -24,7 +24,7 @@ import { validateVideoExists } from "@src/middleware/video"; import { projection } from "@src/utilities"; -import { useTransaction } from "@src/middleware/useTransaction"; +import { useTransaction } from "@src/middleware/transaction"; const router = Router(); From c91ac7ed2030b69172d558e89809d601446542aa Mon Sep 17 00:00:00 2001 From: Michael Nesbitt Date: Fri, 13 Aug 2021 20:14:39 -0500 Subject: [PATCH 5/5] Nothing works --- src/index.ts | 9 ++- src/middleware/transaction.ts | 30 +++++-- src/routes/auth.ts | 143 +++++++++++++++++++++------------- src/routes/channel.ts | 47 +++++++---- src/routes/user.ts | 54 ++++++++----- src/routes/video.ts | 35 ++++++--- 6 files changed, 202 insertions(+), 116 deletions(-) diff --git a/src/index.ts b/src/index.ts index 09e5eac..7f50a21 100644 --- a/src/index.ts +++ b/src/index.ts @@ -50,7 +50,7 @@ app.use("/v1", router); // Handle all errors app.use( - ( + async ( error: WestEggError | Error, req: Request, res: Response, @@ -61,10 +61,13 @@ app.use( return; } + console.log(req.session); if (req.session) { + console.log("Canceling transaction"); // TODO: Abort transaction is async - req.session.abortTransaction(); - req.session.endSession(); + console.log("Aborting transaction in index.ts"); + // await req.session.abortTransaction(); + // req.session.endSession(); } // Check if error is specific with response code diff --git a/src/middleware/transaction.ts b/src/middleware/transaction.ts index 0f79c11..84e451c 100644 --- a/src/middleware/transaction.ts +++ b/src/middleware/transaction.ts @@ -1,16 +1,32 @@ import { NextFunction, Request, Response } from "express"; import mongoose from "mongoose"; -export const useTransaction = async ( +export const startTransaction = async ( req: Request, res: Response, next: NextFunction ) => { - const session = await mongoose.startSession(); + try { + const session = await mongoose.startSession(); - req.session = session; - session.startTransaction(); - next(); - await session.commitTransaction(); - session.endSession(); + req.session = session; + } catch (err) { + next(err); + } +}; + +export const commitTransaction = async ( + req: Request, + res: Response, + next: NextFunction +) => { + try { + console.log("Committing transaction"); + if (!req.session) { + throw new Error("No session found"); + } + req.session.endSession(); + } catch (err) { + next(err); + } }; diff --git a/src/routes/auth.ts b/src/routes/auth.ts index 33e11b1..112573a 100644 --- a/src/routes/auth.ts +++ b/src/routes/auth.ts @@ -17,11 +17,16 @@ import logger from "@src/logger"; import mail from "@src/mail"; import { isValidBody } from "@src/middleware"; import { isAuthenticated, validateSignin } from "@src/middleware/auth"; -import { useTransaction } from "@src/middleware/transaction"; +import { + //commitTransaction, + startTransaction +} from "@src/middleware/transaction"; import { randomString } from "@src/utilities"; import { createHmac } from "crypto"; -import { Router } from "express"; +import { NextFunction, Request, Response, Router } from "express"; import jwt from "jsonwebtoken"; +import { TransactionOptions } from "mongodb"; +import mongoose from "mongoose"; import { keys as keysOf } from "ts-transformer-keys"; const router = Router(); @@ -32,52 +37,70 @@ const router = Router(); router.post( "/signin", validateSignin, - useTransaction, - (req, res, next) => { + //startTransaction, + (req: Request, res: Response, next: NextFunction) => { isValidBody(keysOf(), req, res, next); }, - async (req, res, next) => { + async (req: Request, res: Response, next: NextFunction) => { + const params = new URLSearchParams(); + params.set("retryWrite", "true"); + params.set("w", "majority"); + params.set("authSource", "admin"); + const connection = await mongoose.createConnection( + `${process.env.MONGO_URL}?${params}`, + { + useFindAndModify: false, + useCreateIndex: true + } + ); + const session = await connection.startSession(); try { - const signin = req.body as PostAuthSignInRequest; + await session.withTransaction(async () => { + const signin = req.body as PostAuthSignInRequest; - // Check if user already exists with email - const exists = !!(await User.findOne({ email: signin.email })); + // Check if user already exists with email + const exists = !!(await User.findOne({ email: signin.email })); - // Create a signinKey that expires after set time (see entities/SignInKey.ts) - const key = createHmac("sha256", randomString()).digest("hex"); - const signinKey = new SignInKey({ - key: key, - email: signin.email - }); - await signinKey.save(); - - // Send an email to the user with the signin key and if they need to complete signin - const link = new URL( - `/$magiclink?key=${signinKey.key}&exists=${exists}`, - process.env.GATSBY_URL! - ); - - // Don't send mail in dev - if (process.env.NODE_ENV !== "development") { - await mail.send({ - to: signin.email, - from: "noreply@gatsby.sh", - subject: "New signin request from Gatsby.", - text: `Click this link to complete the sign in process: ${link}` + // Create a signinKey that expires after set time (see entities/SignInKey.ts) + const key = createHmac("sha256", randomString()).digest("hex"); + const signinKey = new SignInKey({ + key: key, + email: signin.email }); - // Return 200 OK if no internal errors as to not indicate email is in use - res.status(StatusCode.OK).json({} as PostAuthSignInResponse); - } else { - // Send signinKey key (ONLY IN DEV) - logger.warn( - `Email not sent to ${signinKey.email}. SignIn key sent in response.` + await signinKey.save(); + + // Send an email to the user with the signin key and if they need to complete signin + const link = new URL( + `/$magiclink?key=${signinKey.key}&exists=${exists}`, + process.env.GATSBY_URL! ); - res.status(StatusCode.OK).json({ key: signinKey.key }); - } + + // Don't send mail in dev + if (process.env.NODE_ENV !== "development") { + await mail.send({ + to: signin.email, + from: "noreply@gatsby.sh", + subject: "New signin request from Gatsby.", + text: `Click this link to complete the sign in process: ${link}` + }); + // Return 200 OK if no internal errors as to not indicate email is in use + res.status(StatusCode.OK).json({} as PostAuthSignInResponse); + } else { + // Send signinKey key (ONLY IN DEV) + logger.warn( + `Email not sent to ${signinKey.email}. SignIn key sent in response.` + ); + res.status(StatusCode.OK).json({ key: signinKey.key }); + } + }); + // next(); } catch (error) { + console.log("Aborting transaction"); + // await session.abortTransaction(); next(error); } } + //commitTransaction ); /** @@ -116,26 +139,32 @@ router.get("/signin/:key", async (req, res, next) => { /** * POST /auth/signin/:key/persist */ -router.post("/signin/:key/persist", useTransaction, async (req, res, next) => { - try { - const params = req.params as PostAuthPersistSignInKeyRequestParams; +router.post( + "/signin/:key/persist", + //startTransaction, + async (req: Request, res: Response, next: NextFunction) => { + try { + const params = req.params as PostAuthPersistSignInKeyRequestParams; + + const signinKey = await SignInKey.findOne({ key: params.key }); + if (signinKey) { + const persistSignInKey = new PersistSignInKey({ + _id: signinKey._id, + key: signinKey.key, + email: signinKey.email + }); + await persistSignInKey.save(); + } - const signinKey = await SignInKey.findOne({ key: params.key }); - if (signinKey) { - const persistSignInKey = new PersistSignInKey({ - _id: signinKey._id, - key: signinKey.key, - email: signinKey.email - }); - await persistSignInKey.save(); + // ALWAYS send back OK as to not let the client know if the key exists or not + res.sendStatus(StatusCode.OK); + next(); + } catch (error) { + next(error); } - - // ALWAYS send back OK as to not let the client know if the key exists or not - res.sendStatus(StatusCode.OK); - } catch (error) { - next(error); } -}); + //commitTransaction +); /** * GET /auth/token/refresh @@ -162,8 +191,8 @@ router.get("/token/refresh", isAuthenticated, async (req, res, next) => { router.post( "/token/invalidate", isAuthenticated, - useTransaction, - async (req, res, next) => { + //startTransaction, + async (req: Request, res: Response, next: NextFunction) => { try { const invalid = new InvalidToken({ expire: Date.now() @@ -174,10 +203,12 @@ router.post( }); res.sendStatus(StatusCode.CREATED); + next(); } catch (error) { next(error); } } + //commitTransaction ); export default router; diff --git a/src/routes/channel.ts b/src/routes/channel.ts index 8fa7b3b..3493f46 100644 --- a/src/routes/channel.ts +++ b/src/routes/channel.ts @@ -23,7 +23,7 @@ import { PutChannelPosterResponse, StatusCode } from "@gatsby-tv/types"; -import { Router } from "express"; +import { NextFunction, Request, Response, Router } from "express"; import { Types } from "mongoose"; import { keys as keysOf } from "ts-transformer-keys"; @@ -39,7 +39,10 @@ import { } from "@src/middleware/channel"; import { upload } from "@src/middleware/multipart"; import { isMongoDuplicateKeyError, projection } from "@src/utilities"; -import { useTransaction } from "@src/middleware/transaction"; +import { + commitTransaction, + startTransaction +} from "@src/middleware/transaction"; const router = Router(); @@ -87,12 +90,12 @@ router.get( router.post( "/", isAuthenticated, - useTransaction, - (req, res, next) => { + //startTransaction, + (req: Request, res: Response, next: NextFunction) => { isValidBody(keysOf(), req, res, next); }, validatePostChannel, - async (req, res, next) => { + async (req: Request, res: Response, next: NextFunction) => { try { const body: PostChannelRequest = req.body; @@ -130,10 +133,12 @@ router.post( keysOf() ) as PostChannelResponse ); + next(); } catch (error) { next(error); } } + //commitTransaction ); /** @@ -200,11 +205,11 @@ router.put( isAuthenticated, hasPermissionToPutChannelRequest, validatePutChannelHandleRequest, - useTransaction, - (req, res, next) => { + //startTransaction, + (req: Request, res: Response, next: NextFunction) => { isValidBody(keysOf(), req, res, next); }, - async (req, res, next) => { + async (req: Request, res: Response, next: NextFunction) => { try { const body = req.body as PutChannelHandleRequest; const params = req.params as PutChannelHandleRequestParams; @@ -229,10 +234,12 @@ router.put( res .status(StatusCode.CREATED) .json(channel.toJSON() as PutChannelHandleResponse); + next(); } catch (error) { next(error); } } + //commitTransaction ); /** @@ -242,11 +249,11 @@ router.put( "/:id/avatar", isAuthenticated, hasPermissionToPutChannelRequest, - useTransaction, - (req, res, next) => { + //startTransaction, + (req: Request, res: Response, next: NextFunction) => { upload(req, res, next, 2); }, - async (req, res, next) => { + async (req: Request, res: Response, next: NextFunction) => { try { const params = req.params as PutChannelAvatarRequestParams; const channel = await Channel.findById( @@ -264,10 +271,12 @@ router.put( res .status(StatusCode.CREATED) .json(channel.toJSON() as PutChannelAvatarResponse); + next(); } catch (error) { next(error); } } + //commitTransaction ); /** @@ -277,11 +286,11 @@ router.put( "/:id/banner", isAuthenticated, hasPermissionToPutChannelRequest, - useTransaction, - (req, res, next) => { + //startTransaction, + (req: Request, res: Response, next: NextFunction) => { upload(req, res, next, 2); }, - async (req, res, next) => { + async (req: Request, res: Response, next: NextFunction) => { try { const params = req.params as PutChannelBannerRequestParams; const channel = await Channel.findById( @@ -299,10 +308,12 @@ router.put( res .status(StatusCode.CREATED) .json(channel.toJSON() as PutChannelBannerResponse); + next(); } catch (error) { next(error); } } + //commitTransaction ); /** @@ -312,11 +323,11 @@ router.put( "/:id/poster", isAuthenticated, hasPermissionToPutChannelRequest, - useTransaction, - (req, res, next) => { + //startTransaction, + (req: Request, res: Response, next: NextFunction) => { upload(req, res, next, 2); }, - async (req, res, next) => { + async (req: Request, res: Response, next: NextFunction) => { try { const params = req.params as PutChannelPosterRequestParams; const channel = await Channel.findById( @@ -334,10 +345,12 @@ router.put( res .status(StatusCode.CREATED) .json(channel.toJSON() as PutChannelPosterResponse); + next(); } catch (error) { next(error); } } + //commitTransaction ); export default router; diff --git a/src/routes/user.ts b/src/routes/user.ts index db15bab..c654941 100644 --- a/src/routes/user.ts +++ b/src/routes/user.ts @@ -21,23 +21,25 @@ import { PutUserSubscriptionResponse, StatusCode } from "@gatsby-tv/types"; -import { Router } from "express"; -import jwt from "jsonwebtoken"; -import { Types } from "mongoose"; -import { keys as keysOf } from "ts-transformer-keys"; - import { PersistSignInKey } from "@src/entities/PersistSignInKey"; import { SignInKey } from "@src/entities/SignInKey"; import { User } from "@src/entities/User"; import { isValidBody } from "@src/middleware"; import { isAuthenticated, validateSignup } from "@src/middleware/auth"; import { upload } from "@src/middleware/multipart"; +import { + //commitTransaction, + startTransaction +} from "@src/middleware/transaction"; import { hasPermissionToPutUserRequest, validatePutUserRequest } from "@src/middleware/user"; import { isMongoDuplicateKeyError, projection } from "@src/utilities"; -import { useTransaction } from "@src/middleware/transaction"; +import { NextFunction, Request, Response, Router } from "express"; +import jwt from "jsonwebtoken"; +import { Types } from "mongoose"; +import { keys as keysOf } from "ts-transformer-keys"; const router = Router(); @@ -83,11 +85,11 @@ router.get( router.post( "/", validateSignup, - (req, res, next) => { + (req: Request, res: Response, next: NextFunction) => { isValidBody(keysOf(), req, res, next); }, - useTransaction, - async (req, res, next) => { + //startTransaction, + async (req: Request, res: Response, next: NextFunction) => { try { const body = req.body as PostUserCompleteSignupRequest; @@ -130,10 +132,12 @@ router.post( res .status(StatusCode.CREATED) .json({ token } as PostAuthCompleteSignUpResponse); + next(); } catch (error) { next(error); } } + //commitTransaction ); /** @@ -194,12 +198,12 @@ router.get("/:id/promotions", async (req, res, next) => { router.put( "/:id", isAuthenticated, - (req, res, next) => { + (req: Request, res: Response, next: NextFunction) => { isValidBody(keysOf(), req, res, next); }, hasPermissionToPutUserRequest, - useTransaction, - async (req, res, next) => { + //startTransaction, + async (req: Request, res: Response, next: NextFunction) => { try { const body = req.body as PutUserRequest; const params = req.params as PutUserRequestParams; @@ -219,10 +223,12 @@ router.put( } res.sendStatus(StatusCode.CREATED); + next(); } catch (error) { next(error); } } + //commitTransaction ); /** @@ -233,11 +239,11 @@ router.put( isAuthenticated, hasPermissionToPutUserRequest, validatePutUserRequest, - useTransaction, - (req, res, next) => { + //startTransaction, + (req: Request, res: Response, next: NextFunction) => { upload(req, res, next, 2); }, - async (req, res, next) => { + async (req: Request, res: Response, next: NextFunction) => { try { const params = req.params as PutUserAvatarRequestParams; @@ -258,10 +264,12 @@ router.put( res .status(StatusCode.CREATED) .json(user.toJSON() as PutUserAvatarResponse); + next(); } catch (error) { next(error); } } + //commitTransaction ); /** @@ -271,11 +279,11 @@ router.put( "/:id/banner", isAuthenticated, hasPermissionToPutUserRequest, - useTransaction, - (req, res, next) => { + //startTransaction, + (req: Request, res: Response, next: NextFunction) => { upload(req, res, next, 2); }, - async (req, res, next) => { + async (req: Request, res: Response, next: NextFunction) => { try { const params = req.params as PutUserBannerRequestParams; @@ -294,10 +302,12 @@ router.put( res .status(StatusCode.CREATED) .json(user.toJSON() as PutUserBannerResponse); + next(); } catch (error) { next(error); } } + //commitTransaction ); /** @@ -306,12 +316,12 @@ router.put( router.put( "/:id/subscription", isAuthenticated, - (req, res, next) => { + (req: Request, res: Response, next: NextFunction) => { isValidBody(keysOf(), req, res, next); }, hasPermissionToPutUserRequest, - useTransaction, - async (req, res, next) => { + //startTransaction, + async (req: Request, res: Response, next: NextFunction) => { try { const body = req.body as PutUserSubscriptionRequest; const params = req.params as PutUserSubscriptionRequestParams; @@ -331,10 +341,12 @@ router.put( res .status(StatusCode.CREATED) .json(user.toJSON() as PutUserSubscriptionResponse); + next(); } catch (error) { next(error); } } + //commitTransaction ); export default router; diff --git a/src/routes/video.ts b/src/routes/video.ts index 16c2d6d..6c3bcbb 100644 --- a/src/routes/video.ts +++ b/src/routes/video.ts @@ -11,7 +11,7 @@ import { PutVideoViewRequestParams, StatusCode } from "@gatsby-tv/types"; -import { Router } from "express"; +import { NextFunction, Request, Response, Router } from "express"; import { keys as keysOf } from "ts-transformer-keys"; import { Channel } from "@src/entities/Channel"; @@ -24,7 +24,10 @@ import { validateVideoExists } from "@src/middleware/video"; import { projection } from "@src/utilities"; -import { useTransaction } from "@src/middleware/transaction"; +import { + commitTransaction, + startTransaction +} from "@src/middleware/transaction"; const router = Router(); @@ -57,11 +60,11 @@ router.post( "/", isAuthenticated, validatePostVideo, - useTransaction, - (req, res, next) => { + //startTransaction, + (req: Request, res: Response, next: NextFunction) => { isValidBody(keysOf(), req, res, next); }, - async (req, res, next) => { + async (req: Request, res: Response, next: NextFunction) => { try { const request = req.body as PostVideoRequest; @@ -93,10 +96,12 @@ router.post( .json( pick(video.toJSON(), keysOf()) as PostVideoResponse ); + next(); } catch (error) { next(error); } } + //commitTransaction ); /* @@ -105,12 +110,12 @@ router.post( router.put( "/:id", isAuthenticated, - useTransaction, - (req, res, next) => { + //startTransaction, + (req: Request, res: Response, next: NextFunction) => { isValidBody(keysOf(), req, res, next); }, validatePutVideo, - async (req, res, next) => { + async (req: Request, res: Response, next: NextFunction) => { try { const body = req.body as PutVideoRequest; const params = req.params as PutVideoRequestParams; @@ -118,10 +123,12 @@ router.put( await Video.findByIdAndUpdate(params.id, body); res.sendStatus(StatusCode.CREATED); + next(); } catch (error) { next(error); } } + //commitTransaction ); /* @@ -131,8 +138,8 @@ router.put( "/:id/view", isAuthenticated, validateVideoExists, - useTransaction, - async (req, res, next) => { + //startTransaction, + async (req: Request, res: Response, next: NextFunction) => { try { const params = req.params as PutVideoViewRequestParams; @@ -146,10 +153,12 @@ router.put( video.save(); res.sendStatus(StatusCode.CREATED); + next(); } catch (error) { next(error); } } + //commitTransaction ); /* @@ -159,18 +168,20 @@ router.delete( "/:id", isAuthenticated, validateVideoExists, - useTransaction, - async (req, res, next) => { + //startTransaction, + async (req: Request, res: Response, next: NextFunction) => { try { const request = req.params as DeleteVideoRequest; await Video.findByIdAndRemove(request.id); res.sendStatus(StatusCode.NO_CONTENT); + next(); } catch (error) { next(error); } } + //commitTransaction ); export default router;