@budibase/server 1.0.46 → 1.0.49-alpha.1

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 (277) hide show
  1. package/builder/assets/index.ba78c49d.css +1 -0
  2. package/builder/assets/{index.e9bff1c6.js → index.ed1cf508.js} +237 -234
  3. package/builder/assets/{vendor.f9faf6ab.js → vendor.d526bfe8.js} +12 -12
  4. package/builder/index.html +3 -3
  5. package/coverage/clover.xml +2368 -2016
  6. package/coverage/coverage-final.json +101 -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 +48 -33
  29. package/coverage/lcov-report/api/controllers/row/index.js.html +26 -26
  30. package/coverage/lcov-report/api/controllers/row/internal.js.html +95 -158
  31. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +41 -17
  32. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +551 -0
  33. package/coverage/lcov-report/api/controllers/row/utils.js.html +39 -57
  34. package/coverage/lcov-report/api/controllers/screen.js.html +7 -7
  35. package/coverage/lcov-report/api/controllers/script.js.html +4 -4
  36. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +629 -0
  37. package/coverage/lcov-report/api/controllers/table/external.js.html +12 -12
  38. package/coverage/lcov-report/api/controllers/table/index.html +49 -34
  39. package/coverage/lcov-report/api/controllers/table/index.js.html +53 -77
  40. package/coverage/lcov-report/api/controllers/table/internal.js.html +59 -35
  41. package/coverage/lcov-report/api/controllers/table/utils.js.html +283 -76
  42. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/user.js.html +30 -24
  44. package/coverage/lcov-report/api/controllers/view/exporters.js.html +4 -4
  45. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  46. package/coverage/lcov-report/api/controllers/view/index.js.html +14 -14
  47. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  48. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +7 -7
  49. package/coverage/lcov-report/api/controllers/webhook.js.html +15 -15
  50. package/coverage/lcov-report/api/index.html +1 -1
  51. package/coverage/lcov-report/api/index.js.html +10 -10
  52. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/index.html +7 -22
  64. package/coverage/lcov-report/api/routes/index.js.html +4 -10
  65. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/static.js.html +17 -2
  76. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  81. package/coverage/lcov-report/automations/actions.js.html +24 -24
  82. package/coverage/lcov-report/automations/automationUtils.js.html +5 -5
  83. package/coverage/lcov-report/automations/bullboard.js.html +16 -16
  84. package/coverage/lcov-report/automations/index.html +1 -1
  85. package/coverage/lcov-report/automations/index.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/bash.js.html +6 -6
  87. package/coverage/lcov-report/automations/steps/createRow.js.html +13 -19
  88. package/coverage/lcov-report/automations/steps/delay.js.html +4 -4
  89. package/coverage/lcov-report/automations/steps/deleteRow.js.html +11 -20
  90. package/coverage/lcov-report/automations/steps/discord.js.html +7 -7
  91. package/coverage/lcov-report/automations/steps/executeQuery.js.html +6 -6
  92. package/coverage/lcov-report/automations/steps/executeScript.js.html +6 -6
  93. package/coverage/lcov-report/automations/steps/filter.js.html +7 -7
  94. package/coverage/lcov-report/automations/steps/index.html +13 -13
  95. package/coverage/lcov-report/automations/steps/integromat.js.html +5 -5
  96. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +8 -8
  97. package/coverage/lcov-report/automations/steps/queryRows.js.html +10 -10
  98. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +5 -5
  99. package/coverage/lcov-report/automations/steps/serverLog.js.html +3 -3
  100. package/coverage/lcov-report/automations/steps/slack.js.html +5 -5
  101. package/coverage/lcov-report/automations/steps/updateRow.js.html +6 -6
  102. package/coverage/lcov-report/automations/steps/utils.js.html +3 -3
  103. package/coverage/lcov-report/automations/steps/zapier.js.html +5 -5
  104. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  105. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  106. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  107. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  108. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  109. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  110. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  111. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  112. package/coverage/lcov-report/automations/triggers.js.html +18 -18
  113. package/coverage/lcov-report/automations/utils.js.html +32 -32
  114. package/coverage/lcov-report/constants/index.html +11 -11
  115. package/coverage/lcov-report/constants/index.js.html +68 -32
  116. package/coverage/lcov-report/constants/layouts.js.html +5 -5
  117. package/coverage/lcov-report/constants/screens.js.html +4 -4
  118. package/coverage/lcov-report/db/client.js.html +18 -18
  119. package/coverage/lcov-report/db/inMemoryView.js.html +7 -7
  120. package/coverage/lcov-report/db/index.html +15 -15
  121. package/coverage/lcov-report/db/index.js.html +3 -3
  122. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +18 -18
  123. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  124. package/coverage/lcov-report/db/linkedRows/index.js.html +38 -41
  125. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +15 -15
  126. package/coverage/lcov-report/db/newid.js.html +4 -4
  127. package/coverage/lcov-report/db/utils.js.html +86 -86
  128. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  129. package/coverage/lcov-report/events/index.html +1 -1
  130. package/coverage/lcov-report/events/index.js.html +5 -5
  131. package/coverage/lcov-report/events/utils.js.html +3 -3
  132. package/coverage/lcov-report/index.html +131 -101
  133. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  134. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  135. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  136. package/coverage/lcov-report/middleware/authorized.js.html +166 -64
  137. package/coverage/lcov-report/middleware/builder.js.html +8 -8
  138. package/coverage/lcov-report/middleware/currentapp.js.html +29 -29
  139. package/coverage/lcov-report/middleware/index.html +30 -30
  140. package/coverage/lcov-report/middleware/joi-validator.js.html +3 -3
  141. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  142. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  143. package/coverage/lcov-report/middleware/usageQuota.js.html +275 -41
  144. package/coverage/lcov-report/middleware/utils.js.html +2 -2
  145. package/coverage/lcov-report/migrations/usageQuotas/index.html +141 -0
  146. package/coverage/lcov-report/migrations/usageQuotas/index.js.html +152 -0
  147. package/coverage/lcov-report/{api/routes/hosting.js.html → migrations/usageQuotas/syncApps.js.html} +42 -27
  148. package/coverage/lcov-report/{api/controllers/hosting.js.html → migrations/usageQuotas/syncRows.js.html} +41 -44
  149. package/coverage/lcov-report/threads/automation.js.html +1 -1
  150. package/coverage/lcov-report/threads/index.html +15 -15
  151. package/coverage/lcov-report/threads/index.js.html +10 -10
  152. package/coverage/lcov-report/threads/query.js.html +23 -8
  153. package/coverage/lcov-report/threads/utils.js.html +14 -14
  154. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  155. package/coverage/lcov-report/utilities/budibaseDir.js.html +7 -7
  156. package/coverage/lcov-report/utilities/centralPath.js.html +4 -4
  157. package/coverage/lcov-report/utilities/csvParser.js.html +6 -6
  158. package/coverage/lcov-report/utilities/global.js.html +26 -26
  159. package/coverage/lcov-report/utilities/index.html +19 -19
  160. package/coverage/lcov-report/utilities/index.js.html +25 -25
  161. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +8 -8
  162. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  163. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  164. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  165. package/coverage/lcov-report/utilities/rowProcessor/index.html +27 -27
  166. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +115 -79
  167. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +75 -27
  168. package/coverage/lcov-report/utilities/scriptRunner.js.html +5 -5
  169. package/coverage/lcov-report/utilities/security.js.html +8 -8
  170. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  171. package/coverage/lcov-report/utilities/usageQuota/index.html +141 -0
  172. package/coverage/lcov-report/utilities/usageQuota/index.js.html +356 -0
  173. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +302 -0
  174. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +134 -0
  175. package/coverage/lcov-report/utilities/users.js.html +5 -5
  176. package/coverage/lcov-report/utilities/workerRequests.js.html +39 -108
  177. package/coverage/lcov.info +3719 -3046
  178. package/dist/api/controllers/application.js +49 -20
  179. package/dist/api/controllers/auth.js +4 -0
  180. package/dist/api/controllers/datasource.js +11 -0
  181. package/dist/api/controllers/row/ExternalRequest.js +2 -1
  182. package/dist/api/controllers/row/internal.js +30 -53
  183. package/dist/api/controllers/row/internalSearch.js +12 -4
  184. package/dist/api/controllers/row/staticFormula.js +147 -0
  185. package/dist/api/controllers/row/utils.js +6 -8
  186. package/dist/api/controllers/static/index.js +59 -10
  187. package/dist/api/controllers/table/bulkFormula.js +160 -0
  188. package/dist/api/controllers/table/index.js +12 -9
  189. package/dist/api/controllers/table/internal.js +9 -0
  190. package/dist/api/controllers/table/utils.js +56 -14
  191. package/dist/api/controllers/user.js +2 -0
  192. package/dist/api/routes/index.js +0 -2
  193. package/dist/api/routes/static.js +2 -1
  194. package/dist/automations/steps/createRow.js +2 -4
  195. package/dist/automations/steps/deleteRow.js +1 -4
  196. package/dist/constants/index.js +12 -5
  197. package/dist/db/linkedRows/index.js +4 -5
  198. package/dist/definitions/datasource.js +1 -0
  199. package/dist/environment.js +1 -0
  200. package/dist/integrations/base/sql.js +13 -6
  201. package/dist/integrations/googlesheets.js +340 -0
  202. package/dist/integrations/index.js +3 -0
  203. package/dist/integrations/utils.js +57 -8
  204. package/dist/middleware/authorized.js +55 -30
  205. package/dist/middleware/usageQuota.js +82 -13
  206. package/dist/migrations/usageQuotas/index.js +24 -0
  207. package/dist/migrations/usageQuotas/syncApps.js +26 -0
  208. package/dist/migrations/usageQuotas/syncRows.js +29 -0
  209. package/dist/package.json +8 -5
  210. package/dist/tests/utilities/TestConfiguration.js +10 -6
  211. package/dist/threads/query.js +4 -0
  212. package/dist/tsconfig.tsbuildinfo +1 -1
  213. package/dist/utilities/rowProcessor/index.js +18 -11
  214. package/dist/utilities/rowProcessor/utils.js +14 -4
  215. package/dist/utilities/{usageQuota.js → usageQuota/index.js} +44 -31
  216. package/dist/utilities/usageQuota/rows.js +77 -0
  217. package/dist/utilities/usageQuota/usageQuoteReset.js +16 -0
  218. package/dist/utilities/workerRequests.js +0 -20
  219. package/nodemon.json +1 -1
  220. package/package.json +9 -6
  221. package/src/api/controllers/application.js +52 -24
  222. package/src/api/controllers/auth.js +4 -0
  223. package/src/api/controllers/datasource.js +12 -0
  224. package/src/api/controllers/row/ExternalRequest.ts +1 -1
  225. package/src/api/controllers/row/internal.js +29 -50
  226. package/src/api/controllers/row/internalSearch.js +12 -4
  227. package/src/api/controllers/row/staticFormula.js +157 -0
  228. package/src/api/controllers/row/utils.js +6 -12
  229. package/src/api/controllers/static/index.js +63 -10
  230. package/src/api/controllers/table/bulkFormula.js +183 -0
  231. package/src/api/controllers/table/index.js +16 -24
  232. package/src/api/controllers/table/internal.js +9 -1
  233. package/src/api/controllers/table/utils.js +80 -11
  234. package/src/api/controllers/user.js +2 -0
  235. package/src/api/routes/index.js +0 -2
  236. package/src/api/routes/static.js +5 -0
  237. package/src/api/routes/tests/application.spec.js +2 -2
  238. package/src/api/routes/tests/auth.spec.js +1 -2
  239. package/src/api/routes/tests/static.spec.js +98 -0
  240. package/src/automations/steps/createRow.js +2 -4
  241. package/src/automations/steps/deleteRow.js +1 -4
  242. package/src/constants/index.js +17 -5
  243. package/src/db/linkedRows/index.js +4 -5
  244. package/src/definitions/common.ts +3 -0
  245. package/src/definitions/datasource.ts +3 -0
  246. package/src/environment.js +1 -0
  247. package/src/integrations/base/sql.ts +14 -7
  248. package/src/integrations/googlesheets.ts +353 -0
  249. package/src/integrations/index.ts +3 -0
  250. package/src/integrations/tests/sql.spec.js +16 -0
  251. package/src/integrations/utils.ts +71 -10
  252. package/src/middleware/authorized.js +64 -30
  253. package/src/middleware/tests/authorized.spec.js +6 -4
  254. package/src/middleware/tests/usageQuota.spec.js +18 -24
  255. package/src/middleware/usageQuota.js +93 -15
  256. package/src/migrations/tests/usageQuotas/index.spec.js +27 -0
  257. package/src/migrations/tests/usageQuotas/syncApps.spec.js +37 -0
  258. package/src/migrations/tests/usageQuotas/syncRows.spec.js +43 -0
  259. package/src/migrations/usageQuotas/index.js +24 -0
  260. package/src/migrations/usageQuotas/syncApps.js +18 -0
  261. package/src/migrations/usageQuotas/syncRows.js +21 -0
  262. package/src/tests/utilities/TestConfiguration.js +10 -6
  263. package/src/threads/query.js +5 -0
  264. package/src/utilities/rowProcessor/index.js +24 -12
  265. package/src/utilities/rowProcessor/utils.js +23 -7
  266. package/src/utilities/tests/usageQuota/usageQuota.spec.js +72 -0
  267. package/src/utilities/usageQuota/index.js +92 -0
  268. package/src/utilities/usageQuota/rows.js +74 -0
  269. package/src/utilities/usageQuota/usageQuoteReset.js +18 -0
  270. package/src/utilities/workerRequests.js +0 -23
  271. package/builder/assets/index.39e01193.css +0 -1
  272. package/dist/api/controllers/hosting.js +0 -30
  273. package/dist/api/routes/hosting.js +0 -11
  274. package/src/api/controllers/hosting.js +0 -22
  275. package/src/api/routes/hosting.js +0 -13
  276. package/src/api/routes/tests/hosting.spec.js +0 -36
  277. 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
@@ -431,6 +431,7 @@ var External;
431
431
  * all and then re-create, as theres no chance of losing data (e.g. delete succeed, but write fail).
432
432
  */
433
433
  handleManyRelationships(mainTableId, row, relationships) {
434
+ var _a;
434
435
  return __awaiter(this, void 0, void 0, function* () {
435
436
  const { appId } = this;
436
437
  // if we're creating (in a through table) need to wipe the existing ones first
@@ -442,7 +443,7 @@ var External;
442
443
  const linkTable = this.getTable(tableId);
443
444
  // @ts-ignore
444
445
  const linkPrimary = linkTable.primary[0];
445
- const rows = related[key].rows || [];
446
+ const rows = ((_a = related[key]) === null || _a === void 0 ? void 0 : _a.rows) || [];
446
447
  const found = rows.find((row) => row[linkPrimary] === relationship.id ||
447
448
  row[linkPrimary] === body[linkPrimary]);
448
449
  const operation = isUpdate
@@ -21,11 +21,10 @@ var __rest = (this && this.__rest) || function (s, e) {
21
21
  };
22
22
  const CouchDB = require("../../../db");
23
23
  const linkRows = require("../../../db/linkedRows");
24
- const { getRowParams, generateRowID, DocumentTypes, InternalTables, } = require("../../../db/utils");
24
+ const { generateRowID, getRowParams, DocumentTypes, InternalTables, } = require("../../../db/utils");
25
25
  const userController = require("../user");
26
- const { inputProcessing, outputProcessing, processAutoColumn, cleanupAttachments, } = require("../../../utilities/rowProcessor");
26
+ const { inputProcessing, outputProcessing, cleanupAttachments, } = require("../../../utilities/rowProcessor");
27
27
  const { FieldTypes } = require("../../../constants");
28
- const { isEqual } = require("lodash");
29
28
  const { validate, findRow } = require("./utils");
30
29
  const { fullSearch, paginatedSearch } = require("./internalSearch");
31
30
  const { getGlobalUsersFromMetadata } = require("../../../utilities/global");
@@ -33,59 +32,12 @@ const inMemoryViews = require("../../../db/inMemoryView");
33
32
  const env = require("../../../environment");
34
33
  const { migrateToInMemoryView, migrateToDesignView, getFromDesignDoc, getFromMemoryDoc, } = require("../view/utils");
35
34
  const { cloneDeep } = require("lodash/fp");
35
+ const { finaliseRow, updateRelatedFormula } = require("./staticFormula");
36
36
  const CALCULATION_TYPES = {
37
37
  SUM: "sum",
38
38
  COUNT: "count",
39
39
  STATS: "stats",
40
40
  };
41
- function storeResponse(ctx, db, row, oldTable, table) {
42
- return __awaiter(this, void 0, void 0, function* () {
43
- row.type = "row";
44
- // don't worry about rev, tables handle rev/lastID updates
45
- // if another row has been written since processing this will
46
- // handle the auto ID clash
47
- if (!isEqual(oldTable, table)) {
48
- try {
49
- yield db.put(table);
50
- }
51
- catch (err) {
52
- if (err.status === 409) {
53
- const updatedTable = yield db.get(table._id);
54
- let response = processAutoColumn(null, updatedTable, row, {
55
- reprocessing: true,
56
- });
57
- yield db.put(response.table);
58
- row = response.row;
59
- }
60
- else {
61
- throw err;
62
- }
63
- }
64
- }
65
- const response = yield db.put(row);
66
- row._rev = response.rev;
67
- // process the row before return, to include relationships
68
- row = yield outputProcessing(ctx, table, row, { squash: false });
69
- return { row, table };
70
- });
71
- }
72
- // doesn't do the outputProcessing
73
- function getRawTableData(ctx, db, tableId) {
74
- return __awaiter(this, void 0, void 0, function* () {
75
- let rows;
76
- if (tableId === InternalTables.USER_METADATA) {
77
- yield userController.fetchMetadata(ctx);
78
- rows = ctx.body;
79
- }
80
- else {
81
- const response = yield db.allDocs(getRowParams(tableId, null, {
82
- include_docs: true,
83
- }));
84
- rows = response.rows.map(row => row.doc);
85
- }
86
- return rows;
87
- });
88
- }
89
41
  function getView(db, viewName) {
90
42
  return __awaiter(this, void 0, void 0, function* () {
91
43
  let mainGetter = env.SELF_HOSTED ? getFromDesignDoc : getFromMemoryDoc;
@@ -114,6 +66,22 @@ function getView(db, viewName) {
114
66
  return viewInfo;
115
67
  });
116
68
  }
69
+ function getRawTableData(ctx, db, tableId) {
70
+ return __awaiter(this, void 0, void 0, function* () {
71
+ let rows;
72
+ if (tableId === InternalTables.USER_METADATA) {
73
+ yield userController.fetchMetadata(ctx);
74
+ rows = ctx.body;
75
+ }
76
+ else {
77
+ const response = yield db.allDocs(getRowParams(tableId, null, {
78
+ include_docs: true,
79
+ }));
80
+ rows = response.rows.map(row => row.doc);
81
+ }
82
+ return rows;
83
+ });
84
+ }
117
85
  exports.patch = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
118
86
  const appId = ctx.appId;
119
87
  const db = new CouchDB(appId);
@@ -169,7 +137,10 @@ exports.patch = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
169
137
  yield userController.updateMetadata(ctx);
170
138
  return { row: ctx.body, table };
171
139
  }
172
- return storeResponse(ctx, db, row, dbTable, table);
140
+ return finaliseRow(ctx.appId, table, row, {
141
+ oldTable: dbTable,
142
+ updateFormula: true,
143
+ });
173
144
  });
174
145
  exports.save = function (ctx) {
175
146
  return __awaiter(this, void 0, void 0, function* () {
@@ -198,7 +169,10 @@ exports.save = function (ctx) {
198
169
  tableId: row.tableId,
199
170
  table,
200
171
  });
201
- return storeResponse(ctx, db, row, dbTable, table);
172
+ return finaliseRow(ctx.appId, table, row, {
173
+ oldTable: dbTable,
174
+ updateFormula: true,
175
+ });
202
176
  });
203
177
  };
204
178
  exports.fetchView = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
@@ -290,6 +264,8 @@ exports.destroy = function (ctx) {
290
264
  });
291
265
  // remove any attachments that were on the row from object storage
292
266
  yield cleanupAttachments(appId, table, { row });
267
+ // remove any static formula
268
+ yield updateRelatedFormula(appId, table, row);
293
269
  let response;
294
270
  if (ctx.params.tableId === InternalTables.USER_METADATA) {
295
271
  ctx.params = {
@@ -333,6 +309,7 @@ exports.bulkDestroy = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
333
309
  }
334
310
  // remove any attachments that were on the rows from object storage
335
311
  yield cleanupAttachments(appId, table, { rows });
312
+ yield updateRelatedFormula(appId, table, rows);
336
313
  yield Promise.all(updates);
337
314
  return { response: { ok: true }, rows };
338
315
  });
@@ -34,19 +34,27 @@ class QueryBuilder {
34
34
  return this;
35
35
  }
36
36
  setLimit(limit) {
37
- this.limit = limit;
37
+ if (limit != null) {
38
+ this.limit = limit;
39
+ }
38
40
  return this;
39
41
  }
40
42
  setSort(sort) {
41
- this.sort = sort;
43
+ if (sort != null) {
44
+ this.sort = sort;
45
+ }
42
46
  return this;
43
47
  }
44
48
  setSortOrder(sortOrder) {
45
- this.sortOrder = sortOrder;
49
+ if (sortOrder != null) {
50
+ this.sortOrder = sortOrder;
51
+ }
46
52
  return this;
47
53
  }
48
54
  setSortType(sortType) {
49
- this.sortType = sortType;
55
+ if (sortType != null) {
56
+ this.sortType = sortType;
57
+ }
50
58
  return this;
51
59
  }
52
60
  setBookmark(bookmark) {
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ const CouchDB = require("../../../db");
12
+ const { getRowParams } = require("../../../db/utils");
13
+ const { outputProcessing, processAutoColumn, processFormulas, } = require("../../../utilities/rowProcessor");
14
+ const { FieldTypes, FormulaTypes } = require("../../../constants");
15
+ const { isEqual } = require("lodash");
16
+ const { cloneDeep } = require("lodash/fp");
17
+ /**
18
+ * This function runs through a list of enriched rows, looks at the rows which
19
+ * are related and then checks if they need the state of their formulas
20
+ * updated.
21
+ * NOTE: this will only for affect static formulas.
22
+ */
23
+ exports.updateRelatedFormula = (appId, table, enrichedRows) => __awaiter(void 0, void 0, void 0, function* () {
24
+ const db = new CouchDB(appId);
25
+ // no formula to update, we're done
26
+ if (!table.relatedFormula) {
27
+ return;
28
+ }
29
+ let promises = [];
30
+ for (let enrichedRow of Array.isArray(enrichedRows)
31
+ ? enrichedRows
32
+ : [enrichedRows]) {
33
+ // the related rows by tableId
34
+ let relatedRows = {};
35
+ for (let [key, field] of Object.entries(enrichedRow)) {
36
+ const columnDefinition = table.schema[key];
37
+ if (columnDefinition && columnDefinition.type === FieldTypes.LINK) {
38
+ const relatedTableId = columnDefinition.tableId;
39
+ if (!relatedRows[relatedTableId]) {
40
+ relatedRows[relatedTableId] = [];
41
+ }
42
+ relatedRows[relatedTableId] = relatedRows[relatedTableId].concat(field);
43
+ }
44
+ }
45
+ for (let tableId of table.relatedFormula) {
46
+ let relatedTable;
47
+ try {
48
+ // no rows to update, skip
49
+ if (!relatedRows[tableId] || relatedRows[tableId].length === 0) {
50
+ continue;
51
+ }
52
+ relatedTable = yield db.get(tableId);
53
+ }
54
+ catch (err) {
55
+ // no error scenario, table doesn't seem to exist anymore, ignore
56
+ }
57
+ for (let column of Object.values(relatedTable.schema)) {
58
+ // needs updated in related rows
59
+ if (column.type === FieldTypes.FORMULA &&
60
+ column.formulaType === FormulaTypes.STATIC) {
61
+ // re-enrich rows for all the related, don't update the related formula for them
62
+ promises = promises.concat(relatedRows[tableId].map(related => exports.finaliseRow(appId, relatedTable, related, {
63
+ updateFormula: false,
64
+ })));
65
+ }
66
+ }
67
+ }
68
+ }
69
+ yield Promise.all(promises);
70
+ });
71
+ exports.updateAllFormulasInTable = (appId, table) => __awaiter(void 0, void 0, void 0, function* () {
72
+ const db = new CouchDB(appId);
73
+ // start by getting the raw rows (which will be written back to DB after update)
74
+ let rows = (yield db.allDocs(getRowParams(table._id, null, {
75
+ include_docs: true,
76
+ }))).rows.map(row => row.doc);
77
+ // now enrich the rows, note the clone so that we have the base state of the
78
+ // rows so that we don't write any of the enriched information back
79
+ let enrichedRows = yield outputProcessing({ appId }, table, cloneDeep(rows), {
80
+ squash: false,
81
+ });
82
+ const updatedRows = [];
83
+ for (let row of rows) {
84
+ // find the enriched row, if found process the formulas
85
+ const enrichedRow = enrichedRows.find(enriched => enriched._id === row._id);
86
+ if (enrichedRow) {
87
+ const processed = processFormulas(table, cloneDeep(row), {
88
+ dynamic: false,
89
+ contextRows: enrichedRow,
90
+ });
91
+ // values have changed, need to add to bulk docs to update
92
+ if (!isEqual(processed, row)) {
93
+ updatedRows.push(processed);
94
+ }
95
+ }
96
+ }
97
+ yield db.bulkDocs(updatedRows);
98
+ });
99
+ /**
100
+ * This function runs at the end of the save/patch functions of the row controller, all this
101
+ * really does is enrich the row, handle any static formula processing, then return the enriched
102
+ * row. The reason we need to return the enriched row is that the automation row created trigger
103
+ * expects the row to be totally enriched/contain all relationships.
104
+ */
105
+ exports.finaliseRow = (appId, table, row, { oldTable, updateFormula } = { updateFormula: true }) => __awaiter(void 0, void 0, void 0, function* () {
106
+ const db = new CouchDB(appId);
107
+ row.type = "row";
108
+ // process the row before return, to include relationships
109
+ let enrichedRow = yield outputProcessing({ appId }, table, cloneDeep(row), {
110
+ squash: false,
111
+ });
112
+ // use enriched row to generate formulas for saving, specifically only use as context
113
+ row = processFormulas(table, row, {
114
+ dynamic: false,
115
+ contextRows: enrichedRow,
116
+ });
117
+ // don't worry about rev, tables handle rev/lastID updates
118
+ // if another row has been written since processing this will
119
+ // handle the auto ID clash
120
+ if (oldTable && !isEqual(oldTable, table)) {
121
+ try {
122
+ yield db.put(table);
123
+ }
124
+ catch (err) {
125
+ if (err.status === 409) {
126
+ const updatedTable = yield db.get(table._id);
127
+ let response = processAutoColumn(null, updatedTable, row, {
128
+ reprocessing: true,
129
+ });
130
+ yield db.put(response.table);
131
+ row = response.row;
132
+ }
133
+ else {
134
+ throw err;
135
+ }
136
+ }
137
+ }
138
+ const response = yield db.put(row);
139
+ // for response, calculate the formulas for the enriched row
140
+ enrichedRow._rev = response.rev;
141
+ enrichedRow = yield processFormulas(table, enrichedRow, { dynamic: false });
142
+ // this updates the related formulas in other rows based on the relations to this row
143
+ if (updateFormula) {
144
+ yield exports.updateRelatedFormula(appId, table, enrichedRow);
145
+ }
146
+ return { row: enrichedRow, table };
147
+ });
@@ -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
+ };