UNPKG

7.1 kBJavaScriptView Raw
1'use strict';
2
3const path = require('path');
4const fs = require('fs');
5const url = require('url');
6const _ = require('lodash');
7const express = require('express');
8const bodyParser = require('body-parser');
9const Watchpack = require('watchpack');
10const rekitCore = require('rekit-core');
11const fetchProjectData = require('./api/fetchProjectData');
12const getFileContent = require('./api/getFileContent');
13const saveFile = require('./api/saveFile');
14const runBuild = require('./api/runBuild');
15const runTest = require('./api/runTest');
16
17const utils = rekitCore.utils;
18
19// rekitCore.utils.setProjectRoot('/Users/i305656/workspace/rekit');
20rekitCore.plugin.loadPlugins(rekitCore);
21
22let lastProjectData = null;
23module.exports = function() { // eslint-disable-line
24 let io = null;
25
26 const bgProcesses = {};
27
28 const wp = new Watchpack({
29 // options:
30 aggregateTimeout: 1000,
31 // fire "aggregated" event when after a change for 1000ms no additonal change occured
32 // aggregated defaults to undefined, which doesn't fire an "aggregated" event
33
34 poll: false,
35 // poll: true - use polling with the default interval
36 // poll: 10000 - use polling with an interval of 10s
37 // poll defaults to undefined, which prefer native watching methods
38 // Note: enable polling when watching on a network path
39
40 ignored: /node_modules/,
41 // anymatch-compatible definition of files/paths to be ignored
42 // see https://github.com/paulmillr/chokidar#path-filtering
43 });
44
45 // Watchpack.prototype.watch(string[] files, string[] directories, [number startTime])
46 // Watch src files change only
47 wp.watch([], [path.join(rekitCore.utils.getProjectRoot(), 'src')], Date.now() - 10);
48 // starts watching these files and directories
49 // calling this again will override the files and directories
50
51 wp.on('aggregated', (changes) => {
52 // changes: an array of all changed files
53 rekitCore.vio.reset();
54 // const newProjectData = fetchProjectData();
55 // if (_.isEqual(newProjectData, lastProjectData)) return;
56 // lastProjectData = newProjectData;
57 if (io) io.emit('fileChanged', changes);
58 });
59
60 const rootPath = '/rekit';
61
62 function execCmd(req, res) {
63 try {
64 const args = req.body;
65 rekitCore.handleCommand(args);
66 const logs = rekitCore.vio.flush();
67 rekitCore.vio.reset();
68 res.write(JSON.stringify({
69 args,
70 logs,
71 }));
72 res.end();
73 } catch (e) {
74 res.statusCode = 500;
75 res.write(e.toString());
76 res.end();
77 }
78 }
79
80 function setupSocketIo(server) {
81 io = require('socket.io')(server);
82
83 io.on('connection', (client) => {
84 client.on('disconnect', () => {
85 console.log('socket disconnected');
86 });
87 });
88 }
89
90 function reply403(res) {
91 res.statusCode = 403;
92 res.write('Forbidden: Rekit portal is running on readonly mode.');
93 res.end();
94 }
95
96 function rekitMiddleware(server, app, args) {
97 args = args || {};
98 setupSocketIo(server);
99 const prjRoot = rekitCore.utils.getProjectRoot();
100 app.use('/coverage', express.static(utils.joinPath(prjRoot, 'coverage'), { fallthrough: false }));
101 app.use(bodyParser.json());
102 app.use(bodyParser.urlencoded({ extended: true }));
103
104 return (req, res, next) => {
105 const urlObject = url.parse(req.originalUrl);
106 const p = urlObject.pathname.replace(rootPath, '');
107
108 try {
109 switch (p) {
110 case '/api/project-data': {
111 lastProjectData = fetchProjectData();
112 res.write(JSON.stringify(Object.assign({
113 bgProcesses,
114 }, lastProjectData)));
115 res.end();
116 break;
117 }
118 case '/api/save-file': {
119 const absPath = utils.joinPath(prjRoot, req.body.file);
120 if (!_.startsWith(absPath, prjRoot)) {
121 // prevent ../.. in req.query.file
122 res.statusCode = 403;
123 res.write('Forbidden: not allowed to access file out of the project.');
124 res.end();
125 break;
126 }
127
128 if (!fs.existsSync(absPath)) {
129 res.statusCode = 404;
130 res.write(JSON.stringify({ error: 'Not found.' }));
131 res.end();
132 } else {
133 try {
134 saveFile(absPath, req.body.content);
135 res.write({ success: true });
136 } catch (err) {
137 res.write({ error: err });
138 }
139 res.end();
140 }
141 break;
142 }
143 case '/api/file-content': {
144 const absPath = utils.joinPath(prjRoot, req.query.file);
145 if (!_.startsWith(absPath, prjRoot)) {
146 // prevent ../.. in req.query.file
147 res.statusCode = 403;
148 res.write('Forbidden: not allowed to access file out of the project.');
149 res.end();
150 break;
151 }
152
153 if (!fs.existsSync(absPath)) {
154 res.statusCode = 404;
155 res.write(JSON.stringify({ error: 'Not found.' }));
156 res.end();
157 } else {
158 res.write(JSON.stringify({ content: getFileContent(absPath) }));
159 res.end();
160 }
161 break;
162 }
163 case '/api/exec-cmd':
164 if (args.readonly) { reply403(res); break; }
165 execCmd(req, res);
166 break;
167 case '/api/run-build':
168 if (args.readonly) { reply403(res); break; }
169 if (bgProcesses.runningBuild) {
170 res.statusCode = 500;
171 res.write(JSON.stringify({ error: 'Build process is running...' }));
172 res.end();
173 } else {
174 bgProcesses.runningBuild = true;
175 runBuild(io).then(() => {
176 bgProcesses.runningBuild = false;
177 }).catch(() => {
178 bgProcesses.runningBuild = false;
179 });
180 res.write('{}');
181 res.end();
182 }
183 break;
184 case '/api/run-test':
185 if (args.readonly) { reply403(res); break; }
186 if (bgProcesses.runningTest) {
187 res.statusCode = 500;
188 res.write(JSON.stringify({ error: 'Test process is running...' }));
189 res.end();
190 } else {
191 bgProcesses.runningTest = true;
192 runTest(io, req.body.testFile || '').then(() => {
193 bgProcesses.runningTest = false;
194 }).catch(() => {
195 bgProcesses.runningTest = false;
196 });
197 res.write('{}');
198 res.end();
199 }
200 break;
201 default: {
202 if (/^\/api\//.test(p)) {
203 res.statusCode = 404;
204 res.write(JSON.stringify({ error: `API not found: ${p}` }));
205 res.end();
206 } else {
207 next();
208 }
209 break;
210 }
211 }
212 } catch (e) {
213 res.statusCode = 500;
214 res.write(e.toString());
215 res.end();
216 }
217 };
218 }
219 return rekitMiddleware;
220};