forked from tsai/budibase
246 changed files with 7143 additions and 6324 deletions
@ -0,0 +1,9 @@ |
|||
packages/server/node_modules |
|||
packages/builder |
|||
packages/frontend-core |
|||
packages/backend-core |
|||
packages/worker/node_modules |
|||
packages/cli |
|||
packages/client |
|||
packages/bbui |
|||
packages/string-templates |
|||
@ -0,0 +1,94 @@ |
|||
{ |
|||
"version": "2", |
|||
"templates": [ |
|||
{ |
|||
"type": 3, |
|||
"title": "Budibase", |
|||
"categories": ["Tools"], |
|||
"description": "Build modern business apps in minutes", |
|||
"logo": "https://budibase.com/favicon.ico", |
|||
"platform": "linux", |
|||
"repository": { |
|||
"url": "https://github.com/Budibase/budibase", |
|||
"stackfile": "hosting/docker-compose.yaml" |
|||
}, |
|||
"env": [ |
|||
{ |
|||
"name": "MAIN_PORT", |
|||
"label": "Main port", |
|||
"default": "10000" |
|||
}, |
|||
{ |
|||
"name": "JWT_SECRET", |
|||
"label": "JWT secret", |
|||
"default": "change-me" |
|||
}, |
|||
{ |
|||
"name": "MINIO_ACCESS_KEY", |
|||
"label": "MinIO access key", |
|||
"default": "change-me" |
|||
}, |
|||
{ |
|||
"name": "MINIO_SECRET_KEY", |
|||
"label": "MinIO secret key", |
|||
"default": "change-me" |
|||
}, |
|||
{ |
|||
"name": "COUCH_DB_USER", |
|||
"default": "budibase", |
|||
"preset": true |
|||
}, |
|||
{ |
|||
"name": "COUCH_DB_PASSWORD", |
|||
"label": "Couch DB password", |
|||
"default": "change-me" |
|||
}, |
|||
{ |
|||
"name": "REDIS_PASSWORD", |
|||
"label": "Redis password", |
|||
"default": "change-me" |
|||
}, |
|||
{ |
|||
"name": "INTERNAL_API_KEY", |
|||
"label": "Internal API key", |
|||
"default": "change-me" |
|||
}, |
|||
{ |
|||
"name": "APP_PORT", |
|||
"default": "4002", |
|||
"preset": true |
|||
}, |
|||
{ |
|||
"name": "WORKER_PORT", |
|||
"default": "4003", |
|||
"preset": true |
|||
}, |
|||
{ |
|||
"name": "MINIO_PORT", |
|||
"default": "4004", |
|||
"preset": true |
|||
}, |
|||
{ |
|||
"name": "COUCH_DB_PORT", |
|||
"default": "4005", |
|||
"preset": true |
|||
}, |
|||
{ |
|||
"name": "REDIS_PORT", |
|||
"default": "6379", |
|||
"preset": true |
|||
}, |
|||
{ |
|||
"name": "WATCHTOWER_PORT", |
|||
"default": "6161", |
|||
"preset": true |
|||
}, |
|||
{ |
|||
"name": "BUDIBASE_ENVIRONMENT", |
|||
"default": "PRODUCTION", |
|||
"preset": true |
|||
} |
|||
] |
|||
} |
|||
] |
|||
} |
|||
@ -0,0 +1,97 @@ |
|||
FROM couchdb |
|||
|
|||
ENV COUCHDB_PASSWORD=budibase |
|||
ENV COUCHDB_USER=budibase |
|||
ENV COUCH_DB_URL=http://budibase:budibase@localhost:5984 |
|||
ENV BUDIBASE_ENVIRONMENT=PRODUCTION |
|||
ENV MINIO_URL=http://localhost:9000 |
|||
ENV REDIS_URL=localhost:6379 |
|||
ENV WORKER_URL=http://localhost:4002 |
|||
ENV INTERNAL_API_KEY=budibase |
|||
ENV JWT_SECRET=testsecret |
|||
ENV MINIO_ACCESS_KEY=budibase |
|||
ENV MINIO_SECRET_KEY=budibase |
|||
ENV SELF_HOSTED=1 |
|||
ENV CLUSTER_PORT=10000 |
|||
ENV REDIS_PASSWORD=budibase |
|||
ENV ARCHITECTURE=amd |
|||
ENV APP_PORT=4001 |
|||
ENV WORKER_PORT=4002 |
|||
|
|||
RUN apt-get update |
|||
RUN apt-get install software-properties-common wget nginx -y |
|||
RUN apt-add-repository 'deb http://security.debian.org/debian-security stretch/updates main' |
|||
RUN apt-get update |
|||
|
|||
# setup nginx |
|||
ADD hosting/single/nginx.conf /etc/nginx |
|||
RUN mkdir /etc/nginx/logs |
|||
RUN useradd www |
|||
RUN touch /etc/nginx/logs/error.log |
|||
RUN touch /etc/nginx/logs/nginx.pid |
|||
|
|||
# install java |
|||
RUN apt-get install openjdk-8-jdk -y |
|||
|
|||
# setup nodejs |
|||
WORKDIR /nodejs |
|||
RUN curl -sL https://deb.nodesource.com/setup_16.x -o /tmp/nodesource_setup.sh |
|||
RUN bash /tmp/nodesource_setup.sh |
|||
RUN apt-get install nodejs |
|||
RUN npm install --global yarn |
|||
RUN npm install --global pm2 |
|||
|
|||
# setup redis |
|||
RUN apt install redis-server -y |
|||
|
|||
# setup server |
|||
WORKDIR /app |
|||
ADD packages/server . |
|||
RUN ls -al |
|||
RUN yarn |
|||
RUN yarn build |
|||
# Install client for oracle datasource |
|||
RUN apt-get install unzip libaio1 |
|||
RUN /bin/bash -e scripts/integrations/oracle/instantclient/linux/x86-64/install.sh |
|||
|
|||
# setup worker |
|||
WORKDIR /worker |
|||
ADD packages/worker . |
|||
RUN yarn |
|||
RUN yarn build |
|||
|
|||
# setup clouseau |
|||
WORKDIR / |
|||
RUN wget https://github.com/cloudant-labs/clouseau/releases/download/2.21.0/clouseau-2.21.0-dist.zip |
|||
RUN unzip clouseau-2.21.0-dist.zip |
|||
RUN mv clouseau-2.21.0 /opt/clouseau |
|||
RUN rm clouseau-2.21.0-dist.zip |
|||
|
|||
WORKDIR /opt/clouseau |
|||
RUN mkdir ./bin |
|||
ADD hosting/single/clouseau ./bin/ |
|||
ADD hosting/single/log4j.properties . |
|||
ADD hosting/single/clouseau.ini . |
|||
RUN chmod +x ./bin/clouseau |
|||
|
|||
# setup CouchDB |
|||
WORKDIR /opt/couchdb |
|||
ADD hosting/single/vm.args ./etc/ |
|||
|
|||
# setup minio |
|||
WORKDIR /minio |
|||
RUN wget https://dl.min.io/server/minio/release/linux-${ARCHITECTURE}64/minio |
|||
RUN chmod +x minio |
|||
|
|||
# setup runner file |
|||
WORKDIR / |
|||
ADD hosting/single/runner.sh . |
|||
RUN chmod +x ./runner.sh |
|||
|
|||
EXPOSE 10000 |
|||
VOLUME /opt/couchdb/data |
|||
VOLUME /minio |
|||
|
|||
# must set this just before running |
|||
ENV NODE_ENV=production |
|||
CMD ["./runner.sh"] |
|||
@ -0,0 +1,12 @@ |
|||
#!/bin/sh |
|||
/usr/bin/java -server \ |
|||
-Xmx2G \ |
|||
-Dsun.net.inetaddr.ttl=30 \ |
|||
-Dsun.net.inetaddr.negative.ttl=30 \ |
|||
-Dlog4j.configuration=file:/opt/clouseau/log4j.properties \ |
|||
-XX:OnOutOfMemoryError="kill -9 %p" \ |
|||
-XX:+UseConcMarkSweepGC \ |
|||
-XX:+CMSParallelRemarkEnabled \ |
|||
-classpath '/opt/clouseau/*' \ |
|||
com.cloudant.clouseau.Main \ |
|||
/opt/clouseau/clouseau.ini |
|||
@ -0,0 +1,13 @@ |
|||
[clouseau] |
|||
|
|||
; the name of the Erlang node created by the service, leave this unchanged |
|||
name=clouseau@127.0.0.1 |
|||
|
|||
; set this to the same distributed Erlang cookie used by the CouchDB nodes |
|||
cookie=monster |
|||
|
|||
; the path where you would like to store the search index files |
|||
dir=/opt/couchdb/data/search |
|||
|
|||
; the number of search indexes that can be open simultaneously |
|||
max_indexes_open=500 |
|||
@ -0,0 +1,4 @@ |
|||
log4j.rootLogger=debug, CONSOLE |
|||
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender |
|||
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout |
|||
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %c [%p] %m%n |
|||
@ -0,0 +1,116 @@ |
|||
user www www; |
|||
error_log /etc/nginx/logs/error.log; |
|||
pid /etc/nginx/logs/nginx.pid; |
|||
worker_processes auto; |
|||
worker_rlimit_nofile 8192; |
|||
|
|||
events { |
|||
worker_connections 1024; |
|||
} |
|||
|
|||
http { |
|||
limit_req_zone $binary_remote_addr zone=ratelimit:10m rate=20r/s; |
|||
proxy_set_header Host $host; |
|||
charset utf-8; |
|||
sendfile on; |
|||
tcp_nopush on; |
|||
tcp_nodelay on; |
|||
server_tokens off; |
|||
types_hash_max_size 2048; |
|||
|
|||
# buffering |
|||
client_header_buffer_size 1k; |
|||
client_max_body_size 20M; |
|||
ignore_invalid_headers off; |
|||
proxy_buffering off; |
|||
|
|||
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' |
|||
'$status $body_bytes_sent "$http_referer" ' |
|||
'"$http_user_agent" "$http_x_forwarded_for"'; |
|||
|
|||
map $http_upgrade $connection_upgrade { |
|||
default "upgrade"; |
|||
} |
|||
|
|||
server { |
|||
listen 10000 default_server; |
|||
listen [::]:10000 default_server; |
|||
server_name _; |
|||
client_max_body_size 1000m; |
|||
ignore_invalid_headers off; |
|||
proxy_buffering off; |
|||
# port_in_redirect off; |
|||
|
|||
location /app { |
|||
proxy_pass http://127.0.0.1:4001; |
|||
} |
|||
|
|||
location = / { |
|||
proxy_pass http://127.0.0.1:4001; |
|||
} |
|||
|
|||
location ~ ^/(builder|app_) { |
|||
proxy_http_version 1.1; |
|||
proxy_set_header Connection $connection_upgrade; |
|||
proxy_set_header Upgrade $http_upgrade; |
|||
proxy_set_header X-Real-IP $remote_addr; |
|||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
|||
proxy_pass http://127.0.0.1:4001; |
|||
} |
|||
|
|||
location ~ ^/api/(system|admin|global)/ { |
|||
proxy_pass http://127.0.0.1:4002; |
|||
} |
|||
|
|||
location /worker/ { |
|||
proxy_pass http://127.0.0.1:4002; |
|||
rewrite ^/worker/(.*)$ /$1 break; |
|||
} |
|||
|
|||
location /api/ { |
|||
# calls to the API are rate limited with bursting |
|||
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_http_version 1.1; |
|||
proxy_set_header Connection $connection_upgrade; |
|||
proxy_set_header Upgrade $http_upgrade; |
|||
proxy_set_header X-Real-IP $remote_addr; |
|||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
|||
|
|||
proxy_pass http://127.0.0.1:4001; |
|||
} |
|||
|
|||
location /db/ { |
|||
proxy_pass http://127.0.0.1:5984; |
|||
rewrite ^/db/(.*)$ /$1 break; |
|||
} |
|||
|
|||
location / { |
|||
proxy_set_header X-Real-IP $remote_addr; |
|||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
|||
proxy_set_header X-Forwarded-Proto $scheme; |
|||
|
|||
proxy_connect_timeout 300; |
|||
proxy_http_version 1.1; |
|||
proxy_set_header Connection ""; |
|||
chunked_transfer_encoding off; |
|||
proxy_pass http://127.0.0.1:9000; |
|||
} |
|||
|
|||
client_header_timeout 60; |
|||
client_body_timeout 60; |
|||
keepalive_timeout 60; |
|||
|
|||
# gzip |
|||
gzip on; |
|||
gzip_vary on; |
|||
gzip_proxied any; |
|||
gzip_comp_level 6; |
|||
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
redis-server --requirepass $REDIS_PASSWORD & |
|||
/opt/clouseau/bin/clouseau & |
|||
/minio/minio server /minio & |
|||
/docker-entrypoint.sh /opt/couchdb/bin/couchdb & |
|||
/etc/init.d/nginx restart |
|||
pushd app |
|||
pm2 start --name app "yarn run:docker" |
|||
popd |
|||
pushd worker |
|||
pm2 start --name worker "yarn run:docker" |
|||
popd |
|||
sleep 10 |
|||
URL=http://${COUCHDB_USER}:${COUCHDB_PASSWORD}@localhost:5984 |
|||
curl -X PUT ${URL}/_users |
|||
curl -X PUT ${URL}/_replicator |
|||
sleep infinity |
|||
@ -0,0 +1,32 @@ |
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may not |
|||
# use this file except in compliance with the License. You may obtain a copy of |
|||
# the License at |
|||
# |
|||
# http://www.apache.org/licenses/LICENSE-2.0 |
|||
# |
|||
# Unless required by applicable law or agreed to in writing, software |
|||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|||
# License for the specific language governing permissions and limitations under |
|||
# the License. |
|||
|
|||
# erlang cookie for clouseau security |
|||
-name couchdb@127.0.0.1 |
|||
-setcookie monster |
|||
|
|||
# Ensure that the Erlang VM listens on a known port |
|||
-kernel inet_dist_listen_min 9100 |
|||
-kernel inet_dist_listen_max 9100 |
|||
|
|||
# Tell kernel and SASL not to log anything |
|||
-kernel error_logger silent |
|||
-sasl sasl_error_logger false |
|||
|
|||
# Use kernel poll functionality if supported by emulator |
|||
+K true |
|||
|
|||
# Start a pool of asynchronous IO threads |
|||
+A 16 |
|||
|
|||
# Comment this line out to enable the interactive Erlang shell on startup |
|||
+Bd -noinput |
|||
@ -0,0 +1,11 @@ |
|||
class BudibaseError extends Error { |
|||
constructor(message, type, code) { |
|||
super(message) |
|||
this.type = type |
|||
this.code = code |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
BudibaseError, |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
const licensing = require("./licensing") |
|||
|
|||
const codes = { |
|||
...licensing.codes, |
|||
} |
|||
|
|||
const types = { |
|||
...licensing.types, |
|||
} |
|||
|
|||
const context = { |
|||
...licensing.context, |
|||
} |
|||
|
|||
const getPublicError = err => { |
|||
let error |
|||
if (err.code || err.type) { |
|||
// add generic error information
|
|||
error = { |
|||
code: err.code, |
|||
type: err.type, |
|||
} |
|||
|
|||
if (err.code && context[err.code]) { |
|||
error = { |
|||
...error, |
|||
// get any additional context from this error
|
|||
...context[err.code](err), |
|||
} |
|||
} |
|||
} |
|||
|
|||
return error |
|||
} |
|||
|
|||
module.exports = { |
|||
codes, |
|||
types, |
|||
UsageLimitError: licensing.UsageLimitError, |
|||
getPublicError, |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
const { BudibaseError } = require("./base") |
|||
|
|||
const types = { |
|||
LICENSE_ERROR: "license_error", |
|||
} |
|||
|
|||
const codes = { |
|||
USAGE_LIMIT_EXCEEDED: "usage_limit_exceeded", |
|||
} |
|||
|
|||
const context = { |
|||
[codes.USAGE_LIMIT_EXCEEDED]: err => { |
|||
return { |
|||
limitName: err.limitName, |
|||
} |
|||
}, |
|||
} |
|||
|
|||
class UsageLimitError extends BudibaseError { |
|||
constructor(message, limitName) { |
|||
super(message, types.LICENSE_ERROR, codes.USAGE_LIMIT_EXCEEDED) |
|||
this.limitName = limitName |
|||
this.status = 400 |
|||
} |
|||
} |
|||
|
|||
module.exports = { |
|||
types, |
|||
codes, |
|||
context, |
|||
UsageLimitError, |
|||
} |
|||
@ -0,0 +1,52 @@ |
|||
const env = require("../environment") |
|||
const tenancy = require("../tenancy") |
|||
|
|||
/** |
|||
* Read the TENANT_FEATURE_FLAGS env var and return an array of features flags for each tenant. |
|||
* The env var is formatted as: |
|||
* tenant1:feature1:feature2,tenant2:feature1 |
|||
*/ |
|||
const getFeatureFlags = () => { |
|||
if (!env.TENANT_FEATURE_FLAGS) { |
|||
return |
|||
} |
|||
|
|||
const tenantFeatureFlags = {} |
|||
|
|||
env.TENANT_FEATURE_FLAGS.split(",").forEach(tenantToFeatures => { |
|||
const [tenantId, ...features] = tenantToFeatures.split(":") |
|||
|
|||
features.forEach(feature => { |
|||
if (!tenantFeatureFlags[tenantId]) { |
|||
tenantFeatureFlags[tenantId] = [] |
|||
} |
|||
tenantFeatureFlags[tenantId].push(feature) |
|||
}) |
|||
}) |
|||
|
|||
return tenantFeatureFlags |
|||
} |
|||
|
|||
const TENANT_FEATURE_FLAGS = getFeatureFlags() |
|||
|
|||
exports.isEnabled = featureFlag => { |
|||
const tenantId = tenancy.getTenantId() |
|||
|
|||
return ( |
|||
TENANT_FEATURE_FLAGS && |
|||
TENANT_FEATURE_FLAGS[tenantId] && |
|||
TENANT_FEATURE_FLAGS[tenantId].includes(featureFlag) |
|||
) |
|||
} |
|||
|
|||
exports.getTenantFeatureFlags = tenantId => { |
|||
if (TENANT_FEATURE_FLAGS && TENANT_FEATURE_FLAGS[tenantId]) { |
|||
return TENANT_FEATURE_FLAGS[tenantId] |
|||
} |
|||
|
|||
return [] |
|||
} |
|||
|
|||
exports.FeatureFlag = { |
|||
LICENSING: "LICENSING", |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
<script> |
|||
import { Select, Label, Combobox } from "@budibase/bbui" |
|||
import { onMount } from "svelte" |
|||
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" |
|||
import { currentAsset, store } from "builderStore" |
|||
import { |
|||
getActionProviderComponents, |
|||
buildFormSchema, |
|||
} from "builderStore/dataBinding" |
|||
import { findComponent } from "builderStore/componentUtils" |
|||
|
|||
export let parameters |
|||
export let bindings = [] |
|||
|
|||
const typeOptions = [ |
|||
{ |
|||
label: "Set value", |
|||
value: "set", |
|||
}, |
|||
{ |
|||
label: "Reset to default value", |
|||
value: "reset", |
|||
}, |
|||
] |
|||
|
|||
$: formComponent = findComponent($currentAsset.props, parameters.componentId) |
|||
$: formSchema = buildFormSchema(formComponent) |
|||
$: fieldOptions = Object.keys(formSchema || {}) |
|||
$: actionProviders = getActionProviderComponents( |
|||
$currentAsset, |
|||
$store.selectedComponentId, |
|||
"ValidateForm" |
|||
) |
|||
|
|||
onMount(() => { |
|||
if (!parameters.type) { |
|||
parameters.type = "set" |
|||
} |
|||
}) |
|||
</script> |
|||
|
|||
<div class="root"> |
|||
<Label small>Form</Label> |
|||
<Select |
|||
bind:value={parameters.componentId} |
|||
options={actionProviders} |
|||
getOptionLabel={x => x._instanceName} |
|||
getOptionValue={x => x._id} |
|||
/> |
|||
<Label small>Type</Label> |
|||
<Select |
|||
placeholder={null} |
|||
bind:value={parameters.type} |
|||
options={typeOptions} |
|||
/> |
|||
<Label small>Field</Label> |
|||
<Combobox bind:value={parameters.field} options={fieldOptions} /> |
|||
{#if parameters.type === "set"} |
|||
<Label small>Value</Label> |
|||
<DrawerBindableInput |
|||
{bindings} |
|||
value={parameters.value} |
|||
on:change={e => (parameters.value = e.detail)} |
|||
/> |
|||
{/if} |
|||
</div> |
|||
|
|||
<style> |
|||
.root { |
|||
display: grid; |
|||
column-gap: var(--spacing-l); |
|||
row-gap: var(--spacing-s); |
|||
grid-template-columns: 60px 1fr; |
|||
align-items: center; |
|||
max-width: 400px; |
|||
margin: 0 auto; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,14 @@ |
|||
import { auth } from "../stores/portal" |
|||
import { get } from "svelte/store" |
|||
|
|||
export const FEATURE_FLAGS = { |
|||
LICENSING: "LICENSING", |
|||
} |
|||
|
|||
export const isEnabled = featureFlag => { |
|||
const user = get(auth).user |
|||
if (user?.featureFlags?.includes(featureFlag)) { |
|||
return true |
|||
} |
|||
return false |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue