Upload File To Slack From Hapi - node.js

I am trying to upload a file that is being uploaded to my hapijs server directly to slack using the web api files.upload.
I get in return an error message {"ok":false,"error":"no_file_data"}.
The same code works if i upload a file that already in the storage using fs.createReadStream
Here is my code:
const Promise = require('bluebird');
const Joi = require('joi');
const Path = require('path');
const Boom = require('boom');
const Utils = require('../utils');
const Request = require('request');
exports.routes = Promise.coroutine(function*(server, options) {
server.route({
method: 'POST',
path: options.prefix,
config: {
tags: ['api', 'v1'],
payload: {
maxBytes: 2097152,
output: 'stream',
parse: true
},
plugins: {
'hapi-swagger': {
payloadType: 'form'
}
},
validate: {
payload: Joi.object({
cv: Joi.object({ pipe: Joi.func().required() }).required().unknown().description('CV file').meta({ swaggerType: 'file' })
})
},
response: {
emptyStatusCode: 204,
schema: false
},
handler: Utils.coHandler(function*(request, reply) {
const supportedFiles = ['docx', 'doc', 'pdf', 'pages'];
const ext = Path.extname(request.payload.cv.hapi.filename).substr(1);
if (!supportedFiles.filter((supExt) => supExt === ext).length) {
return reply(Boom.badRequest('Unsupported file type'));
}
Request.post({
url: 'https://slack.com/api/files.upload',
formData: {
token: 'XXXX',
filename: `${request.payload.name} - CV.${ext}`,
file: request.payload.cv,
channels: 'XXXX'
}
}, (err, res, body) => {
console.log(body);
});
reply();
})
}
});
});

Related

Custom authorizations lambda functions node js [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 14 days ago.
Improve this question
How to use custom authorizer for lambda functions through https API in AWS using node js
I am a novice to the field so I do not have prior experience regarding this. Any help is highly appreciated.
Create following 3 files under respective file names (serverless.yml, middleware-auth.js, auth.js) and write the code inside those files. This allows custom authorization for AWS lambda functions.
serverless.yml
provider:
name: aws
runtime: nodejs14.x
lambdaHashingVersion: 20201221
# you can overwrite defaults here
stage: dev
region: us-east-1
apiGateway:
binaryMediaTypes:
- "*/*"
httpApi:
cors: true
authorizers:
customAuthorizer:
type: request
functionName: authoriserAnyToken
functions:
authoriserAnyToken:
handler: middleware-auth.handler
signin:
handler: auth.signin
timeout: 15
events:
- httpApi:
path: /auth/{role}/signin
method: post
auth_remember_me:
handler: auth.auth_remember_me
timeout: 15
events:
- httpApi:
path: /auth/remember-me
method: post
authorizer:
name: customAuthorizer
type: request
auth_jwt_token_check:
handler: auth.auth_jwt_token_check
timeout: 15
events:
- httpApi:
path: /auth/jwt/check
method: post
authorizer:
name: customAuthorizer
type: request
middleware-auth.js
const JWT_SECRET = "goK!pusp6ThEdURUtRenOwUhAsSURESHBazl!uJLPlS8EbreWLdrupIwabRAsiBa";
const jwt = require("jsonwebtoken");
exports.handler = async event => {
console.log('event', event);
const token = event.headers.authorization? event.headers.authorization.replace('Bearer ', ''): null;
if (!token) {
console.log('could not find a token on the event');
return generatePolicy({ allow: false });
}
try {
const decoded = jwt.verify(token, JWT_SECRET);
console.log('token_details ', decoded);
if(decoded.user == 'admin'){
return generatePolicy({ allow: true });
}
else if (decoded.user == 'seller')
{
return generatePolicySeller({ allow: true });
}
} catch (error) {
console.log('error ', error);
return generatePolicy({ allow: false });
}
};
const generatePolicy = ({ allow }) => {
return {
principalId: 'token',
policyDocument: {
Version: '2012-10-17',
Statement: {
Action: 'execute-api:Invoke',
Effect: allow ? 'Allow' : 'Deny',
Resource: '*',
// Resource: 'arn:aws:execute-api:us-east-1:*:*/*/*',
// Resource: 'arn:aws:execute-api:us-east-1:*:*/*/auth/*',
},
},
};
};
const generatePolicySeller = ({ allow }) => {
return {
principalId: 'token',
policyDocument: {
Version: '2012-10-17',
Statement: {
Action: 'execute-api:Invoke',
Effect: allow ? 'Allow' : 'Deny',
// Resource: '*',
// Resource: 'arn:aws:execute-api:us-east-1:*:*/*/*',
Resource: ['arn:aws:execute-api:us-east-1:*:*/*/sellers/*','arn:aws:execute-api:us-east-1:*:*/*/auth/*'],
},
},
};
};
auth.js
'use strict';
const jsonwebtoken = require("jsonwebtoken");
const bcrypt = require('bcrypt');
const AWS = require("aws-sdk");
const s3 = new AWS.S3()
const MAX_SIZE = 2097152 // 2MB
const bucket = 'S3_BUCKET_NAME' // Name of your bucket.
const Busboy = require("busboy")
const JWT_SECRET = "goK!pusp6ThEdURUtRenOwUhAsSURESHBazl!uJLPlS8EbreWLdrupIwabRAsiBa";
// Tables
const USERS_TABLE = 'users'
const { Validator } = require('node-input-validator');
s3.config.update({
region: "us-east-1",
accessKeyId: 'S3_ACCESS_KEY_ID', // ACCESS_KEY_ID of your S3 bucket.
secretAccessKey: 'S3_SECRET_ACCESS_KEY' // SECRET_ACCESS_KEY of your S3 bucket.
});
const mysql = require('serverless-mysql')({ // DB configuration
config: {
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
debug: false
}
});
const sendJSON = (code, message, data) => {
let resData = {
"status": code < 400 ? 'Success' : 'Error',
"message": message,
}
data ? (resData["data"] = data) : null;
return {
statusCode: code,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
...resData
})
};
}
const FORM = {
parse(body, headers) {
return new Promise((resolve, reject) => {
const data = {};
const buffer = Buffer.from(body, 'base64');
const bb = Busboy({
headers: Object.keys(headers).reduce((newHeaders, key) => {
// busboy expects lower-case headers.
newHeaders[key.toLowerCase()] = headers[key];
return newHeaders;
}, {})
});
bb.on('field', (name, val, info) => {
data[name] = val;
});
bb.on('error', (err) => {
reject(err);
});
bb.on('close', () => {
resolve(data);
});
bb.end(buffer);
});
}
};
module.exports.signin = async (event, context) => {
try {
const data = await FORM.parse(event['body'], event['headers']);
const v = new Validator(data, {
email: 'required|email',
password: 'required'
});
const matched = await v.check();
if (!matched)
return {
statusCode: 400,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(v.errors),
};
let results = "";
let email = data.email;
let password = data.password;
const role = event.pathParameters.role;
results = await mysql.query(`SELECT * FROM ${USERS_TABLE} WHERE email = ? LIMIT 1`, [email]);
await mysql.end();
if (!results[0]) {
return {
statusCode: 400,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
email: {
"message": "User not found"
}
}),
};
} else {
let user_hash = results[0].password;
user_hash = user_hash.replace(/^\$2y(.+)$/i, '$2a$1');
const match = await bcrypt.compare(password, user_hash);
if (!match) {
return {
statusCode: 400,
body: JSON.stringify({
password: {
"message": "Missing or Invalid password"
}
}),
};
}
const user_info = {
'id' : results[0].id,
'role': results[0].role,
'fname' : results[0].fname,
'lname' : results[0].lname
};
const token = jsonwebtoken.sign({ id: results[0].id, user: results[0].role, fname: results[0].fname, lname: results[0].lname}, JWT_SECRET, { expiresIn: "2h" });
return sendJSON(200, 'Successfully logged.',{
'user' : user_info,
'token': token
});
}
} catch (e) {
return sendJSON(400, e.message);
}
};
module.exports.auth_jwt_token_check = async (event) => {
try {
return sendJSON(200, 'verifyed user');
} catch (e) {
return sendJSON(400, e.message);
}
};
module.exports.auth_remember_me = async (event) => {
try {
const token = event.headers.authorization? event.headers.authorization.replace('Bearer ', ''): null;
const decoded = jsonwebtoken.verify(token, JWT_SECRET);
const new_token = jsonwebtoken.sign({ id: decoded.id, user: decoded.user, fname: decoded.fname, lname: decoded.lname}, JWT_SECRET, { expiresIn: "2h" });
return sendJSON(200, 'Get Renew Token.', {
'token': new_token
});
} catch (e) {
return sendJSON(400, e.message);
}
};

Keep getting pending request when trying to call endpoint, what's wrong?

I have made an endpoint to get token for paytm payment integration. In backend when i'm calling api i'm getting reqdata json but in frontend when i'm logging await transactionAPI, i'm getting only pending promise. i've tried using then in backend in PaytmChecksum.generateSignature method & in frontend in fetch but nothing is working. Keep getting the same result. Pls help.
Frontend code:
const makePayment = async () => {
const data = {
oid: String(new Date().valueOf()),
amount: '1.00',
email: 'abc#gmail.com',
};
let transactionAPI = fetch('http://localhost:3000/api/pretransact', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
console.log(await transactionAPI);
}
Backend code:
const https = require('https');
const PaytmChecksum = require('./PaytmChecksum');
export default async function handler(req, res) {
if (req.method == 'POST') {
const { oid, amount, email } = req.body;
let mid = process.env.PAYTM_MID;
let paytmParams = {};
paytmParams.body = {
requestType: 'Payment',
mid,
websiteName: process.env.WEBSITE,
orderId: oid,
callbackUrl: 'http://localhost:3000/api/callback',
txnAmount: {
value: amount,
currency: 'INR',
},
userInfo: {
custId: email,
},
};
const checksum = await PaytmChecksum.generateSignature(
JSON.stringify(paytmParams.body),
process.env.MERCHANT_KEY
);
paytmParams.head = {
signature: checksum,
};
var post_data = JSON.stringify(paytmParams);
const requestAsync = () => {
return new Promise((resolve, reject) => {
var options = {
hostname: 'securegw-stage.paytm.in',
// hostname: 'securegw.paytm.in',
port: 443,
path: `/theia/api/v1/initiateTransaction?mid=${mid}&orderId=${oid}`,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': post_data.length,
},
};
var response = '';
var post_req = https.request(options, function (post_res) {
post_res.on('data', function (chunk) {
response += chunk;
});
post_res.on('end', function () {
resolve(response);
});
});
post_req.write(post_data);
post_req.end();
});
};
const reqdata = await requestAsync();
res.send(200).json(reqdata);
}
}

React Native Axios cannot upload files to server FormData

Im implementing a function to upload titles, descriptions, and images to the server. Here is my snippet of Front-end code:
const [imageFiles, setImageFiles] = React.useState([]);
const callApi = async () => {
const formData = new FormData();
formData.append('title', 'test');
formData.append('content', 'tesst1');
imageFiles.forEach(imageFile => {
formData.append('files', imageFile);
});
const response = await axios({
method: 'post',
url: 'http://localhost:8080/api/complain',
headers: {
'Content-Type': 'multipart/form-data; ',
Authorization:
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9',
},
data: formData,
});
};
const openCamera = () => {
let imgList = [];
ImagePicker.openPicker({
multiple: true,
waitAnimationEnd: false,
includeExif: true,
compressImageQuality: 0.8,
maxFiles: 5,
includeBase64: true,
})
.then(images => {
images.map(image => {
imgList.push({
filename: image.filename,
path: image.path,
data: image.data,
type: image.mime,
height: image.height,
width: image.width,
});
});
setImageFiles(imgList);
})
.catch(err => {
console.log(err);
});
};
Here is the Back end code:
router.post(
"/complain",
authenticateToken,
upload.array("files"),
complainController.createComplain
);
However, when I console log req.files, it returns an empty array. I have tried different methods such as add transform request, using fetch from react native,... but none of them works. Please help me with this issue. Thank you guys.

Uploading Files on production server returns either CORS error or POST 400 Bad Request using Apollo-Graphql

I'm having trouble on uploading files to my production server, in a local environment, everything works fine just as expected, but when I try to do a a Mutation containing file uploads (and only on those mutations) it throws me a CORS error. I'm using Spaces to upload the files and save them into my Database.
app.ts (Back-end):
const configureExpress = async () => {
const app: express.Application = express();
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use('/services', servicesRoute);
const { typeDefs, resolvers } = await buildSchema;
const schema = makeExecutableSchema({ typeDefs, resolvers });
const server = new ApolloServer({
schema,
playground: true,
introspection: true,
uploads: {
maxFileSize: 1000000000,
maxFiles: 100,
},
context: async ({ req }) => ({
auth: await Auth.getUser(req),
}),
formatError: (err) => ({
message: err.message,
code: err.extensions && err.extensions.code,
locations: err.locations,
path: err.path,
extensions: err.extensions && err.extensions.exception,
}),
});
server.applyMiddleware({ app });
return app;
};
export default () => database.connect().then(configureExpress);
client-auth.ts (On the Front-end):
const errorLink = onError(({ graphQLErrors }: any) => {
if (graphQLErrors) {
console.log(graphQLErrors);
graphQLErrors.map(({ message }: any) => console.log(message));
graphQLErrors.map(({ code }: any) => {
if (code === 'UNAUTHENTICATED') {
persistStore(store)
.purge()
.then(() => {
window.location.reload();
});
}
return true;
});
}
});
const authLink = setContext((_, { headers }) => {
const token = store.getState().auth.user!.token;
return {
headers: {
...headers,
authorization: `Bearer ${token}`,
},
};
});
const uploadLink = createUploadLink({
uri: 'https://api.example.com.br/graphql'
// uri: 'http://localhost:4000/graphql',
});
const client = new ApolloClient({
link: ApolloLink.from([errorLink, authLink, uploadLink]),
cache: new InMemoryCache(),
defaultOptions: {
query: {
fetchPolicy: 'network-only',
},
},
});
resolver.ts
return propertyModel.create({
...data
} as DocumentType<any>).then(async property => {
const user = await userModel.findById(input.userId);
if (!user) throw new UserNotFound();
await ownerModel.findByIdAndUpdate(user.owner, {
$push: {
properties: property.id,
}
});
if (input.images) {
input.images.forEach(async image => {
const uploadedImage = await FileS3.upload(image, {
path: 'images',
id: propertyId.toHexString(),
});
await property.updateOne({$push: {images: uploadedImage}});
});
}
if (input.scripture) {
const uploadedScripture = await FileS3.upload(input.scripture, {
path: 'documents',
id: propertyId.toHexString(),
});
await property.updateOne({scripture: uploadedScripture});
}
if (input.registration) {
const uploadedRegistration = await FileS3.upload(input.registration, {
path: 'documents',
id: propertyId.toHexString(),
})
await property.updateOne({
registration: uploadedRegistration,
});
};
if (input.car) {
const uploadedCar = await FileS3.upload(input.car, {
path: 'documents',
id: propertyId.toHexString(),
});
await property.updateOne({
car: uploadedCar,
});
};
if (input.ccir) {
const uploadedCcir = await FileS3.upload(input.ccir, {
path: 'documents',
id: propertyId.toHexString(),
});
await property.updateOne({
ccir: uploadedCcir,
});
};
if (input.itr) {
const uploadedItr = await FileS3.upload(input.itr, {
path: 'documents',
id: propertyId.toHexString(),
});
await property.updateOne({
itr: uploadedItr,
});
};
if (input.subscription) {
const uploadedSubscription = await FileS3.upload(input.subscription, {
path: 'documents',
id: propertyId.toHexString(),
});
await property.updateOne({
subscription: uploadedSubscription
});
return property;
});
};
I'm really lost regarding this error, is this an actual server error? (Production server is on DigitalOcean in Ubuntu) or something wrong regarding the code?
For CORS, if you are using the latest version of ApolloServer then turn on the CORS:
const server = new ApolloServer({
cors: {
credentials: true,
origin: true
},,
...
});
//also apply it here
server.applyMiddleware({ app,
cors: {
credentials: true,
origin: true
}
});
400 status code is returned for bad request which happens when a client sends a malformed request to server, You need to verify that your client is sending the correct data and headers on via correct HTTP verb (post/get etc)
If any one happens to have this same problem, here's how I solved.
After digging through the code I realized that in the server I was receiving a Maximum call stack size exceeded, upon looking further to this problem I realized that It was an error regarding the Graphql-Upload dependency, I fixed it by removing it as a dependency and added the following on my package.json:
"resolutions": {
"fs-capacitor":"^6.2.0",
"graphql-upload": "^11.0.0"
}
after this I just executed this script: npx npm-force-resolutions. And It worked all fine.

File Upload React Native, Node JS, MongoDB, Multer, Grid FS

I am very new to React Native development. I am struggling from the past 2 days to crack the file/image upload with React Native to MongoDB(Grid FS). I literally read all the related forums but there is no luck. I read couple of forums and they gave a sample example but I wasn't succeeded. Here are the sample codes that I wrote.
Client JS (React Native):
import React, { Component } from "react";
import { View, Text, Image, Button, Platform } from "react-native";
import ImagePicker from "react-native-image-picker";
class ImagePickerComponent extends Component {
state = {
photo: null
};
handleChoosePhoto = () => {
const options = {
noData: true
};
ImagePicker.launchImageLibrary(options, response => {
if (response.uri) {
this.setState({ photo: response });
}
});
};
handleUploadPhoto = () => {
const { photo } = this.state;
const bodyJson = { userId: "123" };
console.log("PHOTO");
console.log(photo);
const data = new FormData();
data.append("name", "avatar");
data.append("file", {
uri: photo.uri,
type: photo.type,
name: photo.fileName
});
const config = {
method: "POST",
headers: {
Accept: "application/json"
// "Content-Type": "multipart/form-data"
},
body: data
};
fetch("http://localhost:3900/api/uploadFiles/upload", config)
.then(checkStatusAndGetJSONResponse => {
console.log("checkStatusAndGetJSONResponse");
console.log(checkStatusAndGetJSONResponse);
})
.catch(err => {
console.log(err);
});
};
createFormData = (photo, body) => {
console.log("PHOTO");
console.log(photo);
const data = new FormData();
data.append("photo", {
name: photo.fileName,
type: photo.type,
uri:
Platform.OS === "android" ? photo.uri : photo.uri.replace("file://", "")
});
Object.keys(body).forEach(key => {
data.append(key, body[key]);
});
return data;
};
render() {
const { photo } = this.state;
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
{photo && (
<React.Fragment>
<Image
source={{ uri: photo.uri }}
style={{ width: 300, height: 300 }}
/>
<Button title="Upload" onPress={this.handleUploadPhoto} />
</React.Fragment>
)}
<Button title="Choose Photo" onPress={this.handleChoosePhoto} />
</View>
);
}
}
export default ImagePickerComponent;
And below is the server JS file that handles to store the file in MongoDB with Multer.
const express = require("express");
const router = express();
const multer = require("multer");
const GridFsStorage = require("multer-gridfs-storage");
const path = require("path");
const crypto = require("crypto");
// Create storage engine
const storage = new GridFsStorage({
url: "mongodb://localhost/vidly",
file: (req, file) => {
console.log("In Storage: ");
console.log(file);
return new Promise((resolve, reject) => {
crypto.randomBytes(16, (err, buf) => {
if (err) {
return reject(err);
}
const filename = buf.toString("hex") + path.extname(file.originalname);
const fileInfo = {
filename: filename,
bucketName: "uploads"
};
console.log("fileInfo");
console.log(fileInfo);
resolve(fileInfo);
});
});
}
});
const upload = multer({ storage }).single("file");
// #route POST /upload
// #desc Uploads file to DB
//upload.single("file"),
// router.post("/upload", upload.single("file"), (req, res) => {
// console.log("In Upload Function");
// console.log(req);
// res.json({ file: req.file });
// // res.redirect("/");
// });
router.post("/upload", upload, (req, res) => {
console.log(req);
console.log("file", req.file);
console.log("body", req.body);
res.status(200).json({
message: "success!"
});
});
router.get("/me", (req, res) => {
res.json("Hello!");
});
module.exports = router;
Also, Here is the server package.json files that I am using.
"dependencies": {
"#hapi/joi": "^15.0.3",
"body-parser": "^1.19.0",
"config": "^3.1.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-async-errors": "^3.1.1",
"express-multipart-file-parser": "^0.1.2",
"gridfs-stream": "^1.1.1",
"mongoose": "^5.5.11",
"multer": "^1.4.1",
"multer-gridfs-storage": "^3.2.3",
"winston": "^3.2.1"
}
I am not getting any exception but the file is not storing into the mongoDB. Looks like Multer is not picking up the file from the request. My Server code is working when I test with POSTMAN but its not working with React Native.
I captured the Sample request & response from the react native debugger.
Also, Here are the logs from the server for failing request (From react Native).
route:
Route {
path: '/single',
stack:
[ Layer {
handle: [Function: multerMiddleware],
name: 'multerMiddleware',
params: undefined,
path: undefined,
keys: [],
regexp: /^\/?$/i,
method: 'post' },
Layer {
handle: [Function],
name: '<anonymous>',
params: undefined,
path: undefined,
keys: [],
regexp: /^\/?$/i,
method: 'post' } ],
methods: { post: true } },
**body:** [Object: null prototype] { photo: '[object Object]' },
__onFinished: null }
**undefined**
If we observe the request body we are getting an object.
Here is the server request that was captured through POSTMAN (Its a successful Request).
body: [Object: null prototype] {},
route:
Route {
path: '/upload',
stack:
[ Layer {
handle: [Function: multerMiddleware],
name: 'multerMiddleware',
params: undefined,
path: undefined,
keys: [],
regexp: { /^\/?$/i fast_star: false, fast_slash: false },
method: 'post' },
Layer {
handle: [Function],
name: '<anonymous>',
params: undefined,
path: undefined,
keys: [],
regexp: { /^\/?$/i fast_star: false, fast_slash: false },
method: 'post' } ],
methods: { post: true } },
file:
{ fieldname: 'file',
originalname: 'testAlert.html',
encoding: '7bit',
mimetype: 'text/html',
id: 5d1e3084e3aa4de0b8320773,
filename: 'e7d2080ddbe2cbf2ac7167d79b8ff0f2.html',
metadata: null,
bucketName: 'uploads',
chunkSize: 261120,
size: 148961,
md5: '4e68af2f75a34d2d7371f21544fe9b58',
uploadDate: 2019-07-04T16:59:48.602Z,
contentType: 'text/html' },
__onFinished: null }
If we observe both the requests file object is coming as a separate section through POSTMAN but not the case with react native. Can anyone please help me on this.? Why its not working with React-Native.? I stuck over here. Please help me on this.?
Thanks & Regards,
Amar.T

Resources