Nest.js - Pass data from Guard to #UploadFile - nestjs

Reference Answer: https://stackoverflow.com/a/61407264/8518207
The author of the above answer mentions you can clone the request object before modifying it in your guard.
How can this be done?
My Goal: Guard will basically verify if the auth token is valid before proceeding to doing anything in the controller such as call FileInterceptor or print those console logs. if token is guard is verified, the FileInterceptor should be used to extract the file from the request.
Example Code:
#Put(':uid')
#UseGuards(AuthGuard)
#UseInterceptors(
FileInterceptor('file', {
storage: diskStorage({
destination: './.tmp',
filename: editFileName,
}),
fileFilter: fileFilter,
}),
)
public async update(
#Req() req,
#UploadedFile() file,
) {
this.logger.log('update')
console.log('in controller')
console.log('req', req)
console.log('file', file)
}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest()
const response = context.switchToHttp().getResponse()
const postMulterRequest: any = await new Promise((resolve, reject) => {
const multerReponse = multer().any()
multerReponse(request, response, err => {
const user_uid = request.body.user_uid
if (err) reject(err)
resolve(request)
})
})
if (request) {
// some validations happen here....
request.body = postMulterRequest.body
request.user = await this.validateToken(request.headers.cookie, user_uid)
return true
} else {
return true
}
}

Related

I would like to send multiple images to the S3 from amazon. This is my code so far, sending just one image

I'm using TYPESCRIPT and NODEJS. In addition to sending the results to the database in POSTGRESSQL.
ROUTER.TS
router.post(
"/image",
isAuthenticated,
upload.single("file"),
async (req, res) => {
const { file } = req;
const product_id = req.query.product_id as string;
const uploadImagesService = new UploadImagesService();
await uploadImagesService.execute(file);
const createImage = await prismaClient.uploadImage.create({
data: {
url: `https://upload-joias.s3.amazonaws.com/${file.filename}`,
id: file.filename,
product_id: product_id,
},
});
return res.send(createImage);
}
);
SERVICE.TS
import S3Storage from "../../utils/S3Storage";
class UploadImagesService {
async execute(file: Express.Multer.File): Promise<void> {
const s3Storage = new S3Storage();
await s3Storage.saveFile(file.filename);
}
}
export { UploadImagesService };
S3Storage.ts
async saveFile(filename: string): Promise<void> {
const originalPath = path.resolve(uploadConfig.diretory, filename);
const contentType = mime.getType(originalPath);
if (!contentType) {
throw new Error("File not found");
}
const fileContent = await fs.promises.readFile(originalPath);
this.client
.putObject({
Bucket: "upload-joias",
Key: filename,
ACL: "public-read",
Body: fileContent,
ContentType: contentType,
})
.promise();
await fs.promises.unlink(originalPath);
}
I'm having a hard time dealing with this, I'm new to node js and typescript. I'm grateful for any help.

Context is empty in GraphQL middleware

I'm sending from frontend authorization token in headers and then I want to check validity of this token in some endpoints using middleware and context, but context is always empty.
I'm using type-graphql.
Frontend code (I check request in 'Network' tab and I can see my additional header):
private async mutate<T>(
mutation: DocumentNode,
data: unknown,
token?: string
) {
const response = await apolloClient.mutate<T>({
mutation: mutation,
context: {
headers: {
'auth-token': token || '',
},
},
variables: {
data: data,
},
});
return response.data;
}
Resolver code:
#Mutation(() => Token)
#UseMiddleware(authMiddleware)
async login(#Ctx() ctx: unknown, #Arg('data') data: LoginInput) {
console.log(ctx);
...
}
Middleware code:
export const authMiddleware: MiddlewareFn = ({ context }, next) => {
console.log(context);
try {
return next();
} catch (error) {
return next();
}
};
console.log is always equal to {}
I found the cause.
In declaration of ApollorServer the context was missing.
const server = new ApolloServer({
schema,
context: ({ req }) => {
const context = {
req,
};
return context;
},
cors: {
origin: '*',
credentials: true,
},
});

pass httponly cookies through apollo graphlql server

GOAL: have my api gateway get the httponly cookies being returned from my rest endpoints and pass it along to frontend, also the front end should be able to pass the cookies through.
httpO=httponly
SPA(react) apiGateway(apolloQL) restEndpoint
httpO-cookies----> <-----(httpO)cookies-----> <-----(httpO)cookies
current the resolvers I have are able to see the "set-cookies" in the response from the endpoints but throughout the response lifecycle the header's are lost.
const apolloServer: ApolloServer = new ApolloServer({
context: ({ res }) => {
// console.log(res,"res");
return ({
res
});
},
formatError,
resolvers,
typeDefs,
formatResponse: (response: GraphQLResponse) => {
console.log(response.http?.headers, "header?");
return {
headers: {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Credentials': 'true',
},
data: response.data,
errors: response.errors,
};
}
});
ex. of resolver:
const signupUser = async (parent: any, args: any, context: any, info: any) => {
try {
const response = await UserService.createUser(args);
console.log(response , "response");
return response;
} catch (error) {
console.log(error
}
};
in this example lets assume the UserService.createUser return the entire response Object, not response.data.
const signupUser = async (parent: any, args: any, context: any, info: any) => {
try {
//call get the Express response from the context
const contextResponse = context.res
//or
const {res} = context
//then set the cookies to the response
const response = await UserService.createUser(args);
contextResponse.header('set-cookie', response?.headers['set-cookie']);
//return the original data
return response?.data;
} catch (error) {
console.log(error
}
};

I get undefined when reading my response but there is a response in React.js

I can't figure it out, the answer comes in the network table but when I want to console.log it, this will display undefined. Do you have any idea why? I attach the pictures and the code.
Here is a image with my codes and response
Here is the code - first one is where I send the response. As I said, it's going well on network tab, I get a 200 status.
export const getAccountStatus = async (req, res) => {
const user = await User.findById(req.user._id).exec();
const account = await stripe.accounts.retrieve(user.stripe_account_id);
// console.log("user account retrieve", account);
const updatedUser = await User.findByIdAndUpdate(
user._id,
{
stripe_seller: account
},
{ new: true }
)
.select("-password")
.exec();
console.log(updatedUser);
res.send(updatedUser);
};
Here is the page where i want to console.log it:
const StripeCallback = ({ history }) => {
const { auth } = useSelector(state => ({ ...state }));
const dispatch = useDispatch();
useEffect(() => {
if (auth && auth.token) accountStatus();
}, [auth]);
const accountStatus = async () => {
try {
const res = await getAccountStatus(auth.token);
console.log(res);
} catch (err) {
console.log(err);
}
};
return <div>test</div>;
};
Ang here is the Axios.post (which is working well as I know):
export const getAccountStatus = async token => {
await axios.post(
`${process.env.REACT_APP_API}/get-account-status`,
{},
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
};
Thank you!
getAccountStatus doesn't have a return statement, so res in const res = await getAccountStatus(auth.token); will always be undefined.
export const getAccountStatus = async token => {
return axios.post( // <----- added return
`${process.env.REACT_APP_API}/get-account-status`,
{},
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
};

multer upload file with body and validation and save file name to database

My code looks like this:
const multer = require('multer');
const path = require('path');
const storage = multer.diskStorage({
destination(req: Request, file: any, cb: any) {
cb(null, 'uploads/');
},
filename(req: Request, file: any, cb: any) {
cb(null, `${file.fieldname}-${Date.now()}${path.extname(file.originalname)}`);
}
});
const upload = multer({ storage, limits: { fieldSize: `${UPLOAD_LIMIT}MB` } });
router.route('/post').post(upload.single('file'), validate(createPost), controller.createPost);
createPost validation looks like this:
createPost: {
body: Joi.object({
email: Joi.string().email(),
title: Joi.string().min(1).max(128),
message: Joi.string().min(2).max(5000).required(),
hidden: Joi.boolean().required(),
embed: Joi.string().min(6).max(128),
password: Joi.string().min(6).max(128)
})
},
createPost controller looks like this:
exports.createPost = async (req: Request, res: Response, next: NextFunction) => {
const { threadId } = req.params;
try {
const newPost = new Post(req.body);
const data = await newPost.save();
apiJson({ req, res, data, model: newPost });
} catch (e) {
next(e);
}
};
And my questions are:
How to pass newly generated filename of saved file to my createPost controller?
Will my validation work correctly? Validating body and file in different places?

Resources