From 9d1f23f40a5b1a267bae68c48486c82e0f1e3360 Mon Sep 17 00:00:00 2001 From: Shaurya Sharma Date: Tue, 1 Jul 2025 14:33:10 +0530 Subject: [PATCH 1/5] Added chart border property and removed white background color --- .../src/AppBuilder/WidgetManager/widgets/chart.js | 10 ++++++++++ frontend/src/Editor/Components/Chart.jsx | 14 ++++++++------ .../modules/apps/services/widget-config/chart.js | 10 ++++++++++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/frontend/src/AppBuilder/WidgetManager/widgets/chart.js b/frontend/src/AppBuilder/WidgetManager/widgets/chart.js index 66b5b21e98..d93ca2f2e6 100644 --- a/frontend/src/AppBuilder/WidgetManager/widgets/chart.js +++ b/frontend/src/AppBuilder/WidgetManager/widgets/chart.js @@ -138,6 +138,15 @@ export const chartConfig = { displayName: 'Background color', validation: { schema: { type: 'string' }, defaultValue: 'var(--cc-surface1-surface)' }, }, + borderColor: { + type: 'colorSwatches', + displayName: 'Border color', + validation: { + schema: { type: 'string' }, + defaultValue: 'var(--cc-default-border)', + }, + accordian: 'container', + }, padding: { type: 'code', displayName: 'Padding', @@ -233,6 +242,7 @@ export const chartConfig = { borderRadius: { value: '{{4}}' }, visibility: { value: '{{true}}' }, disabledState: { value: '{{false}}' }, + borderColor: { value: 'var(--cc-default-border)' }, }, }, }; diff --git a/frontend/src/Editor/Components/Chart.jsx b/frontend/src/Editor/Components/Chart.jsx index 54a6114674..151b9f9c7b 100644 --- a/frontend/src/Editor/Components/Chart.jsx +++ b/frontend/src/Editor/Components/Chart.jsx @@ -8,7 +8,7 @@ import { isEqual } from 'lodash'; import { deepClone } from '@/_helpers/utilities/utils.helpers'; import useStore from '@/AppBuilder/_stores/store'; import { shallow } from 'zustand/shallow'; -import { getCssVarValue } from './utils'; +import { getCssVarValue, getModifiedColor } from './utils'; var tinycolor = require('tinycolor2'); @@ -33,11 +33,11 @@ export const Chart = function Chart({ return '#fff'; }; - const { padding, visibility, disabledState, boxShadow, backgroundColor, borderRadius } = styles; + const { padding, visibility, disabledState, boxShadow, backgroundColor, borderRadius, borderColor } = styles; const { title, markerColor, showGridLines, type, data, jsonDescription, plotFromJson, showAxes, barmode } = properties; - const modifiedBackgroundColor = getCssVarValue(document.documentElement, backgroundColor); + const modifiedBackgroundColor = getModifiedColor(backgroundColor, 0); const modifiedMarkerColor = getCssVarValue(document.documentElement, markerColor); const modifiedGridLines = getCssVarValue(document.documentElement, 'var(--cc-weak-border)'); const modifiedTextColor = getCssVarValue(document.documentElement, 'var(--cc-primary-text)'); @@ -55,7 +55,8 @@ export const Chart = function Chart({ width: width - 4, height, display: visibility ? '' : 'none', - background: darkMode ? '#1f2936' : 'white', + // background: darkMode ? '#1f2936' : 'white', + border: `1px solid ${borderColor}`, boxShadow, borderRadius, }; @@ -82,6 +83,7 @@ export const Chart = function Chart({ ? '#1f2936' : '#fff' : modifiedBackgroundColor; + const fontColor = getColor(updatedBgColor); const chartTitle = plotFromJson ? chartLayout?.title ?? title : title; @@ -105,8 +107,8 @@ export const Chart = function Chart({ }, [JSON.stringify(chartLayout, chartTitle)]); const layout = { - width: width - 4, - height, + width: width - 6, + height: height - 4, plot_bgcolor: updatedBgColor, paper_bgcolor: updatedBgColor, title: { diff --git a/server/src/modules/apps/services/widget-config/chart.js b/server/src/modules/apps/services/widget-config/chart.js index 66b5b21e98..d93ca2f2e6 100644 --- a/server/src/modules/apps/services/widget-config/chart.js +++ b/server/src/modules/apps/services/widget-config/chart.js @@ -138,6 +138,15 @@ export const chartConfig = { displayName: 'Background color', validation: { schema: { type: 'string' }, defaultValue: 'var(--cc-surface1-surface)' }, }, + borderColor: { + type: 'colorSwatches', + displayName: 'Border color', + validation: { + schema: { type: 'string' }, + defaultValue: 'var(--cc-default-border)', + }, + accordian: 'container', + }, padding: { type: 'code', displayName: 'Padding', @@ -233,6 +242,7 @@ export const chartConfig = { borderRadius: { value: '{{4}}' }, visibility: { value: '{{true}}' }, disabledState: { value: '{{false}}' }, + borderColor: { value: 'var(--cc-default-border)' }, }, }, }; From c9d01da916e38c975f92666b96cc96febef36916 Mon Sep 17 00:00:00 2001 From: Rohan Lahori <64496391+rohanlahori@users.noreply.github.com> Date: Wed, 2 Jul 2025 17:16:39 +0530 Subject: [PATCH 2/5] Bugfixes/email smtp issues (#13154) --- server/src/modules/app/module.ts | 2 ++ .../{email => email-listener}/listener.ts | 4 +--- server/src/modules/email-listener/module.ts | 17 +++++++++++++++++ server/src/modules/email/module.ts | 5 ++--- server/src/modules/email/service.ts | 14 ++------------ server/src/modules/email/util.service.ts | 16 +++++++++++++++- 6 files changed, 39 insertions(+), 19 deletions(-) rename server/src/modules/{email => email-listener}/listener.ts (93%) create mode 100644 server/src/modules/email-listener/module.ts diff --git a/server/src/modules/app/module.ts b/server/src/modules/app/module.ts index 75210ab9ff..24c78aaaa5 100644 --- a/server/src/modules/app/module.ts +++ b/server/src/modules/app/module.ts @@ -53,6 +53,7 @@ import { SampleDBScheduler } from '@modules/data-sources/schedulers/sample-db.sc import { SessionScheduler } from '@modules/session/scheduler'; import { AuditLogsClearScheduler } from '@modules/audit-logs/scheduler'; import { ModulesModule } from '@modules/modules/module'; +import { EmailListenerModule } from '@modules/email-listener/module'; export class AppModule implements OnModuleInit { static async register(configs: { IS_GET_CONTEXT: boolean }): Promise { // Load static and dynamic modules @@ -113,6 +114,7 @@ export class AppModule implements OnModuleInit { await AppGitModule.register(configs), await CrmModule.register(configs), await OrganizationPaymentModule.register(configs), + await EmailListenerModule.register(configs), ]; return { diff --git a/server/src/modules/email/listener.ts b/server/src/modules/email-listener/listener.ts similarity index 93% rename from server/src/modules/email/listener.ts rename to server/src/modules/email-listener/listener.ts index 379835e376..1bb5179f37 100644 --- a/server/src/modules/email/listener.ts +++ b/server/src/modules/email-listener/listener.ts @@ -1,9 +1,8 @@ import { Injectable } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; import { Logger } from 'nestjs-pino'; -import { EmailEventPayload } from './constants'; +import { EmailEventPayload, EMAIL_EVENTS } from '@modules/email/constants'; import { EmailService } from '@modules/email/service'; -import { EMAIL_EVENTS } from './constants'; @Injectable() export class EmailListener { @@ -15,7 +14,6 @@ export class EmailListener { @OnEvent('emailEvent') async handleEmailEvent(eventData: EmailEventPayload) { const { type, payload } = eventData; - try { switch (type) { case EMAIL_EVENTS.SEND_WELCOME_EMAIL: diff --git a/server/src/modules/email-listener/module.ts b/server/src/modules/email-listener/module.ts new file mode 100644 index 0000000000..a117fc70de --- /dev/null +++ b/server/src/modules/email-listener/module.ts @@ -0,0 +1,17 @@ +import { DynamicModule } from '@nestjs/common'; +import { SubModule } from '@modules/app/sub-module'; +import { EmailModule } from '@modules/email/module'; +import { getImportPath } from '@modules/app/constants'; + +export class EmailListenerModule extends SubModule { + static async register(configs?: { IS_GET_CONTEXT: boolean }): Promise { + const importPath = await getImportPath(configs?.IS_GET_CONTEXT); + const { EmailListener } = await import(`${importPath}/email-listener/listener`); + return { + module: EmailListenerModule, + imports: [await EmailModule.register(configs)], + providers: [EmailListener], + exports: [], + }; + } +} diff --git a/server/src/modules/email/module.ts b/server/src/modules/email/module.ts index 20d8191274..119d714413 100644 --- a/server/src/modules/email/module.ts +++ b/server/src/modules/email/module.ts @@ -10,7 +10,6 @@ export class EmailModule extends SubModule { const importPath = await getImportPath(configs?.IS_GET_CONTEXT); const { EmailService } = await import(`${importPath}/email/service`); const { EmailUtilService } = await import(`${importPath}/email/util.service`); - const { EmailListener } = await import(`${importPath}/email/listener`); return { module: EmailModule, imports: [ @@ -18,8 +17,8 @@ export class EmailModule extends SubModule { await DataSourcesModule.register(configs), await SMTPModule.register(configs), ], - providers: [EmailService, EmailListener, EmailUtilService], - exports: [EmailListener, EmailUtilService], + providers: [EmailService, EmailUtilService], + exports: [EmailUtilService, EmailService], }; } } diff --git a/server/src/modules/email/service.ts b/server/src/modules/email/service.ts index cd51b6c7f2..f2c9ceeb5d 100644 --- a/server/src/modules/email/service.ts +++ b/server/src/modules/email/service.ts @@ -9,7 +9,6 @@ import { } from '@modules/email/dto'; import { EmailUtilService } from './util.service'; import { IEmailService } from './interfaces/IService'; -import { INSTANCE_SYSTEM_SETTINGS } from '@modules/instance-settings/constants'; import { WhiteLabellingUtilService } from '@modules/white-labelling/util.service'; handlebars.registerHelper('capitalize', function (value) { @@ -29,15 +28,6 @@ export class EmailService implements IEmailService { protected WHITE_LABEL_TEXT; protected WHITE_LABEL_LOGO; protected SUB_PATH; - protected SMTP: { - [INSTANCE_SYSTEM_SETTINGS.SMTP_ENABLED]: boolean; - [INSTANCE_SYSTEM_SETTINGS.SMTP_DOMAIN]: string; - [INSTANCE_SYSTEM_SETTINGS.SMTP_PORT]: string; - [INSTANCE_SYSTEM_SETTINGS.SMTP_USERNAME]: string; - [INSTANCE_SYSTEM_SETTINGS.SMTP_PASSWORD]: string; - [INSTANCE_SYSTEM_SETTINGS.SMTP_FROM_EMAIL]: string; - [INSTANCE_SYSTEM_SETTINGS.SMTP_ENV_CONFIGURED]: boolean; - }; protected defaultWhiteLabelState: boolean; constructor( @@ -63,14 +53,13 @@ export class EmailService implements IEmailService { async init(organizationId?: string | null) { const whiteLabelSettings = await this.emailUtilService.retrieveWhiteLabelSettings(null); - this.SMTP = await this.emailUtilService.retrieveSmtpSettings(); this.WHITE_LABEL_TEXT = whiteLabelSettings?.white_label_text; this.WHITE_LABEL_LOGO = whiteLabelSettings?.white_label_logo; this.defaultWhiteLabelState = whiteLabelSettings?.default; } protected compileTemplate(templatePath: string, templateData: object) { - this.emailUtilService.compileTemplate(templatePath, templateData); + return this.emailUtilService.compileTemplate(templatePath, templateData); } protected stripTrailingSlash(hostname: string) { @@ -88,6 +77,7 @@ export class EmailService implements IEmailService { redirectTo, } = payload; await this.init(organizationId); + await this.emailUtilService.init(organizationId); const isOrgInvite = organizationInvitationToken && sender && organizationName; const inviteUrl = generateInviteURL(invitationtoken, organizationInvitationToken, organizationId, null, redirectTo); const subject = isOrgInvite ? `Welcome to ${organizationName || 'ToolJet'}` : 'Set up your account!'; diff --git a/server/src/modules/email/util.service.ts b/server/src/modules/email/util.service.ts index 17e26f3864..34846cfff4 100644 --- a/server/src/modules/email/util.service.ts +++ b/server/src/modules/email/util.service.ts @@ -36,7 +36,11 @@ export class EmailUtilService implements IEmailUtilService { constructor( protected readonly whiteLabellingUtilService: WhiteLabellingUtilService, protected readonly smtpUtilService: SMTPUtilService - ) {} + ) { + this.TOOLJET_HOST = this.stripTrailingSlash(process.env.TOOLJET_HOST); + this.SUB_PATH = process.env.SUB_PATH; + this.NODE_ENV = process.env.NODE_ENV || 'development'; + } async retrieveWhiteLabelSettings(organizationId?: string | null): Promise { const whiteLabelSetting = await this.whiteLabellingUtilService.getProcessedSettings(organizationId); @@ -253,4 +257,14 @@ export class EmailUtilService implements IEmailUtilService { whiteLabelLogo: DEFAULT_WHITE_LABELLING_SETTINGS.white_label_logo, }); } + protected stripTrailingSlash(hostname: string) { + return hostname?.endsWith('/') ? hostname.slice(0, -1) : hostname; + } + async init(organizationId?: string | null) { + const whiteLabelSettings = await this.retrieveWhiteLabelSettings(null); + this.SMTP = await this.retrieveSmtpSettings(); + this.WHITE_LABEL_TEXT = whiteLabelSettings?.white_label_text; + this.WHITE_LABEL_LOGO = whiteLabelSettings?.white_label_logo; + this.defaultWhiteLabelState = whiteLabelSettings?.default; + } } From 890161738e060969cc0d7ff71f045ecc5e33bca2 Mon Sep 17 00:00:00 2001 From: Adish M <44204658+adishM98@users.noreply.github.com> Date: Wed, 2 Jul 2025 17:35:53 +0530 Subject: [PATCH 3/5] refactor: streamline workflow worker setup and command determination (#13152) * refactor: streamline workflow worker setup and command determination * consolidate plugin build and prune commands in Dockerfile --- docker/ee/ee-entrypoint.sh | 49 +++++++++++++++--------------- docker/ee/ee-production.Dockerfile | 14 +++------ 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/docker/ee/ee-entrypoint.sh b/docker/ee/ee-entrypoint.sh index f9319e16be..23cd04ab0e 100755 --- a/docker/ee/ee-entrypoint.sh +++ b/docker/ee/ee-entrypoint.sh @@ -42,6 +42,20 @@ 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 @@ -63,14 +77,14 @@ if [ -n "$NEO4J_AUTH" ]; then export NEO4J_USERNAME export NEO4J_PASSWORD - echo "Neo4j authentication configured with username: $NEO4J_USERNAME" + echo "Neo4j authentication configured with username: $NEO4J_USERNAME" >/dev/null 2>&1 else - echo "NEO4J_AUTH not set, using default authentication" + 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..." + echo "Setting Neo4j initial password..." >/dev/null 2>&1 # Ensure Neo4j is not running before setting the initial password neo4j stop || true @@ -78,27 +92,27 @@ if [ "$NEO4J_AUTH" != "none" ] && [ -n "$NEO4J_PASSWORD" ]; then # 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" + 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" + 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" + 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" + echo "Warning: Could not set Neo4j password, it may already be set" >/dev/null 2>&1 } fi fi # Update Neo4j configuration -echo "Configuring Neo4j..." +echo "Configuring Neo4j..." >/dev/null 2>&1 cat > /etc/neo4j/neo4j.conf << EOF # Neo4j configuration dbms.security.auth_enabled=true @@ -124,12 +138,12 @@ 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..." +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 (via status check)" + echo "Neo4j is ready 🚀" NEO4J_READY=true break fi @@ -143,7 +157,7 @@ for i in {1..60}; do fi fi - echo "Waiting for Neo4j to start... ($i/60)" + echo "Waiting for Neo4j to start... ($i/60)" >/dev/null 2>&1 sleep 2 done @@ -151,19 +165,6 @@ if [ "$NEO4J_READY" = false ]; then echo "WARNING: Neo4j may not be fully started yet, but continuing..." 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 - # 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" diff --git a/docker/ee/ee-production.Dockerfile b/docker/ee/ee-production.Dockerfile index f058b911d5..c69843063b 100644 --- a/docker/ee/ee-production.Dockerfile +++ b/docker/ee/ee-production.Dockerfile @@ -34,8 +34,7 @@ COPY ./package.json ./package.json 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 -RUN npm --prefix plugins prune --omit=dev +RUN NODE_ENV=production npm --prefix plugins run build && npm --prefix plugins prune --omit=dev ENV TOOLJET_EDITION=ee @@ -52,14 +51,12 @@ ENV TOOLJET_EDITION=ee 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 -RUN npm install -g copyfiles -RUN npm --prefix server run build -RUN npm prune --production --prefix 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, unzip, etc. +# Install dependencies for PostgREST, curl, tar, etc. RUN apt-get update && apt-get install -y \ - curl ca-certificates unzip tar \ + curl ca-certificates tar \ && rm -rf /var/lib/apt/lists/* ENV POSTGREST_VERSION=v12.2.0 @@ -81,7 +78,6 @@ RUN apt-get update && \ ca-certificates \ xz-utils \ tar \ - zip \ postgresql-client \ redis \ libaio1 \ From 6aec215e96cf8b6d10de71957d0bdbde48665a0f Mon Sep 17 00:00:00 2001 From: adishM98 Bot Date: Wed, 2 Jul 2025 12:06:41 +0000 Subject: [PATCH 4/5] =?UTF-8?q?=F0=9F=94=84=20chore:=20update=20submodules?= =?UTF-8?q?=20to=20latest=20main=20after=20auto-merge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/ee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/ee b/server/ee index 80f2e4cab8..cc864000dd 160000 --- a/server/ee +++ b/server/ee @@ -1 +1 @@ -Subproject commit 80f2e4cab88aa586e9b8a731f8643bcc0cef52e7 +Subproject commit cc864000dd03cc345e53ae9fc43821d3174f4c64 From f56ea9292b6fcd21338f64aa5b41c6ffd22cf8af Mon Sep 17 00:00:00 2001 From: Rohan Lahori <64496391+rohanlahori@users.noreply.github.com> Date: Wed, 2 Jul 2025 17:42:41 +0530 Subject: [PATCH 5/5] migration fix (#13156) --- .../licensing/services/count.service.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/server/src/modules/licensing/services/count.service.ts b/server/src/modules/licensing/services/count.service.ts index e807a38393..9c27505113 100644 --- a/server/src/modules/licensing/services/count.service.ts +++ b/server/src/modules/licensing/services/count.service.ts @@ -274,6 +274,29 @@ export class LicenseCountsService implements ILicenseCountsService { manager ); } + async getUserIdWithEndUserRole(manager: EntityManager): Promise { + const statusList = [WORKSPACE_USER_STATUS.INVITED, WORKSPACE_USER_STATUS.ACTIVE]; + + const users = await manager.find(User, { + select: ['id'], + where: { + status: Not(USER_STATUS.ARCHIVED), + organizationUsers: { + status: In(statusList), + }, + userPermissions: { + name: USER_ROLE.END_USER, + organization: { + status: WORKSPACE_STATUS.ACTIVE, + }, + }, + }, + relations: ['organizationUsers', 'userPermissions', 'userPermissions.organization'], + }); + + // Extract unique user IDs + return [...new Set(users.map((user) => user.id))]; + } async fetchTotalAppCount(organizationId: string, manager: EntityManager): Promise { if (getTooljetEdition() !== TOOLJET_EDITIONS.Cloud) {