server: add base path management (closes #290589)

update url path asset so that it is possible to serve mosaic from demo.logilab.fr/mosaic

authorAdrien Di Mascio <Adrien.DiMascio@logilab.fr>
changeset9e2ae8a1a773
branchdefault
phasepublic
hiddenno
parent revision#0ad9e966770f Added tag mosaic-version-0.1.0 for changeset 331ceaee160f
child revision#8df4e3324b4d docker: install mercurial-evolve and vim in the container, #f9bb1f44b2dc docker: install mercurial-evolve and vim in the container
files modified by this revision
app/portfolio.js
mosaicserver/config.js
mosaicserver/lib.js
mosaicserver/package.json
mosaicserver/prod_server.js
mosaicserver/routes.js
mosaicserver/test/test_lib.js
mosaicserver/views/layout.jade
portfolios/_template.html
# HG changeset patch
# User Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
# Date 1428662426 -7200
# Fri Apr 10 12:40:26 2015 +0200
# Node ID 9e2ae8a1a773410da12233bc4c438d9fc629fa6f
# Parent 0ad9e966770f9610ffe8f227e03d7a0b31f284c0
server: add base path management (closes #290589)

update url path asset so that it is possible to serve mosaic from
demo.logilab.fr/mosaic

diff --git a/app/portfolio.js b/app/portfolio.js
@@ -28,11 +28,12 @@
1              url: 'saveportfolio',
2              dataType: 'html',
3              type: 'POST',
4              data: {
5                  content: content,
6 -                name: window.location.pathname.replace(/\/p\//, '')
7 +                // keep basename only
8 +                name: /[^\/]+$/.exec(window.location.pathname)[0]
9              }
10          }).done(function(data, xhr, status) {
11              button.disabled = false;
12          }).fail(function(xhr, err, msg) {
13              console.log('could not save document', err, msg);
diff --git a/mosaicserver/config.js b/mosaicserver/config.js
@@ -0,0 +1,12 @@
14 +'use strict';
15 +
16 +module.exports = {
17 +    port: 8080,
18 +    basePath: '',
19 +
20 +    set: function set(key, value) {
21 +        if (value !== undefined) {
22 +            this[key] = value;
23 +        }
24 +    }
25 +};
diff --git a/mosaicserver/lib.js b/mosaicserver/lib.js
@@ -0,0 +1,15 @@
26 +// lib module
27 +
28 +// contains utility functions for mosaicserver
29 +
30 +'use strict';
31 +
32 +function instantiateTemplate(content, context) {
33 +    // substitute {{var}} in `content` with corresponding
34 +    // value in `context`
35 +    return content.replace(/\{\{(.+?)\}\}/g, function($0, $1) {
36 +        return $1 in context ? context[$1] : $0;
37 +    });
38 +}
39 +
40 +exports.instantiateTemplate = instantiateTemplate;
diff --git a/mosaicserver/package.json b/mosaicserver/package.json
@@ -1,11 +1,11 @@
41  {
42    "name": "mosaicserver",
43    "version": "0.1.0",
44    "private": true,
45    "scripts": {
46 -          "start": "NODE_ENV=production node prod_server.js -p 3333"
47 +      "start": "NODE_ENV=production node prod_server.js -p 3333 --base-path /mosaic"
48    },
49    "dependencies": {
50      "body-parser": "^1.12.2",
51      "express": "~4.12.2",
52      "commander": "^2.7.1",
diff --git a/mosaicserver/prod_server.js b/mosaicserver/prod_server.js
@@ -2,26 +2,26 @@
53 
54 
55  var app = require('./app');
56  var http = require('http');
57  var program = require('commander');
58 -
59 -var defaultOpts = {
60 -    port: 8080
61 -};
62 +var config = require('./config');
63 
64  program
65      .version(require('../package.json').version)
66      .usage('[options]')
67      .option('-p, --port <int>',
68              'port number to start the server on (default to ' +
69 -            defaultOpts.port + ')');
70 +            config.port + ')')
71 +    .option('--base-path <base-path>',
72 +            'relative path from hostname (default to "")');
73 
74  program.parse(process.argv);
75 
76  var server = http.createServer(app);
77 
78 -app.set('port', program.port || defaultOpts.port);
79 +config.set('port', program.port);
80 +config.set('basePath', program.basePath);
81 
82 -server.listen(app.get('port'), function() {
83 -    console.log('server started on port', app.get('port'));
84 +server.listen(config.port, function() {
85 +    console.log('server started on port', config.port);
86  });
diff --git a/mosaicserver/routes.js b/mosaicserver/routes.js
@@ -3,10 +3,13 @@
87  var path = require('path');
88 
89  var express = require('express');
90  var router = express.Router();
91 
92 +var config = require('./config'),
93 +    lib = require('./lib');
94 +
95 
96  /* GET home page. */
97  router.get('/', function(req, res, next) {
98      res.render('index', {
99          title: 'Mosaic Server'
@@ -24,29 +27,32 @@
100              res.status(200).send('ok');
101          }
102      });
103  });
104 
105 +var templateContent = fs.readFileSync(path.join(__dirname, '/../portfolios/_template.html')).toString();
106 +
107  router.post('/newportfolio', function(req, res, next) {
108      var slug = req.body.name.replace(/[ \/]/g, '_'), // XXX sanitize
109          filename = path.join(__dirname, '/../portfolios/' + slug + '.html'),
110          template = path.join(__dirname, '/../portfolios/_template.html');
111 
112      fs.exists(filename, function(exists) {
113          if (exists) {
114 -            res.redirect('/' + slug);
115 +            res.redirect(slug);
116          } else {
117 -            var istream = fs.createReadStream(template),
118 -                ostream = fs.createWriteStream(filename);
119 -            ostream.on('finish', function() {
120 -                res.redirect('/' + slug);
121 +            var context = {basePath: config.basePath,
122 +                           name: slug},
123 +                content = lib.instantiateTemplate(templateContent, context);
124 +            fs.writeFile(filename, content, function(err) {
125 +                if (err) {
126 +                    res.status(500).send('could not create resource');
127 +                    next();
128 +                } else {
129 +                    res.redirect(slug);
130 +                }
131              });
132 -            ostream.on('error', function () {
133 -                res.status(500).send('could not create resource');
134 -                next();
135 -            });
136 -            istream.pipe(ostream);
137          }
138      });
139  });
140 
141  router.get('/:slug', function(req, res, next) {
diff --git a/mosaicserver/test/test_lib.js b/mosaicserver/test/test_lib.js
@@ -0,0 +1,33 @@
142 +/* global describe, it */
143 +'use strict';
144 +
145 +var assert = require('assert');
146 +var lib = require('../lib');
147 +
148 +describe('lib module tests', function () {
149 +
150 +    it('should substitute variables', function () {
151 +        var result = lib.instantiateTemplate('{{foo}}', {foo: 'hello'});
152 +        assert.strictEqual(result, 'hello');
153 +    });
154 +
155 +    it('should substitute same variable multiple values', function () {
156 +        var result = lib.instantiateTemplate('{{foo}} - {{foo}}',
157 +                                             {foo: 'hello'});
158 +        assert.strictEqual(result, 'hello - hello');
159 +    });
160 +
161 +    it('should substitute multiple variables', function () {
162 +        var result = lib.instantiateTemplate('{{foo}} {{bar}}',
163 +                                             {foo: 'hello',
164 +                                              bar: 'world'});
165 +        assert.strictEqual(result, 'hello world');
166 +    });
167 +
168 +    it('should ignore unknown variables', function () {
169 +        var result = lib.instantiateTemplate('{{foo}} {{bar}}',
170 +                                             {foo: 'hello'});
171 +        assert.strictEqual(result, 'hello {{bar}}');
172 +    });
173 +
174 +});
diff --git a/mosaicserver/views/layout.jade b/mosaicserver/views/layout.jade
@@ -1,10 +1,10 @@
175  doctype html
176  html
177    head
178      title= title
179 -    link(rel='stylesheet', href='/vendor.css')
180 +    link(rel='stylesheet', href='vendor.css')
181    body
182      div(class='container')
183        block content
184 -    script(src='/vendor.js')
185 +    script(src='vendor.js')
186 
diff --git a/portfolios/_template.html b/portfolios/_template.html
@@ -1,27 +1,27 @@
187  <!doctype html>
188  <html lang="en">
189    <head>
190      <meta charset="UTF-8"/>
191      <title>Portfolio {{name}}</title>
192 -    <script src="/babel-browser-polyfill.js"></script>
193 -    <link rel="stylesheet" href="/vendor.css"/>
194 -    <link rel="stylesheet" href="/app.css"/>
195 -    <script src="/vendor.js"></script>
196 +    <script src="{{basePath}}/babel-browser-polyfill.js"></script>
197 +    <link rel="stylesheet" href="{{basePath}}/vendor.css"/>
198 +    <link rel="stylesheet" href="{{basePath}}/app.css"/>
199 +    <script src="{{basePath}}/vendor.js"></script>
200      <!--
201          app.js contains require and registry module definition,
202          it must be inserted _before_ any plugins
203        -->
204 -    <script src="/app.js"></script>
205 -    <link rel="import" href="/components/xmosaicframe/xmosaicframe.html" />
206 -    <link rel="import" href="/components/neotableview/neotableview.html" />
207 -    <link rel="import" href="/components/neolistview/neolistview.html" />
208 -    <link rel="import" href="/components/sparqltableview/sparqltableview.html" />
209 -    <link rel="import" href="/components/cwtableview/cwtableview.html" />
210 -    <link rel="import" href="/components/markdown-editable/markdown-editable.html" />
211 -    <link rel="import" href="/components/pdfview/pdfview.html" />
212 -    <link rel="import" href="/components/plainview/plainview.html" />
213 +    <script src="{{basePath}}/app.js"></script>
214 +    <link rel="import" href="{{basePath}}/components/xmosaicframe/xmosaicframe.html" />
215 +    <link rel="import" href="{{basePath}}/components/neotableview/neotableview.html" />
216 +    <link rel="import" href="{{basePath}}/components/neolistview/neolistview.html" />
217 +    <link rel="import" href="{{basePath}}/components/sparqltableview/sparqltableview.html" />
218 +    <link rel="import" href="{{basePath}}/components/cwtableview/cwtableview.html" />
219 +    <link rel="import" href="{{basePath}}/components/markdown-editable/markdown-editable.html" />
220 +    <link rel="import" href="{{basePath}}/components/pdfview/pdfview.html" />
221 +    <link rel="import" href="{{basePath}}/components/plainview/plainview.html" />
222    </head>
223    <body>
224      <div class="container">
225        <x-markdown-editable>###Edit me</x-markdown-editable>
226