@budibase/server 1.3.15-alpha.0 → 1.3.15-alpha.3

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 (188) hide show
  1. package/builder/assets/{index.b266d29f.js → index.e1d89439.js} +288 -288
  2. package/builder/assets/{index.f5c54dd2.css → index.ecdac070.css} +2 -2
  3. package/builder/index.html +2 -2
  4. package/coverage/clover.xml +21 -21
  5. package/coverage/coverage-final.json +1 -1
  6. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  7. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  10. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  12. package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/index.html +1 -1
  15. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  16. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  18. package/coverage/lcov-report/api/controllers/migrations.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  21. package/coverage/lcov-report/api/controllers/query/validation.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  23. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  24. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/row/index.html +1 -1
  26. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +1 -1
  29. package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
  30. package/coverage/lcov-report/api/controllers/screen.js.html +5 -2
  31. package/coverage/lcov-report/api/controllers/script.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +1 -1
  33. package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
  34. package/coverage/lcov-report/api/controllers/table/index.html +1 -1
  35. package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
  36. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  37. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  38. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  39. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  40. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  41. package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
  42. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  44. package/coverage/lcov-report/api/index.html +1 -1
  45. package/coverage/lcov-report/api/index.js.html +1 -1
  46. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  47. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  48. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  49. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/index.html +1 -1
  57. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/migrations.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/utils/index.html +1 -1
  71. package/coverage/lcov-report/api/routes/utils/validators.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  74. package/coverage/lcov-report/automations/actions.js.html +1 -1
  75. package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
  76. package/coverage/lcov-report/automations/bullboard.js.html +1 -1
  77. package/coverage/lcov-report/automations/index.html +1 -1
  78. package/coverage/lcov-report/automations/index.js.html +1 -1
  79. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  80. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  81. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  82. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  83. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  84. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  85. package/coverage/lcov-report/automations/steps/index.html +1 -1
  86. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  87. package/coverage/lcov-report/automations/steps/loop.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  96. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  97. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  98. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  99. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  100. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  101. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  102. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  103. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggers.js.html +1 -1
  105. package/coverage/lcov-report/constants/index.html +1 -1
  106. package/coverage/lcov-report/constants/index.js.html +1 -1
  107. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  108. package/coverage/lcov-report/constants/screens.js.html +1 -1
  109. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  110. package/coverage/lcov-report/db/index.html +1 -1
  111. package/coverage/lcov-report/db/index.js.html +1 -1
  112. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
  113. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  114. package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
  115. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  116. package/coverage/lcov-report/db/newid.js.html +1 -1
  117. package/coverage/lcov-report/db/utils.js.html +1 -1
  118. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  119. package/coverage/lcov-report/events/index.html +1 -1
  120. package/coverage/lcov-report/events/index.js.html +1 -1
  121. package/coverage/lcov-report/events/utils.js.html +1 -1
  122. package/coverage/lcov-report/index.html +1 -1
  123. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  124. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  125. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  126. package/coverage/lcov-report/middleware/builder.js.html +1 -1
  127. package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
  128. package/coverage/lcov-report/middleware/index.html +1 -1
  129. package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
  130. package/coverage/lcov-report/middleware/publicApi.js.html +1 -1
  131. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  132. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  133. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  134. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  135. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  136. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  137. package/coverage/lcov-report/utilities/global.js.html +1 -1
  138. package/coverage/lcov-report/utilities/index.html +1 -1
  139. package/coverage/lcov-report/utilities/index.js.html +1 -1
  140. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
  141. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  142. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  143. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  144. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  145. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
  146. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  147. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  148. package/coverage/lcov-report/utilities/security.js.html +1 -1
  149. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  150. package/coverage/lcov-report/utilities/usageQuota/index.html +1 -1
  151. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +1 -1
  152. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
  153. package/coverage/lcov-report/utilities/users.js.html +1 -1
  154. package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
  155. package/coverage/lcov.info +34 -34
  156. package/dist/api/controllers/plugin/file.js +24 -0
  157. package/dist/api/controllers/plugin/github.js +69 -0
  158. package/dist/api/controllers/{plugin.js → plugin/index.js} +87 -14
  159. package/dist/api/controllers/plugin/npm.js +58 -0
  160. package/dist/api/controllers/plugin/uploaders.js +11 -0
  161. package/dist/api/controllers/plugin/url.js +24 -0
  162. package/dist/api/controllers/plugin/utils.js +26 -0
  163. package/dist/api/controllers/screen.js +1 -0
  164. package/dist/api/controllers/static/templates/preview.hbs +3 -1
  165. package/dist/api/routes/plugin.js +1 -0
  166. package/dist/app.js +2 -0
  167. package/dist/integrations/base/sqlTable.js +3 -1
  168. package/dist/package.json +7 -6
  169. package/dist/tsconfig.build.tsbuildinfo +1 -1
  170. package/dist/utilities/fileSystem/index.js +56 -12
  171. package/dist/websocket.js +22 -0
  172. package/package.json +8 -7
  173. package/src/api/controllers/plugin/file.ts +15 -0
  174. package/src/api/controllers/plugin/github.ts +75 -0
  175. package/src/api/controllers/plugin/index.ts +206 -0
  176. package/src/api/controllers/plugin/npm.ts +56 -0
  177. package/src/api/controllers/plugin/uploaders.ts +4 -0
  178. package/src/api/controllers/plugin/url.ts +12 -0
  179. package/src/api/controllers/plugin/utils.ts +19 -0
  180. package/src/api/controllers/screen.js +1 -0
  181. package/src/api/controllers/static/templates/preview.hbs +3 -1
  182. package/src/api/routes/plugin.ts +1 -0
  183. package/src/app.ts +2 -0
  184. package/src/integrations/base/sqlTable.ts +3 -1
  185. package/src/module.d.ts +1 -0
  186. package/src/utilities/fileSystem/index.js +72 -12
  187. package/src/websocket.ts +26 -0
  188. package/src/api/controllers/plugin.ts +0 -110
@@ -14,7 +14,7 @@ const { join } = require("path");
14
14
  const uuid = require("uuid/v4");
15
15
  const { doWithDB, dangerousGetDB, closeDB, } = require("@budibase/backend-core/db");
16
16
  const { ObjectStoreBuckets } = require("../../constants");
17
- const { upload, retrieve, retrieveToTmp, streamUpload, deleteFolder, downloadTarball, deleteFiles, } = require("./utilities");
17
+ const { upload, retrieve, retrieveToTmp, streamUpload, deleteFolder, downloadTarball, downloadTarballDirect, deleteFiles, } = require("./utilities");
18
18
  const { updateClientLibrary } = require("./clientLibrary");
19
19
  const { checkSlashesInUrl } = require("../");
20
20
  const env = require("../../environment");
@@ -295,32 +295,47 @@ exports.cleanup = appIds => {
295
295
  }
296
296
  }
297
297
  };
298
- exports.extractPluginTarball = (file) => __awaiter(void 0, void 0, void 0, function* () {
299
- if (!file.name.endsWith(".tar.gz")) {
300
- throw new Error("Plugin must be compressed into a gzipped tarball.");
298
+ const createTempFolder = item => {
299
+ const path = join(budibaseTempDir(), item);
300
+ try {
301
+ // remove old tmp directories automatically - don't combine
302
+ if (fs.existsSync(path)) {
303
+ fs.rmSync(path, { recursive: true, force: true });
304
+ }
305
+ fs.mkdirSync(path);
301
306
  }
302
- const path = join(budibaseTempDir(), file.name.split(".tar.gz")[0]);
303
- // remove old tmp directories automatically - don't combine
304
- if (fs.existsSync(path)) {
305
- fs.rmSync(path, { recursive: true, force: true });
307
+ catch (err) {
308
+ throw new Error(`Path cannot be created: ${err.message}`);
306
309
  }
307
- fs.mkdirSync(path);
310
+ return path;
311
+ };
312
+ exports.createTempFolder = createTempFolder;
313
+ const extractTarball = (fromFilePath, toPath) => __awaiter(void 0, void 0, void 0, function* () {
308
314
  yield tar.extract({
309
- file: file.path,
310
- C: path,
315
+ file: fromFilePath,
316
+ C: toPath,
311
317
  });
318
+ });
319
+ exports.extractTarball = extractTarball;
320
+ const getPluginMetadata = (path) => __awaiter(void 0, void 0, void 0, function* () {
312
321
  let metadata = {};
313
322
  try {
314
323
  const pkg = fs.readFileSync(join(path, "package.json"), "utf8");
315
324
  const schema = fs.readFileSync(join(path, "schema.json"), "utf8");
316
325
  metadata.schema = JSON.parse(schema);
317
326
  metadata.package = JSON.parse(pkg);
327
+ if (!metadata.package.name ||
328
+ !metadata.package.version ||
329
+ !metadata.package.description) {
330
+ throw new Error("package.json is missing one of 'name', 'version' or 'description'.");
331
+ }
318
332
  }
319
333
  catch (err) {
320
- throw new Error("Unable to process schema.json/package.json in plugin.");
334
+ throw new Error(`Unable to process schema.json/package.json in plugin. ${err.message}`);
321
335
  }
322
336
  return { metadata, directory: path };
323
337
  });
338
+ exports.getPluginMetadata = getPluginMetadata;
324
339
  exports.getDatasourcePlugin = (name, url, hash) => __awaiter(void 0, void 0, void 0, function* () {
325
340
  if (!fs.existsSync(DATASOURCE_PATH)) {
326
341
  fs.mkdirSync(DATASOURCE_PATH);
@@ -350,6 +365,34 @@ exports.getDatasourcePlugin = (name, url, hash) => __awaiter(void 0, void 0, voi
350
365
  throw new Error(`Unable to retrieve plugin - reason: ${yield response.text()}`);
351
366
  }
352
367
  });
368
+ /**
369
+ * Find for a file recursively from start path applying filter, return first match
370
+ */
371
+ exports.findFileRec = (startPath, filter) => {
372
+ if (!fs.existsSync(startPath)) {
373
+ return;
374
+ }
375
+ const files = fs.readdirSync(startPath);
376
+ for (let i = 0, len = files.length; i < len; i++) {
377
+ const filename = join(startPath, files[i]);
378
+ const stat = fs.lstatSync(filename);
379
+ if (stat.isDirectory()) {
380
+ return exports.findFileRec(filename, filter);
381
+ }
382
+ else if (filename.endsWith(filter)) {
383
+ return filename;
384
+ }
385
+ }
386
+ };
387
+ /**
388
+ * Remove a folder which is not empty from the file system
389
+ */
390
+ exports.deleteFolderFileSystem = path => {
391
+ if (!fs.existsSync(path)) {
392
+ return;
393
+ }
394
+ fs.rmSync(path, { recursive: true, force: true });
395
+ };
353
396
  /**
354
397
  * Full function definition for below can be found in the utilities.
355
398
  */
@@ -357,5 +400,6 @@ exports.upload = upload;
357
400
  exports.retrieve = retrieve;
358
401
  exports.retrieveToTmp = retrieveToTmp;
359
402
  exports.deleteFiles = deleteFiles;
403
+ exports.downloadTarballDirect = downloadTarballDirect;
360
404
  exports.TOP_LEVEL_PATH = TOP_LEVEL_PATH;
361
405
  exports.NODE_MODULES_PATH = NODE_MODULES_PATH;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClientAppSocket = exports.initialise = void 0;
4
+ const socket_io_1 = require("socket.io");
5
+ class Websocket {
6
+ constructor(server, path) {
7
+ this.socketServer = new socket_io_1.Server(server, {
8
+ path,
9
+ });
10
+ }
11
+ // Emit an event to all sockets
12
+ emit(event, payload) {
13
+ this.socketServer.sockets.emit(event, payload);
14
+ }
15
+ }
16
+ // Likely to be more socket instances in future
17
+ let ClientAppSocket;
18
+ exports.ClientAppSocket = ClientAppSocket;
19
+ const initialise = (server) => {
20
+ exports.ClientAppSocket = ClientAppSocket = new Websocket(server, "/socket/client");
21
+ };
22
+ exports.initialise = initialise;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/server",
3
3
  "email": "hi@budibase.com",
4
- "version": "1.3.15-alpha.0",
4
+ "version": "1.3.15-alpha.3",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -77,11 +77,11 @@
77
77
  "license": "GPL-3.0",
78
78
  "dependencies": {
79
79
  "@apidevtools/swagger-parser": "10.0.3",
80
- "@budibase/backend-core": "1.3.15-alpha.0",
81
- "@budibase/client": "1.3.15-alpha.0",
82
- "@budibase/pro": "1.3.13",
83
- "@budibase/string-templates": "1.3.15-alpha.0",
84
- "@budibase/types": "1.3.15-alpha.0",
80
+ "@budibase/backend-core": "1.3.15-alpha.3",
81
+ "@budibase/client": "1.3.15-alpha.3",
82
+ "@budibase/pro": "1.3.15-alpha.2",
83
+ "@budibase/string-templates": "1.3.15-alpha.3",
84
+ "@budibase/types": "1.3.15-alpha.3",
85
85
  "@bull-board/api": "3.7.0",
86
86
  "@bull-board/koa": "3.9.4",
87
87
  "@elastic/elasticsearch": "7.10.0",
@@ -139,6 +139,7 @@
139
139
  "redis": "4",
140
140
  "server-destroy": "1.0.1",
141
141
  "snowflake-promise": "^4.5.0",
142
+ "socket.io": "^4.5.1",
142
143
  "svelte": "3.49.0",
143
144
  "swagger-parser": "10.0.3",
144
145
  "tar": "6.1.11",
@@ -198,5 +199,5 @@
198
199
  "optionalDependencies": {
199
200
  "oracledb": "5.3.0"
200
201
  },
201
- "gitHead": "38f3c6d1d713f5e1bd18fdd81bdffb9bf7d4f9d3"
202
+ "gitHead": "a1eafcef980f6ed36933a6d2953e35b0de82be51"
202
203
  }
@@ -0,0 +1,15 @@
1
+ import {
2
+ createTempFolder,
3
+ getPluginMetadata,
4
+ extractTarball,
5
+ } from "../../../utilities/fileSystem"
6
+
7
+ export async function fileUpload(file: { name: string; path: string }) {
8
+ if (!file.name.endsWith(".tar.gz")) {
9
+ throw new Error("Plugin must be compressed into a gzipped tarball.")
10
+ }
11
+ const path = createTempFolder(file.name.split(".tar.gz")[0])
12
+ await extractTarball(file.path, path)
13
+
14
+ return await getPluginMetadata(path)
15
+ }
@@ -0,0 +1,75 @@
1
+ import { getPluginMetadata } from "../../../utilities/fileSystem"
2
+ import fetch from "node-fetch"
3
+ import { downloadUnzipTarball } from "./utils"
4
+
5
+ export async function request(
6
+ url: string,
7
+ headers: { [key: string]: string },
8
+ err: string
9
+ ) {
10
+ const response = await fetch(url, { headers })
11
+ if (response.status >= 300) {
12
+ const respErr = await response.text()
13
+ throw new Error(`Error: ${err} - ${respErr}`)
14
+ }
15
+ return response.json()
16
+ }
17
+
18
+ export async function githubUpload(url: string, name = "", token = "") {
19
+ let githubUrl = url
20
+
21
+ if (!githubUrl.includes("https://github.com/")) {
22
+ throw new Error("The plugin origin must be from Github")
23
+ }
24
+
25
+ if (url.includes(".git")) {
26
+ githubUrl = url.replace(".git", "")
27
+ }
28
+
29
+ const githubApiUrl = githubUrl.replace(
30
+ "https://github.com/",
31
+ "https://api.github.com/repos/"
32
+ )
33
+ const headers: any = token ? { Authorization: `Bearer ${token}` } : {}
34
+ const pluginDetails = await request(
35
+ githubApiUrl,
36
+ headers,
37
+ "Repository not found"
38
+ )
39
+ const pluginName = pluginDetails.name || name
40
+ const pluginLatestReleaseUrl = pluginDetails?.["releases_url"]
41
+ ? pluginDetails?.["releases_url"].replace("{/id}", "/latest")
42
+ : undefined
43
+ if (!pluginLatestReleaseUrl) {
44
+ throw new Error("Github release not found")
45
+ }
46
+
47
+ const pluginReleaseDetails = await request(
48
+ pluginLatestReleaseUrl,
49
+ headers,
50
+ "Github latest release not found"
51
+ )
52
+ const pluginReleaseTarballAsset = pluginReleaseDetails?.assets?.find(
53
+ (x: any) => x?.["content_type"] === "application/gzip"
54
+ )
55
+ const pluginLastReleaseTarballUrl =
56
+ pluginReleaseTarballAsset?.["browser_download_url"]
57
+ if (!pluginLastReleaseTarballUrl) {
58
+ throw new Error("Github latest release url not found")
59
+ }
60
+
61
+ try {
62
+ const path = await downloadUnzipTarball(
63
+ pluginLastReleaseTarballUrl,
64
+ pluginName,
65
+ headers
66
+ )
67
+ return await getPluginMetadata(path)
68
+ } catch (err: any) {
69
+ let errMsg = err?.message || err
70
+ if (errMsg === "unexpected response Not Found") {
71
+ errMsg = "Github release tarball not found"
72
+ }
73
+ throw new Error(errMsg)
74
+ }
75
+ }
@@ -0,0 +1,206 @@
1
+ import { ObjectStoreBuckets } from "../../../constants"
2
+ import { loadJSFile } from "../../../utilities/fileSystem"
3
+ import { npmUpload, urlUpload, githubUpload, fileUpload } from "./uploaders"
4
+ import { getGlobalDB } from "@budibase/backend-core/tenancy"
5
+ import { validate } from "@budibase/backend-core/plugins"
6
+ import { generatePluginID, getPluginParams } from "../../../db/utils"
7
+ import {
8
+ uploadDirectory,
9
+ deleteFolder,
10
+ } from "@budibase/backend-core/objectStore"
11
+ import { PluginType, FileType, PluginSource } from "@budibase/types"
12
+ import env from "../../../environment"
13
+ import { ClientAppSocket } from "../../../websocket"
14
+
15
+ export async function getPlugins(type?: PluginType) {
16
+ const db = getGlobalDB()
17
+ const response = await db.allDocs(
18
+ getPluginParams(null, {
19
+ include_docs: true,
20
+ })
21
+ )
22
+ const plugins = response.rows.map((row: any) => row.doc)
23
+ if (type) {
24
+ return plugins.filter((plugin: any) => plugin.schema?.type === type)
25
+ } else {
26
+ return plugins
27
+ }
28
+ }
29
+
30
+ export async function upload(ctx: any) {
31
+ const plugins: FileType[] =
32
+ ctx.request.files.file.length > 1
33
+ ? Array.from(ctx.request.files.file)
34
+ : [ctx.request.files.file]
35
+ try {
36
+ let docs = []
37
+ // can do single or multiple plugins
38
+ for (let plugin of plugins) {
39
+ const doc = await processPlugin(plugin, PluginSource.FILE)
40
+ docs.push(doc)
41
+ }
42
+ ctx.body = {
43
+ message: "Plugin(s) uploaded successfully",
44
+ plugins: docs,
45
+ }
46
+ } catch (err: any) {
47
+ const errMsg = err?.message ? err?.message : err
48
+
49
+ ctx.throw(400, `Failed to import plugin: ${errMsg}`)
50
+ }
51
+ }
52
+
53
+ export async function create(ctx: any) {
54
+ const { source, url, headers, githubToken } = ctx.request.body
55
+
56
+ if (!env.SELF_HOSTED) {
57
+ ctx.throw(400, "Plugins not supported outside of self-host.")
58
+ }
59
+
60
+ try {
61
+ let metadata
62
+ let directory
63
+ // Generating random name as a backup and needed for url
64
+ let name = "PLUGIN_" + Math.floor(100000 + Math.random() * 900000)
65
+
66
+ switch (source) {
67
+ case PluginSource.NPM:
68
+ const { metadata: metadataNpm, directory: directoryNpm } =
69
+ await npmUpload(url, name)
70
+ metadata = metadataNpm
71
+ directory = directoryNpm
72
+ break
73
+ case PluginSource.GITHUB:
74
+ const { metadata: metadataGithub, directory: directoryGithub } =
75
+ await githubUpload(url, name, githubToken)
76
+ metadata = metadataGithub
77
+ directory = directoryGithub
78
+ break
79
+ case PluginSource.URL:
80
+ const headersObj = headers || {}
81
+ const { metadata: metadataUrl, directory: directoryUrl } =
82
+ await urlUpload(url, name, headersObj)
83
+ metadata = metadataUrl
84
+ directory = directoryUrl
85
+ break
86
+ }
87
+
88
+ validate(metadata?.schema)
89
+
90
+ const doc = await storePlugin(metadata, directory, source)
91
+
92
+ ctx.body = {
93
+ message: "Plugin uploaded successfully",
94
+ plugins: [doc],
95
+ }
96
+ } catch (err: any) {
97
+ const errMsg = err?.message ? err?.message : err
98
+
99
+ ctx.throw(400, `Failed to import plugin: ${errMsg}`)
100
+ }
101
+ ctx.status = 200
102
+ }
103
+
104
+ export async function fetch(ctx: any) {
105
+ ctx.body = await getPlugins()
106
+ }
107
+
108
+ export async function destroy(ctx: any) {
109
+ const db = getGlobalDB()
110
+ const { pluginId } = ctx.params
111
+
112
+ try {
113
+ const plugin = await db.get(pluginId)
114
+ const bucketPath = `${plugin.name}/`
115
+ await deleteFolder(ObjectStoreBuckets.PLUGINS, bucketPath)
116
+
117
+ await db.remove(pluginId, plugin._rev)
118
+ } catch (err: any) {
119
+ const errMsg = err?.message ? err?.message : err
120
+
121
+ ctx.throw(400, `Failed to delete plugin: ${errMsg}`)
122
+ }
123
+
124
+ ctx.message = `Plugin ${ctx.params.pluginId} deleted.`
125
+ ctx.status = 200
126
+ }
127
+
128
+ export async function storePlugin(
129
+ metadata: any,
130
+ directory: any,
131
+ source?: string
132
+ ) {
133
+ const db = getGlobalDB()
134
+ const version = metadata.package.version,
135
+ name = metadata.package.name,
136
+ description = metadata.package.description,
137
+ hash = metadata.schema.hash
138
+
139
+ // first open the tarball into tmp directory
140
+ const bucketPath = `${name}/`
141
+ const files = await uploadDirectory(
142
+ ObjectStoreBuckets.PLUGINS,
143
+ directory,
144
+ bucketPath
145
+ )
146
+ const jsFile = files.find((file: any) => file.name.endsWith(".js"))
147
+ if (!jsFile) {
148
+ throw new Error(`Plugin missing .js file.`)
149
+ }
150
+ // validate the JS for a datasource
151
+ if (metadata.schema.type === PluginType.DATASOURCE) {
152
+ const js = loadJSFile(directory, jsFile.name)
153
+ // TODO: this isn't safe - but we need full node environment
154
+ // in future we should do this in a thread for safety
155
+ try {
156
+ eval(js)
157
+ } catch (err: any) {
158
+ const message = err?.message ? err.message : JSON.stringify(err)
159
+ throw new Error(`JS invalid: ${message}`)
160
+ }
161
+ }
162
+ const jsFileName = jsFile.name
163
+ const pluginId = generatePluginID(name)
164
+
165
+ // overwrite existing docs entirely if they exist
166
+ let rev
167
+ try {
168
+ const existing = await db.get(pluginId)
169
+ rev = existing._rev
170
+ } catch (err) {
171
+ rev = undefined
172
+ }
173
+ let doc = {
174
+ _id: pluginId,
175
+ _rev: rev,
176
+ ...metadata,
177
+ name,
178
+ version,
179
+ hash,
180
+ description,
181
+ jsUrl: `${bucketPath}${jsFileName}`,
182
+ }
183
+
184
+ if (source) {
185
+ doc = {
186
+ ...doc,
187
+ source,
188
+ }
189
+ }
190
+
191
+ const response = await db.put(doc)
192
+ ClientAppSocket.emit("plugin-update", { name, hash })
193
+ return {
194
+ ...doc,
195
+ _rev: response.rev,
196
+ }
197
+ }
198
+
199
+ export async function processPlugin(plugin: FileType, source?: string) {
200
+ if (!env.SELF_HOSTED) {
201
+ throw new Error("Plugins not supported outside of self-host.")
202
+ }
203
+
204
+ const { metadata, directory } = await fileUpload(plugin)
205
+ return await storePlugin(metadata, directory, source)
206
+ }
@@ -0,0 +1,56 @@
1
+ import {
2
+ getPluginMetadata,
3
+ findFileRec,
4
+ extractTarball,
5
+ deleteFolderFileSystem,
6
+ } from "../../../utilities/fileSystem"
7
+ import fetch from "node-fetch"
8
+ import { join } from "path"
9
+ import { downloadUnzipTarball } from "./utils"
10
+
11
+ export async function npmUpload(url: string, name: string, headers = {}) {
12
+ let npmTarballUrl = url
13
+ let pluginName = name
14
+
15
+ if (
16
+ !npmTarballUrl.includes("https://www.npmjs.com") &&
17
+ !npmTarballUrl.includes("https://registry.npmjs.org")
18
+ ) {
19
+ throw new Error("The plugin origin must be from NPM")
20
+ }
21
+
22
+ if (!npmTarballUrl.includes(".tgz")) {
23
+ const npmPackageURl = url.replace(
24
+ "https://www.npmjs.com/package/",
25
+ "https://registry.npmjs.org/"
26
+ )
27
+ const response = await fetch(npmPackageURl)
28
+ if (response.status !== 200) {
29
+ throw new Error("NPM Package not found")
30
+ }
31
+
32
+ let npmDetails = await response.json()
33
+ pluginName = npmDetails.name
34
+ const npmVersion = npmDetails["dist-tags"].latest
35
+ npmTarballUrl = npmDetails?.versions?.[npmVersion]?.dist?.tarball
36
+
37
+ if (!npmTarballUrl) {
38
+ throw new Error("NPM tarball url not found")
39
+ }
40
+ }
41
+
42
+ const path = await downloadUnzipTarball(npmTarballUrl, pluginName, headers)
43
+ const tarballPluginFile = findFileRec(path, ".tar.gz")
44
+ if (!tarballPluginFile) {
45
+ throw new Error("Tarball plugin file not found")
46
+ }
47
+
48
+ try {
49
+ await extractTarball(tarballPluginFile, path)
50
+ deleteFolderFileSystem(join(path, "package"))
51
+ } catch (err: any) {
52
+ throw new Error(err)
53
+ }
54
+
55
+ return await getPluginMetadata(path)
56
+ }
@@ -0,0 +1,4 @@
1
+ export { fileUpload } from "./file"
2
+ export { githubUpload } from "./github"
3
+ export { npmUpload } from "./npm"
4
+ export { urlUpload } from "./url"
@@ -0,0 +1,12 @@
1
+ import { downloadUnzipTarball } from "./utils"
2
+ import { getPluginMetadata } from "../../../utilities/fileSystem"
3
+
4
+ export async function urlUpload(url: string, name = "", headers = {}) {
5
+ if (!url.includes(".tar.gz")) {
6
+ throw new Error("Plugin must be compressed into a gzipped tarball.")
7
+ }
8
+
9
+ const path = await downloadUnzipTarball(url, name, headers)
10
+
11
+ return await getPluginMetadata(path)
12
+ }
@@ -0,0 +1,19 @@
1
+ import {
2
+ createTempFolder,
3
+ downloadTarballDirect,
4
+ } from "../../../utilities/fileSystem"
5
+
6
+ export async function downloadUnzipTarball(
7
+ url: string,
8
+ name: string,
9
+ headers = {}
10
+ ) {
11
+ try {
12
+ const path = createTempFolder(name)
13
+ await downloadTarballDirect(url, path, headers)
14
+
15
+ return path
16
+ } catch (e: any) {
17
+ throw new Error(e.message)
18
+ }
19
+ }
@@ -71,6 +71,7 @@ exports.save = async ctx => {
71
71
  name: plugin.name,
72
72
  version: plugin.version,
73
73
  jsUrl: plugin.jsUrl,
74
+ hash: plugin.hash,
74
75
  })
75
76
  }
76
77
  })
@@ -67,7 +67,8 @@
67
67
  previewDevice,
68
68
  navigation,
69
69
  hiddenComponentIds,
70
- usedPlugins
70
+ usedPlugins,
71
+ location
71
72
  } = parsed
72
73
 
73
74
  // Set some flags so the app knows we're in the builder
@@ -83,6 +84,7 @@
83
84
  window["##BUDIBASE_PREVIEW_NAVIGATION##"] = navigation
84
85
  window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"] = hiddenComponentIds
85
86
  window["##BUDIBASE_USED_PLUGINS##"] = usedPlugins
87
+ window["##BUDIBASE_LOCATION##"] = location
86
88
 
87
89
  // Initialise app
88
90
  try {
@@ -7,6 +7,7 @@ const router = new Router()
7
7
 
8
8
  router
9
9
  .post("/api/plugin/upload", authorized(BUILDER), controller.upload)
10
+ .post("/api/plugin", authorized(BUILDER), controller.create)
10
11
  .get("/api/plugin", authorized(BUILDER), controller.fetch)
11
12
  .delete("/api/plugin/:pluginId", authorized(BUILDER), controller.destroy)
12
13
 
package/src/app.ts CHANGED
@@ -32,6 +32,7 @@ import * as migrations from "./migrations"
32
32
  import { events, installation, tenancy } from "@budibase/backend-core"
33
33
  import { createAdminUser, getChecklist } from "./utilities/workerRequests"
34
34
  import { watch } from "./watch"
35
+ import { initialise as initialiseWebsockets } from "./websocket"
35
36
 
36
37
  const app = new Koa()
37
38
 
@@ -76,6 +77,7 @@ if (env.isProd()) {
76
77
 
77
78
  const server = http.createServer(app.callback())
78
79
  destroyable(server)
80
+ initialiseWebsockets(server)
79
81
 
80
82
  let shuttingDown = false,
81
83
  errCode = 0
@@ -96,7 +96,9 @@ function generateSchema(
96
96
  const deletedColumns = Object.entries(oldTable.schema)
97
97
  .filter(
98
98
  ([key, schema]) =>
99
- schema.type !== FieldTypes.LINK && table.schema[key] == null
99
+ schema.type !== FieldTypes.LINK &&
100
+ schema.type !== FieldTypes.FORMULA &&
101
+ table.schema[key] == null
100
102
  )
101
103
  .map(([key]) => key)
102
104
  deletedColumns.forEach(key => {
package/src/module.d.ts CHANGED
@@ -11,3 +11,4 @@ declare module "@budibase/backend-core/encryption"
11
11
  declare module "@budibase/backend-core/utils"
12
12
  declare module "@budibase/backend-core/redis"
13
13
  declare module "@budibase/backend-core/objectStore"
14
+ declare module "@budibase/backend-core/plugins"