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