1 | 'use strict';
|
2 |
|
3 | const path = require('path');
|
4 | const fs = require('fs');
|
5 | const url = require('url');
|
6 | const _ = require('lodash');
|
7 | const express = require('express');
|
8 | const bodyParser = require('body-parser');
|
9 | const Watchpack = require('watchpack');
|
10 | const prettier = require('prettier');
|
11 | const rekitCore = require('rekit-core');
|
12 | const fetchProjectData = require('./api/fetchProjectData');
|
13 | const getFileContent = require('./api/getFileContent');
|
14 | const saveFile = require('./api/saveFile');
|
15 | const runBuild = require('./api/runBuild');
|
16 | const runTest = require('./api/runTest');
|
17 |
|
18 | const utils = rekitCore.utils;
|
19 |
|
20 |
|
21 | rekitCore.plugin.loadPlugins(rekitCore);
|
22 |
|
23 | let lastProjectData = null;
|
24 | module.exports = function() {
|
25 | let io = null;
|
26 |
|
27 | const bgProcesses = {};
|
28 |
|
29 | const wp = new Watchpack({
|
30 |
|
31 | aggregateTimeout: 1000,
|
32 |
|
33 |
|
34 |
|
35 | poll: false,
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 | ignored: /node_modules/,
|
42 |
|
43 |
|
44 | });
|
45 |
|
46 |
|
47 |
|
48 | wp.watch([], [path.join(rekitCore.utils.getProjectRoot(), 'src'), path.join(rekitCore.utils.getProjectRoot(), 'coverage')], Date.now() - 10);
|
49 |
|
50 |
|
51 |
|
52 | wp.on('aggregated', (changes) => {
|
53 |
|
54 | rekitCore.vio.reset();
|
55 |
|
56 |
|
57 |
|
58 | if (io) io.emit('fileChanged', changes);
|
59 | });
|
60 |
|
61 | const rootPath = '/rekit';
|
62 |
|
63 | function execCmd(req, res) {
|
64 | try {
|
65 | const args = req.body;
|
66 | rekitCore.handleCommand(args);
|
67 | const logs = rekitCore.vio.flush();
|
68 | rekitCore.vio.reset();
|
69 | res.write(JSON.stringify({
|
70 | args,
|
71 | logs,
|
72 | }));
|
73 | res.end();
|
74 | } catch (e) {
|
75 | res.statusCode = 500;
|
76 | res.write(e.toString());
|
77 | res.end();
|
78 | }
|
79 | }
|
80 |
|
81 | function setupSocketIo(server) {
|
82 | io = require('socket.io')(server);
|
83 |
|
84 | io.on('connection', (client) => {
|
85 | client.on('disconnect', () => {
|
86 | console.log('socket disconnected');
|
87 | });
|
88 | });
|
89 | }
|
90 |
|
91 | function reply403(res) {
|
92 | res.statusCode = 403;
|
93 | res.write('Forbidden: Rekit portal is running on readonly mode.');
|
94 | res.end();
|
95 | }
|
96 |
|
97 | function rekitMiddleware(server, app, args) {
|
98 | args = args || {};
|
99 | setupSocketIo(server);
|
100 | const prjRoot = rekitCore.utils.getProjectRoot();
|
101 | app.use('/coverage', express.static(utils.joinPath(prjRoot, 'coverage'), { fallthrough: false }));
|
102 | app.use(bodyParser.json());
|
103 | app.use(bodyParser.urlencoded({ extended: true }));
|
104 |
|
105 | return (req, res, next) => {
|
106 | const urlObject = url.parse(req.originalUrl);
|
107 | const p = urlObject.pathname.replace(rootPath, '');
|
108 |
|
109 | try {
|
110 | switch (p) {
|
111 | case '/api/project-data': {
|
112 | lastProjectData = fetchProjectData();
|
113 | res.write(JSON.stringify(Object.assign({
|
114 | bgProcesses,
|
115 | }, lastProjectData)));
|
116 | res.end();
|
117 | break;
|
118 | }
|
119 | case '/api/format-code': {
|
120 | const content = req.body.content;
|
121 | const ext = req.body.ext;
|
122 | const options = {
|
123 | singleQuote: true,
|
124 | trailingComma: 'es5',
|
125 | printWidth: 120,
|
126 | };
|
127 | if (ext === 'less' || ext === 'scss') options.parser = ext;
|
128 | try {
|
129 | res.write(JSON.stringify({ content: prettier.format(content, options) }));
|
130 | } catch (err) {
|
131 | res.write(JSON.stringify({ content, error: err }));
|
132 | }
|
133 | res.end();
|
134 | break;
|
135 | }
|
136 | case '/api/save-file': {
|
137 | if (args.readonly) { reply403(res); break; }
|
138 | const absPath = utils.joinPath(prjRoot, req.body.file);
|
139 | if (!_.startsWith(absPath, prjRoot)) {
|
140 |
|
141 | res.statusCode = 403;
|
142 | res.write('Forbidden: not allowed to access file out of the project.');
|
143 | res.end();
|
144 | break;
|
145 | }
|
146 |
|
147 | if (!fs.existsSync(absPath)) {
|
148 | res.statusCode = 404;
|
149 | res.write(JSON.stringify({ error: 'Not found.' }));
|
150 | res.end();
|
151 | } else {
|
152 | try {
|
153 | saveFile(absPath, req.body.content);
|
154 | res.write(JSON.stringify({ success: true }));
|
155 | } catch (err) {
|
156 | res.write(JSON.stringify({ error: err }));
|
157 | }
|
158 | res.end();
|
159 | }
|
160 | break;
|
161 | }
|
162 | case '/api/file-content': {
|
163 | const absPath = utils.joinPath(prjRoot, req.query.file);
|
164 | if (!_.startsWith(absPath, prjRoot)) {
|
165 |
|
166 | res.statusCode = 403;
|
167 | res.write('Forbidden: not allowed to access file out of the project.');
|
168 | res.end();
|
169 | break;
|
170 | }
|
171 |
|
172 | if (!fs.existsSync(absPath)) {
|
173 | res.statusCode = 404;
|
174 | res.write(JSON.stringify({ error: 'Not found.' }));
|
175 | res.end();
|
176 | } else {
|
177 | res.write(JSON.stringify({ content: getFileContent(absPath) }));
|
178 | res.end();
|
179 | }
|
180 | break;
|
181 | }
|
182 | case '/api/exec-cmd':
|
183 | if (args.readonly) { reply403(res); break; }
|
184 | execCmd(req, res);
|
185 | break;
|
186 | case '/api/run-build':
|
187 | if (args.readonly) { reply403(res); break; }
|
188 | if (bgProcesses.runningBuild) {
|
189 | res.statusCode = 500;
|
190 | res.write(JSON.stringify({ error: 'Build process is running...' }));
|
191 | res.end();
|
192 | } else {
|
193 | bgProcesses.runningBuild = true;
|
194 | runBuild(io).then(() => {
|
195 | bgProcesses.runningBuild = false;
|
196 | }).catch(() => {
|
197 | bgProcesses.runningBuild = false;
|
198 | });
|
199 | res.write('{}');
|
200 | res.end();
|
201 | }
|
202 | break;
|
203 | case '/api/run-test':
|
204 | if (args.readonly) { reply403(res); break; }
|
205 | if (bgProcesses.runningTest) {
|
206 | res.statusCode = 500;
|
207 | res.write(JSON.stringify({ error: 'Test process is running...' }));
|
208 | res.end();
|
209 | } else {
|
210 | bgProcesses.runningTest = true;
|
211 | runTest(io, req.body.testFile || '').then(() => {
|
212 | bgProcesses.runningTest = false;
|
213 | }).catch(() => {
|
214 | bgProcesses.runningTest = false;
|
215 | });
|
216 | res.write('{}');
|
217 | res.end();
|
218 | }
|
219 | break;
|
220 | default: {
|
221 | if (/^\/api\//.test(p)) {
|
222 | res.statusCode = 404;
|
223 | res.write(JSON.stringify({ error: `API not found: ${p}` }));
|
224 | res.end();
|
225 | } else {
|
226 | next();
|
227 | }
|
228 | break;
|
229 | }
|
230 | }
|
231 | } catch (e) {
|
232 | res.statusCode = 500;
|
233 | res.write(e.toString());
|
234 | res.end();
|
235 | }
|
236 | };
|
237 | }
|
238 | return rekitMiddleware;
|
239 | };
|