Merge branch 'develop' of github.com:Budibase/budibase into new-design-ui
This commit is contained in:
commit
748a7de1ec
59 changed files with 1469 additions and 687 deletions
17
.github/workflows/budibase_ci.yml
vendored
17
.github/workflows/budibase_ci.yml
vendored
|
@ -7,6 +7,7 @@ on:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- develop
|
- develop
|
||||||
|
- new-design-ui
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
@ -59,3 +60,19 @@ jobs:
|
||||||
with:
|
with:
|
||||||
install: false
|
install: false
|
||||||
command: yarn test:e2e:ci
|
command: yarn test:e2e:ci
|
||||||
|
|
||||||
|
- name: Configure AWS Credentials
|
||||||
|
uses: aws-actions/configure-aws-credentials@v1
|
||||||
|
with:
|
||||||
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
aws-region: eu-west-1
|
||||||
|
|
||||||
|
- name: Upload to S3
|
||||||
|
if: github.ref == 'refs/heads/new-design-ui'
|
||||||
|
run: |
|
||||||
|
tar -czvf new_ui.tar.gz packages/server/assets packages/server/index.html
|
||||||
|
aws s3 cp new_ui.tar.gz s3://prod-budi-app-assets/beta:design_ui/
|
||||||
|
aws s3 cp packages/client/dist/budibase-client.js s3://prod-budi-app-assets/beta:design_ui/budibase-client.js
|
||||||
|
aws cloudfront create-invalidation --distribution-id E3ELKP4RCEHVLW --paths "/beta:design_ui/*"
|
||||||
|
|
||||||
|
|
1
.github/workflows/release-develop.yml
vendored
1
.github/workflows/release-develop.yml
vendored
|
@ -22,6 +22,7 @@ env:
|
||||||
POSTHOG_TOKEN: phc_uDYOfnFt6wAbBAXkC6STjcrTpAFiWIhqgFcsC1UVO5F
|
POSTHOG_TOKEN: phc_uDYOfnFt6wAbBAXkC6STjcrTpAFiWIhqgFcsC1UVO5F
|
||||||
INTERCOM_TOKEN: ${{ secrets.INTERCOM_TOKEN }}
|
INTERCOM_TOKEN: ${{ secrets.INTERCOM_TOKEN }}
|
||||||
PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
|
PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
|
||||||
|
FEATURE_PREVIEW_URL: https://budirelease.live
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
|
|
|
@ -103,7 +103,7 @@ globals:
|
||||||
google:
|
google:
|
||||||
clientId: ""
|
clientId: ""
|
||||||
secret: ""
|
secret: ""
|
||||||
automationMaxIterations: "500"
|
automationMaxIterations: "200"
|
||||||
|
|
||||||
createSecrets: true # creates an internal API key, JWT secrets and redis password for you
|
createSecrets: true # creates an internal API key, JWT secrets and redis password for you
|
||||||
|
|
||||||
|
|
|
@ -10,15 +10,14 @@ certbot certonly --webroot --webroot-path="/var/www/html" \
|
||||||
|
|
||||||
if (($? != 0)); then
|
if (($? != 0)); then
|
||||||
echo "ERROR: certbot request failed for $CUSTOM_DOMAIN use http on port 80 - exiting"
|
echo "ERROR: certbot request failed for $CUSTOM_DOMAIN use http on port 80 - exiting"
|
||||||
nginx -s stop
|
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
cp /app/letsencrypt/options-ssl-nginx.conf /etc/letsencrypt/options-ssl-nginx.conf
|
cp /app/letsencrypt/options-ssl-nginx.conf /etc/letsencrypt/options-ssl-nginx.conf
|
||||||
cp /app/letsencrypt/ssl-dhparams.pem /etc/letsencrypt/ssl-dhparams.pem
|
cp /app/letsencrypt/ssl-dhparams.pem /etc/letsencrypt/ssl-dhparams.pem
|
||||||
cp /app/letsencrypt/nginx-ssl.conf /etc/nginx/sites-available/nginx-ssl.conf
|
cp /app/letsencrypt/nginx-ssl.conf /etc/nginx/sites-available/nginx-ssl.conf
|
||||||
sed -i 's/CUSTOM_DOMAIN/$CUSTOM_DOMAIN/g' /etc/nginx/sites-available/nginx-ssl.conf
|
sed -i "s/CUSTOM_DOMAIN/$CUSTOM_DOMAIN/g" /etc/nginx/sites-available/nginx-ssl.conf
|
||||||
ln -s /etc/nginx/sites-available/nginx-ssl.conf /etc/nginx/sites-enabled/nginx-ssl.conf
|
ln -s /etc/nginx/sites-available/nginx-ssl.conf /etc/nginx/sites-enabled/nginx-ssl.conf
|
||||||
|
|
||||||
echo "INFO: restart nginx after certbot request"
|
echo "INFO: restart nginx after certbot request"
|
||||||
nginx -s reload
|
/etc/init.d/nginx restart
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -6,6 +6,7 @@ server {
|
||||||
ssl_certificate_key /etc/letsencrypt/live/CUSTOM_DOMAIN/privkey.pem;
|
ssl_certificate_key /etc/letsencrypt/live/CUSTOM_DOMAIN/privkey.pem;
|
||||||
include /etc/letsencrypt/options-ssl-nginx.conf;
|
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||||
|
|
||||||
client_max_body_size 1000m;
|
client_max_body_size 1000m;
|
||||||
ignore_invalid_headers off;
|
ignore_invalid_headers off;
|
||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
|
@ -91,4 +92,5 @@ server {
|
||||||
gzip_proxied any;
|
gzip_proxied any;
|
||||||
gzip_comp_level 6;
|
gzip_comp_level 6;
|
||||||
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
|
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
17
hosting/scripts/build-target-paths.sh
Normal file
17
hosting/scripts/build-target-paths.sh
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo ${TARGETBUILD} > /buildtarget.txt
|
||||||
|
if [[ "${TARGETBUILD}" = "aas" ]]; then
|
||||||
|
# Azure AppService uses /home for persisent data & SSH on port 2222
|
||||||
|
mkdir -p /home/budibase/{minio,couchdb}
|
||||||
|
mkdir -p /home/budibase/couchdb/data
|
||||||
|
chown -R couchdb:couchdb /home/budibase/couchdb/
|
||||||
|
apt update
|
||||||
|
apt-get install -y openssh-server
|
||||||
|
sed -i 's#dir=/opt/couchdb/data/search#dir=/home/budibase/couchdb/data/search#' /opt/clouseau/clouseau.ini
|
||||||
|
sed -i 's#/minio/minio server /minio &#/minio/minio server /home/budibase/minio &#' /runner.sh
|
||||||
|
sed -i 's#database_dir = ./data#database_dir = /home/budibase/couchdb/data#' /opt/couchdb/etc/default.ini
|
||||||
|
sed -i 's#view_index_dir = ./data#view_index_dir = /home/budibase/couchdb/data#' /opt/couchdb/etc/default.ini
|
||||||
|
sed -i "s/#Port 22/Port 2222/" /etc/ssh/sshd_config
|
||||||
|
/etc/init.d/ssh restart
|
||||||
|
fi
|
|
@ -25,6 +25,13 @@ if [[ $(redis-cli -a $REDIS_PASSWORD --no-auth-warning ping) != 'PONG' ]]; then
|
||||||
healthy=false
|
healthy=false
|
||||||
fi
|
fi
|
||||||
# mino, clouseau,
|
# mino, clouseau,
|
||||||
|
nginx -t -q
|
||||||
|
NGINX_STATUS=$?
|
||||||
|
|
||||||
|
if [[ $NGINX_STATUS -gt 0 ]]; then
|
||||||
|
echo 'ERROR: Nginx config problem';
|
||||||
|
healthy=false
|
||||||
|
fi
|
||||||
|
|
||||||
if [ $healthy == true ]; then
|
if [ $healthy == true ]; then
|
||||||
exit 0
|
exit 0
|
||||||
|
|
|
@ -19,8 +19,12 @@ ADD packages/worker .
|
||||||
RUN node /pinVersions.js && yarn && yarn build && /cleanup.sh
|
RUN node /pinVersions.js && yarn && yarn build && /cleanup.sh
|
||||||
|
|
||||||
FROM couchdb:3.2.1
|
FROM couchdb:3.2.1
|
||||||
|
# TARGETARCH can be amd64 or arm e.g. docker build --build-arg TARGETARCH=amd64
|
||||||
ARG TARGETARCH amd64
|
ARG TARGETARCH amd64
|
||||||
|
#TARGETBUILD can be set to single (for single docker image) or aas (for azure app service)
|
||||||
|
# e.g. docker build --build-arg TARGETBUILD=aas ....
|
||||||
|
ARG TARGETBUILD single
|
||||||
|
ENV TARGETBUILD $TARGETBUILD
|
||||||
|
|
||||||
COPY --from=build /app /app
|
COPY --from=build /app /app
|
||||||
COPY --from=build /worker /worker
|
COPY --from=build /worker /worker
|
||||||
|
@ -33,7 +37,7 @@ ENV \
|
||||||
COUCHDB_PASSWORD=budibase \
|
COUCHDB_PASSWORD=budibase \
|
||||||
COUCHDB_USER=budibase \
|
COUCHDB_USER=budibase \
|
||||||
COUCH_DB_URL=http://budibase:budibase@localhost:5984 \
|
COUCH_DB_URL=http://budibase:budibase@localhost:5984 \
|
||||||
CUSTOM_DOMAIN=budi001.custom.com \
|
# CUSTOM_DOMAIN=budi001.custom.com \
|
||||||
DEPLOYMENT_ENVIRONMENT=docker \
|
DEPLOYMENT_ENVIRONMENT=docker \
|
||||||
INTERNAL_API_KEY=budibase \
|
INTERNAL_API_KEY=budibase \
|
||||||
JWT_SECRET=testsecret \
|
JWT_SECRET=testsecret \
|
||||||
|
@ -44,6 +48,7 @@ ENV \
|
||||||
REDIS_PASSWORD=budibase \
|
REDIS_PASSWORD=budibase \
|
||||||
REDIS_URL=localhost:6379 \
|
REDIS_URL=localhost:6379 \
|
||||||
SELF_HOSTED=1 \
|
SELF_HOSTED=1 \
|
||||||
|
TARGETBUILD=$TARGETBUILD \
|
||||||
WORKER_PORT=4002 \
|
WORKER_PORT=4002 \
|
||||||
WORKER_URL=http://localhost:4002
|
WORKER_URL=http://localhost:4002
|
||||||
|
|
||||||
|
@ -62,6 +67,7 @@ RUN curl -sL https://deb.nodesource.com/setup_16.x -o /tmp/nodesource_setup.sh &
|
||||||
|
|
||||||
# setup nginx
|
# setup nginx
|
||||||
ADD hosting/single/nginx.conf /etc/nginx
|
ADD hosting/single/nginx.conf /etc/nginx
|
||||||
|
ADD hosting/single/nginx-default-site.conf /etc/nginx/sites-enabled/default
|
||||||
RUN mkdir -p /var/log/nginx && \
|
RUN mkdir -p /var/log/nginx && \
|
||||||
touch /var/log/nginx/error.log && \
|
touch /var/log/nginx/error.log && \
|
||||||
touch /var/run/nginx.pid
|
touch /var/run/nginx.pid
|
||||||
|
@ -100,6 +106,12 @@ RUN chmod +x ./runner.sh
|
||||||
ADD hosting/scripts/healthcheck.sh .
|
ADD hosting/scripts/healthcheck.sh .
|
||||||
RUN chmod +x ./healthcheck.sh
|
RUN chmod +x ./healthcheck.sh
|
||||||
|
|
||||||
|
ADD hosting/scripts/build-target-paths.sh .
|
||||||
|
RUN chmod +x ./build-target-paths.sh
|
||||||
|
|
||||||
|
# For Azure App Service install SSH & point data locations to /home
|
||||||
|
RUN /build-target-paths.sh
|
||||||
|
|
||||||
# cleanup cache
|
# cleanup cache
|
||||||
RUN yarn cache clean -f
|
RUN yarn cache clean -f
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
As an alternative to running several docker containers via docker-compose, the files under ./hosting/single can be used to build a docker image containing all of the Budibase components (minio, couch, clouseau etc).
|
As an alternative to running several docker containers via docker-compose, the files under ./hosting/single can be used to build a docker image containing all of the Budibase components (minio, couch, clouseau etc).
|
||||||
We call this the 'single image' container as the Dockerfile adds all the components to a single docker image.
|
We call this the 'single image' container as the Dockerfile adds all the components to a single docker image.
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
- Amend Environment Variables
|
- Amend Environment Variables
|
||||||
|
@ -22,9 +21,9 @@ If you have other arrangements for a proxy in front of the single image containe
|
||||||
We would suggest building the image with 6GB of RAM and 20GB of free disk space for build artifacts. The resulting image size will use approx 2GB of disk space.
|
We would suggest building the image with 6GB of RAM and 20GB of free disk space for build artifacts. The resulting image size will use approx 2GB of disk space.
|
||||||
|
|
||||||
### Build the Image
|
### Build the Image
|
||||||
The guidance below is based on building the Budibase single image on Debian 11. If you use another distro or OS you will need to amend the commands to suit.
|
The guidance below is based on building the Budibase single image on Debian 11 and AlmaLinux 8. If you use another distro or OS you will need to amend the commands to suit.
|
||||||
Install Node
|
#### Install Node
|
||||||
Budibase requires a recent version of node (14+) than is in the base Debian repos so:
|
Budibase requires a more recent version of node (14+) than is available in the base Debian repos so:
|
||||||
|
|
||||||
```
|
```
|
||||||
curl -sL https://deb.nodesource.com/setup_16.x | sudo bash -
|
curl -sL https://deb.nodesource.com/setup_16.x | sudo bash -
|
||||||
|
@ -35,25 +34,26 @@ Install yarn and lerna:
|
||||||
```
|
```
|
||||||
npm install -g yarn jest lerna
|
npm install -g yarn jest lerna
|
||||||
```
|
```
|
||||||
Install Docker
|
#### Install Docker
|
||||||
|
|
||||||
```
|
```
|
||||||
apt install -y docker.io
|
apt install -y docker.io
|
||||||
apt install -y python3-pip
|
|
||||||
pip3 install docker-compose
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Check the versions of each installed version. This process was tested with the version numbers below so YMMV using anything else:
|
Check the versions of each installed version. This process was tested with the version numbers below so YMMV using anything else:
|
||||||
|
|
||||||
- Docker: 20.10.5
|
- Docker: 20.10.5
|
||||||
- docker-compose: 1.29.2
|
|
||||||
- node: 16.15.1
|
- node: 16.15.1
|
||||||
- yarn: 1.22.19
|
- yarn: 1.22.19
|
||||||
- lerna: 5.1.4
|
- lerna: 5.1.4
|
||||||
|
|
||||||
|
#### Get the Code
|
||||||
Clone the Budibase repo
|
Clone the Budibase repo
|
||||||
```
|
```
|
||||||
git clone https://github.com/Budibase/budibase.git
|
git clone https://github.com/Budibase/budibase.git
|
||||||
cd budibase
|
cd budibase
|
||||||
```
|
```
|
||||||
|
#### Setup Node
|
||||||
Node setup:
|
Node setup:
|
||||||
```
|
```
|
||||||
node ./hosting/scripts/setup.js
|
node ./hosting/scripts/setup.js
|
||||||
|
@ -61,15 +61,20 @@ yarn
|
||||||
yarn bootstrap
|
yarn bootstrap
|
||||||
yarn build
|
yarn build
|
||||||
```
|
```
|
||||||
|
#### Build Image
|
||||||
Build the image from the Dockerfile:
|
The following yarn command does some prep and then runs the docker build command:
|
||||||
|
|
||||||
```
|
```
|
||||||
yarn build:docker:single
|
yarn build:docker:single
|
||||||
```
|
```
|
||||||
If the docker build step fails run that step again manually with:
|
If the docker build step fails try running that step again manually with:
|
||||||
```
|
```
|
||||||
docker build --no-cache -t budibase:latest -f ./hosting/single/Dockerfile .
|
docker build --build-arg TARGETARCH=amd --no-cache -t budibase:latest -f ./hosting/single/Dockerfile .
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Azure App Services
|
||||||
|
Azure have some specific requirements for running a container in their App Service. Specifically, installation of SSH to port 2222 and data storage under /home. If you would like to build a budibase container for Azure App Service add the build argument shown below setting it to 'aas'. You can remove the CUSTOM_DOMAIN env variable from the Dockerfile too as Azure terminate SSL before requests reach the container.
|
||||||
|
```
|
||||||
|
docker build --build-arg TARGETARCH=amd --build-arg TARGETBUILD=aas -t budibase:latest -f ./hosting/single/Dockerfile .
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run the Container
|
### Run the Container
|
||||||
|
@ -85,6 +90,9 @@ When the container runs you should be able to access the container over http at
|
||||||
|
|
||||||
When the Budibase UI appears you will be prompted to create an account to get started.
|
When the Budibase UI appears you will be prompted to create an account to get started.
|
||||||
|
|
||||||
|
### Podman
|
||||||
|
The single image container builds fine when using podman in place of docker. You may be prompted for the registry to use for the CouchDB image and the HEALTHCHECK parameter is not OCI compliant so is ignored.
|
||||||
|
|
||||||
### Check
|
### Check
|
||||||
There are many things that could go wrong so if your container is not building or running as expected please check the following before opening a support issue.
|
There are many things that could go wrong so if your container is not building or running as expected please check the following before opening a support issue.
|
||||||
Verify the healthcheck status of the container:
|
Verify the healthcheck status of the container:
|
||||||
|
@ -96,7 +104,6 @@ Check the container logs:
|
||||||
docker logs budibase
|
docker logs budibase
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Support
|
### Support
|
||||||
This single image build is still a work-in-progress so if you open an issue please provide the following information:
|
This single image build is still a work-in-progress so if you open an issue please provide the following information:
|
||||||
- The OS and OS version you are building on
|
- The OS and OS version you are building on
|
||||||
|
|
94
hosting/single/nginx-default-site.conf
Normal file
94
hosting/single/nginx-default-site.conf
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
listen [::]:80 default_server;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
client_max_body_size 1000m;
|
||||||
|
ignore_invalid_headers off;
|
||||||
|
proxy_buffering off;
|
||||||
|
# port_in_redirect off;
|
||||||
|
|
||||||
|
location ^~ /.well-known/acme-challenge/ {
|
||||||
|
default_type "text/plain";
|
||||||
|
root /var/www/html;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
location = /.well-known/acme-challenge/ {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /app {
|
||||||
|
proxy_pass http://127.0.0.1:4001;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = / {
|
||||||
|
proxy_pass http://127.0.0.1:4001;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ ^/(builder|app_) {
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_pass http://127.0.0.1:4001;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ ^/api/(system|admin|global)/ {
|
||||||
|
proxy_pass http://127.0.0.1:4002;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /worker/ {
|
||||||
|
proxy_pass http://127.0.0.1:4002;
|
||||||
|
rewrite ^/worker/(.*)$ /$1 break;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
# calls to the API are rate limited with bursting
|
||||||
|
limit_req zone=ratelimit burst=20 nodelay;
|
||||||
|
|
||||||
|
# 120s timeout on API requests
|
||||||
|
proxy_read_timeout 120s;
|
||||||
|
proxy_connect_timeout 120s;
|
||||||
|
proxy_send_timeout 120s;
|
||||||
|
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection $connection_upgrade;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
|
||||||
|
proxy_pass http://127.0.0.1:4001;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /db/ {
|
||||||
|
proxy_pass http://127.0.0.1:5984;
|
||||||
|
rewrite ^/db/(.*)$ /$1 break;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_connect_timeout 300;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
chunked_transfer_encoding off;
|
||||||
|
proxy_pass http://127.0.0.1:9000;
|
||||||
|
}
|
||||||
|
|
||||||
|
client_header_timeout 60;
|
||||||
|
client_body_timeout 60;
|
||||||
|
keepalive_timeout 60;
|
||||||
|
|
||||||
|
# gzip
|
||||||
|
gzip on;
|
||||||
|
gzip_vary on;
|
||||||
|
gzip_proxied any;
|
||||||
|
gzip_comp_level 6;
|
||||||
|
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -32,94 +32,6 @@ http {
|
||||||
default "upgrade";
|
default "upgrade";
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
include /etc/nginx/sites-enabled/*;
|
||||||
listen 80 default_server;
|
|
||||||
listen [::]:80 default_server;
|
|
||||||
server_name _;
|
|
||||||
client_max_body_size 1000m;
|
|
||||||
ignore_invalid_headers off;
|
|
||||||
proxy_buffering off;
|
|
||||||
# port_in_redirect off;
|
|
||||||
|
|
||||||
location ^~ /.well-known/acme-challenge/ {
|
|
||||||
default_type "text/plain";
|
|
||||||
root /var/www/html;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
location = /.well-known/acme-challenge/ {
|
|
||||||
return 404;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /app {
|
|
||||||
proxy_pass http://127.0.0.1:4001;
|
|
||||||
}
|
|
||||||
|
|
||||||
location = / {
|
|
||||||
proxy_pass http://127.0.0.1:4001;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~ ^/(builder|app_) {
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Connection $connection_upgrade;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_pass http://127.0.0.1:4001;
|
|
||||||
}
|
|
||||||
|
|
||||||
location ~ ^/api/(system|admin|global)/ {
|
|
||||||
proxy_pass http://127.0.0.1:4002;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /worker/ {
|
|
||||||
proxy_pass http://127.0.0.1:4002;
|
|
||||||
rewrite ^/worker/(.*)$ /$1 break;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /api/ {
|
|
||||||
# calls to the API are rate limited with bursting
|
|
||||||
limit_req zone=ratelimit burst=20 nodelay;
|
|
||||||
|
|
||||||
# 120s timeout on API requests
|
|
||||||
proxy_read_timeout 120s;
|
|
||||||
proxy_connect_timeout 120s;
|
|
||||||
proxy_send_timeout 120s;
|
|
||||||
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Connection $connection_upgrade;
|
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
|
|
||||||
proxy_pass http://127.0.0.1:4001;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /db/ {
|
|
||||||
proxy_pass http://127.0.0.1:5984;
|
|
||||||
rewrite ^/db/(.*)$ /$1 break;
|
|
||||||
}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
proxy_set_header X-Real-IP $remote_addr;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
|
|
||||||
proxy_connect_timeout 300;
|
|
||||||
proxy_http_version 1.1;
|
|
||||||
proxy_set_header Connection "";
|
|
||||||
chunked_transfer_encoding off;
|
|
||||||
proxy_pass http://127.0.0.1:9000;
|
|
||||||
}
|
|
||||||
|
|
||||||
client_header_timeout 60;
|
|
||||||
client_body_timeout 60;
|
|
||||||
keepalive_timeout 60;
|
|
||||||
|
|
||||||
# gzip
|
|
||||||
gzip on;
|
|
||||||
gzip_vary on;
|
|
||||||
gzip_proxied any;
|
|
||||||
gzip_comp_level 6;
|
|
||||||
gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
id=$(docker run -t -d -p 10000:10000 budibase:latest)
|
id=$(docker run -t -d -p 80:80 budibase:latest)
|
||||||
docker exec -it $id bash
|
docker exec -it $id bash
|
||||||
docker kill $id
|
docker kill $id
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "1.0.207-alpha.9",
|
"version": "1.0.212-alpha.5",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -3,5 +3,6 @@ const generic = require("./src/cache/generic")
|
||||||
module.exports = {
|
module.exports = {
|
||||||
user: require("./src/cache/user"),
|
user: require("./src/cache/user"),
|
||||||
app: require("./src/cache/appMetadata"),
|
app: require("./src/cache/appMetadata"),
|
||||||
|
writethrough: require("./src/cache/writethrough"),
|
||||||
...generic,
|
...generic,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/backend-core",
|
"name": "@budibase/backend-core",
|
||||||
"version": "1.0.207-alpha.9",
|
"version": "1.0.212-alpha.5",
|
||||||
"description": "Budibase backend core libraries used in server and worker",
|
"description": "Budibase backend core libraries used in server and worker",
|
||||||
"main": "dist/src/index.js",
|
"main": "dist/src/index.js",
|
||||||
"types": "dist/src/index.d.ts",
|
"types": "dist/src/index.d.ts",
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
"test:watch": "jest --watchAll"
|
"test:watch": "jest --watchAll"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/types": "^1.0.207-alpha.9",
|
"@budibase/types": "^1.0.212-alpha.5",
|
||||||
"@techpass/passport-openidconnect": "0.3.2",
|
"@techpass/passport-openidconnect": "0.3.2",
|
||||||
"aws-sdk": "2.1030.0",
|
"aws-sdk": "2.1030.0",
|
||||||
"bcrypt": "5.0.1",
|
"bcrypt": "5.0.1",
|
||||||
|
@ -63,6 +63,7 @@
|
||||||
"@types/koa": "2.0.52",
|
"@types/koa": "2.0.52",
|
||||||
"@types/node": "14.18.20",
|
"@types/node": "14.18.20",
|
||||||
"@types/node-fetch": "2.6.1",
|
"@types/node-fetch": "2.6.1",
|
||||||
|
"@types/pouchdb": "6.4.0",
|
||||||
"@types/redlock": "4.0.3",
|
"@types/redlock": "4.0.3",
|
||||||
"@types/semver": "7.3.7",
|
"@types/semver": "7.3.7",
|
||||||
"@types/tar-fs": "2.0.1",
|
"@types/tar-fs": "2.0.1",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Client: require("./src/redis"),
|
Client: require("./src/redis"),
|
||||||
utils: require("./src/redis/utils"),
|
utils: require("./src/redis/utils"),
|
||||||
clients: require("./src/redis/authRedis"),
|
clients: require("./src/redis/init"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const redis = require("../redis/authRedis")
|
const redis = require("../redis/init")
|
||||||
const { doWithDB } = require("../db")
|
const { doWithDB } = require("../db")
|
||||||
const { DocumentTypes } = require("../db/constants")
|
const { DocumentTypes } = require("../db/constants")
|
||||||
|
|
||||||
|
|
92
packages/backend-core/src/cache/base/index.ts
vendored
Normal file
92
packages/backend-core/src/cache/base/index.ts
vendored
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
import { getTenantId } from "../../context"
|
||||||
|
import redis from "../../redis/init"
|
||||||
|
import RedisWrapper from "../../redis"
|
||||||
|
|
||||||
|
function generateTenantKey(key: string) {
|
||||||
|
const tenantId = getTenantId()
|
||||||
|
return `${key}:${tenantId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export = class BaseCache {
|
||||||
|
client: RedisWrapper | undefined
|
||||||
|
|
||||||
|
constructor(client: RedisWrapper | undefined = undefined) {
|
||||||
|
this.client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
async getClient() {
|
||||||
|
return !this.client ? await redis.getCacheClient() : this.client
|
||||||
|
}
|
||||||
|
|
||||||
|
async keys(pattern: string) {
|
||||||
|
const client = await this.getClient()
|
||||||
|
return client.keys(pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read only from the cache.
|
||||||
|
*/
|
||||||
|
async get(key: string, opts = { useTenancy: true }) {
|
||||||
|
key = opts.useTenancy ? generateTenantKey(key) : key
|
||||||
|
const client = await this.getClient()
|
||||||
|
return client.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write to the cache.
|
||||||
|
*/
|
||||||
|
async store(
|
||||||
|
key: string,
|
||||||
|
value: any,
|
||||||
|
ttl: number | null = null,
|
||||||
|
opts = { useTenancy: true }
|
||||||
|
) {
|
||||||
|
key = opts.useTenancy ? generateTenantKey(key) : key
|
||||||
|
const client = await this.getClient()
|
||||||
|
await client.store(key, value, ttl)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove from cache.
|
||||||
|
*/
|
||||||
|
async delete(key: string, opts = { useTenancy: true }) {
|
||||||
|
key = opts.useTenancy ? generateTenantKey(key) : key
|
||||||
|
const client = await this.getClient()
|
||||||
|
return client.delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read from the cache. Write to the cache if not exists.
|
||||||
|
*/
|
||||||
|
async withCache(
|
||||||
|
key: string,
|
||||||
|
ttl: number,
|
||||||
|
fetchFn: any,
|
||||||
|
opts = { useTenancy: true }
|
||||||
|
) {
|
||||||
|
const cachedValue = await this.get(key, opts)
|
||||||
|
if (cachedValue) {
|
||||||
|
return cachedValue
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fetchedValue = await fetchFn()
|
||||||
|
|
||||||
|
await this.store(key, fetchedValue, ttl, opts)
|
||||||
|
return fetchedValue
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error fetching before cache - ", err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async bustCache(key: string, opts = { client: null }) {
|
||||||
|
const client = await this.getClient()
|
||||||
|
try {
|
||||||
|
await client.delete(generateTenantKey(key))
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error busting cache - ", err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
packages/backend-core/src/cache/generic.js
vendored
75
packages/backend-core/src/cache/generic.js
vendored
|
@ -1,5 +1,6 @@
|
||||||
const redis = require("../redis/authRedis")
|
const BaseCache = require("./base")
|
||||||
const { getTenantId } = require("../context")
|
|
||||||
|
const GENERIC = new BaseCache()
|
||||||
|
|
||||||
exports.CacheKeys = {
|
exports.CacheKeys = {
|
||||||
CHECKLIST: "checklist",
|
CHECKLIST: "checklist",
|
||||||
|
@ -16,67 +17,13 @@ exports.TTL = {
|
||||||
ONE_DAY: 86400,
|
ONE_DAY: 86400,
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateTenantKey(key) {
|
function performExport(funcName) {
|
||||||
const tenantId = getTenantId()
|
return (...args) => GENERIC[funcName](...args)
|
||||||
return `${key}:${tenantId}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.keys = async pattern => {
|
exports.keys = performExport("keys")
|
||||||
const client = await redis.getCacheClient()
|
exports.get = performExport("get")
|
||||||
return client.keys(pattern)
|
exports.store = performExport("store")
|
||||||
}
|
exports.delete = performExport("delete")
|
||||||
|
exports.withCache = performExport("withCache")
|
||||||
/**
|
exports.bustCache = performExport("bustCache")
|
||||||
* Read only from the cache.
|
|
||||||
*/
|
|
||||||
exports.get = async (key, opts = { useTenancy: true }) => {
|
|
||||||
key = opts.useTenancy ? generateTenantKey(key) : key
|
|
||||||
const client = await redis.getCacheClient()
|
|
||||||
const value = await client.get(key)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write to the cache.
|
|
||||||
*/
|
|
||||||
exports.store = async (key, value, ttl, opts = { useTenancy: true }) => {
|
|
||||||
key = opts.useTenancy ? generateTenantKey(key) : key
|
|
||||||
const client = await redis.getCacheClient()
|
|
||||||
await client.store(key, value, ttl)
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.delete = async (key, opts = { useTenancy: true }) => {
|
|
||||||
key = opts.useTenancy ? generateTenantKey(key) : key
|
|
||||||
const client = await redis.getCacheClient()
|
|
||||||
return client.delete(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read from the cache. Write to the cache if not exists.
|
|
||||||
*/
|
|
||||||
exports.withCache = async (key, ttl, fetchFn, opts = { useTenancy: true }) => {
|
|
||||||
const cachedValue = await exports.get(key, opts)
|
|
||||||
if (cachedValue) {
|
|
||||||
return cachedValue
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const fetchedValue = await fetchFn()
|
|
||||||
|
|
||||||
await exports.store(key, fetchedValue, ttl, opts)
|
|
||||||
return fetchedValue
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Error fetching before cache - ", err)
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.bustCache = async key => {
|
|
||||||
const client = await redis.getCacheClient()
|
|
||||||
try {
|
|
||||||
await client.delete(generateTenantKey(key))
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Error busting cache - ", err)
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
59
packages/backend-core/src/cache/tests/writethrough.spec.js
vendored
Normal file
59
packages/backend-core/src/cache/tests/writethrough.spec.js
vendored
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
require("../../../tests/utilities/TestConfiguration")
|
||||||
|
const { Writethrough } = require("../writethrough")
|
||||||
|
const { dangerousGetDB } = require("../../db")
|
||||||
|
const tk = require("timekeeper")
|
||||||
|
|
||||||
|
const START_DATE = Date.now()
|
||||||
|
tk.freeze(START_DATE)
|
||||||
|
|
||||||
|
const DELAY = 5000
|
||||||
|
|
||||||
|
const db = dangerousGetDB("test")
|
||||||
|
const db2 = dangerousGetDB("test2")
|
||||||
|
const writethrough = new Writethrough(db, DELAY), writethrough2 = new Writethrough(db2, DELAY)
|
||||||
|
|
||||||
|
describe("writethrough", () => {
|
||||||
|
describe("put", () => {
|
||||||
|
let first
|
||||||
|
it("should be able to store, will go to DB", async () => {
|
||||||
|
const response = await writethrough.put({ _id: "test", value: 1 })
|
||||||
|
const output = await db.get(response.id)
|
||||||
|
first = output
|
||||||
|
expect(output.value).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("second put shouldn't update DB", async () => {
|
||||||
|
const response = await writethrough.put({ ...first, value: 2 })
|
||||||
|
const output = await db.get(response.id)
|
||||||
|
expect(first._rev).toBe(output._rev)
|
||||||
|
expect(output.value).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should put it again after delay period", async () => {
|
||||||
|
tk.freeze(START_DATE + DELAY + 1)
|
||||||
|
const response = await writethrough.put({ ...first, value: 3 })
|
||||||
|
const output = await db.get(response.id)
|
||||||
|
expect(response.rev).not.toBe(first._rev)
|
||||||
|
expect(output.value).toBe(3)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("get", () => {
|
||||||
|
it("should be able to retrieve", async () => {
|
||||||
|
const response = await writethrough.get("test")
|
||||||
|
expect(response.value).toBe(3)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("same doc, different databases (tenancy)", () => {
|
||||||
|
it("should be able to two different databases", async () => {
|
||||||
|
const resp1 = await writethrough.put({ _id: "db1", value: "first" })
|
||||||
|
const resp2 = await writethrough2.put({ _id: "db1", value: "second" })
|
||||||
|
expect(resp1.rev).toBeDefined()
|
||||||
|
expect(resp2.rev).toBeDefined()
|
||||||
|
expect((await db.get("db1")).value).toBe("first")
|
||||||
|
expect((await db2.get("db1")).value).toBe("second")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
2
packages/backend-core/src/cache/user.js
vendored
2
packages/backend-core/src/cache/user.js
vendored
|
@ -1,4 +1,4 @@
|
||||||
const redis = require("../redis/authRedis")
|
const redis = require("../redis/init")
|
||||||
const { getTenantId, lookupTenantId, doWithGlobalDB } = require("../tenancy")
|
const { getTenantId, lookupTenantId, doWithGlobalDB } = require("../tenancy")
|
||||||
const env = require("../environment")
|
const env = require("../environment")
|
||||||
const accounts = require("../cloud/accounts")
|
const accounts = require("../cloud/accounts")
|
||||||
|
|
120
packages/backend-core/src/cache/writethrough.ts
vendored
Normal file
120
packages/backend-core/src/cache/writethrough.ts
vendored
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import BaseCache from "./base"
|
||||||
|
import { getWritethroughClient } from "../redis/init"
|
||||||
|
|
||||||
|
const DEFAULT_WRITE_RATE_MS = 10000
|
||||||
|
let CACHE: BaseCache | null = null
|
||||||
|
|
||||||
|
interface CacheItem {
|
||||||
|
doc: any
|
||||||
|
lastWrite: number
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCache() {
|
||||||
|
if (!CACHE) {
|
||||||
|
const client = await getWritethroughClient()
|
||||||
|
CACHE = new BaseCache(client)
|
||||||
|
}
|
||||||
|
return CACHE
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeCacheKey(db: PouchDB.Database, key: string) {
|
||||||
|
return db.name + key
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeCacheItem(doc: any, lastWrite: number | null = null): CacheItem {
|
||||||
|
return { doc, lastWrite: lastWrite || Date.now() }
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function put(
|
||||||
|
db: PouchDB.Database,
|
||||||
|
doc: any,
|
||||||
|
writeRateMs: number = DEFAULT_WRITE_RATE_MS
|
||||||
|
) {
|
||||||
|
const cache = await getCache()
|
||||||
|
const key = doc._id
|
||||||
|
let cacheItem: CacheItem | undefined = await cache.get(makeCacheKey(db, key))
|
||||||
|
const updateDb = !cacheItem || cacheItem.lastWrite < Date.now() - writeRateMs
|
||||||
|
let output = doc
|
||||||
|
if (updateDb) {
|
||||||
|
const writeDb = async (toWrite: any) => {
|
||||||
|
// doc should contain the _id and _rev
|
||||||
|
const response = await db.put(toWrite)
|
||||||
|
output = {
|
||||||
|
...doc,
|
||||||
|
_id: response.id,
|
||||||
|
_rev: response.rev,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await writeDb(doc)
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err.status !== 409) {
|
||||||
|
throw err
|
||||||
|
} else {
|
||||||
|
// get the rev, update over it - this is risky, may change in future
|
||||||
|
const readDoc = await db.get(doc._id)
|
||||||
|
doc._rev = readDoc._rev
|
||||||
|
await writeDb(doc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we are updating the DB then need to set the lastWrite to now
|
||||||
|
cacheItem = makeCacheItem(output, updateDb ? null : cacheItem?.lastWrite)
|
||||||
|
await cache.store(makeCacheKey(db, key), cacheItem)
|
||||||
|
return { ok: true, id: output._id, rev: output._rev }
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get(db: PouchDB.Database, id: string): Promise<any> {
|
||||||
|
const cache = await getCache()
|
||||||
|
const cacheKey = makeCacheKey(db, id)
|
||||||
|
let cacheItem: CacheItem = await cache.get(cacheKey)
|
||||||
|
if (!cacheItem) {
|
||||||
|
const doc = await db.get(id)
|
||||||
|
cacheItem = makeCacheItem(doc)
|
||||||
|
await cache.store(cacheKey, cacheItem)
|
||||||
|
}
|
||||||
|
return cacheItem.doc
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function remove(
|
||||||
|
db: PouchDB.Database,
|
||||||
|
docOrId: any,
|
||||||
|
rev?: any
|
||||||
|
): Promise<void> {
|
||||||
|
const cache = await getCache()
|
||||||
|
if (!docOrId) {
|
||||||
|
throw new Error("No ID/Rev provided.")
|
||||||
|
}
|
||||||
|
const id = typeof docOrId === "string" ? docOrId : docOrId._id
|
||||||
|
rev = typeof docOrId === "string" ? rev : docOrId._rev
|
||||||
|
try {
|
||||||
|
await cache.delete(makeCacheKey(db, id))
|
||||||
|
} finally {
|
||||||
|
await db.remove(id, rev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Writethrough {
|
||||||
|
db: PouchDB.Database
|
||||||
|
writeRateMs: number
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
db: PouchDB.Database,
|
||||||
|
writeRateMs: number = DEFAULT_WRITE_RATE_MS
|
||||||
|
) {
|
||||||
|
this.db = db
|
||||||
|
this.writeRateMs = writeRateMs
|
||||||
|
}
|
||||||
|
|
||||||
|
async put(doc: any) {
|
||||||
|
return put(this.db, doc, this.writeRateMs)
|
||||||
|
}
|
||||||
|
|
||||||
|
async get(id: string) {
|
||||||
|
return get(this.db, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async remove(docOrId: any, rev?: any) {
|
||||||
|
return remove(this.db, docOrId, rev)
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,7 +16,7 @@ if (!LOADED && isDev() && !isTest()) {
|
||||||
LOADED = true
|
LOADED = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const env: any = {
|
const env = {
|
||||||
isTest,
|
isTest,
|
||||||
isDev,
|
isDev,
|
||||||
JWT_SECRET: process.env.JWT_SECRET,
|
JWT_SECRET: process.env.JWT_SECRET,
|
||||||
|
@ -66,6 +66,7 @@ const env: any = {
|
||||||
for (let [key, value] of Object.entries(env)) {
|
for (let [key, value] of Object.entries(env)) {
|
||||||
// handle the edge case of "0" to disable an environment variable
|
// handle the edge case of "0" to disable an environment variable
|
||||||
if (value === "0") {
|
if (value === "0") {
|
||||||
|
// @ts-ignore
|
||||||
env[key] = 0
|
env[key] = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -294,6 +294,16 @@ export const uploadDirectory = async (
|
||||||
await Promise.all(uploads)
|
await Promise.all(uploads)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.downloadTarballDirect = async (url: string, path: string) => {
|
||||||
|
path = sanitizeKey(path)
|
||||||
|
const response = await fetch(url)
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`unexpected response ${response.statusText}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
await streamPipeline(response.body, zlib.Unzip(), tar.extract(path))
|
||||||
|
}
|
||||||
|
|
||||||
export const downloadTarball = async (url: any, bucketName: any, path: any) => {
|
export const downloadTarball = async (url: any, bucketName: any, path: any) => {
|
||||||
bucketName = sanitizeBucket(bucketName)
|
bucketName = sanitizeBucket(bucketName)
|
||||||
path = sanitizeKey(path)
|
path = sanitizeKey(path)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// The outer exports can't be used as they now reference dist directly
|
// The outer exports can't be used as they now reference dist directly
|
||||||
import Client from "../redis"
|
import Client from "../redis"
|
||||||
import utils from "../redis/utils"
|
import utils from "../redis/utils"
|
||||||
import clients from "../redis/authRedis"
|
import clients from "../redis/init"
|
||||||
|
|
||||||
export = {
|
export = {
|
||||||
Client,
|
Client,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import RedisWrapper from "../redis"
|
||||||
const env = require("../environment")
|
const env = require("../environment")
|
||||||
// ioredis mock is all in memory
|
// ioredis mock is all in memory
|
||||||
const Redis = env.isTest() ? require("ioredis-mock") : require("ioredis")
|
const Redis = env.isTest() ? require("ioredis-mock") : require("ioredis")
|
||||||
|
@ -6,24 +7,34 @@ const {
|
||||||
removeDbPrefix,
|
removeDbPrefix,
|
||||||
getRedisOptions,
|
getRedisOptions,
|
||||||
SEPARATOR,
|
SEPARATOR,
|
||||||
|
SelectableDatabases,
|
||||||
} = require("./utils")
|
} = require("./utils")
|
||||||
|
|
||||||
const RETRY_PERIOD_MS = 2000
|
const RETRY_PERIOD_MS = 2000
|
||||||
const STARTUP_TIMEOUT_MS = 5000
|
const STARTUP_TIMEOUT_MS = 5000
|
||||||
const CLUSTERED = false
|
const CLUSTERED = false
|
||||||
|
const DEFAULT_SELECT_DB = SelectableDatabases.DEFAULT
|
||||||
|
|
||||||
// for testing just generate the client once
|
// for testing just generate the client once
|
||||||
let CLOSED = false
|
let CLOSED = false
|
||||||
let CLIENT = env.isTest() ? new Redis(getRedisOptions()) : null
|
let CLIENTS: { [key: number]: any } = {}
|
||||||
// if in test always connected
|
// if in test always connected
|
||||||
let CONNECTED = !!env.isTest()
|
let CONNECTED = env.isTest()
|
||||||
|
|
||||||
function connectionError(timeout, err) {
|
function pickClient(selectDb: number): any {
|
||||||
|
return CLIENTS[selectDb]
|
||||||
|
}
|
||||||
|
|
||||||
|
function connectionError(
|
||||||
|
selectDb: number,
|
||||||
|
timeout: NodeJS.Timeout,
|
||||||
|
err: Error | string
|
||||||
|
) {
|
||||||
// manually shut down, ignore errors
|
// manually shut down, ignore errors
|
||||||
if (CLOSED) {
|
if (CLOSED) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
CLIENT.disconnect()
|
pickClient(selectDb).disconnect()
|
||||||
CLOSED = true
|
CLOSED = true
|
||||||
// always clear this on error
|
// always clear this on error
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
|
@ -38,59 +49,69 @@ function connectionError(timeout, err) {
|
||||||
* Inits the system, will error if unable to connect to redis cluster (may take up to 10 seconds) otherwise
|
* Inits the system, will error if unable to connect to redis cluster (may take up to 10 seconds) otherwise
|
||||||
* will return the ioredis client which will be ready to use.
|
* will return the ioredis client which will be ready to use.
|
||||||
*/
|
*/
|
||||||
function init() {
|
function init(selectDb = DEFAULT_SELECT_DB) {
|
||||||
let timeout
|
let timeout: NodeJS.Timeout
|
||||||
CLOSED = false
|
CLOSED = false
|
||||||
// testing uses a single in memory client
|
let client = pickClient(selectDb)
|
||||||
if (env.isTest() || (CLIENT && CONNECTED)) {
|
// already connected, ignore
|
||||||
|
if (client && CONNECTED) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// testing uses a single in memory client
|
||||||
|
if (env.isTest()) {
|
||||||
|
CLIENTS[selectDb] = new Redis(getRedisOptions())
|
||||||
|
}
|
||||||
// start the timer - only allowed 5 seconds to connect
|
// start the timer - only allowed 5 seconds to connect
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
if (!CONNECTED) {
|
if (!CONNECTED) {
|
||||||
connectionError(timeout, "Did not successfully connect in timeout")
|
connectionError(
|
||||||
|
selectDb,
|
||||||
|
timeout,
|
||||||
|
"Did not successfully connect in timeout"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}, STARTUP_TIMEOUT_MS)
|
}, STARTUP_TIMEOUT_MS)
|
||||||
|
|
||||||
// disconnect any lingering client
|
// disconnect any lingering client
|
||||||
if (CLIENT) {
|
if (client) {
|
||||||
CLIENT.disconnect()
|
client.disconnect()
|
||||||
}
|
}
|
||||||
const { redisProtocolUrl, opts, host, port } = getRedisOptions(CLUSTERED)
|
const { redisProtocolUrl, opts, host, port } = getRedisOptions(CLUSTERED)
|
||||||
|
|
||||||
if (CLUSTERED) {
|
if (CLUSTERED) {
|
||||||
CLIENT = new Redis.Cluster([{ host, port }], opts)
|
client = new Redis.Cluster([{ host, port }], opts)
|
||||||
} else if (redisProtocolUrl) {
|
} else if (redisProtocolUrl) {
|
||||||
CLIENT = new Redis(redisProtocolUrl)
|
client = new Redis(redisProtocolUrl)
|
||||||
} else {
|
} else {
|
||||||
CLIENT = new Redis(opts)
|
client = new Redis(opts)
|
||||||
}
|
}
|
||||||
// attach handlers
|
// attach handlers
|
||||||
CLIENT.on("end", err => {
|
client.on("end", (err: Error) => {
|
||||||
connectionError(timeout, err)
|
connectionError(selectDb, timeout, err)
|
||||||
})
|
})
|
||||||
CLIENT.on("error", err => {
|
client.on("error", (err: Error) => {
|
||||||
connectionError(timeout, err)
|
connectionError(selectDb, timeout, err)
|
||||||
})
|
})
|
||||||
CLIENT.on("connect", () => {
|
client.on("connect", () => {
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
CONNECTED = true
|
CONNECTED = true
|
||||||
})
|
})
|
||||||
|
CLIENTS[selectDb] = client
|
||||||
}
|
}
|
||||||
|
|
||||||
function waitForConnection() {
|
function waitForConnection(selectDb: number = DEFAULT_SELECT_DB) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (CLIENT == null) {
|
if (pickClient(selectDb) == null) {
|
||||||
init()
|
init()
|
||||||
} else if (CONNECTED) {
|
} else if (CONNECTED) {
|
||||||
resolve()
|
resolve("")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// check if the connection is ready
|
// check if the connection is ready
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
if (CONNECTED) {
|
if (CONNECTED) {
|
||||||
clearInterval(interval)
|
clearInterval(interval)
|
||||||
resolve()
|
resolve("")
|
||||||
}
|
}
|
||||||
}, 500)
|
}, 500)
|
||||||
})
|
})
|
||||||
|
@ -100,25 +121,26 @@ function waitForConnection() {
|
||||||
* Utility function, takes a redis stream and converts it to a promisified response -
|
* Utility function, takes a redis stream and converts it to a promisified response -
|
||||||
* this can only be done with redis streams because they will have an end.
|
* this can only be done with redis streams because they will have an end.
|
||||||
* @param stream A redis stream, specifically as this type of stream will have an end.
|
* @param stream A redis stream, specifically as this type of stream will have an end.
|
||||||
|
* @param client The client to use for further lookups.
|
||||||
* @return {Promise<object>} The final output of the stream
|
* @return {Promise<object>} The final output of the stream
|
||||||
*/
|
*/
|
||||||
function promisifyStream(stream) {
|
function promisifyStream(stream: any, client: RedisWrapper) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const outputKeys = new Set()
|
const outputKeys = new Set()
|
||||||
stream.on("data", keys => {
|
stream.on("data", (keys: string[]) => {
|
||||||
keys.forEach(key => {
|
keys.forEach(key => {
|
||||||
outputKeys.add(key)
|
outputKeys.add(key)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
stream.on("error", err => {
|
stream.on("error", (err: Error) => {
|
||||||
reject(err)
|
reject(err)
|
||||||
})
|
})
|
||||||
stream.on("end", async () => {
|
stream.on("end", async () => {
|
||||||
const keysArray = Array.from(outputKeys)
|
const keysArray: string[] = Array.from(outputKeys) as string[]
|
||||||
try {
|
try {
|
||||||
let getPromises = []
|
let getPromises = []
|
||||||
for (let key of keysArray) {
|
for (let key of keysArray) {
|
||||||
getPromises.push(CLIENT.get(key))
|
getPromises.push(client.get(key))
|
||||||
}
|
}
|
||||||
const jsonArray = await Promise.all(getPromises)
|
const jsonArray = await Promise.all(getPromises)
|
||||||
resolve(
|
resolve(
|
||||||
|
@ -134,48 +156,52 @@ function promisifyStream(stream) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
class RedisWrapper {
|
export = class RedisWrapper {
|
||||||
constructor(db) {
|
_db: string
|
||||||
|
_select: number
|
||||||
|
|
||||||
|
constructor(db: string, selectDb: number | null = null) {
|
||||||
this._db = db
|
this._db = db
|
||||||
|
this._select = selectDb || DEFAULT_SELECT_DB
|
||||||
}
|
}
|
||||||
|
|
||||||
getClient() {
|
getClient() {
|
||||||
return CLIENT
|
return pickClient(this._select)
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
CLOSED = false
|
CLOSED = false
|
||||||
init()
|
init(this._select)
|
||||||
await waitForConnection()
|
await waitForConnection(this._select)
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
async finish() {
|
async finish() {
|
||||||
CLOSED = true
|
CLOSED = true
|
||||||
CLIENT.disconnect()
|
this.getClient().disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
async scan(key = "") {
|
async scan(key = ""): Promise<any> {
|
||||||
const db = this._db
|
const db = this._db
|
||||||
key = `${db}${SEPARATOR}${key}`
|
key = `${db}${SEPARATOR}${key}`
|
||||||
let stream
|
let stream
|
||||||
if (CLUSTERED) {
|
if (CLUSTERED) {
|
||||||
let node = CLIENT.nodes("master")
|
let node = this.getClient().nodes("master")
|
||||||
stream = node[0].scanStream({ match: key + "*", count: 100 })
|
stream = node[0].scanStream({ match: key + "*", count: 100 })
|
||||||
} else {
|
} else {
|
||||||
stream = CLIENT.scanStream({ match: key + "*", count: 100 })
|
stream = this.getClient().scanStream({ match: key + "*", count: 100 })
|
||||||
}
|
}
|
||||||
return promisifyStream(stream)
|
return promisifyStream(stream, this.getClient())
|
||||||
}
|
}
|
||||||
|
|
||||||
async keys(pattern) {
|
async keys(pattern: string) {
|
||||||
const db = this._db
|
const db = this._db
|
||||||
return CLIENT.keys(addDbPrefix(db, pattern))
|
return this.getClient().keys(addDbPrefix(db, pattern))
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(key) {
|
async get(key: string) {
|
||||||
const db = this._db
|
const db = this._db
|
||||||
let response = await CLIENT.get(addDbPrefix(db, key))
|
let response = await this.getClient().get(addDbPrefix(db, key))
|
||||||
// overwrite the prefixed key
|
// overwrite the prefixed key
|
||||||
if (response != null && response.key) {
|
if (response != null && response.key) {
|
||||||
response.key = key
|
response.key = key
|
||||||
|
@ -188,39 +214,37 @@ class RedisWrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async store(key, value, expirySeconds = null) {
|
async store(key: string, value: any, expirySeconds: number | null = null) {
|
||||||
const db = this._db
|
const db = this._db
|
||||||
if (typeof value === "object") {
|
if (typeof value === "object") {
|
||||||
value = JSON.stringify(value)
|
value = JSON.stringify(value)
|
||||||
}
|
}
|
||||||
const prefixedKey = addDbPrefix(db, key)
|
const prefixedKey = addDbPrefix(db, key)
|
||||||
await CLIENT.set(prefixedKey, value)
|
await this.getClient().set(prefixedKey, value)
|
||||||
if (expirySeconds) {
|
if (expirySeconds) {
|
||||||
await CLIENT.expire(prefixedKey, expirySeconds)
|
await this.getClient().expire(prefixedKey, expirySeconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTTL(key) {
|
async getTTL(key: string) {
|
||||||
const db = this._db
|
const db = this._db
|
||||||
const prefixedKey = addDbPrefix(db, key)
|
const prefixedKey = addDbPrefix(db, key)
|
||||||
return CLIENT.ttl(prefixedKey)
|
return this.getClient().ttl(prefixedKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
async setExpiry(key, expirySeconds) {
|
async setExpiry(key: string, expirySeconds: number | null) {
|
||||||
const db = this._db
|
const db = this._db
|
||||||
const prefixedKey = addDbPrefix(db, key)
|
const prefixedKey = addDbPrefix(db, key)
|
||||||
await CLIENT.expire(prefixedKey, expirySeconds)
|
await this.getClient().expire(prefixedKey, expirySeconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(key) {
|
async delete(key: string) {
|
||||||
const db = this._db
|
const db = this._db
|
||||||
await CLIENT.del(addDbPrefix(db, key))
|
await this.getClient().del(addDbPrefix(db, key))
|
||||||
}
|
}
|
||||||
|
|
||||||
async clear() {
|
async clear() {
|
||||||
let items = await this.scan()
|
let items = await this.scan()
|
||||||
await Promise.all(items.map(obj => this.delete(obj.key)))
|
await Promise.all(items.map((obj: any) => this.delete(obj.key)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = RedisWrapper
|
|
|
@ -2,7 +2,7 @@ const Client = require("./index")
|
||||||
const utils = require("./utils")
|
const utils = require("./utils")
|
||||||
const { getRedlock } = require("./redlock")
|
const { getRedlock } = require("./redlock")
|
||||||
|
|
||||||
let userClient, sessionClient, appClient, cacheClient
|
let userClient, sessionClient, appClient, cacheClient, writethroughClient
|
||||||
let migrationsRedlock
|
let migrationsRedlock
|
||||||
|
|
||||||
// turn retry off so that only one instance can ever hold the lock
|
// turn retry off so that only one instance can ever hold the lock
|
||||||
|
@ -13,6 +13,10 @@ async function init() {
|
||||||
sessionClient = await new Client(utils.Databases.SESSIONS).init()
|
sessionClient = await new Client(utils.Databases.SESSIONS).init()
|
||||||
appClient = await new Client(utils.Databases.APP_METADATA).init()
|
appClient = await new Client(utils.Databases.APP_METADATA).init()
|
||||||
cacheClient = await new Client(utils.Databases.GENERIC_CACHE).init()
|
cacheClient = await new Client(utils.Databases.GENERIC_CACHE).init()
|
||||||
|
writethroughClient = await new Client(
|
||||||
|
utils.Databases.WRITE_THROUGH,
|
||||||
|
utils.SelectableDatabases.WRITE_THROUGH
|
||||||
|
).init()
|
||||||
// pass the underlying ioredis client to redlock
|
// pass the underlying ioredis client to redlock
|
||||||
migrationsRedlock = getRedlock(
|
migrationsRedlock = getRedlock(
|
||||||
cacheClient.getClient(),
|
cacheClient.getClient(),
|
||||||
|
@ -25,6 +29,7 @@ process.on("exit", async () => {
|
||||||
if (sessionClient) await sessionClient.finish()
|
if (sessionClient) await sessionClient.finish()
|
||||||
if (appClient) await appClient.finish()
|
if (appClient) await appClient.finish()
|
||||||
if (cacheClient) await cacheClient.finish()
|
if (cacheClient) await cacheClient.finish()
|
||||||
|
if (writethroughClient) await writethroughClient.finish()
|
||||||
})
|
})
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -52,6 +57,12 @@ module.exports = {
|
||||||
}
|
}
|
||||||
return cacheClient
|
return cacheClient
|
||||||
},
|
},
|
||||||
|
getWritethroughClient: async () => {
|
||||||
|
if (!writethroughClient) {
|
||||||
|
await init()
|
||||||
|
}
|
||||||
|
return writethroughClient
|
||||||
|
},
|
||||||
getMigrationsRedlock: async () => {
|
getMigrationsRedlock: async () => {
|
||||||
if (!migrationsRedlock) {
|
if (!migrationsRedlock) {
|
||||||
await init()
|
await init()
|
|
@ -6,6 +6,14 @@ const SEPARATOR = "-"
|
||||||
const REDIS_URL = !env.REDIS_URL ? "localhost:6379" : env.REDIS_URL
|
const REDIS_URL = !env.REDIS_URL ? "localhost:6379" : env.REDIS_URL
|
||||||
const REDIS_PASSWORD = !env.REDIS_PASSWORD ? "budibase" : env.REDIS_PASSWORD
|
const REDIS_PASSWORD = !env.REDIS_PASSWORD ? "budibase" : env.REDIS_PASSWORD
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These Redis databases help us to segment up a Redis keyspace by prepending the
|
||||||
|
* specified database name onto the cache key. This means that a single real Redis database
|
||||||
|
* can be split up a bit; allowing us to use scans on small databases to find some particular
|
||||||
|
* keys within.
|
||||||
|
* If writing a very large volume of keys is expected (say 10K+) then it is better to keep these out
|
||||||
|
* of the default keyspace and use a separate one - the SelectableDatabases can be used for this.
|
||||||
|
*/
|
||||||
exports.Databases = {
|
exports.Databases = {
|
||||||
PW_RESETS: "pwReset",
|
PW_RESETS: "pwReset",
|
||||||
VERIFICATIONS: "verification",
|
VERIFICATIONS: "verification",
|
||||||
|
@ -19,6 +27,35 @@ exports.Databases = {
|
||||||
QUERY_VARS: "queryVars",
|
QUERY_VARS: "queryVars",
|
||||||
LICENSES: "license",
|
LICENSES: "license",
|
||||||
GENERIC_CACHE: "data_cache",
|
GENERIC_CACHE: "data_cache",
|
||||||
|
WRITE_THROUGH: "writeThrough",
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These define the numeric Redis databases that can be access with the SELECT command -
|
||||||
|
* (https://redis.io/commands/select/). By default a Redis server/cluster will have 16 selectable
|
||||||
|
* databases, increasing this count increases the amount of CPU/memory required to run the server.
|
||||||
|
* Ideally new Redis keyspaces should be used sparingly, only when absolutely necessary for performance
|
||||||
|
* to be maintained. Generally a keyspace can grow to be very large is scans are not needed or desired,
|
||||||
|
* but if you need to walk through all values in a database periodically then a separate selectable
|
||||||
|
* keyspace should be used.
|
||||||
|
*/
|
||||||
|
exports.SelectableDatabases = {
|
||||||
|
DEFAULT: 0,
|
||||||
|
WRITE_THROUGH: 1,
|
||||||
|
UNUSED_1: 2,
|
||||||
|
UNUSED_2: 3,
|
||||||
|
UNUSED_3: 4,
|
||||||
|
UNUSED_4: 5,
|
||||||
|
UNUSED_5: 6,
|
||||||
|
UNUSED_6: 7,
|
||||||
|
UNUSED_7: 8,
|
||||||
|
UNUSED_8: 9,
|
||||||
|
UNUSED_9: 10,
|
||||||
|
UNUSED_10: 11,
|
||||||
|
UNUSED_11: 12,
|
||||||
|
UNUSED_12: 13,
|
||||||
|
UNUSED_13: 14,
|
||||||
|
UNUSED_14: 15,
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.SEPARATOR = SEPARATOR
|
exports.SEPARATOR = SEPARATOR
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const redis = require("../redis/authRedis")
|
const redis = require("../redis/init")
|
||||||
const { v4: uuidv4 } = require("uuid")
|
const { v4: uuidv4 } = require("uuid")
|
||||||
|
|
||||||
// a week in seconds
|
// a week in seconds
|
||||||
|
|
|
@ -197,3 +197,7 @@ exports.platformLogout = async ({ ctx, userId, keepActiveSession }) => {
|
||||||
await events.auth.logout()
|
await events.auth.logout()
|
||||||
await userCache.invalidateUser(userId)
|
await userCache.invalidateUser(userId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.timeout = timeMs => {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, timeMs))
|
||||||
|
}
|
||||||
|
|
|
@ -656,6 +656,13 @@
|
||||||
"@types/keygrip" "*"
|
"@types/keygrip" "*"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/debug@*":
|
||||||
|
version "4.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82"
|
||||||
|
integrity sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==
|
||||||
|
dependencies:
|
||||||
|
"@types/ms" "*"
|
||||||
|
|
||||||
"@types/express-serve-static-core@^4.17.18":
|
"@types/express-serve-static-core@^4.17.18":
|
||||||
version "4.17.28"
|
version "4.17.28"
|
||||||
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8"
|
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8"
|
||||||
|
@ -762,6 +769,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
|
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
|
||||||
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
|
integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
|
||||||
|
|
||||||
|
"@types/ms@*":
|
||||||
|
version "0.7.31"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
|
||||||
|
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
|
||||||
|
|
||||||
"@types/node-fetch@2.6.1":
|
"@types/node-fetch@2.6.1":
|
||||||
version "2.6.1"
|
version "2.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975"
|
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975"
|
||||||
|
@ -780,6 +792,152 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.20.tgz#268f028b36eaf51181c3300252f605488c4f0650"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.20.tgz#268f028b36eaf51181c3300252f605488c4f0650"
|
||||||
integrity sha512-Q8KKwm9YqEmUBRsqJ2GWJDtXltBDxTdC4m5vTdXBolu2PeQh8LX+f6BTwU+OuXPu37fLxoN6gidqBmnky36FXA==
|
integrity sha512-Q8KKwm9YqEmUBRsqJ2GWJDtXltBDxTdC4m5vTdXBolu2PeQh8LX+f6BTwU+OuXPu37fLxoN6gidqBmnky36FXA==
|
||||||
|
|
||||||
|
"@types/pouchdb-adapter-cordova-sqlite@*":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-cordova-sqlite/-/pouchdb-adapter-cordova-sqlite-1.0.1.tgz#49e5ee6df7cc0c23196fcb340f43a560e74eb1d6"
|
||||||
|
integrity sha512-nqlXpW1ho3KBg1mUQvZgH2755y3z/rw4UA7ZJCPMRTHofxGMY8izRVw5rHBL4/7P615or0J2udpRYxgkT3D02g==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-adapter-fruitdown@*":
|
||||||
|
version "6.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-fruitdown/-/pouchdb-adapter-fruitdown-6.1.3.tgz#9b140ad9645cc56068728acf08ec19ac0046658e"
|
||||||
|
integrity sha512-Wz1Z1JLOW1hgmFQjqnSkmyyfH7by/iWb4abKn684WMvQfmxx6BxKJpJ4+eulkVPQzzgMMSgU1MpnQOm9FgRkbw==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-adapter-http@*":
|
||||||
|
version "6.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-http/-/pouchdb-adapter-http-6.1.3.tgz#6e592d5f48deb6274a21ddac1498dd308096bcf3"
|
||||||
|
integrity sha512-9Z4TLbF/KJWy/D2sWRPBA+RNU0odQimfdvlDX+EY7rGcd3aVoH8qjD/X0Xcd/0dfBH5pKrNIMFFQgW/TylRCmA==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-adapter-idb@*":
|
||||||
|
version "6.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-idb/-/pouchdb-adapter-idb-6.1.4.tgz#cb9a18864585d600820cd325f007614c5c3989cd"
|
||||||
|
integrity sha512-KIAXbkF4uYUz0ZwfNEFLtEkK44mEWopAsD76UhucH92XnJloBysav+TjI4FFfYQyTjoW3S1s6V+Z14CUJZ0F6w==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-adapter-leveldb@*":
|
||||||
|
version "6.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-leveldb/-/pouchdb-adapter-leveldb-6.1.3.tgz#17c7e75d75b992050bca15991e97fba575c61bb3"
|
||||||
|
integrity sha512-ex8NFqQGFwEpFi7AaZ5YofmuemfZNsL3nTFZBUCAKYMBkazQij1pe2ILLStSvJr0XS0qxgXjCEW19T5Wqiiskg==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-adapter-localstorage@*":
|
||||||
|
version "6.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-localstorage/-/pouchdb-adapter-localstorage-6.1.3.tgz#0dde02ba6b9d6073a295a20196563942ba9a54bd"
|
||||||
|
integrity sha512-oor040tye1KKiGLWYtIy7rRT7C2yoyX3Tf6elEJRpjOA7Ja/H8lKc4LaSh9ATbptIcES6MRqZDxtp7ly9hsW3Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-adapter-memory@*":
|
||||||
|
version "6.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-memory/-/pouchdb-adapter-memory-6.1.3.tgz#9eabdbc890fcf58960ee8b68b8685f837e75c844"
|
||||||
|
integrity sha512-gVbsIMzDzgZYThFVT4eVNsmuZwVm/4jDxP1sjlgc3qtDIxbtBhGgyNfcskwwz9Zu5Lv1avkDsIWvcxQhnvRlHg==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-adapter-node-websql@*":
|
||||||
|
version "6.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-node-websql/-/pouchdb-adapter-node-websql-6.1.3.tgz#aa18bc68af8cf509acd12c400010dcd5fab2243d"
|
||||||
|
integrity sha512-F/P+os6Jsa7CgHtH64+Z0HfwIcj0hIRB5z8gNhF7L7dxPWoAfkopK5H2gydrP3sQrlGyN4WInF+UJW/Zu1+FKg==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-adapter-websql" "*"
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-adapter-websql@*":
|
||||||
|
version "6.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-adapter-websql/-/pouchdb-adapter-websql-6.1.4.tgz#359fbe42ccac0ac90b492ddb8c32fafd0aa96d79"
|
||||||
|
integrity sha512-zMJQCtXC40hBsIDRn0GhmpeGMK0f9l/OGWfLguvczROzxxcOD7REI+e6SEmX7gJKw5JuMvlfuHzkQwjmvSJbtg==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-browser@*":
|
||||||
|
version "6.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-browser/-/pouchdb-browser-6.1.3.tgz#8f33d6ef58d6817d1f6d36979148a1c7f63244d8"
|
||||||
|
integrity sha512-EdYowrWxW9SWBMX/rux2eq7dbHi5Zeyzz+FF/IAsgQKnUxgeCO5VO2j4zTzos0SDyJvAQU+EYRc11r7xGn5tvA==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-adapter-http" "*"
|
||||||
|
"@types/pouchdb-adapter-idb" "*"
|
||||||
|
"@types/pouchdb-adapter-websql" "*"
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
"@types/pouchdb-mapreduce" "*"
|
||||||
|
"@types/pouchdb-replication" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-core@*":
|
||||||
|
version "7.0.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-core/-/pouchdb-core-7.0.10.tgz#d1ea1549e7fad6cb579f71459b1bc27252e06a5a"
|
||||||
|
integrity sha512-mKhjLlWWXyV3PTTjDhzDV1kc2dolO7VYFa75IoKM/hr8Er9eo8RIbS7mJLfC8r/C3p6ihZu9yZs1PWC1LQ0SOA==
|
||||||
|
dependencies:
|
||||||
|
"@types/debug" "*"
|
||||||
|
"@types/pouchdb-find" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-find@*":
|
||||||
|
version "6.3.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-find/-/pouchdb-find-6.3.7.tgz#f713534a53c1a7f3fd8fbbfb74131a1b04711ddc"
|
||||||
|
integrity sha512-b2dr9xoZRK5Mwl8UiRA9l5j9mmCxNfqXuu63H1KZHwJLILjoIIz7BntCvM0hnlnl7Q8P8wORq0IskuaMq5Nnnw==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-http@*":
|
||||||
|
version "6.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-http/-/pouchdb-http-6.1.3.tgz#09576c0d409da1f8dee34ec5b768415e2472ea52"
|
||||||
|
integrity sha512-0e9E5SqNOyPl/3FnEIbENssB4FlJsNYuOy131nxrZk36S+y1R/6qO7ZVRypWpGTqBWSuVd7gCsq2UDwO/285+w==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-adapter-http" "*"
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-mapreduce@*":
|
||||||
|
version "6.1.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-mapreduce/-/pouchdb-mapreduce-6.1.7.tgz#9ab32d1e0f234f1bf6d1e4c5d7e216e9e23ac0a3"
|
||||||
|
integrity sha512-WzBwm7tmO9QhfRzVaWT4v6JQSS/fG2OoUDrWrhX87rPe2Pn6laPvdK5li6myNRxCoI/l5e8Jd+oYBAFnaiFucA==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-node@*":
|
||||||
|
version "6.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-node/-/pouchdb-node-6.1.4.tgz#5214c0169fcfd2237d373380bbd65a934feb5dfb"
|
||||||
|
integrity sha512-wnTCH8X1JOPpNOfVhz8HW0AvmdHh6pt40MuRj0jQnK7QEHsHS79WujsKTKSOF8QXtPwpvCNSsI7ut7H7tfxxJQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-adapter-http" "*"
|
||||||
|
"@types/pouchdb-adapter-leveldb" "*"
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
"@types/pouchdb-mapreduce" "*"
|
||||||
|
"@types/pouchdb-replication" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb-replication@*":
|
||||||
|
version "6.4.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb-replication/-/pouchdb-replication-6.4.4.tgz#743406c90f13a988fa3e346ea74ce40acd170d00"
|
||||||
|
integrity sha512-BsE5LKpjJK4iAf6Fx5kyrMw+33V+Ip7uWldUnU2BYrrvtR+MLD22dcImm7DZN1st2wPPb91i0XEnQzvP0w1C/Q==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
"@types/pouchdb-find" "*"
|
||||||
|
|
||||||
|
"@types/pouchdb@6.4.0":
|
||||||
|
version "6.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/pouchdb/-/pouchdb-6.4.0.tgz#f9c41ca64b23029f9bf2eb4bf6956e6431cb79f8"
|
||||||
|
integrity sha512-eGCpX+NXhd5VLJuJMzwe3L79fa9+IDTrAG3CPaf4s/31PD56hOrhDJTSmRELSXuiqXr6+OHzzP0PldSaWsFt7w==
|
||||||
|
dependencies:
|
||||||
|
"@types/pouchdb-adapter-cordova-sqlite" "*"
|
||||||
|
"@types/pouchdb-adapter-fruitdown" "*"
|
||||||
|
"@types/pouchdb-adapter-http" "*"
|
||||||
|
"@types/pouchdb-adapter-idb" "*"
|
||||||
|
"@types/pouchdb-adapter-leveldb" "*"
|
||||||
|
"@types/pouchdb-adapter-localstorage" "*"
|
||||||
|
"@types/pouchdb-adapter-memory" "*"
|
||||||
|
"@types/pouchdb-adapter-node-websql" "*"
|
||||||
|
"@types/pouchdb-adapter-websql" "*"
|
||||||
|
"@types/pouchdb-browser" "*"
|
||||||
|
"@types/pouchdb-core" "*"
|
||||||
|
"@types/pouchdb-http" "*"
|
||||||
|
"@types/pouchdb-mapreduce" "*"
|
||||||
|
"@types/pouchdb-node" "*"
|
||||||
|
"@types/pouchdb-replication" "*"
|
||||||
|
|
||||||
"@types/prettier@^2.1.5":
|
"@types/prettier@^2.1.5":
|
||||||
version "2.6.3"
|
version "2.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.3.tgz#68ada76827b0010d0db071f739314fa429943d0a"
|
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.3.tgz#68ada76827b0010d0db071f739314fa429943d0a"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/bbui",
|
"name": "@budibase/bbui",
|
||||||
"description": "A UI solution used in the different Budibase projects.",
|
"description": "A UI solution used in the different Budibase projects.",
|
||||||
"version": "1.0.207-alpha.9",
|
"version": "1.0.212-alpha.5",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"svelte": "src/index.js",
|
"svelte": "src/index.js",
|
||||||
"module": "dist/bbui.es.js",
|
"module": "dist/bbui.es.js",
|
||||||
|
@ -38,7 +38,7 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
|
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
|
||||||
"@budibase/string-templates": "^1.0.207-alpha.9",
|
"@budibase/string-templates": "^1.0.212-alpha.5",
|
||||||
"@spectrum-css/actionbutton": "^1.0.1",
|
"@spectrum-css/actionbutton": "^1.0.1",
|
||||||
"@spectrum-css/actiongroup": "^1.0.1",
|
"@spectrum-css/actiongroup": "^1.0.1",
|
||||||
"@spectrum-css/avatar": "^3.0.2",
|
"@spectrum-css/avatar": "^3.0.2",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "1.0.207-alpha.9",
|
"version": "1.0.212-alpha.5",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -69,10 +69,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.0.207-alpha.9",
|
"@budibase/bbui": "^1.0.212-alpha.5",
|
||||||
"@budibase/client": "^1.0.207-alpha.9",
|
"@budibase/client": "^1.0.212-alpha.5",
|
||||||
"@budibase/frontend-core": "^1.0.207-alpha.9",
|
"@budibase/frontend-core": "^1.0.212-alpha.5",
|
||||||
"@budibase/string-templates": "^1.0.207-alpha.9",
|
"@budibase/string-templates": "^1.0.212-alpha.5",
|
||||||
"@sentry/browser": "5.19.1",
|
"@sentry/browser": "5.19.1",
|
||||||
"@spectrum-css/page": "^3.0.1",
|
"@spectrum-css/page": "^3.0.1",
|
||||||
"@spectrum-css/vars": "^3.0.1",
|
"@spectrum-css/vars": "^3.0.1",
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
<script>
|
<script>
|
||||||
import { store, automationStore } from "builderStore"
|
import { store, automationStore } from "builderStore"
|
||||||
import { roles, flags } from "stores/backend"
|
import { roles, flags } from "stores/backend"
|
||||||
import { Icon, Tabs, Tab, Heading, notifications } from "@budibase/bbui"
|
import { auth } from "stores/portal"
|
||||||
|
import {
|
||||||
|
Icon,
|
||||||
|
Tabs,
|
||||||
|
Tab,
|
||||||
|
Heading,
|
||||||
|
notifications,
|
||||||
|
Banner,
|
||||||
|
} from "@budibase/bbui"
|
||||||
import RevertModal from "components/deploy/RevertModal.svelte"
|
import RevertModal from "components/deploy/RevertModal.svelte"
|
||||||
import DeployNavigation from "components/deploy/DeployNavigation.svelte"
|
import DeployNavigation from "components/deploy/DeployNavigation.svelte"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
|
@ -13,6 +21,7 @@
|
||||||
|
|
||||||
// Get Package and set store
|
// Get Package and set store
|
||||||
let promise = getPackage()
|
let promise = getPackage()
|
||||||
|
let betaAccess = false
|
||||||
|
|
||||||
// Sync once when you load the app
|
// Sync once when you load the app
|
||||||
let hasSynced = false
|
let hasSynced = false
|
||||||
|
@ -56,10 +65,23 @@
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function newDesignUi() {
|
||||||
|
try {
|
||||||
|
await flags.toggleUiFeature("design_ui")
|
||||||
|
window.location.reload()
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error("Error enabling beta access to new design UI")
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (!hasSynced && application) {
|
if (!hasSynced && application) {
|
||||||
try {
|
try {
|
||||||
await API.syncApp(application)
|
await API.syncApp(application)
|
||||||
|
// check if user has beta access
|
||||||
|
const betaResponse = await API.checkBetaAccess($auth?.user?.email)
|
||||||
|
betaAccess = betaResponse.access
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Failed to sync with production database")
|
notifications.error("Failed to sync with production database")
|
||||||
}
|
}
|
||||||
|
@ -80,6 +102,15 @@
|
||||||
<div class="loading" />
|
<div class="loading" />
|
||||||
{:then _}
|
{:then _}
|
||||||
<div class="root">
|
<div class="root">
|
||||||
|
{#if betaAccess}
|
||||||
|
<Banner
|
||||||
|
extraButtonText="Try New UI (Beta)"
|
||||||
|
extraButtonAction={newDesignUi}
|
||||||
|
>
|
||||||
|
Try the <b>all new</b> budibase design interface. (Not recommended for existing
|
||||||
|
budibase apps)
|
||||||
|
</Banner>
|
||||||
|
{/if}
|
||||||
<div class="top-nav">
|
<div class="top-nav">
|
||||||
<div class="topleftnav">
|
<div class="topleftnav">
|
||||||
<Icon
|
<Icon
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/cli",
|
"name": "@budibase/cli",
|
||||||
"version": "1.0.207-alpha.9",
|
"version": "1.0.212-alpha.5",
|
||||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "1.0.207-alpha.9",
|
"version": "1.0.212-alpha.5",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"module": "dist/budibase-client.js",
|
"module": "dist/budibase-client.js",
|
||||||
"main": "dist/budibase-client.js",
|
"main": "dist/budibase-client.js",
|
||||||
|
@ -19,9 +19,9 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.0.207-alpha.9",
|
"@budibase/bbui": "^1.0.212-alpha.5",
|
||||||
"@budibase/frontend-core": "^1.0.207-alpha.9",
|
"@budibase/frontend-core": "^1.0.212-alpha.5",
|
||||||
"@budibase/string-templates": "^1.0.207-alpha.9",
|
"@budibase/string-templates": "^1.0.212-alpha.5",
|
||||||
"@spectrum-css/button": "^3.0.3",
|
"@spectrum-css/button": "^3.0.3",
|
||||||
"@spectrum-css/card": "^3.0.3",
|
"@spectrum-css/card": "^3.0.3",
|
||||||
"@spectrum-css/divider": "^1.0.3",
|
"@spectrum-css/divider": "^1.0.3",
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/frontend-core",
|
"name": "@budibase/frontend-core",
|
||||||
"version": "1.0.207-alpha.9",
|
"version": "1.0.212-alpha.5",
|
||||||
"description": "Budibase frontend core libraries used in builder and client",
|
"description": "Budibase frontend core libraries used in builder and client",
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"svelte": "src/index.js",
|
"svelte": "src/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.0.207-alpha.9",
|
"@budibase/bbui": "^1.0.212-alpha.5",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"svelte": "^3.46.2"
|
"svelte": "^3.46.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,4 +54,14 @@ export const buildOtherEndpoints = API => ({
|
||||||
url: "/api/permission/builtin",
|
url: "/api/permission/builtin",
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if they are part of the budibase beta program.
|
||||||
|
*/
|
||||||
|
checkBetaAccess: async email => {
|
||||||
|
return await API.get({
|
||||||
|
url: `/api/beta/access?email=${email}`,
|
||||||
|
external: true,
|
||||||
|
})
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
1
packages/server/.gitignore
vendored
1
packages/server/.gitignore
vendored
|
@ -2,6 +2,7 @@ node_modules/
|
||||||
myapps/
|
myapps/
|
||||||
.env
|
.env
|
||||||
builder/*
|
builder/*
|
||||||
|
new_design_ui/*
|
||||||
client/*
|
client/*
|
||||||
db/dev.db/
|
db/dev.db/
|
||||||
dist
|
dist
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "1.0.207-alpha.9",
|
"version": "1.0.212-alpha.5",
|
||||||
"description": "Budibase Web Server",
|
"description": "Budibase Web Server",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -77,11 +77,11 @@
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apidevtools/swagger-parser": "10.0.3",
|
"@apidevtools/swagger-parser": "10.0.3",
|
||||||
"@budibase/backend-core": "^1.0.207-alpha.9",
|
"@budibase/backend-core": "^1.0.212-alpha.5",
|
||||||
"@budibase/client": "^1.0.207-alpha.9",
|
"@budibase/client": "^1.0.212-alpha.5",
|
||||||
"@budibase/pro": "1.0.207-alpha.9",
|
"@budibase/pro": "1.0.212-alpha.5",
|
||||||
"@budibase/string-templates": "^1.0.207-alpha.9",
|
"@budibase/string-templates": "^1.0.212-alpha.5",
|
||||||
"@budibase/types": "^1.0.207-alpha.9",
|
"@budibase/types": "^1.0.212-alpha.5",
|
||||||
"@bull-board/api": "3.7.0",
|
"@bull-board/api": "3.7.0",
|
||||||
"@bull-board/koa": "3.9.4",
|
"@bull-board/koa": "3.9.4",
|
||||||
"@elastic/elasticsearch": "7.10.0",
|
"@elastic/elasticsearch": "7.10.0",
|
||||||
|
@ -152,7 +152,6 @@
|
||||||
"@babel/core": "7.17.4",
|
"@babel/core": "7.17.4",
|
||||||
"@babel/preset-env": "7.16.11",
|
"@babel/preset-env": "7.16.11",
|
||||||
"@budibase/standard-components": "^0.9.139",
|
"@budibase/standard-components": "^0.9.139",
|
||||||
"@budibase/types": "^1.0.207-alpha.3",
|
|
||||||
"@jest/test-sequencer": "24.9.0",
|
"@jest/test-sequencer": "24.9.0",
|
||||||
"@types/apidoc": "0.50.0",
|
"@types/apidoc": "0.50.0",
|
||||||
"@types/bson": "4.2.0",
|
"@types/bson": "4.2.0",
|
||||||
|
|
|
@ -227,7 +227,11 @@ export const fetchAppPackage = async (ctx: any) => {
|
||||||
application,
|
application,
|
||||||
screens,
|
screens,
|
||||||
layouts,
|
layouts,
|
||||||
clientLibPath: clientLibraryPath(ctx.params.appId, application.version),
|
clientLibPath: clientLibraryPath(
|
||||||
|
ctx.params.appId,
|
||||||
|
application.version,
|
||||||
|
ctx
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,12 @@ const env = require("../../environment")
|
||||||
const { checkSlashesInUrl } = require("../../utilities")
|
const { checkSlashesInUrl } = require("../../utilities")
|
||||||
const { request } = require("../../utilities/workerRequests")
|
const { request } = require("../../utilities/workerRequests")
|
||||||
const { clearLock } = require("../../utilities/redis")
|
const { clearLock } = require("../../utilities/redis")
|
||||||
const { Replication, getProdAppID } = require("@budibase/backend-core/db")
|
const {
|
||||||
const { DocumentTypes } = require("../../db/utils")
|
Replication,
|
||||||
|
getProdAppID,
|
||||||
|
dangerousGetDB,
|
||||||
|
} = require("@budibase/backend-core/db")
|
||||||
|
const { DocumentTypes, getRowParams } = require("../../db/utils")
|
||||||
const { app: appCache } = require("@budibase/backend-core/cache")
|
const { app: appCache } = require("@budibase/backend-core/cache")
|
||||||
const { getProdAppDB, getAppDB } = require("@budibase/backend-core/context")
|
const { getProdAppDB, getAppDB } = require("@budibase/backend-core/context")
|
||||||
const { events } = require("@budibase/backend-core")
|
const { events } = require("@budibase/backend-core")
|
||||||
|
@ -133,3 +137,44 @@ exports.getBudibaseVersion = async ctx => {
|
||||||
}
|
}
|
||||||
await events.installation.versionChecked(version)
|
await events.installation.versionChecked(version)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove as part of beta program
|
||||||
|
exports.checkBetaAccess = async ctx => {
|
||||||
|
// go to the cloud platform if running self hosted
|
||||||
|
if (env.SELF_HOSTED || !env.MULTI_TENANCY) {
|
||||||
|
const baseUrl = env.ACCOUNT_PORTAL_URL.replace("account.", "")
|
||||||
|
const response = await fetch(
|
||||||
|
`${baseUrl}/api/beta/access?email=${ctx.query.email}`
|
||||||
|
)
|
||||||
|
const json = await response.json()
|
||||||
|
ctx.body = json
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const userToCheck = ctx.query.email
|
||||||
|
const BETA_USERS_DB = "app_bb_f9b77d06b9db4e3ca185476ab87a2364"
|
||||||
|
const BETA_USERS_TABLE = "ta_8c2c6df1c03f49cfb6340e85e066dd15"
|
||||||
|
|
||||||
|
try {
|
||||||
|
const db = dangerousGetDB(BETA_USERS_DB)
|
||||||
|
const betaUsers = (
|
||||||
|
await db.allDocs(
|
||||||
|
getRowParams(BETA_USERS_TABLE, null, {
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).rows.map(row => row.doc)
|
||||||
|
|
||||||
|
let access = false
|
||||||
|
for (let betaUser of betaUsers) {
|
||||||
|
if (betaUser["Email address"].trim() === userToCheck) {
|
||||||
|
access = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.body = { access }
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
ctx.body = { access: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -145,7 +145,7 @@ class QueryBuilder {
|
||||||
|
|
||||||
function build(structure, queryFn) {
|
function build(structure, queryFn) {
|
||||||
for (let [key, value] of Object.entries(structure)) {
|
for (let [key, value] of Object.entries(structure)) {
|
||||||
key = builder.preprocess(key.replace(/ /, "_"), {
|
key = builder.preprocess(key.replace(/ /g, "_"), {
|
||||||
escape: true,
|
escape: true,
|
||||||
})
|
})
|
||||||
const expression = queryFn(key, value)
|
const expression = queryFn(key, value)
|
||||||
|
|
|
@ -16,9 +16,15 @@ const { upload } = require("../../../utilities/fileSystem")
|
||||||
const { attachmentsRelativeURL } = require("../../../utilities")
|
const { attachmentsRelativeURL } = require("../../../utilities")
|
||||||
const { DocumentTypes, isDevAppID } = require("../../../db/utils")
|
const { DocumentTypes, isDevAppID } = require("../../../db/utils")
|
||||||
const { getAppDB, getAppId } = require("@budibase/backend-core/context")
|
const { getAppDB, getAppId } = require("@budibase/backend-core/context")
|
||||||
|
const { setCookie, clearCookie } = require("@budibase/backend-core/utils")
|
||||||
const AWS = require("aws-sdk")
|
const AWS = require("aws-sdk")
|
||||||
const { events } = require("@budibase/backend-core")
|
const { events } = require("@budibase/backend-core")
|
||||||
|
|
||||||
|
const fs = require("fs")
|
||||||
|
const {
|
||||||
|
downloadTarballDirect,
|
||||||
|
} = require("../../../utilities/fileSystem/utilities")
|
||||||
|
|
||||||
async function prepareUpload({ s3Key, bucket, metadata, file }) {
|
async function prepareUpload({ s3Key, bucket, metadata, file }) {
|
||||||
const response = await upload({
|
const response = await upload({
|
||||||
bucket,
|
bucket,
|
||||||
|
@ -38,8 +44,41 @@ async function prepareUpload({ s3Key, bucket, metadata, file }) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.toggleBetaUiFeature = async function (ctx) {
|
||||||
|
const cookieName = `beta:${ctx.params.feature}`
|
||||||
|
|
||||||
|
if (ctx.cookies.get(cookieName)) {
|
||||||
|
clearCookie(ctx, cookieName)
|
||||||
|
ctx.body = {
|
||||||
|
message: `${ctx.params.feature} disabled`,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let builderPath = resolve(TOP_LEVEL_PATH, "new_design_ui")
|
||||||
|
|
||||||
|
// // download it from S3
|
||||||
|
if (!fs.existsSync(builderPath)) {
|
||||||
|
fs.mkdirSync(builderPath)
|
||||||
|
}
|
||||||
|
await downloadTarballDirect(
|
||||||
|
"https://cdn.budi.live/beta:design_ui/new_ui.tar.gz",
|
||||||
|
builderPath
|
||||||
|
)
|
||||||
|
setCookie(ctx, {}, cookieName)
|
||||||
|
|
||||||
|
ctx.body = {
|
||||||
|
message: `${ctx.params.feature} enabled`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
exports.serveBuilder = async function (ctx) {
|
exports.serveBuilder = async function (ctx) {
|
||||||
let builderPath = resolve(TOP_LEVEL_PATH, "builder")
|
// Temporary: New Design UI
|
||||||
|
const designUiCookie = ctx.cookies.get("beta:design_ui")
|
||||||
|
// TODO: get this from the tmp Dir that we downloaded from MinIO
|
||||||
|
const uiPath = designUiCookie ? "new_design_ui" : "builder"
|
||||||
|
|
||||||
|
let builderPath = resolve(TOP_LEVEL_PATH, uiPath)
|
||||||
await send(ctx, ctx.file, { root: builderPath })
|
await send(ctx, ctx.file, { root: builderPath })
|
||||||
if (!ctx.file.includes("assets/")) {
|
if (!ctx.file.includes("assets/")) {
|
||||||
await events.serve.servedBuilder()
|
await events.serve.servedBuilder()
|
||||||
|
@ -78,7 +117,7 @@ exports.serveApp = async function (ctx) {
|
||||||
title: appInfo.name,
|
title: appInfo.name,
|
||||||
production: env.isProd(),
|
production: env.isProd(),
|
||||||
appId,
|
appId,
|
||||||
clientLibPath: clientLibraryPath(appId, appInfo.version),
|
clientLibPath: clientLibraryPath(appId, appInfo.version, ctx),
|
||||||
})
|
})
|
||||||
|
|
||||||
const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)
|
const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)
|
||||||
|
|
|
@ -22,5 +22,6 @@ router
|
||||||
.get("/api/dev/version", authorized(BUILDER), controller.getBudibaseVersion)
|
.get("/api/dev/version", authorized(BUILDER), controller.getBudibaseVersion)
|
||||||
.delete("/api/dev/:appId/lock", authorized(BUILDER), controller.clearLock)
|
.delete("/api/dev/:appId/lock", authorized(BUILDER), controller.clearLock)
|
||||||
.post("/api/dev/:appId/revert", authorized(BUILDER), controller.revert)
|
.post("/api/dev/:appId/revert", authorized(BUILDER), controller.revert)
|
||||||
|
.get("/api/beta/access", controller.checkBetaAccess)
|
||||||
|
|
||||||
module.exports = router
|
module.exports = router
|
||||||
|
|
|
@ -38,6 +38,7 @@ router
|
||||||
// TODO: for now this builder endpoint is not authorized/secured, will need to be
|
// TODO: for now this builder endpoint is not authorized/secured, will need to be
|
||||||
.get("/builder/:file*", controller.serveBuilder)
|
.get("/builder/:file*", controller.serveBuilder)
|
||||||
.post("/api/attachments/process", authorized(BUILDER), controller.uploadFile)
|
.post("/api/attachments/process", authorized(BUILDER), controller.uploadFile)
|
||||||
|
.post("/api/beta/:feature", controller.toggleBetaUiFeature)
|
||||||
.post(
|
.post(
|
||||||
"/api/attachments/:tableId/upload",
|
"/api/attachments/:tableId/upload",
|
||||||
paramResource("tableId"),
|
paramResource("tableId"),
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
const { getTable } = require("../api/controllers/table/utils")
|
const { getTable } = require("../api/controllers/table/utils")
|
||||||
const { findHBSBlocks } = require("@budibase/string-templates")
|
const {
|
||||||
|
findHBSBlocks,
|
||||||
|
decodeJSBinding,
|
||||||
|
isJSBinding,
|
||||||
|
encodeJSBinding,
|
||||||
|
} = require("@budibase/string-templates")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When values are input to the system generally they will be of type string as this is required for template strings.
|
* When values are input to the system generally they will be of type string as this is required for template strings.
|
||||||
|
@ -77,11 +82,21 @@ exports.getError = err => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.substituteLoopStep = (hbsString, substitute) => {
|
exports.substituteLoopStep = (hbsString, substitute) => {
|
||||||
let blocks = findHBSBlocks(hbsString)
|
let blocks = []
|
||||||
|
let checkForJS = isJSBinding(hbsString)
|
||||||
|
if (checkForJS) {
|
||||||
|
hbsString = decodeJSBinding(hbsString)
|
||||||
|
blocks.push(hbsString)
|
||||||
|
} else {
|
||||||
|
blocks = findHBSBlocks(hbsString)
|
||||||
|
}
|
||||||
for (let block of blocks) {
|
for (let block of blocks) {
|
||||||
let oldBlock = block
|
|
||||||
block = block.replace(/loop/, substitute)
|
block = block.replace(/loop/, substitute)
|
||||||
hbsString = hbsString.replace(new RegExp(oldBlock, "g"), block)
|
if (checkForJS) {
|
||||||
|
hbsString = encodeJSBinding(block)
|
||||||
|
} else {
|
||||||
|
hbsString = block
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hbsString
|
return hbsString
|
||||||
|
|
|
@ -67,7 +67,9 @@ module.exports = {
|
||||||
SALT_ROUNDS: process.env.SALT_ROUNDS,
|
SALT_ROUNDS: process.env.SALT_ROUNDS,
|
||||||
LOGGER: process.env.LOGGER,
|
LOGGER: process.env.LOGGER,
|
||||||
LOG_LEVEL: process.env.LOG_LEVEL,
|
LOG_LEVEL: process.env.LOG_LEVEL,
|
||||||
AUTOMATION_MAX_ITERATIONS: process.env.AUTOMATION_MAX_ITERATIONS,
|
ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL,
|
||||||
|
AUTOMATION_MAX_ITERATIONS:
|
||||||
|
parseIntSafe(process.env.AUTOMATION_MAX_ITERATIONS) || 200,
|
||||||
SENDGRID_API_KEY: process.env.SENDGRID_API_KEY,
|
SENDGRID_API_KEY: process.env.SENDGRID_API_KEY,
|
||||||
DYNAMO_ENDPOINT: process.env.DYNAMO_ENDPOINT,
|
DYNAMO_ENDPOINT: process.env.DYNAMO_ENDPOINT,
|
||||||
QUERY_THREAD_TIMEOUT: parseIntSafe(process.env.QUERY_THREAD_TIMEOUT),
|
QUERY_THREAD_TIMEOUT: parseIntSafe(process.env.QUERY_THREAD_TIMEOUT),
|
||||||
|
|
|
@ -264,6 +264,12 @@ module MongoDBModule {
|
||||||
filter: FilterQuery<any>
|
filter: FilterQuery<any>
|
||||||
options: CommonOptions
|
options: CommonOptions
|
||||||
}
|
}
|
||||||
|
if (!json.options) {
|
||||||
|
json = {
|
||||||
|
filter: json,
|
||||||
|
options: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (query.extra.actionTypes) {
|
switch (query.extra.actionTypes) {
|
||||||
case "deleteOne": {
|
case "deleteOne": {
|
||||||
|
|
|
@ -218,7 +218,7 @@ class Orchestrator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
index === parseInt(env.AUTOMATION_MAX_ITERATIONS) ||
|
index === env.AUTOMATION_MAX_ITERATION ||
|
||||||
index === parseInt(loopStep.inputs.iterations)
|
index === parseInt(loopStep.inputs.iterations)
|
||||||
) {
|
) {
|
||||||
this.updateContextAndOutput(loopStepNumber, step, tempOutput, {
|
this.updateContextAndOutput(loopStepNumber, step, tempOutput, {
|
||||||
|
|
|
@ -9,6 +9,7 @@ const {
|
||||||
deleteFolder,
|
deleteFolder,
|
||||||
uploadDirectory,
|
uploadDirectory,
|
||||||
downloadTarball,
|
downloadTarball,
|
||||||
|
downloadTarballDirect,
|
||||||
} = require("@budibase/backend-core/objectStore")
|
} = require("@budibase/backend-core/objectStore")
|
||||||
|
|
||||||
/***********************************
|
/***********************************
|
||||||
|
@ -29,4 +30,5 @@ exports.retrieveToTmp = retrieveToTmp
|
||||||
exports.deleteFolder = deleteFolder
|
exports.deleteFolder = deleteFolder
|
||||||
exports.uploadDirectory = uploadDirectory
|
exports.uploadDirectory = uploadDirectory
|
||||||
exports.downloadTarball = downloadTarball
|
exports.downloadTarball = downloadTarball
|
||||||
|
exports.downloadTarballDirect = downloadTarballDirect
|
||||||
exports.deleteFiles = deleteFiles
|
exports.deleteFiles = deleteFiles
|
||||||
|
|
|
@ -54,11 +54,17 @@ exports.objectStoreUrl = () => {
|
||||||
* @return {string} The URL to be inserted into appPackage response or server rendered
|
* @return {string} The URL to be inserted into appPackage response or server rendered
|
||||||
* app index file.
|
* app index file.
|
||||||
*/
|
*/
|
||||||
exports.clientLibraryPath = (appId, version) => {
|
exports.clientLibraryPath = (appId, version, ctx) => {
|
||||||
if (env.isProd()) {
|
if (env.isProd()) {
|
||||||
|
// TODO: remove - for beta testing UI
|
||||||
|
if (ctx && ctx.cookies.get("beta:design_ui")) {
|
||||||
|
return "https://cdn.budi.live/beta:design_ui/budibase-client.js"
|
||||||
|
}
|
||||||
|
|
||||||
let url = `${exports.objectStoreUrl()}/${sanitizeKey(
|
let url = `${exports.objectStoreUrl()}/${sanitizeKey(
|
||||||
appId
|
appId
|
||||||
)}/budibase-client.js`
|
)}/budibase-client.js`
|
||||||
|
|
||||||
// append app version to bust the cache
|
// append app version to bust the cache
|
||||||
if (version) {
|
if (version) {
|
||||||
url += `?v=${version}`
|
url += `?v=${version}`
|
||||||
|
|
|
@ -1094,12 +1094,12 @@
|
||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@budibase/backend-core@1.0.207-alpha.8":
|
"@budibase/backend-core@1.0.212-alpha.5":
|
||||||
version "1.0.207-alpha.8"
|
version "1.0.212-alpha.5"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.207-alpha.8.tgz#93bf40554acc2bb3f9cff7ea3c52d220fc373e8d"
|
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.212-alpha.5.tgz#5d34ecfe798058892835ddb5eda20947ee04945b"
|
||||||
integrity sha512-DniDV+gPkw6olCrvT3ghuNTLzW8BVuxXnJP4QVgualXgJqo8xwnmOJCzu9slb9W1G3E+Laqk7Lhpn8jHjkTPDA==
|
integrity sha512-BhyEp1EXnwSoX1DkmACnv/ABI53sOv+gRMMxAmFSrz8btWCrAT57yzsWi5p5lU9Ri8CtuYVussCeKNiocLYEEw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/types" "^1.0.207-alpha.8"
|
"@budibase/types" "^1.0.212-alpha.5"
|
||||||
"@techpass/passport-openidconnect" "0.3.2"
|
"@techpass/passport-openidconnect" "0.3.2"
|
||||||
aws-sdk "2.1030.0"
|
aws-sdk "2.1030.0"
|
||||||
bcrypt "5.0.1"
|
bcrypt "5.0.1"
|
||||||
|
@ -1176,12 +1176,12 @@
|
||||||
svelte-flatpickr "^3.2.3"
|
svelte-flatpickr "^3.2.3"
|
||||||
svelte-portal "^1.0.0"
|
svelte-portal "^1.0.0"
|
||||||
|
|
||||||
"@budibase/pro@1.0.207-alpha.8":
|
"@budibase/pro@1.0.212-alpha.5":
|
||||||
version "1.0.207-alpha.8"
|
version "1.0.212-alpha.5"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.207-alpha.8.tgz#77d70a60c9d93dce6620d52008c4e5d0485b3d94"
|
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.212-alpha.5.tgz#7372aba798016e98268653455f5d11f42f0ba4e5"
|
||||||
integrity sha512-q6n/RVjtGVMII65yU50yuZZDBZOBh+lmmt7p/RwXftinsmPY0GK6g6cEXKi4Rya8GytCY2D+XG4ibl2AkQYM5w==
|
integrity sha512-ouO9ZrvCF9PjRQPhrv0GFhwwtRowyDkkGLgsz99pSiZOzwkJBIeMtxpSCMytTQzGCfrSEISlFct6DTTX3qByNA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/backend-core" "1.0.207-alpha.8"
|
"@budibase/backend-core" "1.0.212-alpha.5"
|
||||||
node-fetch "^2.6.1"
|
node-fetch "^2.6.1"
|
||||||
|
|
||||||
"@budibase/standard-components@^0.9.139":
|
"@budibase/standard-components@^0.9.139":
|
||||||
|
@ -1202,10 +1202,10 @@
|
||||||
svelte-apexcharts "^1.0.2"
|
svelte-apexcharts "^1.0.2"
|
||||||
svelte-flatpickr "^3.1.0"
|
svelte-flatpickr "^3.1.0"
|
||||||
|
|
||||||
"@budibase/types@^1.0.207-alpha.8":
|
"@budibase/types@^1.0.212-alpha.5":
|
||||||
version "1.0.208"
|
version "1.0.212"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.0.208.tgz#c45cb494fb5b85229e15a34c6ac1805bae5be867"
|
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.0.212.tgz#e66a15b711544b4fab7767261fd5f2f1dd7f40d7"
|
||||||
integrity sha512-zKIHg6TGK+soVxMNZNrGypP3DCrd3jhlUQEFeQ+rZR6/tCue1G74bjzydY5FjnLEsXeLH1a0hkS5HulTmvQ2bA==
|
integrity sha512-DhGyw6snwJQZQlx7havVYnqPZfZERueKZfmVCBySzwInZZt0+sXZaBl1BVjGjYuwpaUQBMDBf7geBgHXp6DIKg==
|
||||||
|
|
||||||
"@bull-board/api@3.7.0":
|
"@bull-board/api@3.7.0":
|
||||||
version "3.7.0"
|
version "3.7.0"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/string-templates",
|
"name": "@budibase/string-templates",
|
||||||
"version": "1.0.207-alpha.9",
|
"version": "1.0.212-alpha.5",
|
||||||
"description": "Handlebars wrapper for Budibase templating.",
|
"description": "Handlebars wrapper for Budibase templating.",
|
||||||
"main": "src/index.cjs",
|
"main": "src/index.cjs",
|
||||||
"module": "dist/bundle.mjs",
|
"module": "dist/bundle.mjs",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/types",
|
"name": "@budibase/types",
|
||||||
"version": "1.0.207-alpha.9",
|
"version": "1.0.212-alpha.5",
|
||||||
"description": "Budibase types",
|
"description": "Budibase types",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
|
7
packages/worker/.dockerignore
Normal file
7
packages/worker/.dockerignore
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/worker",
|
"name": "@budibase/worker",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "1.0.207-alpha.9",
|
"version": "1.0.212-alpha.5",
|
||||||
"description": "Budibase background service",
|
"description": "Budibase background service",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -34,10 +34,10 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/backend-core": "^1.0.207-alpha.9",
|
"@budibase/backend-core": "^1.0.212-alpha.5",
|
||||||
"@budibase/pro": "1.0.207-alpha.9",
|
"@budibase/pro": "1.0.212-alpha.5",
|
||||||
"@budibase/string-templates": "^1.0.207-alpha.9",
|
"@budibase/string-templates": "^1.0.212-alpha.5",
|
||||||
"@budibase/types": "^1.0.207-alpha.9",
|
"@budibase/types": "^1.0.212-alpha.5",
|
||||||
"@koa/router": "8.0.8",
|
"@koa/router": "8.0.8",
|
||||||
"@sentry/node": "6.17.7",
|
"@sentry/node": "6.17.7",
|
||||||
"@techpass/passport-openidconnect": "0.3.2",
|
"@techpass/passport-openidconnect": "0.3.2",
|
||||||
|
@ -67,7 +67,6 @@
|
||||||
"server-destroy": "1.0.1"
|
"server-destroy": "1.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@budibase/types": "^1.0.207-alpha.3",
|
|
||||||
"@types/jest": "26.0.23",
|
"@types/jest": "26.0.23",
|
||||||
"@types/koa": "2.13.4",
|
"@types/koa": "2.13.4",
|
||||||
"@types/koa-router": "7.4.4",
|
"@types/koa-router": "7.4.4",
|
||||||
|
|
|
@ -291,12 +291,12 @@
|
||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@budibase/backend-core@1.0.207-alpha.8":
|
"@budibase/backend-core@1.0.212-alpha.5":
|
||||||
version "1.0.207-alpha.8"
|
version "1.0.212-alpha.5"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.207-alpha.8.tgz#93bf40554acc2bb3f9cff7ea3c52d220fc373e8d"
|
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.212-alpha.5.tgz#5d34ecfe798058892835ddb5eda20947ee04945b"
|
||||||
integrity sha512-DniDV+gPkw6olCrvT3ghuNTLzW8BVuxXnJP4QVgualXgJqo8xwnmOJCzu9slb9W1G3E+Laqk7Lhpn8jHjkTPDA==
|
integrity sha512-BhyEp1EXnwSoX1DkmACnv/ABI53sOv+gRMMxAmFSrz8btWCrAT57yzsWi5p5lU9Ri8CtuYVussCeKNiocLYEEw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/types" "^1.0.207-alpha.8"
|
"@budibase/types" "^1.0.212-alpha.5"
|
||||||
"@techpass/passport-openidconnect" "0.3.2"
|
"@techpass/passport-openidconnect" "0.3.2"
|
||||||
aws-sdk "2.1030.0"
|
aws-sdk "2.1030.0"
|
||||||
bcrypt "5.0.1"
|
bcrypt "5.0.1"
|
||||||
|
@ -323,18 +323,18 @@
|
||||||
uuid "8.3.2"
|
uuid "8.3.2"
|
||||||
zlib "1.0.5"
|
zlib "1.0.5"
|
||||||
|
|
||||||
"@budibase/pro@1.0.207-alpha.8":
|
"@budibase/pro@1.0.212-alpha.5":
|
||||||
version "1.0.207-alpha.8"
|
version "1.0.212-alpha.5"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.207-alpha.8.tgz#77d70a60c9d93dce6620d52008c4e5d0485b3d94"
|
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.212-alpha.5.tgz#7372aba798016e98268653455f5d11f42f0ba4e5"
|
||||||
integrity sha512-q6n/RVjtGVMII65yU50yuZZDBZOBh+lmmt7p/RwXftinsmPY0GK6g6cEXKi4Rya8GytCY2D+XG4ibl2AkQYM5w==
|
integrity sha512-ouO9ZrvCF9PjRQPhrv0GFhwwtRowyDkkGLgsz99pSiZOzwkJBIeMtxpSCMytTQzGCfrSEISlFct6DTTX3qByNA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/backend-core" "1.0.207-alpha.8"
|
"@budibase/backend-core" "1.0.212-alpha.5"
|
||||||
node-fetch "^2.6.1"
|
node-fetch "^2.6.1"
|
||||||
|
|
||||||
"@budibase/types@^1.0.207-alpha.8":
|
"@budibase/types@^1.0.212-alpha.5":
|
||||||
version "1.0.208"
|
version "1.0.212"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.0.208.tgz#c45cb494fb5b85229e15a34c6ac1805bae5be867"
|
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.0.212.tgz#e66a15b711544b4fab7767261fd5f2f1dd7f40d7"
|
||||||
integrity sha512-zKIHg6TGK+soVxMNZNrGypP3DCrd3jhlUQEFeQ+rZR6/tCue1G74bjzydY5FjnLEsXeLH1a0hkS5HulTmvQ2bA==
|
integrity sha512-DhGyw6snwJQZQlx7havVYnqPZfZERueKZfmVCBySzwInZZt0+sXZaBl1BVjGjYuwpaUQBMDBf7geBgHXp6DIKg==
|
||||||
|
|
||||||
"@cspotcode/source-map-consumer@0.8.0":
|
"@cspotcode/source-map-consumer@0.8.0":
|
||||||
version "0.8.0"
|
version "0.8.0"
|
||||||
|
|
Loading…
Reference in a new issue