sandboxApp.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /* global __dirname */
  2. /* jshint strict:false */
  3. "use strict";
  4. var express = require('express');
  5. var bodyParser = require('body-parser');
  6. var slug = require('slug');
  7. var beautify = require('json-beautify');
  8. var _ = require('lodash');
  9. var path = require('path');
  10. var Mustache = require('mustache');
  11. var app = express();
  12. var fs = require('fs');
  13. var Promise = require('bluebird'); Promise.promisifyAll(fs);
  14. var sandboxTpml = fs.readFileSync(__dirname + '/html/template.mustache.html').toString();
  15. var exampleTmpl = require('./lib/exampleTmpl.json');
  16. var repoTmpl = require('./lib/repoTmpl.json');
  17. var ExampleStore = require('./lib/ExampleStore');
  18. var isTesting = process.env.NODE_ENV === 'testing';
  19. var examplesDir = ! isTesting ? __dirname + '/exemples' :
  20. __dirname + '/test/integration/test-examples';
  21. var exStore = new ExampleStore(examplesDir);
  22. var {
  23. readFileAsync,
  24. readFilesAsync
  25. } = require('./lib/fsio');
  26. var {
  27. getAcceptLanguage,
  28. getIndexBare,
  29. // getIndexTest,
  30. getIndexRepo,
  31. getIndexExample
  32. } = require('./lib/indexHandlers')(exStore, examplesDir, isTesting);
  33. var {
  34. // getIndexBare,
  35. getPartsRepo,
  36. getPartsExample
  37. } = require('./lib/partHandlers')(exStore, examplesDir);
  38. /**
  39. * Initialize example store
  40. */
  41. exStore.init();
  42. // .then(() => console.log(exStore.getMenu()));
  43. /**
  44. * Initialize Express app:
  45. * - root folder as static
  46. * - body parsers
  47. * - browser language detection middleware
  48. */
  49. app.use(express.static(__dirname));
  50. app.use(bodyParser.json());
  51. app.use(bodyParser.urlencoded({ extended: true }));
  52. app.use(getAcceptLanguage);
  53. function addExample(slug, title) {
  54. return fs.writeFileAsync(examplesJSON, beautify(examples, null, 2, 100));
  55. }
  56. function readConfigJson(exampleSlug) {
  57. console.log(exampleSlug);
  58. return require('./exemples/jquery/' + exampleSlug + '/config.json');
  59. }
  60. function mapObjToArray(obj, key, value) {
  61. var arr = [];
  62. for(var p in obj) {
  63. arr.push({
  64. [key]: p,
  65. [value]: obj[p]
  66. });
  67. }
  68. return arr;
  69. }
  70. /**
  71. * Get repo parts
  72. */
  73. app.get('/parts/:repoSlug', getPartsRepo);
  74. /**
  75. * Get example parts
  76. */
  77. app.get('/parts/:repoSlug/:exampleSlug', getPartsExample);
  78. /**
  79. * Index page: render with only repo list in menu
  80. */
  81. app.get('/', getIndexBare);
  82. /**
  83. * Index page: render for tests
  84. */
  85. // if(isTesting) {
  86. // app.get('/test', getIndexTest);
  87. // }
  88. /**
  89. * Repo page: render with repo list and selected repo's example list in menu
  90. */
  91. app.get('/:repoSlug', getIndexRepo);
  92. /**
  93. * Example page: render with repo list and selected repo's example list in menu,
  94. * and the editor with the selected example
  95. */
  96. app.get('/:repoSlug/:exampleSlug', getIndexExample);
  97. function checkRepoExists(req, res, next) {
  98. const { repoSlug } = req.params;
  99. console.log('### checkRepoExists', repoSlug);
  100. // Get repo from store
  101. req.repo = exStore.getRepo(repoSlug);
  102. if(! req.repo) {
  103. res.status(404).send("Repo " + repoSlug + "not found");
  104. }
  105. next();
  106. }
  107. /**
  108. * Create a new repo
  109. */
  110. app.post('/repos', function(req, res) {
  111. // Check for title and extract params
  112. if(! req.body || ! req.body.title) {
  113. res.status(400).send('Le titre ne peut pas être vide !');
  114. }
  115. const { title } = req.body;
  116. const repoSlug = slug(title.toLowerCase());
  117. const existingRepo = exStore.getRepo(repoSlug);
  118. // Prevent duplicate title
  119. if(existingRepo) {
  120. return res.status(409).send("La collection '" + title + "' existe déjà !");
  121. }
  122. // Prepare config
  123. var config = Object.assign({
  124. title
  125. }, repoTmpl);
  126. exStore.addRepository(repoSlug, config)
  127. .then(repo => res.json(repo));
  128. });
  129. /**
  130. * Create a new example for specified repo
  131. */
  132. app.post('/:repoSlug/examples',
  133. checkRepoExists,
  134. function(req, res) {
  135. // Check for title and extract params
  136. if(! req.body || ! req.body.title) {
  137. res.status(400).send('Le titre ne peut pas être vide !');
  138. }
  139. const { title } = req.body;
  140. const { repoSlug } = req.params;
  141. // Prevent duplicate title
  142. var existingTitle = _.find(req.repo.examples, { title: title });
  143. if(existingTitle) {
  144. return res.status(409).send("L'exemple '" + title + "' existe déjà !");
  145. }
  146. var exampleSlug = slug(req.body.title.toLowerCase());
  147. // Prepare config
  148. var config = Object.assign({
  149. slug: exampleSlug,
  150. title,
  151. category: req.repo.defaultCategory
  152. }, exampleTmpl);
  153. // Prepare files to write
  154. var targetDir = __dirname + '/exemples/' + repoSlug + '/' + exampleSlug;
  155. var files = mapObjToArray({
  156. 'example.html': '<!-- ' + title + '-->\n',
  157. 'script.js': '// ' + title,
  158. 'config.json': beautify(config, null, 2, 100)
  159. }, 'file', 'content');
  160. fs.mkdirAsync(targetDir)
  161. .then(() => Promise.map(
  162. files, ({ file, content }) => fs.writeFileAsync(targetDir + '/' + file, content)
  163. ))
  164. .then(files => req.repo.examples.push(config))
  165. .then(() => res.json(config));
  166. }
  167. );
  168. app.get('/examples/:repoSlug/:slug',
  169. checkRepoExists,
  170. function(req, res) {
  171. const { repoSlug, slug } = req.params;
  172. var example = _.find(req.repo.examples, { slug });
  173. const { title, html, js, css, libsCss, libsJs } = example;
  174. console.log(example, title, html, js, css, libsCss, libsJs);
  175. readFileAsync(__dirname + '/exemples/' + repoSlug + '/' + slug + '/example.html')
  176. .then(body =>
  177. Mustache.render(sandboxTpml, { body, repoSlug, slug, title, js, css, libsCss, libsJs })
  178. )
  179. .then(html => res.send(html));
  180. }
  181. );
  182. app.get('/menu', (rea, res) => {
  183. res.send(exStore.getMenu());
  184. });
  185. app.get('/list/:repoPath', function(req, res) {
  186. const { repoPath } = req.params;
  187. const repo = exStore.getList(repoPath);
  188. if(! repo) {
  189. return res.status(404).send('Repo ' + repoPath + ' not found');
  190. }
  191. console.log('found repo', repo);
  192. const data = repo.examples.map(e => (
  193. { slug: e.slug, title: e.title }
  194. ));
  195. res.json(data);
  196. });
  197. app.put('/examples/:slug', function(req, res) {
  198. var slug = req.params.slug;
  199. var existing = _.find(examples, { slug: slug });
  200. if(! existing) {
  201. res.status(404).send("L'exemple avec l'identifiant '" + slug + "' est introuvable !");
  202. }
  203. var targetDir = __dirname + '/exemples/' + slug;
  204. if(req.body.html) {
  205. fs.writeFileSync(targetDir + '/contenu.html', req.body.html);
  206. }
  207. if(req.body.javascript) {
  208. fs.writeFileSync(targetDir + '/script.js', req.body.javascript);
  209. }
  210. var theDate = new Date();
  211. console.log(theDate.getHours() + ':' + theDate.getMinutes() + " - Sauvegarde de l'exemple '" + existing.title + " effectuée'");
  212. res.json({ success: true });
  213. });
  214. module.exports = app;