expressa 2.0.18 → 2.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.circleci/config.yml +21 -21
- package/.eslintrc.json +29 -29
- package/LICENSE +21 -21
- package/README.md +137 -137
- package/auth/index.js +76 -76
- package/auth/jwt.js +31 -31
- package/controllers/collections.js +260 -260
- package/controllers/install.js +105 -105
- package/controllers/status.js +43 -43
- package/controllers/users.js +52 -52
- package/cypress/fixtures/example.json +4 -4
- package/cypress/integration/0_install_spec.js +8 -8
- package/cypress/integration/custom_collections.js +30 -28
- package/cypress/integration/home.js +9 -9
- package/cypress/integration/settings.js +34 -34
- package/cypress/integration/users.js +61 -61
- package/cypress/plugins/index.js +17 -17
- package/cypress/support/commands.js +69 -54
- package/cypress/support/index.js +20 -20
- package/cypress/support/util.js +10 -10
- package/cypress.config.js +18 -0
- package/cypress.json +4 -4
- package/db/cached.js +40 -40
- package/db/file.js +112 -112
- package/db/memory.js +79 -79
- package/db/mongo.js +86 -86
- package/db/postgres.js +93 -93
- package/doc/authentication.md +38 -38
- package/doc/automatic-fields.md +12 -12
- package/doc/blogexample.md +16 -16
- package/doc/custom-endpoints.md +20 -20
- package/doc/database.md +23 -23
- package/doc/development.md +33 -33
- package/doc/listeners.md +103 -103
- package/doc/modules.md +8 -8
- package/doc/permissions.md +14 -14
- package/doc/relationships.md +65 -65
- package/doc/testing.md +47 -47
- package/doc/uploading-files.md +53 -53
- package/index.js +297 -297
- package/listeners.js +65 -65
- package/listeners_collection_permissions.js +29 -29
- package/listeners_users.js +96 -96
- package/listeners_validation.js +43 -43
- package/middleware/logging.js +30 -30
- package/middleware/permissions.js +41 -41
- package/modules/access_keys/access_keys.js +32 -32
- package/modules/admin/.babelrc +19 -19
- package/modules/admin/.editorconfig +14 -14
- package/modules/admin/.eslintignore +5 -5
- package/modules/admin/.eslintrc.js +197 -197
- package/modules/admin/.postcssrc.js +10 -10
- package/modules/admin/.travis.yml +5 -5
- package/modules/admin/LICENSE +21 -21
- package/modules/admin/README.md +88 -88
- package/modules/admin/build/build.js +46 -45
- package/modules/admin/build/check-versions.js +64 -64
- package/modules/admin/build/utils.js +109 -108
- package/modules/admin/build/vue-loader.conf.js +5 -5
- package/modules/admin/build/webpack.base.conf.js +112 -112
- package/modules/admin/build/webpack.dev.conf.js +95 -95
- package/modules/admin/build/webpack.prod.conf.js +112 -112
- package/modules/admin/config/dev.env.js +8 -8
- package/modules/admin/config/index.js +86 -86
- package/modules/admin/config/prod.env.js +5 -5
- package/modules/admin/dist/css/197.7a90435e.css +7 -0
- package/modules/admin/dist/css/197.7a90435e.css.gz +0 -0
- package/modules/admin/dist/css/430.8c9b0482.css +8 -0
- package/modules/admin/dist/css/430.8c9b0482.css.gz +0 -0
- package/modules/admin/dist/css/451.c6646d20.css +1 -0
- package/modules/admin/dist/css/451.c6646d20.css.gz +0 -0
- package/modules/admin/dist/css/566.7a90435e.css +7 -0
- package/modules/admin/dist/css/566.7a90435e.css.gz +0 -0
- package/modules/admin/dist/css/569.f7d5c7d5.css +37 -0
- package/modules/admin/dist/css/569.f7d5c7d5.css.gz +0 -0
- package/modules/admin/dist/css/572.74d8e965.css +34 -0
- package/modules/admin/dist/css/572.74d8e965.css.gz +0 -0
- package/modules/admin/dist/css/651.e946bda7.css +6 -0
- package/modules/admin/dist/css/651.e946bda7.css.gz +0 -0
- package/modules/admin/dist/css/750.4086915d.css +2 -0
- package/modules/admin/dist/css/750.4086915d.css.gz +0 -0
- package/modules/admin/dist/css/950.1fd9d57c.css +7 -0
- package/modules/admin/dist/css/950.1fd9d57c.css.gz +0 -0
- package/modules/admin/dist/css/app.eba5f5e2.css +592 -0
- package/modules/admin/dist/css/app.eba5f5e2.css.gz +0 -0
- package/modules/admin/dist/index.html +1 -1
- package/modules/admin/dist/static/fonts/element-icons.313f7da.woff +0 -0
- package/modules/admin/dist/static/fonts/element-icons.4520188.ttf +0 -0
- package/modules/admin/dist/static/js/197.e511d91e.js +1 -0
- package/modules/admin/dist/static/js/197.e511d91e.js.gz +0 -0
- package/modules/admin/dist/static/js/430.b3941f30.js +1 -0
- package/modules/admin/dist/static/js/430.b3941f30.js.gz +0 -0
- package/modules/admin/dist/static/js/451.645974a8.js +1 -0
- package/modules/admin/dist/static/js/451.645974a8.js.gz +0 -0
- package/modules/admin/dist/static/js/460.962d5bc0.js +1 -0
- package/modules/admin/dist/static/js/460.962d5bc0.js.gz +0 -0
- package/modules/admin/dist/static/js/550.f27feb23.js +1 -0
- package/modules/admin/dist/static/js/550.f27feb23.js.gz +0 -0
- package/modules/admin/dist/static/js/556.1e08866a.js +1 -0
- package/modules/admin/dist/static/js/556.1e08866a.js.gz +0 -0
- package/modules/admin/dist/static/js/569.0fecfbee.js +1 -0
- package/modules/admin/dist/static/js/569.0fecfbee.js.gz +0 -0
- package/modules/admin/dist/static/js/572.13392e1d.js +1 -0
- package/modules/admin/dist/static/js/572.13392e1d.js.gz +0 -0
- package/modules/admin/dist/static/js/603.7b34a650.js +1 -0
- package/modules/admin/dist/static/js/603.7b34a650.js.gz +0 -0
- package/modules/admin/dist/static/js/651.564b24b6.js +1 -0
- package/modules/admin/dist/static/js/651.564b24b6.js.gz +0 -0
- package/modules/admin/dist/static/js/653.02ec1098.js +1 -0
- package/modules/admin/dist/static/js/653.02ec1098.js.gz +0 -0
- package/modules/admin/dist/static/js/750.50c950da.js +1 -0
- package/modules/admin/dist/static/js/750.50c950da.js.gz +0 -0
- package/modules/admin/dist/static/js/950.d4ff0a18.js +1 -0
- package/modules/admin/dist/static/js/950.d4ff0a18.js.gz +0 -0
- package/modules/admin/dist/static/js/app.ea86ae11.js +2 -0
- package/modules/admin/dist/static/js/{chunk-libs.06c38828.js.LICENSE.txt → app.ea86ae11.js.LICENSE.txt} +15 -18
- package/modules/admin/dist/static/js/app.ea86ae11.js.LICENSE.txt.gz +0 -0
- package/modules/admin/dist/static/js/app.ea86ae11.js.gz +0 -0
- package/modules/admin/index.html +16 -16
- package/modules/admin/package.json +101 -101
- package/modules/admin/src/App.vue +11 -11
- package/modules/admin/src/api/login.js +26 -26
- package/modules/admin/src/api/table.js +9 -9
- package/modules/admin/src/components/Breadcrumb/index.vue +79 -79
- package/modules/admin/src/components/Hamburger/index.vue +62 -62
- package/modules/admin/src/components/JSONEditor.vue +180 -180
- package/modules/admin/src/components/SvgIcon/index.vue +43 -43
- package/modules/admin/src/icons/index.js +9 -9
- package/modules/admin/src/icons/svgo.yml +22 -22
- package/modules/admin/src/main.js +42 -42
- package/modules/admin/src/permission.js +49 -49
- package/modules/admin/src/router/index.js +200 -200
- package/modules/admin/src/store/getters.js +11 -11
- package/modules/admin/src/store/index.js +17 -17
- package/modules/admin/src/store/modules/app.js +43 -43
- package/modules/admin/src/store/modules/user.js +104 -104
- package/modules/admin/src/styles/element-ui.scss +29 -29
- package/modules/admin/src/styles/index.scss +78 -78
- package/modules/admin/src/styles/mixin.scss +27 -27
- package/modules/admin/src/styles/sidebar.scss +133 -133
- package/modules/admin/src/styles/transition.scss +46 -46
- package/modules/admin/src/styles/variables.scss +4 -4
- package/modules/admin/src/utils/auth.js +15 -15
- package/modules/admin/src/utils/index.js +74 -74
- package/modules/admin/src/utils/request.js +48 -48
- package/modules/admin/src/utils/validate.js +31 -31
- package/modules/admin/src/views/404.vue +235 -235
- package/modules/admin/src/views/Dev.vue +33 -33
- package/modules/admin/src/views/EditDocument.vue +121 -116
- package/modules/admin/src/views/Endpoints.vue +279 -279
- package/modules/admin/src/views/Home.vue +84 -84
- package/modules/admin/src/views/Install.vue +79 -79
- package/modules/admin/src/views/ListDocuments.vue +72 -72
- package/modules/admin/src/views/ListDocuments2.vue +467 -462
- package/modules/admin/src/views/ManageListeners.vue +67 -67
- package/modules/admin/src/views/ManageMiddleware.vue +43 -43
- package/modules/admin/src/views/ManagePermissions.vue +75 -75
- package/modules/admin/src/views/ViewRequest.vue +77 -77
- package/modules/admin/src/views/form/index.vue +91 -91
- package/modules/admin/src/views/layout/Layout.vue +69 -69
- package/modules/admin/src/views/layout/components/AppMain.vue +41 -41
- package/modules/admin/src/views/layout/components/Navbar.vue +97 -97
- package/modules/admin/src/views/layout/components/Sidebar/Item.vue +29 -29
- package/modules/admin/src/views/layout/components/Sidebar/Link.vue +39 -39
- package/modules/admin/src/views/layout/components/Sidebar/SidebarItem.vue +103 -103
- package/modules/admin/src/views/layout/components/Sidebar/index.vue +62 -62
- package/modules/admin/src/views/layout/components/index.js +3 -3
- package/modules/admin/src/views/layout/mixin/ResizeHandler.js +41 -41
- package/modules/admin/src/views/login/index.vue +195 -195
- package/modules/admin/src/views/tree/index.vue +77 -77
- package/modules/collections/collections.js +39 -39
- package/modules/core/core.js +197 -197
- package/modules/logging/logging.js +88 -88
- package/modules/permissions/permissions.js +106 -106
- package/package.json +59 -59
- package/scripts/expressa +12 -12
- package/scripts/run_cypress_tests.sh +36 -6
- package/scripts/run_db_tests.sh +8 -8
- package/test/0-install.js +96 -96
- package/test/collections.js +431 -431
- package/test/collections.querying.js +288 -288
- package/test/db.js +258 -258
- package/test/db.strings.js +73 -73
- package/test/db.updating.js +144 -144
- package/test/logging.js +29 -29
- package/test/test.js +93 -93
- package/test/testserver.js +17 -17
- package/test/testutils.js +136 -136
- package/test/users.js +425 -425
- package/util.js +522 -522
- package/modules/admin/dist/index.html.gz +0 -0
- package/modules/admin/dist/static/css/app.9dd57eea.css +0 -1
- package/modules/admin/dist/static/css/app.9dd57eea.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-5061.ce0e4bdb.css +0 -1
- package/modules/admin/dist/static/css/chunk-5061.ce0e4bdb.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-5b97.7f9cb53e.css +0 -1
- package/modules/admin/dist/static/css/chunk-5b97.7f9cb53e.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-621e.b826be8d.css +0 -1
- package/modules/admin/dist/static/css/chunk-621e.b826be8d.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-656b.9c828b00.css +0 -1
- package/modules/admin/dist/static/css/chunk-656b.9c828b00.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-7291.13514e45.css +0 -0
- package/modules/admin/dist/static/css/chunk-7291.13514e45.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-7f29.9c828b00.css +0 -1
- package/modules/admin/dist/static/css/chunk-7f29.9c828b00.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-9a95.c647a2ef.css +0 -1
- package/modules/admin/dist/static/css/chunk-9a95.c647a2ef.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-b423.5f239333.css +0 -0
- package/modules/admin/dist/static/css/chunk-b423.5f239333.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-bd5f.aefa49d7.css +0 -1
- package/modules/admin/dist/static/css/chunk-bd5f.aefa49d7.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-e374.025d58bb.css +0 -1
- package/modules/admin/dist/static/css/chunk-e374.025d58bb.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-elementUI.3d004d9f.css +0 -1
- package/modules/admin/dist/static/css/chunk-elementUI.3d004d9f.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-f075.3a895a1e.css +0 -1
- package/modules/admin/dist/static/css/chunk-f075.3a895a1e.css.gz +0 -0
- package/modules/admin/dist/static/css/chunk-libs.327ad89e.css +0 -14
- package/modules/admin/dist/static/css/chunk-libs.327ad89e.css.gz +0 -0
- package/modules/admin/dist/static/fonts/element-icons.6f0a763.ttf +0 -0
- package/modules/admin/dist/static/js/app.e9461593.js +0 -1
- package/modules/admin/dist/static/js/app.e9461593.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-09d8.561880e0.js +0 -2
- package/modules/admin/dist/static/js/chunk-09d8.561880e0.js.LICENSE.txt +0 -13
- package/modules/admin/dist/static/js/chunk-09d8.561880e0.js.LICENSE.txt.gz +0 -0
- package/modules/admin/dist/static/js/chunk-09d8.561880e0.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-5061.83e6ccfa.js +0 -1
- package/modules/admin/dist/static/js/chunk-5061.83e6ccfa.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-5b97.ac360086.js +0 -1
- package/modules/admin/dist/static/js/chunk-5b97.ac360086.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-5e17.a2b69611.js +0 -1
- package/modules/admin/dist/static/js/chunk-5e17.a2b69611.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-621e.ccf42393.js +0 -1
- package/modules/admin/dist/static/js/chunk-621e.ccf42393.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-656b.9d5b03c0.js +0 -1
- package/modules/admin/dist/static/js/chunk-656b.9d5b03c0.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-7291.590524e9.js +0 -1
- package/modules/admin/dist/static/js/chunk-7291.590524e9.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-7f29.6ffc142e.js +0 -1
- package/modules/admin/dist/static/js/chunk-7f29.6ffc142e.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-9a95.bdd76583.js +0 -1
- package/modules/admin/dist/static/js/chunk-9a95.bdd76583.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-b423.387c504c.js +0 -1
- package/modules/admin/dist/static/js/chunk-b423.387c504c.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-bd5f.e9509f10.js +0 -1
- package/modules/admin/dist/static/js/chunk-bd5f.e9509f10.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-e374.7c87a0dd.js +0 -1
- package/modules/admin/dist/static/js/chunk-e374.7c87a0dd.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-e7c3.e5df8ceb.js +0 -1
- package/modules/admin/dist/static/js/chunk-e7c3.e5df8ceb.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-elementUI.b0c99c08.js +0 -1
- package/modules/admin/dist/static/js/chunk-elementUI.b0c99c08.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-f075.315ceec1.js +0 -1
- package/modules/admin/dist/static/js/chunk-f075.315ceec1.js.gz +0 -0
- package/modules/admin/dist/static/js/chunk-libs.06c38828.js +0 -2
- package/modules/admin/dist/static/js/chunk-libs.06c38828.js.LICENSE.txt.gz +0 -0
- package/modules/admin/dist/static/js/chunk-libs.06c38828.js.gz +0 -0
- /package/modules/admin/dist/static/img/{404.a57b6f3.png → 404.4cf6930.png} +0 -0
package/index.js
CHANGED
|
@@ -1,297 +1,297 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
|
-
const express = require('express')
|
|
3
|
-
const bodyParser = require('body-parser')
|
|
4
|
-
const debug = require('debug')('expressa')
|
|
5
|
-
const randomstring = require('randomstring')
|
|
6
|
-
|
|
7
|
-
const dbTypeNames = ['cached', 'file', 'memory', 'postgres', 'mongo']
|
|
8
|
-
const dbTypes = dbTypeNames.reduce((obj, name) => {
|
|
9
|
-
obj[name] = require('./db/' + name)
|
|
10
|
-
return obj
|
|
11
|
-
}, {})
|
|
12
|
-
dbTypes['mongodb'] = dbTypes['mongo'] // alias
|
|
13
|
-
const auth = require('./auth')
|
|
14
|
-
const util = require('./util')
|
|
15
|
-
|
|
16
|
-
const collectionsApi = require('./controllers/collections')
|
|
17
|
-
const usersApi = require('./controllers/users')
|
|
18
|
-
const installApi = require('./controllers/install')
|
|
19
|
-
const statusApi = require('./controllers/status')
|
|
20
|
-
const permissions = require('./middleware/permissions')
|
|
21
|
-
const loggingMiddleware = require('./middleware/logging')
|
|
22
|
-
|
|
23
|
-
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
|
|
24
|
-
|
|
25
|
-
async function bootstrapCollections (router) {
|
|
26
|
-
const collections = await router.db.collection.all()
|
|
27
|
-
await Promise.all(collections.map((collection) => {
|
|
28
|
-
if (!dbTypes[collection.storage]) {
|
|
29
|
-
console.error('missing ' + collection.storage + ' dbtype which is used by ' + collection._id)
|
|
30
|
-
console.error('try updating to the latest version of expressa')
|
|
31
|
-
}
|
|
32
|
-
return router.setupCollectionDb(collection)
|
|
33
|
-
}))
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
async function initCollections (db, router) {
|
|
37
|
-
debug('init collections')
|
|
38
|
-
try {
|
|
39
|
-
const data = await db.settings.get(process.env.NODE_ENV)
|
|
40
|
-
Object.assign(router.settings, data)
|
|
41
|
-
} catch (err) {
|
|
42
|
-
const filePath = router.settings.file_storage_path || 'data'
|
|
43
|
-
if (!fs.existsSync(filePath + '/settings/' + process.env.NODE_ENV + '.json')) {
|
|
44
|
-
console.error('Settings not found. Please visit the expressa admin page for the installation process.')
|
|
45
|
-
console.error('This is likely at http://localhost:3000/admin but may be different if you changed ports, etc.')
|
|
46
|
-
} else {
|
|
47
|
-
console.error('error reading settings')
|
|
48
|
-
console.error(err.stack)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
console.log('Starting expressa server using environment: ' + process.env.NODE_ENV) // eslint-disable-line no-console
|
|
52
|
-
db.collection = dbTypes[router.settings.collection_db_type || 'file'](router.settings, 'collection')
|
|
53
|
-
await bootstrapCollections(router)
|
|
54
|
-
debug('collections loaded.')
|
|
55
|
-
|
|
56
|
-
await Promise.all([
|
|
57
|
-
require('./listeners_collection_permissions')(router),
|
|
58
|
-
require('./listeners')(router),
|
|
59
|
-
require('./listeners_validation')(router),
|
|
60
|
-
require('./listeners_users')(router),
|
|
61
|
-
])
|
|
62
|
-
debug('added standard listeners')
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function ph (requestHandler) {
|
|
66
|
-
return async function wrapper (req, res, next) {
|
|
67
|
-
const collectionName = req.params.collection
|
|
68
|
-
let collection = {}
|
|
69
|
-
try {
|
|
70
|
-
collection = collectionName ? await req.db.collection.get(collectionName) : {}
|
|
71
|
-
} catch (e) {
|
|
72
|
-
if (!e.message.includes('document not found')) {
|
|
73
|
-
console.error(e)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (!collection.allowHTTPCaching) {
|
|
77
|
-
res.header('Cache-Control', 'no-cache, no-store, must-revalidate')
|
|
78
|
-
res.header('Expires', '-1')
|
|
79
|
-
res.header('Pragma', 'no-cache')
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
const result = await requestHandler(req, res, next)
|
|
83
|
-
if (typeof result !== 'object') {
|
|
84
|
-
res.status(500).send('invalid result')
|
|
85
|
-
}
|
|
86
|
-
debug(`${200} ${req.method} ${req.url} body: ${JSON.stringify(req.body)} result: ${JSON.stringify(result)}`)
|
|
87
|
-
res.send(result)
|
|
88
|
-
} catch (err) {
|
|
89
|
-
err.status = err.status || 500
|
|
90
|
-
debug(`${err.status} ${req.method} ${req.url} ${err.stack} ${req.uerror}`)
|
|
91
|
-
if ((req.settings.print_400_errors) || err.status >= 500) {
|
|
92
|
-
console.error(err + ' | ' + req.uerror)
|
|
93
|
-
}
|
|
94
|
-
req.returnedError = err
|
|
95
|
-
res.status(err.status).send({
|
|
96
|
-
error: err.result || err.message || err,
|
|
97
|
-
tokenError: req.uerror ? (req.uerror === 'jwt expired' ? 'expired token' : 'jwt error') : undefined
|
|
98
|
-
})
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
module.exports.api = function (settings) {
|
|
104
|
-
const router = express.Router()
|
|
105
|
-
router.initialized = false
|
|
106
|
-
router.custom = express.Router()
|
|
107
|
-
router.dbTypes = dbTypes
|
|
108
|
-
router.settings = settings || {}
|
|
109
|
-
router.db = {
|
|
110
|
-
settings: dbTypes[router.settings.settings_db_type || 'file'](router.settings, 'settings')
|
|
111
|
-
}
|
|
112
|
-
router.util = {
|
|
113
|
-
get pgpool() {
|
|
114
|
-
return util.getPgPool(router.settings.postgresql_uri)
|
|
115
|
-
},
|
|
116
|
-
...util,
|
|
117
|
-
}
|
|
118
|
-
router.eventListeners = {}
|
|
119
|
-
|
|
120
|
-
router.setupCollectionDb = async function(collection) {
|
|
121
|
-
debug(`init collection db ${collection._id} ${collection.storage}`)
|
|
122
|
-
router.db[collection._id] = dbTypes[collection.storage](router.settings, collection._id, collection)
|
|
123
|
-
if (collection.cacheInMemory) {
|
|
124
|
-
router.db[collection._id] = dbTypes['cached'](router.db[collection._id])
|
|
125
|
-
debug('initializing ' + collection._id + ' as a memory cached collection')
|
|
126
|
-
}
|
|
127
|
-
return Promise.resolve(router.db[collection._id].init())
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
router.addSingleCollectionListener = function (event, listener) {
|
|
131
|
-
if (!router.eventListeners[event]) {
|
|
132
|
-
router.eventListeners[event] = []
|
|
133
|
-
}
|
|
134
|
-
router.eventListeners[event].push(listener)
|
|
135
|
-
router.eventListeners[event].sort((a, b) => a.priority - b.priority)
|
|
136
|
-
if (event === 'ready' && router.initialized) {
|
|
137
|
-
listener(router)
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
router.addCollectionListenerWithPriority = function (events, collections, priority, listener) {
|
|
142
|
-
listener.priority = priority
|
|
143
|
-
router.addCollectionListener(events, collections, listener)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
router.addLateCollectionListener = function (events, collections, listener) {
|
|
147
|
-
listener.priority = 10
|
|
148
|
-
router.addCollectionListener(events, collections, listener)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
router.addCollectionListener = function (events, collections, listener) {
|
|
152
|
-
events = util.castArray(events)
|
|
153
|
-
collections = util.castArray(collections)
|
|
154
|
-
listener.priority = listener.priority || 0
|
|
155
|
-
listener.collections = collections
|
|
156
|
-
if (!listener.name) {
|
|
157
|
-
throw new Error('Listeners must be named')
|
|
158
|
-
}
|
|
159
|
-
events.forEach(function (event) {
|
|
160
|
-
router.addSingleCollectionListener(event, listener)
|
|
161
|
-
})
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
router.addListener = function addListener (events, priority, listener) {
|
|
165
|
-
events = util.castArray(events)
|
|
166
|
-
if (typeof priority === 'function') {
|
|
167
|
-
listener = priority
|
|
168
|
-
priority = 0
|
|
169
|
-
}
|
|
170
|
-
if (!listener.name) {
|
|
171
|
-
throw new Error('Listeners must be named')
|
|
172
|
-
}
|
|
173
|
-
listener.priority = priority
|
|
174
|
-
events.forEach(function (event) {
|
|
175
|
-
router.addSingleCollectionListener(event, listener)
|
|
176
|
-
})
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
router.getSetting = function (name) {
|
|
180
|
-
return util.getPath(router.settings, name)
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
(async function setup() {
|
|
184
|
-
await initCollections(router.db, router)
|
|
185
|
-
|
|
186
|
-
const modules = ['collections', 'core', 'logging', 'permissions', 'access_keys']
|
|
187
|
-
router.modules = {}
|
|
188
|
-
for (const module of modules) {
|
|
189
|
-
router.modules[module] = require(`./modules/${module}/${module}`)
|
|
190
|
-
if (router.modules[module].init) {
|
|
191
|
-
router.modules[module].init(router)
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
await addRoutesAndMiddleware()
|
|
196
|
-
|
|
197
|
-
try {
|
|
198
|
-
await installApi.updateAdminPermissions(router)
|
|
199
|
-
} catch (e) {
|
|
200
|
-
console.error('Error while attempting to update admin permissions')
|
|
201
|
-
console.error(e)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
router.initialized = true
|
|
205
|
-
await util.notify('ready', router)
|
|
206
|
-
})()
|
|
207
|
-
|
|
208
|
-
async function addRoutesAndMiddleware() {
|
|
209
|
-
// Allow CORS
|
|
210
|
-
router.use(function addCorsHeaders(req, res, next) {
|
|
211
|
-
res.header('Access-Control-Allow-Origin', '*')
|
|
212
|
-
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
|
|
213
|
-
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, x-access-token')
|
|
214
|
-
next()
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
router.use(bodyParser.json({
|
|
218
|
-
type: '*/*', // The wildcard type ensures it works even without the application/json header
|
|
219
|
-
limit: router.settings.body_limit || '1mb',
|
|
220
|
-
}))
|
|
221
|
-
|
|
222
|
-
router.use(function attachVariablesToReq(req, res, next) {
|
|
223
|
-
req.settings = router.settings
|
|
224
|
-
req.eventListeners = router.eventListeners
|
|
225
|
-
req.db = router.db
|
|
226
|
-
req.modules = router.modules
|
|
227
|
-
req.setupCollectionDb = router.setupCollectionDb
|
|
228
|
-
req.getSetting = router.getSetting
|
|
229
|
-
req.requestId = randomstring.generate(12)
|
|
230
|
-
res.header('X-Request-ID', req.requestId)
|
|
231
|
-
next()
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
router.use(loggingMiddleware)
|
|
235
|
-
|
|
236
|
-
router.notify = util.notify
|
|
237
|
-
|
|
238
|
-
router.post('/install', ph(async (req) => installApi.install(req, router)))
|
|
239
|
-
router.get('/install/settings/schema', ph(async (req) => installApi.getSettingsSchema(req, router)))
|
|
240
|
-
|
|
241
|
-
const loginCollections = await util.getLoginCollections(router)
|
|
242
|
-
|
|
243
|
-
for (const { _id: name } of loginCollections) {
|
|
244
|
-
router.post(`/${name}/login`, ph((req) => usersApi.login(req, name)))
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
router.use(auth.middleware) // Add user id to request
|
|
248
|
-
router.use(permissions.middleware) // Add user and permissions to request
|
|
249
|
-
|
|
250
|
-
router.get('/status', ph(statusApi.getStatus(router)))
|
|
251
|
-
|
|
252
|
-
router.use(router.custom) // Externally added middleware
|
|
253
|
-
|
|
254
|
-
for (const { _id: name } of loginCollections) {
|
|
255
|
-
router.post(`/${name}/register`, ph((req) => usersApi.register(req, name)))
|
|
256
|
-
router.get(`/${name}/me`, ph((req) => usersApi.getMe(req, name)))
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
router.get('/:collection/schema', ph(collectionsApi.getSchema))
|
|
260
|
-
router.get('/:collection', ph(collectionsApi.get))
|
|
261
|
-
router.post('/:collection', ph(collectionsApi.insert))
|
|
262
|
-
router.get('/:collection/:id', ph(collectionsApi.getById))
|
|
263
|
-
router.put('/:collection/:id', ph(collectionsApi.replaceById))
|
|
264
|
-
router.post('/:collection/:id/update', ph(collectionsApi.updateById))
|
|
265
|
-
router.delete('/:collection/:id', ph(collectionsApi.deleteById))
|
|
266
|
-
|
|
267
|
-
// Error handler, log and send to user
|
|
268
|
-
// eslint-disable-next-line no-unused-vars
|
|
269
|
-
router.use(function safeErrorHandler(err, req, res, next) {
|
|
270
|
-
console.error('my err handler')
|
|
271
|
-
console.error(err.stack)
|
|
272
|
-
req.returnedError = err
|
|
273
|
-
res.status(res.errCode || 500)
|
|
274
|
-
if (process.env.DEBUG || (req.hasPermission && req.hasPermission('view errors'))) {
|
|
275
|
-
res.send({
|
|
276
|
-
error: err
|
|
277
|
-
})
|
|
278
|
-
} else {
|
|
279
|
-
res.send({
|
|
280
|
-
error: 'something wrong happened'
|
|
281
|
-
})
|
|
282
|
-
}
|
|
283
|
-
})
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
return router
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
module.exports.admin = function (settings) {
|
|
290
|
-
const router = express.Router()
|
|
291
|
-
router.get('/settings.js', function (req, res) {
|
|
292
|
-
res.set('Content-Type', 'text/javascript')
|
|
293
|
-
res.send('window.settings = ' + JSON.stringify(settings || {}) + '')
|
|
294
|
-
})
|
|
295
|
-
router.use(express.static(__dirname + '/modules/admin/dist'))
|
|
296
|
-
return router
|
|
297
|
-
}
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const express = require('express')
|
|
3
|
+
const bodyParser = require('body-parser')
|
|
4
|
+
const debug = require('debug')('expressa')
|
|
5
|
+
const randomstring = require('randomstring')
|
|
6
|
+
|
|
7
|
+
const dbTypeNames = ['cached', 'file', 'memory', 'postgres', 'mongo']
|
|
8
|
+
const dbTypes = dbTypeNames.reduce((obj, name) => {
|
|
9
|
+
obj[name] = require('./db/' + name)
|
|
10
|
+
return obj
|
|
11
|
+
}, {})
|
|
12
|
+
dbTypes['mongodb'] = dbTypes['mongo'] // alias
|
|
13
|
+
const auth = require('./auth')
|
|
14
|
+
const util = require('./util')
|
|
15
|
+
|
|
16
|
+
const collectionsApi = require('./controllers/collections')
|
|
17
|
+
const usersApi = require('./controllers/users')
|
|
18
|
+
const installApi = require('./controllers/install')
|
|
19
|
+
const statusApi = require('./controllers/status')
|
|
20
|
+
const permissions = require('./middleware/permissions')
|
|
21
|
+
const loggingMiddleware = require('./middleware/logging')
|
|
22
|
+
|
|
23
|
+
process.env.NODE_ENV = process.env.NODE_ENV || 'development'
|
|
24
|
+
|
|
25
|
+
async function bootstrapCollections (router) {
|
|
26
|
+
const collections = await router.db.collection.all()
|
|
27
|
+
await Promise.all(collections.map((collection) => {
|
|
28
|
+
if (!dbTypes[collection.storage]) {
|
|
29
|
+
console.error('missing ' + collection.storage + ' dbtype which is used by ' + collection._id)
|
|
30
|
+
console.error('try updating to the latest version of expressa')
|
|
31
|
+
}
|
|
32
|
+
return router.setupCollectionDb(collection)
|
|
33
|
+
}))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function initCollections (db, router) {
|
|
37
|
+
debug('init collections')
|
|
38
|
+
try {
|
|
39
|
+
const data = await db.settings.get(process.env.NODE_ENV)
|
|
40
|
+
Object.assign(router.settings, data)
|
|
41
|
+
} catch (err) {
|
|
42
|
+
const filePath = router.settings.file_storage_path || 'data'
|
|
43
|
+
if (!fs.existsSync(filePath + '/settings/' + process.env.NODE_ENV + '.json')) {
|
|
44
|
+
console.error('Settings not found. Please visit the expressa admin page for the installation process.')
|
|
45
|
+
console.error('This is likely at http://localhost:3000/admin but may be different if you changed ports, etc.')
|
|
46
|
+
} else {
|
|
47
|
+
console.error('error reading settings')
|
|
48
|
+
console.error(err.stack)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
console.log('Starting expressa server using environment: ' + process.env.NODE_ENV) // eslint-disable-line no-console
|
|
52
|
+
db.collection = dbTypes[router.settings.collection_db_type || 'file'](router.settings, 'collection')
|
|
53
|
+
await bootstrapCollections(router)
|
|
54
|
+
debug('collections loaded.')
|
|
55
|
+
|
|
56
|
+
await Promise.all([
|
|
57
|
+
require('./listeners_collection_permissions')(router),
|
|
58
|
+
require('./listeners')(router),
|
|
59
|
+
require('./listeners_validation')(router),
|
|
60
|
+
require('./listeners_users')(router),
|
|
61
|
+
])
|
|
62
|
+
debug('added standard listeners')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function ph (requestHandler) {
|
|
66
|
+
return async function wrapper (req, res, next) {
|
|
67
|
+
const collectionName = req.params.collection
|
|
68
|
+
let collection = {}
|
|
69
|
+
try {
|
|
70
|
+
collection = collectionName ? await req.db.collection.get(collectionName) : {}
|
|
71
|
+
} catch (e) {
|
|
72
|
+
if (!e.message.includes('document not found')) {
|
|
73
|
+
console.error(e)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (!collection.allowHTTPCaching) {
|
|
77
|
+
res.header('Cache-Control', 'no-cache, no-store, must-revalidate')
|
|
78
|
+
res.header('Expires', '-1')
|
|
79
|
+
res.header('Pragma', 'no-cache')
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const result = await requestHandler(req, res, next)
|
|
83
|
+
if (typeof result !== 'object') {
|
|
84
|
+
res.status(500).send('invalid result')
|
|
85
|
+
}
|
|
86
|
+
debug(`${200} ${req.method} ${req.url} body: ${JSON.stringify(req.body)} result: ${JSON.stringify(result)}`)
|
|
87
|
+
res.send(result)
|
|
88
|
+
} catch (err) {
|
|
89
|
+
err.status = err.status || 500
|
|
90
|
+
debug(`${err.status} ${req.method} ${req.url} ${err.stack} ${req.uerror}`)
|
|
91
|
+
if ((req.settings.print_400_errors) || err.status >= 500) {
|
|
92
|
+
console.error(err + ' | ' + req.uerror)
|
|
93
|
+
}
|
|
94
|
+
req.returnedError = err
|
|
95
|
+
res.status(err.status).send({
|
|
96
|
+
error: err.result || err.message || err,
|
|
97
|
+
tokenError: req.uerror ? (req.uerror === 'jwt expired' ? 'expired token' : 'jwt error') : undefined
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports.api = function (settings) {
|
|
104
|
+
const router = express.Router()
|
|
105
|
+
router.initialized = false
|
|
106
|
+
router.custom = express.Router()
|
|
107
|
+
router.dbTypes = dbTypes
|
|
108
|
+
router.settings = settings || {}
|
|
109
|
+
router.db = {
|
|
110
|
+
settings: dbTypes[router.settings.settings_db_type || 'file'](router.settings, 'settings')
|
|
111
|
+
}
|
|
112
|
+
router.util = {
|
|
113
|
+
get pgpool() {
|
|
114
|
+
return util.getPgPool(router.settings.postgresql_uri)
|
|
115
|
+
},
|
|
116
|
+
...util,
|
|
117
|
+
}
|
|
118
|
+
router.eventListeners = {}
|
|
119
|
+
|
|
120
|
+
router.setupCollectionDb = async function(collection) {
|
|
121
|
+
debug(`init collection db ${collection._id} ${collection.storage}`)
|
|
122
|
+
router.db[collection._id] = dbTypes[collection.storage](router.settings, collection._id, collection)
|
|
123
|
+
if (collection.cacheInMemory) {
|
|
124
|
+
router.db[collection._id] = dbTypes['cached'](router.db[collection._id])
|
|
125
|
+
debug('initializing ' + collection._id + ' as a memory cached collection')
|
|
126
|
+
}
|
|
127
|
+
return Promise.resolve(router.db[collection._id].init())
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
router.addSingleCollectionListener = function (event, listener) {
|
|
131
|
+
if (!router.eventListeners[event]) {
|
|
132
|
+
router.eventListeners[event] = []
|
|
133
|
+
}
|
|
134
|
+
router.eventListeners[event].push(listener)
|
|
135
|
+
router.eventListeners[event].sort((a, b) => a.priority - b.priority)
|
|
136
|
+
if (event === 'ready' && router.initialized) {
|
|
137
|
+
listener(router)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
router.addCollectionListenerWithPriority = function (events, collections, priority, listener) {
|
|
142
|
+
listener.priority = priority
|
|
143
|
+
router.addCollectionListener(events, collections, listener)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
router.addLateCollectionListener = function (events, collections, listener) {
|
|
147
|
+
listener.priority = 10
|
|
148
|
+
router.addCollectionListener(events, collections, listener)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
router.addCollectionListener = function (events, collections, listener) {
|
|
152
|
+
events = util.castArray(events)
|
|
153
|
+
collections = util.castArray(collections)
|
|
154
|
+
listener.priority = listener.priority || 0
|
|
155
|
+
listener.collections = collections
|
|
156
|
+
if (!listener.name) {
|
|
157
|
+
throw new Error('Listeners must be named')
|
|
158
|
+
}
|
|
159
|
+
events.forEach(function (event) {
|
|
160
|
+
router.addSingleCollectionListener(event, listener)
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
router.addListener = function addListener (events, priority, listener) {
|
|
165
|
+
events = util.castArray(events)
|
|
166
|
+
if (typeof priority === 'function') {
|
|
167
|
+
listener = priority
|
|
168
|
+
priority = 0
|
|
169
|
+
}
|
|
170
|
+
if (!listener.name) {
|
|
171
|
+
throw new Error('Listeners must be named')
|
|
172
|
+
}
|
|
173
|
+
listener.priority = priority
|
|
174
|
+
events.forEach(function (event) {
|
|
175
|
+
router.addSingleCollectionListener(event, listener)
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
router.getSetting = function (name) {
|
|
180
|
+
return util.getPath(router.settings, name)
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
(async function setup() {
|
|
184
|
+
await initCollections(router.db, router)
|
|
185
|
+
|
|
186
|
+
const modules = ['collections', 'core', 'logging', 'permissions', 'access_keys']
|
|
187
|
+
router.modules = {}
|
|
188
|
+
for (const module of modules) {
|
|
189
|
+
router.modules[module] = require(`./modules/${module}/${module}`)
|
|
190
|
+
if (router.modules[module].init) {
|
|
191
|
+
router.modules[module].init(router)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
await addRoutesAndMiddleware()
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
await installApi.updateAdminPermissions(router)
|
|
199
|
+
} catch (e) {
|
|
200
|
+
console.error('Error while attempting to update admin permissions')
|
|
201
|
+
console.error(e)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
router.initialized = true
|
|
205
|
+
await util.notify('ready', router)
|
|
206
|
+
})()
|
|
207
|
+
|
|
208
|
+
async function addRoutesAndMiddleware() {
|
|
209
|
+
// Allow CORS
|
|
210
|
+
router.use(function addCorsHeaders(req, res, next) {
|
|
211
|
+
res.header('Access-Control-Allow-Origin', '*')
|
|
212
|
+
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
|
|
213
|
+
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, x-access-token')
|
|
214
|
+
next()
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
router.use(bodyParser.json({
|
|
218
|
+
type: '*/*', // The wildcard type ensures it works even without the application/json header
|
|
219
|
+
limit: router.settings.body_limit || '1mb',
|
|
220
|
+
}))
|
|
221
|
+
|
|
222
|
+
router.use(function attachVariablesToReq(req, res, next) {
|
|
223
|
+
req.settings = router.settings
|
|
224
|
+
req.eventListeners = router.eventListeners
|
|
225
|
+
req.db = router.db
|
|
226
|
+
req.modules = router.modules
|
|
227
|
+
req.setupCollectionDb = router.setupCollectionDb
|
|
228
|
+
req.getSetting = router.getSetting
|
|
229
|
+
req.requestId = randomstring.generate(12)
|
|
230
|
+
res.header('X-Request-ID', req.requestId)
|
|
231
|
+
next()
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
router.use(loggingMiddleware)
|
|
235
|
+
|
|
236
|
+
router.notify = util.notify
|
|
237
|
+
|
|
238
|
+
router.post('/install', ph(async (req) => installApi.install(req, router)))
|
|
239
|
+
router.get('/install/settings/schema', ph(async (req) => installApi.getSettingsSchema(req, router)))
|
|
240
|
+
|
|
241
|
+
const loginCollections = await util.getLoginCollections(router)
|
|
242
|
+
|
|
243
|
+
for (const { _id: name } of loginCollections) {
|
|
244
|
+
router.post(`/${name}/login`, ph((req) => usersApi.login(req, name)))
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
router.use(auth.middleware) // Add user id to request
|
|
248
|
+
router.use(permissions.middleware) // Add user and permissions to request
|
|
249
|
+
|
|
250
|
+
router.get('/status', ph(statusApi.getStatus(router)))
|
|
251
|
+
|
|
252
|
+
router.use(router.custom) // Externally added middleware
|
|
253
|
+
|
|
254
|
+
for (const { _id: name } of loginCollections) {
|
|
255
|
+
router.post(`/${name}/register`, ph((req) => usersApi.register(req, name)))
|
|
256
|
+
router.get(`/${name}/me`, ph((req) => usersApi.getMe(req, name)))
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
router.get('/:collection/schema', ph(collectionsApi.getSchema))
|
|
260
|
+
router.get('/:collection', ph(collectionsApi.get))
|
|
261
|
+
router.post('/:collection', ph(collectionsApi.insert))
|
|
262
|
+
router.get('/:collection/:id', ph(collectionsApi.getById))
|
|
263
|
+
router.put('/:collection/:id', ph(collectionsApi.replaceById))
|
|
264
|
+
router.post('/:collection/:id/update', ph(collectionsApi.updateById))
|
|
265
|
+
router.delete('/:collection/:id', ph(collectionsApi.deleteById))
|
|
266
|
+
|
|
267
|
+
// Error handler, log and send to user
|
|
268
|
+
// eslint-disable-next-line no-unused-vars
|
|
269
|
+
router.use(function safeErrorHandler(err, req, res, next) {
|
|
270
|
+
console.error('my err handler')
|
|
271
|
+
console.error(err.stack)
|
|
272
|
+
req.returnedError = err
|
|
273
|
+
res.status(res.errCode || 500)
|
|
274
|
+
if (process.env.DEBUG || (req.hasPermission && req.hasPermission('view errors'))) {
|
|
275
|
+
res.send({
|
|
276
|
+
error: err
|
|
277
|
+
})
|
|
278
|
+
} else {
|
|
279
|
+
res.send({
|
|
280
|
+
error: 'something wrong happened'
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return router
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
module.exports.admin = function (settings) {
|
|
290
|
+
const router = express.Router()
|
|
291
|
+
router.get('/settings.js', function (req, res) {
|
|
292
|
+
res.set('Content-Type', 'text/javascript')
|
|
293
|
+
res.send('window.settings = ' + JSON.stringify(settings || {}) + '')
|
|
294
|
+
})
|
|
295
|
+
router.use(express.static(__dirname + '/modules/admin/dist'))
|
|
296
|
+
return router
|
|
297
|
+
}
|