Vercel not sending email - sendgrid

I use SendGrid and I know my SendGrid-API key is working by checking from a terminal.
I verified my email in SendGrid Sender Identity.
curl -i --request POST \
--url https://api.sendgrid.com/v3/mail/send \
--header 'Authorization: Bearer MY-API-KYE_HERE' \
--header 'Content-Type: application/json' \
--data '{"personalizations": [{"to": [{"email": "myname#gmail.com"}]}],"from": {"email": "connect#mywebsite.com"},"subject": "SendGrid Test!","content": [{"type": "text/plain", "value": "Howdy!"}]}'
HTTP/1.1 202 Accepted
Server: nginx
Date: Mon, 20 Dec 2021 04:42:08 GMT
Content-Length: 0
Connection: keep-alive
X-Message-Id: 9G5w8P8_SJWPwj1acrNRPQ
Access-Control-Allow-Origin: https://sendgrid.api-docs.io
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: Authorization, Content-Type, On-behalf-of, x-sg-elas-acl
Access-Control-Max-Age: 600
X-No-CORS-Reason: https://sendgrid.com/docs/Classroom/Basics/API/cors.html
Strict-Transport-Security: max-age=600; includeSubDomains
And I get an email.
It works on localhost as well.
But when I deploy to Vercel with the following code, it won't send any email.
import sgMail from '#sendgrid/mail'
import dotenv from 'dotenv'
dotenv.config()
const host = process.env['HOST_URL']
const email_from = process.env['EMAIL_FROM']
const SENDGRID_API_KEY = process.env['SENDGRID_API']
export const sendGridConfirmationEmail = async (name, email, confirmationCode) => {
await sgMail.setApiKey(SENDGRID_API_KEY)
const msg = {
to: email,
from: `${email_from}`,
subject: "Please confirm your account",
text: `Email Confirmation: Hello ${name}.
Please confirm your email by clicking on the following link.
Click here, ${host}/auth/confirm/${confirmationCode}`,
html:`<h1>Email Confirmation</h1>
<h2>Hello ${name}</h2>
<p>Please confirm your email by clicking on the following link.</p>
<a href=${host}/auth/confirm/${confirmationCode}> Click here</a>
</div>`,
}
await sgMail
.send(msg)
.then(() => {
console.log('Email sent')
})
.catch((error) => {
console.error(error)
})
}
I tried the following too, but not working either.
export const sendGridConfirmationEmail = async (name, email, confirmationCode) => {
await fetch(SENDGRID_API, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${SENDGRID_API_KEY}`
},
body: JSON.stringify({
personalizations: [
{
to: [
{
email
}
],
subject: 'Demo success :)'
}
],
from: {
email: email_from,
name: 'Test SendGrid'
},
content: [
{
type: 'text/html',
value: `Congratulations <b>${name}</b>, you just sent an email with sendGrid. ${confirmationCode}`
}
]
})
});
}
How can I send email from Vercel using SendGrid?

I solve the problem.
When I call sendGridConfirmationEmail, I need to use await.
export const post = async ({ body }) => {
...
if (mail_method === "sendgrid") {
await sendGridConfirmationEmail(
body.name,
body.email,
token
);
console.log('SendGrid registration is emailed.')
}
...

Related

axios POST get 400

This is driving me crazy!
Exactly the same POST request works fine in Insomina per screenshot below:
The only header Insomina has is: Content-Type: application/json.
Now, the same request in code (I even copied the code generated from Insomnia for axios) via axios in Typescript:
const saveReqConfig: AxiosRequestConfig = {
method: 'POST',
url: 'THE SAME URL USED IN Insomina',
timeout: 3000,
data: {
name: `TestName`,
uri: `TestURI`,
statusCode: '200',
simulatedLatency: '0',
contentType: "application/json",
tags: '',
response: 'testing...',
type: 'VA',
},
headers: {
'Content-Type': 'application/json',
}
}
const normalAxios = axios.create();
const test = await normalAxios.request(saveReqConfig);
Don't understand why I am getting AxiosError: Request failed with status code 400 from code but the same request works fine in Insomina.
I think you did not set the headers correctly or you may not have setup the .create() properly.
Something like this:
const instance = axios.create({
url: '/post',
baseURL: 'https://httpbin.org',
method: 'POST',
timeout: 1000,
headers: {
Content-Type: 'application/json' // <- set your headers
}
});
let res = await instance.request({ // <- pass the data here
data: { // This should be whatever you want to post to this url. I just copied what you had.
name: `TestName`,
uri: `TestURI`,
statusCode: '200',
simulatedLatency: '0',
tags: '',
response: 'testing...',
type: 'VA',
}
});
Are you sure you need to use the .create() factory? The normal post like this might suite your needs better?
const data= { title: 'Axios POST Request Example' };
const headers = {
Content-Type: 'application/json'
};
axios.post('url', data, { headers }).then(response => console.log(response.data.title);
Posting here in case it helps someone.
It turned out that I couldn't post the request programmatically is because of lack of a TLS certificate. I didn't know that Insomnia has the option to disable the TLS and that's why it works in Insomnia.
To disable TLS (Do NOT do this in production!) from node with axios, create an instance of axios with a https agent setting rejectedUnauthorized to false e.g.
const instance = axios.create({
httpsAgent: new https.Agent({
rejectedUnauthorized: false
})
});
Also, set the environment variable as:
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';

Can't override some HTTP headers with github.request() in actions/github-script workflow script

My workflow's script with action/github-script(v6) step:
const response = await github.request('POST https://example.com', {
headers: {
authorization: 'Bearer xxx',
accept: 'application/vnd.heroku+json; version=3', // I want header to be like this
'content-type': 'application/json'
},
// some other options, like request body...
});
console.log(response);
When the accept and other HTTP headers are automatically overriden with:
{
status: 400,
reponse: {}, // not important, body complains about incorrect Accept header
request: {
method: 'POST',
url: 'example.com',
headers: {
accept: 'application/vnd.github.-preview+json', // wtf?
authorization: 'token [REDACTED]', // wtf? it should start with "Bearer"
'content-type': 'application/json', // ok, as expected
'user-agent': 'actions/github-script octokit-core.js/3.5.1 Node.js/16.13.0 (linux; x64)' // ok, but I didn't set this...
},
// other stuff...
}
Now the question is what am I missing? Can I make truthly custom request using github.request() api like that?

NestJS FilesInterceptor does not parse files from Axios request

I have a controller that is using FilesInterceptor to process multipart/form-data uploads.
#Post('/upload/:serial')
#UseInterceptors(FilesInterceptor('files[]'))
uploadLogFiles(
#UploadedFiles() files: UploadLog[],
#Param('serial') serial: number,
#Req() request: Request
): LogUploadResponse {
const upLoadedfiles = this.logPersistenceService.persistFiles(
files,
serial
);
return { files: upLoadedfiles };
}
}
When I submit files via a request created with Postman the files are parsed out of the request successfully.
However, when I try to create a request with Nest using the Axios based HttpService and the Form-Data library I cannot get the files from the request.
const formData = new FormData();
formData .append('files[]', 'a,b,c', fileName);
this.httpService
.post<LogUploadResponse>(
`${this.restUrl}/api/logging/upload/${serial}`,
formData,
{
headers: formData.getHeaders()
}
)
I have verified that the controller is receiving the request but files is empty. I have piped formData to a WriteStream and the contents look good and the boundary also matches what is in the header.
----------------------------347967411467094575699495
Content-Disposition: form-data; name="files[]"; filename="a.log"
Content-Type: text/plain
a,b,c
----------------------------347967411467094575699495--
REQUEST Headers { accept: 'application/json, text/plain, */*',
'content-type':
'multipart/form-data; boundary=--------------------------347967411467094575699495',
referer: 'http://localhost/',
'user-agent':
'Mozilla/5.0 (win32) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/15.2.1',
'accept-language': 'en',
origin: 'http://localhost',
host: 'localhost:8081',
'accept-encoding': 'gzip, deflate',
'content-length': '17',
connection: 'keep-alive' }
Update
I am able to make it work if I use node http module directly rather than NestJS/Axios
Works
const form = new FormData();
for (const file of Object.keys(files)) {
form.append('files[]', files[file], file);
}
return new Promise<LogUploadResponse>((resolve, reject) => {
const req = request(
{
method: 'POST',
hostname: 'localhost',
port: 8081,
path: `/api/logging/upload/${serial}`,
headers: form.getHeaders()
},
res => {
res.on('error', r => {
reject(r.message);
});
res.on('data', r => {
console.log('**r', r.toString());
resolve(r.toString());
});
}
);
form.pipe(req);
Does not work
const form = new FormData();
for (const file of Object.keys(files)) {
form.append('files[]', files[file], file);
}
const req = this.httpService.request<LogUploadResponse>({
baseURL: 'http://localhost:8081',
url: `/api/logging/upload/${serial}`,
method: 'POST',
data: form,
headers: form.getHeaders()
});
return req
.pipe(
tap(resp => console.log('status', resp.status)),
map(resp => resp.data),
catchError(_err => of({ files: [] }))
)
.toPromise();
I took a look at Axios source for http.js in GitHub and it looks like it is doing a pipe on the stream data but I didn't dig too deeply.
Was never able to get the Axios version working and just implemented the node http version for this specific request in my application.

Remove X-Socket-Id from axios get request

I get a CORS error and most likely is from X-Socket-Id. I tried to remove X-Socket-Id from Request Headers, but is not working..
.... has been blocked by CORS policy: Request header field x-socket-id is not allowed by Access-Control-Allow-Headers in preflight response.
.get(this.$URL + "/search/", {
params: {
q: this.search,
},
headers: {
Authorization: "Basic " + btoa(this.$KEY+ ":"),
},
transformRequest: (data, headers) => {
delete headers.common["X-Socket-Id"];
return data;
},
})
I found out a solution. Create axios with default settings, and for my specific case I had to remove X-Requested-With.
X-Sockets-Id, was already removed in my request.
var instance = axios.create();
delete instance.defaults.headers.common["X-Requested-With"];
instance
.get(this.$URL+ "/search/", {
params: {
q: this.search_for_company,
},
headers: {
Authorization: "Basic " + btoa(this.$KEY+ ":"),
},
})

Axios + Ionic React + Sails: _csrf token 403 Forbidden

I am developing an API with Sails.js and an user App with Ionic-React. At page load I make an axios request to get the _csrf token. When I submit the data from a login form to sails I always get a 403 Forbidden response. I disabled csrf (config/security.js) in sails and then I could retrieve the response. I am sending the token in the header.
I am trying too to get the session cookie but its not working I think that might be why the server refuses the request.
Ionic App:
componentDidMount(this: this) {
axios.get('http://localhost:1337/api/v1/security/grant-csrf-token')
.then(response => {
const _csrf = response.data._csrf
this.setState({
form: {
...this.state.form,
_csrf: _csrf,
}})
});
}
OnSubmit:
const { emailAddress, password, _csrf } = this.state.form;
const config= {
data: {
"emailAddress": emailAddress,
"password": password,
},
headers: {
"x-csrf-token": _csrf
},
withCredentials: true,
jar:cookieJar,
};
axios.post('http://localhost:1337/api/v1/users/authenticate', null, config)
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
})};
On Chrome DevTools Network Response:
On Postman this same request works and I get a 200 with the user data, and the request does include the sails.sid cookie.
I do not want to disable csrf protection, that wouldn't be a solution. Is it the sails.sid cookie that I am missing?
I am using this,
axios({
method: 'post',
crossdomain: true,
url: apiFormUrl,
data: formData,
headers: {
"Content-Type": "multipart/form-data",
"Authorization": access_token
}
})
.then
and it works