mirror of
https://github.com/ToolJet/ToolJet
synced 2026-04-21 21:47:17 +00:00
Merge pull request #13671 from ToolJet/merge-to-main/3.16.1-mgs
Merge to main v3.16.1-lts
This commit is contained in:
commit
4cfd39afe6
62 changed files with 1094 additions and 217 deletions
4
.github/workflows/cloud-frontend.yml
vendored
4
.github/workflows/cloud-frontend.yml
vendored
|
|
@ -127,7 +127,7 @@ jobs:
|
|||
SENTRY_PROJECT: ${{ secrets.CLOUD_PROD_CLOUD_SENTRY_PROJECT }}
|
||||
SERVE_CLIENT: ${{ secrets.CLOUD_PROD_CLOUD_SERVE_CLIENT }}
|
||||
SERVER_IP: ${{ secrets.CLOUD_PROD_CLOUD_SERVER_IP }}
|
||||
TJDB_SQL_MODE_DISABLE: ${{ secrets.CLOUD_TJDB_SQL_MODE_DISABLE }}
|
||||
TOOLJET_SERVER_URL: ${{ secrets.CLOUD_TOOLJET_SERVER_URL }}
|
||||
TJDB_SQL_MODE_DISABLE: ${{ secrets.CLOUD_PROD_TJDB_SQL_MODE_DISABLE }}
|
||||
TOOLJET_SERVER_URL: ${{ secrets.CLOUD_PROD_TOOLJET_SERVER_URL }}
|
||||
WEBSITE_SIGNUP_URL: https://tooljet.ai/ai-create-account
|
||||
TOOLJET_EDITION: cloud
|
||||
|
|
|
|||
4
.github/workflows/deploy-to-stage.yml
vendored
4
.github/workflows/deploy-to-stage.yml
vendored
|
|
@ -10,10 +10,10 @@ on:
|
|||
dockerfile_path:
|
||||
description: 'Path to Dockerfile'
|
||||
required: true
|
||||
default: './docker/cloud/cloud-server.Dockerfile'
|
||||
default: './docker/LTS/cloud/cloud-server.Dockerfile'
|
||||
type: choice
|
||||
options:
|
||||
- ./docker/cloud/cloud-server.Dockerfile
|
||||
- ./docker/LTS/cloud/cloud-server.Dockerfile
|
||||
docker_tag:
|
||||
description: 'Docker tag suffix (e.g., cloud-staging-v14)'
|
||||
required: true
|
||||
|
|
|
|||
21
.github/workflows/docker-release.yml
vendored
21
.github/workflows/docker-release.yml
vendored
|
|
@ -81,17 +81,6 @@ jobs:
|
|||
curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"$message\"}" ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
|
||||
|
||||
- name: Send Slack Notification
|
||||
run: |
|
||||
if [[ "${{ job.status }}" == "success" ]]; then
|
||||
message="ToolJet community image published:\n\`tooljet/tooljet-ce:${{ github.event.release.tag_name }}\`"
|
||||
else
|
||||
message="Job '${{ env.JOB_NAME }}' failed! tooljet/tooljet-ce:${{ github.event.release.tag_name }}"
|
||||
fi
|
||||
|
||||
curl -X POST -H 'Content-type: application/json' --data "{\"text\":\"$message\"}" ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
|
||||
|
||||
build-tooljet-image-for-ee-edtion:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
|
@ -140,7 +129,7 @@ jobs:
|
|||
build-args: |
|
||||
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||
BRANCH_NAME=main
|
||||
file: docker/ee/ee-production.Dockerfile
|
||||
file: docker/pre-release/ee/ee-production.Dockerfile
|
||||
push: true
|
||||
tags: tooljet/tooljet-ee:${{ github.event.release.tag_name }},tooljet/tooljet-ee:ee-latest,tooljet/tooljet:ee-latest,tooljet/tooljet:${{ github.event.release.tag_name }}
|
||||
platforms: linux/amd64
|
||||
|
|
@ -156,7 +145,7 @@ jobs:
|
|||
build-args: |
|
||||
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||
BRANCH_NAME=lts-3.16
|
||||
file: docker/ee/ee-production.Dockerfile
|
||||
file: docker/LTS/ee/ee-production.Dockerfile
|
||||
push: true
|
||||
tags: tooljet/tooljet-ee:${{ github.event.release.tag_name }},tooljet/tooljet-ee:ee-lts-latest,tooljet/tooljet:ee-lts-latest,tooljet/tooljet:${{ github.event.release.tag_name }}
|
||||
platforms: linux/amd64
|
||||
|
|
@ -216,7 +205,7 @@ jobs:
|
|||
build-args: |
|
||||
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||
BRANCH_NAME=lts-3.16
|
||||
file: docker/cloud/cloud-server.Dockerfile
|
||||
file: docker/LTS/cloud/cloud-server.Dockerfile
|
||||
push: true
|
||||
tags: tooljet/saas:${{ github.event.release.tag_name }}
|
||||
platforms: linux/amd64
|
||||
|
|
@ -295,7 +284,7 @@ jobs:
|
|||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: docker/ee/ee-try-tooljet.Dockerfile
|
||||
file: docker/pre-release/ee/ee-try-tooljet.Dockerfile
|
||||
push: true
|
||||
tags: tooljet/try:${{ github.event.release.tag_name }},tooljet/try:ee-latest
|
||||
platforms: linux/amd64
|
||||
|
|
@ -308,7 +297,7 @@ jobs:
|
|||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: docker/ee/ee-try-tooljet-lts.Dockerfile
|
||||
file: docker/LTS/ee/ee-try-tooljet-lts.Dockerfile
|
||||
push: true
|
||||
tags: tooljet/try:${{ github.event.release.tag_name }},tooljet/try:ee-lts-latest
|
||||
platforms: linux/amd64
|
||||
|
|
|
|||
4
.github/workflows/merging-pr.yml
vendored
4
.github/workflows/merging-pr.yml
vendored
|
|
@ -6,7 +6,7 @@ on:
|
|||
|
||||
jobs:
|
||||
merge-submodules:
|
||||
if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main'
|
||||
if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main' && github.event.pull_request.base.ref == 'lts-3.16'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
|
@ -40,7 +40,7 @@ jobs:
|
|||
|
||||
update-submodule-sha:
|
||||
needs: merge-submodules
|
||||
if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main'
|
||||
if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main' && github.event.pull_request.base.ref == 'lts-3.16'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
|
|
|||
6
.github/workflows/packer-build.yml
vendored
6
.github/workflows/packer-build.yml
vendored
|
|
@ -1,8 +1,8 @@
|
|||
name: AWS AMI build using Packer config
|
||||
|
||||
on:
|
||||
# release:
|
||||
# types: [published]
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
|
@ -56,7 +56,7 @@ jobs:
|
|||
|
||||
# Echo RENDER_GITHUB_PAT
|
||||
- name: Set PACKER_GITHUB_PAT
|
||||
run: echo "PACKER_GITHUB_PAT=${{ secrets.PACKER_GITHUB_PAT}}" >> $GITHUB_ENV
|
||||
run: echo "PACKER_GITHUB_PAT=${{ secrets.CUSTOM_GITHUB_TOKEN }}" >> $GITHUB_ENV
|
||||
|
||||
# Dynamically update setup_machine.sh with PAT
|
||||
- name: Validate PAT
|
||||
|
|
|
|||
21
.github/workflows/render-preview-deploy.yml
vendored
21
.github/workflows/render-preview-deploy.yml
vendored
|
|
@ -427,6 +427,25 @@ jobs:
|
|||
if: env.is_fork == 'false'
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Detect base branch and set Dockerfile path
|
||||
run: |
|
||||
BASE_BRANCH="${{ github.event.pull_request.base.ref }}"
|
||||
echo "Base branch: $BASE_BRANCH"
|
||||
|
||||
if [[ "$BASE_BRANCH" == "main" || "$BASE_BRANCH" == release/* ]]; then
|
||||
DOCKERFILE="./docker/pre-release/ee/ee-preview.Dockerfile"
|
||||
echo "Using pre-release track"
|
||||
elif [[ "$BASE_BRANCH" == "lts-3.16" || "$BASE_BRANCH" == release-lts/* ]]; then
|
||||
DOCKERFILE="./docker/LTS/ee/ee-preview.Dockerfile"
|
||||
echo "Using LTS track"
|
||||
else
|
||||
echo "Error: Unsupported base branch '$BASE_BRANCH'"
|
||||
echo "Supported branches: main, release/*, lts-3.16, release-lts/*"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Selected Dockerfile: $DOCKERFILE"
|
||||
echo "DOCKERFILE=$DOCKERFILE" >> $GITHUB_ENV
|
||||
|
||||
- name: Creating deployment for Enterprise Edition
|
||||
id: create-ee-deployment
|
||||
|
|
@ -584,7 +603,7 @@ jobs:
|
|||
"envSpecificDetails": {
|
||||
"dockerCommand": "",
|
||||
"dockerContext": "./",
|
||||
"dockerfilePath": "./docker/ee/ee-preview.Dockerfile"
|
||||
"dockerfilePath": "'"$DOCKERFILE"'"
|
||||
},
|
||||
"healthCheckPath": "/api/health",
|
||||
"numInstances": 1,
|
||||
|
|
|
|||
2
.version
2
.version
|
|
@ -1 +1 @@
|
|||
3.16.0
|
||||
3.16.1-lts
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ mkdir -p ~/app
|
|||
git config --global url."https://x-access-token:CUSTOM_GITHUB_TOKEN@github.com/".insteadOf "https://github.com/"
|
||||
|
||||
#The below url will be edited dynamically when actions is triggered
|
||||
git clone -b main https://github.com/ToolJet/ToolJet.git ~/app && cd ~/app
|
||||
git clone -b lts-3.16 https://github.com/ToolJet/ToolJet.git ~/app && cd ~/app
|
||||
git submodule update --init --recursive
|
||||
git submodule foreach 'git checkout main || true'
|
||||
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@ RUN git checkout ${BRANCH_NAME}
|
|||
|
||||
RUN git submodule update --init --recursive
|
||||
|
||||
# Checkout the same branch in submodules if it exists, otherwise stay on default branch
|
||||
# Checkout the same branch in submodules if it exists, otherwise fallback to lts-3.16
|
||||
RUN git submodule foreach " \
|
||||
if git show-ref --verify --quiet refs/heads/${BRANCH_NAME} || \
|
||||
git ls-remote --exit-code --heads origin ${BRANCH_NAME}; then \
|
||||
git checkout ${BRANCH_NAME}; \
|
||||
else \
|
||||
echo 'Branch ${BRANCH_NAME} not found in submodule \$name, falling back to main'; \
|
||||
git checkout main; \
|
||||
echo 'Branch ${BRANCH_NAME} not found in submodule \$name, falling back to lts-3.16'; \
|
||||
git checkout lts-3.16; \
|
||||
fi"
|
||||
|
||||
# Scripts for building
|
||||
|
|
@ -127,7 +127,7 @@ COPY --from=builder /app/server/scripts ./app/server/scripts
|
|||
COPY --from=builder /app/server/dist ./app/server/dist
|
||||
COPY --from=builder --chown=appuser:0 /app/server/ee/ai/assets ./app/server/ee/ai/assets
|
||||
|
||||
COPY ./docker/cloud/cloud-entrypoint.sh ./app/server/cloud-entrypoint.sh
|
||||
COPY ./docker/LTS/cloud/cloud-entrypoint.sh ./app/server/cloud-entrypoint.sh
|
||||
|
||||
# Define non-sudo user
|
||||
RUN useradd --create-home --home-dir /home/appuser appuser \
|
||||
|
|
@ -26,14 +26,14 @@ RUN git checkout ${BRANCH_NAME}
|
|||
|
||||
RUN git submodule update --init --recursive
|
||||
|
||||
# Checkout the same branch in submodules if it exists, otherwise stay on default branch
|
||||
# Checkout the same branch in submodules if it exists, otherwise fallback to lts-3.16
|
||||
RUN git submodule foreach " \
|
||||
if git show-ref --verify --quiet refs/heads/${BRANCH_NAME} || \
|
||||
git ls-remote --exit-code --heads origin ${BRANCH_NAME}; then \
|
||||
git checkout ${BRANCH_NAME}; \
|
||||
else \
|
||||
echo 'Branch ${BRANCH_NAME} not found in submodule \$name, falling back to main'; \
|
||||
git checkout main; \
|
||||
echo 'Branch ${BRANCH_NAME} not found in submodule \$name, falling back to lts-3.16'; \
|
||||
git checkout lts-3.16; \
|
||||
fi"
|
||||
|
||||
|
||||
|
|
@ -115,7 +115,7 @@ COPY --from=builder /app/server/scripts ./app/server/scripts
|
|||
COPY --from=builder /app/server/dist ./app/server/dist
|
||||
COPY --from=builder --chown=appuser:0 /app/server/ee/ai/assets ./app/server/ee/ai/assets
|
||||
|
||||
COPY ./docker/cloud/cloud-entrypoint.sh ./app/server/cloud-entrypoint.sh
|
||||
COPY ./docker/LTS/cloud/cloud-entrypoint.sh ./app/server/cloud-entrypoint.sh
|
||||
|
||||
|
||||
# Installing git for simple git commands
|
||||
184
docker/LTS/ee/ee-preview.Dockerfile
Normal file
184
docker/LTS/ee/ee-preview.Dockerfile
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
FROM node:22.15.1 AS builder
|
||||
# Fix for JS heap limit allocation issue
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
RUN mkdir -p /app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Set GitHub token and branch as build arguments
|
||||
ARG CUSTOM_GITHUB_TOKEN
|
||||
ARG BRANCH_NAME
|
||||
|
||||
# Clone and checkout the frontend repository
|
||||
RUN git config --global url."https://x-access-token:${CUSTOM_GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/"
|
||||
|
||||
RUN git config --global http.version HTTP/1.1
|
||||
RUN git config --global http.postBuffer 524288000
|
||||
RUN git clone https://github.com/ToolJet/ToolJet.git .
|
||||
|
||||
# The branch name needs to be changed the branch with modularisation in CE repo
|
||||
RUN git checkout ${BRANCH_NAME}
|
||||
|
||||
RUN git submodule update --init --recursive
|
||||
|
||||
# Checkout the same branch in submodules if it exists, otherwise fallback to lts-3.16
|
||||
RUN git submodule foreach " \
|
||||
if git show-ref --verify --quiet refs/heads/${BRANCH_NAME} || \
|
||||
git ls-remote --exit-code --heads origin ${BRANCH_NAME}; then \
|
||||
git checkout ${BRANCH_NAME}; \
|
||||
else \
|
||||
echo 'Branch ${BRANCH_NAME} not found in submodule \$name, falling back to lts-3.16'; \
|
||||
git checkout lts-3.16; \
|
||||
fi"
|
||||
|
||||
# Scripts for building
|
||||
COPY ./package.json ./package.json
|
||||
|
||||
# Build plugins
|
||||
COPY ./plugins/package.json ./plugins/package-lock.json ./plugins/
|
||||
RUN npm --prefix plugins install
|
||||
COPY ./plugins/ ./plugins/
|
||||
RUN NODE_ENV=production npm --prefix plugins run build
|
||||
RUN npm --prefix plugins prune --production
|
||||
|
||||
ENV TOOLJET_EDITION=ee
|
||||
|
||||
# Build frontend
|
||||
COPY ./frontend/package.json ./frontend/package-lock.json ./frontend/
|
||||
RUN npm --prefix frontend install
|
||||
COPY ./frontend/ ./frontend/
|
||||
RUN npm --prefix frontend run build --production
|
||||
RUN npm --prefix frontend prune --production
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV TOOLJET_EDITION=ee
|
||||
|
||||
# Build server
|
||||
COPY ./server/package.json ./server/package-lock.json ./server/
|
||||
RUN npm --prefix server install
|
||||
COPY ./server/ ./server/
|
||||
RUN npm install -g @nestjs/cli
|
||||
RUN npm install -g copyfiles
|
||||
RUN npm --prefix server run build
|
||||
|
||||
FROM node:22.15.1-bullseye
|
||||
|
||||
RUN apt-get update -yq \
|
||||
&& apt-get install curl gnupg zip -yq \
|
||||
&& apt-get install -yq build-essential \
|
||||
&& apt-get clean -y
|
||||
|
||||
# copy postgrest executable
|
||||
COPY --from=postgrest/postgrest:v12.2.0 /bin/postgrest /bin
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV TOOLJET_EDITION=ee
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
RUN apt-get update && apt-get install -y freetds-dev libaio1 wget supervisor
|
||||
|
||||
# Install Instantclient Basic Light Oracle and Dependencies
|
||||
WORKDIR /opt/oracle
|
||||
RUN wget https://tooljet-plugins-production.s3.us-east-2.amazonaws.com/marketplace-assets/oracledb/instantclients/instantclient-basiclite-linuxx64.zip && \
|
||||
wget https://tooljet-plugins-production.s3.us-east-2.amazonaws.com/marketplace-assets/oracledb/instantclients/instantclient-basiclite-linux.x64-11.2.0.4.0.zip && \
|
||||
unzip instantclient-basiclite-linuxx64.zip && rm -f instantclient-basiclite-linuxx64.zip && \
|
||||
unzip instantclient-basiclite-linux.x64-11.2.0.4.0.zip && rm -f instantclient-basiclite-linux.x64-11.2.0.4.0.zip && \
|
||||
cd /opt/oracle/instantclient_21_10 && rm -f *jdbc* *occi* *mysql* *mql1* *ipc1* *jar uidrvci genezi adrci && \
|
||||
cd /opt/oracle/instantclient_11_2 && rm -f *jdbc* *occi* *mysql* *mql1* *ipc1* *jar uidrvci genezi adrci && \
|
||||
echo /opt/oracle/instantclient* > /etc/ld.so.conf.d/oracle-instantclient.conf && ldconfig
|
||||
# Set the Instant Client library paths
|
||||
ENV LD_LIBRARY_PATH="/opt/oracle/instantclient_11_2:/opt/oracle/instantclient_21_10:${LD_LIBRARY_PATH}"
|
||||
|
||||
WORKDIR /
|
||||
|
||||
# copy npm scripts
|
||||
COPY --from=builder /app/package.json ./app/package.json
|
||||
# copy plugins dependencies
|
||||
COPY --from=builder /app/plugins/dist ./app/plugins/dist
|
||||
COPY --from=builder /app/plugins/client.js ./app/plugins/client.js
|
||||
COPY --from=builder /app/plugins/node_modules ./app/plugins/node_modules
|
||||
COPY --from=builder /app/plugins/packages/common ./app/plugins/packages/common
|
||||
COPY --from=builder /app/plugins/package.json ./app/plugins/package.json
|
||||
# copy frontend build
|
||||
COPY --from=builder /app/frontend/build ./app/frontend/build
|
||||
# copy server build
|
||||
COPY --from=builder /app/server/package.json ./app/server/package.json
|
||||
COPY --from=builder /app/server/.version ./app/server/.version
|
||||
COPY --from=builder /app/server/ee/keys ./app/server/ee/keys
|
||||
COPY --from=builder /app/server/node_modules ./app/server/node_modules
|
||||
COPY --from=builder /app/server/templates ./app/server/templates
|
||||
COPY --from=builder /app/server/scripts ./app/server/scripts
|
||||
COPY --from=builder /app/server/dist ./app/server/dist
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install PostgreSQL
|
||||
USER root
|
||||
RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
|
||||
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list
|
||||
RUN apt update && apt -y install postgresql-13 postgresql-client-13 supervisor --fix-missing
|
||||
|
||||
|
||||
# Explicitly create PG main directory with correct ownership
|
||||
RUN mkdir -p /var/lib/postgresql/13/main && \
|
||||
chown -R postgres:postgres /var/lib/postgresql
|
||||
|
||||
RUN mkdir -p /var/log/supervisor /var/run/postgresql && \
|
||||
chown -R postgres:postgres /var/run/postgresql /var/log/supervisor
|
||||
|
||||
# Remove existing data and create directory with proper ownership
|
||||
RUN rm -rf /var/lib/postgresql/13/main && \
|
||||
mkdir -p /var/lib/postgresql/13/main && \
|
||||
chown -R postgres:postgres /var/lib/postgresql
|
||||
|
||||
# Initialize PostgreSQL
|
||||
RUN su - postgres -c "/usr/lib/postgresql/13/bin/initdb -D /var/lib/postgresql/13/main"
|
||||
|
||||
# Configure Supervisor to manage PostgREST, ToolJet, and Redis
|
||||
RUN echo "[supervisord] \n" \
|
||||
"nodaemon=true \n" \
|
||||
"user=root \n" \
|
||||
"\n" \
|
||||
"[program:postgrest] \n" \
|
||||
"command=/bin/postgrest \n" \
|
||||
"autostart=true \n" \
|
||||
"autorestart=true \n" \
|
||||
"\n" \
|
||||
"[program:tooljet] \n" \
|
||||
"user=root \n" \
|
||||
"command=/bin/bash -c '/app/server/scripts/boot.sh' \n" \
|
||||
"autostart=true \n" \
|
||||
"autorestart=true \n" \
|
||||
"stderr_logfile=/dev/stdout \n" \
|
||||
"stderr_logfile_maxbytes=0 \n" \
|
||||
"stdout_logfile=/dev/stdout \n" \
|
||||
"stdout_logfile_maxbytes=0 \n" | sed 's/ //' > /etc/supervisor/conf.d/supervisord.conf
|
||||
|
||||
# ENV defaults
|
||||
ENV TOOLJET_HOST=http://localhost \
|
||||
PORT=80 \
|
||||
NODE_ENV=production \
|
||||
LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key \
|
||||
SECRET_KEY_BASE=replace_with_secret_key_base \
|
||||
PG_DB=tooljet_production \
|
||||
PG_USER=postgres \
|
||||
PG_PASS=postgres \
|
||||
PG_HOST=localhost \
|
||||
ENABLE_TOOLJET_DB=true \
|
||||
TOOLJET_DB_HOST=localhost \
|
||||
TOOLJET_DB_USER=postgres \
|
||||
TOOLJET_DB_PASS=postgres \
|
||||
TOOLJET_DB=tooljet_db \
|
||||
PGRST_HOST=http://localhost:3000 \
|
||||
PGRST_DB_URI=postgres://postgres:postgres@localhost/tooljet_db \
|
||||
PGRST_JWT_SECRET=r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj \
|
||||
PGRST_DB_PRE_CONFIG=postgrest.pre_config \
|
||||
ORM_LOGGING=true \
|
||||
DEPLOYMENT_PLATFORM=docker:local \
|
||||
HOME=/home/appuser \
|
||||
TERM=xterm
|
||||
|
||||
|
||||
RUN chmod +x ./server/scripts/preview.sh
|
||||
# Set the entrypoint
|
||||
ENTRYPOINT ["./server/scripts/preview.sh"]
|
||||
214
docker/LTS/ee/ee-production.Dockerfile
Normal file
214
docker/LTS/ee/ee-production.Dockerfile
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
FROM node:22.15.1 AS builder
|
||||
|
||||
# Fix for JS heap limit allocation issue
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
RUN npm i -g npm@10.9.2 && npm cache clean --force
|
||||
|
||||
RUN mkdir -p /app
|
||||
WORKDIR /app
|
||||
|
||||
# Set GitHub token and branch as build arguments
|
||||
ARG CUSTOM_GITHUB_TOKEN
|
||||
ARG BRANCH_NAME
|
||||
|
||||
# Clone and checkout the frontend repository
|
||||
RUN git config --global url."https://x-access-token:${CUSTOM_GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/"
|
||||
|
||||
RUN git config --global http.version HTTP/1.1
|
||||
RUN git config --global http.postBuffer 524288000
|
||||
RUN git clone https://github.com/ToolJet/ToolJet.git .
|
||||
|
||||
# The branch name needs to be changed the branch with modularisation in CE repo
|
||||
RUN git checkout ${BRANCH_NAME}
|
||||
|
||||
RUN git submodule update --init --recursive
|
||||
|
||||
# Checkout the same branch in submodules if it exists, otherwise fallback to lts-3.16
|
||||
RUN git submodule foreach " \
|
||||
if git show-ref --verify --quiet refs/heads/${BRANCH_NAME} || \
|
||||
git ls-remote --exit-code --heads origin ${BRANCH_NAME}; then \
|
||||
git checkout ${BRANCH_NAME}; \
|
||||
else \
|
||||
echo 'Branch ${BRANCH_NAME} not found in submodule \$name, falling back to lts-3.16'; \
|
||||
git checkout lts-3.16; \
|
||||
fi"
|
||||
|
||||
# Scripts for building
|
||||
COPY ./package.json ./package.json
|
||||
|
||||
# Build plugins
|
||||
COPY ./plugins/package.json ./plugins/package-lock.json ./plugins/
|
||||
RUN npm --prefix plugins ci --omit=dev
|
||||
COPY ./plugins/ ./plugins/
|
||||
RUN NODE_ENV=production npm --prefix plugins run build && npm --prefix plugins prune --omit=dev
|
||||
|
||||
ENV TOOLJET_EDITION=ee
|
||||
|
||||
# Build frontend
|
||||
COPY ./frontend/package.json ./frontend/package-lock.json ./frontend/
|
||||
RUN npm --prefix frontend install
|
||||
COPY ./frontend/ ./frontend/
|
||||
RUN npm --prefix frontend run build --production && npm --prefix frontend prune --production
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV TOOLJET_EDITION=ee
|
||||
|
||||
# Build server
|
||||
COPY ./server/package.json ./server/package-lock.json ./server/
|
||||
RUN npm --prefix server ci --omit=dev
|
||||
COPY ./server/ ./server/
|
||||
RUN npm install -g @nestjs/cli && npm install -g copyfiles
|
||||
RUN npm --prefix server run build && npm prune --production --prefix server
|
||||
|
||||
# Install dependencies for PostgREST, curl, tar, etc.
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl ca-certificates tar \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV POSTGREST_VERSION=v12.2.0
|
||||
|
||||
RUN curl -Lo postgrest.tar.xz https://github.com/PostgREST/postgrest/releases/download/${POSTGREST_VERSION}/postgrest-v12.2.0-linux-static-x64.tar.xz && \
|
||||
tar -xf postgrest.tar.xz && \
|
||||
mv postgrest /postgrest && \
|
||||
rm postgrest.tar.xz && \
|
||||
chmod +x /postgrest
|
||||
|
||||
FROM debian:12-slim
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
wget \
|
||||
gnupg \
|
||||
unzip \
|
||||
ca-certificates \
|
||||
xz-utils \
|
||||
tar \
|
||||
postgresql-client \
|
||||
redis \
|
||||
libaio1 \
|
||||
git \
|
||||
openssh-client \
|
||||
freetds-dev \
|
||||
&& apt-get upgrade -y -o Dpkg::Options::="--force-confold" \
|
||||
&& apt-get autoremove -y \
|
||||
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
RUN curl -O https://nodejs.org/dist/v22.15.1/node-v22.15.1-linux-x64.tar.xz \
|
||||
&& tar -xf node-v22.15.1-linux-x64.tar.xz \
|
||||
&& mv node-v22.15.1-linux-x64 /usr/local/lib/nodejs \
|
||||
&& echo 'export PATH="/usr/local/lib/nodejs/bin:$PATH"' >> /etc/profile.d/nodejs.sh \
|
||||
&& /bin/bash -c "source /etc/profile.d/nodejs.sh" \
|
||||
&& rm node-v22.15.1-linux-x64.tar.xz
|
||||
ENV PATH=/usr/local/lib/nodejs/bin:$PATH
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV TOOLJET_EDITION=ee
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
|
||||
# Install Neo4j + APOC
|
||||
RUN wget -O - https://debian.neo4j.com/neotechnology.gpg.key | apt-key add - && \
|
||||
echo "deb https://debian.neo4j.com stable 5" > /etc/apt/sources.list.d/neo4j.list && \
|
||||
apt-get update && apt-get install -y neo4j=1:5.26.6 && apt-mark hold neo4j && \
|
||||
mkdir -p /var/lib/neo4j/plugins && \
|
||||
wget -P /var/lib/neo4j/plugins https://github.com/neo4j/apoc/releases/download/5.26.6/apoc-5.26.6-core.jar && \
|
||||
echo "dbms.security.procedures.unrestricted=apoc.*" >> /etc/neo4j/neo4j.conf && \
|
||||
echo "dbms.security.procedures.allowlist=apoc.*,algo.*,gds.*" >> /etc/neo4j/neo4j.conf && \
|
||||
echo "dbms.directories.plugins=/var/lib/neo4j/plugins" >> /etc/neo4j/neo4j.conf && \
|
||||
echo "dbms.security.auth_enabled=true" >> /etc/neo4j/neo4j.conf && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Instantclient Basic Light Oracle and Dependencies
|
||||
WORKDIR /opt/oracle
|
||||
|
||||
RUN wget https://tooljet-plugins-production.s3.us-east-2.amazonaws.com/marketplace-assets/oracledb/instantclients/instantclient-basiclite-linuxx64.zip && \
|
||||
wget https://tooljet-plugins-production.s3.us-east-2.amazonaws.com/marketplace-assets/oracledb/instantclients/instantclient-basiclite-linux.x64-11.2.0.4.0.zip && \
|
||||
unzip instantclient-basiclite-linuxx64.zip && rm -f instantclient-basiclite-linuxx64.zip && \
|
||||
unzip instantclient-basiclite-linux.x64-11.2.0.4.0.zip && rm -f instantclient-basiclite-linux.x64-11.2.0.4.0.zip && \
|
||||
cd /opt/oracle/instantclient_21_10 && rm -f *jdbc* *occi* *mysql* *mql1* *ipc1* *jar uidrvci genezi adrci && \
|
||||
cd /opt/oracle/instantclient_11_2 && rm -f *jdbc* *occi* *mysql* *mql1* *ipc1* *jar uidrvci genezi adrci && \
|
||||
echo /opt/oracle/instantclient* > /etc/ld.so.conf.d/oracle-instantclient.conf && ldconfig
|
||||
# Set the Instant Client library paths
|
||||
ENV LD_LIBRARY_PATH="/opt/oracle/instantclient_11_2:/opt/oracle/instantclient_21_10:${LD_LIBRARY_PATH}"
|
||||
|
||||
RUN rm -f *.zip *.key && apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /
|
||||
|
||||
RUN mkdir -p /app
|
||||
|
||||
RUN useradd --create-home --home-dir /home/appuser appuser
|
||||
|
||||
# Use the PostgREST binary from the builder stage
|
||||
COPY --from=builder --chown=appuser:0 /postgrest /usr/local/bin/postgrest
|
||||
|
||||
RUN mv /usr/local/bin/postgrest /usr/local/bin/postgrest-original && \
|
||||
echo '#!/bin/bash\nexec /usr/local/bin/postgrest-original "$@" 2>&1 | sed "s/^/[PostgREST] /"' > /usr/local/bin/postgrest && \
|
||||
chmod +x /usr/local/bin/postgrest
|
||||
|
||||
|
||||
# Copy application with ownership set directly to avoid chown -R
|
||||
COPY --from=builder --chown=appuser:0 /app/package.json ./app/package.json
|
||||
COPY --from=builder --chown=appuser:0 /app/plugins/dist ./app/plugins/dist
|
||||
COPY --from=builder --chown=appuser:0 /app/plugins/client.js ./app/plugins/client.js
|
||||
COPY --from=builder --chown=appuser:0 /app/plugins/node_modules ./app/plugins/node_modules
|
||||
COPY --from=builder --chown=appuser:0 /app/plugins/packages/common ./app/plugins/packages/common
|
||||
COPY --from=builder --chown=appuser:0 /app/plugins/package.json ./app/plugins/package.json
|
||||
COPY --from=builder --chown=appuser:0 /app/frontend/build ./app/frontend/build
|
||||
COPY --from=builder --chown=appuser:0 /app/server/package.json ./app/server/package.json
|
||||
COPY --from=builder --chown=appuser:0 /app/server/.version ./app/server/.version
|
||||
COPY --from=builder --chown=appuser:0 /app/server/ee/keys ./app/server/ee/keys
|
||||
COPY --from=builder --chown=appuser:0 /app/server/node_modules ./app/server/node_modules
|
||||
COPY --from=builder --chown=appuser:0 /app/server/templates ./app/server/templates
|
||||
COPY --from=builder --chown=appuser:0 /app/server/scripts ./app/server/scripts
|
||||
COPY --from=builder --chown=appuser:0 /app/server/dist ./app/server/dist
|
||||
COPY --from=builder --chown=appuser:0 /app/server/ee/ai/assets ./app/server/ee/ai/assets
|
||||
COPY ./docker/LTS/ee/ee-entrypoint.sh ./app/server/ee-entrypoint.sh
|
||||
|
||||
RUN mkdir -p /var/lib/neo4j/data/databases /var/lib/neo4j/data/transactions /var/log/neo4j /opt/neo4j/run && \
|
||||
chown -R appuser:0 /var/lib/neo4j /var/log/neo4j /etc/neo4j /opt/neo4j/run && \
|
||||
chmod -R 770 /var/lib/neo4j /var/log/neo4j /etc/neo4j /opt/neo4j/run && \
|
||||
chmod -R 644 /var/lib/neo4j/plugins/*.jar && \
|
||||
chown -R appuser:0 /var/lib/neo4j/plugins && \
|
||||
chmod 755 /var/lib/neo4j/plugins
|
||||
|
||||
# Create directory /home/appuser and set ownership to appuser
|
||||
RUN mkdir -p /home/appuser \
|
||||
&& chown -R appuser:0 /home/appuser \
|
||||
&& chmod g+s /home/appuser \
|
||||
&& chmod -R g=u /home/appuser \
|
||||
&& npm cache clean --force
|
||||
|
||||
# Create directory /tmp/.npm/npm-cache/ and set ownership to appuser
|
||||
RUN mkdir -p /tmp/.npm/npm-cache/ \
|
||||
&& chown -R appuser:0 /tmp/.npm/npm-cache/ \
|
||||
&& chmod g+s /tmp/.npm/npm-cache/ \
|
||||
&& chmod -R g=u /tmp/.npm/npm-cache \
|
||||
&& npm cache clean --force
|
||||
|
||||
# Set npm cache directory globally
|
||||
RUN npm config set cache /tmp/.npm/npm-cache/ --global
|
||||
ENV npm_config_cache /tmp/.npm/npm-cache/
|
||||
|
||||
# Create directory /tmp/.npm/npm-cache/_logs and set ownership to appuser
|
||||
RUN mkdir -p /tmp/.npm/npm-cache/_logs \
|
||||
&& chown -R appuser:0 /tmp/.npm/npm-cache/_logs \
|
||||
&& chmod g+s /tmp/.npm/npm-cache/_logs \
|
||||
&& chmod -R g=u /tmp/.npm/npm-cache/_logs
|
||||
|
||||
# Create Redis data, log, and configuration directories
|
||||
RUN mkdir -p /var/lib/redis /var/log/redis /etc/redis \
|
||||
&& chown -R appuser:0 /var/lib/redis /var/log/redis /etc/redis \
|
||||
&& chmod g+s /var/lib/redis /var/log/redis /etc/redis \
|
||||
&& chmod -R g=u /var/lib/redis /var/log/redis /etc/redis
|
||||
|
||||
ENV HOME=/home/appuser
|
||||
# Switch back to appuser
|
||||
USER appuser
|
||||
WORKDIR /app
|
||||
|
||||
RUN npm install --prefix server --no-save dotenv@10.0.0 joi@17.4.1 && npm cache clean --force
|
||||
|
||||
ENTRYPOINT ["./server/ee-entrypoint.sh"]
|
||||
|
|
@ -3,11 +3,12 @@ FROM tooljet/tooljet:ee-lts-latest
|
|||
# Copy postgrest executable
|
||||
COPY --from=postgrest/postgrest:v12.2.0 /bin/postgrest /bin
|
||||
|
||||
RUN apt-get update && apt-get install -y wget libicu72 libldap-2.5-0 libssl3 || true
|
||||
|
||||
# Install Postgres
|
||||
USER root
|
||||
RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
|
||||
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list
|
||||
RUN echo "deb http://deb.debian.org/debian"
|
||||
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bookworm-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list
|
||||
RUN apt update && apt -y install postgresql-13 postgresql-client-13 supervisor
|
||||
USER postgres
|
||||
RUN service postgresql start && \
|
||||
|
|
@ -49,8 +50,8 @@ RUN apt update && apt install -y gettext-base curl \
|
|||
&& curl -sSL https://github.com/fullstorydev/grpcurl/releases/download/v1.8.0/grpcurl_1.8.0_linux_x86_64.tar.gz | tar -xzv -C /usr/local/bin grpcurl
|
||||
|
||||
# Copy Temporal configuration files
|
||||
COPY ./docker/ee/temporal-server.yaml /etc/temporal/temporal-server.template.yaml
|
||||
COPY ./docker/ee/temporal-ui-server.yaml /etc/temporal/temporal-ui-server.yaml
|
||||
COPY ./docker/LTS/ee/temporal-server.yaml /etc/temporal/temporal-server.template.yaml
|
||||
COPY ./docker/LTS/ee/temporal-ui-server.yaml /etc/temporal/temporal-ui-server.yaml
|
||||
|
||||
# Install Neo4j + APOC
|
||||
RUN wget -O - https://debian.neo4j.com/neotechnology.gpg.key | apt-key add - && \
|
||||
|
|
@ -136,6 +137,6 @@ ENV TOOLJET_HOST=http://localhost \
|
|||
TEMPORAL_CORS_ORIGINS=http://localhost:8080
|
||||
|
||||
# Set the entrypoint
|
||||
COPY ./docker/ee/ee-try-entrypoint-lts.sh /ee-try-entrypoint-lts.sh
|
||||
RUN chmod +x /ee-try-entrypoint-lts
|
||||
COPY ./docker/LTS/ee/ee-try-entrypoint-lts.sh /ee-try-entrypoint-lts.sh
|
||||
RUN chmod +x /ee-try-entrypoint-lts.sh
|
||||
ENTRYPOINT ["/ee-try-entrypoint-lts.sh"]
|
||||
183
docker/pre-release/ee/ee-entrypoint.sh
Executable file
183
docker/pre-release/ee/ee-entrypoint.sh
Executable file
|
|
@ -0,0 +1,183 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
npm cache clean --force
|
||||
|
||||
# Load environment variables from .env if the file exists
|
||||
if [ -f "./.env" ]; then
|
||||
export $(grep -v '^#' ./.env | xargs -d '\n') || true
|
||||
fi
|
||||
|
||||
# Start Redis server only if REDIS_HOST is localhost or not set
|
||||
if [ -z "$REDIS_HOST" ] || [ "$REDIS_HOST" = "localhost" ]; then
|
||||
echo "Starting Redis server locally..."
|
||||
redis-server /etc/redis/redis.conf &
|
||||
elif [ -n "$REDIS_URL" ]; then
|
||||
echo "REDIS_URL connection is set: $REDIS_URL"
|
||||
else
|
||||
echo "Using external Redis at $REDIS_HOST:$REDIS_PORT."
|
||||
|
||||
# Validate external Redis connection
|
||||
if ! ./server/scripts/wait-for-it.sh "$REDIS_HOST:${REDIS_PORT:-6379}" --strict --timeout=300 -- echo "Redis is up"; then
|
||||
echo "Error: Unable to connect to Redis at $REDIS_HOST:$REDIS_PORT."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if PGRST_HOST starts with "localhost"
|
||||
if [[ "$PGRST_HOST" == localhost:* ]]; then
|
||||
echo "Starting PostgREST server locally..."
|
||||
|
||||
# Generate PostgREST configuration in a writable directory
|
||||
POSTGREST_CONFIG_PATH="/tmp/postgrest.conf"
|
||||
|
||||
echo "db-uri = \"${PGRST_DB_URI}\"" > "$POSTGREST_CONFIG_PATH"
|
||||
echo "db-pre-config = \"postgrest.pre_config\"" >> "$POSTGREST_CONFIG_PATH"
|
||||
echo "server-port = \"${PGRST_SERVER_PORT}\"" >> "$POSTGREST_CONFIG_PATH"
|
||||
|
||||
# Starting PostgREST
|
||||
echo "Starting PostgREST..."
|
||||
postgrest "$POSTGREST_CONFIG_PATH" &
|
||||
else
|
||||
echo "Using external PostgREST at $PGRST_HOST."
|
||||
fi
|
||||
|
||||
|
||||
# Check WORKLOW_WORKER and skip SETUP_CMD if true
|
||||
if [ "${WORKFLOW_WORKER}" == "true" ]; then
|
||||
echo "WORKFLOW_WORKER is set to true. Running worker process."
|
||||
npm run worker:prod
|
||||
else
|
||||
# Determine setup command based on the presence of ./server/dist
|
||||
if [ -d "./server/dist" ]; then
|
||||
SETUP_CMD='npm run db:setup:prod'
|
||||
else
|
||||
SETUP_CMD='npm run db:setup'
|
||||
fi
|
||||
fi
|
||||
|
||||
# Neo4j configuration
|
||||
# ----------------------------------
|
||||
# Default Neo4j environment values
|
||||
# ----------------------------------
|
||||
export NEO4J_USER=${NEO4J_USER:-"neo4j"}
|
||||
export NEO4J_PASSWORD=${NEO4J_PASSWORD:-"appaqvyvRLbeukhFE"}
|
||||
export NEO4J_AUTH=${NEO4J_AUTH:-"neo4j/appaqvyvRLbeukhFE"}
|
||||
export NEO4J_URI=${NEO4J_URI:-"bolt://localhost:7687"}
|
||||
export NEO4J_PLUGINS=${NEO4J_PLUGINS:-'["apoc"]'}
|
||||
export NEO4J_AUTH
|
||||
|
||||
# Extract username and password from NEO4J_AUTH if set
|
||||
if [ -n "$NEO4J_AUTH" ]; then
|
||||
# Extract username and password from NEO4J_AUTH (format: username/password)
|
||||
NEO4J_USERNAME=$(echo "$NEO4J_AUTH" | cut -d'/' -f1)
|
||||
NEO4J_PASSWORD=$(echo "$NEO4J_AUTH" | cut -d'/' -f2)
|
||||
|
||||
# Export these for application use
|
||||
export NEO4J_USERNAME
|
||||
export NEO4J_PASSWORD
|
||||
|
||||
echo "Neo4j authentication configured with username: $NEO4J_USERNAME" >/dev/null 2>&1
|
||||
else
|
||||
echo "NEO4J_AUTH not set, using default authentication" >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
# Check if Neo4j is already initialized and set password if necessary
|
||||
if [ "$NEO4J_AUTH" != "none" ] && [ -n "$NEO4J_PASSWORD" ]; then
|
||||
echo "Setting Neo4j initial password..." >/dev/null 2>&1
|
||||
|
||||
# Ensure Neo4j is not running before setting the initial password
|
||||
neo4j stop || true
|
||||
|
||||
# Set the initial password using the correct command format for Neo4j 5.x
|
||||
NEO4J_ADMIN_CMD=$(which neo4j-admin)
|
||||
NEO4J_VERSION=$(neo4j --version | grep -o "[0-9]\+\.[0-9]\+\.[0-9]\+" | head -n 1)
|
||||
echo "Detected Neo4j version: $NEO4J_VERSION" >/dev/null 2>&1
|
||||
|
||||
# Use version-specific command format
|
||||
MAJOR_VERSION=$(echo $NEO4J_VERSION | cut -d. -f1)
|
||||
if [ "$MAJOR_VERSION" -ge "5" ]; then
|
||||
# For Neo4j 5.x and higher
|
||||
echo "Using Neo4j 5.x+ password command format" >/dev/null 2>&1
|
||||
$NEO4J_ADMIN_CMD dbms set-initial-password "$NEO4J_PASSWORD" --require-password-change=false >/dev/null 2>&1 || {
|
||||
echo "Warning: Could not set Neo4j password, it may already be set" >/dev/null 2>&1
|
||||
}
|
||||
else
|
||||
# For Neo4j 4.x and lower
|
||||
echo "Using Neo4j 4.x password command format" >/dev/null 2>&1
|
||||
$NEO4J_ADMIN_CMD set-initial-password "$NEO4J_PASSWORD" >/dev/null 2>&1 || {
|
||||
echo "Warning: Could not set Neo4j password, it may already be set" >/dev/null 2>&1
|
||||
}
|
||||
fi
|
||||
fi
|
||||
|
||||
# Update Neo4j configuration
|
||||
echo "Configuring Neo4j..." >/dev/null 2>&1
|
||||
cat > /etc/neo4j/neo4j.conf << EOF
|
||||
# Neo4j configuration
|
||||
dbms.security.auth_enabled=true
|
||||
server.bolt.enabled=true
|
||||
server.bolt.listen_address=0.0.0.0:7687
|
||||
server.directories.data=/var/lib/neo4j/data
|
||||
server.directories.logs=/var/log/neo4j
|
||||
initial.dbms.default_database=neo4j
|
||||
server.directories.plugins=/var/lib/neo4j/plugins
|
||||
server.directories.import=/var/lib/neo4j/import
|
||||
|
||||
# APOC Settings
|
||||
dbms.security.procedures.unrestricted=apoc.*
|
||||
dbms.security.procedures.allowlist=apoc.*,algo.*,gds.*
|
||||
EOF
|
||||
|
||||
if [ -w "$NEO4J_LOG_DIR" ]; then
|
||||
chmod -R 770 "$NEO4J_LOG_DIR" || echo "Warning: Could not set log directory permissions" >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
# Start Neo4j
|
||||
echo "Starting Neo4j service..."
|
||||
neo4j console >/dev/null 2>&1 &
|
||||
|
||||
# Add a wait for Neo4j to be ready with more robust checking
|
||||
echo "Waiting for Neo4j to be ready..." >/dev/null 2>&1
|
||||
NEO4J_READY=false
|
||||
for i in {1..60}; do
|
||||
# First try standard status check
|
||||
if neo4j status >/dev/null 2>&1; then
|
||||
echo "Neo4j is ready 🚀"
|
||||
NEO4J_READY=true
|
||||
break
|
||||
fi
|
||||
|
||||
# Also try connecting to the bolt port as a fallback
|
||||
if command -v nc >/dev/null 2>&1; then
|
||||
if nc -z localhost 7687 >/dev/null 2>&1; then
|
||||
echo "Neo4j is ready (port 7687 is open)"
|
||||
NEO4J_READY=true
|
||||
break
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Waiting for Neo4j to start... ($i/60)" >/dev/null 2>&1
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ "$NEO4J_READY" = false ]; then
|
||||
echo "WARNING: Neo4j may not be fully started yet, but continuing..."
|
||||
fi
|
||||
|
||||
# Wait for PostgreSQL connection
|
||||
if [ -z "$DATABASE_URL" ]; then
|
||||
./server/scripts/wait-for-it.sh $PG_HOST:${PG_PORT:-5432} --strict --timeout=300 -- echo "PostgreSQL is up"
|
||||
else
|
||||
PG_HOST=$(echo "$DATABASE_URL" | awk -F'[/:@?]' '{print $6}')
|
||||
PG_PORT=$(echo "$DATABASE_URL" | awk -F'[/:@?]' '{print $7}')
|
||||
|
||||
./server/scripts/wait-for-it.sh "$PG_HOST:$PG_PORT" --strict --timeout=300 -- echo "PostgreSQL is up"
|
||||
fi
|
||||
|
||||
# Run setup command if defined
|
||||
if [ -n "$SETUP_CMD" ]; then
|
||||
$SETUP_CMD
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
|
|
@ -165,7 +165,7 @@ COPY --from=builder --chown=appuser:0 /app/server/templates ./app/server/templat
|
|||
COPY --from=builder --chown=appuser:0 /app/server/scripts ./app/server/scripts
|
||||
COPY --from=builder --chown=appuser:0 /app/server/dist ./app/server/dist
|
||||
COPY --from=builder --chown=appuser:0 /app/server/ee/ai/assets ./app/server/ee/ai/assets
|
||||
COPY ./docker/ee/ee-entrypoint.sh ./app/server/ee-entrypoint.sh
|
||||
COPY ./docker/pre-release/ee/ee-entrypoint.sh ./app/server/ee-entrypoint.sh
|
||||
|
||||
RUN mkdir -p /var/lib/neo4j/data/databases /var/lib/neo4j/data/transactions /var/log/neo4j /opt/neo4j/run && \
|
||||
chown -R appuser:0 /var/lib/neo4j /var/log/neo4j /etc/neo4j /opt/neo4j/run && \
|
||||
|
|
@ -1,13 +1,11 @@
|
|||
FROM tooljet/tooljet:ee-latest
|
||||
|
||||
# Copy postgrest executable
|
||||
COPY --from=postgrest/postgrest:v12.2.0 /bin/postgrest /bin
|
||||
RUN apt-get update && apt-get install -y wget libicu72 libldap-2.5-0 libssl3 || true
|
||||
|
||||
# Install Postgres
|
||||
USER root
|
||||
RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
|
||||
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list
|
||||
RUN echo "deb http://deb.debian.org/debian"
|
||||
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bookworm-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list
|
||||
RUN apt update && apt -y install postgresql-13 postgresql-client-13 supervisor
|
||||
USER postgres
|
||||
RUN service postgresql start && \
|
||||
|
|
@ -49,8 +47,8 @@ RUN apt update && apt install -y gettext-base curl \
|
|||
&& curl -sSL https://github.com/fullstorydev/grpcurl/releases/download/v1.8.0/grpcurl_1.8.0_linux_x86_64.tar.gz | tar -xzv -C /usr/local/bin grpcurl
|
||||
|
||||
# Copy Temporal configuration files
|
||||
COPY ./docker/ee/temporal-server.yaml /etc/temporal/temporal-server.template.yaml
|
||||
COPY ./docker/ee/temporal-ui-server.yaml /etc/temporal/temporal-ui-server.yaml
|
||||
COPY ./docker/pre-release/ee/temporal-server.yaml /etc/temporal/temporal-server.template.yaml
|
||||
COPY ./docker/pre-release/ee/temporal-ui-server.yaml /etc/temporal/temporal-ui-server.yaml
|
||||
|
||||
# Install Neo4j + APOC
|
||||
RUN wget -O - https://debian.neo4j.com/neotechnology.gpg.key | apt-key add - && \
|
||||
|
|
@ -136,6 +134,6 @@ ENV TOOLJET_HOST=http://localhost \
|
|||
TEMPORAL_CORS_ORIGINS=http://localhost:8080
|
||||
|
||||
# Set the entrypoint
|
||||
COPY ./docker/ee/ee-try-entrypoint.sh /ee-try-entrypoint.sh
|
||||
COPY ./docker/pre-release/ee/ee-try-entrypoint.sh /ee-try-entrypoint.sh
|
||||
RUN chmod +x /ee-try-entrypoint.sh
|
||||
ENTRYPOINT ["/ee-try-entrypoint.sh"]
|
||||
70
docker/pre-release/ee/temporal-server.yaml
Normal file
70
docker/pre-release/ee/temporal-server.yaml
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
log:
|
||||
stdout: true
|
||||
level: info
|
||||
|
||||
persistence:
|
||||
defaultStore: postgres-default
|
||||
visibilityStore: postgres-visibility
|
||||
numHistoryShards: 4
|
||||
dataStores:
|
||||
postgres-default:
|
||||
sql:
|
||||
pluginName: "postgres12"
|
||||
databaseName: "temporal"
|
||||
connectAddr: "localhost:5432"
|
||||
user: "tooljet"
|
||||
password: "postgres"
|
||||
postgres-visibility:
|
||||
sql:
|
||||
pluginName: "postgres12"
|
||||
databaseName: "temporal_visibility"
|
||||
connectAddr: "localhost:5432"
|
||||
user: "tooljet"
|
||||
password: "postgres"
|
||||
|
||||
global:
|
||||
membership:
|
||||
maxJoinDuration: 30s
|
||||
broadcastAddress: "127.0.0.1"
|
||||
pprof:
|
||||
port: 7936
|
||||
|
||||
services:
|
||||
frontend:
|
||||
rpc:
|
||||
grpcPort: 7233
|
||||
membershipPort: 6933
|
||||
bindOnLocalHost: true
|
||||
httpPort: 7243
|
||||
|
||||
matching:
|
||||
rpc:
|
||||
grpcPort: 7235
|
||||
membershipPort: 6935
|
||||
bindOnLocalHost: true
|
||||
|
||||
history:
|
||||
rpc:
|
||||
grpcPort: 7234
|
||||
membershipPort: 6934
|
||||
bindOnLocalHost: true
|
||||
|
||||
worker:
|
||||
rpc:
|
||||
membershipPort: 6939
|
||||
|
||||
clusterMetadata:
|
||||
enableGlobalNamespace: false
|
||||
failoverVersionIncrement: 10
|
||||
masterClusterName: "active"
|
||||
currentClusterName: "active"
|
||||
clusterInformation:
|
||||
active:
|
||||
enabled: true
|
||||
initialFailoverVersion: 1
|
||||
rpcName: "frontend"
|
||||
rpcAddress: "localhost:7233"
|
||||
httpAddress: "localhost:7243"
|
||||
|
||||
dcRedirectionPolicy:
|
||||
policy: "noop"
|
||||
8
docker/pre-release/ee/temporal-ui-server.yaml
Normal file
8
docker/pre-release/ee/temporal-ui-server.yaml
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
temporalGrpcAddress: 127.0.0.1:7233 # Use the correct Temporal server address
|
||||
host: 0.0.0.0
|
||||
port: 8080
|
||||
enableUi: true
|
||||
cors:
|
||||
allowOrigins:
|
||||
- http://localhost:8080
|
||||
defaultNamespace: default
|
||||
|
|
@ -1 +1 @@
|
|||
3.16.0
|
||||
3.16.1-lts
|
||||
|
|
|
|||
|
|
@ -151,34 +151,36 @@ export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => {
|
|||
function getMinWidth() {
|
||||
if (isModuleMode) return '100%';
|
||||
|
||||
const isSidebarOpenInEditor = currentMode === 'edit' ? isSidebarOpen : false;
|
||||
|
||||
const shouldAdjust = isSidebarOpen || (isRightSidebarOpen && currentMode === 'edit');
|
||||
|
||||
if (!shouldAdjust) return '';
|
||||
let offset;
|
||||
if (isViewerSidebarPinned && !isPagesSidebarHidden) {
|
||||
if (position === 'side' && isSidebarOpen && isRightSidebarOpen && !isPagesSidebarHidden) {
|
||||
if (position === 'side' && isSidebarOpenInEditor && isRightSidebarOpen && !isPagesSidebarHidden) {
|
||||
offset = `${LEFT_SIDEBAR_WIDTH + RIGHT_SIDEBAR_WIDTH - PAGES_SIDEBAR_WIDTH_EXPANDED}px`;
|
||||
} else if (position === 'side' && isSidebarOpen && !isRightSidebarOpen && !isPagesSidebarHidden) {
|
||||
} else if (position === 'side' && isSidebarOpenInEditor && !isRightSidebarOpen && !isPagesSidebarHidden) {
|
||||
offset = `${LEFT_SIDEBAR_WIDTH - PAGES_SIDEBAR_WIDTH_EXPANDED}px`;
|
||||
} else if (position === 'side' && isRightSidebarOpen && !isSidebarOpen && !isPagesSidebarHidden) {
|
||||
} else if (position === 'side' && isRightSidebarOpen && !isSidebarOpenInEditor && !isPagesSidebarHidden) {
|
||||
offset = `${RIGHT_SIDEBAR_WIDTH - PAGES_SIDEBAR_WIDTH_EXPANDED}px`;
|
||||
}
|
||||
} else {
|
||||
if (position === 'side' && isSidebarOpen && isRightSidebarOpen && !isPagesSidebarHidden) {
|
||||
if (position === 'side' && isSidebarOpenInEditor && isRightSidebarOpen && !isPagesSidebarHidden) {
|
||||
offset = `${LEFT_SIDEBAR_WIDTH + RIGHT_SIDEBAR_WIDTH - PAGES_SIDEBAR_WIDTH_COLLAPSED}px`;
|
||||
} else if (position === 'side' && isSidebarOpen && !isRightSidebarOpen && !isPagesSidebarHidden) {
|
||||
} else if (position === 'side' && isSidebarOpenInEditor && !isRightSidebarOpen && !isPagesSidebarHidden) {
|
||||
offset = `${LEFT_SIDEBAR_WIDTH - PAGES_SIDEBAR_WIDTH_COLLAPSED}px`;
|
||||
} else if (position === 'side' && isRightSidebarOpen && !isSidebarOpen && !isPagesSidebarHidden) {
|
||||
} else if (position === 'side' && isRightSidebarOpen && !isSidebarOpenInEditor && !isPagesSidebarHidden) {
|
||||
offset = `${RIGHT_SIDEBAR_WIDTH - PAGES_SIDEBAR_WIDTH_COLLAPSED}px`;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentMode === 'edit') {
|
||||
if ((position === 'top' || isPagesSidebarHidden) && isSidebarOpen && isRightSidebarOpen) {
|
||||
if ((position === 'top' || isPagesSidebarHidden) && isSidebarOpenInEditor && isRightSidebarOpen) {
|
||||
offset = `${LEFT_SIDEBAR_WIDTH + RIGHT_SIDEBAR_WIDTH}px`;
|
||||
} else if ((position === 'top' || isPagesSidebarHidden) && isSidebarOpen && !isRightSidebarOpen) {
|
||||
} else if ((position === 'top' || isPagesSidebarHidden) && isSidebarOpenInEditor && !isRightSidebarOpen) {
|
||||
offset = `${LEFT_SIDEBAR_WIDTH}px`;
|
||||
} else if ((position === 'top' || isPagesSidebarHidden) && isRightSidebarOpen && !isSidebarOpen) {
|
||||
} else if ((position === 'top' || isPagesSidebarHidden) && isRightSidebarOpen && !isSidebarOpenInEditor) {
|
||||
offset = `${RIGHT_SIDEBAR_WIDTH}px`;
|
||||
}
|
||||
}
|
||||
|
|
@ -205,7 +207,7 @@ export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => {
|
|||
)}
|
||||
style={canvasContainerStyles}
|
||||
>
|
||||
{showOnDesktop && appType !== 'module' && (
|
||||
{appType !== 'module' && (
|
||||
<PagesSidebarNavigation
|
||||
showHeader={showHeader}
|
||||
isMobileDevice={currentLayout === 'mobile'}
|
||||
|
|
@ -248,8 +250,12 @@ export const AppCanvas = ({ appId, switchDarkMode, darkMode }) => {
|
|||
pagePositionType={position}
|
||||
appType={appType}
|
||||
/>
|
||||
<DragGhostWidget />
|
||||
<ResizeGhostWidget />
|
||||
{currentMode === 'edit' && (
|
||||
<>
|
||||
<DragGhostWidget />
|
||||
<ResizeGhostWidget />
|
||||
</>
|
||||
)}
|
||||
<div id="component-portal" />
|
||||
{appType !== 'module' && <div id="component-portal" />}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ const Container = React.memo(
|
|||
componentType,
|
||||
appType,
|
||||
}) => {
|
||||
const { moduleId } = useModuleContext();
|
||||
const { moduleId, isModuleEditor } = useModuleContext();
|
||||
const realCanvasRef = useRef(null);
|
||||
const components = useStore((state) => state.getContainerChildrenMapping(id, moduleId), shallow);
|
||||
const setLastCanvasClickPosition = useStore((state) => state.setLastCanvasClickPosition, shallow);
|
||||
|
|
@ -170,8 +170,8 @@ const Container = React.memo(
|
|||
currentMode === 'view'
|
||||
? computeViewerBackgroundColor(darkMode, canvasBgColor)
|
||||
: id === 'canvas'
|
||||
? canvasBgColor
|
||||
: '#f0f0f0',
|
||||
? canvasBgColor
|
||||
: '#f0f0f0',
|
||||
width: '100%',
|
||||
maxWidth: (() => {
|
||||
// For Main Canvas
|
||||
|
|
@ -196,6 +196,7 @@ const Container = React.memo(
|
|||
'sub-canvas': id !== 'canvas' && appType !== 'module',
|
||||
'show-grid': isDragging && (index === 0 || index === null) && currentMode === 'edit' && appType !== 'module',
|
||||
'module-container': appType === 'module',
|
||||
'is-module-editor': isModuleEditor,
|
||||
})}
|
||||
id={id === 'canvas' ? 'real-canvas' : `canvas-${id}`}
|
||||
data-cy="real-canvas"
|
||||
|
|
|
|||
|
|
@ -923,6 +923,10 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
const targetGridWidth = useGridStore.getState().subContainerWidths[targetSlotId] || gridWidth;
|
||||
const isParentChangeAllowed = dragContext.isDroppable;
|
||||
|
||||
const isParentModuleContainer =
|
||||
!isModuleEditor &&
|
||||
document.getElementById(`canvas-${target.slotId}`)?.getAttribute('component-type') === 'ModuleContainer';
|
||||
|
||||
// Compute new position
|
||||
let { left, top } = getAdjustedDropPosition(e, target, isParentChangeAllowed, targetGridWidth, dragged);
|
||||
const componentParentType = target?.widget?.componentType;
|
||||
|
|
@ -933,7 +937,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
componentParentType === 'Form' || componentParentType === 'Container'
|
||||
? document.getElementById(`canvas-${target.slotId}`)?.scrollTop || 0
|
||||
: computeScrollDelta({ source });
|
||||
if (isParentChangeAllowed && !isModalToCanvas) {
|
||||
if (isParentChangeAllowed && !isModalToCanvas && !isParentModuleContainer) {
|
||||
// Special case for Modal; If source widget is modal, prevent drops to canvas
|
||||
const parent = target.slotId === 'real-canvas' ? null : target.slotId;
|
||||
handleDragEnd([{ id: e.target.id, x: left, y: top + scrollDelta, parent }]);
|
||||
|
|
@ -944,6 +948,7 @@ export default function Grid({ gridWidth, currentLayout }) {
|
|||
top = dragged.top;
|
||||
!isModalToCanvas ??
|
||||
toast.error(`${dragged.widgetType} is not compatible as a child component of ${target.widgetType}`);
|
||||
isParentModuleContainer ? toast.error('Modules cannot be edited inside an app') : null;
|
||||
}
|
||||
// Apply transform for smooth transition
|
||||
e.target.style.transform = `translate(${left}px, ${top + scrollDelta}px)`;
|
||||
|
|
|
|||
|
|
@ -398,7 +398,18 @@ export function hasParentWithClass(child, className) {
|
|||
export function showGridLines() {
|
||||
var canvasElms = document.getElementsByClassName('real-canvas');
|
||||
// Filter out module canvas
|
||||
var elementsArray = Array.from(canvasElms).filter((element) => !element.classList.contains('module-container'));
|
||||
var elementsArray = Array.from(canvasElms).filter((element) => {
|
||||
if (element.classList.contains('module-container')) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
!element.classList.contains('is-module-editor') &&
|
||||
element.getAttribute('component-type') === 'ModuleContainer'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
elementsArray.forEach(function (element) {
|
||||
element.classList.remove('hide-grid');
|
||||
element.classList.add('show-grid');
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ export const getDroppableSlotIdOnScreen = (event, widgets) => {
|
|||
// Hack: This is a temporary solution. We need to find a better way to handle this.
|
||||
// We have added this solution to fix dragging widget not being correctly dropped when it is there is scroll
|
||||
const widgetType = getWidgetById(widgets, event.target.id)?.component?.component || CANVAS_ID;
|
||||
if (!DROPPABLE_PARENTS.has(widgetType)) {
|
||||
if (!DROPPABLE_PARENTS.has(widgetType) && widgetType !== 'ModuleViewer') {
|
||||
const targetElems = document.elementsFromPoint(event.clientX, event.clientY);
|
||||
const draggedOverElements = targetElems.filter(
|
||||
(ele) => (ele.id !== event.target.id && ele.classList.contains('target')) || ele.classList.contains('real-canvas')
|
||||
|
|
|
|||
|
|
@ -15,12 +15,11 @@ import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
|
|||
import { handleDeactivateTargets, hideGridLines } from '../AppCanvas/Grid/gridUtils';
|
||||
|
||||
export const useCanvasDropHandler = () => {
|
||||
const { moduleId, isModuleEditor, appType } = useModuleContext();
|
||||
const { isModuleEditor } = useModuleContext();
|
||||
|
||||
const addComponentToCurrentPage = useStore((state) => state.addComponentToCurrentPage, shallow);
|
||||
const setActiveRightSideBarTab = useStore((state) => state.setActiveRightSideBarTab, shallow);
|
||||
const setShowModuleBorder = useStore((state) => state.setShowModuleBorder, shallow) || noop;
|
||||
const currentMode = useStore((state) => state.modeStore.modules[moduleId].currentMode, shallow);
|
||||
const currentLayout = useStore((state) => state.currentLayout, shallow);
|
||||
const setCurrentDragCanvasId = useGridStore((state) => state.actions.setCurrentDragCanvasId);
|
||||
const setRightSidebarOpen = useStore((state) => state.setRightSidebarOpen, shallow);
|
||||
|
|
@ -30,19 +29,20 @@ export const useCanvasDropHandler = () => {
|
|||
!canvasId || canvasId === 'canvas'
|
||||
? document.getElementById(`real-canvas`)
|
||||
: document.getElementById(`canvas-${canvasId}`);
|
||||
|
||||
const isParentModuleContainer = realCanvasRef?.getAttribute('component-type') === 'ModuleContainer';
|
||||
handleDeactivateTargets();
|
||||
hideGridLines();
|
||||
|
||||
setShowModuleBorder(false); // Hide the module border when dropping
|
||||
if (
|
||||
currentMode === 'view' ||
|
||||
(!isModuleEditor && appType === 'module' && draggedComponentType !== 'ModuleContainer') ||
|
||||
(isModuleEditor && canvasId === 'canvas')
|
||||
) {
|
||||
|
||||
if (isModuleEditor && canvasId === 'canvas') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isModuleEditor && isParentModuleContainer) {
|
||||
return toast.error('Modules cannot be edited inside an app');
|
||||
}
|
||||
|
||||
if (draggedComponentType === 'PDF' && !isPDFSupported()) {
|
||||
toast.error(
|
||||
'PDF is not supported in this version of browser. We recommend upgrading to the latest version for full support.'
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@ export const DateTimePicker = ({
|
|||
return (
|
||||
<div
|
||||
data-disabled={styles.disabledState}
|
||||
className={cx('datepicker-widget', {
|
||||
className={cx('datepicker-widget tjdb-datepicker-wrapper', {
|
||||
'theme-tjdb': !darkMode,
|
||||
'theme-dark': darkMode,
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -226,4 +226,33 @@
|
|||
line-height: normal !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.tjdb-datepicker-wrapper {
|
||||
|
||||
.react-datepicker__day--selected {
|
||||
background-color: rgb(77,94,240) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.react-datepicker__day.react-datepicker__day--selected:hover {
|
||||
background-color: rgb(77,94,240) !important;
|
||||
}
|
||||
|
||||
.react-datepicker__day:not(.react-datepicker__day--selected):hover {
|
||||
background-color: #f0f0f0 !important;
|
||||
}
|
||||
|
||||
.react-datepicker__day--keyboard-selected {
|
||||
background-color: #bad9f1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.tjdb-datepicker-wrapper.theme-dark {
|
||||
.react-datepicker__day:not(.react-datepicker__day--selected):hover {
|
||||
background-color: #636466 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -421,8 +421,8 @@ export const PagesSidebarNavigation = ({
|
|||
|
||||
const isTopPositioned = position === 'top';
|
||||
const labelHidden = labelStyle?.label?.hidden;
|
||||
const headerHidden = isLicensed ? hideHeader : false;
|
||||
const logoHidden = isLicensed ? hideLogo : false;
|
||||
const headerHidden = isLicensed ? hideHeader : true;
|
||||
const logoHidden = isLicensed ? hideLogo : true;
|
||||
|
||||
if (headerHidden && logoHidden && isPagesSidebarHidden) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ const FilePicker = (props) => {
|
|||
backgroundColor: 'var(--cc-surface1-surface)',
|
||||
color: darkMode ? '#c3c9d2' : '#5e6571',
|
||||
height: `${numericWidgetHeight + (containerPadding === 'default' ? 0 : 4)}px`,
|
||||
overflowY: isSmallWidget ? 'auto' : 'visible',
|
||||
overflowY: isSmallWidget ? 'auto' : 'scroll',
|
||||
opacity: disabledState ? 0.5 : 1,
|
||||
}),
|
||||
[darkMode, numericWidgetHeight, isVisible, isSmallWidget, disabledState, containerPadding]
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { getColorModeFromLuminance, getCssVarValue, getModifiedColor } from '@/E
|
|||
import { useDynamicHeight } from '@/_hooks/useDynamicHeight';
|
||||
import { useHeightObserver } from '@/_hooks/useHeightObserver';
|
||||
import { useModuleContext } from '@/AppBuilder/_contexts/ModuleContext';
|
||||
import './table.scss';
|
||||
|
||||
export const Table = memo(
|
||||
({
|
||||
|
|
@ -187,6 +188,7 @@ export const Table = memo(
|
|||
'--cc-table-row-hover': hoverColor,
|
||||
'--cc-table-row-active': activeColor,
|
||||
'--cc-table-scroll-bar-color': activeColor,
|
||||
'--cc-table-border-color': borderColor,
|
||||
}}
|
||||
>
|
||||
<TableContainer
|
||||
|
|
|
|||
|
|
@ -7,6 +7,21 @@ import HighLightSearch from '@/AppBuilder/Widgets/NewTable/_components/HighLight
|
|||
import useTextColor from '../DataTypes/_hooks/useTextColor';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
|
||||
// Utility function to generate input step for decimal places
|
||||
const getInputStep = (allowedDecimalPlaces) => {
|
||||
if (allowedDecimalPlaces === null || allowedDecimalPlaces === undefined) {
|
||||
return 'any';
|
||||
}
|
||||
|
||||
const num = Number(allowedDecimalPlaces);
|
||||
if (!Number.isFinite(num) || num < 0) {
|
||||
return 'any';
|
||||
}
|
||||
|
||||
const validDecimalPlaces = Math.floor(num);
|
||||
return validDecimalPlaces === 0 ? '1' : `0.${'0'.repeat(validDecimalPlaces - 1)}1`;
|
||||
};
|
||||
|
||||
export const NumberColumn = ({
|
||||
id,
|
||||
isEditable,
|
||||
|
|
@ -129,7 +144,7 @@ export const NumberColumn = ({
|
|||
className={`table-column-type-input-element input-number h-100 ${!isValid ? 'is-invalid' : ''}`}
|
||||
value={displayValue}
|
||||
onChange={(e) => setDisplayValue(e.target.value)}
|
||||
step={allowedDecimalPlaces !== null ? `0.${'0'.repeat(allowedDecimalPlaces - 1)}1` : 'any'}
|
||||
step={getInputStep(allowedDecimalPlaces)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
if (displayValue !== cellValue) {
|
||||
|
|
|
|||
|
|
@ -79,9 +79,15 @@ export const TableData = ({
|
|||
|
||||
// Handles row click for row selection
|
||||
const handleRowClick = (row) => {
|
||||
if (!allowSelection) return;
|
||||
lastClickedRowRef.current = { row: row?.original, index: row.index };
|
||||
|
||||
if (!allowSelection) {
|
||||
setExposedVariables({
|
||||
selectedRow: row?.original ?? {},
|
||||
selectedRowId: isNaN(row.index) ? String(row.index) : row.index,
|
||||
});
|
||||
fireEvent('onRowClicked');
|
||||
return;
|
||||
}
|
||||
// Update row selection
|
||||
row.toggleSelected();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -54,10 +54,6 @@ export const TableRow = ({
|
|||
>
|
||||
{row.getVisibleCells().map((cell) => {
|
||||
const cellStyles = {
|
||||
width:
|
||||
cell.column.id === 'rightActions' || cell.column.id === 'leftActions'
|
||||
? 'fit-content'
|
||||
: cell.column.getSize(),
|
||||
backgroundColor: getResolvedValue(cell.column.columnDef?.meta?.cellBackgroundColor ?? 'inherit', {
|
||||
rowData: row.original,
|
||||
cellValue: cell.getValue(),
|
||||
|
|
@ -66,10 +62,8 @@ export const TableRow = ({
|
|||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
textAlign: cell.column.columnDef?.meta?.horizontalAlignment,
|
||||
width: cell.column.getSize(),
|
||||
};
|
||||
if (cell.column.id === 'rightActions' || cell.column.id === 'leftActions') {
|
||||
cellStyles.maxWidth = 'fit-content';
|
||||
}
|
||||
|
||||
const isEditable = getResolvedValue(cell.column.columnDef?.meta?.isEditable ?? false, {
|
||||
rowData: row.original,
|
||||
|
|
|
|||
|
|
@ -263,6 +263,8 @@ export const TableExposedVariables = ({
|
|||
]);
|
||||
|
||||
useEffect(() => {
|
||||
// onRowClicked event will be fired when a row is clicked
|
||||
// it should be triggered even when allowSelection is false which is handled in the handleRowClick()
|
||||
if (allowSelection && Object.keys(lastClickedRow).length > 0) {
|
||||
fireEvent('onRowClicked');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,75 @@
|
|||
import React from 'react';
|
||||
import { ActionButtons } from '../_components/ActionButtons/ActionButtons';
|
||||
|
||||
// Function to calculate text width using improved canvas measurement
|
||||
const calculateTextWidth = (text) => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d');
|
||||
|
||||
// Use the exact font properties from the CSS
|
||||
// .btn-sm has font-size: .75rem which is typically 12px
|
||||
// IBM Plex Sans is the primary font family used in the app
|
||||
context.font = '500 12px "IBM Plex Sans"';
|
||||
|
||||
const textMetrics = context.measureText(text);
|
||||
const textWidth = textMetrics.width;
|
||||
|
||||
// Add padding from .btn-sm: .125rem .5rem = 2px 8px (top/bottom left/right)
|
||||
// Add border: typically 1px on each side
|
||||
// Add margins from .m-1: .25rem = 4px on each side
|
||||
const horizontalPadding = 16; // 8px left + 8px right
|
||||
const horizontalBorder = 2; // 1px left + 1px right
|
||||
const horizontalMargin = 8; // 4px left + 4px right
|
||||
|
||||
return textWidth + horizontalPadding + horizontalBorder + horizontalMargin;
|
||||
};
|
||||
|
||||
// Function to calculate total width needed for action buttons
|
||||
const calculateActionColumnWidth = (actions) => {
|
||||
if (!actions || actions.length === 0) return 90; // minimum width
|
||||
|
||||
let totalWidth = 0;
|
||||
const containerPadding = 24; // container padding
|
||||
|
||||
actions.forEach((action) => {
|
||||
const buttonWidth = calculateTextWidth(action.buttonText || action.name || 'Button');
|
||||
totalWidth += buttonWidth;
|
||||
});
|
||||
|
||||
// Add container padding and 2px as the calculation is not accurate in the decimal values
|
||||
totalWidth += containerPadding + 2;
|
||||
|
||||
// Ensure minimum width
|
||||
return Math.max(90, totalWidth);
|
||||
};
|
||||
|
||||
export const generateActionColumns = ({ actions, fireEvent, setExposedVariables, id }) => {
|
||||
const leftActions = actions?.filter((action) => action.position === 'left') || [];
|
||||
const rightActions = actions?.filter((action) => [undefined, 'right'].includes(action.position)) || [];
|
||||
|
||||
const createActionColumn = (position) => ({
|
||||
id: `${position}Actions`,
|
||||
accessorKey: 'actions',
|
||||
enableResizing: false,
|
||||
meta: { columnType: 'action', position, skipFilter: true, skipAddNewRow: true },
|
||||
size: 90,
|
||||
header: 'Actions',
|
||||
cell: ({ row, cell }) => (
|
||||
<ActionButtons
|
||||
actions={position === 'left' ? leftActions : rightActions}
|
||||
row={row}
|
||||
cell={cell}
|
||||
fireEvent={fireEvent}
|
||||
setExposedVariables={setExposedVariables}
|
||||
id={id}
|
||||
/>
|
||||
),
|
||||
});
|
||||
const createActionColumn = (position) => {
|
||||
const actionsForPosition = position === 'left' ? leftActions : rightActions;
|
||||
const calculatedWidth = calculateActionColumnWidth(actionsForPosition);
|
||||
|
||||
return {
|
||||
id: `${position}Actions`,
|
||||
accessorKey: 'actions',
|
||||
enableResizing: false,
|
||||
meta: { columnType: 'action', position, skipFilter: true, skipAddNewRow: true },
|
||||
size: calculatedWidth,
|
||||
header: 'Actions',
|
||||
cell: ({ row, cell }) => (
|
||||
<ActionButtons
|
||||
actions={actionsForPosition}
|
||||
row={row}
|
||||
cell={cell}
|
||||
fireEvent={fireEvent}
|
||||
setExposedVariables={setExposedVariables}
|
||||
id={id}
|
||||
/>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const columns = [];
|
||||
if (leftActions.length > 0) columns.push(createActionColumn('left'));
|
||||
|
|
|
|||
|
|
@ -121,7 +121,9 @@ export default function generateColumnsData({
|
|||
? getAddNewRowDetailFromIndex(id, row.index)
|
||||
: getEditedRowFromIndex(id, row.index);
|
||||
|
||||
let cellValue = changeSet ? changeSet[cell.column.columnDef?.meta?.name] ?? cell.getValue() : cell.getValue();
|
||||
let cellValue = changeSet
|
||||
? changeSet[cell.column.columnDef?.accessorKey] ?? cell.getValue()
|
||||
: cell.getValue();
|
||||
cellValue = cellValue === undefined || cellValue === null ? '' : cellValue;
|
||||
const rowData = tableData?.[row.index];
|
||||
|
||||
|
|
|
|||
4
frontend/src/AppBuilder/Widgets/NewTable/table.scss
Normal file
4
frontend/src/AppBuilder/Widgets/NewTable/table.scss
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
.card.jet-table.table-component {
|
||||
border: 1px solid var(--cc-table-border-color) !important;
|
||||
}
|
||||
|
|
@ -492,39 +492,79 @@ export const Tabs = function Tabs({
|
|||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
width: `${tabItems.length * 100}%`,
|
||||
transform: `translateX(-${findTabIndex(currentTab) * (100 / tabItems.length)}%)`,
|
||||
transition: transition === 'none' ? 'none' : 'transform 0.3s ease-in-out',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
{tabItems.map((tab) => (
|
||||
{transition === 'none' ? (
|
||||
// Simple show/hide when no transition
|
||||
tabItems.map((tab) => (
|
||||
<div
|
||||
key={tab.id}
|
||||
style={{
|
||||
width: `${100 / tabItems.length}%`,
|
||||
flexShrink: 0,
|
||||
display: tab.id === currentTab ? 'block' : 'none',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: 'hidden',
|
||||
boxSizing: 'border-box',
|
||||
}}
|
||||
>
|
||||
<TabContent
|
||||
id={id}
|
||||
tab={tab}
|
||||
height={height}
|
||||
width={width}
|
||||
parsedHideTabs={parsedHideTabs}
|
||||
bgColor={bgColor}
|
||||
darkMode={darkMode}
|
||||
dynamicHeight={dynamicHeight}
|
||||
currentTab={currentTab}
|
||||
isTransitioning={isTransitioning}
|
||||
/>
|
||||
{shouldRenderTabContent(tab) && (
|
||||
<TabContent
|
||||
id={id}
|
||||
tab={tab}
|
||||
height={height}
|
||||
width={width}
|
||||
parsedHideTabs={parsedHideTabs}
|
||||
bgColor={bgColor}
|
||||
darkMode={darkMode}
|
||||
dynamicHeight={dynamicHeight}
|
||||
currentTab={currentTab}
|
||||
isTransitioning={isTransitioning}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
// Sliding animation when transition is enabled
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
width: `${tabItems.length * 100}%`,
|
||||
transform: `translateX(-${findTabIndex(currentTab) * (100 / tabItems.length)}%)`,
|
||||
transition: 'transform 0.3s ease-in-out',
|
||||
height: '100%',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{tabItems.map((tab) => (
|
||||
<div
|
||||
key={tab.id}
|
||||
style={{
|
||||
width: `${100 / tabItems.length}%`,
|
||||
flexShrink: 0,
|
||||
height: '100%',
|
||||
overflow: 'hidden',
|
||||
boxSizing: 'border-box',
|
||||
minWidth: 0,
|
||||
contain: 'layout style size',
|
||||
}}
|
||||
>
|
||||
{shouldRenderTabContent(tab) && (
|
||||
<TabContent
|
||||
id={id}
|
||||
tab={tab}
|
||||
height={height}
|
||||
width={width}
|
||||
parsedHideTabs={parsedHideTabs}
|
||||
bgColor={bgColor}
|
||||
darkMode={darkMode}
|
||||
dynamicHeight={dynamicHeight}
|
||||
currentTab={currentTab}
|
||||
isTransitioning={isTransitioning}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -577,6 +617,8 @@ const TabContent = memo(function TabContent({
|
|||
backgroundColor: fieldBackgroundColor || bgColor,
|
||||
opacity: disable ? 0.5 : 1,
|
||||
pointerEvents: disable ? 'none' : 'auto',
|
||||
overflow: 'hidden', // Ensure TabContent doesn't overflow
|
||||
boxSizing: 'border-box', // Include padding/border in size calculation
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
|
|
@ -600,6 +642,10 @@ const TabContent = memo(function TabContent({
|
|||
overflow: isTransitioning ? 'hidden' : 'hidden auto',
|
||||
backgroundColor: fieldBackgroundColor || bgColor,
|
||||
opacity: disable ? 0.5 : 1,
|
||||
width: '100%', // Ensure it doesn't exceed container width
|
||||
maxWidth: '100%', // Additional constraint
|
||||
boxSizing: 'border-box', // Include padding/border in width
|
||||
contain: 'layout style', // Add containment for better overflow control
|
||||
}}
|
||||
darkMode={darkMode}
|
||||
/>
|
||||
|
|
@ -607,4 +653,4 @@ const TabContent = memo(function TabContent({
|
|||
</div>
|
||||
);
|
||||
},
|
||||
areEqual);
|
||||
areEqual);
|
||||
|
|
|
|||
|
|
@ -263,7 +263,8 @@ export const createQueryPanelSlice = (set, get) => ({
|
|||
});
|
||||
},
|
||||
onComplete: async (result) => {
|
||||
await processQueryResults(result);
|
||||
const processedResult = { data: result };
|
||||
await processQueryResults(processedResult);
|
||||
// Remove the AsyncQueryHandler instance from asyncQueryRuns on completion
|
||||
get().queryPanel.setAsyncQueryRuns((currentRuns) =>
|
||||
currentRuns.filter((handler) => handler.jobId !== asyncHandler.jobId)
|
||||
|
|
@ -508,10 +509,12 @@ export const createQueryPanelSlice = (set, get) => ({
|
|||
queryId,
|
||||
{
|
||||
isLoading: false,
|
||||
...(errorData?.data?.type === 'tj-401' ? {
|
||||
metadata: errorData?.metadata,
|
||||
response: errorData?.data?.responseObject,
|
||||
} : query.kind === 'restapi'
|
||||
...(errorData?.data?.type === 'tj-401'
|
||||
? {
|
||||
metadata: errorData?.metadata,
|
||||
response: errorData?.data?.responseObject,
|
||||
}
|
||||
: query.kind === 'restapi'
|
||||
? {
|
||||
metadata: errorData?.metadata,
|
||||
request: errorData?.data?.requestObject,
|
||||
|
|
@ -666,7 +669,8 @@ export const createQueryPanelSlice = (set, get) => ({
|
|||
break;
|
||||
}
|
||||
|
||||
errorData = (query.kind === 'runpy' || query.kind === 'runjs') && (data?.data?.type !== 'tj-401') ? data?.data : data;
|
||||
errorData =
|
||||
(query.kind === 'runpy' || query.kind === 'runjs') && data?.data?.type !== 'tj-401' ? data?.data : data;
|
||||
const result = handleFailure(errorData);
|
||||
resolve(result);
|
||||
return;
|
||||
|
|
@ -756,6 +760,7 @@ export const createQueryPanelSlice = (set, get) => ({
|
|||
} else if (query.kind === 'workflows') {
|
||||
queryExecutionPromise = triggerWorkflow(
|
||||
moduleId,
|
||||
query,
|
||||
query.options.workflowId,
|
||||
query.options.blocking,
|
||||
query.options?.params,
|
||||
|
|
@ -773,7 +778,7 @@ export const createQueryPanelSlice = (set, get) => ({
|
|||
// async queries in the future
|
||||
if (query.kind === 'workflows') {
|
||||
const processQueryResultsPreview = async (result) => {
|
||||
let finalData = result;
|
||||
let finalData = result?.data;
|
||||
if (query.options.enableTransformation) {
|
||||
finalData = await runTransformation(
|
||||
finalData,
|
||||
|
|
@ -1232,7 +1237,6 @@ export const createQueryPanelSlice = (set, get) => ({
|
|||
},
|
||||
|
||||
createProxy: (obj, path = '') => {
|
||||
|
||||
return new Proxy(obj, {
|
||||
get(target, prop) {
|
||||
const fullPath = path ? `${path}.${prop}` : prop;
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ export const Datepicker = function Datepicker({
|
|||
className={`input-field form-control ${
|
||||
!isValid && showValidationError ? 'is-invalid' : ''
|
||||
} validation-without-icon px-2 ${darkMode ? 'bg-dark color-white' : 'bg-light'}`}
|
||||
popperClassName={cx('tj-datepicker-widget', { 'dark-theme': darkMode })}
|
||||
popperClassName={cx('legacy-datepicker-poppper tj-datepicker-widget', { 'dark-theme': darkMode })}
|
||||
selected={date}
|
||||
value={date !== null ? computeDateString(date) : 'select date'}
|
||||
onChange={(date) => onDateChange(date)}
|
||||
|
|
@ -123,6 +123,7 @@ export const Datepicker = function Datepicker({
|
|||
}}
|
||||
showMonthDropdown
|
||||
showYearDropdown
|
||||
portalId="component-portal"
|
||||
dropdownMode="select"
|
||||
excludeDates={excludedDates}
|
||||
customInput={<input style={{ borderRadius: `${borderRadius}px`, boxShadow, height }} />}
|
||||
|
|
|
|||
|
|
@ -50,4 +50,9 @@
|
|||
// select option {
|
||||
// color: #fff !important // This needs to be black since this has no effect on mac but has on windows
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
.legacy-datepicker-poppper.react-datepicker-popper.tj-datepicker-widget {
|
||||
width: 254px !important;
|
||||
border-radius: 6px !important;
|
||||
}
|
||||
|
|
@ -300,7 +300,7 @@ export const DateTimePicker = ({
|
|||
return (
|
||||
<div
|
||||
data-disabled={styles.disabledState}
|
||||
className={cx('datepicker-widget position-relative', {
|
||||
className={cx('datepicker-widget tjdb-datepicker-wrapper position-relative', {
|
||||
'theme-tjdb': !darkMode,
|
||||
'theme-dark': darkMode,
|
||||
})}
|
||||
|
|
|
|||
|
|
@ -245,3 +245,34 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.tjdb-datepicker-wrapper {
|
||||
|
||||
.react-datepicker__day--selected {
|
||||
background-color: rgb(77,94,240) !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.react-datepicker__day.react-datepicker__day--selected:hover {
|
||||
background-color: rgb(77,94,240) !important;
|
||||
}
|
||||
|
||||
.react-datepicker__day:not(.react-datepicker__day--selected):hover {
|
||||
background-color: #f0f0f0 !important;
|
||||
}
|
||||
|
||||
.react-datepicker__day--keyboard-selected {
|
||||
background-color: #bad9f1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.tjdb-datepicker-wrapper.theme-dark {
|
||||
.react-datepicker__day:not(.react-datepicker__day--selected):hover {
|
||||
background-color: #636466 !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
.react-select__option {
|
||||
background-color: var(--surfaces-surface-01) !important;
|
||||
border-radius: 6px;
|
||||
min-height: 38px;
|
||||
|
||||
|
||||
>div {
|
||||
|
|
@ -52,6 +53,9 @@
|
|||
background-color: transparent !important;
|
||||
}
|
||||
}
|
||||
&:hover .current-org-indicator {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -972,10 +972,6 @@
|
|||
|
||||
.jet-data-table .has-actions {
|
||||
padding: 0 12px;
|
||||
|
||||
.justify-content-start {
|
||||
justify-content: center !important;
|
||||
}
|
||||
}
|
||||
|
||||
.jet-table.table-component {
|
||||
|
|
@ -1600,37 +1596,39 @@
|
|||
}
|
||||
|
||||
|
||||
.card.jet-table.table-component {
|
||||
|
||||
.table-cell {
|
||||
input[type="checkbox"] {
|
||||
position: relative;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
border: 1px solid var(--cc-default-border,--borders-default);
|
||||
border-radius: 3px;
|
||||
background-color: var(--cc-surface1-surface);
|
||||
cursor: pointer;
|
||||
|
||||
&:checked {
|
||||
background-color: var(--cc-primary-brand);
|
||||
border-color: var(--cc-primary-brand);
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
top: 1px;
|
||||
width: 5px;
|
||||
height: 9px;
|
||||
border: solid white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:checked) {
|
||||
.table-cell {
|
||||
input[type="checkbox"] {
|
||||
position: relative;
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
border: 1px solid var(--cc-default-border,--borders-default);
|
||||
border-radius: 3px;
|
||||
background-color: var(--cc-surface1-surface);
|
||||
border-color: var(--cc-default-border,--borders-default);
|
||||
cursor: pointer;
|
||||
|
||||
&:checked {
|
||||
background-color: var(--cc-primary-brand);
|
||||
border-color: var(--cc-primary-brand);
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
top: 1px;
|
||||
width: 5px;
|
||||
height: 9px;
|
||||
border: solid white;
|
||||
border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:checked) {
|
||||
background-color: var(--cc-surface1-surface);
|
||||
border-color: var(--cc-default-border,--borders-default);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3899,7 +3899,7 @@ input[type="text"] {
|
|||
}
|
||||
|
||||
.react-datepicker__day--selected {
|
||||
background-color: var(--cc-primary-brand);
|
||||
background-color: var(--cc-primary-brand, #216ba5);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7545,19 +7545,6 @@ tbody {
|
|||
max-width: 230px !important;
|
||||
}
|
||||
|
||||
.table-cell {
|
||||
padding: 0;
|
||||
|
||||
input[type="checkbox"] {
|
||||
accent-color: var(--cc-primary-brand);
|
||||
background-color: var(--cc-surface1-surface);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked::before {
|
||||
background-color: var(--cc-surface1-surface);
|
||||
}
|
||||
}
|
||||
|
||||
.add-more-columns-btn {
|
||||
background: var(--indigo3);
|
||||
font-weight: 500;
|
||||
|
|
@ -11619,10 +11606,6 @@ tbody {
|
|||
display: none;
|
||||
}
|
||||
|
||||
&:hover .current-org-indicator {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.org-name {
|
||||
color: var(--slate12) !important;
|
||||
white-space: nowrap;
|
||||
|
|
@ -16276,12 +16259,12 @@ fieldset:disabled {
|
|||
|
||||
|
||||
.create-app-with-ai-prompt-wrapper {
|
||||
padding: 16px 0;
|
||||
padding: 30px 0;
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 16px 24px;
|
||||
// padding: 16px 24px;
|
||||
background: var(--bg-surface-layer-01);
|
||||
|
||||
.left {
|
||||
|
|
@ -18415,14 +18398,16 @@ section.ai-message-prompt-input-wrapper {
|
|||
line-height: 20px;
|
||||
}
|
||||
|
||||
.table-cell {
|
||||
input[type="checkbox"] {
|
||||
accent-color: var(--cc-primary-brand);
|
||||
background-color: var(--cc-surface1-surface);
|
||||
}
|
||||
.card.jet-table.table-component {
|
||||
.table-cell {
|
||||
input[type="checkbox"] {
|
||||
accent-color: var(--cc-primary-brand);
|
||||
background-color: var(--cc-surface1-surface);
|
||||
}
|
||||
|
||||
input[type="checkbox"]:checked::before {
|
||||
background-color: var(--cc-surface1-surface);
|
||||
input[type="checkbox"]:checked::before {
|
||||
background-color: var(--cc-surface1-surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
3.16.0
|
||||
3.16.1-lts
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class IncreaseWhiteLabellingLogoFaviconSize1722729600000 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
ALTER TABLE white_labelling
|
||||
ALTER COLUMN logo TYPE varchar(1024),
|
||||
ALTER COLUMN favicon TYPE varchar(1024)
|
||||
`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> { }
|
||||
}
|
||||
|
|
@ -35,7 +35,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="padding-y-10">
|
||||
<td class="padding-y-20">
|
||||
<a href={{inviteUrl}} target="_blank" class="padding-r-40" href="#">
|
||||
<button class="primary-btn">
|
||||
Join workspace
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="padding-y-10">
|
||||
<td class="padding-y-20">
|
||||
<a href={{resetLink}} target="_blank" class="padding-r-40" href="#">
|
||||
<button class="primary-btn">
|
||||
Reset your password
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="padding-y-10">
|
||||
<td class="padding-y-20">
|
||||
<a href={{inviteUrl}} target="_blank" class="padding-r-40" href="#">
|
||||
<button class="primary-btn">
|
||||
Confirm email address
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="padding-y-10">
|
||||
<td class="padding-y-20">
|
||||
<a href={{inviteUrl}} target="_blank" class="padding-r-40" href="#">
|
||||
<button class="primary-btn">
|
||||
Join workspace
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="padding-y-10">
|
||||
<td class="padding-y-20">
|
||||
<a href={{resetLink}} target="_blank" class="padding-r-40" href="#">
|
||||
<button class="primary-btn">
|
||||
Reset your password
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="padding-y-10">
|
||||
<td class="padding-y-20">
|
||||
<a href={{inviteUrl}} target="_blank" class="padding-r-40" href="#">
|
||||
<button class="primary-btn">
|
||||
Confirm email address
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ export class EmailUtilService implements IEmailUtilService {
|
|||
const transporter = nodemailer.createTransport({
|
||||
host: smtpSettings.host,
|
||||
port: smtpSettings.port,
|
||||
secure: smtpSettings.port === 465, // Use `true` for port 465, `false` for others
|
||||
secure: smtpSettings.port == 465, // Use `true` for port 465, `false` for others
|
||||
auth: {
|
||||
user: smtpSettings.username,
|
||||
pass: smtpSettings.password,
|
||||
|
|
|
|||
Loading…
Reference in a new issue