Merge branch 'master' of github.com:Budibase/budibase into cheeks-lab-day-binding-eval
This commit is contained in:
commit
87e8b89075
|
@ -1,4 +1,101 @@
|
||||||
FROM couchdb:3.2.1
|
# Modified from https://github.com/apache/couchdb-docker/blob/main/3.3.3/Dockerfile
|
||||||
|
#
|
||||||
|
# Everything in this `base` image is adapted from the official `couchdb` image's
|
||||||
|
# Dockerfile. Only modifications related to upgrading from Debian bullseye to
|
||||||
|
# bookworm have been included. The `runner` image contains Budibase's
|
||||||
|
# customisations to the image, e.g. adding Clouseau.
|
||||||
|
FROM node:20-slim AS base
|
||||||
|
|
||||||
|
# Add CouchDB user account to make sure the IDs are assigned consistently
|
||||||
|
RUN groupadd -g 5984 -r couchdb && useradd -u 5984 -d /opt/couchdb -g couchdb couchdb
|
||||||
|
|
||||||
|
# be sure GPG and apt-transport-https are available and functional
|
||||||
|
RUN set -ex; \
|
||||||
|
apt-get update; \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
apt-transport-https \
|
||||||
|
ca-certificates \
|
||||||
|
dirmngr \
|
||||||
|
gnupg \
|
||||||
|
; \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# grab tini for signal handling and zombie reaping
|
||||||
|
# see https://github.com/apache/couchdb-docker/pull/28#discussion_r141112407
|
||||||
|
RUN set -eux; \
|
||||||
|
apt-get update; \
|
||||||
|
apt-get install -y --no-install-recommends tini; \
|
||||||
|
rm -rf /var/lib/apt/lists/*; \
|
||||||
|
tini --version
|
||||||
|
|
||||||
|
# http://docs.couchdb.org/en/latest/install/unix.html#installing-the-apache-couchdb-packages
|
||||||
|
ENV GPG_COUCH_KEY \
|
||||||
|
# gpg: rsa8192 205-01-19 The Apache Software Foundation (Package repository signing key) <root@apache.org>
|
||||||
|
390EF70BB1EA12B2773962950EE62FB37A00258D
|
||||||
|
RUN set -eux; \
|
||||||
|
apt-get update; \
|
||||||
|
apt-get install -y curl; \
|
||||||
|
export GNUPGHOME="$(mktemp -d)"; \
|
||||||
|
curl -fL -o keys.asc https://couchdb.apache.org/repo/keys.asc; \
|
||||||
|
gpg --batch --import keys.asc; \
|
||||||
|
gpg --batch --export "${GPG_COUCH_KEY}" > /usr/share/keyrings/couchdb-archive-keyring.gpg; \
|
||||||
|
command -v gpgconf && gpgconf --kill all || :; \
|
||||||
|
rm -rf "$GNUPGHOME"; \
|
||||||
|
apt-key list; \
|
||||||
|
apt purge -y --autoremove curl; \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV COUCHDB_VERSION 3.3.3
|
||||||
|
|
||||||
|
RUN . /etc/os-release; \
|
||||||
|
echo "deb [signed-by=/usr/share/keyrings/couchdb-archive-keyring.gpg] https://apache.jfrog.io/artifactory/couchdb-deb/ ${VERSION_CODENAME} main" | \
|
||||||
|
tee /etc/apt/sources.list.d/couchdb.list >/dev/null
|
||||||
|
|
||||||
|
# https://github.com/apache/couchdb-pkg/blob/master/debian/README.Debian
|
||||||
|
RUN set -eux; \
|
||||||
|
apt-get update; \
|
||||||
|
\
|
||||||
|
echo "couchdb couchdb/mode select none" | debconf-set-selections; \
|
||||||
|
# we DO want recommends this time
|
||||||
|
DEBIAN_FRONTEND=noninteractive apt-get install -y --allow-downgrades --allow-remove-essential --allow-change-held-packages \
|
||||||
|
couchdb="$COUCHDB_VERSION"~bookworm \
|
||||||
|
; \
|
||||||
|
# Undo symlinks to /var/log and /var/lib
|
||||||
|
rmdir /var/lib/couchdb /var/log/couchdb; \
|
||||||
|
rm /opt/couchdb/data /opt/couchdb/var/log; \
|
||||||
|
mkdir -p /opt/couchdb/data /opt/couchdb/var/log; \
|
||||||
|
chown couchdb:couchdb /opt/couchdb/data /opt/couchdb/var/log; \
|
||||||
|
chmod 777 /opt/couchdb/data /opt/couchdb/var/log; \
|
||||||
|
# Remove file that sets logging to a file
|
||||||
|
rm /opt/couchdb/etc/default.d/10-filelog.ini; \
|
||||||
|
# Check we own everything in /opt/couchdb. Matches the command in dockerfile_entrypoint.sh
|
||||||
|
find /opt/couchdb \! \( -user couchdb -group couchdb \) -exec chown -f couchdb:couchdb '{}' +; \
|
||||||
|
# Setup directories and permissions for config. Technically these could be 555 and 444 respectively
|
||||||
|
# but we keep them as 755 and 644 for consistency with CouchDB defaults and the dockerfile_entrypoint.sh.
|
||||||
|
find /opt/couchdb/etc -type d ! -perm 0755 -exec chmod -f 0755 '{}' +; \
|
||||||
|
find /opt/couchdb/etc -type f ! -perm 0644 -exec chmod -f 0644 '{}' +; \
|
||||||
|
# only local.d needs to be writable for the docker_entrypoint.sh
|
||||||
|
chmod -f 0777 /opt/couchdb/etc/local.d; \
|
||||||
|
# apt clean-up
|
||||||
|
rm -rf /var/lib/apt/lists/*;
|
||||||
|
|
||||||
|
# Add configuration
|
||||||
|
COPY --chown=couchdb:couchdb couch/10-docker-default.ini /opt/couchdb/etc/default.d/
|
||||||
|
# COPY --chown=couchdb:couchdb vm.args /opt/couchdb/etc/
|
||||||
|
|
||||||
|
COPY docker-entrypoint.sh /usr/local/bin
|
||||||
|
RUN ln -s usr/local/bin/docker-entrypoint.sh /docker-entrypoint.sh # backwards compat
|
||||||
|
ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"]
|
||||||
|
|
||||||
|
VOLUME /opt/couchdb/data
|
||||||
|
|
||||||
|
# 5984: Main CouchDB endpoint
|
||||||
|
# 4369: Erlang portmap daemon (epmd)
|
||||||
|
# 9100: CouchDB cluster communication port
|
||||||
|
EXPOSE 5984 4369 9100
|
||||||
|
CMD ["/opt/couchdb/bin/couchdb"]
|
||||||
|
|
||||||
|
FROM base as runner
|
||||||
|
|
||||||
ENV COUCHDB_USER admin
|
ENV COUCHDB_USER admin
|
||||||
ENV COUCHDB_PASSWORD admin
|
ENV COUCHDB_PASSWORD admin
|
||||||
|
@ -6,9 +103,9 @@ EXPOSE 5984
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends software-properties-common wget unzip curl && \
|
RUN apt-get update && apt-get install -y --no-install-recommends software-properties-common wget unzip curl && \
|
||||||
wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | apt-key add - && \
|
wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | apt-key add - && \
|
||||||
apt-add-repository 'deb http://security.debian.org/debian-security bullseye-security/updates main' && \
|
apt-add-repository 'deb http://security.debian.org/debian-security bookworm-security/updates main' && \
|
||||||
apt-add-repository 'deb http://archive.debian.org/debian stretch-backports main' && \
|
apt-add-repository 'deb http://archive.debian.org/debian stretch-backports main' && \
|
||||||
apt-add-repository 'deb https://packages.adoptium.net/artifactory/deb bullseye main' && \
|
apt-add-repository 'deb https://packages.adoptium.net/artifactory/deb bookworm main' && \
|
||||||
apt-get update && apt-get install -y --no-install-recommends temurin-8-jdk && \
|
apt-get update && apt-get install -y --no-install-recommends temurin-8-jdk && \
|
||||||
rm -rf /var/lib/apt/lists/
|
rm -rf /var/lib/apt/lists/
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
name=clouseau@127.0.0.1
|
name=clouseau@127.0.0.1
|
||||||
|
|
||||||
; set this to the same distributed Erlang cookie used by the CouchDB nodes
|
; set this to the same distributed Erlang cookie used by the CouchDB nodes
|
||||||
cookie=monster
|
cookie=COUCHDB_ERLANG_COOKIE
|
||||||
|
|
||||||
; the path where you would like to store the search index files
|
; the path where you would like to store the search index files
|
||||||
dir=DATA_DIR/search
|
dir=DATA_DIR/search
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
; CouchDB Configuration Settings
|
||||||
|
|
||||||
|
; Custom settings should be made in this file. They will override settings
|
||||||
|
; in default.ini, but unlike changes made to default.ini, this file won't be
|
||||||
|
; overwritten on server upgrade.
|
||||||
|
|
||||||
|
[chttpd]
|
||||||
|
bind_address = any
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
# erlang cookie for clouseau security
|
# erlang cookie for clouseau security
|
||||||
-name couchdb@127.0.0.1
|
-name couchdb@127.0.0.1
|
||||||
-setcookie monster
|
-setcookie COUCHDB_ERLANG_COOKIE
|
||||||
|
|
||||||
# Ensure that the Erlang VM listens on a known port
|
# Ensure that the Erlang VM listens on a known port
|
||||||
-kernel inet_dist_listen_min 9100
|
-kernel inet_dist_listen_min 9100
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# first arg is `-something` or `+something`
|
||||||
|
if [ "${1#-}" != "$1" ] || [ "${1#+}" != "$1" ]; then
|
||||||
|
set -- /opt/couchdb/bin/couchdb "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# first arg is the bare word `couchdb`
|
||||||
|
if [ "$1" = 'couchdb' ]; then
|
||||||
|
shift
|
||||||
|
set -- /opt/couchdb/bin/couchdb "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = '/opt/couchdb/bin/couchdb' ]; then
|
||||||
|
# this is where runtime configuration changes will be written.
|
||||||
|
# we need to explicitly touch it here in case /opt/couchdb/etc has
|
||||||
|
# been mounted as an external volume, in which case it won't exist.
|
||||||
|
# If running as the couchdb user (i.e. container starts as root),
|
||||||
|
# write permissions will be granted below.
|
||||||
|
touch /opt/couchdb/etc/local.d/docker.ini
|
||||||
|
|
||||||
|
# if user is root, assume running under the couchdb user (default)
|
||||||
|
# and ensure it is able to access files and directories that may be mounted externally
|
||||||
|
if [ "$(id -u)" = '0' ]; then
|
||||||
|
# Check that we own everything in /opt/couchdb and fix if necessary. We also
|
||||||
|
# add the `-f` flag in all the following invocations because there may be
|
||||||
|
# cases where some of these ownership and permissions issues are non-fatal
|
||||||
|
# (e.g. a config file owned by root with o+r is actually fine), and we don't
|
||||||
|
# to be too aggressive about crashing here ...
|
||||||
|
find /opt/couchdb \! \( -user couchdb -group couchdb \) -exec chown -f couchdb:couchdb '{}' +
|
||||||
|
|
||||||
|
# Ensure that data files have the correct permissions. We were previously
|
||||||
|
# preventing any access to these files outside of couchdb:couchdb, but it
|
||||||
|
# turns out that CouchDB itself does not set such restrictive permissions
|
||||||
|
# when it creates the files. The approach taken here ensures that the
|
||||||
|
# contents of the datadir have the same permissions as they had when they
|
||||||
|
# were initially created. This should minimize any startup delay.
|
||||||
|
find /opt/couchdb/data -type d ! -perm 0755 -exec chmod -f 0755 '{}' +
|
||||||
|
find /opt/couchdb/data -type f ! -perm 0644 -exec chmod -f 0644 '{}' +
|
||||||
|
|
||||||
|
# Do the same thing for configuration files and directories. Technically
|
||||||
|
# CouchDB only needs read access to the configuration files as all online
|
||||||
|
# changes will be applied to the "docker.ini" file below, but we set 644
|
||||||
|
# for the sake of consistency.
|
||||||
|
find /opt/couchdb/etc -type d ! -perm 0755 -exec chmod -f 0755 '{}' +
|
||||||
|
find /opt/couchdb/etc -type f ! -perm 0644 -exec chmod -f 0644 '{}' +
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -z "$NODENAME" ] && ! grep "couchdb@" /opt/couchdb/etc/vm.args; then
|
||||||
|
echo "-name couchdb@$NODENAME" >> /opt/couchdb/etc/vm.args
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$COUCHDB_USER" ] && [ "$COUCHDB_PASSWORD" ]; then
|
||||||
|
# Create admin only if not already present
|
||||||
|
if ! grep -Pzoqr "\[admins\]\n$COUCHDB_USER =" /opt/couchdb/etc/local.d/*.ini /opt/couchdb/etc/local.ini; then
|
||||||
|
printf "\n[admins]\n%s = %s\n" "$COUCHDB_USER" "$COUCHDB_PASSWORD" >> /opt/couchdb/etc/local.d/docker.ini
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$COUCHDB_SECRET" ]; then
|
||||||
|
# Set secret only if not already present
|
||||||
|
if ! grep -Pzoqr "\[chttpd_auth\]\nsecret =" /opt/couchdb/etc/local.d/*.ini /opt/couchdb/etc/local.ini; then
|
||||||
|
printf "\n[chttpd_auth]\nsecret = %s\n" "$COUCHDB_SECRET" >> /opt/couchdb/etc/local.d/docker.ini
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$COUCHDB_ERLANG_COOKIE" ]; then
|
||||||
|
cookieFile='/opt/couchdb/.erlang.cookie'
|
||||||
|
if [ -e "$cookieFile" ]; then
|
||||||
|
if [ "$(cat "$cookieFile" 2>/dev/null)" != "$COUCHDB_ERLANG_COOKIE" ]; then
|
||||||
|
echo >&2
|
||||||
|
echo >&2 "warning: $cookieFile contents do not match COUCHDB_ERLANG_COOKIE"
|
||||||
|
echo >&2
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "$COUCHDB_ERLANG_COOKIE" > "$cookieFile"
|
||||||
|
fi
|
||||||
|
chown couchdb:couchdb "$cookieFile"
|
||||||
|
chmod 600 "$cookieFile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(id -u)" = '0' ]; then
|
||||||
|
chown -f couchdb:couchdb /opt/couchdb/etc/local.d/docker.ini || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if we don't find an [admins] section followed by a non-comment, display a warning
|
||||||
|
if ! grep -Pzoqr '\[admins\]\n[^;]\w+' /opt/couchdb/etc/default.d/*.ini /opt/couchdb/etc/local.d/*.ini /opt/couchdb/etc/local.ini; then
|
||||||
|
# The - option suppresses leading tabs but *not* spaces. :)
|
||||||
|
cat >&2 <<-'EOWARN'
|
||||||
|
*************************************************************
|
||||||
|
ERROR: CouchDB 3.0+ will no longer run in "Admin Party"
|
||||||
|
mode. You *MUST* specify an admin user and
|
||||||
|
password, either via your own .ini file mapped
|
||||||
|
into the container at /opt/couchdb/etc/local.ini
|
||||||
|
or inside /opt/couchdb/etc/local.d, or with
|
||||||
|
"-e COUCHDB_USER=admin -e COUCHDB_PASSWORD=password"
|
||||||
|
to set it via "docker run".
|
||||||
|
*************************************************************
|
||||||
|
EOWARN
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(id -u)" = '0' ]; then
|
||||||
|
export HOME=$(echo ~couchdb)
|
||||||
|
exec setpriv --reuid=couchdb --regid=couchdb --clear-groups "$@"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$@"
|
|
@ -1,6 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
DATA_DIR=${DATA_DIR:-/data}
|
DATA_DIR=${DATA_DIR:-/data}
|
||||||
|
COUCHDB_ERLANG_COOKIE=${COUCHDB_ERLANG_COOKIE:-B9CFC32C-3458-4A86-8448-B3C753991CA7}
|
||||||
|
|
||||||
mkdir -p ${DATA_DIR}
|
mkdir -p ${DATA_DIR}
|
||||||
mkdir -p ${DATA_DIR}/couch/{dbs,views}
|
mkdir -p ${DATA_DIR}/couch/{dbs,views}
|
||||||
|
@ -60,6 +61,9 @@ else
|
||||||
sed -i "s#DATA_DIR#/data#g" /opt/couchdb/etc/local.ini
|
sed -i "s#DATA_DIR#/data#g" /opt/couchdb/etc/local.ini
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
sed -i "s#COUCHDB_ERLANG_COOKIE#${COUCHDB_ERLANG_COOKIE}#g" /opt/couchdb/etc/vm.args
|
||||||
|
sed -i "s#COUCHDB_ERLANG_COOKIE#${COUCHDB_ERLANG_COOKIE}#g" /opt/clouseau/clouseau.ini
|
||||||
|
|
||||||
# Start Clouseau. Budibase won't function correctly without Clouseau running, it
|
# Start Clouseau. Budibase won't function correctly without Clouseau running, it
|
||||||
# powers the search API endpoints which are used to do all sorts, including
|
# powers the search API endpoints which are used to do all sorts, including
|
||||||
# populating app grids.
|
# populating app grids.
|
||||||
|
|
|
@ -98,7 +98,6 @@ services:
|
||||||
couchdb-service:
|
couchdb-service:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
image: budibase/couchdb
|
image: budibase/couchdb
|
||||||
pull_policy: always
|
|
||||||
environment:
|
environment:
|
||||||
- COUCHDB_PASSWORD=${COUCH_DB_PASSWORD}
|
- COUCHDB_PASSWORD=${COUCH_DB_PASSWORD}
|
||||||
- COUCHDB_USER=${COUCH_DB_USER}
|
- COUCHDB_USER=${COUCH_DB_USER}
|
||||||
|
|
|
@ -3,7 +3,6 @@ FROM node:20-slim as build
|
||||||
# install node-gyp dependencies
|
# install node-gyp dependencies
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends g++ make python3 jq
|
RUN apt-get update && apt-get install -y --no-install-recommends g++ make python3 jq
|
||||||
|
|
||||||
|
|
||||||
# copy and install dependencies
|
# copy and install dependencies
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY package.json .
|
COPY package.json .
|
||||||
|
@ -39,10 +38,9 @@ COPY packages/worker/pm2.config.js packages/worker/pm2.config.js
|
||||||
COPY packages/string-templates packages/string-templates
|
COPY packages/string-templates packages/string-templates
|
||||||
|
|
||||||
|
|
||||||
FROM budibase/couchdb as runner
|
FROM budibase/couchdb:v3.3.3 as runner
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
ENV TARGETARCH $TARGETARCH
|
ENV TARGETARCH $TARGETARCH
|
||||||
ENV NODE_MAJOR 20
|
|
||||||
#TARGETBUILD can be set to single (for single docker image) or aas (for azure app service)
|
#TARGETBUILD can be set to single (for single docker image) or aas (for azure app service)
|
||||||
# e.g. docker build --build-arg TARGETBUILD=aas ....
|
# e.g. docker build --build-arg TARGETBUILD=aas ....
|
||||||
ARG TARGETBUILD=single
|
ARG TARGETBUILD=single
|
||||||
|
@ -60,10 +58,8 @@ RUN apt install -y software-properties-common apt-transport-https ca-certificate
|
||||||
&& apt install postgresql-client-15 -y \
|
&& apt install postgresql-client-15 -y \
|
||||||
&& apt remove software-properties-common apt-transport-https gpg -y
|
&& apt remove software-properties-common apt-transport-https gpg -y
|
||||||
|
|
||||||
# install other dependencies, nodejs, oracle requirements, jdk8, redis, nginx
|
# We use pm2 in order to run multiple node processes in a single container
|
||||||
WORKDIR /nodejs
|
RUN npm install --global pm2
|
||||||
COPY scripts/install-node.sh ./install.sh
|
|
||||||
RUN chmod +x install.sh && ./install.sh
|
|
||||||
|
|
||||||
# setup nginx
|
# setup nginx
|
||||||
COPY hosting/single/nginx/nginx.conf /etc/nginx
|
COPY hosting/single/nginx/nginx.conf /etc/nginx
|
||||||
|
|
|
@ -97,10 +97,12 @@ fi
|
||||||
sleep 10
|
sleep 10
|
||||||
|
|
||||||
pushd app
|
pushd app
|
||||||
pm2 start -l /dev/stdout --name app "yarn run:docker"
|
pm2 start --name app "yarn run:docker"
|
||||||
popd
|
popd
|
||||||
pushd worker
|
pushd worker
|
||||||
pm2 start -l /dev/stdout --name worker "yarn run:docker"
|
pm2 start --name worker "yarn run:docker"
|
||||||
popd
|
popd
|
||||||
echo "end of runner.sh, sleeping ..."
|
echo "end of runner.sh, sleeping ..."
|
||||||
|
|
||||||
|
tail -f $HOME/.pm2/logs/*.log
|
||||||
sleep infinity
|
sleep infinity
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://www.budibase.com">
|
||||||
|
<img alt="Budibase" src="https://res.cloudinary.com/daog6scxm/image/upload/v1696515725/Branding/Assets/Symbol/RGB/Full%20Colour/Budibase_Symbol_RGB_FullColour_cbqvha_1_z5cwq2.svg" width="60" />
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<h1 align="center">
|
||||||
|
Budibase
|
||||||
|
</h1>
|
||||||
|
<h3 align="center">
|
||||||
|
자체 인프라에서 몇 분 만에 맞춤형 비즈니스 도구를 구축하세요.
|
||||||
|
</h3>
|
||||||
|
<p align="center">
|
||||||
|
Budibase는 개발자와 IT 전문가가 몇 분 만에 맞춤형 애플리케이션을 구축하고 자동화할 수 있는 오픈 소스 로우코드 플랫폼입니다.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 align="center">
|
||||||
|
🤖 🎨 🚀
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="Budibase design ui" src="https://res.cloudinary.com/daog6scxm/image/upload/v1633524049/ui/design-ui-wide-mobile_gdaveq.jpg">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/Budibase/budibase/releases">
|
||||||
|
<img alt="GitHub all releases" src="https://img.shields.io/github/downloads/Budibase/budibase/total">
|
||||||
|
</a>
|
||||||
|
<a href="https://github.com/Budibase/budibase/releases">
|
||||||
|
<img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/Budibase/budibase">
|
||||||
|
</a>
|
||||||
|
<a href="https://twitter.com/intent/follow?screen_name=budibase">
|
||||||
|
<img src="https://img.shields.io/twitter/follow/budibase?style=social" alt="Follow @budibase" />
|
||||||
|
</a>
|
||||||
|
<img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Code of conduct" />
|
||||||
|
<a href="https://codecov.io/gh/Budibase/budibase">
|
||||||
|
<img src="https://codecov.io/gh/Budibase/budibase/graph/badge.svg?token=E8W2ZFXQOH"/>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 align="center">
|
||||||
|
<a href="https://docs.budibase.com/getting-started">소개</a>
|
||||||
|
<span> · </span>
|
||||||
|
<a href="https://docs.budibase.com">문서</a>
|
||||||
|
<span> · </span>
|
||||||
|
<a href="https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas">기능 요청</a>
|
||||||
|
<span> · </span>
|
||||||
|
<a href="https://github.com/Budibase/budibase/issues">버그 보고</a>
|
||||||
|
<span> · </span>
|
||||||
|
지원: <a href="https://github.com/Budibase/budibase/discussions">토론</a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
## ✨ 특징
|
||||||
|
|
||||||
|
### "실제" 소프트웨어를 구축할 수 있습니다.
|
||||||
|
Budibase를 사용하면 고성능 단일 페이지 애플리케이션을 구축할 수 있습니다. 또한 반응형 디자인으로 제작하여 사용자에게 멋진 경험을 제공할 수 있습니다.
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
### 오픈 소스 및 확장성
|
||||||
|
Budibase는 오픈소스이며, GPL v3 라이선스에 따라 공개되어 있습니다. 이는 Budibase가 항상 당신 곁에 있다는 안도감을 줄 것입니다. 그리고 우리는 개발자 친화적인 환경을 제공하고 있기 때문에, 당신은 원하는 만큼 소스 코드를 포크하여 수정하거나 Budibase에 직접 기여할 수 있습니다.
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
### 기존 데이터 또는 처음부터 시작
|
||||||
|
Budibase를 사용하면 다음과 같은 여러 소스에서 데이터를 가져올 수 있습니다: MondoDB, CouchDB, PostgreSQL, MySQL, Airtable, S3, DynamoDB 또는 REST API.
|
||||||
|
|
||||||
|
또는 원하는 경우 외부 도구 없이도 Budibase를 사용하여 처음부터 시작하여 자체 애플리케이션을 구축할 수 있습니다.[데이터 소스 제안](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="Budibase data" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970242/Out%20of%20beta%20launch/data_n1tlhf.png">
|
||||||
|
</p>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
### 강력한 내장 구성 요소로 애플리케이션을 설계하고 구축할 수 있습니다.
|
||||||
|
|
||||||
|
Budibase에는 아름답게 디자인된 강력한 컴포넌트들이 제공되며, 이를 사용하여 UI를 쉽게 구축할 수 있습니다. 또한, CSS를 통한 스타일링 옵션도 풍부하게 제공되어 보다 창의적인 표현도 가능하다.
|
||||||
|
[Request new component](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="Budibase design" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970243/Out%20of%20beta%20launch/design-like-a-pro_qhlfeu.gif">
|
||||||
|
</p>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
### 프로세스를 자동화하고, 다른 도구와 연동하고, 웹훅으로 연결하세요!
|
||||||
|
워크플로우와 수동 프로세스를 자동화하여 시간을 절약하세요. 웹훅 이벤트 연결부터 이메일 자동화까지, Budibase에 수행할 작업을 지시하기만 하면 자동으로 처리됩니다. [새로운 자동화 만들기](https://github.com/Budibase/automations)또는[새로운 자동화를 요청할 수 있습니다](https://github.com/Budibase/budibase/discussions?discussions_q=category%3AIdeas).
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="Budibase automations" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970486/Out%20of%20beta%20launch/automation_riro7u.png">
|
||||||
|
</p>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
### 선호하는 도구
|
||||||
|
Budibase는 사용자의 선호도에 따라 애플리케이션을 구축할 수 있는 다양한 도구를 통합하고 있습니다.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="Budibase integrations" src="https://res.cloudinary.com/daog6scxm/image/upload/v1636970242/Out%20of%20beta%20launch/integrations_kc7dqt.png">
|
||||||
|
</p>
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
### 관리자의 천국
|
||||||
|
Budibase는 어떤 규모의 프로젝트에도 유연하게 대응할 수 있으며, Budibase를 사용하면 개인 또는 조직의 서버에서 자체 호스팅하고 사용자, 온보딩, SMTP, 앱, 그룹, 테마 등을 한꺼번에 관리할 수 있습니다. 또한, 사용자나 그룹에 앱 포털을 제공하고 그룹 관리자에게 사용자 관리를 맡길 수도 있다.
|
||||||
|
- 프로모션 비디오: https://youtu.be/xoljVpty_Kw
|
||||||
|
|
||||||
|
<br /><br /><br />
|
||||||
|
|
||||||
|
## 🏁 시작
|
||||||
|
|
||||||
|
Docker, Kubernetes 또는 Digital Ocean을 사용하여 자체 인프라에서 Budibase를 호스팅하거나, 걱정 없이 빠르게 애플리케이션을 구축하려는 경우 클라우드에서 Budibase를 사용할 수 있습니다.
|
||||||
|
|
||||||
|
### [Budibase 셀프 호스팅으로 시작하기](https://docs.budibase.com/docs/hosting-methods)
|
||||||
|
|
||||||
|
- [Docker - single ARM compatible image](https://docs.budibase.com/docs/docker)
|
||||||
|
- [Docker Compose](https://docs.budibase.com/docs/docker-compose)
|
||||||
|
- [Kubernetes](https://docs.budibase.com/docs/kubernetes-k8s)
|
||||||
|
- [Digital Ocean](https://docs.budibase.com/docs/digitalocean)
|
||||||
|
- [Portainer](https://docs.budibase.com/docs/portainer)
|
||||||
|
|
||||||
|
|
||||||
|
### [클라우드에서 Budibase 시작하기](https://budibase.com)
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
## 🎓 Budibase 알아보기
|
||||||
|
|
||||||
|
문서 [documentacion de Budibase](https://docs.budibase.com/docs).
|
||||||
|
<br />
|
||||||
|
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
## 💬 커뮤니티
|
||||||
|
|
||||||
|
질문하고, 다른 사람을 돕고, 다른 Budibase 사용자와 즐거운 대화를 나눌 수 있는 Budibase 커뮤니티에 여러분을 초대합니다.
|
||||||
|
[깃허브 토론](https://github.com/Budibase/budibase/discussions)
|
||||||
|
<br /><br /><br />
|
||||||
|
|
||||||
|
|
||||||
|
## ❗ 행동강령
|
||||||
|
|
||||||
|
Budibase 는 모든 계층의 사람들을 환영하고 상호 존중하는 환경을 제공하는 데 특별한 주의를 기울이고 있습니다. 저희는 커뮤니티에도 같은 기대를 가지고 있습니다.
|
||||||
|
[**행동 강령**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md).
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
|
||||||
|
## 🙌 Contribuir en Budibase
|
||||||
|
|
||||||
|
버그 신고부터 코드의 버그 수정에 이르기까지 모든 기여를 감사하고 환영합니다. 새로운 기능을 구현하거나 API를 변경할 계획이 있다면 [여기에 새 메시지](https://github.com/Budibase/budibase/issues),
|
||||||
|
이렇게 하면 여러분의 노력이 헛되지 않도록 보장할 수 있습니다.
|
||||||
|
|
||||||
|
여기에는 다음을 위해 Budibase 환경을 설정하는 방법에 대한 지침이 나와 있습니다. [여기를 클릭하세요](https://github.com/Budibase/budibase/tree/HEAD/docs/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
### 어디서부터 시작해야 할지 혼란스러우신가요?
|
||||||
|
이곳은 기여를 시작하기에 최적의 장소입니다! [First time issues project](https://github.com/Budibase/budibase/projects/22).
|
||||||
|
|
||||||
|
### 리포지토리 구성
|
||||||
|
|
||||||
|
Budibase는 Lerna에서 관리하는 단일 리포지토리입니다. Lerna는 변경 사항이 있을 때마다 이를 동기화하여 Budibase 패키지를 빌드하고 게시합니다. 크게 보면 이러한 패키지가 Budibase를 구성하는 패키지입니다:
|
||||||
|
|
||||||
|
- [packages/builder](https://github.com/Budibase/budibase/tree/HEAD/packages/builder) - budibase builder 클라이언트 측의 svelte 애플리케이션 코드가 포함되어 있습니다.
|
||||||
|
|
||||||
|
- [packages/client](https://github.com/Budibase/budibase/tree/HEAD/packages/client) - budibase builder 클라이언트 측의 svelte 애플리케이션 코드가 포함되어 있습니다.
|
||||||
|
|
||||||
|
- [packages/server](https://github.com/Budibase/budibase/tree/HEAD/packages/server) - Budibase의 서버 부분입니다. 이 Koa 애플리케이션은 빌더에게 Budibase 애플리케이션을 생성하는 데 필요한 것을 제공하는 역할을 합니다. 또한 데이터베이스 및 파일 저장소와 상호 작용할 수 있는 API를 제공합니다.
|
||||||
|
|
||||||
|
자세한 내용은 다음 문서를 참조하세요. [CONTRIBUTING.md](https://github.com/Budibase/budibase/blob/HEAD/docs/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
|
||||||
|
## 📝 라이선스
|
||||||
|
|
||||||
|
Budibase는 오픈 소스이며, 라이선스는 다음과 같습니다 [GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html). 클라이언트 및 컴포넌트 라이브러리는 다음과 같이 라이선스가 부여됩니다. [MPL](https://directory.fsf.org/wiki/License:MPL-2.0) - 이렇게 하면 빌드한 애플리케이션에 원하는 대로 라이선스를 부여할 수 있습니다.
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
## ⭐ 스타 수의 역사
|
||||||
|
|
||||||
|
[![Stargazers over time](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase)
|
||||||
|
|
||||||
|
빌더 업데이트 중 문제가 발생하는 경우 [여기](https://github.com/Budibase/budibase/blob/HEAD/docs/CONTRIBUTING.md#troubleshooting) 를 참고하여 환경을 정리해 주세요.
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
## Contributors ✨
|
||||||
|
|
||||||
|
훌륭한 여러분께 감사할 따름입니다. ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||||
|
<!-- prettier-ignore-start -->
|
||||||
|
<!-- markdownlint-disable -->
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="http://martinmck.com"><img src="https://avatars1.githubusercontent.com/u/11256663?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martin McKeaveney</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=shogunpurple" title="Tests">⚠️</a> <a href="#infra-shogunpurple" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
||||||
|
<td align="center"><a href="http://www.michaeldrury.co.uk/"><img src="https://avatars2.githubusercontent.com/u/4407001?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Drury</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=mike12345567" title="Tests">⚠️</a> <a href="#infra-mike12345567" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/aptkingston"><img src="https://avatars3.githubusercontent.com/u/9075550?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrew Kingston</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=aptkingston" title="Tests">⚠️</a> <a href="#design-aptkingston" title="Design">🎨</a></td>
|
||||||
|
<td align="center"><a href="https://budibase.com/"><img src="https://avatars3.githubusercontent.com/u/3524181?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Shanks</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=mjashanks" title="Tests">⚠️</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/kevmodrome"><img src="https://avatars3.githubusercontent.com/u/534488?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Åberg Kultalahti</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=kevmodrome" title="Tests">⚠️</a></td>
|
||||||
|
<td align="center"><a href="https://www.budibase.com/"><img src="https://avatars2.githubusercontent.com/u/49767913?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joe</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=joebudi" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=joebudi" title="Code">💻</a> <a href="#content-joebudi" title="Content">🖋</a> <a href="#design-joebudi" title="Design">🎨</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/Rory-Powell"><img src="https://avatars.githubusercontent.com/u/8755148?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rory Powell</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=Rory-Powell" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=Rory-Powell" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=Rory-Powell" title="Tests">⚠️</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/PClmnt"><img src="https://avatars.githubusercontent.com/u/5665926?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Peter Clement</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=PClmnt" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=PClmnt" title="Documentation">📖</a> <a href="https://github.com/Budibase/budibase/commits?author=PClmnt" title="Tests">⚠️</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/Conor-Mack"><img src="https://avatars1.githubusercontent.com/u/36074859?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Conor_Mack</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=Conor-Mack" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=Conor-Mack" title="Tests">⚠️</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/pngwn"><img src="https://avatars1.githubusercontent.com/u/12937446?v=4?s=100" width="100px;" alt=""/><br /><sub><b>pngwn</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=pngwn" title="Code">💻</a> <a href="https://github.com/Budibase/budibase/commits?author=pngwn" title="Tests">⚠️</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/HugoLd"><img src="https://avatars0.githubusercontent.com/u/26521848?v=4?s=100" width="100px;" alt=""/><br /><sub><b>HugoLd</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=HugoLd" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/victoriasloan"><img src="https://avatars.githubusercontent.com/u/9913651?v=4?s=100" width="100px;" alt=""/><br /><sub><b>victoriasloan</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=victoriasloan" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/yashank09"><img src="https://avatars.githubusercontent.com/u/37672190?v=4?s=100" width="100px;" alt=""/><br /><sub><b>yashank09</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=yashank09" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/SOVLOOKUP"><img src="https://avatars.githubusercontent.com/u/53158137?v=4?s=100" width="100px;" alt=""/><br /><sub><b>SOVLOOKUP</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=SOVLOOKUP" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/seoulaja"><img src="https://avatars.githubusercontent.com/u/15101654?v=4?s=100" width="100px;" alt=""/><br /><sub><b>seoulaja</b></sub></a><br /><a href="#translation-seoulaja" title="Translation">🌍</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/mslourens"><img src="https://avatars.githubusercontent.com/u/1907152?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Maurits Lourens</b></sub></a><br /><a href="https://github.com/Budibase/budibase/commits?author=mslourens" title="Tests">⚠️</a> <a href="https://github.com/Budibase/budibase/commits?author=mslourens" title="Code">💻</a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- markdownlint-restore -->
|
||||||
|
<!-- prettier-ignore-end -->
|
||||||
|
|
||||||
|
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||||
|
|
||||||
|
이 프로젝트는 다음 사양을 따릅니다. [all-contributors](https://github.com/all-contributors/all-contributors).
|
||||||
|
모든 종류의 기여를 환영합니다!
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "2.19.4",
|
"version": "2.20.14",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*",
|
"packages/*",
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
"nx-cloud": "16.0.5",
|
"nx-cloud": "16.0.5",
|
||||||
"prettier": "2.8.8",
|
"prettier": "2.8.8",
|
||||||
"prettier-plugin-svelte": "^2.3.0",
|
"prettier-plugin-svelte": "^2.3.0",
|
||||||
"svelte": "3.49.0",
|
"svelte": "^4.2.10",
|
||||||
"svelte-eslint-parser": "^0.33.1",
|
"svelte-eslint-parser": "^0.33.1",
|
||||||
"typescript": "5.2.2",
|
"typescript": "5.2.2",
|
||||||
"yargs": "^17.7.2"
|
"yargs": "^17.7.2"
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
Document,
|
Document,
|
||||||
isDocument,
|
isDocument,
|
||||||
RowResponse,
|
RowResponse,
|
||||||
|
RowValue,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import { getCouchInfo } from "./connections"
|
import { getCouchInfo } from "./connections"
|
||||||
import { directCouchUrlCall } from "./utils"
|
import { directCouchUrlCall } from "./utils"
|
||||||
|
@ -221,7 +222,7 @@ export class DatabaseImpl implements Database {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async allDocs<T extends Document>(
|
async allDocs<T extends Document | RowValue>(
|
||||||
params: DatabaseQueryOpts
|
params: DatabaseQueryOpts
|
||||||
): Promise<AllDocsResponse<T>> {
|
): Promise<AllDocsResponse<T>> {
|
||||||
return this.performCall(db => {
|
return this.performCall(db => {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
DocumentScope,
|
|
||||||
DocumentDestroyResponse,
|
DocumentDestroyResponse,
|
||||||
DocumentInsertResponse,
|
DocumentInsertResponse,
|
||||||
DocumentBulkResponse,
|
DocumentBulkResponse,
|
||||||
|
@ -13,6 +12,7 @@ import {
|
||||||
DatabasePutOpts,
|
DatabasePutOpts,
|
||||||
DatabaseQueryOpts,
|
DatabaseQueryOpts,
|
||||||
Document,
|
Document,
|
||||||
|
RowValue,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import tracer from "dd-trace"
|
import tracer from "dd-trace"
|
||||||
import { Writable } from "stream"
|
import { Writable } from "stream"
|
||||||
|
@ -79,7 +79,7 @@ export class DDInstrumentedDatabase implements Database {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
allDocs<T extends Document>(
|
allDocs<T extends Document | RowValue>(
|
||||||
params: DatabaseQueryOpts
|
params: DatabaseQueryOpts
|
||||||
): Promise<AllDocsResponse<T>> {
|
): Promise<AllDocsResponse<T>> {
|
||||||
return tracer.trace("db.allDocs", span => {
|
return tracer.trace("db.allDocs", span => {
|
||||||
|
|
|
@ -74,7 +74,7 @@ export function getGlobalIDFromUserMetadataID(id: string) {
|
||||||
* Generates a template ID.
|
* Generates a template ID.
|
||||||
* @param ownerId The owner/user of the template, this could be global or a workspace level.
|
* @param ownerId The owner/user of the template, this could be global or a workspace level.
|
||||||
*/
|
*/
|
||||||
export function generateTemplateID(ownerId: any) {
|
export function generateTemplateID(ownerId: string) {
|
||||||
return `${DocumentType.TEMPLATE}${SEPARATOR}${ownerId}${SEPARATOR}${newid()}`
|
return `${DocumentType.TEMPLATE}${SEPARATOR}${ownerId}${SEPARATOR}${newid()}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ export function prefixRoleID(name: string) {
|
||||||
* Generates a new dev info document ID - this is scoped to a user.
|
* Generates a new dev info document ID - this is scoped to a user.
|
||||||
* @returns The new dev info ID which info for dev (like api key) can be stored under.
|
* @returns The new dev info ID which info for dev (like api key) can be stored under.
|
||||||
*/
|
*/
|
||||||
export const generateDevInfoID = (userId: any) => {
|
export const generateDevInfoID = (userId: string) => {
|
||||||
return `${DocumentType.DEV_INFO}${SEPARATOR}${userId}`
|
return `${DocumentType.DEV_INFO}${SEPARATOR}${userId}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
Event,
|
Event,
|
||||||
Datasource,
|
Datasource,
|
||||||
Query,
|
Query,
|
||||||
|
QueryPreview,
|
||||||
QueryCreatedEvent,
|
QueryCreatedEvent,
|
||||||
QueryUpdatedEvent,
|
QueryUpdatedEvent,
|
||||||
QueryDeletedEvent,
|
QueryDeletedEvent,
|
||||||
|
@ -68,9 +69,9 @@ const run = async (count: number, timestamp?: string | number) => {
|
||||||
await publishEvent(Event.QUERIES_RUN, properties, timestamp)
|
await publishEvent(Event.QUERIES_RUN, properties, timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
const previewed = async (datasource: Datasource, query: Query) => {
|
const previewed = async (datasource: Datasource, query: QueryPreview) => {
|
||||||
const properties: QueryPreviewedEvent = {
|
const properties: QueryPreviewedEvent = {
|
||||||
queryId: query._id,
|
queryId: query.queryId,
|
||||||
datasourceId: datasource._id as string,
|
datasourceId: datasource._id as string,
|
||||||
source: datasource.source,
|
source: datasource.source,
|
||||||
queryVerb: query.queryVerb,
|
queryVerb: query.queryVerb,
|
||||||
|
|
|
@ -6,6 +6,7 @@ import * as context from "./context"
|
||||||
import semver from "semver"
|
import semver from "semver"
|
||||||
import { bustCache, withCache, TTL, CacheKey } from "./cache/generic"
|
import { bustCache, withCache, TTL, CacheKey } from "./cache/generic"
|
||||||
import environment from "./environment"
|
import environment from "./environment"
|
||||||
|
import { logAlert } from "./logging"
|
||||||
|
|
||||||
export const getInstall = async (): Promise<Installation> => {
|
export const getInstall = async (): Promise<Installation> => {
|
||||||
return withCache(CacheKey.INSTALLATION, TTL.ONE_DAY, getInstallFromDB, {
|
return withCache(CacheKey.INSTALLATION, TTL.ONE_DAY, getInstallFromDB, {
|
||||||
|
@ -80,27 +81,35 @@ export const checkInstallVersion = async (): Promise<void> => {
|
||||||
const currentVersion = install.version
|
const currentVersion = install.version
|
||||||
const newVersion = environment.VERSION
|
const newVersion = environment.VERSION
|
||||||
|
|
||||||
if (currentVersion !== newVersion) {
|
try {
|
||||||
const isUpgrade = semver.gt(newVersion, currentVersion)
|
if (currentVersion !== newVersion) {
|
||||||
const isDowngrade = semver.lt(newVersion, currentVersion)
|
const isUpgrade = semver.gt(newVersion, currentVersion)
|
||||||
|
const isDowngrade = semver.lt(newVersion, currentVersion)
|
||||||
|
|
||||||
const success = await updateVersion(newVersion)
|
const success = await updateVersion(newVersion)
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
await context.doInIdentityContext(
|
await context.doInIdentityContext(
|
||||||
{
|
{
|
||||||
_id: install.installId,
|
_id: install.installId,
|
||||||
type: IdentityType.INSTALLATION,
|
type: IdentityType.INSTALLATION,
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
if (isUpgrade) {
|
if (isUpgrade) {
|
||||||
await events.installation.upgraded(currentVersion, newVersion)
|
await events.installation.upgraded(currentVersion, newVersion)
|
||||||
} else if (isDowngrade) {
|
} else if (isDowngrade) {
|
||||||
await events.installation.downgraded(currentVersion, newVersion)
|
await events.installation.downgraded(currentVersion, newVersion)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
)
|
await events.identification.identifyInstallationGroup(install.installId)
|
||||||
await events.identification.identifyInstallationGroup(install.installId)
|
}
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err?.message?.includes("Invalid Version")) {
|
||||||
|
logAlert(`Invalid version "${newVersion}" - is it semver?`)
|
||||||
|
} else {
|
||||||
|
logAlert("Failed to retrieve version", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,12 @@ import { Header } from "../../constants"
|
||||||
|
|
||||||
const correlator = require("correlation-id")
|
const correlator = require("correlation-id")
|
||||||
|
|
||||||
export const setHeader = (headers: any) => {
|
export const setHeader = (headers: Record<string, string>) => {
|
||||||
const correlationId = correlator.getId()
|
const correlationId = correlator.getId()
|
||||||
if (correlationId) {
|
if (!correlationId) {
|
||||||
headers[Header.CORRELATION_ID] = correlationId
|
return
|
||||||
}
|
}
|
||||||
|
headers[Header.CORRELATION_ID] = correlationId
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getId() {
|
export function getId() {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import Joi, { ObjectSchema } from "joi"
|
import Joi from "joi"
|
||||||
import { BBContext } from "@budibase/types"
|
import { Ctx } from "@budibase/types"
|
||||||
|
|
||||||
function validate(
|
function validate(
|
||||||
schema: Joi.ObjectSchema | Joi.ArraySchema,
|
schema: Joi.ObjectSchema | Joi.ArraySchema,
|
||||||
property: string
|
property: string
|
||||||
) {
|
) {
|
||||||
// Return a Koa middleware function
|
// Return a Koa middleware function
|
||||||
return (ctx: BBContext, next: any) => {
|
return (ctx: Ctx, next: any) => {
|
||||||
if (!schema) {
|
if (!schema) {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ function validate(
|
||||||
const { error } = schema.validate(params)
|
const { error } = schema.validate(params)
|
||||||
if (error) {
|
if (error) {
|
||||||
ctx.throw(400, `Invalid ${property} - ${error.message}`)
|
ctx.throw(400, `Invalid ${property} - ${error.message}`)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { Plugin } from "@budibase/types"
|
||||||
|
|
||||||
// URLS
|
// URLS
|
||||||
|
|
||||||
export function enrichPluginURLs(plugins: Plugin[]) {
|
export function enrichPluginURLs(plugins?: Plugin[]): Plugin[] {
|
||||||
if (!plugins || !plugins.length) {
|
if (!plugins || !plugins.length) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ export enum Databases {
|
||||||
WRITE_THROUGH = "writeThrough",
|
WRITE_THROUGH = "writeThrough",
|
||||||
LOCKS = "locks",
|
LOCKS = "locks",
|
||||||
SOCKET_IO = "socket_io",
|
SOCKET_IO = "socket_io",
|
||||||
|
BPM_EVENTS = "bpmEvents",
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -58,7 +58,7 @@ export const useCloudFree = () => {
|
||||||
// FEATURES
|
// FEATURES
|
||||||
|
|
||||||
const useFeature = (feature: Feature) => {
|
const useFeature = (feature: Feature) => {
|
||||||
const license = cloneDeep(UNLIMITED_LICENSE)
|
const license = cloneDeep(getCachedLicense() || UNLIMITED_LICENSE)
|
||||||
const opts: UseLicenseOpts = {
|
const opts: UseLicenseOpts = {
|
||||||
features: [feature],
|
features: [feature],
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,7 @@
|
||||||
"rollup": "^2.45.2",
|
"rollup": "^2.45.2",
|
||||||
"rollup-plugin-postcss": "^4.0.0",
|
"rollup-plugin-postcss": "^4.0.0",
|
||||||
"rollup-plugin-svelte": "^7.1.0",
|
"rollup-plugin-svelte": "^7.1.0",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2"
|
||||||
"svelte": "3.49.0"
|
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"svelte"
|
"svelte"
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<span
|
<span
|
||||||
class="btn-wrap"
|
class="btn-wrap"
|
||||||
on:mouseover={() => (showTooltip = true)}
|
on:mouseover={() => (showTooltip = true)}
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
setContext("actionMenu", { show, hide })
|
setContext("actionMenu", { show, hide })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div use:getAnchor on:click={openMenu}>
|
<div use:getAnchor on:click={openMenu}>
|
||||||
<slot name="control" />
|
<slot name="control" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
export let hoverable = false
|
export let hoverable = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<span
|
<span
|
||||||
on:click
|
on:click
|
||||||
class="spectrum-Label"
|
class="spectrum-Label"
|
||||||
|
|
|
@ -123,6 +123,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div
|
<div
|
||||||
bind:this={preview}
|
bind:this={preview}
|
||||||
class="preview size--{size || 'M'}"
|
class="preview size--{size || 'M'}"
|
||||||
|
@ -137,6 +139,8 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<Popover bind:this={dropdown} anchor={preview} maxHeight={320} {offset} {align}>
|
<Popover bind:this={dropdown} anchor={preview} maxHeight={320} {offset} {align}>
|
||||||
<Layout paddingX="XL" paddingY="L">
|
<Layout paddingX="XL" paddingY="L">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class="property-group-container">
|
<div class="property-group-container">
|
||||||
{#if name}
|
{#if name}
|
||||||
<div class="property-group-name" on:click={onHeaderClick}>
|
<div class="property-group-name" on:click={onHeaderClick}>
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div
|
<div
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
class="fancy-field"
|
class="fancy-field"
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div
|
<div
|
||||||
class="spectrum-InputGroup"
|
class="spectrum-InputGroup"
|
||||||
class:is-focused={open || focus}
|
class:is-focused={open || focus}
|
||||||
|
|
|
@ -193,6 +193,8 @@
|
||||||
aria-required="false"
|
aria-required="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
>
|
>
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div
|
<div
|
||||||
on:click={flatpickr?.open}
|
on:click={flatpickr?.open}
|
||||||
class="spectrum-Textfield spectrum-InputGroup-textfield"
|
class="spectrum-Textfield spectrum-InputGroup-textfield"
|
||||||
|
@ -230,6 +232,7 @@
|
||||||
</Flatpickr>
|
</Flatpickr>
|
||||||
{/key}
|
{/key}
|
||||||
{#if open}
|
{#if open}
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="overlay" on:mousedown|self={flatpickr?.close} />
|
<div class="overlay" on:mousedown|self={flatpickr?.close} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,9 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||||
<div class="container" class:compact>
|
<div class="container" class:compact>
|
||||||
{#if selectedImage}
|
{#if selectedImage}
|
||||||
{#if gallery}
|
{#if gallery}
|
||||||
|
|
|
@ -96,6 +96,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class="spectrum-InputGroup">
|
<div class="spectrum-InputGroup">
|
||||||
<div
|
<div
|
||||||
class:is-disabled={disabled || hbsValue.length}
|
class:is-disabled={disabled || hbsValue.length}
|
||||||
|
|
|
@ -50,6 +50,8 @@
|
||||||
on:change={handleFile}
|
on:change={handleFile}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class="field">
|
<div class="field">
|
||||||
{#if value}
|
{#if value}
|
||||||
<div class="file-view">
|
<div class="file-view">
|
||||||
|
|
|
@ -110,6 +110,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class="spectrum-InputGroup" class:is-disabled={disabled}>
|
<div class="spectrum-InputGroup" class:is-disabled={disabled}>
|
||||||
<div
|
<div
|
||||||
class="spectrum-Textfield spectrum-InputGroup-textfield"
|
class="spectrum-Textfield spectrum-InputGroup-textfield"
|
||||||
|
|
|
@ -146,6 +146,7 @@
|
||||||
<use xlink:href="#spectrum-css-icon-Chevron100" />
|
<use xlink:href="#spectrum-css-icon-Chevron100" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<Popover
|
<Popover
|
||||||
anchor={customAnchor ? customAnchor : button}
|
anchor={customAnchor ? customAnchor : button}
|
||||||
align={align || "left"}
|
align={align || "left"}
|
||||||
|
|
|
@ -104,6 +104,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class="spectrum-InputGroup" class:is-disabled={disabled}>
|
<div class="spectrum-InputGroup" class:is-disabled={disabled}>
|
||||||
<div
|
<div
|
||||||
class="spectrum-Textfield spectrum-InputGroup-textfield"
|
class="spectrum-Textfield spectrum-InputGroup-textfield"
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div
|
<div
|
||||||
class="icon"
|
class="icon"
|
||||||
on:mouseover={() => (showTooltip = true)}
|
on:mouseover={() => (showTooltip = true)}
|
||||||
|
|
|
@ -58,6 +58,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="preview size--{size || 'M'}" on:click={() => (open = true)}>
|
<div class="preview size--{size || 'M'}" on:click={() => (open = true)}>
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
let showTooltip = false
|
let showTooltip = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div
|
<div
|
||||||
class="icon-side-nav-item"
|
class="icon-side-nav-item"
|
||||||
class:active
|
class:active
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div>
|
<div>
|
||||||
<Input readonly {value} {label} />
|
<Input readonly {value} {label} />
|
||||||
<div class="icon" on:click={() => copyToClipboard(value)}>
|
<div class="icon" on:click={() => copyToClipboard(value)}>
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
|
overflow-y: scroll !important;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
$: initials = avatar ? title?.[0] : null
|
$: initials = avatar ? title?.[0] : null
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class="list-item" class:hoverable on:click>
|
<div class="list-item" class:hoverable on:click>
|
||||||
<div class="left">
|
<div class="left">
|
||||||
{#if icon}
|
{#if icon}
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<li
|
<li
|
||||||
on:click|preventDefault={disabled ? null : onClick}
|
on:click|preventDefault={disabled ? null : onClick}
|
||||||
class="spectrum-Menu-item"
|
class="spectrum-Menu-item"
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div on:click={increment}>
|
<div on:click={increment}>
|
||||||
Click me
|
Click me
|
||||||
{remaining}
|
{remaining}
|
||||||
|
|
|
@ -100,6 +100,7 @@
|
||||||
-->
|
-->
|
||||||
<Portal target=".modal-container">
|
<Portal target=".modal-container">
|
||||||
{#if visible}
|
{#if visible}
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="spectrum-Underlay is-open" on:mousedown|self={cancel}>
|
<div class="spectrum-Underlay is-open" on:mousedown|self={cancel}>
|
||||||
<div
|
<div
|
||||||
class="background"
|
class="background"
|
||||||
|
|
|
@ -81,6 +81,8 @@
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
class="actions"
|
class="actions"
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
export let hasNextPage = true
|
export let hasNextPage = true
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<nav class="spectrum-Pagination spectrum-Pagination--explicit">
|
<nav class="spectrum-Pagination spectrum-Pagination--explicit">
|
||||||
<div
|
<div
|
||||||
href="#"
|
href="#"
|
||||||
|
|
|
@ -78,6 +78,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if open}
|
{#if open}
|
||||||
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||||
<Portal {target}>
|
<Portal {target}>
|
||||||
<div
|
<div
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
|
|
|
@ -40,6 +40,8 @@
|
||||||
export let overBackground
|
export let overBackground
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div
|
<div
|
||||||
on:click
|
on:click
|
||||||
class:spectrum-ProgressCircle--indeterminate={value == null}
|
class:spectrum-ProgressCircle--indeterminate={value == null}
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
export let badge = ""
|
export let badge = ""
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
||||||
<li
|
<li
|
||||||
class="spectrum-SideNav-item"
|
class="spectrum-SideNav-item"
|
||||||
class:is-selected={selected}
|
class:is-selected={selected}
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
export let hoverable = false
|
export let hoverable = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div
|
<div
|
||||||
on:click
|
on:click
|
||||||
class="spectrum-StatusLight spectrum-StatusLight--size{size}"
|
class="spectrum-StatusLight spectrum-StatusLight--size{size}"
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div on:click|stopPropagation={onClick}>
|
<div on:click|stopPropagation={onClick}>
|
||||||
<Icon size="S" name="Copy" />
|
<Icon size="S" name="Copy" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -303,6 +303,8 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#key fields?.length}
|
{#key fields?.length}
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div
|
<div
|
||||||
class="wrapper"
|
class="wrapper"
|
||||||
class:wrapper--quiet={quiet}
|
class:wrapper--quiet={quiet}
|
||||||
|
|
|
@ -48,6 +48,9 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
||||||
<div
|
<div
|
||||||
{id}
|
{id}
|
||||||
bind:this={tab_internal}
|
bind:this={tab_internal}
|
||||||
|
|
|
@ -90,6 +90,7 @@
|
||||||
onDestroy(hide)
|
onDestroy(hide)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div
|
<div
|
||||||
bind:this={wrapper}
|
bind:this={wrapper}
|
||||||
class="abs-tooltip"
|
class="abs-tooltip"
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
let showTooltip = false
|
let showTooltip = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class:container={!!tooltip}>
|
<div class:container={!!tooltip}>
|
||||||
<slot />
|
<slot />
|
||||||
{#if tooltip}
|
{#if tooltip}
|
||||||
|
|
|
@ -20,3 +20,9 @@
|
||||||
>
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
p {
|
||||||
|
text-wrap: pretty;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -21,4 +21,8 @@
|
||||||
h1 {
|
h1 {
|
||||||
font-family: var(--font-accent);
|
font-family: var(--font-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -87,14 +87,13 @@
|
||||||
"@rollup/plugin-replace": "^5.0.3",
|
"@rollup/plugin-replace": "^5.0.3",
|
||||||
"@roxi/routify": "2.18.12",
|
"@roxi/routify": "2.18.12",
|
||||||
"@sveltejs/vite-plugin-svelte": "1.4.0",
|
"@sveltejs/vite-plugin-svelte": "1.4.0",
|
||||||
"@testing-library/jest-dom": "5.17.0",
|
"@testing-library/jest-dom": "6.4.2",
|
||||||
"@testing-library/svelte": "^3.2.2",
|
"@testing-library/svelte": "^4.1.0",
|
||||||
"babel-jest": "^29.6.2",
|
"babel-jest": "^29.6.2",
|
||||||
"identity-obj-proxy": "^3.0.0",
|
"identity-obj-proxy": "^3.0.0",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"jsdom": "^21.1.1",
|
"jsdom": "^21.1.1",
|
||||||
"ncp": "^2.0.0",
|
"ncp": "^2.0.0",
|
||||||
"svelte": "^3.49.0",
|
|
||||||
"svelte-jester": "^1.3.2",
|
"svelte-jester": "^1.3.2",
|
||||||
"vite": "^4.5.0",
|
"vite": "^4.5.0",
|
||||||
"vite-plugin-static-copy": "^0.17.0",
|
"vite-plugin-static-copy": "^0.17.0",
|
||||||
|
|
|
@ -110,6 +110,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title="Add automation step"
|
title="Add automation step"
|
||||||
confirmText="Save"
|
confirmText="Save"
|
||||||
|
|
|
@ -46,6 +46,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="header" class:scrolling>
|
<div class="header" class:scrolling>
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<UndoRedoControl store={automationHistoryStore} />
|
<UndoRedoControl store={automationHistoryStore} />
|
||||||
|
@ -130,6 +132,7 @@
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
padding: 23px 23px 80px;
|
padding: 23px 23px 80px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header.scrolling {
|
.header.scrolling {
|
||||||
|
|
|
@ -103,6 +103,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class={`block ${block.type} hoverable`} class:selected on:click={() => {}}>
|
<div class={`block ${block.type} hoverable`} class:selected on:click={() => {}}>
|
||||||
{#if loopBlock}
|
{#if loopBlock}
|
||||||
<div class="blockSection">
|
<div class="blockSection">
|
||||||
|
|
|
@ -93,6 +93,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div
|
<div
|
||||||
class:typing={typing && !automationNameError}
|
class:typing={typing && !automationNameError}
|
||||||
class:typing-error={automationNameError}
|
class:typing-error={automationNameError}
|
||||||
|
|
|
@ -46,6 +46,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<ModalContent
|
<ModalContent
|
||||||
title="Create Automation"
|
title="Create Automation"
|
||||||
confirmText="Save"
|
confirmText="Save"
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
import Editor from "components/integration/QueryEditor.svelte"
|
import Editor from "components/integration/QueryEditor.svelte"
|
||||||
import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte"
|
import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte"
|
||||||
import CodeEditor from "components/common/CodeEditor/CodeEditor.svelte"
|
import CodeEditor from "components/common/CodeEditor/CodeEditor.svelte"
|
||||||
import BindingPicker from "components/common/bindings/BindingSidePanel.svelte"
|
import BindingSidePanel from "components/common/bindings/BindingSidePanel.svelte"
|
||||||
import { BindingHelpers } from "components/common/bindings/utils"
|
import { BindingHelpers } from "components/common/bindings/utils"
|
||||||
import {
|
import {
|
||||||
bindingsToCompletions,
|
bindingsToCompletions,
|
||||||
|
@ -577,7 +577,7 @@
|
||||||
</div>
|
</div>
|
||||||
{#if editingJs}
|
{#if editingJs}
|
||||||
<div class="js-binding-picker">
|
<div class="js-binding-picker">
|
||||||
<BindingPicker
|
<BindingSidePanel
|
||||||
{bindings}
|
{bindings}
|
||||||
allowHelpers={false}
|
allowHelpers={false}
|
||||||
addBinding={binding =>
|
addBinding={binding =>
|
||||||
|
|
|
@ -65,6 +65,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="root">
|
<div class="root">
|
||||||
<div class="spacer" />
|
<div class="spacer" />
|
||||||
{#each fieldsArray as field}
|
{#each fieldsArray as field}
|
||||||
|
|
|
@ -750,6 +750,8 @@
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
bind:this={confirmDeleteDialog}
|
bind:this={confirmDeleteDialog}
|
||||||
okText="Delete Column"
|
okText="Delete Column"
|
||||||
|
|
|
@ -32,6 +32,7 @@ vi.mock("svelte", async () => {
|
||||||
},
|
},
|
||||||
createEventDispatcher: vi.fn(),
|
createEventDispatcher: vi.fn(),
|
||||||
onDestroy: vi.fn(),
|
onDestroy: vi.fn(),
|
||||||
|
tick: vi.fn(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
export let indented
|
export let indented
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class:indented class:selected on:click class={className}>
|
<div class:indented class:selected on:click class={className}>
|
||||||
<i class={icon} />
|
<i class={icon} />
|
||||||
<span>{title}</span>
|
<span>{title}</span>
|
||||||
|
|
|
@ -237,6 +237,8 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:keydown={onKeyDown} />
|
<svelte:window on:keydown={onKeyDown} />
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<ModalContent
|
<ModalContent
|
||||||
size="L"
|
size="L"
|
||||||
showCancelButton={false}
|
showCancelButton={false}
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
$: actionDefined = typeof action === "function"
|
$: actionDefined = typeof action === "function"
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="dash-card">
|
<div class="dash-card">
|
||||||
<div class="dash-card-header" class:active={actionDefined} on:click={action}>
|
<div class="dash-card-header" class:active={actionDefined} on:click={action}>
|
||||||
<span class="dash-card-title">
|
<span class="dash-card-title">
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="dropdown-container" on:click>
|
<div class="dropdown-container" on:click>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="dropdown-item" class:disabled on:click {...$$restProps}>
|
<div class="dropdown-item" class:disabled on:click {...$$restProps}>
|
||||||
{#if icon}<i class={icon} />{/if}
|
{#if icon}<i class={icon} />{/if}
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
let modal
|
let modal
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="editable-icon">
|
<div class="editable-icon">
|
||||||
<div class="hover" on:click={modal.show}>
|
<div class="hover" on:click={modal.show}>
|
||||||
<Icon name="Edit" {size} color="var(--spectrum-global-color-gray-600)" />
|
<Icon name="Edit" {size} color="var(--spectrum-global-color-gray-600)" />
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<script>
|
<script>
|
||||||
import FontAwesomeIcon from "./FontAwesomeIcon.svelte"
|
import FontAwesomeIcon from "./FontAwesomeIcon.svelte"
|
||||||
import { Popover, Heading, Body } from "@budibase/bbui"
|
import { Popover, Heading, Body } from "@budibase/bbui"
|
||||||
import { licensing } from "stores/portal"
|
|
||||||
import { isEnabled, TENANT_FEATURE_FLAGS } from "helpers/featureFlags"
|
import { isEnabled, TENANT_FEATURE_FLAGS } from "helpers/featureFlags"
|
||||||
|
import { licensing } from "stores/portal"
|
||||||
|
import { isPremiumOrAbove } from "helpers/planTitle"
|
||||||
|
|
||||||
$: isBusinessAndAbove =
|
$: premiumOrAboveLicense = isPremiumOrAbove($licensing?.license.plan.type)
|
||||||
$licensing.isBusinessPlan || $licensing.isEnterprisePlan
|
|
||||||
|
|
||||||
let show
|
let show
|
||||||
let hide
|
let hide
|
||||||
|
@ -56,22 +56,25 @@
|
||||||
<div class="divider" />
|
<div class="divider" />
|
||||||
{#if isEnabled(TENANT_FEATURE_FLAGS.LICENSING)}
|
{#if isEnabled(TENANT_FEATURE_FLAGS.LICENSING)}
|
||||||
<a
|
<a
|
||||||
href={isBusinessAndAbove
|
href={premiumOrAboveLicense
|
||||||
? "mailto:support@budibase.com"
|
? "mailto:support@budibase.com"
|
||||||
: "/builder/portal/account/usage"}
|
: "/builder/portal/account/usage"}
|
||||||
>
|
>
|
||||||
<div class="premiumLinkContent" class:disabled={!isBusinessAndAbove}>
|
<div
|
||||||
|
class="premiumLinkContent"
|
||||||
|
class:disabled={!premiumOrAboveLicense}
|
||||||
|
>
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<FontAwesomeIcon name="fa-solid fa-envelope" />
|
<FontAwesomeIcon name="fa-solid fa-envelope" />
|
||||||
</div>
|
</div>
|
||||||
<Body size="S">Email support</Body>
|
<Body size="S">Email support</Body>
|
||||||
</div>
|
</div>
|
||||||
{#if !isBusinessAndAbove}
|
{#if !premiumOrAboveLicense}
|
||||||
<div class="premiumBadge">
|
<div class="premiumBadge">
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<FontAwesomeIcon name="fa-solid fa-lock" />
|
<FontAwesomeIcon name="fa-solid fa-lock" />
|
||||||
</div>
|
</div>
|
||||||
<Body size="XS">Business</Body>
|
<Body size="XS">Premium</Body>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<svg
|
<svg
|
||||||
on:click
|
on:click
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 566 B |
|
@ -39,6 +39,7 @@
|
||||||
|
|
||||||
<svelte:window on:keydown={onKeyDown} />
|
<svelte:window on:keydown={onKeyDown} />
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="header" class:search>
|
<div class="header" class:search>
|
||||||
<input
|
<input
|
||||||
readonly={!search}
|
readonly={!search}
|
||||||
|
|
|
@ -60,6 +60,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div
|
<div
|
||||||
class="nav-item"
|
class="nav-item"
|
||||||
class:hovering
|
class:hovering
|
||||||
|
|
|
@ -57,6 +57,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="control" class:disabled>
|
<div class="control" class:disabled>
|
||||||
<Combobox
|
<Combobox
|
||||||
{label}
|
{label}
|
||||||
|
|
|
@ -61,6 +61,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="control" class:disabled>
|
<div class="control" class:disabled>
|
||||||
<Input
|
<Input
|
||||||
{label}
|
{label}
|
||||||
|
|
|
@ -124,6 +124,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="control" class:disabled>
|
<div class="control" class:disabled>
|
||||||
{#if !isValid(value)}
|
{#if !isValid(value)}
|
||||||
<Input
|
<Input
|
||||||
|
|
|
@ -149,6 +149,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="action-top-nav">
|
<div class="action-top-nav">
|
||||||
<div class="action-buttons">
|
<div class="action-buttons">
|
||||||
{#if updateAvailable && $isOnlyUser}
|
{#if updateAvailable && $isOnlyUser}
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
$: customTitleContent = $$slots["panel-title-content"]
|
$: customTitleContent = $$slots["panel-title-content"]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div
|
<div
|
||||||
class="panel"
|
class="panel"
|
||||||
class:wide
|
class:wide
|
||||||
|
|
|
@ -249,6 +249,9 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-no-noninteractive-element-interactions-->
|
||||||
<DrawerContent>
|
<DrawerContent>
|
||||||
<Layout noPadding gap="S" slot="sidebar">
|
<Layout noPadding gap="S" slot="sidebar">
|
||||||
{#if showAvailableActions || !actions?.length}
|
{#if showAvailableActions || !actions?.length}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<script>
|
<script>
|
||||||
import { Label, Select, Body, Multiselect } from "@budibase/bbui"
|
import { Label, Select, Body } from "@budibase/bbui"
|
||||||
import { findAllMatchingComponents, findComponent } from "helpers/components"
|
|
||||||
import { selectedScreen } from "stores/builder"
|
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { getDatasourceForProvider, getSchemaForDatasource } from "dataBinding"
|
import ColumnEditor from "../../ColumnEditor/ColumnEditor.svelte"
|
||||||
|
import { findAllMatchingComponents } from "helpers/components"
|
||||||
|
import { selectedScreen } from "stores/builder"
|
||||||
|
|
||||||
export let parameters
|
export let parameters
|
||||||
|
|
||||||
|
@ -18,37 +18,65 @@
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const DELIMITERS = [
|
||||||
|
{
|
||||||
|
label: ",",
|
||||||
|
value: ",",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: ";",
|
||||||
|
value: ";",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: ":",
|
||||||
|
value: ":",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "|",
|
||||||
|
value: "|",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "~",
|
||||||
|
value: "~",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "[tab]",
|
||||||
|
value: "\t",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "[space]",
|
||||||
|
value: " ",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
$: tables = findAllMatchingComponents($selectedScreen?.props, component =>
|
$: tables = findAllMatchingComponents($selectedScreen?.props, component =>
|
||||||
component._component.endsWith("table")
|
component._component.endsWith("table")
|
||||||
).map(table => ({
|
)
|
||||||
label: table._instanceName,
|
|
||||||
value: table._id,
|
|
||||||
}))
|
|
||||||
$: tableBlocks = findAllMatchingComponents(
|
$: tableBlocks = findAllMatchingComponents(
|
||||||
$selectedScreen?.props,
|
$selectedScreen?.props,
|
||||||
component => component._component.endsWith("tableblock")
|
component => component._component.endsWith("tableblock")
|
||||||
).map(block => ({
|
)
|
||||||
label: block._instanceName,
|
$: components = tables.concat(tableBlocks)
|
||||||
value: `${block._id}-table`,
|
$: componentOptions = components.map(table => ({
|
||||||
|
label: table._instanceName,
|
||||||
|
value: table._component.includes("tableblock")
|
||||||
|
? `${table._id}-table`
|
||||||
|
: table._id,
|
||||||
}))
|
}))
|
||||||
$: componentOptions = tables.concat(tableBlocks)
|
$: selectedTableId = parameters.tableComponentId?.includes("-")
|
||||||
$: columnOptions = getColumnOptions(parameters.tableComponentId)
|
? parameters.tableComponentId.split("-")[0]
|
||||||
|
: parameters.tableComponentId
|
||||||
const getColumnOptions = tableId => {
|
$: selectedTable = components.find(
|
||||||
// Strip block suffix if block component
|
component => component._id === selectedTableId
|
||||||
if (tableId?.includes("-")) {
|
)
|
||||||
tableId = tableId.split("-")[0]
|
|
||||||
}
|
|
||||||
const selectedTable = findComponent($selectedScreen?.props, tableId)
|
|
||||||
const datasource = getDatasourceForProvider($selectedScreen, selectedTable)
|
|
||||||
const { schema } = getSchemaForDatasource($selectedScreen, datasource)
|
|
||||||
return Object.keys(schema || {})
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (!parameters.type) {
|
if (!parameters.type) {
|
||||||
parameters.type = "csv"
|
parameters.type = "csv"
|
||||||
}
|
}
|
||||||
|
if (!parameters.delimiter) {
|
||||||
|
parameters.delimiter = ","
|
||||||
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -67,13 +95,30 @@
|
||||||
options={componentOptions}
|
options={componentOptions}
|
||||||
on:change={() => (parameters.columns = [])}
|
on:change={() => (parameters.columns = [])}
|
||||||
/>
|
/>
|
||||||
|
<span />
|
||||||
<Label small>Export as</Label>
|
<Label small>Export as</Label>
|
||||||
<Select bind:value={parameters.type} options={FORMATS} />
|
<Select bind:value={parameters.type} options={FORMATS} />
|
||||||
|
<Select
|
||||||
|
bind:value={parameters.delimiter}
|
||||||
|
placeholder={null}
|
||||||
|
options={DELIMITERS}
|
||||||
|
disabled={parameters.type !== "csv"}
|
||||||
|
/>
|
||||||
<Label small>Export columns</Label>
|
<Label small>Export columns</Label>
|
||||||
<Multiselect
|
<ColumnEditor
|
||||||
placeholder="All columns"
|
value={parameters.columns}
|
||||||
bind:value={parameters.columns}
|
allowCellEditing={false}
|
||||||
options={columnOptions}
|
componentInstance={selectedTable}
|
||||||
|
on:change={e => {
|
||||||
|
const columns = e.detail
|
||||||
|
parameters.columns = columns
|
||||||
|
parameters.customHeaders = columns.reduce((headerMap, column) => {
|
||||||
|
return {
|
||||||
|
[column.name]: column.displayName,
|
||||||
|
...headerMap,
|
||||||
|
}
|
||||||
|
}, {})
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -97,8 +142,8 @@
|
||||||
.params {
|
.params {
|
||||||
display: grid;
|
display: grid;
|
||||||
column-gap: var(--spacing-xs);
|
column-gap: var(--spacing-xs);
|
||||||
row-gap: var(--spacing-s);
|
row-gap: var(--spacing-m);
|
||||||
grid-template-columns: 90px 1fr;
|
grid-template-columns: 90px 1fr 90px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -95,6 +95,8 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="button-configuration">
|
<div class="button-configuration">
|
||||||
{#if buttonCount}
|
{#if buttonCount}
|
||||||
<DraggableList
|
<DraggableList
|
||||||
|
|
|
@ -85,6 +85,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<DrawerContent>
|
<DrawerContent>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Layout noPadding gap="S">
|
<Layout noPadding gap="S">
|
||||||
|
|
|
@ -29,6 +29,12 @@
|
||||||
allowLinks: true,
|
allowLinks: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
$: {
|
||||||
|
value = (value || []).filter(
|
||||||
|
column => (schema || {})[column.name || column] !== undefined
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const getText = value => {
|
const getText = value => {
|
||||||
if (!value?.length) {
|
if (!value?.length) {
|
||||||
return "All columns"
|
return "All columns"
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
<Heading size="XS">{heading}</Heading>
|
<Heading size="XS">{heading}</Heading>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<ul class="spectrum-Menu" role="listbox">
|
<ul class="spectrum-Menu" role="listbox">
|
||||||
{#each dataSet as data}
|
{#each dataSet as data}
|
||||||
<li
|
<li
|
||||||
|
|
|
@ -127,10 +127,14 @@
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
$: jsonArrays = bindings
|
$: jsonArrays = bindings
|
||||||
.filter(x => x.fieldSchema?.type === "jsonarray")
|
.filter(
|
||||||
|
x =>
|
||||||
|
x.fieldSchema?.type === "jsonarray" ||
|
||||||
|
(x.fieldSchema?.type === "json" && x.fieldSchema?.subtype === "array")
|
||||||
|
)
|
||||||
.map(binding => {
|
.map(binding => {
|
||||||
const { providerId, readableBinding, runtimeBinding, tableId } = binding
|
const { providerId, readableBinding, runtimeBinding, tableId } = binding
|
||||||
const { name, type, prefixKeys } = binding.fieldSchema
|
const { name, type, prefixKeys, subtype } = binding.fieldSchema
|
||||||
return {
|
return {
|
||||||
providerId,
|
providerId,
|
||||||
label: readableBinding,
|
label: readableBinding,
|
||||||
|
@ -138,7 +142,8 @@
|
||||||
fieldType: type,
|
fieldType: type,
|
||||||
tableId,
|
tableId,
|
||||||
prefixKeys,
|
prefixKeys,
|
||||||
type: "jsonarray",
|
type: type === "jsonarray" ? "jsonarray" : "queryarray",
|
||||||
|
subtype,
|
||||||
value: `{{ literal ${runtimeBinding} }}`,
|
value: `{{ literal ${runtimeBinding} }}`,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -80,6 +80,9 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-no-noninteractive-element-interactions-->
|
||||||
<ul
|
<ul
|
||||||
class="list-wrap"
|
class="list-wrap"
|
||||||
use:dndzone={{
|
use:dndzone={{
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
$: useIcon = !!icon
|
$: useIcon = !!icon
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
<div class="flatbutton" class:selected on:click={() => onClick(value || text)}>
|
<div class="flatbutton" class:selected on:click={() => onClick(value || text)}>
|
||||||
{#if useIcon}
|
{#if useIcon}
|
||||||
<i class={icon} />
|
<i class={icon} />
|
||||||
|
|
|
@ -1,15 +1,25 @@
|
||||||
<script>
|
<script>
|
||||||
import EditComponentPopover from "../EditComponentPopover.svelte"
|
import EditComponentPopover from "../EditComponentPopover.svelte"
|
||||||
import { FieldTypeToComponentMap } from "../FieldConfiguration/utils"
|
|
||||||
import { Toggle, Icon } from "@budibase/bbui"
|
import { Toggle, Icon } from "@budibase/bbui"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { componentStore } from "stores/builder"
|
import { FIELDS } from "constants/backend"
|
||||||
|
|
||||||
export let item
|
export let item
|
||||||
export let anchor
|
export let anchor
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
$: fieldIconLookupMap = buildFieldIconLookupMap(FIELDS)
|
||||||
|
|
||||||
|
const buildFieldIconLookupMap = fields => {
|
||||||
|
let map = {}
|
||||||
|
Object.values(fields).forEach(fieldInfo => {
|
||||||
|
map[fieldInfo.type] = fieldInfo.icon
|
||||||
|
})
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
const onToggle = item => {
|
const onToggle = item => {
|
||||||
return e => {
|
return e => {
|
||||||
item.active = e.detail
|
item.active = e.detail
|
||||||
|
@ -24,13 +34,6 @@
|
||||||
return { ...setting, nested: true }
|
return { ...setting, nested: true }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const getIcon = () => {
|
|
||||||
const component = `@budibase/standard-components/${
|
|
||||||
FieldTypeToComponentMap[item.columnType]
|
|
||||||
}`
|
|
||||||
return componentStore.getDefinition(component).icon
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="list-item-body">
|
<div class="list-item-body">
|
||||||
|
@ -42,7 +45,7 @@
|
||||||
on:change
|
on:change
|
||||||
>
|
>
|
||||||
<div slot="header" class="type-icon">
|
<div slot="header" class="type-icon">
|
||||||
<Icon name={getIcon()} />
|
<Icon name={fieldIconLookupMap[item.columnType]} />
|
||||||
<span>{item.field}</span>
|
<span>{item.field}</span>
|
||||||
</div>
|
</div>
|
||||||
</EditComponentPopover>
|
</EditComponentPopover>
|
||||||
|
|
|
@ -121,6 +121,8 @@
|
||||||
{displayValue}
|
{displayValue}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<Popover bind:this={dropdown} on:open={setSelectedUI} anchor={buttonAnchor}>
|
<Popover bind:this={dropdown} on:open={setSelectedUI} anchor={buttonAnchor}>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="search-area">
|
<div class="search-area">
|
||||||
|
|
|
@ -85,6 +85,16 @@
|
||||||
activity = newActivity
|
activity = newActivity
|
||||||
dispatch("change", fields)
|
dispatch("change", fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isJsonArray(value) {
|
||||||
|
if (!value || typeof value === "string") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (value.type === "array") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return value.type === "json" && value.subtype === "array"
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Builds Objects with Key Value Pairs. Useful for building things like Request Headers. -->
|
<!-- Builds Objects with Key Value Pairs. Useful for building things like Request Headers. -->
|
||||||
|
@ -112,7 +122,9 @@
|
||||||
bind:value={field.name}
|
bind:value={field.name}
|
||||||
on:blur={changed}
|
on:blur={changed}
|
||||||
/>
|
/>
|
||||||
{#if options}
|
{#if isJsonArray(field.value)}
|
||||||
|
<Select readonly={true} value="Array" options={["Array"]} />
|
||||||
|
{:else if options}
|
||||||
<Select
|
<Select
|
||||||
bind:value={field.value}
|
bind:value={field.value}
|
||||||
{compare}
|
{compare}
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
let schemaType
|
let schemaType
|
||||||
|
|
||||||
let autoSchema = {}
|
let autoSchema = {}
|
||||||
|
let nestedSchemaFields = {}
|
||||||
let rows = []
|
let rows = []
|
||||||
let keys = {}
|
let keys = {}
|
||||||
|
|
||||||
|
@ -83,13 +84,14 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nestedSchemaFields = response.nestedSchemaFields
|
||||||
|
|
||||||
if (Object.keys(newQuery.schema).length === 0) {
|
if (Object.keys(newQuery.schema).length === 0) {
|
||||||
// Assign this to a variable instead of directly to the newQuery.schema so that a user
|
// Assign this to a variable instead of directly to the newQuery.schema so that a user
|
||||||
// can change the table they're querying and have the schema update until they first
|
// can change the table they're querying and have the schema update until they first
|
||||||
// edit it
|
// edit it
|
||||||
autoSchema = response.schema
|
autoSchema = response.schema
|
||||||
}
|
}
|
||||||
|
|
||||||
rows = response.rows
|
rows = response.rows
|
||||||
|
|
||||||
notifications.success("Query executed successfully")
|
notifications.success("Query executed successfully")
|
||||||
|
@ -120,6 +122,7 @@
|
||||||
Object.keys(newQuery.schema).length === 0
|
Object.keys(newQuery.schema).length === 0
|
||||||
? autoSchema
|
? autoSchema
|
||||||
: newQuery.schema,
|
: newQuery.schema,
|
||||||
|
nestedSchemaFields,
|
||||||
})
|
})
|
||||||
|
|
||||||
notifications.success("Query saved successfully")
|
notifications.success("Query saved successfully")
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
let authConfigId
|
let authConfigId
|
||||||
let dynamicVariables, addVariableModal, varBinding, globalDynamicBindings
|
let dynamicVariables, addVariableModal, varBinding, globalDynamicBindings
|
||||||
let restBindings = getRestBindings()
|
let restBindings = getRestBindings()
|
||||||
|
let nestedSchemaFields = {}
|
||||||
|
|
||||||
$: staticVariables = datasource?.config?.staticVariables || {}
|
$: staticVariables = datasource?.config?.staticVariables || {}
|
||||||
|
|
||||||
|
@ -160,6 +161,7 @@
|
||||||
newQuery.fields.authConfigId = authConfigId
|
newQuery.fields.authConfigId = authConfigId
|
||||||
newQuery.fields.disabledHeaders = restUtils.flipHeaderState(enabledHeaders)
|
newQuery.fields.disabledHeaders = restUtils.flipHeaderState(enabledHeaders)
|
||||||
newQuery.schema = schema || {}
|
newQuery.schema = schema || {}
|
||||||
|
newQuery.nestedSchemaFields = nestedSchemaFields || {}
|
||||||
|
|
||||||
return newQuery
|
return newQuery
|
||||||
}
|
}
|
||||||
|
@ -238,6 +240,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
schema = response.schema
|
schema = response.schema
|
||||||
|
nestedSchemaFields = response.nestedSchemaFields
|
||||||
notifications.success("Request sent successfully")
|
notifications.success("Request sent successfully")
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
Label,
|
Label,
|
||||||
Input,
|
Input,
|
||||||
Select,
|
Select,
|
||||||
Divider,
|
|
||||||
Layout,
|
Layout,
|
||||||
Icon,
|
Icon,
|
||||||
Button,
|
Button,
|
||||||
|
@ -124,7 +123,6 @@
|
||||||
{#each query.fields.steps ?? [] as step, index}
|
{#each query.fields.steps ?? [] as step, index}
|
||||||
<div class="block">
|
<div class="block">
|
||||||
<div class="subblock">
|
<div class="subblock">
|
||||||
<Divider noMargin />
|
|
||||||
<div class="blockSection">
|
<div class="blockSection">
|
||||||
<div class="block-options">
|
<div class="block-options">
|
||||||
Stage {index + 1}
|
Stage {index + 1}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||||
|
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||||
<div class="side-nav-item">
|
<div class="side-nav-item">
|
||||||
{#if url}
|
{#if url}
|
||||||
<a class="text" on:click href={url} class:active class:disabled>
|
<a class="text" on:click href={url} class:active class:disabled>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue