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_DB=tooljet_prod
|
||||
ORM_LOGGING=true
|
||||
NODE_ENV=production
|
||||
DEPLOYMENT_PLATFORM=ec2
|
||||
|
||||
# ToolJet Database
|
||||
|
|
|
|||
|
|
@ -89,7 +89,9 @@ COPY --from=builder /app/server/dist ./app/server/dist
|
|||
|
||||
# Define non-sudo user
|
||||
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
|
||||
|
||||
WORKDIR /app
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ FROM tooljet/tooljet-ce:latest
|
|||
COPY --from=postgrest/postgrest:v10.1.1.20221215 /bin/postgrest /bin
|
||||
|
||||
# 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"
|
||||
|
|
@ -33,6 +34,7 @@ RUN echo "[supervisord] \n" \
|
|||
# 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 \
|
||||
|
|
@ -49,6 +51,7 @@ ENV TOOLJET_HOST=http://localhost \
|
|||
PGRST_JWT_SECRET=r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj \
|
||||
ORM_LOGGING=true \
|
||||
DEPLOYMENT_PLATFORM=docker:local \
|
||||
HOME=/home/appuser \
|
||||
TERM=xterm
|
||||
|
||||
# Prepare DB and start application
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ export function CodeHinter({
|
|||
const getPreview = () => {
|
||||
if (!enablePreview) return;
|
||||
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';
|
||||
|
||||
if (error) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { CodeHinter } from '../../../CodeBuilder/CodeHinter';
|
|||
export const ProgramaticallyHandleToggleSwitch = ({
|
||||
currentState,
|
||||
darkMode,
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
label,
|
||||
index,
|
||||
callbackFunction,
|
||||
|
|
@ -18,28 +19,29 @@ export const ProgramaticallyHandleToggleSwitch = ({
|
|||
const param = { name: property };
|
||||
const definition = { value, fxActive: props.fxActive };
|
||||
const initialValue = definition?.value ?? `{{false}}`;
|
||||
|
||||
const options = {};
|
||||
return (
|
||||
<CodeHinter
|
||||
enablePreview={true}
|
||||
currentState={currentState}
|
||||
initialValue={initialValue}
|
||||
mode={options.mode}
|
||||
theme={darkMode ? 'monokai' : options.theme}
|
||||
lineWrapping={true}
|
||||
onChange={(value) => callbackFunction(index, property, value)}
|
||||
componentName={`widget/${component.name}::${label}`}
|
||||
type={paramMeta.type}
|
||||
paramName={param.name}
|
||||
paramLabel={paramMeta.displayName}
|
||||
fieldMeta={paramMeta}
|
||||
onFxPress={(active) => {
|
||||
callbackFunction(index, 'fxActive', active);
|
||||
}}
|
||||
fxActive={props?.fxActive ?? false}
|
||||
component={component}
|
||||
className="codehinter-default-input"
|
||||
/>
|
||||
<div className={`mb-2 field ${options.className}`} onClick={(e) => e.stopPropagation()}>
|
||||
<CodeHinter
|
||||
enablePreview={true}
|
||||
currentState={currentState}
|
||||
initialValue={initialValue}
|
||||
mode={options.mode}
|
||||
theme={darkMode ? 'monokai' : options.theme}
|
||||
lineWrapping={true}
|
||||
onChange={(value) => callbackFunction(index, property, value)}
|
||||
componentName={`widget/${component?.component?.name}::${param.name}`}
|
||||
type={paramMeta.type}
|
||||
paramName={param.name}
|
||||
paramLabel={paramMeta.displayName}
|
||||
fieldMeta={paramMeta}
|
||||
onFxPress={(active) => {
|
||||
callbackFunction(index, 'fxActive', active);
|
||||
}}
|
||||
fxActive={props?.fxActive ?? false}
|
||||
component={component.component}
|
||||
className={options.className}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -777,10 +777,19 @@ export function getQueryVariables(options, state) {
|
|||
switch (optionsType) {
|
||||
case 'string': {
|
||||
options = options.replace(/\n/g, ' ');
|
||||
const dynamicVariables = getDynamicVariables(options) || [];
|
||||
dynamicVariables.forEach((variable) => {
|
||||
queryVariables[variable] = resolveReferences(variable, state);
|
||||
});
|
||||
// check if {{var}} and %%var%% are present in the string
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -800,6 +809,7 @@ export function getQueryVariables(options, state) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return queryVariables;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -91,8 +91,62 @@ function resolveCode(code, state, customObjects = {}, withError = false, reserve
|
|||
if (withError) return [result, error];
|
||||
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 '';
|
||||
const reservedKeyword = ['app']; //Keywords that slows down the app
|
||||
object = _.clone(object);
|
||||
|
|
@ -100,6 +154,10 @@ export function resolveReferences(object, state, defaultValue, customObjects = {
|
|||
let error;
|
||||
switch (objectType) {
|
||||
case 'string': {
|
||||
if (object.includes('{{') && object.includes('}}') && object.includes('%%') && object.includes('%%')) {
|
||||
object = resolveString(object, state, customObjects, reservedKeyword, withError, forPreviewBox);
|
||||
}
|
||||
|
||||
if (object.startsWith('{{') && object.endsWith('}}')) {
|
||||
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 { PluginsModule } from './modules/plugins/plugins.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 { RequestContextModule } from './modules/request_context/request-context.module';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
|
|
@ -101,31 +99,7 @@ const imports = [
|
|||
CopilotModule,
|
||||
];
|
||||
|
||||
if (process.env.SERVE_CLIENT !== 'false') {
|
||||
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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (process.env.SERVE_CLIENT !== 'false' && process.env.NODE_ENV === 'production') {
|
||||
imports.unshift(
|
||||
ServeStaticModule.forRoot({
|
||||
// Have to remove trailing slash of SUB_PATH.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,28 @@ const fs = require('fs');
|
|||
|
||||
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() {
|
||||
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
|
||||
bufferLogs: true,
|
||||
|
|
@ -97,9 +119,13 @@ async function bootstrap() {
|
|||
defaultVersion: VERSION_NEUTRAL,
|
||||
});
|
||||
|
||||
const listen_addr = process.env.LISTEN_ADDR || "::";
|
||||
const listen_addr = process.env.LISTEN_ADDR || '::';
|
||||
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 () {
|
||||
const tooljetHost = configService.get<string>('TOOLJET_HOST');
|
||||
console.log(`Ready to use at ${tooljetHost} 🚀`);
|
||||
|
|
|
|||
|
|
@ -409,6 +409,38 @@ export class DataQueriesService {
|
|||
return object;
|
||||
} else if (typeof object === 'string') {
|
||||
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) {
|
||||
object = options[object];
|
||||
return object;
|
||||
|
|
|
|||
Loading…
Reference in a new issue