diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 817cd17652..68e71f4cf7 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -61,3 +61,12 @@ jobs:
# macOS notarization API key
API_KEY_ID: ${{ secrets.api_key_id }}
API_KEY_ISSUER_ID: ${{ secrets.api_key_issuer_id }}
+
+ - name: Build/release Docker images
+ # only run the docker image build on linux, easiest way
+ if: startsWith(matrix.os, 'ubuntu')
+ env:
+ DOCKER_USER: ${{ secrets.DOCKER_USERNAME }}
+ DOCKER_PASSWORD: ${{ secrets.DOCKER_API_KEY }}
+ run: docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
+ run: yarn build:docker
diff --git a/.gitignore b/.gitignore
index 54401c6b36..edad41cdec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -62,6 +62,7 @@ typings/
# dotenv environment variables file
.env
+!hosting/.env
# parcel-bundler cache (https://parceljs.org/)
.cache
diff --git a/hosting/.env b/hosting/.env
new file mode 120000
index 0000000000..bb1b54ad77
--- /dev/null
+++ b/hosting/.env
@@ -0,0 +1 @@
+hosting.properties
\ No newline at end of file
diff --git a/hosting/build/docker-compose.yaml b/hosting/build/docker-compose.yaml
new file mode 100644
index 0000000000..6988e3841b
--- /dev/null
+++ b/hosting/build/docker-compose.yaml
@@ -0,0 +1,16 @@
+version: "3"
+
+services:
+ app-service:
+ build: ./server
+ volumes:
+ - ./server:/app
+ environment:
+ SELF_HOSTED: 1
+ PORT: 4002
+
+ worker-service:
+ build: ./worker
+ environment:
+ SELF_HOSTED: 1,
+ PORT: 4003
diff --git a/hosting/build/server b/hosting/build/server
new file mode 120000
index 0000000000..c40730cce5
--- /dev/null
+++ b/hosting/build/server
@@ -0,0 +1 @@
+../../packages/server/
\ No newline at end of file
diff --git a/hosting/build/worker b/hosting/build/worker
new file mode 120000
index 0000000000..8582fefbee
--- /dev/null
+++ b/hosting/build/worker
@@ -0,0 +1 @@
+../../packages/worker/
\ No newline at end of file
diff --git a/hosting/docker-compose.yaml b/hosting/docker-compose.yaml
new file mode 100644
index 0000000000..e2538774ef
--- /dev/null
+++ b/hosting/docker-compose.yaml
@@ -0,0 +1,89 @@
+version: "3"
+
+services:
+ app-service:
+ image: budibase/budibase-apps
+ ports:
+ - "${APP_PORT}:4002"
+ environment:
+ SELF_HOSTED: 1
+ COUCH_DB_URL: http://${COUCH_DB_USER}:${COUCH_DB_PASSWORD}@couchdb-service:5984
+ BUDIBASE_ENVIRONMENT: ${BUDIBASE_ENVIRONMENT}
+ PORT: 4002
+ JWT_SECRET: ${JWT_SECRET}
+ depends_on:
+ - worker-service
+
+ worker-service:
+ image: budibase/budibase-worker
+ ports:
+ - "${WORKER_PORT}:4003"
+ environment:
+ SELF_HOSTED: 1,
+ DEPLOYMENT_API_KEY: ${WORKER_API_KEY}
+ PORT: 4003
+ MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
+ MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
+ RAW_MINIO_URL: http://minio-service:9000
+ COUCH_DB_USERNAME: ${COUCH_DB_USER}
+ COUCH_DB_PASSWORD: ${COUCH_DB_PASSWORD}
+ RAW_COUCH_DB_URL: http://couchdb-service:5984
+ SELF_HOST_KEY: ${HOSTING_KEY}
+ depends_on:
+ - minio-service
+ - couch-init
+
+ minio-service:
+ image: minio/minio
+ volumes:
+ - minio_data:/data
+ ports:
+ - "${MINIO_PORT}:9000"
+ environment:
+ MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
+ MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}
+ command: server /data
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
+ interval: 30s
+ timeout: 20s
+ retries: 3
+
+ proxy-service:
+ image: envoyproxy/envoy:v1.16-latest
+ volumes:
+ - ./envoy.yaml:/etc/envoy/envoy.yaml
+ ports:
+ - "${MAIN_PORT}:10000"
+ - "9901:9901"
+ depends_on:
+ - minio-service
+ - worker-service
+ - app-service
+ - couchdb-service
+
+ couchdb-service:
+ image: apache/couchdb:3.0
+ environment:
+ - COUCHDB_PASSWORD=${COUCH_DB_PASSWORD}
+ - COUCHDB_USER=${COUCH_DB_USER}
+ ports:
+ - "${COUCH_DB_PORT}:5984"
+ - "4369:4369"
+ - "9100:9100"
+ volumes:
+ - couchdb_data:/couchdb
+
+ couch-init:
+ image: curlimages/curl
+ environment:
+ PUT_CALL: "curl -u ${COUCH_DB_USER}:${COUCH_DB_PASSWORD} -X PUT couchdb-service:5984"
+ depends_on:
+ - couchdb-service
+ command: ["sh","-c","sleep 10 && $${PUT_CALL}/_users && $${PUT_CALL}/_replicator; fg;"]
+
+volumes:
+ couchdb_data:
+ driver: local
+ minio_data:
+ driver: local
diff --git a/hosting/envoy.yaml b/hosting/envoy.yaml
new file mode 100644
index 0000000000..3c816cb1ca
--- /dev/null
+++ b/hosting/envoy.yaml
@@ -0,0 +1,104 @@
+static_resources:
+ listeners:
+ - name: main_listener
+ address:
+ socket_address: { address: 0.0.0.0, port_value: 10000 }
+ filter_chains:
+ - filters:
+ - name: envoy.filters.network.http_connection_manager
+ typed_config:
+ "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ stat_prefix: ingress
+ codec_type: auto
+ route_config:
+ name: local_route
+ virtual_hosts:
+ - name: local_services
+ domains: ["*"]
+ routes:
+ - match: { prefix: "/app/" }
+ route:
+ cluster: app-service
+ prefix_rewrite: "/"
+
+ # special case for when API requests are made, can just forward, not to minio
+ - match: { prefix: "/api/" }
+ route:
+ cluster: app-service
+
+ - match: { prefix: "/worker/" }
+ route:
+ cluster: worker-service
+ prefix_rewrite: "/"
+
+ - match: { prefix: "/db/" }
+ route:
+ cluster: couchdb-service
+ prefix_rewrite: "/"
+
+ # minio is on the default route because this works
+ # best, minio + AWS SDK doesn't handle path proxy
+ - match: { prefix: "/" }
+ route:
+ cluster: minio-service
+
+ http_filters:
+ - name: envoy.filters.http.router
+
+ clusters:
+ - name: app-service
+ connect_timeout: 0.25s
+ type: strict_dns
+ lb_policy: round_robin
+ load_assignment:
+ cluster_name: app-service
+ endpoints:
+ - lb_endpoints:
+ - endpoint:
+ address:
+ socket_address:
+ address: app-service
+ port_value: 4002
+
+ - name: minio-service
+ connect_timeout: 0.25s
+ type: strict_dns
+ lb_policy: round_robin
+ load_assignment:
+ cluster_name: minio-service
+ endpoints:
+ - lb_endpoints:
+ - endpoint:
+ address:
+ socket_address:
+ address: minio-service
+ port_value: 9000
+
+ - name: worker-service
+ connect_timeout: 0.25s
+ type: strict_dns
+ lb_policy: round_robin
+ load_assignment:
+ cluster_name: worker-service
+ endpoints:
+ - lb_endpoints:
+ - endpoint:
+ address:
+ socket_address:
+ address: worker-service
+ port_value: 4003
+
+ - name: couchdb-service
+ connect_timeout: 0.25s
+ type: strict_dns
+ lb_policy: round_robin
+ load_assignment:
+ cluster_name: couchdb-service
+ endpoints:
+ - lb_endpoints:
+ - endpoint:
+ address:
+ socket_address:
+ address: couchdb-service
+ port_value: 5984
+
diff --git a/hosting/hosting.properties b/hosting/hosting.properties
new file mode 100644
index 0000000000..2ef83543a4
--- /dev/null
+++ b/hosting/hosting.properties
@@ -0,0 +1,22 @@
+# Use the main port in the builder for your self hosting URL, e.g. localhost:10000
+MAIN_PORT=10000
+
+# Use this password when configuring your self hosting settings
+# This should be updated
+HOSTING_KEY=budibase
+
+# This section contains all secrets pertaining to the system
+# These should be updated
+JWT_SECRET=testsecret
+MINIO_ACCESS_KEY=budibase
+MINIO_SECRET_KEY=budibase
+COUCH_DB_PASSWORD=budibase
+COUCH_DB_USER=budibase
+WORKER_API_KEY=budibase
+
+# This section contains variables that do not need to be altered under normal circumstances
+APP_PORT=4002
+WORKER_PORT=4003
+MINIO_PORT=4004
+COUCH_DB_PORT=4005
+BUDIBASE_ENVIRONMENT=PRODUCTION
diff --git a/hosting/scripts/linux/install-docker-compose.sh b/hosting/scripts/linux/install-docker-compose.sh
new file mode 100755
index 0000000000..6d466a7655
--- /dev/null
+++ b/hosting/scripts/linux/install-docker-compose.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
+sudo chmod +x /usr/local/bin/docker-compose
+sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
diff --git a/hosting/scripts/linux/install-docker.sh b/hosting/scripts/linux/install-docker.sh
new file mode 100755
index 0000000000..a71809c31f
--- /dev/null
+++ b/hosting/scripts/linux/install-docker.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+echo "**** WARNING - not for production environments ****"
+# warning this is a convience script, for production installations install docker
+# properly for your environment!
+curl -fsSL https://get.docker.com -o get-docker.sh
+sudo sh get-docker.sh
diff --git a/hosting/scripts/linux/release-to-docker-hub.sh b/hosting/scripts/linux/release-to-docker-hub.sh
new file mode 100755
index 0000000000..b1921916e4
--- /dev/null
+++ b/hosting/scripts/linux/release-to-docker-hub.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+pushd ../../build
+docker-compose build --force app-service
+docker-compose build --force worker-service
+docker tag build_app-service budibase/budibase-apps:latest
+docker push budibase/budibase-apps
+docker tag build_worker-service budibase/budibase-worker:latest
+docker push budibase/budibase-worker
+popd
diff --git a/hosting/start.sh b/hosting/start.sh
new file mode 100755
index 0000000000..b32098a3b7
--- /dev/null
+++ b/hosting/start.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+docker-compose --env-file hosting.properties up
diff --git a/hosting/utils/testing.sh b/hosting/utils/testing.sh
new file mode 100755
index 0000000000..94f2fe8896
--- /dev/null
+++ b/hosting/utils/testing.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+function dockerInstalled {
+ echo "Checking docker installation..."
+ if [ ! -x "$(command -v docker)" ]; then
+ echo "Please install docker to continue"
+ exit -1
+ fi
+}
+
+dockerInstalled
+
+source "${BASH_SOURCE%/*}/hosting.properties"
+
+opts="-e MINIO_ACCESS_KEY=$minio_access_key -e MINIO_SECRET_KEY=$minio_secret_key"
+if [ -n "$minio_secret_key_old" ] && [ -n "$minio_access_key_old" ]; then
+ opts="$opts -e MINIO_SECRET_KEY_OLD=$minio_secret_key_old -e MINIO_ACCESS_KEY_OLD=$minio_access_key_old"
+fi
+
+docker run -p $minio_port:$minio_port $opts -v /mnt/data:/data minio/minio server /data
diff --git a/package.json b/package.json
index d58e36517d..fd601dd099 100644
--- a/package.json
+++ b/package.json
@@ -32,7 +32,8 @@
"lint:fix": "eslint --fix packages",
"format": "prettier --write \"{,!(node_modules)/**/}*.{js,jsx,svelte}\"",
"test:e2e": "lerna run cy:test",
- "test:e2e:ci": "lerna run cy:ci"
+ "test:e2e:ci": "lerna run cy:ci",
+ "build:docker": "cd hosting/scripts/linux/ && ./release-to-docker-hub.sh && cd -"
},
"dependencies": {
"@fortawesome/fontawesome": "^1.1.8"
diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js
index a22678fa92..2e9ec1166c 100644
--- a/packages/builder/src/builderStore/index.js
+++ b/packages/builder/src/builderStore/index.js
@@ -1,6 +1,8 @@
import { getFrontendStore } from "./store/frontend"
import { getBackendUiStore } from "./store/backend"
-import { getAutomationStore } from "./store/automation/"
+import { getAutomationStore } from "./store/automation"
+import { getHostingStore } from "./store/hosting"
+
import { getThemeStore } from "./store/theme"
import { derived, writable } from "svelte/store"
import analytics from "analytics"
@@ -11,6 +13,7 @@ export const store = getFrontendStore()
export const backendUiStore = getBackendUiStore()
export const automationStore = getAutomationStore()
export const themeStore = getThemeStore()
+export const hostingStore = getHostingStore()
export const currentAsset = derived(store, $store => {
const type = $store.currentFrontEndType
diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js
index 6369e0e41a..8f131ac0ff 100644
--- a/packages/builder/src/builderStore/store/frontend.js
+++ b/packages/builder/src/builderStore/store/frontend.js
@@ -3,6 +3,7 @@ import { cloneDeep } from "lodash/fp"
import {
allScreens,
backendUiStore,
+ hostingStore,
currentAsset,
mainLayout,
selectedComponent,
@@ -56,6 +57,7 @@ export const getFrontendStore = () => {
hasAppPackage: true,
appInstance: pkg.application.instance,
}))
+ await hostingStore.actions.fetch()
await backendUiStore.actions.database.select(pkg.application.instance)
},
routing: {
diff --git a/packages/builder/src/builderStore/store/hosting.js b/packages/builder/src/builderStore/store/hosting.js
new file mode 100644
index 0000000000..36067773b5
--- /dev/null
+++ b/packages/builder/src/builderStore/store/hosting.js
@@ -0,0 +1,38 @@
+import { writable } from "svelte/store"
+import api from "../api"
+
+const INITIAL_BACKEND_UI_STATE = {
+ hostingInfo: {},
+ appUrl: "",
+}
+
+export const getHostingStore = () => {
+ const store = writable({ ...INITIAL_BACKEND_UI_STATE })
+ store.actions = {
+ fetch: async () => {
+ const responses = await Promise.all([
+ api.get("/api/hosting/"),
+ api.get("/api/hosting/urls"),
+ ])
+ const [info, urls] = await Promise.all(responses.map(resp => resp.json()))
+ store.update(state => {
+ state.hostingInfo = info
+ state.appUrl = urls.app
+ return state
+ })
+ return info
+ },
+ save: async hostingInfo => {
+ const response = await api.post("/api/hosting", hostingInfo)
+ const revision = (await response.json()).rev
+ store.update(state => {
+ state.hostingInfo = {
+ ...hostingInfo,
+ _rev: revision,
+ }
+ return state
+ })
+ },
+ }
+ return store
+}
diff --git a/packages/builder/src/components/automation/Shared/WebhookDisplay.svelte b/packages/builder/src/components/automation/Shared/WebhookDisplay.svelte
index 3299206e78..2edcbec3f1 100644
--- a/packages/builder/src/components/automation/Shared/WebhookDisplay.svelte
+++ b/packages/builder/src/components/automation/Shared/WebhookDisplay.svelte
@@ -1,16 +1,17 @@
-
-
-
-
-
-
-
-
-
-
- {#if $themeStore.darkMode && !showAdvanced}
-
-
-
- {/if}
- {#if $themeStore.darkMode && showAdvanced}
-
-
-
-
-
-
- {/if}
+
+
+
+
+ {#if $themeStore.darkMode && !showAdvanced}
+
+
-
+ {/if}
+ {#if $themeStore.darkMode && showAdvanced}
+
+
+
+
+
+
+ {/if}
diff --git a/packages/builder/src/components/start/BuilderSettingsButton.svelte b/packages/builder/src/components/start/BuilderSettingsButton.svelte
new file mode 100644
index 0000000000..5f0008e375
--- /dev/null
+++ b/packages/builder/src/components/start/BuilderSettingsButton.svelte
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
diff --git a/packages/builder/src/components/start/BuilderSettingsModal.svelte b/packages/builder/src/components/start/BuilderSettingsModal.svelte
new file mode 100644
index 0000000000..57cdef0c29
--- /dev/null
+++ b/packages/builder/src/components/start/BuilderSettingsModal.svelte
@@ -0,0 +1,72 @@
+
+
+
+ Theme
+
+ Hosting
+
+ This section contains settings that relate to the deployment and hosting of
+ apps made in this builder.
+
+
+ {#if selfhosted}
+
+
+
+ {/if}
+
+
+
diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte
index b88397b6c4..1ca192a6c9 100644
--- a/packages/builder/src/components/start/CreateAppModal.svelte
+++ b/packages/builder/src/components/start/CreateAppModal.svelte
@@ -1,6 +1,11 @@
@@ -16,27 +19,30 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
@@ -76,8 +82,10 @@
}
.nav-section {
- margin: 20px 0px;
+ margin: 20px 0 0 0;
display: flex;
flex-direction: column;
+ justify-content: space-between;
+ height: 100%;
}
diff --git a/packages/client/rollup.config.js b/packages/client/rollup.config.js
index 30064bde26..6a4144caf5 100644
--- a/packages/client/rollup.config.js
+++ b/packages/client/rollup.config.js
@@ -10,7 +10,7 @@ export default {
output: [
{
sourcemap: true,
- format: "esm",
+ format: "iife",
file: `./dist/budibase-client.js`,
},
],
diff --git a/packages/client/src/api/api.js b/packages/client/src/api/api.js
index ea88787865..1e73baf602 100644
--- a/packages/client/src/api/api.js
+++ b/packages/client/src/api/api.js
@@ -1,17 +1,8 @@
-import { getAppId } from "../utils/getAppId"
-
/**
* API cache for cached request responses.
*/
let cache = {}
-/**
- * Makes a fully formatted URL based on the SDK configuration.
- */
-const makeFullURL = path => {
- return `/${path}`.replace("//", "/")
-}
-
/**
* Handler for API errors.
*/
@@ -29,7 +20,7 @@ const makeApiCall = async ({ method, url, body, json = true }) => {
let headers = {
Accept: "application/json",
"Content-Type": "application/json",
- "x-budibase-app-id": getAppId(),
+ "x-budibase-app-id": window["##BUDIBASE_APP_ID##"],
}
if (!window["##BUDIBASE_IN_BUILDER##"]) {
headers["x-budibase-type"] = "client"
@@ -82,8 +73,8 @@ const makeCachedApiCall = async params => {
*/
const requestApiCall = method => async params => {
const { url, cache = false } = params
- const fullURL = makeFullURL(url)
- const enrichedParams = { ...params, method, url: fullURL }
+ const fixedUrl = `/${url}`.replace("//", "/")
+ const enrichedParams = { ...params, method, fixedUrl }
return await (cache ? makeCachedApiCall : makeApiCall)(enrichedParams)
}
diff --git a/packages/client/src/index.js b/packages/client/src/index.js
index 19ac28d6fb..66bbce2770 100644
--- a/packages/client/src/index.js
+++ b/packages/client/src/index.js
@@ -7,6 +7,7 @@ const loadBudibase = () => {
// Update builder store with any builder flags
builderStore.set({
inBuilder: !!window["##BUDIBASE_IN_BUILDER##"],
+ appId: window["##BUDIBASE_APP_ID##"],
layout: window["##BUDIBASE_PREVIEW_LAYOUT##"],
screen: window["##BUDIBASE_PREVIEW_SCREEN##"],
selectedComponentId: window["##BUDIBASE_SELECTED_COMPONENT_ID##"],
diff --git a/packages/client/src/sdk.js b/packages/client/src/sdk.js
index fefd659986..ba7a3d3a9e 100644
--- a/packages/client/src/sdk.js
+++ b/packages/client/src/sdk.js
@@ -2,7 +2,6 @@ import * as API from "./api"
import { authStore, routeStore, screenStore, bindingStore } from "./store"
import { styleable } from "./utils/styleable"
import { linkable } from "./utils/linkable"
-import { getAppId } from "./utils/getAppId"
import DataProvider from "./components/DataProvider.svelte"
export default {
@@ -12,7 +11,6 @@ export default {
screenStore,
styleable,
linkable,
- getAppId,
DataProvider,
setBindableValue: bindingStore.actions.setBindableValue,
}
diff --git a/packages/client/src/store/auth.js b/packages/client/src/store/auth.js
index a08510ac31..d2ce04b604 100644
--- a/packages/client/src/store/auth.js
+++ b/packages/client/src/store/auth.js
@@ -1,8 +1,8 @@
import * as API from "../api"
-import { getAppId } from "../utils/getAppId"
-import { writable } from "svelte/store"
+import { writable, get } from "svelte/store"
import { initialise } from "./initialise"
import { routeStore } from "./routes"
+import { builderStore } from "./builder"
const createAuthStore = () => {
const store = writable("")
@@ -25,7 +25,7 @@ const createAuthStore = () => {
}
const logOut = async () => {
store.set("")
- const appId = getAppId()
+ const appId = get(builderStore).appId
if (appId) {
for (let environment of ["local", "cloud"]) {
window.document.cookie = `budibase:${appId}:${environment}=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;`
diff --git a/packages/client/src/store/builder.js b/packages/client/src/store/builder.js
index 7cbe49bde8..c7599e5254 100644
--- a/packages/client/src/store/builder.js
+++ b/packages/client/src/store/builder.js
@@ -3,6 +3,7 @@ import { writable } from "svelte/store"
const createBuilderStore = () => {
const initialState = {
inBuilder: false,
+ appId: null,
layout: null,
screen: null,
selectedComponentId: null,
diff --git a/packages/client/src/store/screens.js b/packages/client/src/store/screens.js
index 54c7dda109..49927db9d7 100644
--- a/packages/client/src/store/screens.js
+++ b/packages/client/src/store/screens.js
@@ -1,8 +1,7 @@
-import { writable, derived } from "svelte/store"
+import { writable, derived, get } from "svelte/store"
import { routeStore } from "./routes"
import { builderStore } from "./builder"
import * as API from "../api"
-import { getAppId } from "../utils/getAppId"
const createScreenStore = () => {
const config = writable({
@@ -40,7 +39,7 @@ const createScreenStore = () => {
)
const fetchScreens = async () => {
- const appDefinition = await API.fetchAppDefinition(getAppId())
+ const appDefinition = await API.fetchAppDefinition(get(builderStore).appId)
config.set({
screens: appDefinition.screens,
layouts: appDefinition.layouts,
diff --git a/packages/client/src/utils/getAppId.js b/packages/client/src/utils/getAppId.js
deleted file mode 100644
index 355dd55d6b..0000000000
--- a/packages/client/src/utils/getAppId.js
+++ /dev/null
@@ -1,47 +0,0 @@
-const COOKIE_SEPARATOR = ";"
-const APP_PREFIX = "app_"
-const KEY_VALUE_SPLIT = "="
-
-function confirmAppId(possibleAppId) {
- return possibleAppId && possibleAppId.startsWith(APP_PREFIX)
- ? possibleAppId
- : undefined
-}
-
-function tryGetFromCookie({ cookies }) {
- if (!cookies) {
- return undefined
- }
- const cookie = cookies
- .split(COOKIE_SEPARATOR)
- .find(cookie => cookie.trim().startsWith("budibase:currentapp"))
- let appId
- if (cookie && cookie.split(KEY_VALUE_SPLIT).length === 2) {
- appId = cookie.split("=")[1]
- }
- return confirmAppId(appId)
-}
-
-function tryGetFromPath() {
- const appId = location.pathname.split("/")[1]
- return confirmAppId(appId)
-}
-
-function tryGetFromSubdomain() {
- const parts = window.location.host.split(".")
- const appId = parts[1] ? parts[0] : undefined
- return confirmAppId(appId)
-}
-
-export const getAppId = (cookies = window.document.cookie) => {
- const functions = [tryGetFromSubdomain, tryGetFromPath, tryGetFromCookie]
- // try getting the app Id in order
- let appId
- for (let func of functions) {
- appId = func({ cookies })
- if (appId) {
- break
- }
- }
- return appId
-}
diff --git a/packages/server/Dockerfile b/packages/server/Dockerfile
index 96ae3b7a30..9a6b058a17 100644
--- a/packages/server/Dockerfile
+++ b/packages/server/Dockerfile
@@ -4,12 +4,16 @@ WORKDIR /app
ENV CLOUD=1
ENV COUCH_DB_URL=https://couchdb.budi.live:5984
-env BUDIBASE_ENVIRONMENT=PRODUCTION
+ENV BUDIBASE_ENVIRONMENT=PRODUCTION
# copy files and install dependencies
COPY . ./
-RUN yarn
+RUN yarn
EXPOSE 4001
+# have to add node environment production after install
+# due to this causing yarn to stop installing dev dependencies
+# which are actually needed to get this environment up and running
+ENV NODE_ENV=production
CMD ["yarn", "run:docker"]
diff --git a/packages/server/scripts/createApiKeyAndAppId.js b/packages/server/scripts/createApiKeyAndAppId.js
deleted file mode 100644
index 76b0893d30..0000000000
--- a/packages/server/scripts/createApiKeyAndAppId.js
+++ /dev/null
@@ -1,56 +0,0 @@
-// THIS will create API Keys and App Ids input in a local Dynamo instance if it is running
-const dynamoClient = require("../src/db/dynamoClient")
-const env = require("../src/environment")
-
-if (process.argv[2] == null || process.argv[3] == null) {
- console.error(
- "Inputs incorrect format, was expecting: node createApiKeyAndAppId.js "
- )
- process.exit(-1)
-}
-
-const FAKE_STRING = "fakestring"
-
-// set fake credentials for local dynamo to actually work
-env._set("AWS_ACCESS_KEY_ID", "KEY_ID")
-env._set("AWS_SECRET_ACCESS_KEY", "SECRET_KEY")
-dynamoClient.init("http://localhost:8333")
-
-async function run() {
- await dynamoClient.apiKeyTable.put({
- item: {
- pk: process.argv[2],
- accountId: FAKE_STRING,
- trackingId: FAKE_STRING,
- quotaReset: Date.now() + 2592000000,
- usageQuota: {
- automationRuns: 0,
- rows: 0,
- storage: 0,
- users: 0,
- views: 0,
- },
- usageLimits: {
- automationRuns: 10,
- rows: 10,
- storage: 1000,
- users: 10,
- views: 10,
- },
- },
- })
- await dynamoClient.apiKeyTable.put({
- item: {
- pk: process.argv[3],
- apiKey: process.argv[2],
- },
- })
-}
-
-run()
- .then(() => {
- console.log("Rows should have been created.")
- })
- .catch(err => {
- console.error("Cannot create rows - " + err)
- })
diff --git a/packages/server/src/api/controllers/apikeys.js b/packages/server/src/api/controllers/apikeys.js
index 96754f17cc..1cd69c54df 100644
--- a/packages/server/src/api/controllers/apikeys.js
+++ b/packages/server/src/api/controllers/apikeys.js
@@ -3,13 +3,20 @@ const { join } = require("../../utilities/centralPath")
const readline = require("readline")
const { budibaseAppsDir } = require("../../utilities/budibaseDir")
const env = require("../../environment")
+const selfhost = require("../../selfhost")
const ENV_FILE_PATH = "/.env"
exports.fetch = async function(ctx) {
ctx.status = 200
- ctx.body = {
- budibase: env.BUDIBASE_API_KEY,
- userId: env.USERID_API_KEY,
+ if (env.SELF_HOSTED) {
+ ctx.body = {
+ selfhost: await selfhost.getSelfHostAPIKey(),
+ }
+ } else {
+ ctx.body = {
+ budibase: env.BUDIBASE_API_KEY,
+ userId: env.USERID_API_KEY,
+ }
}
}
diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js
index 293c72d7c3..1d259ac425 100644
--- a/packages/server/src/api/controllers/application.js
+++ b/packages/server/src/api/controllers/application.js
@@ -150,6 +150,9 @@ exports.create = async function(ctx) {
name: ctx.request.body.name,
template: ctx.request.body.template,
instance: instance,
+ deployment: {
+ type: "cloud",
+ },
}
const instanceDb = new CouchDB(appId)
await instanceDb.put(newApplication)
diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js
index e8bc000e03..f9c3ef945f 100644
--- a/packages/server/src/api/controllers/auth.js
+++ b/packages/server/src/api/controllers/auth.js
@@ -35,8 +35,8 @@ exports.authenticate = async ctx => {
roleId: dbUser.roleId,
version: app.version,
}
- // if in cloud add the user api key
- if (env.CLOUD) {
+ // if in cloud add the user api key, unless self hosted
+ if (env.CLOUD && !env.SELF_HOSTED) {
const { apiKey } = await getAPIKey(ctx.user.appId)
payload.apiKey = apiKey
}
diff --git a/packages/server/src/api/controllers/deploy/Deployment.js b/packages/server/src/api/controllers/deploy/Deployment.js
new file mode 100644
index 0000000000..4c0f0f2df9
--- /dev/null
+++ b/packages/server/src/api/controllers/deploy/Deployment.js
@@ -0,0 +1,88 @@
+const { getAppQuota } = require("./quota")
+const env = require("../../../environment")
+const newid = require("../../../db/newid")
+
+/**
+ * This is used to pass around information about the deployment that is occurring
+ */
+class Deployment {
+ constructor(appId, id = null) {
+ this.appId = appId
+ this._id = id || newid()
+ }
+
+ // purely so that we can do quota stuff outside the main deployment context
+ async init() {
+ if (!env.SELF_HOSTED) {
+ this.setQuota(await getAppQuota(this.appId))
+ }
+ }
+
+ setQuota(quota) {
+ if (!quota) {
+ return
+ }
+ this.quota = quota
+ }
+
+ getQuota() {
+ return this.quota
+ }
+
+ getAppId() {
+ return this.appId
+ }
+
+ setVerification(verification) {
+ if (!verification) {
+ return
+ }
+ this.verification = verification
+ if (this.verification.quota) {
+ this.quota = this.verification.quota
+ }
+ }
+
+ getVerification() {
+ return this.verification
+ }
+
+ setStatus(status, err = null) {
+ this.status = status
+ if (err) {
+ this.err = err
+ }
+ }
+
+ fromJSON(json) {
+ if (json.verification) {
+ this.setVerification(json.verification)
+ }
+ if (json.quota) {
+ this.setQuota(json.quota)
+ }
+ if (json.status) {
+ this.setStatus(json.status, json.err)
+ }
+ }
+
+ getJSON() {
+ const obj = {
+ _id: this._id,
+ appId: this.appId,
+ status: this.status,
+ }
+ if (this.err) {
+ obj.err = this.err
+ }
+ if (this.verification && this.verification.cfDistribution) {
+ obj.cfDistribution = this.verification.cfDistribution
+ }
+ if (this.quota) {
+ obj.quota = this.quota
+ }
+ return obj
+ }
+}
+
+module.exports = Deployment
diff --git a/packages/server/src/api/controllers/deploy/aws.js b/packages/server/src/api/controllers/deploy/aws.js
deleted file mode 100644
index 63c5921bc9..0000000000
--- a/packages/server/src/api/controllers/deploy/aws.js
+++ /dev/null
@@ -1,189 +0,0 @@
-const fs = require("fs")
-const { join } = require("../../../utilities/centralPath")
-const AWS = require("aws-sdk")
-const fetch = require("node-fetch")
-const sanitize = require("sanitize-s3-objectkey")
-const { budibaseAppsDir } = require("../../../utilities/budibaseDir")
-const PouchDB = require("../../../db")
-const env = require("../../../environment")
-
-/**
- * Finalises the deployment, updating the quota for the user API key
- * The verification process returns the levels to update to.
- * Calls the "deployment-success" lambda.
- * @param {object} quota The usage quota levels returned from the verifyDeploy
- * @returns {Promise