From a9e63338edcc3f230806eb290a288431b889e10e Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 12 Mar 2025 11:22:04 +0000 Subject: [PATCH 01/65] pwa first pass --- .../app/[application]/settings/_layout.svelte | 5 + .../app/[application]/settings/pwa.svelte | 430 ++++++++++++++++++ packages/builder/src/stores/builder/app.ts | 22 +- .../builder/src/stores/builder/websocket.ts | 1 + .../src/api/controllers/static/index.ts | 81 +++- packages/server/src/api/routes/static.ts | 1 + packages/types/src/documents/app/app.ts | 13 + 7 files changed, 549 insertions(+), 4 deletions(-) create mode 100644 packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte diff --git a/packages/builder/src/pages/builder/app/[application]/settings/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/settings/_layout.svelte index 5691c2556b..fe360de808 100644 --- a/packages/builder/src/pages/builder/app/[application]/settings/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/settings/_layout.svelte @@ -29,6 +29,11 @@ url={$url("./embed")} active={$isActive("./embed")} /> + + import { + Layout, + Divider, + Heading, + Body, + Input, + TextArea, + ColorPicker, + Button, + Label, + File, + notifications, + Select, + } from "@budibase/bbui" + import { appStore } from "@/stores/builder" + import { API } from "@/api" + + // Only allow PNG files for better PWA compatibility + const imageExtensions = [".png"] + + let pwaConfig = $appStore.pwa || { + name: "", + short_name: "", + description: "", + icons: [], + background_color: "#FFFFFF", + theme_color: "#FFFFFF", + display: "standalone", + start_url: "", + scope: "", + } + + let saving = false + let iconFile = null + let iconPreview = null + + // Display mode options + const displayOptions = [ + { label: "Standalone", value: "standalone" }, + { label: "Fullscreen", value: "fullscreen" }, + { label: "Minimal UI", value: "minimal-ui" }, + { label: "Browser", value: "browser" }, + ] + + // Get existing icon if available + $: icon = + pwaConfig.icons && pwaConfig.icons.length > 0 + ? { url: pwaConfig.icons[0].src, type: "image", name: "PWA Icon" } + : null + + const previewUrl = async localFile => { + if (!localFile) { + return Promise.resolve(null) + } + + return new Promise(resolve => { + let reader = new FileReader() + try { + reader.onload = e => { + resolve({ + result: e.target.result, + }) + } + reader.readAsDataURL(localFile) + } catch (error) { + console.error(error) + resolve(null) + } + }) + } + + $: previewUrl(iconFile).then(response => { + if (response) { + iconPreview = response.result + } + }) + + async function uploadIcon(file) { + let response = {} + try { + let data = new FormData() + data.append("file", file) + response = await API.uploadBuilderAttachment(data) + } catch (error) { + notifications.error("Error uploading icon") + console.error("Error uploading icon:", error) + } + return response + } + + async function saveFiles() { + if (iconFile) { + const iconResp = await uploadIcon(iconFile) + if (iconResp[0]?.url) { + // Get the full absolute URL + let iconUrl = iconResp[0].url + // Ensure the URL is absolute + if (iconUrl.startsWith("/")) { + iconUrl = window.location.origin + iconUrl + } + + // Update the PWA config with the new icon URL + pwaConfig = { + ...pwaConfig, + icons: [ + { + src: iconUrl, + sizes: "192x192", + type: "image/png", + }, + ], + } + } + } + } + + function ensureHexFormat(color) { + if (!color) return "#FFFFFF" + + if (color.startsWith("#")) return color + + const rgbMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/) + if (rgbMatch) { + const r = parseInt(rgbMatch[1]).toString(16).padStart(2, "0") + const g = parseInt(rgbMatch[2]).toString(16).padStart(2, "0") + const b = parseInt(rgbMatch[3]).toString(16).padStart(2, "0") + return `#${r}${g}${b}`.toUpperCase() + } + + const rgbaMatch = color.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)/) + if (rgbaMatch) { + const r = parseInt(rgbaMatch[1]).toString(16).padStart(2, "0") + const g = parseInt(rgbaMatch[2]).toString(16).padStart(2, "0") + const b = parseInt(rgbaMatch[3]).toString(16).padStart(2, "0") + return `#${r}${g}${b}`.toUpperCase() + } + + return "#FFFFFF" + } + + function getCssVariableValue(cssVar) { + if (!cssVar || !cssVar.startsWith("var(--")) { + return ensureHexFormat(cssVar) + } + + const spectrumColors = { + "var(--spectrum-global-color-gray-50)": "#FFFFFF", + "var(--spectrum-global-color-gray-75)": "#FAFAFA", + "var(--spectrum-global-color-gray-100)": "#F5F5F5", + "var(--spectrum-global-color-gray-200)": "#EAEAEA", + "var(--spectrum-global-color-gray-300)": "#E1E1E1", + "var(--spectrum-global-color-gray-400)": "#CACACA", + "var(--spectrum-global-color-gray-500)": "#B3B3B3", + "var(--spectrum-global-color-gray-600)": "#8E8E8E", + "var(--spectrum-global-color-gray-700)": "#6E6E6E", + "var(--spectrum-global-color-gray-800)": "#4B4B4B", + "var(--spectrum-global-color-gray-900)": "#2C2C2C", + "var(--spectrum-global-color-blue-400)": "#2680EB", + "var(--spectrum-global-color-blue-500)": "#1473E6", + "var(--spectrum-global-color-blue-600)": "#0D66D0", + "var(--spectrum-global-color-blue-700)": "#095ABA", + "var(--spectrum-global-color-red-400)": "#E34850", + "var(--spectrum-global-color-red-500)": "#D7373F", + "var(--spectrum-global-color-red-600)": "#C9252D", + "var(--spectrum-global-color-red-700)": "#BB121A", + "var(--spectrum-global-color-green-400)": "#2D9D78", + "var(--spectrum-global-color-green-500)": "#268E6C", + "var(--spectrum-global-color-green-600)": "#12805C", + "var(--spectrum-global-color-green-700)": "#107154", + "var(--spectrum-global-color-orange-400)": "#E68619", + "var(--spectrum-global-color-orange-500)": "#DA7B11", + "var(--spectrum-global-color-orange-600)": "#CB6F10", + "var(--spectrum-global-color-orange-700)": "#BD640D", + "var(--spectrum-global-color-yellow-400)": "#DFBF00", + "var(--spectrum-global-color-yellow-500)": "#D2B200", + "var(--spectrum-global-color-yellow-600)": "#C4A600", + "var(--spectrum-global-color-yellow-700)": "#B79900", + "var(--spectrum-global-color-seafoam-400)": "#1B959A", + "var(--spectrum-global-color-seafoam-500)": "#16878C", + "var(--spectrum-global-color-seafoam-600)": "#0F797D", + "var(--spectrum-global-color-seafoam-700)": "#096C6F", + "var(--spectrum-global-color-indigo-400)": "#6767EC", + "var(--spectrum-global-color-indigo-500)": "#5C5CE0", + "var(--spectrum-global-color-indigo-600)": "#5151D3", + "var(--spectrum-global-color-indigo-700)": "#4646C6", + "var(--spectrum-global-color-magenta-400)": "#D83790", + "var(--spectrum-global-color-magenta-500)": "#CE2783", + "var(--spectrum-global-color-magenta-600)": "#BC1C74", + "var(--spectrum-global-color-magenta-700)": "#AE0E66", + "var(--spectrum-global-color-static-white)": "#FFFFFF", + "var(--spectrum-global-color-static-black)": "#000000", + } + + try { + if (spectrumColors[cssVar]) { + return spectrumColors[cssVar] + } + + const varName = cssVar.match(/var\((.*?)\)/)[1] + const computedValue = getComputedStyle(document.documentElement) + .getPropertyValue(varName) + .trim() + + if (computedValue) { + return ensureHexFormat(computedValue) + } + + return "#FFFFFF" + } catch (error) { + console.error("Error converting CSS variable:", error) + return "#FFFFFF" + } + } + + const handleSubmit = async () => { + try { + saving = true + + await saveFiles() + + const bgColor = getCssVariableValue(pwaConfig.background_color) + const themeColor = getCssVariableValue(pwaConfig.theme_color) + + const pwaConfigToSave = { + ...pwaConfig, + background_color: bgColor, + theme_color: themeColor, + } + + await API.saveAppMetadata($appStore.appId, { pwa: pwaConfigToSave }) + + appStore.update(state => ({ + ...state, + pwa: pwaConfigToSave, + })) + + notifications.success("PWA settings saved successfully") + } catch (error) { + notifications.error("Error saving PWA settings") + console.error("Error saving PWA settings:", error) + } finally { + saving = false + } + } + + + + + Progressive Web App + + Transform your app into an installable, app-like experience with a + Progressive Web App (PWA). Developers can configure app details, visuals, + and notifications to create a branded, professional experience for their + users. + + + + +
+
+ +
+ App details + + Define the identity of your app, including its name, description, and + how it will appear to users when installed. + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ Appearance + + Make your app visually appealing with a custom icon and theme. These + settings control how your app appears on splash screens and device + interfaces. + +
+ +
+ +
+ { + notifications.warn("File too large. 20mb limit") + }} + extensions={imageExtensions} + previewUrl={iconPreview || icon?.url} + on:change={e => { + if (e.detail) { + iconFile = e.detail + iconPreview = null + } else { + iconFile = null + iconPreview = null + pwaConfig = { + ...pwaConfig, + icons: [], + } + } + }} + value={iconFile || icon} + disabled={saving} + allowClear={true} + /> +
+

Use a 192x192 PNG image

+
+
+
+ +
+ + (pwaConfig.background_color = e.detail)} + /> +
+ +
+ + (pwaConfig.theme_color = e.detail)} + /> +
+ + + +
+ Manifest settings + + The manifest settings control how your app behaves once installed. + These settings define the app's entry point, navigation boundaries, + and how it appears on the user's device. Configuring these fields + ensures your app is treated as a native-like application. + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+
+
+ + diff --git a/packages/builder/src/stores/builder/app.ts b/packages/builder/src/stores/builder/app.ts index abebb5d9f1..1bf9438bda 100644 --- a/packages/builder/src/stores/builder/app.ts +++ b/packages/builder/src/stores/builder/app.ts @@ -6,6 +6,7 @@ import { AppIcon, AutomationSettings, Plugin, + PWAManifest, } from "@budibase/types" interface ClientFeatures { @@ -46,6 +47,7 @@ interface AppMetaState { revertableVersion?: string upgradableVersion?: string icon?: AppIcon + pwa?: PWAManifest } export const INITIAL_APP_META_STATE: AppMetaState = { @@ -79,6 +81,14 @@ export const INITIAL_APP_META_STATE: AppMetaState = { usedPlugins: [], automations: {}, routes: {}, + pwa: { + name: "", + short_name: "", + description: "", + icons: [], + background_color: "", + theme_color: "", + }, } export class AppMetaStore extends BudiStore { @@ -96,7 +106,6 @@ export class AppMetaStore extends BudiStore { hasLock: boolean }) { const { application: app, clientLibPath, hasLock } = pkg - this.update(state => ({ ...state, name: app.name, @@ -118,6 +127,7 @@ export class AppMetaStore extends BudiStore { initialised: true, automations: app.automations || {}, hasAppPackage: true, + pwa: app.pwa, })) } @@ -149,11 +159,21 @@ export class AppMetaStore extends BudiStore { // Returned from socket syncMetadata(metadata: { name: string; url: string; icon?: AppIcon }) { const { name, url, icon } = metadata + console.log(name) this.update(state => ({ ...state, name, url, icon, + pwa: { + ...state.pwa, + name, + short_name: state.pwa?.short_name || "", + description: state.pwa?.description || "", + icons: state.pwa?.icons || [], + background_color: state.pwa?.background_color || "", + theme_color: state.pwa?.theme_color || "", + }, })) } } diff --git a/packages/builder/src/stores/builder/websocket.ts b/packages/builder/src/stores/builder/websocket.ts index a56fec2227..b79a6fd336 100644 --- a/packages/builder/src/stores/builder/websocket.ts +++ b/packages/builder/src/stores/builder/websocket.ts @@ -101,6 +101,7 @@ export const createBuilderWebsocket = (appId: string) => { socket.onOther( BuilderSocketEvent.AppMetadataChange, ({ metadata }: { metadata: any }) => { + console.log("hello?") appStore.syncMetadata(metadata) themeStore.syncMetadata(metadata) navigationStore.syncMetadata(metadata) diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index 6b8ecda0d9..b8e78cedec 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -32,6 +32,7 @@ import { GetSignedUploadUrlRequest, GetSignedUploadUrlResponse, ProcessAttachmentResponse, + PWAManifest, ServeAppResponse, ServeBuilderPreviewResponse, ServeClientLibraryResponse, @@ -189,8 +190,9 @@ export const serveApp = async function (ctx: UserCtx) { const sideNav = appInfo.navigation.navigation === "Left" const hideFooter = ctx?.user?.license?.features?.includes(Feature.BRANDING) || false - const themeVariables = getThemeVariables(appInfo?.theme) - + const themeVariables = getThemeVariables(appInfo?.theme || {}) + const hasPWA = Object.keys(appInfo.pwa || {}).length > 0 + const manifestUrl = hasPWA ? `/api/apps/${appId}/manifest.json` : "" if (!env.isJest()) { const plugins = await objectStore.enrichPluginURLs(appInfo.usedPlugins) /* @@ -223,8 +225,25 @@ export const serveApp = async function (ctx: UserCtx) { const { head, html, css } = AppComponent.render({ props }) const appHbs = loadHandlebarsFile(appHbsPath) + + let extraHead = "" + if (hasPWA) { + extraHead = `` + + extraHead += ` + +` + if (appInfo.pwa.icons && appInfo.pwa.icons.length > 0) { + let appleIconUrl = appInfo.pwa.icons[0].src + console.log(appleIconUrl) + extraHead += `` + } + } + ctx.body = await processString(appHbs, { - head, + head: `${head}${extraHead}`, body: html, css: `:root{${themeVariables}} ${css.code}`, appId, @@ -365,3 +384,59 @@ export const getSignedUploadURL = async function ( ctx.body = { signedUrl, publicUrl } } + +export const serveManifest = async function (ctx: UserCtx) { + const appId = context.getAppId() + if (!appId) { + ctx.status = 404 + ctx.body = { message: "App not found" } + return + } + + try { + const db = context.getAppDB({ skip_setup: true }) + const appInfo = await db.get(DocumentType.APP_METADATA) + + if (!appInfo.pwa) { + ctx.status = 404 + ctx.body = { message: "PWA not configured for this app" } + return + } + + const manifest: PWAManifest = { + name: appInfo.pwa.name || appInfo.name, + short_name: appInfo.pwa.short_name || appInfo.name, + description: appInfo.pwa.description || "", + start_url: + `/app${appInfo.url}#${appInfo.pwa.start_url}` || `/app/${appInfo.url}`, + display: appInfo.pwa.display || "standalone", + background_color: appInfo.pwa.background_color || "#FFFFFF", + theme_color: appInfo.pwa.theme_color || "#FFFFFF", + icons: [], + } + + if (appInfo.pwa.icons && appInfo.pwa.icons.length > 0) { + manifest.icons = appInfo.pwa.icons.map(icon => { + let src = icon.src + + if (src && src.startsWith("/") && !src.startsWith("//")) { + const origin = ctx.request.origin + src = `${origin}${src}` + } + + return { + ...icon, + src, + type: icon.type || "image/png", + } + }) + } + + ctx.set("Content-Type", "application/json") + ctx.body = manifest + } catch (error) { + console.error("Error serving manifest:", error) + ctx.status = 500 + ctx.body = { message: "Error generating manifest" } + } +} diff --git a/packages/server/src/api/routes/static.ts b/packages/server/src/api/routes/static.ts index f331609923..4fbd96249a 100644 --- a/packages/server/src/api/routes/static.ts +++ b/packages/server/src/api/routes/static.ts @@ -31,6 +31,7 @@ router.param("file", async (file: any, ctx: any, next: any) => { router .get("/builder/:file*", controller.serveBuilder) .get("/api/assets/client", controller.serveClientLibrary) + .get("/api/apps/:appId/manifest.json", controller.serveManifest) .post("/api/attachments/process", authorized(BUILDER), controller.uploadFile) .post("/api/beta/:feature", controller.toggleBetaUiFeature) .post( diff --git a/packages/types/src/documents/app/app.ts b/packages/types/src/documents/app/app.ts index 46799cb61a..02bc4a915e 100644 --- a/packages/types/src/documents/app/app.ts +++ b/packages/types/src/documents/app/app.ts @@ -29,6 +29,7 @@ export interface App extends Document { snippets?: Snippet[] creationVersion?: string updatedBy?: string + pwa?: PWAManifest } export interface AppInstance { @@ -82,3 +83,15 @@ export interface AppFeatures { export interface AutomationSettings { chainAutomations?: boolean } + +export interface PWAManifest { + name: string + short_name: string + description: string + icons: { src: string; sizes: string; type: string }[] + background_color: string + theme_color: string + display?: string + start_url?: string + scope?: string +} From 5181ac1be5ccb2c72b84a40414a11e7319f2d931 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 13 Mar 2025 16:11:22 +0000 Subject: [PATCH 02/65] enriching of file urls --- .../src/objectStore/buckets/app.ts | 26 ++++++++++++++ .../app/[application]/settings/pwa.svelte | 33 ++++------------- packages/builder/src/stores/builder/app.ts | 1 - .../builder/src/stores/builder/websocket.ts | 1 - .../server/src/api/controllers/application.ts | 7 ++++ .../src/api/controllers/static/index.ts | 36 +++++++++---------- packages/types/src/documents/app/app.ts | 8 ++++- 7 files changed, 64 insertions(+), 48 deletions(-) diff --git a/packages/backend-core/src/objectStore/buckets/app.ts b/packages/backend-core/src/objectStore/buckets/app.ts index dbf49ca994..109ee17222 100644 --- a/packages/backend-core/src/objectStore/buckets/app.ts +++ b/packages/backend-core/src/objectStore/buckets/app.ts @@ -3,6 +3,7 @@ import * as objectStore from "../objectStore" import * as cloudfront from "../cloudfront" import qs from "querystring" import { DEFAULT_TENANT_ID, getTenantId } from "../../context" +import { PWAManifestIcon } from "@budibase/types" export function clientLibraryPath(appId: string) { return `${objectStore.sanitizeKey(appId)}/budibase-client.js` @@ -51,3 +52,28 @@ export async function getAppFileUrl(s3Key: string) { return await objectStore.getPresignedUrl(env.APPS_BUCKET_NAME, s3Key) } } + +export async function enrichPWAIcons( + icons: PWAManifestIcon[] +): Promise { + if (!icons || icons.length === 0) { + return [] + } + + try { + return await Promise.all( + icons.map(async icon => { + let src = icon.src + src = await getAppFileUrl(src) + return { + ...icon, + src, + type: icon.type || "image/png", + } + }) + ) + } catch (error) { + console.error("Error enriching PWA icons:", error) + return icons + } +} diff --git a/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte b/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte index c805044abb..68020264c3 100644 --- a/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte +++ b/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte @@ -5,7 +5,6 @@ Heading, Body, Input, - TextArea, ColorPicker, Button, Label, @@ -27,8 +26,6 @@ background_color: "#FFFFFF", theme_color: "#FFFFFF", display: "standalone", - start_url: "", - scope: "", } let saving = false @@ -40,7 +37,6 @@ { label: "Standalone", value: "standalone" }, { label: "Fullscreen", value: "fullscreen" }, { label: "Minimal UI", value: "minimal-ui" }, - { label: "Browser", value: "browser" }, ] // Get existing icon if available @@ -92,20 +88,16 @@ async function saveFiles() { if (iconFile) { const iconResp = await uploadIcon(iconFile) - if (iconResp[0]?.url) { - // Get the full absolute URL - let iconUrl = iconResp[0].url - // Ensure the URL is absolute - if (iconUrl.startsWith("/")) { - iconUrl = window.location.origin + iconUrl - } + if (iconResp[0]?.key) { + // Store the S3 key directly in the src field + const iconKey = iconResp[0].key - // Update the PWA config with the new icon URL + // Update the PWA config with the icon key as the src pwaConfig = { ...pwaConfig, icons: [ { - src: iconUrl, + src: iconKey, // Store the S3 key directly in src sizes: "192x192", type: "image/png", }, @@ -357,27 +349,16 @@ Manifest settings The manifest settings control how your app behaves once installed. - These settings define the app's entry point, navigation boundaries, - and how it appears on the user's device. Configuring these fields - ensures your app is treated as a native-like application. + These settings define how it appears on the user's device. Configuring + these fields ensures your app is treated as a native-like application. -
- - -
-
-
-
@@ -371,11 +391,21 @@ diff --git a/packages/client/src/service-worker.js b/packages/client/src/service-worker.js new file mode 100644 index 0000000000..2ce949f734 --- /dev/null +++ b/packages/client/src/service-worker.js @@ -0,0 +1,13 @@ +// Minimal service worker to enable PWA installation +self.addEventListener("install", () => { + self.skipWaiting() +}) + +self.addEventListener("activate", () => { + clients.claim() +}) + +// Minimal fetch handler - just to have the required service worker functionality +self.addEventListener("fetch", event => { + event.respondWith(fetch(event.request)) +}) From 237914d0b6760f98d8a306af47249629c3b2d6be Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 1 Apr 2025 09:11:46 +0100 Subject: [PATCH 25/65] service worker debugging --- packages/backend-core/src/middleware/contentSecurityPolicy.ts | 2 +- packages/client/src/components/ClientApp.svelte | 2 +- packages/client/src/components/overlay/InstallPrompt.svelte | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/backend-core/src/middleware/contentSecurityPolicy.ts b/packages/backend-core/src/middleware/contentSecurityPolicy.ts index d1668d3dd5..79fdbb6b61 100644 --- a/packages/backend-core/src/middleware/contentSecurityPolicy.ts +++ b/packages/backend-core/src/middleware/contentSecurityPolicy.ts @@ -83,7 +83,7 @@ const CSP_DIRECTIVES = { "https://js.intercomcdn.com", "https://cdn.budi.live", ], - "worker-src": ["blob:"], + "worker-src": ["blob:", "'self'"], } export async function contentSecurityPolicy(ctx: any, next: any) { diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 60e3ac1433..424587e1f1 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -97,7 +97,7 @@ // Register service worker if ("serviceWorker" in navigator) { try { - await navigator.serviceWorker.register("/service-worker.js") + await navigator.serviceWorker.register("service-worker.js") } catch (error) { console.error("Service worker registration failed:", error) } diff --git a/packages/client/src/components/overlay/InstallPrompt.svelte b/packages/client/src/components/overlay/InstallPrompt.svelte index 6c81ef3476..7a25feb361 100644 --- a/packages/client/src/components/overlay/InstallPrompt.svelte +++ b/packages/client/src/components/overlay/InstallPrompt.svelte @@ -5,6 +5,7 @@ onMount(() => { window.addEventListener("beforeinstallprompt", e => { + console.log("[PWA] beforeinstallprompt fired!", e) e.preventDefault() deferredPrompt = e showButton = true From 92343225102cd5added4b5cc2d90bb19d4fb71ea Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 1 Apr 2025 14:28:16 +0100 Subject: [PATCH 26/65] refactor how we fetch the service worker --- .../app/[application]/settings/pwa.svelte | 2 +- .../client/src/components/ClientApp.svelte | 6 +- .../components/overlay/InstallPrompt.svelte | 65 ++++++++++++++----- packages/client/src/index.ts | 10 +++ packages/client/src/service-worker.js | 13 ---- .../src/api/controllers/static/index.ts | 16 +++++ packages/server/src/api/routes/static.ts | 1 + 7 files changed, 79 insertions(+), 34 deletions(-) delete mode 100644 packages/client/src/service-worker.js diff --git a/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte b/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte index 643c964092..41c13ac4b6 100644 --- a/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte +++ b/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte @@ -26,7 +26,7 @@ ] let saving = false - let pwaEnabled = $licensing.pwaEnabled + let pwaEnabled = true let uploadingIcons = false let pwaConfig = $appStore.pwa || { diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 424587e1f1..4aa1756437 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -92,12 +92,12 @@ let fontsLoaded = false - // Load app config onMount(async () => { - // Register service worker if ("serviceWorker" in navigator) { try { - await navigator.serviceWorker.register("service-worker.js") + await navigator.serviceWorker.register("/app/service-worker.js", { + scope: "/app/", + }) } catch (error) { console.error("Service worker registration failed:", error) } diff --git a/packages/client/src/components/overlay/InstallPrompt.svelte b/packages/client/src/components/overlay/InstallPrompt.svelte index 7a25feb361..1deaceb615 100644 --- a/packages/client/src/components/overlay/InstallPrompt.svelte +++ b/packages/client/src/components/overlay/InstallPrompt.svelte @@ -1,39 +1,70 @@ {#if showButton} - +
+ +
{/if} diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index 43c5d71d68..1be6072343 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -32,6 +32,13 @@ import { import { ActionTypes } from "@/constants" import { APIClient } from "@budibase/frontend-core" +if (typeof window !== "undefined") { + window.addEventListener("beforeinstallprompt", e => { + e.preventDefault() + window.deferredPwaPrompt = e + }) +} + // Provide svelte and svelte/internal as globals for custom components import * as svelte from "svelte" // @ts-ignore @@ -67,6 +74,9 @@ declare global { // Other flags MIGRATING_APP: boolean + // PWA install prompt + deferredPwaPrompt: any + // Client additions handleBuilderRuntimeEvent: (type: string, data: any) => void registerCustomComponent: typeof componentStore.actions.registerCustomComponent diff --git a/packages/client/src/service-worker.js b/packages/client/src/service-worker.js deleted file mode 100644 index 2ce949f734..0000000000 --- a/packages/client/src/service-worker.js +++ /dev/null @@ -1,13 +0,0 @@ -// Minimal service worker to enable PWA installation -self.addEventListener("install", () => { - self.skipWaiting() -}) - -self.addEventListener("activate", () => { - clients.claim() -}) - -// Minimal fetch handler - just to have the required service worker functionality -self.addEventListener("fetch", event => { - event.respondWith(fetch(event.request)) -}) diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index f79a600ab5..4bf4ff2acd 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -467,6 +467,22 @@ export const serveClientLibrary = async function ( } } +export const serveServiceWorker = async function (ctx: Ctx) { + const serviceWorkerContent = ` +self.addEventListener('install', () => { + self.skipWaiting(); +}); + +// Minimal fetch handler +self.addEventListener('fetch', event => { + event.respondWith(fetch(event.request)); +}); +` + + ctx.set("Content-Type", "application/javascript") + ctx.body = serviceWorkerContent +} + export const getSignedUploadURL = async function ( ctx: Ctx ) { diff --git a/packages/server/src/api/routes/static.ts b/packages/server/src/api/routes/static.ts index cb89fedf30..4d77118c43 100644 --- a/packages/server/src/api/routes/static.ts +++ b/packages/server/src/api/routes/static.ts @@ -24,6 +24,7 @@ router controller.uploadFile ) .get("/app/preview", authorized(BUILDER), controller.serveBuilderPreview) + .get("/app/service-worker.js", controller.serveServiceWorker) .get("/app/:appUrl/:path*", controller.serveApp) .get("/:appId/:path*", controller.serveApp) .post( From fb9533fddf0c505bc8bcfede83572af69fab3d9f Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 1 Apr 2025 14:28:38 +0100 Subject: [PATCH 27/65] rogue char --- packages/client/src/components/overlay/InstallPrompt.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/components/overlay/InstallPrompt.svelte b/packages/client/src/components/overlay/InstallPrompt.svelte index 1deaceb615..cb6c78e440 100644 --- a/packages/client/src/components/overlay/InstallPrompt.svelte +++ b/packages/client/src/components/overlay/InstallPrompt.svelte @@ -25,7 +25,7 @@ if (outcome === "accepted") { showButton = false } else if (outcome === "dismissed") { -= localStorage.setItem(STORAGE_KEY, "true") + localStorage.setItem(STORAGE_KEY, "true") showButton = false } From 71bc0bcb93db4bc95cf2192d4caacf7375b6a4fb Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 1 Apr 2025 14:44:39 +0100 Subject: [PATCH 28/65] pro --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 1079d50f24..5472ee0db8 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 1079d50f243f688039906d05108283991de51b52 +Subproject commit 5472ee0db8ae2af9f36529484805c6b87fb34929 From bb6b9d3d8ad6a41e71b621775fad75a2bbe3d7b6 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 1 Apr 2025 16:07:36 +0100 Subject: [PATCH 29/65] fix lint --- .../src/pages/builder/app/[application]/settings/pwa.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte b/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte index 41c13ac4b6..643c964092 100644 --- a/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte +++ b/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte @@ -26,7 +26,7 @@ ] let saving = false - let pwaEnabled = true + let pwaEnabled = $licensing.pwaEnabled let uploadingIcons = false let pwaConfig = $appStore.pwa || { From 41cc20b39f36bed918727bd7301418f9ce9e5ab5 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 1 Apr 2025 16:13:03 +0100 Subject: [PATCH 30/65] fix lint oncemore --- .../src/pages/builder/app/[application]/settings/pwa.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte b/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte index 643c964092..41c13ac4b6 100644 --- a/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte +++ b/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte @@ -26,7 +26,7 @@ ] let saving = false - let pwaEnabled = $licensing.pwaEnabled + let pwaEnabled = true let uploadingIcons = false let pwaConfig = $appStore.pwa || { From b03dbbab51bf52cdd580296b2a2bb384e33c5c1a Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 1 Apr 2025 16:13:11 +0100 Subject: [PATCH 31/65] fix lint oncemore --- .../src/pages/builder/app/[application]/settings/pwa.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte b/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte index 41c13ac4b6..5eb7fedf84 100644 --- a/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte +++ b/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte @@ -15,7 +15,6 @@ Tag, } from "@budibase/bbui" import { appStore } from "@/stores/builder" - import { licensing } from "@/stores/portal" import { API } from "@/api" From 49d825616d5da54351047a8499448366712f5d35 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 2 Apr 2025 08:24:23 +0100 Subject: [PATCH 32/65] some debug logging --- packages/client/src/components/ClientApp.svelte | 1 + packages/client/src/components/overlay/InstallPrompt.svelte | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 4aa1756437..ad32137a56 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -94,6 +94,7 @@ onMount(async () => { if ("serviceWorker" in navigator) { + console.log("Service worker supported") try { await navigator.serviceWorker.register("/app/service-worker.js", { scope: "/app/", diff --git a/packages/client/src/components/overlay/InstallPrompt.svelte b/packages/client/src/components/overlay/InstallPrompt.svelte index cb6c78e440..f719e66cf9 100644 --- a/packages/client/src/components/overlay/InstallPrompt.svelte +++ b/packages/client/src/components/overlay/InstallPrompt.svelte @@ -5,6 +5,8 @@ const STORAGE_KEY = "pwa-install-declined" function checkForDeferredPrompt() { + console.log("Checking for deferred prompt") + if (localStorage.getItem(STORAGE_KEY) === "true") { return false } From ee7d270908480bffca372d4f66f3f473058aea4a Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 2 Apr 2025 08:43:06 +0100 Subject: [PATCH 33/65] more logging --- packages/client/src/components/ClientApp.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index ad32137a56..18c18b32a3 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -93,6 +93,7 @@ let fontsLoaded = false onMount(async () => { + console.log("Mounting ClientApp") if ("serviceWorker" in navigator) { console.log("Service worker supported") try { From d74315d6fe9a7b4a54dd7a29b55520c039ff43b5 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 2 Apr 2025 09:34:53 +0100 Subject: [PATCH 34/65] remove logging and add appId to storage key --- packages/client/src/components/ClientApp.svelte | 2 -- .../src/components/overlay/InstallPrompt.svelte | 15 ++++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 18c18b32a3..4aa1756437 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -93,9 +93,7 @@ let fontsLoaded = false onMount(async () => { - console.log("Mounting ClientApp") if ("serviceWorker" in navigator) { - console.log("Service worker supported") try { await navigator.serviceWorker.register("/app/service-worker.js", { scope: "/app/", diff --git a/packages/client/src/components/overlay/InstallPrompt.svelte b/packages/client/src/components/overlay/InstallPrompt.svelte index f719e66cf9..5f3d01af9f 100644 --- a/packages/client/src/components/overlay/InstallPrompt.svelte +++ b/packages/client/src/components/overlay/InstallPrompt.svelte @@ -1,17 +1,20 @@ @@ -243,9 +234,7 @@
- +
diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 4aa1756437..0dbd607637 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -92,6 +92,7 @@ let fontsLoaded = false + // Load app config onMount(async () => { if ("serviceWorker" in navigator) { try { diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index 4bf4ff2acd..ed26ccc5e0 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -201,7 +201,7 @@ export async function processPWAZip(ctx: UserCtx) { }) } } catch (uploadError) { - console.error(`Failed to upload icon ${icon.src}:`, uploadError) + throw new Error(`Failed to upload icon ${icon.src}: ${uploadError}`) } } @@ -359,7 +359,7 @@ export const serveApp = async function (ctx: UserCtx) { extraHead += `` } } catch (error) { - console.error("Error enriching PWA icons:", error) + throw new Error("Error enriching PWA icons: " + error) } } } @@ -594,14 +594,13 @@ export async function servePwaManifest(ctx: UserCtx) { }) } } catch (error) { - console.error("Error processing manifest icons:", error) + throw new Error("Error processing manifest icons: " + error) } } ctx.set("Content-Type", "application/json") ctx.body = manifest } catch (error) { - console.error("Error serving manifest:", error) ctx.status = 500 ctx.body = { message: "Error generating manifest" } } From 680796164bb72814efe8c4d70fd8ca531238cf3f Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 2 Apr 2025 11:41:43 +0100 Subject: [PATCH 38/65] re-enable licensing --- .../app/[application]/settings/pwa.svelte | 16 +++++++++++++--- .../server/src/api/controllers/static/index.ts | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte b/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte index a441f1a34f..ddb7bb3bae 100644 --- a/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte +++ b/packages/builder/src/pages/builder/app/[application]/settings/pwa.svelte @@ -5,6 +5,7 @@ Heading, Body, Input, + Icon, ColorPicker, Button, Label, @@ -15,7 +16,7 @@ Tag, } from "@budibase/bbui" import { appStore } from "@/stores/builder" - + import { licensing } from "@/stores/portal" import { API } from "@/api" const DISPLAY_OPTIONS = [ @@ -24,7 +25,7 @@ { label: "Minimal UI", value: "minimal-ui" }, ] - let pwaEnabled = true + let pwaEnabled = $licensing.pwaEnabled let uploadingIcons = false let pwaConfig = $appStore.pwa || { @@ -177,7 +178,16 @@
- +
+ + +
) { const appHbs = loadHandlebarsFile(appHbsPath) let extraHead = "" - const pwaEnabled = true // await pro.features.isPWAEnabled() + const pwaEnabled = await pro.features.isPWAEnabled() if (hasPWA && pwaEnabled) { extraHead = `` extraHead += ` From 1c4e733110306444e53444d373f37218ed4d44cf Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 2 Apr 2025 13:37:32 +0100 Subject: [PATCH 39/65] gate features in client and csp change --- .../src/middleware/contentSecurityPolicy.ts | 2 +- .../client/src/components/ClientApp.svelte | 2 +- .../components/overlay/InstallPrompt.svelte | 31 ++++++++++--------- packages/client/src/index.ts | 12 ++++--- packages/client/src/stores/features.js | 1 + 5 files changed, 28 insertions(+), 20 deletions(-) diff --git a/packages/backend-core/src/middleware/contentSecurityPolicy.ts b/packages/backend-core/src/middleware/contentSecurityPolicy.ts index d1668d3dd5..79fdbb6b61 100644 --- a/packages/backend-core/src/middleware/contentSecurityPolicy.ts +++ b/packages/backend-core/src/middleware/contentSecurityPolicy.ts @@ -83,7 +83,7 @@ const CSP_DIRECTIVES = { "https://js.intercomcdn.com", "https://cdn.budi.live", ], - "worker-src": ["blob:"], + "worker-src": ["blob:", "'self'"], } export async function contentSecurityPolicy(ctx: any, next: any) { diff --git a/packages/client/src/components/ClientApp.svelte b/packages/client/src/components/ClientApp.svelte index 0dbd607637..25b3235493 100644 --- a/packages/client/src/components/ClientApp.svelte +++ b/packages/client/src/components/ClientApp.svelte @@ -94,7 +94,7 @@ // Load app config onMount(async () => { - if ("serviceWorker" in navigator) { + if ("serviceWorker" in navigator && $featuresStore.pwaEnabled) { try { await navigator.serviceWorker.register("/app/service-worker.js", { scope: "/app/", diff --git a/packages/client/src/components/overlay/InstallPrompt.svelte b/packages/client/src/components/overlay/InstallPrompt.svelte index 5f3d01af9f..6f2b6afb9c 100644 --- a/packages/client/src/components/overlay/InstallPrompt.svelte +++ b/packages/client/src/components/overlay/InstallPrompt.svelte @@ -1,12 +1,16 @@ diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index bbe852185e..a6606d4334 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -532,9 +532,7 @@ export const getSignedUploadURL = async function ( export async function servePwaManifest(ctx: UserCtx) { const appId = context.getAppId() if (!appId) { - ctx.status = 404 - ctx.body = { message: "App not found" } - return + ctx.throw(404) } try { @@ -542,9 +540,7 @@ export async function servePwaManifest(ctx: UserCtx) { const appInfo = await db.get(DocumentType.APP_METADATA) if (!appInfo.pwa) { - ctx.status = 404 - ctx.body = { message: "PWA not configured for this app" } - return + ctx.throw(404) } const manifest: PWAManifest = { @@ -567,7 +563,7 @@ export async function servePwaManifest(ctx: UserCtx) { icon => icon.sizes === "1240x600" || icon.sizes === "2480x1200" ) if (desktopScreenshot) { - manifest.screenshots?.push({ + manifest.screenshots.push({ src: desktopScreenshot.src, sizes: desktopScreenshot.sizes, type: "image/png", @@ -580,7 +576,7 @@ export async function servePwaManifest(ctx: UserCtx) { icon => icon.sizes === "620x620" || icon.sizes === "1024x1024" ) if (mobileScreenshot) { - manifest.screenshots?.push({ + manifest.screenshots.push({ src: mobileScreenshot.src, sizes: mobileScreenshot.sizes, type: "image/png", diff --git a/packages/types/src/documents/app/app.ts b/packages/types/src/documents/app/app.ts index d61ce3c83f..5acbe95b17 100644 --- a/packages/types/src/documents/app/app.ts +++ b/packages/types/src/documents/app/app.ts @@ -90,7 +90,7 @@ export interface PWAManifest { short_name: string description: string icons: PWAManifestImage[] - screenshots?: PWAManifestImage[] + screenshots: PWAManifestImage[] background_color: string theme_color: string display?: string From 83aa2a59fc2a4bd74a59215631c896f7464838d5 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 8 Apr 2025 10:38:34 +0100 Subject: [PATCH 59/65] pro --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index d0ddd2ddd9..dfdde33f48 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit d0ddd2ddd9fc01e25e60c17bd74ae96783e22d0a +Subproject commit dfdde33f4813d74bf784de62a992a024d0d604dd From dc0b9d489f86fea4987ec481e40819c919e244b7 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 8 Apr 2025 10:47:49 +0100 Subject: [PATCH 60/65] types --- packages/builder/src/stores/builder/app.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/builder/src/stores/builder/app.ts b/packages/builder/src/stores/builder/app.ts index 6cd9938916..ca0fdd2f67 100644 --- a/packages/builder/src/stores/builder/app.ts +++ b/packages/builder/src/stores/builder/app.ts @@ -93,6 +93,7 @@ export const INITIAL_APP_META_STATE: AppMetaState = { background_color: "", theme_color: "", start_url: "", + screenshots: [], }, scripts: [], } @@ -192,6 +193,7 @@ export class AppMetaStore extends BudiStore { background_color: state.pwa?.background_color || "", theme_color: state.pwa?.theme_color || "", start_url: state.pwa?.start_url || "", + screenshots: state.pwa?.screenshots || [], }, })) } From 3e67aaa8694bfdfe10ae4f5645e5941696c31850 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 8 Apr 2025 11:00:32 +0100 Subject: [PATCH 61/65] pro final --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index dfdde33f48..305c909a9f 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit dfdde33f4813d74bf784de62a992a024d0d604dd +Subproject commit 305c909a9f6ca88281c1213a4f4088c9f00dbc2c From 289309f199f11da964f0409589ed44c5150e43be Mon Sep 17 00:00:00 2001 From: mikesealey Date: Tue, 8 Apr 2025 11:31:53 +0100 Subject: [PATCH 62/65] updates column name in import modal --- .../components/backend/TableNavigator/TableDataImport.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte index a7d2b2e6d7..319416c9cb 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte @@ -38,7 +38,7 @@ }, }, [FIELDS.OPTIONS.type]: { - label: "Options", + label: "Single-select", value: FIELDS.OPTIONS.type, config: { type: FIELDS.OPTIONS.type, From 90c4963125a0ddf9cdea92285d22b33a3a26ff56 Mon Sep 17 00:00:00 2001 From: Conor Webb <126772285+ConorWebb96@users.noreply.github.com> Date: Tue, 8 Apr 2025 14:27:33 +0100 Subject: [PATCH 63/65] Make Proxy Timeouts Configurable via PROXY_TIMEOUT_SECONDS (#15908) --- hosting/proxy/Dockerfile | 1 + hosting/proxy/nginx.prod.conf | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/hosting/proxy/Dockerfile b/hosting/proxy/Dockerfile index 9ec458a219..085d8dbc81 100644 --- a/hosting/proxy/Dockerfile +++ b/hosting/proxy/Dockerfile @@ -17,6 +17,7 @@ COPY error.html /usr/share/nginx/html/error.html # Default environment ENV PROXY_RATE_LIMIT_WEBHOOKS_PER_SECOND=10 ENV PROXY_RATE_LIMIT_API_PER_SECOND=20 +ENV PROXY_TIMEOUT_SECONDS=120 # Use docker-compose values as defaults for backwards compatibility ENV APPS_UPSTREAM_URL=http://app-service:4002 ENV WORKER_UPSTREAM_URL=http://worker-service:4003 diff --git a/hosting/proxy/nginx.prod.conf b/hosting/proxy/nginx.prod.conf index c5d378afd8..038cb49e4f 100644 --- a/hosting/proxy/nginx.prod.conf +++ b/hosting/proxy/nginx.prod.conf @@ -144,9 +144,9 @@ http { limit_req zone=ratelimit burst=20 nodelay; # 120s timeout on API requests - proxy_read_timeout 120s; - proxy_connect_timeout 120s; - proxy_send_timeout 120s; + proxy_read_timeout ${PROXY_TIMEOUT_SECONDS}s; + proxy_connect_timeout ${PROXY_TIMEOUT_SECONDS}s; + proxy_send_timeout ${PROXY_TIMEOUT_SECONDS}s; proxy_http_version 1.1; proxy_set_header Connection $connection_upgrade; @@ -164,9 +164,9 @@ http { # Rest of configuration copied from /api/ location above # 120s timeout on API requests - proxy_read_timeout 120s; - proxy_connect_timeout 120s; - proxy_send_timeout 120s; + proxy_read_timeout ${PROXY_TIMEOUT_SECONDS}s; + proxy_connect_timeout ${PROXY_TIMEOUT_SECONDS}s; + proxy_send_timeout ${PROXY_TIMEOUT_SECONDS}s; proxy_http_version 1.1; proxy_set_header Connection $connection_upgrade; From ad2937bdec0f439aec8bb7d0ffd42a349c15db94 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 8 Apr 2025 15:47:46 +0100 Subject: [PATCH 64/65] Update pro reference --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 305c909a9f..c03471e03f 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 305c909a9f6ca88281c1213a4f4088c9f00dbc2c +Subproject commit c03471e03f0ea6ce635d20f2195d26b279ac3bea From 8364e4ce8065e6994287a7055cb9e4373854c47c Mon Sep 17 00:00:00 2001 From: mikesealey Date: Tue, 8 Apr 2025 16:52:03 +0100 Subject: [PATCH 65/65] addresses feedback --- .../components/backend/TableNavigator/TableDataImport.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte index 319416c9cb..76e505c821 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte @@ -38,7 +38,7 @@ }, }, [FIELDS.OPTIONS.type]: { - label: "Single-select", + label: "Single select", value: FIELDS.OPTIONS.type, config: { type: FIELDS.OPTIONS.type, @@ -46,7 +46,7 @@ }, }, [FIELDS.ARRAY.type]: { - label: "Multi-select", + label: "Multi select", value: FIELDS.ARRAY.type, config: { type: FIELDS.ARRAY.type,