I have tried to create a custom email receipt template with SendGrid using commercejs webhooks, by following this tutorial. I have uploaded this github repository to
this test Netlify site. The main code is /functions/email.js I am pretty sure my .env variables are correct, still no receipt emails are received and probably send. When checking the Netlify function email log says:
5:55:03 PM: f7c003e5 ERROR Invoke Error {"errorType":"ReferenceError","errorMessage":"fetch is not defined","stack":["ReferenceError: fetch is not defined"," at Runtime.exports.handler (/var/task/email.js:30:22)"," at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"]}
5:55:03 PM: f7c003e5 Duration: 2.24 ms Memory Usage: 52 MB
However my package.json dependencies look like this:
"dependencies": {
"#chec/commerce.js": "2.2.0",
"#hookform/error-message": "0.0.5",
"#sendgrid/mail": "^7.4.7",
"#stripe/react-stripe-js": "1.1.2",
"#stripe/stripe-js": "1.11.0",
"autoprefixer": "10.0.4",
"classcat": "4.1.0",
"framer-motion": "2.9.4",
"next": "10.0.2",
"next-google-fonts": "1.2.1",
"node-fetch": "^3.0.0",
"pdf-lib": "^1.16.0",
"postcss": "8.1.14",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-hook-form": "6.11.5",
"react-toastify": "6.1.0",
"use-debounce": "^7.0.0"
},
Therefore it's confusing why I am getting the fetch is not defined error. Also, I am also confused about how to implement the headers correctly, because the tutorial did not specify how. So I just added the headers like this, without knowing if this is the way to do it:
let headers = {
"Accept": "application/json",
"Content-Type": "application/json",
};
let sgheaders = {
Authorization: `Bearer ${process.env.SENDGRID_SECRET}`,
"Content-Type": "application/json",
};
In the code currently uploaded to my netlify site, I had to change the export default async function handler(req, res) {
to
exports.handler = async function(req, res) { according to the Netlify functions documentation the Netlify functions documentation. (because of "errorMessage": "SyntaxError: Unexpected token 'export'")
// Create the API endpoint function with a req and res parameter
exports.handler = async function(req, res) {
const checSecretAPIKey = process.env.CHEC_SECRET_KEY;
let headers = {
"Accept": "application/json",
"Content-Type": "application/json",
};
//export default async function handler(req, res) {
if (!req.body || req.httpMethod !== 'POST') {
return {
status: 405,
headers,
body: JSON.stringify({
status: 'Invalid HTTP method',
}),
}
}
const { data } = JSON.parse(req.body);
// Request for your merchant information so that you can use your email
// to include as the 'from' property to send to the SendGrid API
const merchant = fetch(`${process.env.CHEC_API_URL}/v1/merchants`, {
headers: {
'X-Authoriza†ion': process.env.CHEC_SECRET_KEY,
},
}).then((response) => response.json);
// Extract the signature from the registered `orders.create` webhook
const { signature } = data;
delete data.signature;
// Your Chec webhook signing key, from the Chec Dashboard webhooks view
const webHookSigningKey = 'KEJlxz6cIlrWIpsX5jypcMeGl2uh7jJg';
// Verify the signature
const expectedSignature = crypto.createHmac('sha256', webHookSigningKey)
.update(JSON.stringify(data))
.digest('hex');
if (expectedSignature !== signature) {
console.error('Signature mismatched, skipping.')
}
// Verify the age of the request to make sure it isn't more than 5 minutes old.
if (new Date(data.created * 1000) < new Date() - 5 * 60 * 1000) {
console.error('Webhook was sent too long ago, could potentially be fake, ignoring');
}
// Because you will need to list out the order line items, map through the returned line items
// and structure out the data you need to display in the email receipt for your customer
// Note that we are keeping the data structure minimal here
const orderLineItems = data.payload.order.line_items.map((lineItem) => ({
text: lineItem.product_name,
price: lineItem.line_total.formatted_with_symbol,
}));
// Signature is verified, continue to send data to SendGrid
// Create the email object payload to fire off to SendGrid
const emailPayload = {
to: data.payload.customer.email,
from: merchant.support_email,
subject: `Thank you for your order ${data.payload.customer.firstname}`,
text: `Your order number is ${data.payload.customer_reference}`,
// SendGrid expects a JSON blog containing the dynamic order data your template will use
// More information below in 'What's next?' on how to configure your dynamic template in SendGrid
// The property key names will depend on what dynamic template you create in SendGrid
dynamic_template_data: {
total: data.payload.order.subtotal.formatted_with_symbol,
items: orderLineItems,
receipt: true,
name: data.payload.shipping.name,
address01: data.payload.shipping.street,
city: data.payload.shipping.town_city,
state: data.payload.shipping.county_state,
zip : data.payload.shipping.postal_zip_code,
orderId : data.payload.id,
},
// In addition to specifying the dynamic template data, you need to specify the template ID. This comes from your SendGrid dashboard when you create you dynamic template
// https://mc.sendgrid.com/dynamic-templates
template_id: 'd-xxx'
}
let sgheaders = {
Authorization: `Bearer ${process.env.SENDGRID_SECRET}`,
"Content-Type": "application/json",
};
let response = {};
try {
// Call the SendGrid send mail endpoint
response = await sgMailClient.send(emailPayload);
return {
statusCode: 200,
headers: sgheaders,
body: 'Email sent!'
}
} catch (err) {
console.error('Error', err)
}
// Return the response from the request
return res.status(200).json(response);
}
Need some advice on how to get this code to actually work, the tutorial seems to be uncompleted or I might have misunderstood some curtail details.
UPDATE (Working code below)
Had to use axios instead of node.fetch (thanks #hotcakedev) wehn deplodey on netlify. Also other changes to the code in order to make it work with commerce.js (see working code for detalis)
const axios = require('axios');
const sgMailClient = require("#sendgrid/mail");
sgMailClient.setApiKey(process.env.SENDGRID_API_KEY);
// Includes crypto module
const crypto = require('crypto');
// Create the API endpoint function with a req and res parameter
exports.handler = async function(req, res) {
//export default async function handler(req, res) {
if (!req.body || req.httpMethod !== 'POST') {
return {
status: 405,
headers: {},
body: JSON.stringify({
status: 'Invalid HTTP method',
}),
}
}
const data = JSON.parse(req.body);
// Request for your merchant information so that you can use your email
// to include as the 'from' property to send to the SendGrid API
const merchant = axios.get(`${process.env.CHEC_API_URL}/v1/merchants`, {
headers: {
'X-Authorization': process.env.CHEC_SECRET_KEY,
"Accept": "application/json",
"Content-Type": "application/json",
},
}).then((response) => response.json);
//console.log(data);
// Extract the signature from the registered `orders.create` webhook
const { signature } = data;
delete data.signature;
// Your Chec webhook signing key, from the Chec Dashboard webhooks view
const webHookSigningKey = 'KEJlxz6cIlrWIpsX5jypcMeGl2uh7jJg';
// Verify the signature
const expectedSignature = crypto.createHmac('sha256', webHookSigningKey)
.update(JSON.stringify(data))
.digest('hex');
if (expectedSignature !== signature) {
console.error('Signature mismatched, skipping.')
}
// Verify the age of the request to make sure it isn't more than 5 minutes old.
if (new Date(data.created * 1000) < new Date() - 5 * 60 * 1000) {
console.error('Webhook was sent too long ago, could potentially be fake, ignoring');
}
// Because you will need to list out the order line items, map through the returned line items
// and structure out the data you need to display in the email receipt for your customer
// Note that we are keeping the data structure minimal here
const orderLineItems = data.payload.order.line_items.map((lineItem) => ({
text: lineItem.product_name,
price: lineItem.line_total.formatted_with_symbol,
}));
// Signature is verified, continue to send data to SendGrid
// Create the email object payload to fire off to SendGrid
const emailPayload = {
to: data.payload.customer.email,
from: data.payload.merchant.support_email,
subject: `Thank you for your order ${data.payload.customer.firstname}`,
text: `Your order number is ${data.payload.customer_reference}`,
// SendGrid expects a JSON blog containing the dynamic order data your template will use
// More information below in 'What's next?' on how to configure your dynamic template in SendGrid
// The property key names will depend on what dynamic template you create in SendGrid
dynamic_template_data: {
total: data.payload.order.subtotal.formatted_with_symbol,
items: orderLineItems,
receipt: true,
name: data.payload.billing.name,
address01: data.payload.billing.street,
city: data.payload.billing.town_city,
state: data.payload.billing.county_state,
zip : data.payload.billing.postal_zip_code,
orderId : data.payload.id,
},
// In addition to specifying the dynamic template data, you need to specify the template ID. This comes from your SendGrid dashboard when you create you dynamic template
// https://mc.sendgrid.com/dynamic-templates
template_id: 'd-xxx'
};
/*let sgheaders = {
Authorization: `Bearer ${process.env.SENDGRID_SECRET}`,
"Content-Type": "application/json",
};*/
let response = {};
try {
// Call the SendGrid send mail endpoint
response = await sgMailClient.send(emailPayload);
return {
statusCode: 200,
headers: {},
body: JSON.stringify({
status: 'Email sent!',
}),
}
} catch (err) {
console.error('Error from function: ', err)
console.error(err.response.body);
console.log("Payload content: ", emailPayload );
}
// Return the response from the request
//return res.status(200).json(response);
}
If you want to integrate sendgrid using Rest API, I suggest you to use axios.
So in your case,
import axios from 'axios';
...
const merchant = axios.get(`${process.env.CHEC_API_URL}/v1/merchants`, {
headers: {
'X-Authoriza†ion': process.env.CHEC_SECRET_KEY,
},
}).then((response) => response.json);
...
Twilio SendGrid developer evangelist here.
You have installed node-fetch to the project, but the tutorial did not include requiring the library into the function to use it. So you need to require node-fetch.
The tutorial also fails to require the SendGrid library and set the API key. You should set your SendGrid API key in the environment in Netlify, called something like SENDGRID_API_KEY. Then add the following to the top of your function:
const fetch = require("node-fetch");
const sgMailClient = require("#sendgrid/mail");
sgMailClient.setApiKey(process.env.SENDGRID_API_KEY);
As for the headers that you are asking about, they are the response headers that your function returns in response to the incoming HTTP request. What you return depends on how you are calling this function, but you should not return your SendGrid API Key in the headers.
While you are getting the function working, I would recommend setting the return headers to an empty object until you work out what Content-Type you want to set (the response body is "Email sent!" right now, which would be text/plain but that's not super useful in the front end) and what other headers you may or may not need.
Related
I am trying to associate my Alexa skill with a catalog that I created by hitting the https://api.amazon.com/v0/catalogs endpoint with an Auth token that I had generated through a LWA profile.
This worked, and I created a catalog like so:
{
associatedSkillIds: [],
createdDate: '2022-01-22T20:50:37.318Z',
id: 'amzn1.ask-catalog.cat.[REDACTED]',
lastUpdatedDate: '2022-01-22T20:50:37.318Z',
title: 'TestCatalog',
type: 'AMAZON.AudioRecording',
usage: 'AlexaTest.Catalog.AudioRecording'
}
However, the next step, associating my Alexa skill with the Catalog is always returning 401 https://developer.amazon.com/en-US/docs/alexa/smapi/catalog-content-upload.html#associate-catalog-with-skill
This is my function to attempt to associate the skill with the catalog:
async associateSkillWithCatalog() {
console.log(`Associating skill...`);
const accessToken = await this.getRefreshToken(); // makes post to https://api.amazon.com/auth/o2/token
console.log(this.alexaEndpoint + this.skillAssoc(cat.id, skillId));
const response = await axios.put(
"https://api.amazonalexa.com/v0/skills/amzn1.ask.skill.[REDACTED]/catalogs/amzn1.ask-catalog.cat.[REDACTED]",
{
headers: {
'Content-type': 'application/json',
'Authorization': `Bearer ${accessToken}`
}
}
);
return response.data;
}
Always receiving back this Error: Request failed with status code 401\n at createError.
Why would I be receiving 401 Error here, despite other requests against this API not failing?
Thanks!
I'm using axios to upload an audio file to AWS s3 bucket.
The workflow is: React => AWS API Gateway => Lambda.
Here is the backend Lambda code where generates the S3 presigned URL:
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(AUDIO_S3_BUCKET)
.key(objectKey)
.contentType("audio/mpeg")
.build();
PutObjectPresignRequest putObjectPresignRequest = PutObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(10))
.putObjectRequest(putObjectRequest)
.build();
PresignedPutObjectRequest presignedPutObjectRequest = s3Presigner.presignPutObject(putObjectPresignRequest);
AwsProxyResponse awsProxyResponse = new AwsProxyResponse();
awsProxyResponse.setStatusCode(HttpStatus.SC_OK);
awsProxyResponse.setBody(
GetS3PresignedUrlResponse.builder()
.s3PresignedUrl(presignedPutObjectRequest.url().toString())
.build().toString());
return awsProxyResponse;
Here is the java code to create the bucket:
private void setBucketCorsSettings(#NonNull final String bucketName) {
s3Client.putBucketCors(PutBucketCorsRequest.builder()
.bucket(bucketName)
.corsConfiguration(CORSConfiguration.builder()
.corsRules(CORSRule.builder()
.allowedHeaders("*")
.allowedMethods("GET", "PUT", "POST")
.allowedOrigins("*") // TODO: Replace with domain name
.exposeHeaders("ETag")
.maxAgeSeconds(3600)
.build())
.build())
.build());
log.info("Set bucket CORS settings successfully for bucketName={}.", bucketName);
}
In my frontend, here is the part that try to upload file:
const uploadFile = (s3PresignedUrl: string, file: File) => {
let formData = new FormData();
formData.append("file", file);
formData.append('Content-Type', file.type);
const config = {
headers: {
"Content-Type": 'multipart/form-data; boundary=---daba-boundary---'
//"Content-Type": file.type,
},
onUploadProgress: (progressEvent: { loaded: any; total: any; }) => {
const { loaded, total } = progressEvent;
let percent = Math.floor((loaded * 100) / total);
if (percent < 100) {
setUploadPercentage(percent);
}
},
cancelToken: new axios.CancelToken(
cancel => (cancelFileUpload.current = cancel)
)
};
axios(
{
method: 'post',
url: s3PresignedUrl,
data: formData,
headers: {
"Content-Type": 'multipart/form-data; boundary=---daba-boundary---'
}
}
)
.then(res => {
console.log(res);
setUploadPercentage(100);
setTimeout(() => {
setUploadPercentage(0);
}, 1000);
})
.catch(err => {
console.log(err);
if (axios.isCancel(err)) {
alert(err.message);
}
setUploadPercentage(0);
});
};
However, when try to upload the file, it return 403 error.
And if I use fetch instead of axios instead and it works, like this:
export async function putToS3(presignedUrl: string, fileObject: any) {
const requestOptions = {
method: "PUT",
headers: {
"Content-Type": fileObject.type,
},
body: fileObject,
};
//console.log(presignedUrl);
const response = await fetch(presignedUrl, requestOptions);
//console.log(response);
return await response;
}
putToS3(getPresignedUrlResponse['s3PresignedUrl'], values.selectdFile).then(
(putToS3Response) => {
console.log(putToS3Response);
Toast("Success!!", "File has been uploaded.", "success");
}
);
It seems to me that the only difference between these two is that: when using fetch the request's Content-Type header is Content-Type: audio/mpeg, but when using axios it is Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryClLJS3r5Xetv3rN7 .
How can I make it work with axios? I'm switching to axios for its ability to monitor request progress as I want to show an upload progress bar.
I followed this blog and not sure what I missed: https://bobbyhadz.com/blog/aws-s3-presigned-url-react
You are using POST in your axios. Should be PUT instead.
Also I think the content type has to match the one specified during requesting the pre-signed URL, which is audio/mpeg as you rightly pointed out.
Correspondingly, your data should be just file, instead of formData.
axios(
{
method: 'put',
url: s3PresignedUrl,
data: file,
headers: {
"Content-Type": 'audio/mpeg'
}
}
...
You didn't mark any answers as accepted so I guess you didn't solve it.
For any future viewers out there. The reason why you are getting 403 forbidden error is because your Content-Type in your server and client side are not matching. I'm assuming you set up the AWS policies correctly.
Your code in the backend should look like this:
const presignedPUTURL = s3.getSignedUrl("putObject", {
Bucket: "bucket-name",
Key: String(Date.now()),
Expires: 100,
ContentType: "image/png", // important
});
and in the front-end (assuming you are using axios):
const file = e.target.files[0]
const result = await axios.put(url, file, {
withCredentials: true,
headers: { "Content-Type": "image/png" },
});
In practical, you would normally have to send the file type to generate the pre-signed url in the POST body or whatever and then in axios you do file.type to get the file type of the uploaded file.
Check your Lambda execution role. It may be the culprit. Perhaps it does not grant enough permissions to allow PUTting files into your bucket.
URL signing is a delegation of power on behalf of the signer, which is restricted to a specified object, action... Signing does not magically grants full read/write permissions on S3, even on the specific object related to the presigned URL.
The "user" who generates the signature requires sufficient permissions to allow the actions you want to delegate through that presigned URL. In this case, this is the execution role of your Lambda function.
You can add the AmazonS3FullAccess managed policy to the execution role and see if it solves your situation. This change took me out of a blocked situation me after days of struggle. Afterwards, before going to production, restrict that rule to the specific bucket you want to allow uploads into (least privilege principle).
If you develop using SAM local emulation, those execution roles seem not to be taken into account as long as you run your functions locally; the signed links work in that context even without S3 permissions.
I need to post a request using AXIOS. The request body is in XML format. I am able to POST a request using static data in XML body using AXIOS but wanted to pass the value dynamically.
Can you please let me know how can i add dynamic value(TripName,TotalFare etc.) in xml body?
requestBody='<Itinerary xmlns="http://www.testsol.com/api/travel/trip/2010/06">\
<TripName>SFO Trip- air and hotel </TripName>\
<Comments />\
<StartDateLocal>2020-05-10T07:25:00</StartDateLocal>\
<EndDateLocal>2020-05-14T23:59:00</EndDateLocal>\
<Bookings>\
<Booking>\
<AirlineTickets>\
<AirlineTicket>\
<DateCreatedUtc>2020-05-11T07:34:13</DateCreatedUtc>\
<DateModifiedUtc>2020-05-13T10:52:27</DateModifiedUtc>\
<IssueDateTime>2020-05-11T00:34:13</IssueDateTime>\
<TotalFare>3948.0000</TotalFare>\
<TotalFareCurrency>INR</TotalFareCurrency>\
<AirlineTicketCoupons>\
<AirlineTicketCoupon>\
<EndCityCode>DEL</EndCityCode>\
<FlightNumber>198</FlightNumber>\
<StartCityCode>BLR</StartCityCode>\
<StartDateLocal>2020-03-19T20:30:00</StartDateLocal>\
<Vendor>SG</Vendor>\
</AirlineTicketCoupon>\
</AirlineTicketCoupons>\
</AirlineTicket>\
</AirlineTickets>\
</Passengers>\
<PassengerCount>1</PassengerCount>\
</Booking>\
</Bookings>\
</Itinerary>';
const config_req = {
headers: {
// Accept: "application/json",
Authorization: "Bearer " + token,
},
};
CODE SNIPPET
axios
.post("https://test.com/api/travel/trip", requestBody, config_req)
.then((result) => {
console.log("create Itin API" + result.data);
})
.catch((error) => {
console.log(error);
console.log(error.data);
});
You can use template strings for your request body.
For example
const startDateLocal = ...
const endDateTotal = ...
const dateCreated = ...
...
requestBody=`<Itinerary xmlns="http://www.testsol.com/api/travel/trip/2010/06">\
<StartDateLocal>${startDateLocal}</StartDateLocal>\
<EndDateLocal>${endDateTotal}</EndDateLocal>\
<Bookings>\
<Booking>\
<AirlineTickets>\
<AirlineTicket>\
<DateCreatedUtc>${dateCreated}</DateCreatedUtc>\
<DateModifiedUtc>${dateModified}</DateModifiedUtc>\
<IssueDateTime>${issueDate}</IssueDateTime>\
<TotalFare>${totalFare}</TotalFare>\
<TotalFareCurrency>${currency}</TotalFareCurrency>\
</AirlineTicket>\
</AirlineTickets>\
</Passengers>\
<PassengerCount>${passengerCount}</PassengerCount>\
</Booking>\
</Bookings>\
</Itinerary>`;
Assuming you have all the variables defined
I have a Lambda function that handles reading data from a file(stored inside S3 bucket) as well as inserting data to a Dynamodb table. This Lambda function is exposed as a REST endpoint using API gateway. The function accepts GET request as well as POST request. I'm making GET/POST requests from my REACT project using axios and aws4(for signing) libraries. GET request is to read data from a file stored inside S3 and it works just fine. And POST request is for inserting data into Dynamodb table. However, it doesn't work and AWS returns InvalidSignatureException error as a respond. This is an excerpt of my code :
createAWSSignedRequest(postData) {
let request = {};
if (postData) {
request = {
host: process.env.AWS_HOST,
method: 'POST',
url: process.env.AWS_URL,
path: process.env.AWS_PATH,
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(postData)
}
} else {
request = {
host: process.env.AWS_HOST,
method: 'GET',
url: process.env.AWS_URL,
path: process.env.AWS_PATH
}
}
let signedRequest = aws4.sign(request, {
secretAccessKey: process.env.AWS_SECRET_KEY,
accessKeyId: process.env.AWS_ACCESS_KEY
});
return signedRequest;
}
This is how GET request is made :
let signedRequest = this.createAWSSignedRequest('GET');
axios(signedRequest)
.then(response => {
})
.catch((error) => {
console.log("error",error);
});
This is how POST request is made :
const data = {
uuid: "916b7d90-0137-11e8-94e6-116965754e23", //just a mock value
date : "22/jan/2018",
user_response: [
{
question:"this is quesiton1",
choice:"user selected A"
},
{
question:"this is quesiton2",
choice: "user selected b"
},
{
question:"this is quesiton3",
choice: "user selected C"
}
]
};
let signedRequest = this.createAWSSignedRequest(data);
axios(signedRequest)
.then(response => {
......
})
.catch((error) => {
console.log("error",error);
});
As you can see, the code for both GET and POST requests are exactly the same (except payload and method type). I'm singing with the same secret access key and access key id for both requests. I'm not sure why one request results in "InvalidSignatureException" when the other doesn't. Can anyone shed a light on this issue for me.
Thanks
After having discussion with AWS4 lib developer, I figured out what I did wrong. AWS4 uses "body" as a payload attribute to compute signature. However, Axios uses "data" attribute as payload. My mistake was only setting either one of them. So when I set just "data" attribute, the payload was present in the request and content-length is computed correctly. However, the signature was incorrect since the payload was not taken into consideration when computing signature. When I set just "body", payload was not present in the request because Axios does not use "body" attribute for payload. The solution is to set both attributes with payload. I hope this helps to anyone who are having the same issue I have.
If you use the AWS Amplify library it has a module called API which should fit your use cases, and it will perform Sigv4 signing for you either with authenticated or unauthenticated roles. The Auth category uses Cognito as the default implementation. For instance:
npm install aws-amplify --save
Then import and configure the lib:
import Amplify, { API } from 'aws-amplify';
Amplify.configure({
Auth: {
identityPoolId: 'XX-XXXX-X:XXXXXXXX-XXXX-1234-abcd-1234567890ab',
region: 'XX-XXXX-X'
},
API: {
endpoints: [
{
name: "APIName",
endpoint: "https://invokeURI.amazonaws.com"
}
]
}
});
Then for your API Gateway endpoint calling a Lambda:
let apiName = 'MyApiName';
let path = '/path';
let options = {
headers: {...} // OPTIONAL
}
API.get(apiName, path, options).then(response => {
// Add your code here
});
More info here: https://github.com/aws/aws-amplify
I have tried implementing the superagent way of uploading multiple files in axios. But somehow, I'm getting an error in console
Failed to load https://api.cloudinary.com/v1_1/xxxx/image/upload:
Request header field Authorization is not allowed by
Access-Control-Allow-Headers in preflight response.
My upload handler looks like this
uploadFile(){
const uploaders = this.state.filesToBeSent.map(file => {
const formData = new FormData();
formData.append("file", file);
formData.append("upload_preset", "xxxxx");
formData.append("api_key", "xxxxx");
formData.append("timestamp", (Date.now() / 1000) | 0);
return axios.post(url, formData, {
headers: { "X-Requested-With": "XMLHttpRequest" },
}).then(response => {
const data = response.data;
const fileURL = data.secure_url
console.log(data);
})
});
// Once all the files are uploaded
axios.all(uploaders).then(() => {
// ... perform after upload is successful operation
console.log("upload completed ", uploaders);
});
}
I have got this example from here
Another thing is confusing to me. In superagent we can attach parameters to the request field which includes API Secret Key of Cloudinary like this:
const paramsStr = 'timestamp='+timestamp+'&upload_preset='+uploadPreset+secretKey;
const signature = sha1(paramsStr);
const params = {
'api_key': 'xxxx',
'timestamp': timestamp,
'upload_preset': uploadPreset,
'signature': signature
}
Object.keys(params).forEach(key => {
uploadRequest.field(key, params[key])
});
But in that example, it is not mentioned how to append the secret key and other params to axios.
You will need to generate the signature on your backend, and then perform the upload with the generated signature.
You can generate a signature via the following instructions- https://support.cloudinary.com/hc/en-us/articles/203817991-How-to-generate-a-Cloudinary-signature-on-my-own-
You can also take a look at the following example on how to append the signature to your request. It's in PHP, however, the guidelines still apply.
https://gist.github.com/taragano/a000965b1514befbaa03a24e32efdfe5