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.
Files changed (258) hide show
  1. package/.circleci/config.yml +21 -21
  2. package/.eslintrc.json +29 -29
  3. package/LICENSE +21 -21
  4. package/README.md +137 -137
  5. package/auth/index.js +76 -76
  6. package/auth/jwt.js +31 -31
  7. package/controllers/collections.js +260 -260
  8. package/controllers/install.js +105 -105
  9. package/controllers/status.js +43 -43
  10. package/controllers/users.js +52 -52
  11. package/cypress/fixtures/example.json +4 -4
  12. package/cypress/integration/0_install_spec.js +8 -8
  13. package/cypress/integration/custom_collections.js +30 -28
  14. package/cypress/integration/home.js +9 -9
  15. package/cypress/integration/settings.js +34 -34
  16. package/cypress/integration/users.js +61 -61
  17. package/cypress/plugins/index.js +17 -17
  18. package/cypress/support/commands.js +69 -54
  19. package/cypress/support/index.js +20 -20
  20. package/cypress/support/util.js +10 -10
  21. package/cypress.config.js +18 -0
  22. package/cypress.json +4 -4
  23. package/db/cached.js +40 -40
  24. package/db/file.js +112 -112
  25. package/db/memory.js +79 -79
  26. package/db/mongo.js +86 -86
  27. package/db/postgres.js +93 -93
  28. package/doc/authentication.md +38 -38
  29. package/doc/automatic-fields.md +12 -12
  30. package/doc/blogexample.md +16 -16
  31. package/doc/custom-endpoints.md +20 -20
  32. package/doc/database.md +23 -23
  33. package/doc/development.md +33 -33
  34. package/doc/listeners.md +103 -103
  35. package/doc/modules.md +8 -8
  36. package/doc/permissions.md +14 -14
  37. package/doc/relationships.md +65 -65
  38. package/doc/testing.md +47 -47
  39. package/doc/uploading-files.md +53 -53
  40. package/index.js +297 -297
  41. package/listeners.js +65 -65
  42. package/listeners_collection_permissions.js +29 -29
  43. package/listeners_users.js +96 -96
  44. package/listeners_validation.js +43 -43
  45. package/middleware/logging.js +30 -30
  46. package/middleware/permissions.js +41 -41
  47. package/modules/access_keys/access_keys.js +32 -32
  48. package/modules/admin/.babelrc +19 -19
  49. package/modules/admin/.editorconfig +14 -14
  50. package/modules/admin/.eslintignore +5 -5
  51. package/modules/admin/.eslintrc.js +197 -197
  52. package/modules/admin/.postcssrc.js +10 -10
  53. package/modules/admin/.travis.yml +5 -5
  54. package/modules/admin/LICENSE +21 -21
  55. package/modules/admin/README.md +88 -88
  56. package/modules/admin/build/build.js +46 -45
  57. package/modules/admin/build/check-versions.js +64 -64
  58. package/modules/admin/build/utils.js +109 -108
  59. package/modules/admin/build/vue-loader.conf.js +5 -5
  60. package/modules/admin/build/webpack.base.conf.js +112 -112
  61. package/modules/admin/build/webpack.dev.conf.js +95 -95
  62. package/modules/admin/build/webpack.prod.conf.js +112 -112
  63. package/modules/admin/config/dev.env.js +8 -8
  64. package/modules/admin/config/index.js +86 -86
  65. package/modules/admin/config/prod.env.js +5 -5
  66. package/modules/admin/dist/css/197.7a90435e.css +7 -0
  67. package/modules/admin/dist/css/197.7a90435e.css.gz +0 -0
  68. package/modules/admin/dist/css/430.8c9b0482.css +8 -0
  69. package/modules/admin/dist/css/430.8c9b0482.css.gz +0 -0
  70. package/modules/admin/dist/css/451.c6646d20.css +1 -0
  71. package/modules/admin/dist/css/451.c6646d20.css.gz +0 -0
  72. package/modules/admin/dist/css/566.7a90435e.css +7 -0
  73. package/modules/admin/dist/css/566.7a90435e.css.gz +0 -0
  74. package/modules/admin/dist/css/569.f7d5c7d5.css +37 -0
  75. package/modules/admin/dist/css/569.f7d5c7d5.css.gz +0 -0
  76. package/modules/admin/dist/css/572.74d8e965.css +34 -0
  77. package/modules/admin/dist/css/572.74d8e965.css.gz +0 -0
  78. package/modules/admin/dist/css/651.e946bda7.css +6 -0
  79. package/modules/admin/dist/css/651.e946bda7.css.gz +0 -0
  80. package/modules/admin/dist/css/750.4086915d.css +2 -0
  81. package/modules/admin/dist/css/750.4086915d.css.gz +0 -0
  82. package/modules/admin/dist/css/950.1fd9d57c.css +7 -0
  83. package/modules/admin/dist/css/950.1fd9d57c.css.gz +0 -0
  84. package/modules/admin/dist/css/app.eba5f5e2.css +592 -0
  85. package/modules/admin/dist/css/app.eba5f5e2.css.gz +0 -0
  86. package/modules/admin/dist/index.html +1 -1
  87. package/modules/admin/dist/static/fonts/element-icons.313f7da.woff +0 -0
  88. package/modules/admin/dist/static/fonts/element-icons.4520188.ttf +0 -0
  89. package/modules/admin/dist/static/js/197.e511d91e.js +1 -0
  90. package/modules/admin/dist/static/js/197.e511d91e.js.gz +0 -0
  91. package/modules/admin/dist/static/js/430.b3941f30.js +1 -0
  92. package/modules/admin/dist/static/js/430.b3941f30.js.gz +0 -0
  93. package/modules/admin/dist/static/js/451.645974a8.js +1 -0
  94. package/modules/admin/dist/static/js/451.645974a8.js.gz +0 -0
  95. package/modules/admin/dist/static/js/460.962d5bc0.js +1 -0
  96. package/modules/admin/dist/static/js/460.962d5bc0.js.gz +0 -0
  97. package/modules/admin/dist/static/js/550.f27feb23.js +1 -0
  98. package/modules/admin/dist/static/js/550.f27feb23.js.gz +0 -0
  99. package/modules/admin/dist/static/js/556.1e08866a.js +1 -0
  100. package/modules/admin/dist/static/js/556.1e08866a.js.gz +0 -0
  101. package/modules/admin/dist/static/js/569.0fecfbee.js +1 -0
  102. package/modules/admin/dist/static/js/569.0fecfbee.js.gz +0 -0
  103. package/modules/admin/dist/static/js/572.13392e1d.js +1 -0
  104. package/modules/admin/dist/static/js/572.13392e1d.js.gz +0 -0
  105. package/modules/admin/dist/static/js/603.7b34a650.js +1 -0
  106. package/modules/admin/dist/static/js/603.7b34a650.js.gz +0 -0
  107. package/modules/admin/dist/static/js/651.564b24b6.js +1 -0
  108. package/modules/admin/dist/static/js/651.564b24b6.js.gz +0 -0
  109. package/modules/admin/dist/static/js/653.02ec1098.js +1 -0
  110. package/modules/admin/dist/static/js/653.02ec1098.js.gz +0 -0
  111. package/modules/admin/dist/static/js/750.50c950da.js +1 -0
  112. package/modules/admin/dist/static/js/750.50c950da.js.gz +0 -0
  113. package/modules/admin/dist/static/js/950.d4ff0a18.js +1 -0
  114. package/modules/admin/dist/static/js/950.d4ff0a18.js.gz +0 -0
  115. package/modules/admin/dist/static/js/app.ea86ae11.js +2 -0
  116. package/modules/admin/dist/static/js/{chunk-libs.06c38828.js.LICENSE.txt → app.ea86ae11.js.LICENSE.txt} +15 -18
  117. package/modules/admin/dist/static/js/app.ea86ae11.js.LICENSE.txt.gz +0 -0
  118. package/modules/admin/dist/static/js/app.ea86ae11.js.gz +0 -0
  119. package/modules/admin/index.html +16 -16
  120. package/modules/admin/package.json +101 -101
  121. package/modules/admin/src/App.vue +11 -11
  122. package/modules/admin/src/api/login.js +26 -26
  123. package/modules/admin/src/api/table.js +9 -9
  124. package/modules/admin/src/components/Breadcrumb/index.vue +79 -79
  125. package/modules/admin/src/components/Hamburger/index.vue +62 -62
  126. package/modules/admin/src/components/JSONEditor.vue +180 -180
  127. package/modules/admin/src/components/SvgIcon/index.vue +43 -43
  128. package/modules/admin/src/icons/index.js +9 -9
  129. package/modules/admin/src/icons/svgo.yml +22 -22
  130. package/modules/admin/src/main.js +42 -42
  131. package/modules/admin/src/permission.js +49 -49
  132. package/modules/admin/src/router/index.js +200 -200
  133. package/modules/admin/src/store/getters.js +11 -11
  134. package/modules/admin/src/store/index.js +17 -17
  135. package/modules/admin/src/store/modules/app.js +43 -43
  136. package/modules/admin/src/store/modules/user.js +104 -104
  137. package/modules/admin/src/styles/element-ui.scss +29 -29
  138. package/modules/admin/src/styles/index.scss +78 -78
  139. package/modules/admin/src/styles/mixin.scss +27 -27
  140. package/modules/admin/src/styles/sidebar.scss +133 -133
  141. package/modules/admin/src/styles/transition.scss +46 -46
  142. package/modules/admin/src/styles/variables.scss +4 -4
  143. package/modules/admin/src/utils/auth.js +15 -15
  144. package/modules/admin/src/utils/index.js +74 -74
  145. package/modules/admin/src/utils/request.js +48 -48
  146. package/modules/admin/src/utils/validate.js +31 -31
  147. package/modules/admin/src/views/404.vue +235 -235
  148. package/modules/admin/src/views/Dev.vue +33 -33
  149. package/modules/admin/src/views/EditDocument.vue +121 -116
  150. package/modules/admin/src/views/Endpoints.vue +279 -279
  151. package/modules/admin/src/views/Home.vue +84 -84
  152. package/modules/admin/src/views/Install.vue +79 -79
  153. package/modules/admin/src/views/ListDocuments.vue +72 -72
  154. package/modules/admin/src/views/ListDocuments2.vue +467 -462
  155. package/modules/admin/src/views/ManageListeners.vue +67 -67
  156. package/modules/admin/src/views/ManageMiddleware.vue +43 -43
  157. package/modules/admin/src/views/ManagePermissions.vue +75 -75
  158. package/modules/admin/src/views/ViewRequest.vue +77 -77
  159. package/modules/admin/src/views/form/index.vue +91 -91
  160. package/modules/admin/src/views/layout/Layout.vue +69 -69
  161. package/modules/admin/src/views/layout/components/AppMain.vue +41 -41
  162. package/modules/admin/src/views/layout/components/Navbar.vue +97 -97
  163. package/modules/admin/src/views/layout/components/Sidebar/Item.vue +29 -29
  164. package/modules/admin/src/views/layout/components/Sidebar/Link.vue +39 -39
  165. package/modules/admin/src/views/layout/components/Sidebar/SidebarItem.vue +103 -103
  166. package/modules/admin/src/views/layout/components/Sidebar/index.vue +62 -62
  167. package/modules/admin/src/views/layout/components/index.js +3 -3
  168. package/modules/admin/src/views/layout/mixin/ResizeHandler.js +41 -41
  169. package/modules/admin/src/views/login/index.vue +195 -195
  170. package/modules/admin/src/views/tree/index.vue +77 -77
  171. package/modules/collections/collections.js +39 -39
  172. package/modules/core/core.js +197 -197
  173. package/modules/logging/logging.js +88 -88
  174. package/modules/permissions/permissions.js +106 -106
  175. package/package.json +59 -59
  176. package/scripts/expressa +12 -12
  177. package/scripts/run_cypress_tests.sh +36 -6
  178. package/scripts/run_db_tests.sh +8 -8
  179. package/test/0-install.js +96 -96
  180. package/test/collections.js +431 -431
  181. package/test/collections.querying.js +288 -288
  182. package/test/db.js +258 -258
  183. package/test/db.strings.js +73 -73
  184. package/test/db.updating.js +144 -144
  185. package/test/logging.js +29 -29
  186. package/test/test.js +93 -93
  187. package/test/testserver.js +17 -17
  188. package/test/testutils.js +136 -136
  189. package/test/users.js +425 -425
  190. package/util.js +522 -522
  191. package/modules/admin/dist/index.html.gz +0 -0
  192. package/modules/admin/dist/static/css/app.9dd57eea.css +0 -1
  193. package/modules/admin/dist/static/css/app.9dd57eea.css.gz +0 -0
  194. package/modules/admin/dist/static/css/chunk-5061.ce0e4bdb.css +0 -1
  195. package/modules/admin/dist/static/css/chunk-5061.ce0e4bdb.css.gz +0 -0
  196. package/modules/admin/dist/static/css/chunk-5b97.7f9cb53e.css +0 -1
  197. package/modules/admin/dist/static/css/chunk-5b97.7f9cb53e.css.gz +0 -0
  198. package/modules/admin/dist/static/css/chunk-621e.b826be8d.css +0 -1
  199. package/modules/admin/dist/static/css/chunk-621e.b826be8d.css.gz +0 -0
  200. package/modules/admin/dist/static/css/chunk-656b.9c828b00.css +0 -1
  201. package/modules/admin/dist/static/css/chunk-656b.9c828b00.css.gz +0 -0
  202. package/modules/admin/dist/static/css/chunk-7291.13514e45.css +0 -0
  203. package/modules/admin/dist/static/css/chunk-7291.13514e45.css.gz +0 -0
  204. package/modules/admin/dist/static/css/chunk-7f29.9c828b00.css +0 -1
  205. package/modules/admin/dist/static/css/chunk-7f29.9c828b00.css.gz +0 -0
  206. package/modules/admin/dist/static/css/chunk-9a95.c647a2ef.css +0 -1
  207. package/modules/admin/dist/static/css/chunk-9a95.c647a2ef.css.gz +0 -0
  208. package/modules/admin/dist/static/css/chunk-b423.5f239333.css +0 -0
  209. package/modules/admin/dist/static/css/chunk-b423.5f239333.css.gz +0 -0
  210. package/modules/admin/dist/static/css/chunk-bd5f.aefa49d7.css +0 -1
  211. package/modules/admin/dist/static/css/chunk-bd5f.aefa49d7.css.gz +0 -0
  212. package/modules/admin/dist/static/css/chunk-e374.025d58bb.css +0 -1
  213. package/modules/admin/dist/static/css/chunk-e374.025d58bb.css.gz +0 -0
  214. package/modules/admin/dist/static/css/chunk-elementUI.3d004d9f.css +0 -1
  215. package/modules/admin/dist/static/css/chunk-elementUI.3d004d9f.css.gz +0 -0
  216. package/modules/admin/dist/static/css/chunk-f075.3a895a1e.css +0 -1
  217. package/modules/admin/dist/static/css/chunk-f075.3a895a1e.css.gz +0 -0
  218. package/modules/admin/dist/static/css/chunk-libs.327ad89e.css +0 -14
  219. package/modules/admin/dist/static/css/chunk-libs.327ad89e.css.gz +0 -0
  220. package/modules/admin/dist/static/fonts/element-icons.6f0a763.ttf +0 -0
  221. package/modules/admin/dist/static/js/app.e9461593.js +0 -1
  222. package/modules/admin/dist/static/js/app.e9461593.js.gz +0 -0
  223. package/modules/admin/dist/static/js/chunk-09d8.561880e0.js +0 -2
  224. package/modules/admin/dist/static/js/chunk-09d8.561880e0.js.LICENSE.txt +0 -13
  225. package/modules/admin/dist/static/js/chunk-09d8.561880e0.js.LICENSE.txt.gz +0 -0
  226. package/modules/admin/dist/static/js/chunk-09d8.561880e0.js.gz +0 -0
  227. package/modules/admin/dist/static/js/chunk-5061.83e6ccfa.js +0 -1
  228. package/modules/admin/dist/static/js/chunk-5061.83e6ccfa.js.gz +0 -0
  229. package/modules/admin/dist/static/js/chunk-5b97.ac360086.js +0 -1
  230. package/modules/admin/dist/static/js/chunk-5b97.ac360086.js.gz +0 -0
  231. package/modules/admin/dist/static/js/chunk-5e17.a2b69611.js +0 -1
  232. package/modules/admin/dist/static/js/chunk-5e17.a2b69611.js.gz +0 -0
  233. package/modules/admin/dist/static/js/chunk-621e.ccf42393.js +0 -1
  234. package/modules/admin/dist/static/js/chunk-621e.ccf42393.js.gz +0 -0
  235. package/modules/admin/dist/static/js/chunk-656b.9d5b03c0.js +0 -1
  236. package/modules/admin/dist/static/js/chunk-656b.9d5b03c0.js.gz +0 -0
  237. package/modules/admin/dist/static/js/chunk-7291.590524e9.js +0 -1
  238. package/modules/admin/dist/static/js/chunk-7291.590524e9.js.gz +0 -0
  239. package/modules/admin/dist/static/js/chunk-7f29.6ffc142e.js +0 -1
  240. package/modules/admin/dist/static/js/chunk-7f29.6ffc142e.js.gz +0 -0
  241. package/modules/admin/dist/static/js/chunk-9a95.bdd76583.js +0 -1
  242. package/modules/admin/dist/static/js/chunk-9a95.bdd76583.js.gz +0 -0
  243. package/modules/admin/dist/static/js/chunk-b423.387c504c.js +0 -1
  244. package/modules/admin/dist/static/js/chunk-b423.387c504c.js.gz +0 -0
  245. package/modules/admin/dist/static/js/chunk-bd5f.e9509f10.js +0 -1
  246. package/modules/admin/dist/static/js/chunk-bd5f.e9509f10.js.gz +0 -0
  247. package/modules/admin/dist/static/js/chunk-e374.7c87a0dd.js +0 -1
  248. package/modules/admin/dist/static/js/chunk-e374.7c87a0dd.js.gz +0 -0
  249. package/modules/admin/dist/static/js/chunk-e7c3.e5df8ceb.js +0 -1
  250. package/modules/admin/dist/static/js/chunk-e7c3.e5df8ceb.js.gz +0 -0
  251. package/modules/admin/dist/static/js/chunk-elementUI.b0c99c08.js +0 -1
  252. package/modules/admin/dist/static/js/chunk-elementUI.b0c99c08.js.gz +0 -0
  253. package/modules/admin/dist/static/js/chunk-f075.315ceec1.js +0 -1
  254. package/modules/admin/dist/static/js/chunk-f075.315ceec1.js.gz +0 -0
  255. package/modules/admin/dist/static/js/chunk-libs.06c38828.js +0 -2
  256. package/modules/admin/dist/static/js/chunk-libs.06c38828.js.LICENSE.txt.gz +0 -0
  257. package/modules/admin/dist/static/js/chunk-libs.06c38828.js.gz +0 -0
  258. /package/modules/admin/dist/static/img/{404.a57b6f3.png → 404.4cf6930.png} +0 -0
package/db/postgres.js CHANGED
@@ -1,93 +1,93 @@
1
- const util = require('../util')
2
-
3
- module.exports = function (settings, collectionId, collection) {
4
- const pool = util.getPgPool(settings.postgresql_uri)
5
-
6
- return {
7
- init: async function () {
8
- if (collection.plainStringIds) {
9
- await pool.query('CREATE TABLE IF NOT EXISTS ' + collectionId + ' (id text primary key, data jsonb)')
10
- } else {
11
- await pool.query('CREATE TABLE IF NOT EXISTS ' + collectionId + ' (id uuid primary key, data jsonb)')
12
- }
13
- },
14
- all: async function () {
15
- return this.find({})
16
- },
17
- find: async function (rawQuery, offset, limit, orderby, fields) {
18
- const pgSelect = util.mongoToPostgresSelect(collectionId, fields)
19
- const pgWhere = util.mongoToPostgresWhere(collectionId, rawQuery)
20
- let query = 'SELECT ' + pgSelect + ' FROM ' + collectionId + (pgWhere ? ' WHERE ' + pgWhere : '')
21
- if (typeof orderby !== 'undefined') {
22
- query += ' ORDER BY '
23
- query += util.mongoToPostgresOrderBy(collectionId, orderby)
24
- }
25
- if (typeof offset !== 'undefined') {
26
- query += ' OFFSET ' + offset
27
- }
28
- if (typeof limit !== 'undefined') {
29
- query += ' LIMIT ' + limit
30
- }
31
- const result = await pool.query(query)
32
- return result.rows.map((row) => row.data)
33
- },
34
- count: async function(rawQuery, offset, limit) {
35
- const pgWhere = util.mongoToPostgresWhere(collectionId, rawQuery)
36
- let query = 'SELECT COUNT(*) FROM ' + collectionId + (pgWhere ? ' WHERE ' + pgWhere : '')
37
- if (typeof offset !== 'undefined') {
38
- query += ' OFFSET ' + offset
39
- }
40
- if (typeof limit !== 'undefined') {
41
- query += ' LIMIT ' + limit
42
- }
43
- const result = await pool.query(query)
44
- return parseInt(result.rows[0].count)
45
- },
46
- get: async function (id, fields) {
47
- const pgSelect = util.mongoToPostgresSelect(collectionId, fields)
48
- const result = await pool.query(`SELECT ${pgSelect} FROM ${collectionId} WHERE id = $1`, [id])
49
- if (result.rowCount === 0) {
50
- throw new util.ApiError(404, 'document not found')
51
- }
52
- return result.rows[0].data
53
- },
54
- create: async function (data) {
55
- util.addIdIfMissing(data)
56
- try {
57
- await pool.query('INSERT INTO ' + collectionId + ' (id, data) VALUES ($1, $2)', [data._id, data])
58
- } catch (e) {
59
- if (e.message.includes('duplicate key')) {
60
- throw new util.ApiError(409, 'document already exists')
61
- }
62
- throw e
63
- }
64
- return data._id
65
- },
66
- update: async function (id, data) {
67
- if (typeof data._id === 'undefined') {
68
- data._id = id
69
- }
70
- const result = await pool.query('UPDATE ' + collectionId + ' SET data=$2,id=$3 WHERE id=$1', [id, data, data._id])
71
- if (result.rowCount === 0) {
72
- throw new util.ApiError(404, 'document not found')
73
- }
74
- return data
75
- },
76
- // eslint-disable-next-line no-unused-vars
77
- updateWithQuery: async function (query, update, options) {
78
- const pgWhere = util.mongoToPostgresWhere(collectionId, query)
79
- const updateSql = util.mongoToPostgresUpdate(collectionId, update)
80
- const result = await pool.query('UPDATE ' + collectionId + ' SET data=' + updateSql + ' WHERE ' + pgWhere)
81
- return {
82
- matchedCount: result.rowCount
83
- }
84
- },
85
- delete: async function (id) {
86
- const result = await pool.query('DELETE FROM ' + collectionId + ' WHERE id=$1', [id])
87
- if (result.rowCount === 0) {
88
- throw new util.ApiError(404, 'document not found')
89
- }
90
- return 'OK'
91
- }
92
- }
93
- }
1
+ const util = require('../util')
2
+
3
+ module.exports = function (settings, collectionId, collection) {
4
+ const pool = util.getPgPool(settings.postgresql_uri)
5
+
6
+ return {
7
+ init: async function () {
8
+ if (collection.plainStringIds) {
9
+ await pool.query('CREATE TABLE IF NOT EXISTS ' + collectionId + ' (id text primary key, data jsonb)')
10
+ } else {
11
+ await pool.query('CREATE TABLE IF NOT EXISTS ' + collectionId + ' (id uuid primary key, data jsonb)')
12
+ }
13
+ },
14
+ all: async function () {
15
+ return this.find({})
16
+ },
17
+ find: async function (rawQuery, offset, limit, orderby, fields) {
18
+ const pgSelect = util.mongoToPostgresSelect(collectionId, fields)
19
+ const pgWhere = util.mongoToPostgresWhere(collectionId, rawQuery)
20
+ let query = 'SELECT ' + pgSelect + ' FROM ' + collectionId + (pgWhere ? ' WHERE ' + pgWhere : '')
21
+ if (typeof orderby !== 'undefined') {
22
+ query += ' ORDER BY '
23
+ query += util.mongoToPostgresOrderBy(collectionId, orderby)
24
+ }
25
+ if (typeof offset !== 'undefined') {
26
+ query += ' OFFSET ' + offset
27
+ }
28
+ if (typeof limit !== 'undefined') {
29
+ query += ' LIMIT ' + limit
30
+ }
31
+ const result = await pool.query(query)
32
+ return result.rows.map((row) => row.data)
33
+ },
34
+ count: async function(rawQuery, offset, limit) {
35
+ const pgWhere = util.mongoToPostgresWhere(collectionId, rawQuery)
36
+ let query = 'SELECT COUNT(*) FROM ' + collectionId + (pgWhere ? ' WHERE ' + pgWhere : '')
37
+ if (typeof offset !== 'undefined') {
38
+ query += ' OFFSET ' + offset
39
+ }
40
+ if (typeof limit !== 'undefined') {
41
+ query += ' LIMIT ' + limit
42
+ }
43
+ const result = await pool.query(query)
44
+ return parseInt(result.rows[0].count)
45
+ },
46
+ get: async function (id, fields) {
47
+ const pgSelect = util.mongoToPostgresSelect(collectionId, fields)
48
+ const result = await pool.query(`SELECT ${pgSelect} FROM ${collectionId} WHERE id = $1`, [id])
49
+ if (result.rowCount === 0) {
50
+ throw new util.ApiError(404, 'document not found')
51
+ }
52
+ return result.rows[0].data
53
+ },
54
+ create: async function (data) {
55
+ util.addIdIfMissing(data)
56
+ try {
57
+ await pool.query('INSERT INTO ' + collectionId + ' (id, data) VALUES ($1, $2)', [data._id, data])
58
+ } catch (e) {
59
+ if (e.message.includes('duplicate key')) {
60
+ throw new util.ApiError(409, 'document already exists')
61
+ }
62
+ throw e
63
+ }
64
+ return data._id
65
+ },
66
+ update: async function (id, data) {
67
+ if (typeof data._id === 'undefined') {
68
+ data._id = id
69
+ }
70
+ const result = await pool.query('UPDATE ' + collectionId + ' SET data=$2,id=$3 WHERE id=$1', [id, data, data._id])
71
+ if (result.rowCount === 0) {
72
+ throw new util.ApiError(404, 'document not found')
73
+ }
74
+ return data
75
+ },
76
+ // eslint-disable-next-line no-unused-vars
77
+ updateWithQuery: async function (query, update, options) {
78
+ const pgWhere = util.mongoToPostgresWhere(collectionId, query)
79
+ const updateSql = util.mongoToPostgresUpdate(collectionId, update)
80
+ const result = await pool.query('UPDATE ' + collectionId + ' SET data=' + updateSql + ' WHERE ' + pgWhere)
81
+ return {
82
+ matchedCount: result.rowCount
83
+ }
84
+ },
85
+ delete: async function (id) {
86
+ const result = await pool.query('DELETE FROM ' + collectionId + ' WHERE id=$1', [id])
87
+ if (result.rowCount === 0) {
88
+ throw new util.ApiError(404, 'document not found')
89
+ }
90
+ return 'OK'
91
+ }
92
+ }
93
+ }
@@ -1,38 +1,38 @@
1
- ## Authentication
2
- Uses [JSON Web Tokens](https://jwt.io/)
3
-
4
- | method | endpoint | description |
5
- |--------|-------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
6
- | POST | /users/login | expects JSON in the message body. e.g. `{"email": "email@example.com", password: "<the password>"} |
7
-
8
- Returns the following
9
- ```javascript
10
- {
11
- "uid": "a52ee085-ff47-4ee1-bac3-a8a319f674ed",
12
- "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NmNkNWM5NmY4MjA4N2I1MDQ0OTM3YjEiLCJ1bml2ZXJzaXR5IjoiQllVIiwiZnVsbE5hbWUiOiJUaG9tYXMgSGFuc2VuIiwicGFzc3dvcmQiOiIkMmEkMTAkb0prdlBnTTlkR2FJRTIzaWFabGEvT0tjZC9PL3phSGFJOHFRUDBuZ2pPUVV1Ums3Vng2QkciLCJlbWFpbCI6InRoNDAxOUBnbWFpbC5jb20iLCJfX3YiOjAsImxpc3RpbmdzIjpbXSwiaWF0IjoxNDU2NDMwMjE5LCJleHAiOjE0NTY1MTY2MTl9._ijngdgwLU9AJnAjbySUgEFsR8hJCSw8PhH1AnyBHuM"
13
- }
14
- ```
15
- Or it will respond with a status code of 401 and a message explaining why they can't login.
16
-
17
- ```javascript
18
- {
19
- "success": false,
20
- "message": "Authentication failed. Wrong password."
21
- }
22
- ```
23
-
24
- The returned token must then be passed in as a header on future requests using the header x-access-token
25
-
26
- * `GET /users/me` - returns the logged in user's object
27
-
28
- ## Making authenticated requests
29
-
30
- After obtaining a token as described above you need to pass it with your request in one of these ways
31
-
32
- 1. Using the `x-access-token`
33
-
34
- `curl --header "x-access-token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NmNkNWM5NmY4MjA4N2I1MDQ0OTM3YjEiLCJ1bml2ZXJzaXR5IjoiQllVIiwiZnVsbE5hbWUiOiJUaG9tYXMgSGFuc2VuIiwicGFzc3dvcmQiOiIkMmEkMTAkb0prdlBnTTlkR2FJRTIzaWFabGEvT0tjZC9PL3phSGFJOHFRUDBuZ2pPUVV1Ums3Vng2QkciLCJlbWFpbCI6InRoNDAxOUBnbWFpbC5jb20iLCJfX3YiOjAsImxpc3RpbmdzIjpbXSwiaWF0IjoxNDU2NDMwMjE5LCJleHAiOjE0NTY1MTY2MTl9._ijngdgwLU9AJnAjbySUgEFsR8hJCSw8PhH1AnyBHuM" api.example.com/users/me`
35
-
36
- 2. Pass the token as a query parameter.
37
-
38
- `curl api.example.com/users/me?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NmNkNWM5NmY4MjA4N2I1MDQ0OTM3YjEiLCJ1bml2ZXJzaXR5IjoiQllVIiwiZnVsbE5hbWUiOiJUaG9tYXMgSGFuc2VuIiwicGFzc3dvcmQiOiIkMmEkMTAkb0prdlBnTTlkR2FJRTIzaWFabGEvT0tjZC9PL3phSGFJOHFRUDBuZ2pPUVV1Ums3Vng2QkciLCJlbWFpbCI6InRoNDAxOUBnbWFpbC5jb20iLCJfX3YiOjAsImxpc3RpbmdzIjpbXSwiaWF0IjoxNDU2NDMwMjE5LCJleHAiOjE0NTY1MTY2MTl9._ijngdgwLU9AJnAjbySUgEFsR8hJCSw8PhH1AnyBHuM`
1
+ ## Authentication
2
+ Uses [JSON Web Tokens](https://jwt.io/)
3
+
4
+ | method | endpoint | description |
5
+ |--------|-------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
6
+ | POST | /users/login | expects JSON in the message body. e.g. `{"email": "email@example.com", password: "<the password>"} |
7
+
8
+ Returns the following
9
+ ```javascript
10
+ {
11
+ "uid": "a52ee085-ff47-4ee1-bac3-a8a319f674ed",
12
+ "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NmNkNWM5NmY4MjA4N2I1MDQ0OTM3YjEiLCJ1bml2ZXJzaXR5IjoiQllVIiwiZnVsbE5hbWUiOiJUaG9tYXMgSGFuc2VuIiwicGFzc3dvcmQiOiIkMmEkMTAkb0prdlBnTTlkR2FJRTIzaWFabGEvT0tjZC9PL3phSGFJOHFRUDBuZ2pPUVV1Ums3Vng2QkciLCJlbWFpbCI6InRoNDAxOUBnbWFpbC5jb20iLCJfX3YiOjAsImxpc3RpbmdzIjpbXSwiaWF0IjoxNDU2NDMwMjE5LCJleHAiOjE0NTY1MTY2MTl9._ijngdgwLU9AJnAjbySUgEFsR8hJCSw8PhH1AnyBHuM"
13
+ }
14
+ ```
15
+ Or it will respond with a status code of 401 and a message explaining why they can't login.
16
+
17
+ ```javascript
18
+ {
19
+ "success": false,
20
+ "message": "Authentication failed. Wrong password."
21
+ }
22
+ ```
23
+
24
+ The returned token must then be passed in as a header on future requests using the header x-access-token
25
+
26
+ * `GET /users/me` - returns the logged in user's object
27
+
28
+ ## Making authenticated requests
29
+
30
+ After obtaining a token as described above you need to pass it with your request in one of these ways
31
+
32
+ 1. Using the `x-access-token`
33
+
34
+ `curl --header "x-access-token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NmNkNWM5NmY4MjA4N2I1MDQ0OTM3YjEiLCJ1bml2ZXJzaXR5IjoiQllVIiwiZnVsbE5hbWUiOiJUaG9tYXMgSGFuc2VuIiwicGFzc3dvcmQiOiIkMmEkMTAkb0prdlBnTTlkR2FJRTIzaWFabGEvT0tjZC9PL3phSGFJOHFRUDBuZ2pPUVV1Ums3Vng2QkciLCJlbWFpbCI6InRoNDAxOUBnbWFpbC5jb20iLCJfX3YiOjAsImxpc3RpbmdzIjpbXSwiaWF0IjoxNDU2NDMwMjE5LCJleHAiOjE0NTY1MTY2MTl9._ijngdgwLU9AJnAjbySUgEFsR8hJCSw8PhH1AnyBHuM" api.example.com/users/me`
35
+
36
+ 2. Pass the token as a query parameter.
37
+
38
+ `curl api.example.com/users/me?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NmNkNWM5NmY4MjA4N2I1MDQ0OTM3YjEiLCJ1bml2ZXJzaXR5IjoiQllVIiwiZnVsbE5hbWUiOiJUaG9tYXMgSGFuc2VuIiwicGFzc3dvcmQiOiIkMmEkMTAkb0prdlBnTTlkR2FJRTIzaWFabGEvT0tjZC9PL3phSGFJOHFRUDBuZ2pPUVV1Ums3Vng2QkciLCJlbWFpbCI6InRoNDAxOUBnbWFpbC5jb20iLCJfX3YiOjAsImxpc3RpbmdzIjpbXSwiaWF0IjoxNDU2NDMwMjE5LCJleHAiOjE0NTY1MTY2MTl9._ijngdgwLU9AJnAjbySUgEFsR8hJCSw8PhH1AnyBHuM`
@@ -1,12 +1,12 @@
1
- ## Automatic Fields
2
-
3
- Each object will contain this meta property which has autogenerated fields shared across all collection types:
4
-
5
- ```javascript
6
- "meta": {
7
- "created": "2016-05-16T23:56:11.615Z",
8
- "updated": "2016-05-16T23:56:28.262Z",
9
- "owner": "56cb5df7f56ef0b92f7b984b"
10
- },
11
- ```
12
-
1
+ ## Automatic Fields
2
+
3
+ Each object will contain this meta property which has autogenerated fields shared across all collection types:
4
+
5
+ ```javascript
6
+ "meta": {
7
+ "created": "2016-05-16T23:56:11.615Z",
8
+ "updated": "2016-05-16T23:56:28.262Z",
9
+ "owner": "56cb5df7f56ef0b92f7b984b"
10
+ },
11
+ ```
12
+
@@ -1,16 +1,16 @@
1
- ## Blog Example
2
-
3
- ### Creating the blog "post" collection and its schema.
4
-
5
- Here's what the UI looks like for adding this new custom collection type.
6
-
7
- ![post schema 3](https://cloud.githubusercontent.com/assets/406149/15308189/c3612ade-1b97-11e6-8f87-faeee4aba8bd.png)
8
-
9
- --------------------------------
10
-
11
- ### Creating a post
12
-
13
- After creating the blog post collection, here's what adding a document of that type looks like using the built in editor.
14
-
15
- ![create post 3](https://cloud.githubusercontent.com/assets/406149/15308188/c361368c-1b97-11e6-869a-ac40ba838dde.png)
16
-
1
+ ## Blog Example
2
+
3
+ ### Creating the blog "post" collection and its schema.
4
+
5
+ Here's what the UI looks like for adding this new custom collection type.
6
+
7
+ ![post schema 3](https://cloud.githubusercontent.com/assets/406149/15308189/c3612ade-1b97-11e6-8f87-faeee4aba8bd.png)
8
+
9
+ --------------------------------
10
+
11
+ ### Creating a post
12
+
13
+ After creating the blog post collection, here's what adding a document of that type looks like using the built in editor.
14
+
15
+ ![create post 3](https://cloud.githubusercontent.com/assets/406149/15308188/c361368c-1b97-11e6-869a-ac40ba838dde.png)
16
+
@@ -1,20 +1,20 @@
1
- ## Custom Endpoints
2
-
3
- Since Expressa exports a middleware you can include it in express apps that have other endpoints. These other endpoints
4
- do not have access to the current user and other expressa objects attached to the request object. If you need these see
5
- the section below.
6
-
7
- ## Add endpoints within expressa
8
-
9
- The api middleware exposes a "custom" which is an express router you can attach additional endpoints to. These endpoints
10
- are called after internal expressa middleware, but before any expressa endpoints.
11
-
12
- Note: you can use req.hasPermission('users: edit') to check if the user has a specific permission.
13
-
14
- Example:
15
- ```javascript
16
- const api = expressa.api();
17
- api.custom.get('/test', function (req, res) {
18
- res.send(req.user)
19
- })
20
- ```
1
+ ## Custom Endpoints
2
+
3
+ Since Expressa exports a middleware you can include it in express apps that have other endpoints. These other endpoints
4
+ do not have access to the current user and other expressa objects attached to the request object. If you need these see
5
+ the section below.
6
+
7
+ ## Add endpoints within expressa
8
+
9
+ The api middleware exposes a "custom" which is an express router you can attach additional endpoints to. These endpoints
10
+ are called after internal expressa middleware, but before any expressa endpoints.
11
+
12
+ Note: you can use req.hasPermission('users: edit') to check if the user has a specific permission.
13
+
14
+ Example:
15
+ ```javascript
16
+ const api = expressa.api();
17
+ api.custom.get('/test', function (req, res) {
18
+ res.send(req.user)
19
+ })
20
+ ```
package/doc/database.md CHANGED
@@ -1,23 +1,23 @@
1
- ### Generic JSON Database API
2
-
3
- You can access a collection's database using `expressa.db[collectionName].action where action is one of the following:
4
-
5
- * all - returns all documents
6
- * find - returns array of documents matching the mongo query
7
- * get - retrieve a document by id
8
- * create - create a new document
9
- * update - modify an existing document
10
- * delete - delete a document by id
11
- * init - called once during startup, useful to create/ensure collection exists
12
-
13
- ### Implemented JSON databases
14
- * MongoDB
15
- * PostgreSQL (using [jsonb](http://www.postgresql.org/docs/9.4/static/datatype-json.html) and [mongo-query-to-postgres-jsonb](https://github.com/thomas4019/mongo-query-to-postgres-jsonb))
16
- * Text files (using [json-file-store](https://github.com/flosse/json-file-store) and [mongo-query](https://github.com/Automattic/mongo-query))
17
- * In-memory (useful for small/medium ephemeral datasets like logs)
18
-
19
- Other JSON capable databases can be added easily (pull requests welcome!) by writing a wrapper that supports each of the above methods.
20
-
21
- ### Smart database objects
22
-
23
- [expressa-folder](https://npmjs.org/package/expressa-folder) will automatically add functions to objects returned by `all`,`find` and `get`
1
+ ### Generic JSON Database API
2
+
3
+ You can access a collection's database using `expressa.db[collectionName].action where action is one of the following:
4
+
5
+ * all - returns all documents
6
+ * find - returns array of documents matching the mongo query
7
+ * get - retrieve a document by id
8
+ * create - create a new document
9
+ * update - modify an existing document
10
+ * delete - delete a document by id
11
+ * init - called once during startup, useful to create/ensure collection exists
12
+
13
+ ### Implemented JSON databases
14
+ * MongoDB
15
+ * PostgreSQL (using [jsonb](http://www.postgresql.org/docs/9.4/static/datatype-json.html) and [mongo-query-to-postgres-jsonb](https://github.com/thomas4019/mongo-query-to-postgres-jsonb))
16
+ * Text files (using [json-file-store](https://github.com/flosse/json-file-store) and [mongo-query](https://github.com/Automattic/mongo-query))
17
+ * In-memory (useful for small/medium ephemeral datasets like logs)
18
+
19
+ Other JSON capable databases can be added easily (pull requests welcome!) by writing a wrapper that supports each of the above methods.
20
+
21
+ ### Smart database objects
22
+
23
+ [expressa-folder](https://npmjs.org/package/expressa-folder) will automatically add functions to objects returned by `all`,`find` and `get`
@@ -1,34 +1,34 @@
1
- ## Setup for developing expressa
2
- 1. git clone git@github.com:thomas4019/expressa.git
3
- 1. cd expressa
4
- 1. npm install
5
- 1. npm link
6
- 1. cd modules/admin
7
- 1. npm install
8
- 1. npm run build
9
-
10
- In another folder, create a project using app.js from the readme.
11
- 1. npm link expressa
12
- 1. node app.js
13
-
14
- ## Developing admin UI locally
15
- * npm run testserver
16
- * (cd modules/admin && npm run dev)
17
- * npm run cypress (to manually run specific tests)
18
-
19
- ## Before committing/releasing
20
- * npm run test
21
- * npm run testdbs (if changing Postgres or Mongo code)
22
- * npm run testcypress (if changing admin UI or related)
23
- * npm run fixlint
24
- * (cd modules/admin && npm run lint) (if changing admin UI)
25
-
26
- ## To release
27
- * Update version in package.json
28
- * (cd modules/admin && npm run build)
29
- * ls -la modules/admin/dist/static/js
30
- * npm publish
31
- * Create release on GitHub
32
-
33
- ## More info
1
+ ## Setup for developing expressa
2
+ 1. git clone git@github.com:thomas4019/expressa.git
3
+ 1. cd expressa
4
+ 1. npm install
5
+ 1. npm link
6
+ 1. cd modules/admin
7
+ 1. npm install
8
+ 1. npm run build
9
+
10
+ In another folder, create a project using app.js from the readme.
11
+ 1. npm link expressa
12
+ 1. node app.js
13
+
14
+ ## Developing admin UI locally
15
+ * npm run testserver
16
+ * (cd modules/admin && npm run dev)
17
+ * npm run cypress (to manually run specific tests)
18
+
19
+ ## Before committing/releasing
20
+ * npm run test
21
+ * npm run testdbs (if changing Postgres or Mongo code)
22
+ * npm run testcypress (if changing admin UI or related)
23
+ * npm run fixlint
24
+ * (cd modules/admin && npm run lint) (if changing admin UI)
25
+
26
+ ## To release
27
+ * Update version in package.json
28
+ * (cd modules/admin && npm run build)
29
+ * ls -la modules/admin/dist/static/js
30
+ * npm publish
31
+ * Create release on GitHub
32
+
33
+ ## More info
34
34
  [Modules](doc/modules.md)