@budibase/server 2.4.42 → 2.4.44-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 (73) hide show
  1. package/__mocks__/node-fetch.ts +6 -1
  2. package/builder/assets/{index.3d64bc07.js → index.6846a382.js} +385 -384
  3. package/builder/assets/index.7f9a008b.css +6 -0
  4. package/builder/index.html +7 -7
  5. package/dist/api/controllers/application.js +28 -24
  6. package/dist/api/controllers/public/metrics.js +113 -0
  7. package/dist/api/controllers/row/external.js +15 -0
  8. package/dist/api/controllers/row/utils.js +4 -3
  9. package/dist/api/controllers/static/index.js +84 -24
  10. package/dist/api/controllers/static/templates/BudibaseApp.svelte +34 -11
  11. package/dist/api/controllers/table/utils.js +2 -4
  12. package/dist/api/routes/public/index.js +8 -0
  13. package/dist/api/routes/public/metrics.js +30 -0
  14. package/dist/app.js +1 -0
  15. package/dist/integrations/googlesheets.js +4 -0
  16. package/dist/integrations/redis.js +1 -1
  17. package/dist/middleware/currentapp.js +1 -27
  18. package/dist/package.json +12 -11
  19. package/dist/sdk/users/utils.js +11 -6
  20. package/dist/tsconfig.build.tsbuildinfo +1 -1
  21. package/dist/utilities/fileSystem/app.js +1 -10
  22. package/dist/utilities/fileSystem/filesystem.js +0 -4
  23. package/dist/utilities/global.js +17 -7
  24. package/jest.config.ts +2 -0
  25. package/package.json +13 -12
  26. package/scripts/test.sh +6 -4
  27. package/specs/openapi.json +39 -0
  28. package/specs/openapi.yaml +169 -0
  29. package/specs/resources/application.ts +11 -0
  30. package/specs/resources/index.ts +2 -0
  31. package/specs/resources/metrics.ts +81 -0
  32. package/src/api/controllers/application.ts +20 -21
  33. package/src/api/controllers/component.ts +2 -2
  34. package/src/api/controllers/public/metrics.ts +251 -0
  35. package/src/api/controllers/row/external.ts +14 -0
  36. package/src/api/controllers/row/utils.ts +4 -3
  37. package/src/api/controllers/static/index.ts +69 -26
  38. package/src/api/controllers/static/templates/BudibaseApp.svelte +34 -11
  39. package/src/api/controllers/table/tests/utils.spec.ts +97 -0
  40. package/src/api/controllers/table/utils.ts +20 -12
  41. package/src/api/controllers/view/tests/__snapshots__/viewBuilder.spec.js.snap +48 -48
  42. package/src/api/routes/public/index.ts +10 -1
  43. package/src/api/routes/public/metrics.ts +28 -0
  44. package/src/api/routes/public/tests/metrics.spec.js +34 -0
  45. package/src/api/routes/tests/__snapshots__/datasource.spec.ts.snap +22 -22
  46. package/src/api/routes/tests/__snapshots__/view.spec.js.snap +5 -5
  47. package/src/api/routes/tests/appSync.spec.ts +31 -0
  48. package/src/api/routes/tests/internalSearch.spec.js +8 -7
  49. package/src/app.ts +2 -1
  50. package/src/automations/automationUtils.ts +1 -1
  51. package/src/automations/tests/automation.spec.ts +99 -0
  52. package/src/db/defaultData/datasource_bb_default.ts +1 -1
  53. package/src/definitions/openapi.ts +15 -0
  54. package/src/integration-test/postgres.spec.ts +46 -52
  55. package/src/integrations/googlesheets.ts +4 -0
  56. package/src/integrations/redis.ts +1 -1
  57. package/src/integrations/tests/googlesheets.spec.ts +13 -13
  58. package/src/integrations/tests/redis.spec.ts +9 -5
  59. package/src/middleware/currentapp.ts +3 -32
  60. package/src/middleware/tests/currentapp.spec.js +6 -42
  61. package/src/sdk/users/tests/utils.spec.ts +159 -0
  62. package/src/sdk/users/utils.ts +18 -7
  63. package/src/tests/jestEnv.ts +1 -0
  64. package/src/tests/jestSetup.ts +5 -1
  65. package/src/tests/utilities/TestConfiguration.ts +18 -19
  66. package/src/tests/utilities/structures.ts +13 -1
  67. package/src/utilities/fileSystem/app.ts +2 -9
  68. package/src/utilities/fileSystem/filesystem.ts +0 -4
  69. package/src/utilities/global.ts +21 -9
  70. package/src/utilities/rowProcessor/index.ts +1 -1
  71. package/builder/assets/favicon.e7fc7733.png +0 -0
  72. package/builder/assets/index.b0e3aca6.css +0 -6
  73. package/src/automations/tests/automation.spec.js +0 -84
@@ -20,10 +20,10 @@ import {
20
20
  cache,
21
21
  tenancy,
22
22
  context,
23
- errors,
24
23
  events,
25
24
  migrations,
26
25
  objectStore,
26
+ ErrorCode,
27
27
  } from "@budibase/backend-core"
28
28
  import { USERS_TABLE_SCHEMA } from "../../constants"
29
29
  import { buildDefaultDocs } from "../../db/defaultData/datasource_bb_default"
@@ -44,7 +44,6 @@ import {
44
44
  Layout,
45
45
  Screen,
46
46
  MigrationType,
47
- BBContext,
48
47
  Database,
49
48
  UserCtx,
50
49
  } from "@budibase/types"
@@ -74,14 +73,14 @@ async function getScreens() {
74
73
  ).rows.map((row: any) => row.doc)
75
74
  }
76
75
 
77
- function getUserRoleId(ctx: BBContext) {
76
+ function getUserRoleId(ctx: UserCtx) {
78
77
  return !ctx.user?.role || !ctx.user.role._id
79
78
  ? roles.BUILTIN_ROLE_IDS.PUBLIC
80
79
  : ctx.user.role._id
81
80
  }
82
81
 
83
82
  function checkAppUrl(
84
- ctx: BBContext,
83
+ ctx: UserCtx,
85
84
  apps: App[],
86
85
  url: string,
87
86
  currentAppId?: string
@@ -95,7 +94,7 @@ function checkAppUrl(
95
94
  }
96
95
 
97
96
  function checkAppName(
98
- ctx: BBContext,
97
+ ctx: UserCtx,
99
98
  apps: App[],
100
99
  name: string,
101
100
  currentAppId?: string
@@ -160,7 +159,7 @@ async function addDefaultTables(db: Database) {
160
159
  await db.bulkDocs([...defaultDbDocs])
161
160
  }
162
161
 
163
- export async function fetch(ctx: BBContext) {
162
+ export async function fetch(ctx: UserCtx) {
164
163
  const dev = ctx.query && ctx.query.status === AppStatus.DEV
165
164
  const all = ctx.query && ctx.query.status === AppStatus.ALL
166
165
  const apps = (await dbCore.getAllApps({ dev, all })) as App[]
@@ -185,7 +184,7 @@ export async function fetch(ctx: BBContext) {
185
184
  ctx.body = await checkAppMetadata(apps)
186
185
  }
187
186
 
188
- export async function fetchAppDefinition(ctx: BBContext) {
187
+ export async function fetchAppDefinition(ctx: UserCtx) {
189
188
  const layouts = await getLayouts()
190
189
  const userRoleId = getUserRoleId(ctx)
191
190
  const accessController = new roles.AccessController()
@@ -231,7 +230,7 @@ export async function fetchAppPackage(ctx: UserCtx) {
231
230
  }
232
231
  }
233
232
 
234
- async function performAppCreate(ctx: BBContext) {
233
+ async function performAppCreate(ctx: UserCtx) {
235
234
  const apps = (await dbCore.getAllApps({ dev: true })) as App[]
236
235
  const name = ctx.request.body.name,
237
236
  possibleUrl = ctx.request.body.url
@@ -360,7 +359,7 @@ async function creationEvents(request: any, app: App) {
360
359
  }
361
360
  }
362
361
 
363
- async function appPostCreate(ctx: BBContext, app: App) {
362
+ async function appPostCreate(ctx: UserCtx, app: App) {
364
363
  const tenantId = tenancy.getTenantId()
365
364
  await migrations.backPopulateMigrations({
366
365
  type: MigrationType.APP,
@@ -378,7 +377,7 @@ async function appPostCreate(ctx: BBContext, app: App) {
378
377
  return quotas.addRows(rowCount)
379
378
  })
380
379
  } catch (err: any) {
381
- if (err.code && err.code === errors.codes.USAGE_LIMIT_EXCEEDED) {
380
+ if (err.code && err.code === ErrorCode.USAGE_LIMIT_EXCEEDED) {
382
381
  // this import resulted in row usage exceeding the quota
383
382
  // delete the app
384
383
  // skip pre and post-steps as no rows have been added to quotas yet
@@ -391,7 +390,7 @@ async function appPostCreate(ctx: BBContext, app: App) {
391
390
  }
392
391
  }
393
392
 
394
- export async function create(ctx: BBContext) {
393
+ export async function create(ctx: UserCtx) {
395
394
  const newApplication = await quotas.addApp(() => performAppCreate(ctx))
396
395
  await appPostCreate(ctx, newApplication)
397
396
  await cache.bustCache(cache.CacheKey.CHECKLIST)
@@ -401,7 +400,7 @@ export async function create(ctx: BBContext) {
401
400
 
402
401
  // This endpoint currently operates as a PATCH rather than a PUT
403
402
  // Thus name and url fields are handled only if present
404
- export async function update(ctx: BBContext) {
403
+ export async function update(ctx: UserCtx) {
405
404
  const apps = (await dbCore.getAllApps({ dev: true })) as App[]
406
405
  // validation
407
406
  const name = ctx.request.body.name,
@@ -421,7 +420,7 @@ export async function update(ctx: BBContext) {
421
420
  ctx.body = app
422
421
  }
423
422
 
424
- export async function updateClient(ctx: BBContext) {
423
+ export async function updateClient(ctx: UserCtx) {
425
424
  // Get current app version
426
425
  const db = context.getAppDB()
427
426
  const application = await db.get(DocumentType.APP_METADATA)
@@ -445,7 +444,7 @@ export async function updateClient(ctx: BBContext) {
445
444
  ctx.body = app
446
445
  }
447
446
 
448
- export async function revertClient(ctx: BBContext) {
447
+ export async function revertClient(ctx: UserCtx) {
449
448
  // Check app can be reverted
450
449
  const db = context.getAppDB()
451
450
  const application = await db.get(DocumentType.APP_METADATA)
@@ -471,7 +470,7 @@ export async function revertClient(ctx: BBContext) {
471
470
  ctx.body = app
472
471
  }
473
472
 
474
- const unpublishApp = async (ctx: any) => {
473
+ async function unpublishApp(ctx: UserCtx) {
475
474
  let appId = ctx.params.appId
476
475
  appId = dbCore.getProdAppID(appId)
477
476
 
@@ -487,7 +486,7 @@ const unpublishApp = async (ctx: any) => {
487
486
  return result
488
487
  }
489
488
 
490
- async function destroyApp(ctx: BBContext) {
489
+ async function destroyApp(ctx: UserCtx) {
491
490
  let appId = ctx.params.appId
492
491
  appId = dbCore.getProdAppID(appId)
493
492
  const devAppId = dbCore.getDevAppID(appId)
@@ -515,12 +514,12 @@ async function destroyApp(ctx: BBContext) {
515
514
  return result
516
515
  }
517
516
 
518
- async function preDestroyApp(ctx: BBContext) {
517
+ async function preDestroyApp(ctx: UserCtx) {
519
518
  const { rows } = await getUniqueRows([ctx.params.appId])
520
519
  ctx.rowCount = rows.length
521
520
  }
522
521
 
523
- async function postDestroyApp(ctx: BBContext) {
522
+ async function postDestroyApp(ctx: UserCtx) {
524
523
  const rowCount = ctx.rowCount
525
524
  await groups.cleanupApp(ctx.params.appId)
526
525
  if (rowCount) {
@@ -528,7 +527,7 @@ async function postDestroyApp(ctx: BBContext) {
528
527
  }
529
528
  }
530
529
 
531
- export async function destroy(ctx: BBContext) {
530
+ export async function destroy(ctx: UserCtx) {
532
531
  await preDestroyApp(ctx)
533
532
  const result = await destroyApp(ctx)
534
533
  await postDestroyApp(ctx)
@@ -536,7 +535,7 @@ export async function destroy(ctx: BBContext) {
536
535
  ctx.body = result
537
536
  }
538
537
 
539
- export const unpublish = async (ctx: BBContext) => {
538
+ export async function unpublish(ctx: UserCtx) {
540
539
  const prodAppId = dbCore.getProdAppID(ctx.params.appId)
541
540
  const dbExists = await dbCore.dbExists(prodAppId)
542
541
 
@@ -551,7 +550,7 @@ export const unpublish = async (ctx: BBContext) => {
551
550
  ctx.status = 204
552
551
  }
553
552
 
554
- export async function sync(ctx: BBContext) {
553
+ export async function sync(ctx: UserCtx) {
555
554
  const appId = ctx.params.appId
556
555
  try {
557
556
  ctx.body = await sdk.applications.syncApp(appId)
@@ -2,9 +2,9 @@ import { DocumentType } from "../../db/utils"
2
2
  import { Plugin } from "@budibase/types"
3
3
  import { db as dbCore, context, tenancy } from "@budibase/backend-core"
4
4
  import { getComponentLibraryManifest } from "../../utilities/fileSystem"
5
- import { BBContext } from "@budibase/types"
5
+ import { UserCtx } from "@budibase/types"
6
6
 
7
- export async function fetchAppComponentDefinitions(ctx: BBContext) {
7
+ export async function fetchAppComponentDefinitions(ctx: UserCtx) {
8
8
  try {
9
9
  const db = context.getAppDB()
10
10
  const app = await db.get(DocumentType.APP_METADATA)
@@ -0,0 +1,251 @@
1
+ import { Ctx } from "@budibase/types"
2
+ import { users as userCore, db as dbCore } from "@budibase/backend-core"
3
+ import { quotas, licensing } from "@budibase/pro"
4
+
5
+ import os from "os"
6
+
7
+ export async function fetch(ctx: Ctx) {
8
+ // *** OPERATING SYSTEM ***
9
+ const freeMem = os.freemem()
10
+ const totalMem = os.totalmem()
11
+ const usedMem = totalMem - freeMem
12
+ const uptime = os.uptime()
13
+
14
+ // *** APPS ***
15
+ const allDatabases = await dbCore.getAllDbs()
16
+ const devAppIDs = await dbCore.getDevAppIDs()
17
+ const prodAppIDs = await dbCore.getProdAppIDs()
18
+ const allAppIds = await dbCore.getAllApps({ idsOnly: true })
19
+
20
+ // *** USERS ***
21
+ const usersObject = await userCore.getAllUserIds()
22
+
23
+ // *** QUOTAS ***
24
+ const usage = await quotas.getQuotaUsage()
25
+ const license = await licensing.cache.getCachedLicense()
26
+ const appsQuotaUsage = usage.usageQuota.apps
27
+ const rowsQuotaUsage = usage.usageQuota.rows
28
+ const pluginsQuotaUsage = usage.usageQuota.plugins
29
+ const userGroupsQuotaUsage = usage.usageQuota.userGroups
30
+ const queryQuotaUsage = usage.monthly.current.queries
31
+ const automationsQuotaUsage = usage.monthly.current.automations
32
+ const appsQuotaLimit = license.quotas.usage.static.apps.value
33
+ const rowsQuotaLimit = license.quotas.usage.static.rows.value
34
+ const userGroupsQuotaLimit = license.quotas.usage.static.userGroups.value
35
+ const pluginsQuotaLimit = license.quotas.usage.static.plugins.value
36
+ const queryQuotaLimit = license.quotas.usage.monthly.queries.value
37
+ const automationsQuotaLimit = license.quotas.usage.monthly.automations.value
38
+
39
+ // *** BUILD THE OUTPUT STRING ***
40
+ var outputString = ""
41
+
42
+ // **** budibase_os_uptime ****
43
+ outputString += convertToOpenMetrics(
44
+ "budibase_os_uptime",
45
+ "Time in seconds that the host operating system has been up",
46
+ "counter",
47
+ uptime
48
+ )
49
+
50
+ // **** budibase_os_free_mem ****
51
+ outputString += convertToOpenMetrics(
52
+ "budibase_os_free_mem",
53
+ "Bytes of memory free for usage on the host operating system",
54
+ "gauge",
55
+ freeMem
56
+ )
57
+
58
+ // **** budibase_os_total_mem ****
59
+ outputString += convertToOpenMetrics(
60
+ "budibase_os_total_mem",
61
+ "Total bytes of memory on the host operating system",
62
+ "gauge",
63
+ totalMem
64
+ )
65
+
66
+ // **** budibase_os_used_mem ****
67
+ outputString += convertToOpenMetrics(
68
+ "budibase_os_used_mem",
69
+ "Total bytes of memory in use on the host operating system",
70
+ "gauge",
71
+ usedMem
72
+ )
73
+
74
+ // **** budibase_os_load1 ****
75
+ outputString += convertToOpenMetrics(
76
+ "budibase_os_load1",
77
+ "Host operating system load average",
78
+ "gauge",
79
+ os.loadavg()[0]
80
+ )
81
+
82
+ // **** budibase_os_load5 ****
83
+ outputString += convertToOpenMetrics(
84
+ "budibase_os_load5",
85
+ "Host operating system load average",
86
+ "gauge",
87
+ os.loadavg()[1]
88
+ )
89
+ // **** budibase_os_load15 ****
90
+ outputString += convertToOpenMetrics(
91
+ "budibase_os_load15",
92
+ "Host operating system load average",
93
+ "gauge",
94
+ os.loadavg()[2]
95
+ )
96
+
97
+ // **** budibase_tenant_user_count ****
98
+ outputString += convertToOpenMetrics(
99
+ "budibase_tenant_user_count",
100
+ "The number of users created",
101
+ "gauge",
102
+ usersObject.length
103
+ )
104
+
105
+ // **** budibase_tenant_app_count ****
106
+ outputString += convertToOpenMetrics(
107
+ "budibase_tenant_app_count",
108
+ "The number of apps created by a user",
109
+ "gauge",
110
+ allAppIds.length
111
+ )
112
+
113
+ // **** budibase_tenant_production_app_count ****
114
+ outputString += convertToOpenMetrics(
115
+ "budibase_tenant_production_app_count",
116
+ "The number of apps a user has published",
117
+ "gauge",
118
+ prodAppIDs.length
119
+ )
120
+
121
+ // **** budibase_tenant_dev_app_count ****
122
+ outputString += convertToOpenMetrics(
123
+ "budibase_tenant_dev_app_count",
124
+ "The number of apps a user has unpublished in development",
125
+ "gauge",
126
+ devAppIDs.length
127
+ )
128
+
129
+ // **** budibase_tenant_db_count ****
130
+ outputString += convertToOpenMetrics(
131
+ "budibase_tenant_db_count",
132
+ "The number of couchdb databases including global tables such as _users",
133
+ "gauge",
134
+ allDatabases.length
135
+ )
136
+
137
+ // **** budibase_quota_usage_apps ****
138
+ outputString += convertToOpenMetrics(
139
+ "budibase_quota_usage_apps",
140
+ "The number of apps created",
141
+ "gauge",
142
+ appsQuotaUsage
143
+ )
144
+
145
+ // **** budibase_quota_limit_apps ****
146
+ outputString += convertToOpenMetrics(
147
+ "budibase_quota_limit_apps",
148
+ "The limit on the number of apps that can be created",
149
+ "gauge",
150
+ appsQuotaLimit == -1 ? Number.MAX_SAFE_INTEGER : appsQuotaLimit
151
+ )
152
+
153
+ // **** budibase_quota_usage_rows ****
154
+ outputString += convertToOpenMetrics(
155
+ "budibase_quota_usage_rows",
156
+ "The number of database rows used from the quota",
157
+ "gauge",
158
+ rowsQuotaUsage
159
+ )
160
+
161
+ // **** budibase_quota_limit_rows ****
162
+ outputString += convertToOpenMetrics(
163
+ "budibase_quota_limit_rows",
164
+ "The limit on the number of rows that can be created",
165
+ "gauge",
166
+ rowsQuotaLimit == -1 ? Number.MAX_SAFE_INTEGER : rowsQuotaLimit
167
+ )
168
+
169
+ // **** budibase_quota_usage_plugins ****
170
+ outputString += convertToOpenMetrics(
171
+ "budibase_quota_usage_plugins",
172
+ "The number of plugins in use",
173
+ "gauge",
174
+ pluginsQuotaUsage
175
+ )
176
+
177
+ // **** budibase_quota_limit_plugins ****
178
+ outputString += convertToOpenMetrics(
179
+ "budibase_quota_limit_plugins",
180
+ "The limit on the number of plugins that can be created",
181
+ "gauge",
182
+ pluginsQuotaLimit == -1 ? Number.MAX_SAFE_INTEGER : pluginsQuotaLimit
183
+ )
184
+
185
+ // **** budibase_quota_usage_user_groups ****
186
+ outputString += convertToOpenMetrics(
187
+ "budibase_quota_usage_user_groups",
188
+ "The number of user groups created",
189
+ "gauge",
190
+ userGroupsQuotaUsage
191
+ )
192
+
193
+ // **** budibase_quota_limit_user_groups ****
194
+ outputString += convertToOpenMetrics(
195
+ "budibase_quota_limit_user_groups",
196
+ "The limit on the number of user groups that can be created",
197
+ "gauge",
198
+ userGroupsQuotaLimit == -1 ? Number.MAX_SAFE_INTEGER : userGroupsQuotaLimit
199
+ )
200
+
201
+ // **** budibase_quota_usage_queries ****
202
+ outputString += convertToOpenMetrics(
203
+ "budibase_quota_usage_queries",
204
+ "The number of queries used in the current month",
205
+ "gauge",
206
+ queryQuotaUsage
207
+ )
208
+
209
+ // **** budibase_quota_limit_queries ****
210
+ outputString += convertToOpenMetrics(
211
+ "budibase_quota_limit_queries",
212
+ "The limit on the number of queries for the current month",
213
+ "gauge",
214
+ queryQuotaLimit == -1 ? Number.MAX_SAFE_INTEGER : queryQuotaLimit
215
+ )
216
+
217
+ // **** budibase_quota_usage_automations ****
218
+ outputString += convertToOpenMetrics(
219
+ "budibase_quota_usage_automations",
220
+ "The number of automations used in the current month",
221
+ "gauge",
222
+ automationsQuotaUsage
223
+ )
224
+
225
+ // **** budibase_quota_limit_automations ****
226
+ outputString += convertToOpenMetrics(
227
+ "budibase_quota_limit_automations",
228
+ "The limit on the number of automations that can be created",
229
+ "gauge",
230
+ automationsQuotaLimit == -1
231
+ ? Number.MAX_SAFE_INTEGER
232
+ : automationsQuotaLimit
233
+ )
234
+ ctx.body = outputString
235
+ ctx.set("Content-Type", "text/plain")
236
+ }
237
+
238
+ export function convertToOpenMetrics(
239
+ metricName: string,
240
+ metricHelp: string,
241
+ metricType: string,
242
+ metricValue: number
243
+ ) {
244
+ return `# HELP ${metricName} ${metricHelp}.
245
+ # TYPE ${metricName} ${metricType}
246
+ ${metricName} ${metricValue}\n`
247
+ }
248
+
249
+ export default {
250
+ fetch,
251
+ }
@@ -56,6 +56,13 @@ export async function patch(ctx: UserCtx) {
56
56
  const id = inputs._id
57
57
  // don't save the ID to db
58
58
  delete inputs._id
59
+ const validateResult = await utils.validate({
60
+ row: inputs,
61
+ tableId,
62
+ })
63
+ if (!validateResult.valid) {
64
+ throw { validation: validateResult.errors }
65
+ }
59
66
  return handleRequest(Operation.UPDATE, tableId, {
60
67
  id: breakRowIdField(id),
61
68
  row: inputs,
@@ -66,6 +73,13 @@ export async function patch(ctx: UserCtx) {
66
73
  export async function save(ctx: UserCtx) {
67
74
  const inputs = ctx.request.body
68
75
  const tableId = ctx.params.tableId
76
+ const validateResult = await utils.validate({
77
+ row: inputs,
78
+ tableId,
79
+ })
80
+ if (!validateResult.valid) {
81
+ throw { validation: validateResult.errors }
82
+ }
69
83
  return handleRequest(Operation.CREATE, tableId, {
70
84
  row: inputs,
71
85
  includeSqlRelationships: IncludeRelationship.EXCLUDE,
@@ -62,10 +62,11 @@ export async function validate({
62
62
  }
63
63
  const errors: any = {}
64
64
  for (let fieldName of Object.keys(fetchedTable.schema)) {
65
- const constraints = cloneDeep(fetchedTable.schema[fieldName].constraints)
66
- const type = fetchedTable.schema[fieldName].type
65
+ const column = fetchedTable.schema[fieldName]
66
+ const constraints = cloneDeep(column.constraints)
67
+ const type = column.type
67
68
  // formulas shouldn't validated, data will be deleted anyway
68
- if (type === FieldTypes.FORMULA) {
69
+ if (type === FieldTypes.FORMULA || column.autocolumn) {
69
70
  continue
70
71
  }
71
72
  // special case for options, need to always allow unselected (null)
@@ -11,10 +11,12 @@ import {
11
11
  } from "../../../utilities/fileSystem"
12
12
  import env from "../../../environment"
13
13
  import { DocumentType } from "../../../db/utils"
14
- import { context, objectStore, utils } from "@budibase/backend-core"
14
+ import { context, objectStore, utils, configs } from "@budibase/backend-core"
15
15
  import AWS from "aws-sdk"
16
16
  import fs from "fs"
17
17
  import sdk from "../../../sdk"
18
+ import * as pro from "@budibase/pro"
19
+
18
20
  const send = require("koa-send")
19
21
 
20
22
  async function prepareUpload({ s3Key, bucket, metadata, file }: any) {
@@ -98,33 +100,74 @@ export const deleteObjects = async function (ctx: any) {
98
100
  }
99
101
 
100
102
  export const serveApp = async function (ctx: any) {
101
- const db = context.getAppDB({ skip_setup: true })
102
- const appInfo = await db.get(DocumentType.APP_METADATA)
103
- let appId = context.getAppId()
103
+ //Public Settings
104
+ const { config } = await configs.getSettingsConfigDoc()
105
+ const branding = await pro.branding.getBrandingConfig(config)
104
106
 
105
- if (!env.isJest()) {
106
- const App = require("./templates/BudibaseApp.svelte").default
107
- const plugins = objectStore.enrichPluginURLs(appInfo.usedPlugins)
108
- const { head, html, css } = App.render({
109
- metaImage:
110
- "https://res.cloudinary.com/daog6scxm/image/upload/v1666109324/meta-images/budibase-meta-image_uukc1m.png",
111
- title: appInfo.name,
112
- production: env.isProd(),
113
- appId,
114
- clientLibPath: objectStore.clientLibraryUrl(appId!, appInfo.version),
115
- usedPlugins: plugins,
116
- })
107
+ let db
108
+ try {
109
+ db = context.getAppDB({ skip_setup: true })
110
+ const appInfo = await db.get(DocumentType.APP_METADATA)
111
+ let appId = context.getAppId()
117
112
 
118
- const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)
119
- ctx.body = await processString(appHbs, {
120
- head,
121
- body: html,
122
- style: css.code,
123
- appId,
124
- })
125
- } else {
126
- // just return the app info for jest to assert on
127
- ctx.body = appInfo
113
+ if (!env.isJest()) {
114
+ const App = require("./templates/BudibaseApp.svelte").default
115
+ const plugins = objectStore.enrichPluginURLs(appInfo.usedPlugins)
116
+ const { head, html, css } = App.render({
117
+ metaImage:
118
+ branding?.metaImageUrl ||
119
+ "https://res.cloudinary.com/daog6scxm/image/upload/v1666109324/meta-images/budibase-meta-image_uukc1m.png",
120
+ metaDescription: branding?.metaDescription || "",
121
+ metaTitle:
122
+ branding?.metaTitle || `${appInfo.name} - built with Budibase`,
123
+ title: appInfo.name,
124
+ production: env.isProd(),
125
+ appId,
126
+ clientLibPath: objectStore.clientLibraryUrl(appId!, appInfo.version),
127
+ usedPlugins: plugins,
128
+ favicon:
129
+ branding.faviconUrl !== ""
130
+ ? objectStore.getGlobalFileUrl("settings", "faviconUrl")
131
+ : "",
132
+ logo:
133
+ config?.logoUrl !== ""
134
+ ? objectStore.getGlobalFileUrl("settings", "logoUrl")
135
+ : "",
136
+ })
137
+ const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)
138
+ ctx.body = await processString(appHbs, {
139
+ head,
140
+ body: html,
141
+ style: css.code,
142
+ appId,
143
+ })
144
+ } else {
145
+ // just return the app info for jest to assert on
146
+ ctx.body = appInfo
147
+ }
148
+ } catch (error) {
149
+ if (!env.isJest()) {
150
+ const App = require("./templates/BudibaseApp.svelte").default
151
+ const { head, html, css } = App.render({
152
+ title: branding?.metaTitle,
153
+ metaTitle: branding?.metaTitle,
154
+ metaImage:
155
+ branding?.metaImageUrl ||
156
+ "https://res.cloudinary.com/daog6scxm/image/upload/v1666109324/meta-images/budibase-meta-image_uukc1m.png",
157
+ metaDescription: branding?.metaDescription || "",
158
+ favicon:
159
+ branding.faviconUrl !== ""
160
+ ? objectStore.getGlobalFileUrl("settings", "faviconUrl")
161
+ : "",
162
+ })
163
+
164
+ const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)
165
+ ctx.body = await processString(appHbs, {
166
+ head,
167
+ body: html,
168
+ style: css.code,
169
+ })
170
+ }
128
171
  }
129
172
  }
130
173
 
@@ -1,7 +1,10 @@
1
1
  <script>
2
2
  export let title = ""
3
3
  export let favicon = ""
4
+
4
5
  export let metaImage = ""
6
+ export let metaTitle = ""
7
+ export let metaDescription = ""
5
8
 
6
9
  export let clientLibPath
7
10
  export let usedPlugins
@@ -13,18 +16,33 @@
13
16
  name="viewport"
14
17
  content="width=device-width, initial-scale=1.0, viewport-fit=cover"
15
18
  />
19
+
20
+ <!-- Primary Meta Tags -->
21
+ <meta name="title" content={metaTitle} />
22
+ <meta name="description" content={metaDescription} />
23
+
16
24
  <!-- Opengraph Meta Tags -->
17
- <meta name="twitter:card" content="summary_large_image" />
18
- <meta name="twitter:site" content="@budibase" />
19
- <meta name="twitter:image" content={metaImage} />
20
- <meta name="twitter:title" content="{title} - built with Budibase" />
21
25
  <meta property="og:site_name" content="Budibase" />
22
- <meta property="og:title" content="{title} - built with Budibase" />
26
+ <meta property="og:title" content={metaTitle} />
27
+ <meta property="og:description" content={metaDescription} />
23
28
  <meta property="og:type" content="website" />
24
29
  <meta property="og:image" content={metaImage} />
25
30
 
31
+ <!-- Twitter -->
32
+ <meta property="twitter:card" content="summary_large_image" />
33
+ <meta property="twitter:site" content="@budibase" />
34
+ <meta property="twitter:image" content={metaImage} />
35
+ <meta property="twitter:image:alt" content={metaTitle} />
36
+ <meta property="twitter:title" content={metaTitle} />
37
+ <meta property="twitter:description" content={metaDescription} />
38
+
26
39
  <title>{title}</title>
27
- <link rel="icon" type="image/png" href={favicon} />
40
+ {#if favicon !== ""}
41
+ <link rel="icon" type="image/png" href={favicon} />
42
+ {:else}
43
+ <link rel="icon" type="image/png" href="https://i.imgur.com/Xhdt1YP.png" />
44
+ {/if}
45
+
28
46
  <link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
29
47
  <link rel="preconnect" href="https://fonts.gstatic.com" />
30
48
  <link
@@ -83,11 +101,16 @@
83
101
 
84
102
  <body id="app">
85
103
  <div id="error">
86
- <h1>There was an error loading your app</h1>
87
- <h2>
88
- The Budibase client library could not be loaded. Try republishing your
89
- app.
90
- </h2>
104
+ {#if clientLibPath}
105
+ <h1>There was an error loading your app</h1>
106
+ <h2>
107
+ The Budibase client library could not be loaded. Try republishing your
108
+ app.
109
+ </h2>
110
+ {:else}
111
+ <h2>We couldn't find that application</h2>
112
+ <p />
113
+ {/if}
91
114
  </div>
92
115
  <script type="application/javascript">
93
116
  window.INIT_TIME = Date.now()