mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-05 22:38:48 +00:00
Merge pull request #6552 from ToolJet/merge-back/2.6.1
Merge back/2.6.1
This commit is contained in:
commit
d24744e0cf
12 changed files with 166 additions and 58 deletions
2
.version
2
.version
|
|
@ -1 +1 @@
|
||||||
2.6.0
|
2.6.1
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ PG_HOST=__required__
|
||||||
PG_PASS=__required__
|
PG_PASS=__required__
|
||||||
PG_DB=tooljet_prod
|
PG_DB=tooljet_prod
|
||||||
ORM_LOGGING=true
|
ORM_LOGGING=true
|
||||||
|
NODE_ENV=production
|
||||||
DEPLOYMENT_PLATFORM=ec2
|
DEPLOYMENT_PLATFORM=ec2
|
||||||
|
|
||||||
# ToolJet Database
|
# ToolJet Database
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,9 @@ COPY --from=builder /app/server/dist ./app/server/dist
|
||||||
|
|
||||||
# Define non-sudo user
|
# Define non-sudo user
|
||||||
RUN useradd --create-home appuser \
|
RUN useradd --create-home appuser \
|
||||||
&& chown -R appuser:appuser /app
|
&& chown -R appuser:0 /app \
|
||||||
|
&& chmod u+x /app \
|
||||||
|
&& chmod -R g=u /app
|
||||||
USER appuser
|
USER appuser
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ FROM tooljet/tooljet-ce:latest
|
||||||
COPY --from=postgrest/postgrest:v10.1.1.20221215 /bin/postgrest /bin
|
COPY --from=postgrest/postgrest:v10.1.1.20221215 /bin/postgrest /bin
|
||||||
|
|
||||||
# Install Postgres
|
# Install Postgres
|
||||||
|
USER root
|
||||||
RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
|
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://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://deb.debian.org/debian"
|
||||||
|
|
@ -33,6 +34,7 @@ RUN echo "[supervisord] \n" \
|
||||||
# ENV defaults
|
# ENV defaults
|
||||||
ENV TOOLJET_HOST=http://localhost \
|
ENV TOOLJET_HOST=http://localhost \
|
||||||
PORT=80 \
|
PORT=80 \
|
||||||
|
NODE_ENV=production \
|
||||||
LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key \
|
LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key \
|
||||||
SECRET_KEY_BASE=replace_with_secret_key_base \
|
SECRET_KEY_BASE=replace_with_secret_key_base \
|
||||||
PG_DB=tooljet_production \
|
PG_DB=tooljet_production \
|
||||||
|
|
@ -49,6 +51,7 @@ ENV TOOLJET_HOST=http://localhost \
|
||||||
PGRST_JWT_SECRET=r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj \
|
PGRST_JWT_SECRET=r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj \
|
||||||
ORM_LOGGING=true \
|
ORM_LOGGING=true \
|
||||||
DEPLOYMENT_PLATFORM=docker:local \
|
DEPLOYMENT_PLATFORM=docker:local \
|
||||||
|
HOME=/home/appuser \
|
||||||
TERM=xterm
|
TERM=xterm
|
||||||
|
|
||||||
# Prepare DB and start application
|
# Prepare DB and start application
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,7 @@ export function CodeHinter({
|
||||||
const getPreview = () => {
|
const getPreview = () => {
|
||||||
if (!enablePreview) return;
|
if (!enablePreview) return;
|
||||||
const customResolvables = getCustomResolvables();
|
const customResolvables = getCustomResolvables();
|
||||||
const [preview, error] = resolveReferences(currentValue, realState, null, customResolvables, true);
|
const [preview, error] = resolveReferences(currentValue, realState, null, customResolvables, true, true);
|
||||||
const themeCls = darkMode ? 'bg-dark py-1' : 'bg-light py-1';
|
const themeCls = darkMode ? 'bg-dark py-1' : 'bg-light py-1';
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { CodeHinter } from '../../../CodeBuilder/CodeHinter';
|
||||||
export const ProgramaticallyHandleToggleSwitch = ({
|
export const ProgramaticallyHandleToggleSwitch = ({
|
||||||
currentState,
|
currentState,
|
||||||
darkMode,
|
darkMode,
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
label,
|
label,
|
||||||
index,
|
index,
|
||||||
callbackFunction,
|
callbackFunction,
|
||||||
|
|
@ -18,28 +19,29 @@ export const ProgramaticallyHandleToggleSwitch = ({
|
||||||
const param = { name: property };
|
const param = { name: property };
|
||||||
const definition = { value, fxActive: props.fxActive };
|
const definition = { value, fxActive: props.fxActive };
|
||||||
const initialValue = definition?.value ?? `{{false}}`;
|
const initialValue = definition?.value ?? `{{false}}`;
|
||||||
|
|
||||||
const options = {};
|
const options = {};
|
||||||
return (
|
return (
|
||||||
<CodeHinter
|
<div className={`mb-2 field ${options.className}`} onClick={(e) => e.stopPropagation()}>
|
||||||
enablePreview={true}
|
<CodeHinter
|
||||||
currentState={currentState}
|
enablePreview={true}
|
||||||
initialValue={initialValue}
|
currentState={currentState}
|
||||||
mode={options.mode}
|
initialValue={initialValue}
|
||||||
theme={darkMode ? 'monokai' : options.theme}
|
mode={options.mode}
|
||||||
lineWrapping={true}
|
theme={darkMode ? 'monokai' : options.theme}
|
||||||
onChange={(value) => callbackFunction(index, property, value)}
|
lineWrapping={true}
|
||||||
componentName={`widget/${component.name}::${label}`}
|
onChange={(value) => callbackFunction(index, property, value)}
|
||||||
type={paramMeta.type}
|
componentName={`widget/${component?.component?.name}::${param.name}`}
|
||||||
paramName={param.name}
|
type={paramMeta.type}
|
||||||
paramLabel={paramMeta.displayName}
|
paramName={param.name}
|
||||||
fieldMeta={paramMeta}
|
paramLabel={paramMeta.displayName}
|
||||||
onFxPress={(active) => {
|
fieldMeta={paramMeta}
|
||||||
callbackFunction(index, 'fxActive', active);
|
onFxPress={(active) => {
|
||||||
}}
|
callbackFunction(index, 'fxActive', active);
|
||||||
fxActive={props?.fxActive ?? false}
|
}}
|
||||||
component={component}
|
fxActive={props?.fxActive ?? false}
|
||||||
className="codehinter-default-input"
|
component={component.component}
|
||||||
/>
|
className={options.className}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -777,10 +777,19 @@ export function getQueryVariables(options, state) {
|
||||||
switch (optionsType) {
|
switch (optionsType) {
|
||||||
case 'string': {
|
case 'string': {
|
||||||
options = options.replace(/\n/g, ' ');
|
options = options.replace(/\n/g, ' ');
|
||||||
const dynamicVariables = getDynamicVariables(options) || [];
|
// check if {{var}} and %%var%% are present in the string
|
||||||
dynamicVariables.forEach((variable) => {
|
|
||||||
queryVariables[variable] = resolveReferences(variable, state);
|
if (options.includes('{{') && options.includes('%%')) {
|
||||||
});
|
const vars = resolveReferences(options, state);
|
||||||
|
console.log('queryVariables', { options, vars });
|
||||||
|
queryVariables[options] = vars;
|
||||||
|
} else {
|
||||||
|
const dynamicVariables = getDynamicVariables(options) || [];
|
||||||
|
dynamicVariables.forEach((variable) => {
|
||||||
|
queryVariables[variable] = resolveReferences(variable, state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -800,6 +809,7 @@ export function getQueryVariables(options, state) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return queryVariables;
|
return queryVariables;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,8 +91,62 @@ function resolveCode(code, state, customObjects = {}, withError = false, reserve
|
||||||
if (withError) return [result, error];
|
if (withError) return [result, error];
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
export function resolveString(str, state, customObjects, reservedKeyword, withError, forPreviewBox) {
|
||||||
|
let resolvedStr = str;
|
||||||
|
|
||||||
export function resolveReferences(object, state, defaultValue, customObjects = {}, withError = false) {
|
// Resolve {{object}}
|
||||||
|
const codeRegex = /(\{\{.+?\}\})/g;
|
||||||
|
const codeMatches = resolvedStr.match(codeRegex);
|
||||||
|
|
||||||
|
if (codeMatches) {
|
||||||
|
codeMatches.forEach((codeMatch) => {
|
||||||
|
const code = codeMatch.replace('{{', '').replace('}}', '');
|
||||||
|
|
||||||
|
if (reservedKeyword.includes(code)) {
|
||||||
|
resolvedStr = resolvedStr.replace(codeMatch, '');
|
||||||
|
} else {
|
||||||
|
const resolvedCode = resolveCode(code, state, customObjects, withError, reservedKeyword, true);
|
||||||
|
if (forPreviewBox) {
|
||||||
|
resolvedStr = resolvedStr.replace(codeMatch, resolvedCode[0]);
|
||||||
|
} else {
|
||||||
|
resolvedStr = resolvedStr.replace(codeMatch, resolvedCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve %%object%%
|
||||||
|
const serverRegex = /(%%.+?%%)/g;
|
||||||
|
const serverMatches = resolvedStr.match(serverRegex);
|
||||||
|
|
||||||
|
if (serverMatches) {
|
||||||
|
serverMatches.forEach((serverMatch) => {
|
||||||
|
const code = serverMatch.replace(/%%/g, '');
|
||||||
|
|
||||||
|
if (code.includes('server.') && !/^server\.[A-Za-z0-9]+$/.test(code)) {
|
||||||
|
resolvedStr = resolvedStr.replace(serverMatch, '');
|
||||||
|
} else {
|
||||||
|
const resolvedCode = resolveCode(code, state, customObjects, withError, reservedKeyword, false);
|
||||||
|
if (forPreviewBox) {
|
||||||
|
resolvedStr = resolvedStr.replace(serverMatch, resolvedCode[0]);
|
||||||
|
} else {
|
||||||
|
resolvedStr = resolvedStr.replace(serverMatch, resolvedCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveReferences(
|
||||||
|
object,
|
||||||
|
state,
|
||||||
|
defaultValue,
|
||||||
|
customObjects = {},
|
||||||
|
withError = false,
|
||||||
|
forPreviewBox = false
|
||||||
|
) {
|
||||||
if (object === '{{{}}}') return '';
|
if (object === '{{{}}}') return '';
|
||||||
const reservedKeyword = ['app']; //Keywords that slows down the app
|
const reservedKeyword = ['app']; //Keywords that slows down the app
|
||||||
object = _.clone(object);
|
object = _.clone(object);
|
||||||
|
|
@ -100,6 +154,10 @@ export function resolveReferences(object, state, defaultValue, customObjects = {
|
||||||
let error;
|
let error;
|
||||||
switch (objectType) {
|
switch (objectType) {
|
||||||
case 'string': {
|
case 'string': {
|
||||||
|
if (object.includes('{{') && object.includes('}}') && object.includes('%%') && object.includes('%%')) {
|
||||||
|
object = resolveString(object, state, customObjects, reservedKeyword, withError, forPreviewBox);
|
||||||
|
}
|
||||||
|
|
||||||
if (object.startsWith('{{') && object.endsWith('}}')) {
|
if (object.startsWith('{{') && object.endsWith('}}')) {
|
||||||
const code = object.replace('{{', '').replace('}}', '');
|
const code = object.replace('{{', '').replace('}}', '');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
2.6.0
|
2.6.1
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,6 @@ import { GroupPermissionsModule } from './modules/group_permissions/group_permis
|
||||||
import { TooljetDbModule } from './modules/tooljet_db/tooljet_db.module';
|
import { TooljetDbModule } from './modules/tooljet_db/tooljet_db.module';
|
||||||
import { PluginsModule } from './modules/plugins/plugins.module';
|
import { PluginsModule } from './modules/plugins/plugins.module';
|
||||||
import { CopilotModule } from './modules/copilot/copilot.module';
|
import { CopilotModule } from './modules/copilot/copilot.module';
|
||||||
import * as path from 'path';
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import { AppEnvironmentsModule } from './modules/app_environments/app_environments.module';
|
import { AppEnvironmentsModule } from './modules/app_environments/app_environments.module';
|
||||||
import { RequestContextModule } from './modules/request_context/request-context.module';
|
import { RequestContextModule } from './modules/request_context/request-context.module';
|
||||||
import { ScheduleModule } from '@nestjs/schedule';
|
import { ScheduleModule } from '@nestjs/schedule';
|
||||||
|
|
@ -101,31 +99,7 @@ const imports = [
|
||||||
CopilotModule,
|
CopilotModule,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (process.env.SERVE_CLIENT !== 'false') {
|
if (process.env.SERVE_CLIENT !== 'false' && process.env.NODE_ENV === 'production') {
|
||||||
const filesToReplaceAssetPath = ['index.html', 'runtime.js', 'main.js'];
|
|
||||||
|
|
||||||
for (const fileName of filesToReplaceAssetPath) {
|
|
||||||
const file = join(__dirname, '../../../', 'frontend/build', fileName);
|
|
||||||
|
|
||||||
let newValue = process.env.SUB_PATH;
|
|
||||||
|
|
||||||
if (process.env.SUB_PATH === undefined) {
|
|
||||||
newValue = fileName === 'index.html' ? '/' : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.readFile(file, 'utf8', function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
return console.log(err);
|
|
||||||
}
|
|
||||||
const result = data
|
|
||||||
.replace(/__REPLACE_SUB_PATH__\/api/g, path.join(newValue, '/api'))
|
|
||||||
.replace(/__REPLACE_SUB_PATH__/g, newValue);
|
|
||||||
fs.writeFile(file, result, 'utf8', function (err) {
|
|
||||||
if (err) return console.log(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
imports.unshift(
|
imports.unshift(
|
||||||
ServeStaticModule.forRoot({
|
ServeStaticModule.forRoot({
|
||||||
// Have to remove trailing slash of SUB_PATH.
|
// Have to remove trailing slash of SUB_PATH.
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,28 @@ const fs = require('fs');
|
||||||
|
|
||||||
globalThis.TOOLJET_VERSION = fs.readFileSync('./.version', 'utf8').trim();
|
globalThis.TOOLJET_VERSION = fs.readFileSync('./.version', 'utf8').trim();
|
||||||
|
|
||||||
|
function replaceSubpathPlaceHoldersInStaticAssets() {
|
||||||
|
const filesToReplaceAssetPath = ['index.html', 'runtime.js', 'main.js'];
|
||||||
|
|
||||||
|
for (const fileName of filesToReplaceAssetPath) {
|
||||||
|
const file = join(__dirname, '../../../', 'frontend/build', fileName);
|
||||||
|
|
||||||
|
let newValue = process.env.SUB_PATH;
|
||||||
|
|
||||||
|
if (process.env.SUB_PATH === undefined) {
|
||||||
|
newValue = fileName === 'index.html' ? '/' : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = fs.readFileSync(file, { encoding: 'utf8' });
|
||||||
|
|
||||||
|
const result = data
|
||||||
|
.replace(/__REPLACE_SUB_PATH__\/api/g, join(newValue, '/api'))
|
||||||
|
.replace(/__REPLACE_SUB_PATH__/g, newValue);
|
||||||
|
|
||||||
|
fs.writeFileSync(file, result, { encoding: 'utf8' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
|
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
|
||||||
bufferLogs: true,
|
bufferLogs: true,
|
||||||
|
|
@ -97,9 +119,13 @@ async function bootstrap() {
|
||||||
defaultVersion: VERSION_NEUTRAL,
|
defaultVersion: VERSION_NEUTRAL,
|
||||||
});
|
});
|
||||||
|
|
||||||
const listen_addr = process.env.LISTEN_ADDR || "::";
|
const listen_addr = process.env.LISTEN_ADDR || '::';
|
||||||
const port = parseInt(process.env.PORT) || 3000;
|
const port = parseInt(process.env.PORT) || 3000;
|
||||||
|
|
||||||
|
if (process.env.SERVE_CLIENT !== 'false' && process.env.NODE_ENV === 'production') {
|
||||||
|
replaceSubpathPlaceHoldersInStaticAssets();
|
||||||
|
}
|
||||||
|
|
||||||
await app.listen(port, listen_addr, function () {
|
await app.listen(port, listen_addr, function () {
|
||||||
const tooljetHost = configService.get<string>('TOOLJET_HOST');
|
const tooljetHost = configService.get<string>('TOOLJET_HOST');
|
||||||
console.log(`Ready to use at ${tooljetHost} 🚀`);
|
console.log(`Ready to use at ${tooljetHost} 🚀`);
|
||||||
|
|
|
||||||
|
|
@ -409,6 +409,38 @@ export class DataQueriesService {
|
||||||
return object;
|
return object;
|
||||||
} else if (typeof object === 'string') {
|
} else if (typeof object === 'string') {
|
||||||
object = object.replace(/\n/g, ' ');
|
object = object.replace(/\n/g, ' ');
|
||||||
|
|
||||||
|
//if object has {{}} and %%%% then resolve %% in a single string
|
||||||
|
if (object.includes('{{') && object.includes('}}') && object.includes('%%') && object.includes('%%')) {
|
||||||
|
let resolvedvar = options[object];
|
||||||
|
|
||||||
|
if (object.includes(`server.`)) {
|
||||||
|
// find all server variables in the string
|
||||||
|
const serverVariables = object.match(/server.(.*?)%%/g);
|
||||||
|
|
||||||
|
serverVariables?.map((variable) => {
|
||||||
|
return variable
|
||||||
|
.match(/server.(.*?)%%/g)[0]
|
||||||
|
.replace('%%', '')
|
||||||
|
.replace('server.', '');
|
||||||
|
});
|
||||||
|
|
||||||
|
const resolvedOrgVar = [];
|
||||||
|
|
||||||
|
for (const variable of serverVariables) {
|
||||||
|
const resolvedVariable = await this.resolveVariable(variable, organization_id);
|
||||||
|
resolvedOrgVar.push(resolvedVariable);
|
||||||
|
}
|
||||||
|
|
||||||
|
//replace the HiddenEnvironmentVariable with the resolved value
|
||||||
|
for (let i = 0; i < serverVariables.length; i++) {
|
||||||
|
resolvedvar = resolvedvar.replace('HiddenEnvironmentVariable', resolvedOrgVar[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedvar;
|
||||||
|
}
|
||||||
|
|
||||||
if (object.startsWith('{{') && object.endsWith('}}') && (object.match(/{{/g) || []).length === 1) {
|
if (object.startsWith('{{') && object.endsWith('}}') && (object.match(/{{/g) || []).length === 1) {
|
||||||
object = options[object];
|
object = options[object];
|
||||||
return object;
|
return object;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue