From bd2f7cef495b87050ec3f126e7425095078440b2 Mon Sep 17 00:00:00 2001
From: WilliButz <wbutz@cyberfnord.de>
Date: Wed, 5 Sep 2018 19:49:01 +0200
Subject: [PATCH 1/7] lib/models/revision.js: make independent of exec-path

Previously calling `app.js` from another directory than
the base directory of CodiMD would result in an error being
thrown because `lib/workers/dmpWorker.js` could not be found.

This change makes the function call independent of the path CodiMD
is started from.

Signed-off-by: WilliButz <wbutz@cyberfnord.de>
---
 lib/models/revision.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/models/revision.js b/lib/models/revision.js
index 8bc95cb..4ee080d 100644
--- a/lib/models/revision.js
+++ b/lib/models/revision.js
@@ -5,6 +5,7 @@ var async = require('async')
 var moment = require('moment')
 var childProcess = require('child_process')
 var shortId = require('shortid')
+var path = require('path')
 
 // core
 var config = require('../config')
@@ -14,7 +15,7 @@ var dmpWorker = createDmpWorker()
 var dmpCallbackCache = {}
 
 function createDmpWorker () {
-  var worker = childProcess.fork('./lib/workers/dmpWorker.js', {
+  var worker = childProcess.fork(path.resolve(__dirname, '../workers/dmpWorker.js'), {
     stdio: 'ignore'
   })
   if (config.debug) logger.info('dmp worker process started')

From e48852e0e2de4441b5ef84dc9bb16be8ba9c01d8 Mon Sep 17 00:00:00 2001
From: WilliButz <wbutz@cyberfnord.de>
Date: Wed, 5 Sep 2018 19:50:46 +0200
Subject: [PATCH 2/7] lib/config: add environment variable to set config file

Previously it was assumed that `config.json` would be placed in
the same directory as the rest of CodiMD without any optional override.

This allows to override the path to the `config.json` by setting
`CMD_CONFIG_FILE` to the canonical path of the desired config file.

Signed-off-by: WilliButz <wbutz@cyberfnord.de>
---
 lib/config/index.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/config/index.js b/lib/config/index.js
index 26f0ae9..76c8bbf 100644
--- a/lib/config/index.js
+++ b/lib/config/index.js
@@ -23,7 +23,8 @@ const packageConfig = {
   minimumCompatibleVersion: '0.5.0'
 }
 
-const configFilePath = path.join(appRootPath, 'config.json')
+const configFilePath = path.resolve(appRootPath, process.env.CMD_CONFIG_FILE ||
+'config.json')
 const fileConfig = fs.existsSync(configFilePath) ? require(configFilePath)[env] : undefined
 
 let config = require('./default')

From 556783ffad2e26c490c0d9d9520d7ee13a750dbb Mon Sep 17 00:00:00 2001
From: WilliButz <wbutz@cyberfnord.de>
Date: Wed, 5 Sep 2018 19:56:41 +0200
Subject: [PATCH 3/7] lib/config: use `path.resolve` instead of `path.join`

While paths like `tmpPath` could previously be configured,
they were all interpreted relative to `appRootPath` because
of `path.join`.

Now the configurable paths can be canonical and therefore
independent of the `appRootPath`.

Signed-off-by: WilliButz <wbutz@cyberfnord.de>
---
 lib/config/index.js | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/lib/config/index.js b/lib/config/index.js
index 76c8bbf..59db861 100644
--- a/lib/config/index.js
+++ b/lib/config/index.js
@@ -174,20 +174,20 @@ config.sslCAPath.forEach(function (capath, i, array) {
   array[i] = path.resolve(appRootPath, capath)
 })
 
-config.sslCertPath = path.join(appRootPath, config.sslCertPath)
-config.sslKeyPath = path.join(appRootPath, config.sslKeyPath)
-config.dhParamPath = path.join(appRootPath, config.dhParamPath)
+config.sslCertPath = path.resolve(appRootPath, config.sslCertPath)
+config.sslKeyPath = path.resolve(appRootPath, config.sslKeyPath)
+config.dhParamPath = path.resolve(appRootPath, config.dhParamPath)
 
-config.tmpPath = path.join(appRootPath, config.tmpPath)
-config.defaultNotePath = path.join(appRootPath, config.defaultNotePath)
-config.docsPath = path.join(appRootPath, config.docsPath)
-config.indexPath = path.join(appRootPath, config.indexPath)
-config.codimdPath = path.join(appRootPath, config.codimdPath)
-config.errorPath = path.join(appRootPath, config.errorPath)
-config.prettyPath = path.join(appRootPath, config.prettyPath)
-config.slidePath = path.join(appRootPath, config.slidePath)
-config.constantsPath = path.join(appRootPath, config.constantsPath)
-config.uploadsPath = path.join(appRootPath, config.uploadsPath)
+config.tmpPath = path.resolve(appRootPath, config.tmpPath)
+config.defaultNotePath = path.resolve(appRootPath, config.defaultNotePath)
+config.docsPath = path.resolve(appRootPath, config.docsPath)
+config.indexPath = path.resolve(appRootPath, config.indexPath)
+config.codimdPath = path.resolve(appRootPath, config.codimdPath)
+config.errorPath = path.resolve(appRootPath, config.errorPath)
+config.prettyPath = path.resolve(appRootPath, config.prettyPath)
+config.slidePath = path.resolve(appRootPath, config.slidePath)
+config.constantsPath = path.resolve(appRootPath, config.constantsPath)
+config.uploadsPath = path.resolve(appRootPath, config.uploadsPath)
 
 // make config readonly
 config = deepFreeze(config)

From 12cd74727065079905cb385119d7a2db35276cca Mon Sep 17 00:00:00 2001
From: WilliButz <wbutz@cyberfnord.de>
Date: Wed, 26 Sep 2018 20:40:28 +0200
Subject: [PATCH 4/7] imageRouter/filesystem: make callback path-independent

Images are now properly served when `config.uploadsPath`
differs from its default value.

Signed-off-by: WilliButz <wbutz@cyberfnord.de>
---
 lib/web/imageRouter/filesystem.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/web/imageRouter/filesystem.js b/lib/web/imageRouter/filesystem.js
index 4bf82b3..8c432b0 100644
--- a/lib/web/imageRouter/filesystem.js
+++ b/lib/web/imageRouter/filesystem.js
@@ -1,5 +1,6 @@
 'use strict'
 const url = require('url')
+const path = require('path')
 
 const config = require('../../config')
 const logger = require('../../logger')
@@ -15,5 +16,5 @@ exports.uploadImage = function (imagePath, callback) {
     return
   }
 
-  callback(null, url.resolve(config.serverURL + '/', imagePath.match(/public\/(.+)$/)[1]))
+  callback(null, url.resolve(config.serverURL + '/uploads/', path.basename(imagePath)))
 }

From 825ee4e66e79271c39631f2b9eb059caa8f17bfc Mon Sep 17 00:00:00 2001
From: WilliButz <wbutz@cyberfnord.de>
Date: Wed, 26 Sep 2018 17:18:33 +0200
Subject: [PATCH 5/7] app.js: add missing routes for configurable paths

Signed-off-by: WilliButz <wbutz@cyberfnord.de>
---
 app.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/app.js b/app.js
index ab37bff..76ff664 100644
--- a/app.js
+++ b/app.js
@@ -126,6 +126,9 @@ app.use(i18n.init)
 // routes without sessions
 // static files
 app.use('/', express.static(path.join(__dirname, '/public'), { maxAge: config.staticCacheTime }))
+app.use('/docs', express.static(path.resolve(__dirname, config.docsPath), { maxAge: config.staticCacheTime }))
+app.use('/uploads', express.static(path.resolve(__dirname, config.uploadsPath), { maxAge: config.staticCacheTime }))
+app.use('/default.md', express.static(path.resolve(__dirname, config.defaultNotePath), { maxAge: config.staticCacheTime }))
 
 // session
 app.use(session({

From bb80bc2292d2841776e8bfd6285f96194d990284 Mon Sep 17 00:00:00 2001
From: Claudius <opensource@amenthes.de>
Date: Mon, 10 Sep 2018 22:35:38 +0200
Subject: [PATCH 6/7] removing superfluous config parameters for template files

Signed-off-by: Claudius <opensource@amenthes.de>
---
 app.js                  |  2 +-
 lib/config/default.js   |  7 +------
 lib/config/index.js     | 10 ++--------
 lib/response.js         | 12 ++++++------
 lib/web/statusRouter.js |  2 +-
 5 files changed, 11 insertions(+), 22 deletions(-)

diff --git a/app.js b/app.js
index 76ff664..d6d607b 100644
--- a/app.js
+++ b/app.js
@@ -170,7 +170,7 @@ app.use(require('./lib/web/middleware/codiMDVersion'))
 
 // routes need sessions
 // template files
-app.set('views', path.join(__dirname, '/public/views'))
+app.set('views', config.viewPath)
 // set render engine
 app.engine('ejs', ejs.renderFile)
 // set view engine
diff --git a/lib/config/default.js b/lib/config/default.js
index 6096bce..5d10ad2 100644
--- a/lib/config/default.js
+++ b/lib/config/default.js
@@ -38,15 +38,10 @@ module.exports = {
   sslCAPath: '',
   dhParamPath: '',
   // other path
+  viewPath: './public/views',
   tmpPath: './tmp',
   defaultNotePath: './public/default.md',
   docsPath: './public/docs',
-  indexPath: './public/views/index.ejs',
-  codimdPath: './public/views/codimd.ejs',
-  errorPath: './public/views/error.ejs',
-  prettyPath: './public/views/pretty.ejs',
-  slidePath: './public/views/slide.ejs',
-  constantsPath: './public/js/lib/common/constant.ejs',
   uploadsPath: './public/uploads',
   // session
   sessionName: 'connect.sid',
diff --git a/lib/config/index.js b/lib/config/index.js
index 59db861..7d059c5 100644
--- a/lib/config/index.js
+++ b/lib/config/index.js
@@ -9,7 +9,7 @@ const deepFreeze = require('deep-freeze')
 const {Environment, Permission} = require('./enum')
 const logger = require('../logger')
 
-const appRootPath = path.join(__dirname, '../../')
+const appRootPath = path.resolve(__dirname, '../../')
 const env = process.env.NODE_ENV || Environment.development
 const debugConfig = {
   debug: (env === Environment.development)
@@ -177,16 +177,10 @@ config.sslCAPath.forEach(function (capath, i, array) {
 config.sslCertPath = path.resolve(appRootPath, config.sslCertPath)
 config.sslKeyPath = path.resolve(appRootPath, config.sslKeyPath)
 config.dhParamPath = path.resolve(appRootPath, config.dhParamPath)
-
+config.viewPath = path.resolve(appRootPath, config.viewPath)
 config.tmpPath = path.resolve(appRootPath, config.tmpPath)
 config.defaultNotePath = path.resolve(appRootPath, config.defaultNotePath)
 config.docsPath = path.resolve(appRootPath, config.docsPath)
-config.indexPath = path.resolve(appRootPath, config.indexPath)
-config.codimdPath = path.resolve(appRootPath, config.codimdPath)
-config.errorPath = path.resolve(appRootPath, config.errorPath)
-config.prettyPath = path.resolve(appRootPath, config.prettyPath)
-config.slidePath = path.resolve(appRootPath, config.slidePath)
-config.constantsPath = path.resolve(appRootPath, config.constantsPath)
 config.uploadsPath = path.resolve(appRootPath, config.uploadsPath)
 
 // make config readonly
diff --git a/lib/response.js b/lib/response.js
index 295f91d..8862304 100644
--- a/lib/response.js
+++ b/lib/response.js
@@ -51,7 +51,7 @@ var response = {
 }
 
 function responseError (res, code, detail, msg) {
-  res.status(code).render(config.errorPath, {
+  res.status(code).render('error.ejs', {
     url: config.serverURL,
     title: code + ' ' + detail + ' ' + msg,
     code: code,
@@ -101,11 +101,11 @@ function showIndex (req, res, next) {
     }).then(function (user) {
       if (user) {
         data.deleteToken = user.deleteToken
-        res.render(config.indexPath, data)
+        res.render('index.ejs', data)
       }
     })
   } else {
-    res.render(config.indexPath, data)
+    res.render('index.ejs', data)
   }
 }
 
@@ -119,7 +119,7 @@ function responseCodiMD (res, note) {
     'Cache-Control': 'private', // only cache by client
     'X-Robots-Tag': 'noindex, nofollow' // prevent crawling
   })
-  res.render(config.codimdPath, {
+  res.render('codimd.ejs', {
     url: config.serverURL,
     title: title,
     useCDN: config.useCDN,
@@ -275,7 +275,7 @@ function renderPublish (data, res) {
   res.set({
     'Cache-Control': 'private' // only cache by client
   })
-  res.render(config.prettyPath, data)
+  res.render('pretty.ejs', data)
 }
 
 function actionPublish (req, res, note) {
@@ -657,7 +657,7 @@ function renderPublishSlide (data, res) {
   res.set({
     'Cache-Control': 'private' // only cache by client
   })
-  res.render(config.slidePath, data)
+  res.render('slide.ejs', data)
 }
 
 module.exports = response
diff --git a/lib/web/statusRouter.js b/lib/web/statusRouter.js
index 7ecf383..fb2609e 100644
--- a/lib/web/statusRouter.js
+++ b/lib/web/statusRouter.js
@@ -105,5 +105,5 @@ statusRouter.get('/config', function (req, res) {
     'X-Robots-Tag': 'noindex, nofollow', // prevent crawling
     'Content-Type': 'application/javascript'
   })
-  res.render(config.constantsPath, data)
+  res.render('../js/lib/common/constant.ejs', data)
 })

From 61e240192e3ab7e02cb59115e10343da03f18951 Mon Sep 17 00:00:00 2001
From: WilliButz <wbutz@cyberfnord.de>
Date: Thu, 27 Sep 2018 12:08:29 +0200
Subject: [PATCH 7/7] README: add note about configurable paths

Signed-off-by: WilliButz <wbutz@cyberfnord.de>
---
 README.md | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/README.md b/README.md
index ecdc8c7..e111b4c 100644
--- a/README.md
+++ b/README.md
@@ -174,6 +174,7 @@ There are some config settings you need to change in the files below.
 | --------- | ------ | ----------- |
 | `NODE_ENV`  | `production` or `development` | set current environment (will apply corresponding settings in the `config.json`) |
 | `DEBUG` | `true` or `false` | set debug mode; show more logs |
+| `CMD_CONFIG_FILE` | `/path/to/config.json` | optional override for the path to CodiMD's config file |
 | `CMD_DOMAIN` | `codimd.org` | domain name |
 | `CMD_URL_PATH` | `codimd` | sub URL path, like `www.example.com/<URL_PATH>` |
 | `CMD_HOST` | `localhost` | host to listen on |
@@ -277,19 +278,15 @@ There are some config settings you need to change in the files below.
 | `defaultPermission` | `freely`, `editable`, `limited`, `locked`, `protected` or `private` | set notes default permission (only applied on signed users) |
 | `dbURL` | `mysql://localhost:3306/database` | set the db URL; if set, then db config (below) won't be applied |
 | `db` | `{ "dialect": "sqlite", "storage": "./db.codimd.sqlite" }` | set the db configs, [see more here](http://sequelize.readthedocs.org/en/latest/api/sequelize/) |
-| `sslKeyPath` | `./cert/client.key` | SSL key path (only need when you set `useSSL`) |
-| `sslCertPath` | `./cert/codimd_io.crt` | SSL cert path (only need when you set `useSSL`) |
-| `sslCAPath` | `['./cert/COMODORSAAddTrustCA.crt']` | SSL ca chain (only need when you set `useSSL`) |
-| `dhParamPath` | `./cert/dhparam.pem` | SSL dhparam path (only need when you set `useSSL`) |
-| `tmpPath` | `./tmp/` | temp directory path |
-| `defaultNotePath` | `./public/default.md` | default note file path |
-| `docsPath` | `./public/docs` | docs directory path |
-| `indexPath` | `./public/views/index.ejs` | index template file path |
-| `hackmdPath` | `./public/views/hackmd.ejs` | hackmd template file path |
-| `errorPath` | `./public/views/error.ejs` | error template file path |
-| `prettyPath` | `./public/views/pretty.ejs` | pretty template file path |
-| `slidePath` | `./public/views/slide.hbs` | slide template file path |
-| `uploadsPath` | `./public/uploads` | uploads directory - needs to be persistent when you use imageUploadType `filesystem` |
+| `sslKeyPath` | `./cert/client.key` | SSL key path<sup>1</sup> (only need when you set `useSSL`) |
+| `sslCertPath` | `./cert/codimd_io.crt` | SSL cert path<sup>1</sup> (only need when you set `useSSL`) |
+| `sslCAPath` | `['./cert/COMODORSAAddTrustCA.crt']` | SSL ca chain<sup>1</sup> (only need when you set `useSSL`) |
+| `dhParamPath` | `./cert/dhparam.pem` | SSL dhparam path<sup>1</sup> (only need when you set `useSSL`) |
+| `tmpPath` | `./tmp/` | temp directory path<sup>1</sup> |
+| `defaultNotePath` | `./public/default.md` | default note file path<sup>1</sup> |
+| `docsPath` | `./public/docs` | docs directory path<sup>1</sup> |
+| `viewPath` | `./public/views` | template directory path<sup>1</sup> |
+| `uploadsPath` | `./public/uploads` | uploads directory<sup>1</sup> - needs to be persistent when you use imageUploadType `filesystem` |
 | `sessionName` | `connect.sid` | cookie session name |
 | `sessionSecret` | `secret` | cookie session secret |
 | `sessionLife` | `14 * 24 * 60 * 60 * 1000` | cookie session life |
@@ -305,6 +302,8 @@ There are some config settings you need to change in the files below.
 | `s3` | `{ "accessKeyId": "YOUR_S3_ACCESS_KEY_ID", "secretAccessKey": "YOUR_S3_ACCESS_KEY", "region": "YOUR_S3_REGION" }` | When `imageuploadtype` be set to `s3`, you would also need to setup this key, check our [S3 Image Upload Guide](docs/guides/s3-image-upload.md) |
 | `s3bucket` | `YOUR_S3_BUCKET_NAME` | bucket name when `imageUploadType` is set to `s3` or `minio` |
 
+<sup>1</sup>: relative paths are based on CodiMD's base directory
+
 ## Third-party integration API key settings
 
 | service | settings location | description |