diff --git a/api/domains.js b/api/domains.js index fd1f97a..18d7675 100644 --- a/api/domains.js +++ b/api/domains.js @@ -3,16 +3,12 @@ const http = require('../http'); const DOMAINS_API_PATH = `/cms/v3/domains`; async function fetchDomains(accountId) { - try { - const result = await http.get(accountId, { - uri: DOMAINS_API_PATH, - json: true, - }); + const result = await http.get(accountId, { + uri: DOMAINS_API_PATH, + json: true, + }); - return result.results; - } catch (err) { - throw err; - } + return result.results; } module.exports = { diff --git a/api/preview.js b/api/preview.js index 915a705..cbc3d9b 100644 --- a/api/preview.js +++ b/api/preview.js @@ -1,3 +1,5 @@ +const { request } = require('../http'); + async function fetchPreviewRender(url, sessionInfo) { const { sessionToken } = sessionInfo; @@ -6,7 +8,7 @@ async function fetchPreviewRender(url, sessionInfo) { urlObject.searchParams.append('localPreviewToken', sessionToken); urlObject.searchParams.append('hsCacheBuster', Date.now()); - return fetch(urlObject.href).then(res => res.text()); + return request(urlObject.href); } module.exports = { diff --git a/lang/en.lyaml b/lang/en.lyaml index 9051199..d1b65eb 100644 --- a/lang/en.lyaml +++ b/lang/en.lyaml @@ -52,5 +52,3 @@ en: fieldsJsSyntaxError: "There was an error converting JS file \"{{ path }}\"" fieldsJsNotReturnArray: "There was an error loading JS file \"{{ path }}\". Expected type \"Array\" but received type \"{{ returned }}\" . Make sure that your function returns an array" fieldsJsNotFunction: "There was an error loading JS file \"{{ path }}\". Expected type \"Function\" but received type \"{{ returned }}\". Make sure that your default export is a function." - - diff --git a/lib/config.js b/lib/config.js index 453127a..852ef62 100644 --- a/lib/config.js +++ b/lib/config.js @@ -763,7 +763,22 @@ const isConfigFlagEnabled = flag => { return config[flag] || false; }; + +const getAuthType = accountId => { + let authType = 'unknown'; + + if (accountId) { + const accountConfig = getAccountConfig(accountId); + if (accountConfig && accountConfig.authType) { + authType = accountConfig.authType; + } + } + + return authType; +}; + module.exports = { + getAuthType, getAndLoadConfigIfNeeded, getEnv, getConfig, diff --git a/lib/preview.js b/lib/preview.js index 3017326..f4b264f 100644 --- a/lib/preview.js +++ b/lib/preview.js @@ -23,7 +23,7 @@ const { isUngatedForPreview, } = require('./preview/previewUtils'); const { markRemoteFsDirty } = require('./preview/routes/meta'); -const { startShadowDevServer } = require('./preview/shadowDevServer'); +const { startSprocketMenuServer } = require('./preview/sprocketMenuServer'); const { createHttpsRedirectingServer, } = require('./preview/httpsRedirectingServer'); @@ -108,7 +108,7 @@ const buildDeleteFileFromPreviewBufferCallback = (sessionInfo, type) => { }; const buildUploadFileToPreviewBufferCallback = (sessionInfo, notifyMessage) => { - const { portalId, src, dest, notify } = sessionInfo; + const { accountId, src, dest, notify } = sessionInfo; return async filePath => { if (!isAllowedExtension(filePath)) { @@ -120,15 +120,17 @@ const buildUploadFileToPreviewBufferCallback = (sessionInfo, notifyMessage) => { return; } const destPath = getDesignManagerPath(src, dest, filePath); - const uploadPromise = uploadFile(portalId, filePath, destPath); + const uploadPromise = uploadFile(accountId, filePath, destPath); triggerNotify(notify, notifyMessage, filePath, uploadPromise); }; }; -const initialPreviewBufferUpload = async (sessionInfo, filePaths) => { - const { portalId, src, dest } = sessionInfo; +const initialPreviewBufferUpload = async (sessionInfo, filePaths, uploadOptions) => { + const { accountId, src, dest } = sessionInfo; + const { onFinishCallback, ...rest } = uploadOptions; - return uploadFolder(portalId, src, dest, fileMapperArgs, {}, filePaths); + const results = await uploadFolder(accountId, src, dest, fileMapperArgs, rest, filePaths); + onFinishCallback(results); }; const startPreviewWatcher = async sessionInfo => { @@ -158,11 +160,14 @@ const startPreviewWatcher = async sessionInfo => { ); watcher.on('ready', () => { - console.log('Local file watching service has started!'); watcherIsReady = true; }); watcher.on('add', addFileCallback); watcher.on('change', changeFileCallback); + watcher.on('error', error => + logger.error(`An error occurred while watching files: ${error}`) + ); + watcher.on('unlink', deleteFileCallback); watcher.on('unlinkDir', deleteFolderCallback); @@ -180,7 +185,6 @@ const startPreviewWatcher = async sessionInfo => { const createLocalHttpServer = async sessionInfo => { const expressServer = express(); - //expressServer.use(bodyParser.json()); expressServer.use('/', await createPreviewServerRoutes(sessionInfo)); return expressServer; @@ -190,19 +194,20 @@ const preview = async ( accountId, src, dest, - { notify, filePaths, skipUpload, noSsl, port } + { notify, filePaths, skipUpload, noSsl, port, uploadOptions } ) => { const accountConfig = getAccountConfig(accountId); const domains = await getPortalDomains(accountId); - const sessionToken = '96cd331a-189d-41f2-8a4c-a12485402eff'; + const sessionToken = uuidv4(); const PORT = port || 3000; const protocol = noSsl ? 'http' : 'https'; const sessionInfo = { src, dest: `@preview/${sessionToken}/${dest}`, + fakeDest: dest, portalName: accountConfig.name, - portalId: accountId, + accountId, env: accountConfig.env, personalAccessKey: accountConfig.personalAccessKey, // we find hublet later in the content metadata fetch @@ -215,7 +220,7 @@ const preview = async ( }; const ungated = await isUngatedForPreview(sessionInfo); if (!ungated) { - console.log( + logger.log( `Portal ${accountId} is missing a required gate for this feature.` ); process.exit(); @@ -225,7 +230,7 @@ const preview = async ( } if (!skipUpload) { - await initialPreviewBufferUpload(sessionInfo, filePaths); + await initialPreviewBufferUpload(sessionInfo, filePaths, uploadOptions); } const expressServer = await createLocalHttpServer(sessionInfo); const previewWatcher = await startPreviewWatcher(sessionInfo); @@ -241,9 +246,9 @@ const preview = async ( const httpServer = http.createServer(expressServer); httpServer.listen(PORT); } - startShadowDevServer(sessionInfo); - console.log( - `HubSpot preview local dev server hosting at ${protocol}://hslocal.net:${PORT}, portalId=${accountId}` + startSprocketMenuServer(sessionInfo); + logger.log( + `Local dev server started at ${protocol}://hslocal.net:${PORT} for portal ${accountId}` ); }; diff --git a/lib/preview/createRoutes.js b/lib/preview/createRoutes.js index f13b379..ba743c7 100644 --- a/lib/preview/createRoutes.js +++ b/lib/preview/createRoutes.js @@ -1,5 +1,6 @@ const { Router } = require('express'); -const cors = require('cors'); +const { logger } = require('./../../logger'); + const { buildIndexRouteHandler } = require('./routes/index.js'); const { buildModuleRouteHandler } = require('./routes/module.js'); const { buildTemplateRouteHandler } = require('./routes/template.js'); @@ -15,8 +16,9 @@ const createPreviewServerRoutes = async (sessionInfo) => { previewServerRouter.get('/proxy', buildProxyRouteHandler(sessionInfo)); previewServerRouter.get('/module/:modulePath(*)', buildModuleRouteHandler(sessionInfo)); previewServerRouter.get('/template/:templatePath(*)', buildTemplateRouteHandler(sessionInfo)); + // fetches server metadata from the client (used by refresh script to check if fs has been changed) previewServerRouter.get('/meta', buildMetaRouteHandler(sessionInfo)); - + // handles resources on the proxied page, so a fetch from relative path gets proxied too previewServerRouter.get('/*', proxyPathPageResourceRedirect) previewServerRouter.get('/*', proxyPageResourceRedirect); previewServerRouter.post('/*', proxyPageResourceRedirect); @@ -25,7 +27,7 @@ const createPreviewServerRoutes = async (sessionInfo) => { previewServerRouter.put('/*', proxyPageResourceRedirect); previewServerRouter.options('/*', proxyPageResourceRedirect); previewServerRouter.get('/*', buildProxyPageRouteHandler(sessionInfo)); - + // index route previewServerRouter.get('/', buildIndexRouteHandler(sessionInfo)); return previewServerRouter; diff --git a/lib/preview/httpsRedirectingServer.js b/lib/preview/httpsRedirectingServer.js index ed21398..604530c 100644 --- a/lib/preview/httpsRedirectingServer.js +++ b/lib/preview/httpsRedirectingServer.js @@ -1,6 +1,7 @@ const http = require('http'); const https = require('https'); const net = require('net'); +const os = require('os') const { unlinkSync } = require('fs'); const { silenceConsoleWhile } = require('./previewUtils'); @@ -14,11 +15,11 @@ const createCert = async (domainsToProxy) => { const hosts = ['localhost', 'hslocal.net', ...additionalMkcertHosts]; const { createCertificate } = await import('mkcert-cli'); const { key, cert } = await silenceConsoleWhile(createCertificate, { - keyFilePath: `${__dirname}/key.pem`, - certFilePath: `${__dirname}/cert.pem` + keyFilePath: `${os.tmpdir()}/hstmp/hsLocalSshKey.pem`, + certFilePath: `${os.tmpdir()}/hstmp/hsLocalSshCert.pem` }, hosts); - unlinkSync(`${__dirname}/key.pem`); - unlinkSync(`${__dirname}/cert.pem`); + unlinkSync(`${os.tmpdir()}/hstmp/hsLocalSshKey.pem`); + unlinkSync(`${os.tmpdir()}/hstmp/hsLocalSshCert.pem`); return { key, cert }; } diff --git a/lib/preview/previewUtils.js b/lib/preview/previewUtils.js index 50b78d6..ccacd1f 100644 --- a/lib/preview/previewUtils.js +++ b/lib/preview/previewUtils.js @@ -2,34 +2,29 @@ const { fetchDomains } = require('../../api/domains'); const { getAccountId, isTrackingAllowed, getAccountConfig } = require('../config'); const { platform, release } = require('os'); const { trackUsage } = require('../../api/fileMapper'); -const unAuth = require('../../api/localDevAuth/unauthenticated'); - +const { enabledFeaturesForPersonalAccessKey } = require('../../personalAccessKey'); +const { stringify } = require('querystring'); +const { logger } = require('./../../logger'); +const { getAuthType } = require('./../../lib/config'); const VALID_PROXY_DOMAIN_SUFFIXES = ['localhost', 'hslocal.net']; + const HS_PREVIEW_GATE = "cms:localHublPreviews"; -const getPortalDomains = async (portalId) => { +const getPortalDomains = async (accountId) => { try { - const result = await fetchDomains(portalId); + const result = await fetchDomains(accountId); return result; } catch (error) { - console.log("There was a problem fetching domains for your portal. You may be missing a scope necessary for this feature.") return []; } } const getPreviewUrl = (sessionInfo, queryParams) => { - const { portalId, env, hublet } = sessionInfo; + const { accountId, env, hublet } = sessionInfo; - return `http://${portalId}.hubspotpreview${ + return `http://${accountId}.hubspotpreview${ env === 'qa' ? 'qa' : '' - }-${hublet}.com/_hcms/preview/template/multi?${stringifyQuery(queryParams)}`; -} - -const stringifyQuery = (query) => { - return Object.keys(query) - .sort() - .map(key => `${key}=${query[key]}`) - .join('&'); + }-${hublet}.com/_hcms/preview/template/multi?${stringify(queryParams)}`; } const insertAtEndOfBody = (html, script) => { @@ -44,6 +39,7 @@ const addRefreshScript = (html) => { const refreshScript = `