diff --git a/Composer/packages/client/src/components/WebChat/WebChatPanel.tsx b/Composer/packages/client/src/components/WebChat/WebChatPanel.tsx index 6dc8adc948..bd914198bc 100644 --- a/Composer/packages/client/src/components/WebChat/WebChatPanel.tsx +++ b/Composer/packages/client/src/components/WebChat/WebChatPanel.tsx @@ -22,7 +22,7 @@ import { WebChatHeader } from './WebChatHeader'; import { WebChatComposer } from './WebChatComposer'; import { BotSecret, ChatData, RestartOption } from './types'; -const BASEPATH = process.env.PUBLIC_URL || 'http://localhost:3000/'; +const BASEPATH = process.env.PUBLIC_URL || `http://${location.hostname}:3000/`; // TODO: Refactor to include Webchat header component as a part of WebchatComposer to avoid this variable. const webChatHeaderHeight = '85px'; @@ -67,7 +67,7 @@ export const WebChatPanel: React.FC = ({ const conversationServerPort = await conversationService.setUpConversationServer(); try { // set up Web Chat traffic listener - webChatTrafficChannel.current = new WebSocket(`ws://localhost:${conversationServerPort}/ws/traffic`); + webChatTrafficChannel.current = new WebSocket(`ws://${location.hostname}:${conversationServerPort}/ws/traffic`); if (webChatTrafficChannel.current) { webChatTrafficChannel.current.onmessage = (event) => { const data: diff --git a/Composer/packages/client/src/components/WebChat/utils/conversationService.ts b/Composer/packages/client/src/components/WebChat/utils/conversationService.ts index e17cbda0e4..541695cbd3 100644 --- a/Composer/packages/client/src/components/WebChat/utils/conversationService.ts +++ b/Composer/packages/client/src/components/WebChat/utils/conversationService.ts @@ -80,7 +80,7 @@ export class ConversationService { secret, domain: `${this.directlineHostUrl}/v3/directline`, webSocket: true, - streamUrl: `ws://localhost:${this.restServerForWSPort}/ws/conversation/${conversationId}`, + streamUrl: `ws://${location.hostname}:${this.restServerForWSPort}/ws/conversation/${conversationId}`, }); return directLine; } diff --git a/Composer/packages/client/src/constants.tsx b/Composer/packages/client/src/constants.tsx index 9532120c5c..9634860dca 100644 --- a/Composer/packages/client/src/constants.tsx +++ b/Composer/packages/client/src/constants.tsx @@ -527,7 +527,7 @@ export const defaultTeamsManifest: TeamsManifest = { }; export const defaultBotPort = 3979; -export const defaultBotEndpoint = `http://localhost:${defaultBotPort}/api/messages`; +export const defaultBotEndpoint = `http://${location.hostname}:${defaultBotPort}/api/messages`; const DAYS_IN_MS = 1000 * 60 * 60 * 24; export const SURVEY_PARAMETERS = { diff --git a/Composer/packages/electron-server/src/main.ts b/Composer/packages/electron-server/src/main.ts index e28be0c972..a3338f5b8a 100644 --- a/Composer/packages/electron-server/src/main.ts +++ b/Composer/packages/electron-server/src/main.ts @@ -41,13 +41,14 @@ const waitForMainWindowToShow = new Promise((resolve) => { // webpack dev server runs on :3000 const getBaseUrl = () => { + const host = process.env.COMPOSER_HOST ?? 'localhost'; if (isDevelopment) { - return 'http://localhost:3000/'; + return `http://${host}:3000/`; } if (!serverPort) { throw new Error('getBaseUrl() called before serverPort is defined.'); } - return `http://localhost:${serverPort}/`; + return `http://${host}:${serverPort}/`; }; // set production flag diff --git a/Composer/packages/server/src/controllers/publisher.ts b/Composer/packages/server/src/controllers/publisher.ts index bba3599b48..cc04744212 100644 --- a/Composer/packages/server/src/controllers/publisher.ts +++ b/Composer/packages/server/src/controllers/publisher.ts @@ -15,6 +15,7 @@ import AssetService from '../services/asset'; import logger from '../logger'; import { LocationRef } from '../models/bot/interface'; import { TelemetryService } from '../services/telemetry'; +import { serverListenHost, serverHostname } from '../settings/env'; const log = logger.extend('publisher-controller'); @@ -318,7 +319,7 @@ export const PublishController = { const pluginMethod = ExtensionContext.extensions.publish[extensionName].methods.setupRuntimeLogServer; if (typeof pluginMethod === 'function') { try { - const runtimeLogUrl = await pluginMethod.call(null, projectId); + const runtimeLogUrl = await pluginMethod.call(null, projectId, serverHostname, serverListenHost); return res.status(200).send(runtimeLogUrl); } catch (ex) { res.status(400).json({ diff --git a/Composer/packages/server/src/directline/store/dlServerState.ts b/Composer/packages/server/src/directline/store/dlServerState.ts index 8c79813312..dfe656ce00 100644 --- a/Composer/packages/server/src/directline/store/dlServerState.ts +++ b/Composer/packages/server/src/directline/store/dlServerState.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +import { serverHostname } from '../../settings/env'; + import { BotEndpoint } from './entities/botEndpoint'; import { Attachments } from './entities/attachments'; import { ConversationSet } from './entities/conversationSet'; @@ -27,7 +29,7 @@ class DLServerContext { conversations: new ConversationSet(), endpoints: new EndpointSet(), attachments: new Attachments(), - serviceUrl: serverPort ? `http://localhost:${serverPort}` : '', + serviceUrl: serverPort ? `http://${serverHostname}:${serverPort}` : '', dispatchers: { getDefaultEndpoint: this.getDefaultEndpoint, updateConversation: this.updateConversation, diff --git a/Composer/packages/server/src/directline/utils/webSocketServer.ts b/Composer/packages/server/src/directline/utils/webSocketServer.ts index 68da273c46..edf12bf65d 100644 --- a/Composer/packages/server/src/directline/utils/webSocketServer.ts +++ b/Composer/packages/server/src/directline/utils/webSocketServer.ts @@ -13,6 +13,8 @@ import { ConversationNetworkTrafficItem, } from '@botframework-composer/types'; +import { serverListenHost } from '../../settings/env'; + import log from './logger'; const socketTrafficChannelKey = 'DL_TRAFFIC_SOCKET'; @@ -87,7 +89,7 @@ export class WebSocketServer { }); this.port = port; log(`Using ${port} port for directline`); - this.restServer.listen(port); + this.restServer.listen(port, serverListenHost); app.use('/ws/conversation/:conversationId', (req: express.Request, res: express.Response) => { if (!(req as any).claimUpgrade) { diff --git a/Composer/packages/server/src/server.ts b/Composer/packages/server/src/server.ts index ad16cbe04f..e9cb49b964 100644 --- a/Composer/packages/server/src/server.ts +++ b/Composer/packages/server/src/server.ts @@ -36,6 +36,7 @@ import { mountDirectLineRoutes } from './directline/mountDirectlineRoutes'; import { mountAttachmentRoutes } from './directline/mountAttachmentRoutes'; import { cleanHostedBots } from './utility/cleanHostedBots'; import { getVersion } from './utility/getVersion'; +import { serverListenHost, serverHostname } from './settings/env'; // eslint-disable-next-line @typescript-eslint/no-var-requires const session = require('express-session'); @@ -183,12 +184,14 @@ export async function start(electronContext?: ElectronContext): Promise { - server = app.listen(port, () => { + await new Promise((resolve) => { + server = app.listen(port, serverListenHost, () => { if (process.env.NODE_ENV === 'production') { // We don't use the debug logger here because we always want it to be shown. // eslint-disable-next-line no-console - console.log(`\n\n${chalk.green('Composer now running at:')}\n\n${chalk.blue(`http://localhost:${port}`)}\n`); + console.log( + `\n\n${chalk.green('Composer now running at:')}\n\n${chalk.blue(`http://${serverHostname}:${port}`)}\n` + ); } resolve(); }); diff --git a/Composer/packages/server/src/settings/env.ts b/Composer/packages/server/src/settings/env.ts index d766182104..800783bb73 100644 --- a/Composer/packages/server/src/settings/env.ts +++ b/Composer/packages/server/src/settings/env.ts @@ -5,10 +5,13 @@ import childProcess from 'child_process'; import { Path } from '../utility/path'; +export const serverListenHost = process.env.COMPOSER_HOST || 'localhost'; +export const serverHostname = serverListenHost === '0.0.0.0' || !serverListenHost ? 'localhost' : serverListenHost; + export const absHosted = process.env.COMPOSER_AUTH_PROVIDER === 'abs-h'; export const absHostRoot = process.env.WEBSITE_HOSTNAME ? `https://${process.env.WEBSITE_HOSTNAME}` - : 'http://localhost:3978'; + : `http://${serverHostname}:3978`; let folder = process.env.COMPOSER_BOTS_FOLDER; if (folder?.endsWith(':')) { diff --git a/Dockerfile b/Dockerfile index d9e0b67a05..b987c10850 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,12 +6,14 @@ # before doing yarn install due to yarn workspace symlinking. # ################ -FROM mcr.microsoft.com/dotnet/core/sdk:3.1-focal as base +FROM mcr.microsoft.com/dotnet/core/sdk:3.1-focal as base RUN apt update \ && apt -y install curl dirmngr apt-transport-https lsb-release ca-certificates \ && curl -sL https://deb.nodesource.com/setup_14.x | bash - \ && apt install -y nodejs libgomp1 \ - && npm install -g yarn + && corepack enable \ + && corepack prepare yarn@3.2.1 --activate \ + && yarn --version FROM base as build ARG YARN_ARGS @@ -32,6 +34,7 @@ RUN yarn build:prod $YARN_ARGS ENV COMPOSER_REMOTE_EXTENSIONS_DIR "/src/remote-extensions" ENV COMPOSER_REMOTE_EXTENSION_DATA_DIR "/src/extension-data" ENV COMPOSER_EXTENSION_MANIFEST "/src/extensions.json" +ENV COMPOSER_HOST="0.0.0.0" CMD ["yarn","start:server"] FROM base as composerbasic @@ -46,7 +49,7 @@ COPY --from=build /src/Composer/packages ./packages COPY --from=build /src/extensions ../extensions ENV NODE_ENV "production" -RUN yarn --production --immutable --force $YARN_ARGS && yarn cache clean +RUN yarn install --immutable $YARN_ARGS && yarn cache clean FROM base ENV NODE_ENV "production" @@ -59,4 +62,5 @@ ENV COMPOSER_BUILTIN_EXTENSIONS_DIR "/app/extensions" ENV COMPOSER_REMOTE_EXTENSIONS_DIR "/app/remote-extensions" ENV COMPOSER_REMOTE_EXTENSION_DATA_DIR "/app/extension-data" ENV COMPOSER_EXTENSION_MANIFEST "/app/extensions.json" +ENV COMPOSER_HOST="0.0.0.0" CMD ["yarn","start:server"] diff --git a/extensions/localPublish/src/index.ts b/extensions/localPublish/src/index.ts index 84ef94e938..aa5d8316b6 100644 --- a/extensions/localPublish/src/index.ts +++ b/extensions/localPublish/src/index.ts @@ -243,9 +243,11 @@ class LocalPublisher implements PublishPlugin { } }; - setupRuntimeLogServer = async (projectId: string) => { + setupRuntimeLogServer = async (projectId: string, serverHostname?: string, serverListenHost?: string) => { await RuntimeLogServer.init({ log: this.composer.log, + hostname: serverHostname, + boundHost: serverListenHost, }); return RuntimeLogServer.getRuntimeLogStreamingUrl(projectId); }; diff --git a/extensions/localPublish/src/runtimeLogServer.ts b/extensions/localPublish/src/runtimeLogServer.ts index a891f34fdc..3888602d7b 100644 --- a/extensions/localPublish/src/runtimeLogServer.ts +++ b/extensions/localPublish/src/runtimeLogServer.ts @@ -18,12 +18,21 @@ export class RuntimeLogServer { private static servers: WSServer = {}; private static sockets: Record = {}; private static port: number; + private static hostname: string; public static getRuntimeLogStreamingUrl(projectId: string): string { - return `ws://localhost:${this.port}/ws/runtimeLog/${projectId}`; + return `ws://${this.hostname}:${this.port}/ws/runtimeLog/${projectId}`; } - public static async init({ log }: { log: Debugger }): Promise { + public static async init({ + log, + boundHost = 'localhost', + hostname = 'localhost', + }: { + log: Debugger; + boundHost?: string; + hostname?: string; + }): Promise { if (!this.restServer) { const app = express(); this.restServer = http.createServer(app); @@ -42,7 +51,7 @@ export class RuntimeLogServer { return preferredPort; }); log(`Using ${port} port for runtime-log`); - this.restServer.listen(port); + this.restServer.listen(port, boundHost); app.use('/ws/runtimeLog/:projectId', (req: Request, res: Response) => { if (!(req as any).claimUpgrade) { @@ -74,6 +83,7 @@ export class RuntimeLogServer { } }); this.port = port; + this.hostname = hostname; return this.port; } }