@budibase/server 1.0.48 → 1.0.49-alpha.0

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 (257) hide show
  1. package/builder/assets/{index.e083dcaf.js → index.8184eed9.js} +224 -222
  2. package/builder/assets/{index.39e01193.css → index.dd9e386f.css} +1 -1
  3. package/builder/assets/{vendor.f9faf6ab.js → vendor.3bd2f661.js} +12 -12
  4. package/builder/index.html +3 -3
  5. package/coverage/clover.xml +1860 -1683
  6. package/coverage/coverage-final.json +99 -95
  7. package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/application.js.html +210 -126
  10. package/coverage/lcov-report/api/controllers/auth.js.html +18 -6
  11. package/coverage/lcov-report/api/controllers/automation.js.html +21 -21
  12. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  15. package/coverage/lcov-report/api/controllers/datasource.js.html +72 -36
  16. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/index.html +38 -53
  18. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/layout.js.html +6 -6
  20. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/permission.js.html +15 -15
  22. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  23. package/coverage/lcov-report/api/controllers/query/index.js.html +19 -19
  24. package/coverage/lcov-report/api/controllers/query/validation.js.html +6 -6
  25. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  26. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/row/external.js.html +16 -16
  28. package/coverage/lcov-report/api/controllers/row/index.html +15 -15
  29. package/coverage/lcov-report/api/controllers/row/index.js.html +26 -26
  30. package/coverage/lcov-report/api/controllers/row/internal.js.html +44 -44
  31. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +8 -8
  32. package/coverage/lcov-report/api/controllers/row/utils.js.html +39 -57
  33. package/coverage/lcov-report/api/controllers/screen.js.html +7 -7
  34. package/coverage/lcov-report/api/controllers/script.js.html +4 -4
  35. package/coverage/lcov-report/api/controllers/table/external.js.html +12 -12
  36. package/coverage/lcov-report/api/controllers/table/index.html +23 -23
  37. package/coverage/lcov-report/api/controllers/table/index.js.html +57 -39
  38. package/coverage/lcov-report/api/controllers/table/internal.js.html +40 -34
  39. package/coverage/lcov-report/api/controllers/table/utils.js.html +87 -63
  40. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  41. package/coverage/lcov-report/api/controllers/user.js.html +30 -24
  42. package/coverage/lcov-report/api/controllers/view/exporters.js.html +4 -4
  43. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  44. package/coverage/lcov-report/api/controllers/view/index.js.html +14 -14
  45. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  46. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +7 -7
  47. package/coverage/lcov-report/api/controllers/webhook.js.html +15 -15
  48. package/coverage/lcov-report/api/index.html +1 -1
  49. package/coverage/lcov-report/api/index.js.html +10 -10
  50. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/index.html +7 -22
  62. package/coverage/lcov-report/api/routes/index.js.html +4 -10
  63. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/static.js.html +17 -2
  74. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  79. package/coverage/lcov-report/automations/actions.js.html +24 -24
  80. package/coverage/lcov-report/automations/automationUtils.js.html +5 -5
  81. package/coverage/lcov-report/automations/bullboard.js.html +16 -16
  82. package/coverage/lcov-report/automations/index.html +1 -1
  83. package/coverage/lcov-report/automations/index.js.html +1 -1
  84. package/coverage/lcov-report/automations/steps/bash.js.html +6 -6
  85. package/coverage/lcov-report/automations/steps/createRow.js.html +13 -19
  86. package/coverage/lcov-report/automations/steps/delay.js.html +4 -4
  87. package/coverage/lcov-report/automations/steps/deleteRow.js.html +11 -20
  88. package/coverage/lcov-report/automations/steps/discord.js.html +7 -7
  89. package/coverage/lcov-report/automations/steps/executeQuery.js.html +6 -6
  90. package/coverage/lcov-report/automations/steps/executeScript.js.html +6 -6
  91. package/coverage/lcov-report/automations/steps/filter.js.html +7 -7
  92. package/coverage/lcov-report/automations/steps/index.html +13 -13
  93. package/coverage/lcov-report/automations/steps/integromat.js.html +5 -5
  94. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +8 -8
  95. package/coverage/lcov-report/automations/steps/queryRows.js.html +10 -10
  96. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +5 -5
  97. package/coverage/lcov-report/automations/steps/serverLog.js.html +3 -3
  98. package/coverage/lcov-report/automations/steps/slack.js.html +5 -5
  99. package/coverage/lcov-report/automations/steps/updateRow.js.html +6 -6
  100. package/coverage/lcov-report/automations/steps/utils.js.html +3 -3
  101. package/coverage/lcov-report/automations/steps/zapier.js.html +5 -5
  102. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  103. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  104. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  105. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  106. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  107. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  108. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  109. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  110. package/coverage/lcov-report/automations/triggers.js.html +18 -18
  111. package/coverage/lcov-report/automations/utils.js.html +32 -32
  112. package/coverage/lcov-report/constants/index.html +5 -5
  113. package/coverage/lcov-report/constants/index.js.html +38 -26
  114. package/coverage/lcov-report/constants/layouts.js.html +5 -5
  115. package/coverage/lcov-report/constants/screens.js.html +4 -4
  116. package/coverage/lcov-report/db/client.js.html +18 -18
  117. package/coverage/lcov-report/db/inMemoryView.js.html +7 -7
  118. package/coverage/lcov-report/db/index.html +15 -15
  119. package/coverage/lcov-report/db/index.js.html +3 -3
  120. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +18 -18
  121. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  122. package/coverage/lcov-report/db/linkedRows/index.js.html +31 -31
  123. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +15 -15
  124. package/coverage/lcov-report/db/newid.js.html +4 -4
  125. package/coverage/lcov-report/db/utils.js.html +85 -85
  126. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  127. package/coverage/lcov-report/events/index.html +1 -1
  128. package/coverage/lcov-report/events/index.js.html +5 -5
  129. package/coverage/lcov-report/events/utils.js.html +3 -3
  130. package/coverage/lcov-report/index.html +107 -77
  131. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  132. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  133. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  134. package/coverage/lcov-report/middleware/authorized.js.html +166 -64
  135. package/coverage/lcov-report/middleware/builder.js.html +8 -8
  136. package/coverage/lcov-report/middleware/currentapp.js.html +29 -29
  137. package/coverage/lcov-report/middleware/index.html +30 -30
  138. package/coverage/lcov-report/middleware/joi-validator.js.html +3 -3
  139. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  140. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  141. package/coverage/lcov-report/middleware/usageQuota.js.html +275 -41
  142. package/coverage/lcov-report/middleware/utils.js.html +2 -2
  143. package/coverage/lcov-report/migrations/usageQuotas/index.html +141 -0
  144. package/coverage/lcov-report/migrations/usageQuotas/index.js.html +152 -0
  145. package/coverage/lcov-report/{api/routes/hosting.js.html → migrations/usageQuotas/syncApps.js.html} +42 -27
  146. package/coverage/lcov-report/{api/controllers/hosting.js.html → migrations/usageQuotas/syncRows.js.html} +41 -44
  147. package/coverage/lcov-report/threads/automation.js.html +1 -1
  148. package/coverage/lcov-report/threads/index.html +15 -15
  149. package/coverage/lcov-report/threads/index.js.html +10 -10
  150. package/coverage/lcov-report/threads/query.js.html +23 -8
  151. package/coverage/lcov-report/threads/utils.js.html +14 -14
  152. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  153. package/coverage/lcov-report/utilities/budibaseDir.js.html +7 -7
  154. package/coverage/lcov-report/utilities/centralPath.js.html +4 -4
  155. package/coverage/lcov-report/utilities/csvParser.js.html +6 -6
  156. package/coverage/lcov-report/utilities/global.js.html +26 -26
  157. package/coverage/lcov-report/utilities/index.html +19 -19
  158. package/coverage/lcov-report/utilities/index.js.html +25 -25
  159. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +8 -8
  160. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  161. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  162. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  163. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  164. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +53 -53
  165. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +10 -10
  166. package/coverage/lcov-report/utilities/scriptRunner.js.html +5 -5
  167. package/coverage/lcov-report/utilities/security.js.html +8 -8
  168. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  169. package/coverage/lcov-report/utilities/usageQuota/index.html +141 -0
  170. package/coverage/lcov-report/utilities/usageQuota/index.js.html +356 -0
  171. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +302 -0
  172. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +134 -0
  173. package/coverage/lcov-report/utilities/users.js.html +5 -5
  174. package/coverage/lcov-report/utilities/workerRequests.js.html +39 -108
  175. package/coverage/lcov.info +2730 -2419
  176. package/dist/api/controllers/application.js +49 -20
  177. package/dist/api/controllers/auth.js +4 -0
  178. package/dist/api/controllers/datasource.js +11 -0
  179. package/dist/api/controllers/row/utils.js +6 -8
  180. package/dist/api/controllers/static/index.js +59 -10
  181. package/dist/api/controllers/table/index.js +10 -4
  182. package/dist/api/controllers/table/internal.js +2 -0
  183. package/dist/api/controllers/table/utils.js +9 -2
  184. package/dist/api/controllers/user.js +2 -0
  185. package/dist/api/routes/index.js +0 -2
  186. package/dist/api/routes/static.js +2 -1
  187. package/dist/automations/steps/createRow.js +2 -4
  188. package/dist/automations/steps/deleteRow.js +1 -4
  189. package/dist/constants/index.js +3 -0
  190. package/dist/definitions/datasource.js +1 -0
  191. package/dist/environment.js +1 -0
  192. package/dist/integrations/base/sql.js +13 -6
  193. package/dist/integrations/googlesheets.js +340 -0
  194. package/dist/integrations/index.js +3 -0
  195. package/dist/integrations/utils.js +57 -9
  196. package/dist/middleware/authorized.js +55 -30
  197. package/dist/middleware/usageQuota.js +82 -13
  198. package/dist/migrations/usageQuotas/index.js +24 -0
  199. package/dist/migrations/usageQuotas/syncApps.js +26 -0
  200. package/dist/migrations/usageQuotas/syncRows.js +29 -0
  201. package/dist/package.json +8 -5
  202. package/dist/tests/utilities/TestConfiguration.js +10 -6
  203. package/dist/threads/query.js +4 -0
  204. package/dist/tsconfig.tsbuildinfo +1 -1
  205. package/dist/utilities/{usageQuota.js → usageQuota/index.js} +44 -31
  206. package/dist/utilities/usageQuota/rows.js +77 -0
  207. package/dist/utilities/usageQuota/usageQuoteReset.js +16 -0
  208. package/dist/utilities/workerRequests.js +0 -20
  209. package/nodemon.json +1 -1
  210. package/package.json +9 -6
  211. package/src/api/controllers/application.js +52 -24
  212. package/src/api/controllers/auth.js +4 -0
  213. package/src/api/controllers/datasource.js +12 -0
  214. package/src/api/controllers/row/utils.js +6 -12
  215. package/src/api/controllers/static/index.js +63 -10
  216. package/src/api/controllers/table/index.js +15 -9
  217. package/src/api/controllers/table/internal.js +2 -0
  218. package/src/api/controllers/table/utils.js +9 -1
  219. package/src/api/controllers/user.js +2 -0
  220. package/src/api/routes/index.js +0 -2
  221. package/src/api/routes/static.js +5 -0
  222. package/src/api/routes/tests/application.spec.js +2 -2
  223. package/src/api/routes/tests/auth.spec.js +1 -2
  224. package/src/api/routes/tests/static.spec.js +98 -0
  225. package/src/automations/steps/createRow.js +2 -4
  226. package/src/automations/steps/deleteRow.js +1 -4
  227. package/src/constants/index.js +4 -0
  228. package/src/definitions/datasource.ts +3 -0
  229. package/src/environment.js +1 -0
  230. package/src/integrations/base/sql.ts +14 -7
  231. package/src/integrations/googlesheets.ts +353 -0
  232. package/src/integrations/index.ts +3 -0
  233. package/src/integrations/tests/sql.spec.js +16 -0
  234. package/src/integrations/utils.ts +71 -11
  235. package/src/middleware/authorized.js +64 -30
  236. package/src/middleware/tests/authorized.spec.js +6 -4
  237. package/src/middleware/tests/usageQuota.spec.js +18 -24
  238. package/src/middleware/usageQuota.js +93 -15
  239. package/src/migrations/tests/usageQuotas/index.spec.js +27 -0
  240. package/src/migrations/tests/usageQuotas/syncApps.spec.js +37 -0
  241. package/src/migrations/tests/usageQuotas/syncRows.spec.js +43 -0
  242. package/src/migrations/usageQuotas/index.js +24 -0
  243. package/src/migrations/usageQuotas/syncApps.js +18 -0
  244. package/src/migrations/usageQuotas/syncRows.js +21 -0
  245. package/src/tests/utilities/TestConfiguration.js +10 -6
  246. package/src/threads/query.js +5 -0
  247. package/src/utilities/tests/usageQuota/usageQuota.spec.js +72 -0
  248. package/src/utilities/usageQuota/index.js +92 -0
  249. package/src/utilities/usageQuota/rows.js +74 -0
  250. package/src/utilities/usageQuota/usageQuoteReset.js +18 -0
  251. package/src/utilities/workerRequests.js +0 -23
  252. package/dist/api/controllers/hosting.js +0 -30
  253. package/dist/api/routes/hosting.js +0 -11
  254. package/src/api/controllers/hosting.js +0 -22
  255. package/src/api/routes/hosting.js +0 -13
  256. package/src/api/routes/tests/hosting.spec.js +0 -36
  257. package/src/utilities/usageQuota.js +0 -73
@@ -20,7 +20,7 @@ const { cloneDeep } = require("lodash/fp");
20
20
  const { processObject } = require("@budibase/string-templates");
21
21
  const { getAllApps, isDevAppID, getDeployedAppID, Replication, } = require("@budibase/backend-core/db");
22
22
  const { USERS_TABLE_SCHEMA } = require("../../constants");
23
- const { getDeployedApps, removeAppFromUserRoles, } = require("../../utilities/workerRequests");
23
+ const { removeAppFromUserRoles } = require("../../utilities/workerRequests");
24
24
  const { clientLibraryPath, stringToReadStream } = require("../../utilities");
25
25
  const { getAllLocks } = require("../../utilities/redis");
26
26
  const { updateClientLibrary, backupClientLibrary, revertClientLibrary, } = require("../../utilities/fileSystem/clientLibrary");
@@ -49,31 +49,44 @@ function getUserRoleId(ctx) {
49
49
  ? BUILTIN_ROLE_IDS.PUBLIC
50
50
  : ctx.user.role._id;
51
51
  }
52
- function getAppUrlIfNotInUse(ctx) {
52
+ function getAppUrl(ctx) {
53
53
  return __awaiter(this, void 0, void 0, function* () {
54
+ // construct the url
54
55
  let url;
55
56
  if (ctx.request.body.url) {
57
+ // if the url is provided, use that
56
58
  url = encodeURI(ctx.request.body.url);
57
59
  }
58
60
  else if (ctx.request.body.name) {
61
+ // otherwise use the name
59
62
  url = encodeURI(`${ctx.request.body.name}`);
60
63
  }
61
64
  if (url) {
62
65
  url = `/${url.replace(URL_REGEX_SLASH, "")}`.toLowerCase();
63
66
  }
64
- if (!env.SELF_HOSTED) {
65
- return url;
66
- }
67
- const deployedApps = yield getDeployedApps();
68
- if (url &&
69
- deployedApps[url] != null &&
70
- ctx.params != null &&
71
- deployedApps[url].appId !== ctx.params.appId) {
72
- ctx.throw(400, "App name/URL is already in use.");
73
- }
74
67
  return url;
75
68
  });
76
69
  }
70
+ const checkAppUrl = (ctx, apps, url, currentAppId) => {
71
+ if (currentAppId) {
72
+ apps = apps.filter(app => app.appId !== currentAppId);
73
+ }
74
+ if (apps.some(app => app.url === url)) {
75
+ ctx.throw(400, "App URL is already in use.");
76
+ }
77
+ };
78
+ const checkAppName = (ctx, apps, name, currentAppId) => {
79
+ // TODO: Replace with Joi
80
+ if (!name) {
81
+ ctx.throw(400, "Name is required");
82
+ }
83
+ if (currentAppId) {
84
+ apps = apps.filter(app => app.appId !== currentAppId);
85
+ }
86
+ if (apps.some(app => app.name === name)) {
87
+ ctx.throw(400, "App name is already in use.");
88
+ }
89
+ };
77
90
  function createInstance(template) {
78
91
  return __awaiter(this, void 0, void 0, function* () {
79
92
  const tenantId = isMultiTenant() ? getTenantId() : null;
@@ -167,6 +180,11 @@ exports.fetchAppPackage = (ctx) => __awaiter(void 0, void 0, void 0, function* (
167
180
  };
168
181
  });
169
182
  exports.create = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
183
+ const apps = yield getAllApps(CouchDB, { dev: true });
184
+ const name = ctx.request.body.name;
185
+ checkAppName(ctx, apps, name);
186
+ const url = yield getAppUrl(ctx);
187
+ checkAppUrl(ctx, apps, url);
170
188
  const { useTemplate, templateKey, templateString } = ctx.request.body;
171
189
  const instanceConfig = {
172
190
  useTemplate,
@@ -178,7 +196,6 @@ exports.create = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
178
196
  }
179
197
  const instance = yield createInstance(instanceConfig);
180
198
  const appId = instance._id;
181
- const url = yield getAppUrlIfNotInUse(ctx);
182
199
  const db = new CouchDB(appId);
183
200
  let _rev;
184
201
  try {
@@ -196,7 +213,7 @@ exports.create = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
196
213
  type: "app",
197
214
  version: packageJson.version,
198
215
  componentLibraries: ["@budibase/standard-components"],
199
- name: ctx.request.body.name,
216
+ name: name,
200
217
  url: url,
201
218
  template: ctx.request.body.template,
202
219
  instance: instance,
@@ -219,8 +236,21 @@ exports.create = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
219
236
  ctx.status = 200;
220
237
  ctx.body = newApplication;
221
238
  });
239
+ // This endpoint currently operates as a PATCH rather than a PUT
240
+ // Thus name and url fields are handled only if present
222
241
  exports.update = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
223
- const data = yield updateAppPackage(ctx, ctx.request.body, ctx.params.appId);
242
+ const apps = yield getAllApps(CouchDB, { dev: true });
243
+ // validation
244
+ const name = ctx.request.body.name;
245
+ if (name) {
246
+ checkAppName(ctx, apps, name, ctx.params.appId);
247
+ }
248
+ const url = yield getAppUrl(ctx);
249
+ if (url) {
250
+ checkAppUrl(ctx, apps, url, ctx.params.appId);
251
+ ctx.request.body.url = url;
252
+ }
253
+ const data = yield updateAppPackage(ctx.request.body, ctx.params.appId);
224
254
  ctx.status = 200;
225
255
  ctx.body = data;
226
256
  });
@@ -239,7 +269,7 @@ exports.updateClient = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
239
269
  version: packageJson.version,
240
270
  revertableVersion: currentVersion,
241
271
  };
242
- const data = yield updateAppPackage(ctx, appPackageUpdates, ctx.params.appId);
272
+ const data = yield updateAppPackage(appPackageUpdates, ctx.params.appId);
243
273
  ctx.status = 200;
244
274
  ctx.body = data;
245
275
  });
@@ -259,7 +289,7 @@ exports.revertClient = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
259
289
  version: application.revertableVersion,
260
290
  revertableVersion: null,
261
291
  };
262
- const data = yield updateAppPackage(ctx, appPackageUpdates, ctx.params.appId);
292
+ const data = yield updateAppPackage(appPackageUpdates, ctx.params.appId);
263
293
  ctx.status = 200;
264
294
  ctx.body = data;
265
295
  });
@@ -326,11 +356,10 @@ exports.sync = (ctx, next) => __awaiter(void 0, void 0, void 0, function* () {
326
356
  };
327
357
  }
328
358
  });
329
- const updateAppPackage = (ctx, appPackage, appId) => __awaiter(void 0, void 0, void 0, function* () {
330
- const url = yield getAppUrlIfNotInUse(ctx);
359
+ const updateAppPackage = (appPackage, appId) => __awaiter(void 0, void 0, void 0, function* () {
331
360
  const db = new CouchDB(appId);
332
361
  const application = yield db.get(DocumentTypes.APP_METADATA);
333
- const newAppPackage = Object.assign(Object.assign(Object.assign({}, application), appPackage), { url });
362
+ const newAppPackage = Object.assign(Object.assign({}, application), appPackage);
334
363
  if (appPackage._rev !== application._rev) {
335
364
  newAppPackage._rev = application._rev;
336
365
  }
@@ -24,6 +24,8 @@ exports.fetchSelf = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
24
24
  const user = yield getFullUser(ctx, userId);
25
25
  // this shouldn't be returned by the app self
26
26
  delete user.roles;
27
+ // forward the csrf token from the session
28
+ user.csrfToken = ctx.user.csrfToken;
27
29
  if (appId) {
28
30
  const db = new CouchDB(appId);
29
31
  // remove the full roles structure
@@ -31,6 +33,8 @@ exports.fetchSelf = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
31
33
  try {
32
34
  const userTable = yield db.get(InternalTables.USER_METADATA);
33
35
  const metadata = yield db.get(userId);
36
+ // make sure there is never a stale csrf token
37
+ delete metadata.csrfToken;
34
38
  // specifically needs to make sure is enriched
35
39
  ctx.body = yield outputProcessing(ctx, userTable, Object.assign(Object.assign({}, user), metadata));
36
40
  }
@@ -28,6 +28,12 @@ exports.fetch = function (ctx) {
28
28
  const datasources = (yield database.allDocs(getDatasourceParams(null, {
29
29
  include_docs: true,
30
30
  }))).rows.map(row => row.doc);
31
+ for (let datasource of datasources) {
32
+ if (datasource.config && datasource.config.auth) {
33
+ // strip secrets from response so they don't show in the network request
34
+ delete datasource.config.auth;
35
+ }
36
+ }
31
37
  ctx.body = [bbInternalDb, ...datasources];
32
38
  });
33
39
  };
@@ -78,8 +84,13 @@ exports.update = function (ctx) {
78
84
  const db = new CouchDB(ctx.appId);
79
85
  const datasourceId = ctx.params.datasourceId;
80
86
  let datasource = yield db.get(datasourceId);
87
+ const auth = datasource.config.auth;
81
88
  yield invalidateVariables(datasource, ctx.request.body);
82
89
  datasource = Object.assign(Object.assign({}, datasource), ctx.request.body);
90
+ if (auth && !ctx.request.body.auth) {
91
+ // don't strip auth config from DB
92
+ datasource.config.auth = auth;
93
+ }
83
94
  const response = yield db.put(datasource);
84
95
  datasource._rev = response.rev;
85
96
  // Drain connection pools when configuration is changed
@@ -64,19 +64,17 @@ exports.validate = ({ appId, tableId, row, table }) => __awaiter(void 0, void 0,
64
64
  }
65
65
  let res;
66
66
  // Validate.js doesn't seem to handle array
67
- if (type === FieldTypes.ARRAY) {
68
- const hasValues = Array.isArray(row[fieldName]) && row[fieldName].length > 0;
69
- // Check values are valid if values are specified
70
- if (hasValues) {
67
+ if (type === FieldTypes.ARRAY && row[fieldName]) {
68
+ if (row[fieldName].length) {
71
69
  row[fieldName].map(val => {
72
70
  if (!constraints.inclusion.includes(val)) {
73
- errors[fieldName] = "Value not in list";
71
+ errors[fieldName] = "Field not in list";
74
72
  }
75
73
  });
76
74
  }
77
- // Check for required constraint
78
- if (constraints.presence === true && !hasValues) {
79
- errors[fieldName] = "Required field";
75
+ else if (constraints.presence && row[fieldName].length === 0) {
76
+ // non required MultiSelect creates an empty array, which should not throw errors
77
+ errors[fieldName] = [`${fieldName} is required`];
80
78
  }
81
79
  }
82
80
  else if (type === FieldTypes.JSON && typeof row[fieldName] === "string") {
@@ -14,7 +14,7 @@ const { resolve, join } = require("../../../utilities/centralPath");
14
14
  const uuid = require("uuid");
15
15
  const { ObjectStoreBuckets } = require("../../../constants");
16
16
  const { processString } = require("@budibase/string-templates");
17
- const { getDeployedApps } = require("../../../utilities/workerRequests");
17
+ const { getAllApps } = require("@budibase/backend-core/db");
18
18
  const CouchDB = require("../../../db");
19
19
  const { loadHandlebarsFile, NODE_MODULES_PATH, TOP_LEVEL_PATH, } = require("../../../utilities/fileSystem");
20
20
  const env = require("../../../environment");
@@ -22,6 +22,8 @@ const { clientLibraryPath } = require("../../../utilities");
22
22
  const { upload } = require("../../../utilities/fileSystem");
23
23
  const { attachmentsRelativeURL } = require("../../../utilities");
24
24
  const { DocumentTypes } = require("../../../db/utils");
25
+ const AWS = require("aws-sdk");
26
+ const AWS_REGION = env.AWS_REGION ? env.AWS_REGION : "eu-west-1";
25
27
  function prepareUpload({ s3Key, bucket, metadata, file }) {
26
28
  return __awaiter(this, void 0, void 0, function* () {
27
29
  const response = yield upload({
@@ -41,13 +43,15 @@ function prepareUpload({ s3Key, bucket, metadata, file }) {
41
43
  };
42
44
  });
43
45
  }
44
- function checkForSelfHostedURL(ctx) {
46
+ function getAppIdFromUrl(ctx) {
45
47
  return __awaiter(this, void 0, void 0, function* () {
46
- // the "appId" component of the URL may actually be a specific self hosted URL
48
+ // the "appId" component of the URL can be the id or the custom url
47
49
  let possibleAppUrl = `/${encodeURI(ctx.params.appId).toLowerCase()}`;
48
- const apps = yield getDeployedApps();
49
- if (apps[possibleAppUrl] && apps[possibleAppUrl].appId) {
50
- return apps[possibleAppUrl].appId;
50
+ // search prod apps for a url that matches, exclude dev where id is always used
51
+ const apps = yield getAllApps(CouchDB, { dev: false });
52
+ const app = apps.filter(a => a.url && a.url.toLowerCase() === possibleAppUrl)[0];
53
+ if (app && app.appId) {
54
+ return app.appId;
51
55
  }
52
56
  else {
53
57
  return ctx.params.appId;
@@ -80,10 +84,7 @@ exports.uploadFile = function (ctx) {
80
84
  };
81
85
  exports.serveApp = function (ctx) {
82
86
  return __awaiter(this, void 0, void 0, function* () {
83
- let appId = ctx.params.appId;
84
- if (env.SELF_HOSTED) {
85
- appId = yield checkForSelfHostedURL(ctx);
86
- }
87
+ let appId = yield getAppIdFromUrl(ctx);
87
88
  const App = require("./templates/BudibaseApp.svelte").default;
88
89
  const db = new CouchDB(appId, { skip_setup: true });
89
90
  const appInfo = yield db.get(DocumentTypes.APP_METADATA);
@@ -109,3 +110,51 @@ exports.serveClientLibrary = function (ctx) {
109
110
  });
110
111
  });
111
112
  };
113
+ exports.getSignedUploadURL = function (ctx) {
114
+ var _a, _b, _c;
115
+ return __awaiter(this, void 0, void 0, function* () {
116
+ const database = new CouchDB(ctx.appId);
117
+ // Ensure datasource is valid
118
+ let datasource;
119
+ try {
120
+ const { datasourceId } = ctx.params;
121
+ datasource = yield database.get(datasourceId);
122
+ if (!datasource) {
123
+ ctx.throw(400, "The specified datasource could not be found");
124
+ }
125
+ }
126
+ catch (error) {
127
+ ctx.throw(400, "The specified datasource could not be found");
128
+ }
129
+ // Ensure we aren't using a custom endpoint
130
+ if ((_a = datasource === null || datasource === void 0 ? void 0 : datasource.config) === null || _a === void 0 ? void 0 : _a.endpoint) {
131
+ ctx.throw(400, "S3 datasources with custom endpoints are not supported");
132
+ }
133
+ // Determine type of datasource and generate signed URL
134
+ let signedUrl;
135
+ let publicUrl;
136
+ if (datasource.source === "S3") {
137
+ const { bucket, key } = ctx.request.body || {};
138
+ if (!bucket || !key) {
139
+ ctx.throw(400, "bucket and key values are required");
140
+ return;
141
+ }
142
+ try {
143
+ const s3 = new AWS.S3({
144
+ region: AWS_REGION,
145
+ accessKeyId: (_b = datasource === null || datasource === void 0 ? void 0 : datasource.config) === null || _b === void 0 ? void 0 : _b.accessKeyId,
146
+ secretAccessKey: (_c = datasource === null || datasource === void 0 ? void 0 : datasource.config) === null || _c === void 0 ? void 0 : _c.secretAccessKey,
147
+ apiVersion: "2006-03-01",
148
+ signatureVersion: "v4",
149
+ });
150
+ const params = { Bucket: bucket, Key: key };
151
+ signedUrl = s3.getSignedUrl("putObject", params);
152
+ publicUrl = `https://${bucket}.s3.${AWS_REGION}.amazonaws.com/${key}`;
153
+ }
154
+ catch (error) {
155
+ ctx.throw(400, error);
156
+ }
157
+ }
158
+ ctx.body = { signedUrl, publicUrl };
159
+ });
160
+ };
@@ -12,7 +12,7 @@ const CouchDB = require("../../../db");
12
12
  const internal = require("./internal");
13
13
  const external = require("./external");
14
14
  const csvParser = require("../../../utilities/csvParser");
15
- const { isExternalTable } = require("../../../integrations/utils");
15
+ const { isExternalTable, isSQL } = require("../../../integrations/utils");
16
16
  const { getTableParams, getDatasourceParams, BudibaseInternalDB, } = require("../../../db/utils");
17
17
  const { getTable } = require("./utils");
18
18
  function pickApi({ tableId, table }) {
@@ -34,12 +34,18 @@ exports.fetch = function (ctx) {
34
34
  const internalTables = yield db.allDocs(getTableParams(null, {
35
35
  include_docs: true,
36
36
  }));
37
- const internal = internalTables.rows.map(row => (Object.assign(Object.assign({}, row.doc), { type: "internal", sourceId: BudibaseInternalDB._id })));
37
+ const internal = internalTables.rows.map(tableDoc => (Object.assign(Object.assign({}, tableDoc.doc), { type: "internal", sourceId: BudibaseInternalDB._id })));
38
38
  const externalTables = yield db.allDocs(getDatasourceParams("plus", {
39
39
  include_docs: true,
40
40
  }));
41
- const external = externalTables.rows.flatMap(row => {
42
- return Object.values(row.doc.entities || {}).map(entity => (Object.assign(Object.assign({}, entity), { type: "external", sourceId: row.doc._id })));
41
+ const external = externalTables.rows.flatMap(tableDoc => {
42
+ let entities = tableDoc.doc.entities;
43
+ if (entities) {
44
+ return Object.values(entities).map(entity => (Object.assign(Object.assign({}, entity), { type: "external", sourceId: tableDoc.doc._id, sql: isSQL(tableDoc.doc) })));
45
+ }
46
+ else {
47
+ return [];
48
+ }
43
49
  });
44
50
  ctx.body = [...internal, ...external];
45
51
  });
@@ -24,6 +24,7 @@ const linkRows = require("../../../db/linkedRows");
24
24
  const { getRowParams, generateTableID } = require("../../../db/utils");
25
25
  const { FieldTypes } = require("../../../constants");
26
26
  const { TableSaveFunctions, hasTypeChanged, getTable, handleDataImport, } = require("./utils");
27
+ const usageQuota = require("../../../utilities/usageQuota");
27
28
  exports.save = function (ctx) {
28
29
  return __awaiter(this, void 0, void 0, function* () {
29
30
  const appId = ctx.appId;
@@ -118,6 +119,7 @@ exports.destroy = function (ctx) {
118
119
  include_docs: true,
119
120
  }));
120
121
  yield db.bulkDocs(rows.rows.map(row => (Object.assign(Object.assign({}, row.doc), { _deleted: true }))));
122
+ yield usageQuota.update(usageQuota.Properties.ROW, -rows.rows.length);
121
123
  // update linked rows
122
124
  yield linkRows.updateLinks({
123
125
  appId,
@@ -15,9 +15,10 @@ const { isEqual } = require("lodash/fp");
15
15
  const { AutoFieldSubTypes, FieldTypes } = require("../../../constants");
16
16
  const { inputProcessing } = require("../../../utilities/rowProcessor");
17
17
  const { USERS_TABLE_SCHEMA, SwitchableTypes, CanSwitchTypes, } = require("../../../constants");
18
- const { isExternalTable, breakExternalTableId, } = require("../../../integrations/utils");
18
+ const { isExternalTable, breakExternalTableId, isSQL, } = require("../../../integrations/utils");
19
19
  const { getViews, saveView } = require("../view/utils");
20
20
  const viewTemplate = require("../view/viewBuilder");
21
+ const usageQuota = require("../../../utilities/usageQuota");
21
22
  exports.checkForColumnUpdates = (db, oldTable, updatedTable) => __awaiter(void 0, void 0, void 0, function* () {
22
23
  let updatedRows = [];
23
24
  const rename = updatedTable._rename;
@@ -95,7 +96,11 @@ exports.handleDataImport = (appId, user, table, dataImport) => __awaiter(void 0,
95
96
  }
96
97
  finalData.push(row);
97
98
  }
99
+ yield usageQuota.update(usageQuota.Properties.ROW, finalData.length, {
100
+ dryRun: true,
101
+ });
98
102
  yield db.bulkDocs(finalData);
103
+ yield usageQuota.update(usageQuota.Properties.ROW, finalData.length);
99
104
  let response = yield db.put(table);
100
105
  table._rev = response._rev;
101
106
  return table;
@@ -207,7 +212,9 @@ exports.getTable = (appId, tableId) => __awaiter(void 0, void 0, void 0, functio
207
212
  const db = new CouchDB(appId);
208
213
  if (isExternalTable(tableId)) {
209
214
  let { datasourceId, tableName } = breakExternalTableId(tableId);
210
- return exports.getExternalTable(appId, datasourceId, tableName);
215
+ const datasource = yield db.get(datasourceId);
216
+ const table = yield exports.getExternalTable(appId, datasourceId, tableName);
217
+ return Object.assign(Object.assign({}, table), { sql: isSQL(datasource) });
211
218
  }
212
219
  else {
213
220
  return db.get(tableId);
@@ -155,6 +155,8 @@ exports.updateSelfMetadata = function (ctx) {
155
155
  ctx.request.body._id = ctx.user._id;
156
156
  // make sure no stale rev
157
157
  delete ctx.request.body._rev;
158
+ // make sure no csrf token
159
+ delete ctx.request.body.csrfToken;
158
160
  yield exports.updateMetadata(ctx);
159
161
  });
160
162
  };
@@ -21,7 +21,6 @@ const integrationRoutes = require("./integration");
21
21
  const permissionRoutes = require("./permission");
22
22
  const datasourceRoutes = require("./datasource");
23
23
  const queryRoutes = require("./query");
24
- const hostingRoutes = require("./hosting");
25
24
  const backupRoutes = require("./backup");
26
25
  const metadataRoutes = require("./metadata");
27
26
  const devRoutes = require("./dev");
@@ -46,7 +45,6 @@ exports.mainRoutes = [
46
45
  permissionRoutes,
47
46
  datasourceRoutes,
48
47
  queryRoutes,
49
- hostingRoutes,
50
48
  backupRoutes,
51
49
  metadataRoutes,
52
50
  devRoutes,
@@ -43,5 +43,6 @@ router
43
43
  .post("/api/attachments/process", authorized(BUILDER), controller.uploadFile)
44
44
  .post("/api/attachments/:tableId/upload", paramResource("tableId"), authorized(PermissionTypes.TABLE, PermissionLevels.WRITE), controller.uploadFile)
45
45
  // TODO: this likely needs to be secured in some way
46
- .get("/:appId/:path*", controller.serveApp);
46
+ .get("/:appId/:path*", controller.serveApp)
47
+ .post("/api/attachments/:datasourceId/url", authorized(PermissionTypes.TABLE, PermissionLevels.READ), controller.getSignedUploadURL);
47
48
  module.exports = router;
@@ -10,7 +10,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  const rowController = require("../../api/controllers/row");
12
12
  const automationUtils = require("../automationUtils");
13
- const env = require("../../environment");
14
13
  const usage = require("../../utilities/usageQuota");
15
14
  const { buildCtx } = require("./utils");
16
15
  exports.definition = {
@@ -87,10 +86,9 @@ exports.run = function ({ inputs, appId, emitter }) {
87
86
  });
88
87
  try {
89
88
  inputs.row = yield automationUtils.cleanUpRow(appId, inputs.row.tableId, inputs.row);
90
- if (env.USE_QUOTAS) {
91
- yield usage.update(usage.Properties.ROW, 1);
92
- }
89
+ yield usage.update(usage.Properties.ROW, 1, { dryRun: true });
93
90
  yield rowController.save(ctx);
91
+ yield usage.update(usage.Properties.ROW, 1);
94
92
  return {
95
93
  row: inputs.row,
96
94
  response: ctx.body,
@@ -9,7 +9,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  const rowController = require("../../api/controllers/row");
12
- const env = require("../../environment");
13
12
  const usage = require("../../utilities/usageQuota");
14
13
  const { buildCtx } = require("./utils");
15
14
  const automationUtils = require("../automationUtils");
@@ -81,9 +80,7 @@ exports.run = function ({ inputs, appId, emitter }) {
81
80
  },
82
81
  });
83
82
  try {
84
- if (env.isProd()) {
85
- yield usage.update(usage.Properties.ROW, -1);
86
- }
83
+ yield usage.update(usage.Properties.ROW, -1);
87
84
  yield rowController.destroy(ctx);
88
85
  return {
89
86
  response: ctx.body,
@@ -67,6 +67,9 @@ exports.DataSourceOperation = {
67
67
  UPDATE_TABLE: "UPDATE_TABLE",
68
68
  DELETE_TABLE: "DELETE_TABLE",
69
69
  };
70
+ exports.DatasourceAuthTypes = {
71
+ GOOGLE: "google",
72
+ };
70
73
  exports.SortDirection = {
71
74
  ASCENDING: "ASCENDING",
72
75
  DESCENDING: "DESCENDING",
@@ -49,6 +49,7 @@ var SourceNames;
49
49
  SourceNames["ARANGODB"] = "ARANGODB";
50
50
  SourceNames["REST"] = "REST";
51
51
  SourceNames["ORACLE"] = "ORACLE";
52
+ SourceNames["GOOGLE_SHEETS"] = "GOOGLE_SHEETS";
52
53
  })(SourceNames = exports.SourceNames || (exports.SourceNames = {}));
53
54
  var IncludeRelationships;
54
55
  (function (IncludeRelationships) {
@@ -30,6 +30,7 @@ module.exports = {
30
30
  MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
31
31
  MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
32
32
  USE_QUOTAS: process.env.USE_QUOTAS,
33
+ EXCLUDE_QUOTAS_TENANTS: process.env.EXCLUDE_QUOTAS_TENANTS,
33
34
  REDIS_URL: process.env.REDIS_URL,
34
35
  REDIS_PASSWORD: process.env.REDIS_PASSWORD,
35
36
  INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
@@ -69,10 +69,16 @@ class InternalBuilder {
69
69
  this.client = client;
70
70
  }
71
71
  // right now we only do filters on the specific table being queried
72
- addFilters(tableName, query, filters) {
72
+ addFilters(query, filters, opts) {
73
73
  function iterate(structure, fn) {
74
74
  for (let [key, value] of Object.entries(structure)) {
75
- fn(`${tableName}.${key}`, value);
75
+ const isRelationshipField = key.includes(".");
76
+ if (!opts.relationship && !isRelationshipField) {
77
+ fn(`${opts.tableName}.${key}`, value);
78
+ }
79
+ if (opts.relationship && isRelationshipField) {
80
+ fn(key, value);
81
+ }
76
82
  }
77
83
  }
78
84
  if (!filters) {
@@ -248,7 +254,7 @@ class InternalBuilder {
248
254
  if (foundOffset) {
249
255
  query = query.offset(foundOffset);
250
256
  }
251
- query = this.addFilters(tableName, query, filters);
257
+ query = this.addFilters(query, filters, { tableName });
252
258
  // add sorting to pre-query
253
259
  query = this.addSorting(query, json);
254
260
  // @ts-ignore
@@ -261,13 +267,14 @@ class InternalBuilder {
261
267
  preQuery = this.addSorting(preQuery, json);
262
268
  }
263
269
  // handle joins
264
- return this.addRelationships(knex, preQuery, selectStatement, tableName, relationships);
270
+ query = this.addRelationships(knex, preQuery, selectStatement, tableName, relationships);
271
+ return this.addFilters(query, filters, { relationship: true });
265
272
  }
266
273
  update(knex, json, opts) {
267
274
  const { endpoint, body, filters } = json;
268
275
  let query = knex(endpoint.entityId);
269
276
  const parsedBody = parseBody(body);
270
- query = this.addFilters(endpoint.entityId, query, filters);
277
+ query = this.addFilters(query, filters, { tableName: endpoint.entityId });
271
278
  // mysql can't use returning
272
279
  if (opts.disableReturning) {
273
280
  return query.update(parsedBody);
@@ -279,7 +286,7 @@ class InternalBuilder {
279
286
  delete(knex, json, opts) {
280
287
  const { endpoint, filters } = json;
281
288
  let query = knex(endpoint.entityId);
282
- query = this.addFilters(endpoint.entityId, query, filters);
289
+ query = this.addFilters(query, filters, { tableName: endpoint.entityId });
283
290
  // mysql can't use returning
284
291
  if (opts.disableReturning) {
285
292
  return query.delete();