"error - unhandledRejection: Error: listen EADDRINUSE: address already in use :::3000" when I use Slack Bolt for JavaScript with ngrok and Next.js - next.js

Background
We are developing a Slack Bot. This time we are using Bolt for JavaScript (Node.js) provided by Slack, React, Next.js, and ngrok. Here is what each of them does.
Bolt for JavaScript: I don't want to use Slack's bare-bones API, but want to benefit from the function that wraps it.
React: Needed to use Next.js
Next.js: Slack needs a request URL to notify my bot app when events such as mentions occur in Slack, but Next.js makes it easy to create an API endpoint to be set to that URL (e.g. /api/something)
ngrok: In the local development environment, that URL will be http://localhost:3000, so the protocol will be http instead of https. Slack does not allow this, so we need a URL that starts with https that tunnels to the local http://localhost:3000. ngrok provides that easily!
Problem to be solved.
I have already confirmed that if I type #xxxx in a certain workspace in Slack, the event is notified to https://xxxx.jp.ngrok.io/api/slack/events. However, in this API file
app.event("app_mention", async ({ event, say }) => {
.
.
.
}
is not invoked and the following error occurs
error - unhandledRejection: Error: listen EADDRINUSE: address already in use :::3000
I would like to know why and how to resolve this.
Source code
/api/slack/events.ts
import type { NextApiRequest, NextApiResponse } from "next";
require("dotenv").config();
import app from "../../../config/slackAuth";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// Unique case for Slack challenge
if (req.body.challenge) return res.status(200).json(req.body);
// Subscribe to 'app_mention' event in your App config
// See https://api.slack.com/tutorials/tracks/responding-to-app-mentions
app.event("app_mention", async ({ event, say }) => {
try {
// Response to the message in the thread where the event was triggered with #${message.user}
// See https://slack.dev/bolt-js/concepts#message-sending
await say({
text: `Hi <#${event.user}>!`,
thread_ts: event.ts,
});
} catch (error) {
await say({
text: `<#${event.user}> ${error.message}.`, // #userName Request failed with status code 429.
thread_ts: event.ts,
});
}
});
(async () => {
// Start this app
await app.start(process.env.PORT || 3000);
console.log("⚡️ Bolt app is running!");
})();
return res.status(404).json({ message: "Unknown event type" });
}
Error code
error - unhandledRejection: Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _listen2] (net.js:1331:16)
at listenInCluster (net.js:1379:12)
at Server.listen (net.js:1465:7)
at C:\Users\81906\Documents\slackGpt3\node_modules\#slack\bolt\dist\receivers\HTTPReceiver.js:176:25
at new Promise (<anonymous>)
at HTTPReceiver.start (C:\Users\81906\Documents\slackGpt3\node_modules\#slack\bolt\dist\receivers\HTTPReceiver.js:142:16)
at App.start (C:\Users\81906\Documents\slackGpt3\node_modules\#slack\bolt\dist\App.js:241:30)
at eval (webpack-internal:///(api)/./pages/api/slack/events.ts:69:69)
at handler (webpack-internal:///(api)/./pages/api/slack/events.ts:71:7)
at Object.apiResolver (C:\Users\81906\Documents\slackGpt3\node_modules\next\dist\server\api-utils\node.js:363:15) {
code: 'EADDRINUSE',
errno: -4091,
syscall: 'listen',
address: '::',
port: 3000
}

Issue
Using Slack Bolt for JavaScript with Next.js is not straightforward due to the following reasons:
Running npm run dev in a Next.js project starts a server at localhost:3000.
Running app.start() in Slack Bolt for JavaScript starts a server using Express.js, which also tries to use localhost:3000.
This causes an error because two servers are trying to use the same port.
This information was provided by someone at Slack, and the source can be found at https://github.com/slackapi/bolt-js/issues/1687.
Solution
You can change the port number used by Bolt to, for example, 3001.
However, this will make it difficult for the two servers at localhost:3000 and 3001 to communicate with each other.
The request URL registered in the Slack Bolt for JavaScript console is set to 3000, so events received there will not be able to flow to port 3001.

Related

Unable to start Next 13 app directory (beta) in production mode

Step 1: Automatically create a new Next.js project using the new beta app directory:
npx create-next-app#latest --experimental-app
pages/api/hello.ts
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'
type Data = {
name: string
}
export default function handler(
req: NextApiRequest,
res: NextApiResponse<Data>
) {
res.status(200).json({ name: 'John Doe' })
}
This file is identical to the one created automatically created by npx - there are no modifications.
I am trying to build a simple home page, which fetches data from the api which gets data from my database. Either way an await/async will be required. I am following the instructions from here.
In this example I will demonstrate that even awaiting the supplied hello api can't seem to run in production, and I can't work out why.
app/page.tsx
async function getHelloAsync() {
const res = await fetch('http://localhost:3000/api/hello', { cache: 'no-store' });
// The return value is *not* serialized
// You can return Date, Map, Set, etc.
// Recommendation: handle errors
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error('Failed to fetch data');
}
return res.json();
}
export default async function Page() {
const hello = await getHelloAsync();
return (
<main>
<h1>Hello: {hello.name}</h1>
</main>
)
}
To test the hello api works, I confirm that running pn run dev and then curl http://localhost:3000/api/hello the following successful response is received:
{"name":"John Doe"}
Next up we exit the dev server and run:
pn run build
The first headache is that the build will completely fail to build unless one adds { cache: 'no-store' } to the fetch command:
const res = await fetch('http://localhost:3000/api/hello', { cache: 'no-store' });
or adds this to the top of app/page.tsx:
export const fetchCache = 'force-no-store';
I am actually not sure how one would even build this if you wanted to cache the response or use revalidate instead and provide an initial optimistic response, because without cache: no-store it refuses to build outright. Ideally instead it should just cache the result from /api/hello and not fail. Running the dev server at the same idea as doing the build does allow the build to work, but then as soon as you exit the dev server and run pn run start then all the api calls fail anyway. So that is not a good idea.
This leads us to the next problem - why are the api calls not working in production (i.e. when calling pn run start).
Step 2:
pn run build
pn run start
Confirm that the following still works and yes it does:
curl http://localhost:3000/api/hello
Result:
{"name":"John Doe"}
Now we visit http://localhost:3000 in a browser but, surprise! We get the following error:
> next start
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
warn - You have enabled experimental feature (appDir) in next.config.js.
warn - Experimental features are not covered by semver, and may cause unexpected or broken application behavior. Use at your own risk.
info - Thank you for testing `appDir` please leave your feedback at https://nextjs.link/app-feedback
(node:787) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
TypeError: fetch failed
at Object.fetch (node:internal/deps/undici/undici:11118:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async getHelloAsync (/Users/username/nextjstest/.next/server/app/page.js:229:17)
at async Page (/Users/username/nextjstest/.next/server/app/page.js:242:19) {
cause: Error: connect ECONNREFUSED ::1:3000
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1300:16)
at TCPConnectWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
errno: -61,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '::1',
port: 3000
}
}
[Error: An error occurred in the Server Components render. The specific message is omitted in production builds to avoid leaking sensitive details. A digest property is included on this error instance which may provide additional details about the nature of the error.] {
digest: '3567993178'
}
Why is it saying that the connection is refused when we know the API is available? I can't get this to run at all. I know this is beta but surely the code should actually run right? How do I make this code work?
Also if anyone knows where where the logs are that I'm supposed to be accessing to see digest '3567993178' please let me know.

Why do I get a "502 Gateway" error from NextJs app hosted on Firebase for POST requests only?

I started to build an API using NextJs framework. I want it to be hosted on Firebase (Hosting and Functions). Everything is working as long as I send only GET requests. When I send a POST request I receive a "502 Bad Gateway" error.
It's very simple to reproduce. You just have to download and deploy the example provided by the team developing NextJs.
create a new project on Firebase console
install the "with Firebase hosting" example
change the project name in the .firebaserc (line 3) file
create a folder "api" under the folder "pages"
create a file "hello.js" under the folder "api" and add the following snippet
export default async (req, res) => {
const {
body,
method
} = req;
console.log("method :>> ", method);
console.log("body :>> ", body);
switch (method) {
case "POST":
res.status(200).end(`Method ${method} supported!`);
break;
default:
res.setHeader("Allow", ["POST"]);
res.status(405).end(`Method ${method} Not Allowed`);
}
};
deploy the app
send a GET request to "https://[project-name].web.app/api/hello" and see it works
send a POST request to "https://[project-name].web.app/api/hello" and see it does not work
Do you have a the same error as me?
I spent 2 days to read articles, watch videos and try different configurations. You can even update the firebaseFunctions to add a console.log and see the POST request is caught by the Firebase Cloud Function but the NextJs server does not pass it to our API like it does for a GET request. It's out of my skills range...
Below the output you should have. The POST request should be answered with 200 - Method POST is supported!.
This was a real pain to track down, but after poking around myself for a while, I found that the same issue crops up for PUT and PATCH requests. Which suggested that it had something to do with the body of the request. Annoyingly, after finding that out, I stumbled across the thread of Issue #7960, where they found the same problem.
Simply put, the body of the request processed once by https.onRequest() and then nextjsHandle() tries to parse it again. Because the body was handled already, the raw-body module (within nextjsHandle()) waits indefinitely for 'data' events that will never come.
Currently, there isn't a way to turn off the body parsing done by https.onRequest(), so it must be disabled on the next.js end. Unfortunately, there isn't a global off switch for body parsing that can be added in next.config.js and it must be done for each and every API route (the files in pages/api) (which may change if the proposed fix in PR #16169 is added).
To disable body parsing for a given route, you add the following to the route's file
export const config = {
api: {
// disables call to body parsing module
bodyParser: false,
}
};
However, as mentioned in Issue #7960 by #rscotten, you might also want to use next dev while developing your app, so you need to enable it while using next dev but disable it while deployed. This can be done using
export const config = {
api: {
// disables call to body parsing module while deployed
bodyParser: process.env.NODE_ENV !== 'production',
}
};
Applying these changes to hello.js gives:
export default async (req, res) => {
const {
body,
method
} = req;
console.log("method :>> ", method);
console.log("body :>> ", body);
switch (method) {
case "POST":
res.status(200).end(`Method ${method} supported!`);
break;
default:
res.setHeader("Allow", ["POST"]);
res.status(405).end(`Method ${method} Not Allowed`);
}
};
export const config = {
api: {
// disable nextjs's body parser while deployed
// (as body parsing is handled by `https.onRequest()`),
// but enable it for local development using `next dev`
bodyParser: process.env.NODE_ENV !== 'production',
}
};

CORS error when making Axios calls to Cloudrun service from Firebase hosted app

This looks to be pretty obvious but I've been trying to figure it out for a couple of days now and still can't get it to work. Please, can someone point out what I'm missing.
I'm trying to make an axios call to my cloud run service from my firebase hosted SPA. To isolate the issue I followed the steps outlined in the [firebase tutorial for cloud run] (https://firebase.google.com/docs/hosting/cloud-run#node.js)
Step 4 of the tutorial talks about setting up rewrite to use the firebase domain as a proxy to your cloud run service. It says that the helloworld service would be reachable via
Your Firebase subdomains:
projectID.web.app/helloworld and projectID.firebaseapp.com/helloworld
So I follow the steps and deploy the service. Then I try to make an axios call to the service from the SPA like below
testHelloWorld () {
axios.get(`https://myProjectId.firebaseapp.com/helloworld`)
.then((data) => {
console.log(data)
})
.catch(ex => {
console.error(ex)
})
})
}
But then I get the CORS error
Access to XMLHttpRequest at 'https://myProjectId.firebaseapp.com/helloworld' from origin 'https://myFirabaseApp.firebaseapp.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
This answer states that this should be possible so I'm not sure what I'm doing wrong.
N.B:
While debugging, I updated the node app from the tutorial to add cors support like below. Still didnt work.
const express = require('express');
const app = express();
const cors = require('cors'); //Imported and added this
app.use(cors()); // Used here
app.get('/', (req, res) => {
console.log('Hello world received a request.');
const target = process.env.TARGET || 'World';
res.send(`Hello ${target}!`);
});
const port = process.env.PORT || 8080;
app.listen(port, () => {
console.log('Hello world listening on port', port);
});
So the question here is, how do I make an Axios/AJAX call to my cloud run service using the firebase rewrite rule?
Please check if you have installed cors: npm install cors.
Please check if the 2 following options can solve your issue:
1= Use the following code :
app.use(cors({origin:true,credentials: true}));
2) If the previous didn't work, please use the following code:
app.get('/', (req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
console.log('Hello world received a request.');
}
Please let me know if it works for you.

how to send a email in firebase https function through own smtp [duplicate]

This question already has answers here:
Cloud Functions for Firebase - getaddrinfo ENOTFOUND
(5 answers)
Closed 4 years ago.
i am facing with some strange (for me) behavior.
I am trying to create feedback form in my firebase app
Its a simple form which should send me a email when user submit it.
I created onCall function in my firebase app.
When i test it locally through firebase experimental:functions:shell its working and i receive a email, but deployed one always fails with:
{
code: "ECONNECTION"
command: "CONN"
errno: "ENOTFOUND"
}
the body of function:
function feedbacks(data)
{
let email = createEmail(data);
let transport = nodemailer.createTransport({
host: 'smtp.yandex.ru',
port: 465,
secure: true,
// tried this one, but without success too
//tls:{ secureProtocol: "TLSv1_method" },
auth: {
user: 'xxxx#xxxx',
pass: 'xxxx'
}
});
return new Promise((resolve, reject) => {
transport.sendMail(email, err => {
if (err == null) {
resolve(true);
} else {
reject(new functions.https.HttpsError('internal', 'failed', err))
}
});
});
}
It seems that deployed function just can not access smtp server due to some firebase restrictions i don't know.
EDIT:
i tested deployed function with gmail smtp and my gmail credentials which i use in firebase too and it works.
The docs says, that i CAN use custom smtp while its port not 25.
If someone can shed light on that i would appreciate it.
You need to be on the "Flame" or "Blaze" pricing plan.
As a matter of fact, the free "Spark" plan "allows outbound network requests only to Google-owned services". See https://firebase.google.com/pricing/ (hover your mouse on the question mark situated after the "Cloud Functions" title)
Since your SMTP server is not a Google-owned service, you need to switch to the "Flame" or "Blaze" plan.

SignalR: getting error: WebSocket closed

I am working on project of Ionic with angular and AspNet with SignalR that have chat module.
I use SignalR for Chat.It's working smoothly but some time i am getting error as per below screen shot and because of that it's get stop working at all.
I have hosted my service on IIS and creating proxy and communicating with client and server. Here is sample
(function () {
angular
.module('app')
.factory('SignalRFactory', SignalRFactory);
SignalRFactory.$inject = ['$rootScope', 'Hub', 'ionicToast'];
function SignalRFactory($rootScope, Hub, ionicToast) {
var signalRLocal = this;
var serverURL = 'https://serivcerURL.com/signalr';
//Hub setup
var hub = new Hub('CommunicationHub', {
rootPath: serverURL,
listeners: {
'send': function (data) {
console.log("send " + data);
}
},
errorHandler: function (error) {
//Here i am getting that websocket closed error
console.error(error);
}
});
signalRLocal.Connect = function (user) {
console.log("SignalR Connecting as :" + user.UserName);
hub.invoke('connect', user);
};
return signalRLocal;
}
})();
I have hosted service on IIS. I search for the solution and find something like this link
I also try with above link solution by using "long Polling" as per below
Hub.connection.start({ transport: 'longPolling' });
But i don't want to use "long Polling" at all.
So can someone help me to figure out this issue without use of 'long Polling'.
Can someone tell me what configuration i have to do at client side or at IIS level.
As we said in comments, SignalR client will try to reconnect after the connection is lost. Besides, many factors (such as physical network interruption, client browser failure, server offline etc) can cause the connection lost, this article explains some disconnection scenarios, you can refer to it and find the possible causes of the issue.
Besides, as I mentioned in comment, you can call the Start method from your Closed event handler (disconnected event handler on JavaScript clients) to start a new connection to make client automatically re-establish a connection after it has been lost.
edit:
The connection to ws://localhost:3156/signalr/signalr/connect?transport=webSoc‌​kets&clientProtocol=‌​1.5&connectionToken=‌​g8vpRv9ncVDjPIYB9UuE‌​pAAILEaOcTMTG9p46IA2‌​4 was interrupted while the page was loading.
Under "Client disconnection scenarios" section in the article, you can find:
In a browser client, the SignalR client code that maintains a SignalR connection runs in the JavaScript context of a web page. That's why the SignalR connection has to end when you navigate from one page to another, and that's why you have multiple connections with multiple connection IDs if you connect from multiple browser windows or tabs. When the user closes a browser window or tab, or navigates to a new page or refreshes the page, the SignalR connection immediately ends because SignalR client code handles that browser event for you and calls the Stop method.
I am trying same and my client is in vue.js. I have changed below in vue.config.js
module.exports = {
devServer: {
proxy: {
'/hub': {
target: 'https://localhost:5001',
changeOrigin: false,
secure: false,
headers: {
'x-forwarded-proto': 'http',
},
},
},
},
}
Previously i am trying 'x-forwarded-proto': 'https', when I changed to http its work.
https://localhost:5001 is my .net endpoint and http://localhost:8080 is my vue enpoint
Changed in Startup.csbelow,
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<CardsHub>("/hub/cardsHub");
});
Added below code in vue component
this.connection = new signalR.HubConnectionBuilder()
.withUrl('/hub/cardsHub')
.build()
In my case the culprit was the wrong version of Microsoft.AspNetCore.SignalR.Common
the default one installed was 5.0.3
but I was targeting 3.1.0
Downgrading to 3.1.12 fixed the issue with the connection.

Resources