|
|
|
@ -1,604 +1,35 @@ |
|
|
|
#!/usr/bin/env node
|
|
|
|
|
|
|
|
import {PRODUCTION_REGISTRY_URL} from "./env.js"; |
|
|
|
import path from 'path'; |
|
|
|
import fetch from "node-fetch"; |
|
|
|
import express from 'express'; |
|
|
|
import {create} from 'express-handlebars'; |
|
|
|
import browserSync from 'browser-sync'; |
|
|
|
import config from 'config'; |
|
|
|
import gulp from 'gulp'; |
|
|
|
import babel from "gulp-babel"; |
|
|
|
import uglify from "gulp-uglify"; |
|
|
|
import rename from "gulp-rename"; |
|
|
|
import dartSass from 'sass'; |
|
|
|
import gulpSass from 'gulp-sass'; |
|
|
|
import sourcemaps from "gulp-sourcemaps"; |
|
|
|
import fs from "fs/promises"; |
|
|
|
import open from "open"; |
|
|
|
import {sanitizeUrl} from "@braintree/sanitize-url"; |
|
|
|
import sanitizeHtml from 'sanitize-html'; |
|
|
|
import {escape} from "lodash-es"; |
|
|
|
import { |
|
|
|
getBlockConfigs, |
|
|
|
getBlockData, |
|
|
|
getBlockName, |
|
|
|
getConfigs, |
|
|
|
getImagesList, |
|
|
|
isFileEmpty, |
|
|
|
readJSONFile, |
|
|
|
removeCommentsFromCss, |
|
|
|
removeCommentsFromJs, |
|
|
|
replaceNames, |
|
|
|
syncFilesWithCloud, |
|
|
|
uploadFile, |
|
|
|
verifyVersion, |
|
|
|
zipProject |
|
|
|
} from "./helpers.js"; |
|
|
|
import PluginError from 'plugin-error'; |
|
|
|
import {Server} from "socket.io"; |
|
|
|
import {createServer} from 'http'; |
|
|
|
import {authtoken, connect} from "ngrok"; |
|
|
|
import bodyParser from "body-parser"; |
|
|
|
import {init} from "./inc/server.js"; |
|
|
|
import {index} from "./routes/index.js"; |
|
|
|
import dataRoute from "./routes/data.js"; |
|
|
|
import baseView from "./routes/base-view.js"; |
|
|
|
import syncRoute from "./routes/sync.js"; |
|
|
|
import publishRoute from "./routes/publish.js"; |
|
|
|
import {ViewSync} from "./inc/view-sync.js"; |
|
|
|
import {previewFiles, staticFiles} from "./routes/static-files.js"; |
|
|
|
import {setupWatcher} from "./inc/changes-watcher.js"; |
|
|
|
|
|
|
|
/** |
|
|
|
* Constants |
|
|
|
*/ |
|
|
|
const {expressApp, httpServer} = init(); |
|
|
|
|
|
|
|
const {isDev, modulesPath, projectPath, developmentBlockName} = getConfigs(); |
|
|
|
const blocksRegistry = isDev ? 'http://localhost:3020' : PRODUCTION_REGISTRY_URL; |
|
|
|
const DevToolToken = 'D9lgz0TvzXCnp0xnwVBL109DaAR6Puk6F7YewDhgmP8='; // Temporary token for development purposes.
|
|
|
|
const viewSync = new ViewSync(httpServer); |
|
|
|
setupWatcher(viewSync); |
|
|
|
|
|
|
|
const dataFiles = await getDataFiles(projectPath); |
|
|
|
const DEFAULT_VIEW_LAYOUT = 'alignfull'; |
|
|
|
const PORT = process.env.PORT || 3010; |
|
|
|
|
|
|
|
/** |
|
|
|
* State |
|
|
|
*/ |
|
|
|
|
|
|
|
let port = 3000; // This variable is used in `*.hbs` and it will be updated once BrowserSync is ready.
|
|
|
|
let previewFrameUrl = `/`; // This variable is used in `*.hbs` and it will be updated once BrowserSync is ready.
|
|
|
|
let shareUrl = ''; |
|
|
|
const sessions = []; |
|
|
|
let ignoreVersionSync = false; |
|
|
|
let bs; |
|
|
|
|
|
|
|
/** |
|
|
|
* Init server |
|
|
|
*/ |
|
|
|
|
|
|
|
const app = express(); |
|
|
|
|
|
|
|
// parse application/x-www-form-urlencoded
|
|
|
|
app.use(bodyParser.urlencoded({extended: false})) |
|
|
|
|
|
|
|
// parse application/json
|
|
|
|
app.use(bodyParser.json()) |
|
|
|
|
|
|
|
const httpServer = createServer(app); |
|
|
|
initSessionsServer(httpServer); |
|
|
|
|
|
|
|
const sass = gulpSass(dartSass); |
|
|
|
|
|
|
|
const hbs = create({ |
|
|
|
extname: '.hbs', defaultLayout: false, partialsDir: ['.'], helpers: { |
|
|
|
esc_attr(attr) { |
|
|
|
return escape(attr); |
|
|
|
}, esc_url(url) { |
|
|
|
return sanitizeUrl(url); |
|
|
|
}, esc_html(html) { |
|
|
|
// TODO: Check if we can remove this helper.
|
|
|
|
return html; |
|
|
|
}, safe_html(html) { |
|
|
|
return sanitizeHtml(html); |
|
|
|
} |
|
|
|
} |
|
|
|
httpServer.listen(PORT, '0.0.0.0', () => { |
|
|
|
console.log(`Server app listening on port ${PORT}`) |
|
|
|
}); |
|
|
|
|
|
|
|
app.engine('.hbs', hbs.engine); |
|
|
|
app.set('view engine', '.hbs'); |
|
|
|
app.set('views', path.join(modulesPath, 'layouts')); |
|
|
|
// Middleware
|
|
|
|
// expressApp.use(setHeaders);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Routes
|
|
|
|
//
|
|
|
|
|
|
|
|
app.get('/', async (req, res, next) => { |
|
|
|
const data = getBlockConfigs({modulesPath, dataFiles}); |
|
|
|
if (data.error && data.errorMessage) { |
|
|
|
return res.send(data.errorMessage); |
|
|
|
} |
|
|
|
|
|
|
|
const baseView = config.has('baseView') ? config.get('baseView') : DEFAULT_VIEW_LAYOUT; |
|
|
|
const baseViewUrl = `view/${baseView}`; |
|
|
|
|
|
|
|
data.helpers = { |
|
|
|
include_partial: (filesPath) => handlebarLayoutsPath(modulesPath, filesPath), |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
const verifiedVersion = await verifyVersion(projectPath, blocksRegistry); |
|
|
|
if (!verifiedVersion && !ignoreVersionSync) { |
|
|
|
return res.render('sync', data); |
|
|
|
} |
|
|
|
} catch (err) { |
|
|
|
const errorMessage = "Can't verify block version."; |
|
|
|
console.log(errorMessage, err); |
|
|
|
return next(new Error(errorMessage)); |
|
|
|
} |
|
|
|
|
|
|
|
data.baseView = baseView; |
|
|
|
data.port = `/${baseViewUrl}`; |
|
|
|
data.previewFrameUrl = `${previewFrameUrl}/${baseViewUrl}`; |
|
|
|
data.shareUrl = shareUrl; |
|
|
|
|
|
|
|
// TODO: Need to review this logic, conflicts with the browsersync work after "/sync" action.
|
|
|
|
// if (req.headers.referer) {
|
|
|
|
// // NGROK, public URL
|
|
|
|
// data.shareUrl = undefined; // Link already shared.
|
|
|
|
// data.previewFrameUrl = `/${baseViewUrl}`;
|
|
|
|
// data.publicUrl = true;
|
|
|
|
// }
|
|
|
|
|
|
|
|
res.render('index', data); |
|
|
|
}); |
|
|
|
|
|
|
|
app.get('/view/:baseView', (req, res) => { |
|
|
|
const data = getDataOfFrame(!!req.query.iframe); |
|
|
|
|
|
|
|
if (data.error && data.errorMessage) { |
|
|
|
return res.send(data.errorMessage); |
|
|
|
} |
|
|
|
|
|
|
|
const baseView = req.params.baseView ?? DEFAULT_VIEW_LAYOUT; |
|
|
|
|
|
|
|
res.render(baseView, data) |
|
|
|
}); |
|
|
|
|
|
|
|
app.get('/publish', async (req, res) => { |
|
|
|
const data = await readJSONFile(path.join(projectPath, `block.json`)); |
|
|
|
|
|
|
|
// Trigger build on the registry server only if the type of the unit is `foundation` or `component`.
|
|
|
|
const uploadStaticFiles = ['foundation', 'component'].includes(data.type); |
|
|
|
|
|
|
|
// Prepare list of static files for the registry server.
|
|
|
|
if (uploadStaticFiles) { |
|
|
|
data.static_files = { |
|
|
|
css: getBlockName(data.name).name + '.min.css', |
|
|
|
js: getBlockName(data.name).name + '.min.js', |
|
|
|
images: await getImagesList(path.join(projectPath, 'src', 'images')), |
|
|
|
} |
|
|
|
|
|
|
|
if (await isFileEmpty(path.join(projectPath, `src/scripts`, data.static_files.js), true)) { |
|
|
|
delete data.static_files.js; |
|
|
|
} |
|
|
|
|
|
|
|
if (!data.static_files.images.length) { |
|
|
|
delete data.static_files.images; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
let responseData = { |
|
|
|
uploadBundleUrl: undefined, |
|
|
|
staticFilesUrls: {} |
|
|
|
}; |
|
|
|
|
|
|
|
try { |
|
|
|
const response = await fetch(`${blocksRegistry}`, { |
|
|
|
method: 'POST', |
|
|
|
body: JSON.stringify(data), |
|
|
|
headers: {'Content-Type': 'application/json'} |
|
|
|
}); |
|
|
|
|
|
|
|
responseData = await response.json(); |
|
|
|
} catch (e) { |
|
|
|
res.json({success: false, message: 'Blocks Registry server is not available.'}); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (responseData.statusCode !== 200) { |
|
|
|
res.json({success: false, message: 'Error on registry level.'}); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// Start files uploading process.
|
|
|
|
try { |
|
|
|
if (responseData.uploadBundleUrl) { |
|
|
|
await zipProject(path.join(projectPath, 'src'), path.join(projectPath, 'dist.zip')); |
|
|
|
await uploadFile(path.join(projectPath, 'dist.zip'), responseData.uploadBundleUrl); // Bundle
|
|
|
|
} |
|
|
|
|
|
|
|
if (uploadStaticFiles) { |
|
|
|
if (responseData.staticFilesUrls.css) { |
|
|
|
await uploadFile( |
|
|
|
path.join(projectPath, 'src/styles', data.static_files.css), |
|
|
|
responseData.staticFilesUrls.css.uploadUrl, |
|
|
|
(content) => { |
|
|
|
if (responseData.staticFilesUrls.images) { |
|
|
|
content = replaceNames(content, data.static_files.images, responseData.staticFilesUrls.images); |
|
|
|
} |
|
|
|
|
|
|
|
removeCommentsFromCss(content) |
|
|
|
return content; |
|
|
|
}); // CSS
|
|
|
|
} |
|
|
|
|
|
|
|
if (responseData.staticFilesUrls.js) { |
|
|
|
await uploadFile( |
|
|
|
path.join(projectPath, 'src/scripts', data.static_files.js), |
|
|
|
responseData.staticFilesUrls.js.uploadUrl, |
|
|
|
(data) => removeCommentsFromJs(data) |
|
|
|
); // JS
|
|
|
|
} |
|
|
|
|
|
|
|
if (responseData.staticFilesUrls.images) { |
|
|
|
for (let i = 0; i < data.static_files.images.length; i++) { |
|
|
|
await uploadFile( |
|
|
|
path.join(projectPath, 'src/images', data.static_files.images[i]), |
|
|
|
responseData.staticFilesUrls.images[i].uploadUrl, |
|
|
|
); // Images
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} catch (err) { |
|
|
|
// TODO: Need to update the registry server.
|
|
|
|
await fs.unlink(path.join(projectPath, 'dist.zip')); // Remove local bundle
|
|
|
|
|
|
|
|
res.json({success: false, message: err.message}); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
await fs.unlink(path.join(projectPath, 'dist.zip')); // Remove local bundle
|
|
|
|
|
|
|
|
// Trigger project's global files build on the registry server.
|
|
|
|
if (uploadStaticFiles) { |
|
|
|
try { |
|
|
|
await triggerGlobalProjectFilesBuild(getBlockName(data.name).project); |
|
|
|
} catch (err) { |
|
|
|
res.json({success: false, message: 'Something wrong with Project Builder.'}); |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
await syncFilesWithCloud(path.join(projectPath, 'block.json'), bs); |
|
|
|
} catch (err) { |
|
|
|
res.json({success: false, message: err}); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
res.json({success: true}); |
|
|
|
}); |
|
|
|
|
|
|
|
app.get('/data', async (req, res) => { |
|
|
|
let jsonDataFileName = req.query.name ? req.query.name : 'default'; |
|
|
|
|
|
|
|
const dataFiles = await getDataFiles(projectPath); |
|
|
|
const data = await getBlockData(jsonDataFileName, {projectPath}); |
|
|
|
|
|
|
|
let designPreviewFiles = []; |
|
|
|
try { |
|
|
|
designPreviewFiles = getListOfDesignPreviewFiles(jsonDataFileName, await fs.readdir(path.join(projectPath, 'design', 'preview'))); |
|
|
|
} catch (err) { |
|
|
|
console.log('Preview Design doesn\'t exist'); |
|
|
|
} |
|
|
|
|
|
|
|
return res.json({ |
|
|
|
dataOptions: dataFiles, |
|
|
|
designPreview: designPreviewFiles, |
|
|
|
data, |
|
|
|
}); |
|
|
|
}); |
|
|
|
|
|
|
|
app.post('/sync', async (req, res) => { |
|
|
|
if (req.body['ignore']) { |
|
|
|
ignoreVersionSync = true; |
|
|
|
res.json({status: 200, message: 'Version upgrade is ignored.'}); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
try { |
|
|
|
await syncFilesWithCloud(path.join(projectPath, `block.json`), bs, true); |
|
|
|
} catch (err) { |
|
|
|
res.status(500).json({status: 500, message: err}); |
|
|
|
} |
|
|
|
|
|
|
|
res.json({status: 200, message: 'Successfully synced!'}); |
|
|
|
}); |
|
|
|
|
|
|
|
// Errors handler
|
|
|
|
app.use(handleSyntaxErrors); |
|
|
|
|
|
|
|
// Static Files
|
|
|
|
app.use(express.static(path.join(projectPath, 'src'))); |
|
|
|
app.use(express.static(path.join(projectPath, 'design'))); |
|
|
|
app.use(express.static(path.join(modulesPath, 'layouts'))); |
|
|
|
|
|
|
|
// Custom Middleware
|
|
|
|
app.use(setHeaders); |
|
|
|
|
|
|
|
// Setup Gulp
|
|
|
|
await buildAssetFiles(); |
|
|
|
|
|
|
|
// BrowserSync
|
|
|
|
shareUrl = await getShareableUrl(); |
|
|
|
const bsOptions = await startBrowserSync(); |
|
|
|
port = bsOptions.port; |
|
|
|
previewFrameUrl = bsOptions.previewFrameUrl; |
|
|
|
await open(bsOptions.devToolUrl); |
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Functions
|
|
|
|
//
|
|
|
|
|
|
|
|
function getListOfDesignPreviewFiles(jsonDataFileName, previewFiles) { |
|
|
|
return previewFiles |
|
|
|
.filter(fileName => { |
|
|
|
return fileName.startsWith(jsonDataFileName + '.'); |
|
|
|
}) |
|
|
|
.map(fileName => { |
|
|
|
const fileData = fileName.split('.'); |
|
|
|
const fileFormat = fileData.pop(); |
|
|
|
const previewSize = fileData.pop(); |
|
|
|
|
|
|
|
return { |
|
|
|
dataSource: jsonDataFileName, |
|
|
|
widthDimension: Number.parseInt(previewSize, 10), |
|
|
|
url: `/preview/${fileName}`, |
|
|
|
}; |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
function getDataOfFrame(isIframe = false) { |
|
|
|
const data = {config: getBlockConfigs({modulesPath, dataFiles})}; |
|
|
|
if (data.error && data.errorMessage) { |
|
|
|
return data; |
|
|
|
} |
|
|
|
|
|
|
|
data.helpers = { |
|
|
|
include_partial: (filesPath) => handlebarLayoutsPath(modulesPath, filesPath), |
|
|
|
} |
|
|
|
|
|
|
|
if (isIframe) { |
|
|
|
data.iframeMode = true; |
|
|
|
} |
|
|
|
|
|
|
|
return data; |
|
|
|
} |
|
|
|
|
|
|
|
function startBrowserSync() { |
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
const listener = httpServer.listen(0, async () => { |
|
|
|
const PORT = listener.address().port; |
|
|
|
|
|
|
|
console.log(`The web server has started on port ${PORT}`); |
|
|
|
|
|
|
|
// BS is global variable.
|
|
|
|
bs = browserSync.create(); |
|
|
|
|
|
|
|
const files = getJSBundleFiles(); |
|
|
|
gulp.watch(files, {delay: 400}, gulp.series(['build-script-files', function (cb) { |
|
|
|
browserSyncReload(bs, 'js', 'Script Files Change'); |
|
|
|
return cb(); |
|
|
|
}])); |
|
|
|
|
|
|
|
gulp.watch(path.posix.join(projectPath, "src/**/*.scss"), {delay: 400}, gulp.series(['build-styling-files', function (cb) { |
|
|
|
browserSyncReload(bs, 'css', 'Style Files Change'); |
|
|
|
return cb(); |
|
|
|
}])); |
|
|
|
|
|
|
|
bs.watch(path.join(projectPath, "src/**/*.hbs"), function () { |
|
|
|
return syncTemplate(sessions); |
|
|
|
}); |
|
|
|
|
|
|
|
const args = { |
|
|
|
proxy: `http://localhost:${PORT}`, |
|
|
|
open: false |
|
|
|
}; |
|
|
|
|
|
|
|
if (shareUrl) { |
|
|
|
args.socket = { |
|
|
|
domain: shareUrl |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
bs.init(args, (err, bs) => { |
|
|
|
if (err) { |
|
|
|
return reject(err); |
|
|
|
} |
|
|
|
|
|
|
|
const options = bs.getOptions().toJS(); |
|
|
|
|
|
|
|
const urls = { |
|
|
|
devTool: options.urls.local.replace(options.port, options.proxy.url.port), |
|
|
|
previewFrame: options.urls.local, |
|
|
|
}; |
|
|
|
|
|
|
|
// If local network is available.
|
|
|
|
if (options.urls.external) { |
|
|
|
urls.devTool = options.urls.external.replace(options.port, options.proxy.url.port); |
|
|
|
urls.previewFrame = options.urls.external; |
|
|
|
} |
|
|
|
|
|
|
|
resolve({ |
|
|
|
devToolUrl: urls.devTool, |
|
|
|
previewFrameUrl: urls.previewFrame, |
|
|
|
port: options.port |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
function browserSyncReload(bs, extension = '', message = '') { |
|
|
|
if (isDev) { |
|
|
|
// console.log(event, file);
|
|
|
|
console.log(message); |
|
|
|
} |
|
|
|
|
|
|
|
if (extension) { |
|
|
|
extension = "*." + extension; |
|
|
|
} |
|
|
|
|
|
|
|
bs.reload(extension); |
|
|
|
} |
|
|
|
|
|
|
|
function getJSBundleFiles() { |
|
|
|
return [path.posix.join(projectPath, "src/**/*.js"), path.posix.join(projectPath, "src/**/*.mjs"), "!" + path.posix.join(projectPath, "src/**/*.min.js")]; |
|
|
|
} |
|
|
|
|
|
|
|
function buildScriptFiles(done) { |
|
|
|
const files = getJSBundleFiles(); |
|
|
|
return gulp.src(files, {base: path.posix.join(projectPath, 'src')}) |
|
|
|
.pipe(sourcemaps.init({})) |
|
|
|
.pipe(babel()).on('error', function (error) { |
|
|
|
showError(new PluginError('JavaScript', error).toString()); |
|
|
|
done(); |
|
|
|
}) |
|
|
|
.pipe(gulp.src(path.join(projectPath, 'vendor/*.js'))) |
|
|
|
// .pipe(gulp.dest('src/'))
|
|
|
|
.pipe(rename({extname: '.min.js'})) |
|
|
|
.pipe(uglify()) |
|
|
|
.pipe(sourcemaps.write('.')) |
|
|
|
.pipe(gulp.dest(path.posix.join(projectPath, 'src'))); |
|
|
|
} |
|
|
|
|
|
|
|
function buildStyleFiles(done) { |
|
|
|
return gulp.src(path.join(projectPath, 'src/**/*.scss'), {base: path.posix.join(projectPath, 'src')}) |
|
|
|
.pipe(sourcemaps.init({})) |
|
|
|
.pipe(sass.sync({outputStyle: 'compressed'}).on('error', function (error) { |
|
|
|
showError(new PluginError('SCSS', error.messageFormatted).toString()); |
|
|
|
// sass.logError(error);
|
|
|
|
done(); |
|
|
|
})) |
|
|
|
// .pipe(gulp.dest('src/'))
|
|
|
|
.pipe(rename({extname: '.min.css'})) |
|
|
|
.pipe(sourcemaps.write('.', {})) |
|
|
|
.pipe(gulp.dest(path.posix.join(projectPath, 'src'))) |
|
|
|
} |
|
|
|
|
|
|
|
function buildAssetFiles() { |
|
|
|
// Register tasks.
|
|
|
|
gulp.task('build-script-files', buildScriptFiles); |
|
|
|
gulp.task('build-styling-files', buildStyleFiles); |
|
|
|
|
|
|
|
// Run first build.
|
|
|
|
return new Promise((resolve) => { |
|
|
|
gulp.series('build-script-files', 'build-styling-files', function () { |
|
|
|
resolve(); |
|
|
|
})(); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
function showError(errorMessage) { |
|
|
|
console.log(errorMessage); |
|
|
|
|
|
|
|
// TODO: Send this message to browser.
|
|
|
|
// So the developer can understand there is an error.
|
|
|
|
} |
|
|
|
|
|
|
|
function prepareListOfDataFiles(dataFiles) { |
|
|
|
return dataFiles |
|
|
|
.filter((fileName) => fileName.split('.').pop() === 'json') |
|
|
|
.map((fileName) => { |
|
|
|
const splitName = fileName.split('.'); |
|
|
|
splitName.pop(); |
|
|
|
return splitName.join(''); |
|
|
|
}) |
|
|
|
.sort(); |
|
|
|
} |
|
|
|
|
|
|
|
function handleSyntaxErrors(err, req, res, next) { |
|
|
|
if (err) { |
|
|
|
return res.render('error', { |
|
|
|
helpers: { |
|
|
|
include_partial: (filesPath) => path.join(modulesPath, filesPath), |
|
|
|
}, |
|
|
|
err |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
next(); |
|
|
|
} |
|
|
|
|
|
|
|
function handlebarLayoutsPath() { |
|
|
|
return path.join(...arguments) |
|
|
|
.replace(/\\/g, '/'); // Windows path issue. Fix all "\" for Handlebars.
|
|
|
|
} |
|
|
|
|
|
|
|
function initSessionsServer(httpServer) { |
|
|
|
const io = new Server(httpServer); |
|
|
|
io.on('connection', async (socket) => { |
|
|
|
sessions.push(socket); |
|
|
|
await syncTemplate(sessions); |
|
|
|
}); |
|
|
|
|
|
|
|
return httpServer; |
|
|
|
} |
|
|
|
|
|
|
|
function setHeaders(req, res, next) { |
|
|
|
res.setHeader('Access-Control-Allow-Origin', '*'); |
|
|
|
next(); |
|
|
|
} |
|
|
|
|
|
|
|
async function syncTemplate(sessions = []) { |
|
|
|
const blockName = config.has('blockName') ? config.get('blockName') : developmentBlockName; |
|
|
|
const hbsTemplate = await fs.readFile(handlebarLayoutsPath(projectPath, 'src', `${blockName}.template.hbs`), 'utf8'); |
|
|
|
|
|
|
|
sessions.forEach(socket => { |
|
|
|
socket.emit('templateUpdate', {template: hbsTemplate}); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
async function getShareableUrl() { |
|
|
|
let domain = PRODUCTION_REGISTRY_URL; |
|
|
|
let data = {} |
|
|
|
|
|
|
|
try { |
|
|
|
const response = await fetch(`${domain}/dev-tool/?devToolToken=${DevToolToken}`); |
|
|
|
data = await response.json(); |
|
|
|
} catch (e) { |
|
|
|
console.log('Error:', `Can't load data from DevTool endpoint: ${domain}`); |
|
|
|
} |
|
|
|
|
|
|
|
if (data.statusCode !== 200) { |
|
|
|
console.log('Reply from DevTool endpoint:', data.message); |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
let url; |
|
|
|
try { |
|
|
|
await authtoken(data.ngrokApiToken); |
|
|
|
url = await connect(port); |
|
|
|
} catch (e) { |
|
|
|
console.log('Error:', `Can't connect to ngrok, probably wrong token.`); |
|
|
|
} |
|
|
|
|
|
|
|
return url; |
|
|
|
} |
|
|
|
|
|
|
|
async function triggerGlobalProjectFilesBuild(project) { |
|
|
|
const response = await fetch(`${blocksRegistry}/project-files`, { |
|
|
|
method: 'POST', |
|
|
|
body: JSON.stringify({project}), |
|
|
|
headers: {'Content-Type': 'application/json'} |
|
|
|
}); |
|
|
|
|
|
|
|
return response.json(); |
|
|
|
} |
|
|
|
|
|
|
|
async function getDataFiles(projectPath) { |
|
|
|
const dataFiles = []; |
|
|
|
|
|
|
|
try { |
|
|
|
await fs.access(path.join(projectPath, 'data')); |
|
|
|
|
|
|
|
const files = prepareListOfDataFiles(await fs.readdir(path.join(projectPath, 'data'))); |
|
|
|
dataFiles.push(...files); |
|
|
|
} catch (e) { |
|
|
|
console.log('Warning: data folder not found.'); |
|
|
|
} |
|
|
|
expressApp.get('/', index); |
|
|
|
expressApp.get('/data', dataRoute); |
|
|
|
expressApp.get('/view/:baseView', baseView); |
|
|
|
expressApp.get('/design/:project/:blockName/*', previewFiles); |
|
|
|
expressApp.get('/block/:project/:blockName/*', staticFiles); |
|
|
|
expressApp.post('/sync', syncRoute); |
|
|
|
expressApp.get('/publish', publishRoute); |
|
|
|
|
|
|
|
return dataFiles; |
|
|
|
} |
|
|
|
|