UNPKG

novu

Version:

Novu CLI. Run Novu Studio and sync workflows with Novu Cloud

144 lines (140 loc) 5.51 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TUNNEL_URL = void 0; exports.devCommand = devCommand; const ws_1 = __importDefault(require("ws")); const ora_1 = __importDefault(require("ora")); const open_1 = __importDefault(require("open")); const chalk_1 = __importDefault(require("chalk")); const ntfr_client_1 = require("@novu/ntfr-client"); const dev_server_1 = require("../../dev-server"); const shared_1 = require("../shared"); const index_1 = require("../../index"); const utils_1 = require("./utils"); const package_json_1 = __importDefault(require("../../../package.json")); process.on('SIGINT', function () { process.exit(); }); let tunnelClient = null; exports.TUNNEL_URL = 'https://novu.sh/api/tunnels'; const { version } = package_json_1.default; async function devCommand(options, anonymousId) { await (0, shared_1.showWelcomeScreen)(); const parsedOptions = (0, utils_1.parseOptions)(options); const NOVU_ENDPOINT_PATH = options.route; let tunnelOrigin; const devSpinner = (0, ora_1.default)('Creating a development local tunnel').start(); if (parsedOptions.tunnel) { tunnelOrigin = parsedOptions.tunnel; } else { tunnelOrigin = await createTunnel(parsedOptions.origin, NOVU_ENDPOINT_PATH); } devSpinner.succeed(`🛣️ Tunnel → ${tunnelOrigin}${NOVU_ENDPOINT_PATH}`); const opts = Object.assign(Object.assign({}, parsedOptions), { tunnelOrigin, anonymousId }); const httpServer = new dev_server_1.DevServer(opts); const dashboardSpinner = (0, ora_1.default)('Opening dashboard').start(); const studioSpinner = (0, ora_1.default)('Starting local studio server').start(); await httpServer.listen(); dashboardSpinner.succeed(`🖥️ Dashboard → ${parsedOptions.dashboardUrl}`); studioSpinner.succeed(`🎨 Studio → ${httpServer.getStudioAddress()}`); if (process.env.NODE_ENV !== 'dev' && parsedOptions.headless === false) { await (0, open_1.default)(httpServer.getStudioAddress()); } await monitorEndpointHealth(parsedOptions, NOVU_ENDPOINT_PATH); } async function monitorEndpointHealth(parsedOptions, endpointRoute) { const fullEndpoint = `${parsedOptions.origin}${endpointRoute}`; let healthy = false; const endpointText = `Bridge Endpoint scan:\t${fullEndpoint} Ensure your application is configured and running locally.`; const endpointSpinner = (0, ora_1.default)(endpointText).start(); let counter = 0; while (!healthy) { try { healthy = await tunnelHealthCheck(fullEndpoint); if (healthy) { endpointSpinner.succeed(`🌉 Endpoint → ${fullEndpoint}`); } else { await (0, utils_1.wait)(1000); } } catch (e) { await (0, utils_1.wait)(1000); } finally { counter += 1; if (counter === 10) { endpointSpinner.text = `Bridge Endpoint scan:\t${fullEndpoint} Ensure your application is configured and running locally. Starting out? Use our starter ${chalk_1.default.bold('npx novu@latest init')} Running on a different route or port? Use ${chalk_1.default.bold('--route')} or ${chalk_1.default.bold('--port')} `; } } } } async function tunnelHealthCheck(configTunnelUrl) { try { const res = await (await fetch(`${configTunnelUrl}?action=health-check`, { method: 'GET', headers: { accept: 'application/json', 'Content-Type': 'application/json', 'User-Agent': `novu@${version}`, }, })).json(); return res.status === 'ok'; } catch (e) { return false; } } async function createTunnel(localOrigin, endpointRoute) { const originUrl = new URL(localOrigin); const configTunnelUrl = index_1.config.getValue(`tunnelUrl-${parseInt(originUrl.port, 10)}`); const storeUrl = configTunnelUrl ? new URL(configTunnelUrl) : null; if (storeUrl) { try { await connectToTunnel(storeUrl, originUrl); if (tunnelClient.isConnected) { return storeUrl.origin; } } catch (error) { return await connectToNewTunnel(originUrl); } } return await connectToNewTunnel(originUrl); } async function fetchNewTunnel(originUrl) { const response = await fetch(exports.TUNNEL_URL, { method: 'POST', headers: { accept: 'application/json', 'Content-Type': 'application/json', authorization: `Bearer 12345`, }, }); const { url } = (await response.json()); index_1.config.setValue(`tunnelUrl-${parseInt(originUrl.port, 10)}`, url); return new URL(url); } async function connectToTunnel(parsedUrl, parsedOrigin) { tunnelClient = new ntfr_client_1.NtfrTunnel(parsedUrl.host, parsedOrigin.host, false, { WebSocket: ws_1.default, connectionTimeout: 2000, maxRetries: Infinity, }, { verbose: false }); await tunnelClient.connect(); } async function connectToNewTunnel(originUrl) { const parsedUrl = await fetchNewTunnel(originUrl); await connectToTunnel(parsedUrl, originUrl); return parsedUrl.origin; }