diff --git a/.gitignore b/.gitignore index 63dd4bf4ff..4ff2ca8a8b 100644 --- a/.gitignore +++ b/.gitignore @@ -20,27 +20,13 @@ docs-dist/ dist/ docker/**/storage cache/diskstore-* -*.nbdump -storage/duplicator/* -storage/backups/* **/.dumi/tmp **/.dumi/tmp-test **/.dumi/tmp-production packages/core/client/docs/contributing.md packages/core/app/client/src/.plugins -storage/plugins -storage/tar -storage/tmp -storage/print-templates -storage/cache -storage/app.watch.ts -storage/.upgrading -storage/logs-e2e -storage/uploads-e2e -storage/.pm2-* tsconfig.paths.json /playwright -/storage/playwright .swc ncc-cache/ yarn--** diff --git a/Dockerfile b/Dockerfile index a94d3e8f45..662dfdbdb2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.13-bullseye as builder +FROM node:20-bookworm as builder ARG VERDACCIO_URL=http://host.docker.internal:10104/ ARG COMMIT_HASH ARG APPEND_PRESET_LOCAL_PLUGINS @@ -7,10 +7,17 @@ ARG PLUGINS_DIRS ENV PLUGINS_DIRS=${PLUGINS_DIRS} +RUN apt-get update && apt-get install -y jq expect -RUN npx npm-cli-adduser --username test --password test -e test@nocobase.com -r $VERDACCIO_URL +RUN expect < /etc/apt/sources.list.d/pgdg.list' +RUN sh -c 'echo "deb http://mirrors.ustc.edu.cn/postgresql/repos/apt bookworm-pgdg main" > /etc/apt/sources.list.d/pgdg.list' RUN wget --quiet -O - http://mirrors.ustc.edu.cn/postgresql/repos/apt/ACCC4CF8.asc | apt-key add - RUN apt-get update && apt-get -y --no-install-recommends install nginx libaio1 postgresql-client-16 postgresql-client-17 \ diff --git a/docker/nocobase/Dockerfile b/docker/nocobase/Dockerfile index 41841cb7d5..f81c515592 100644 --- a/docker/nocobase/Dockerfile +++ b/docker/nocobase/Dockerfile @@ -1,4 +1,4 @@ -FROM node:18-bullseye-slim as builder +FROM node:20-bookworm-slim as builder ARG CNA_VERSION @@ -14,7 +14,7 @@ RUN cd /app \ && rm -rf nocobase.tar.gz \ && tar -zcf ./nocobase.tar.gz -C /app/my-nocobase-app . -FROM node:18-bullseye-slim +FROM node:20-bookworm-slim # COPY ./sources.list /etc/apt/sources.list RUN ARCH= && dpkgArch="$(dpkg --print-architecture)" \ diff --git a/lerna.json b/lerna.json index d2dd1e12e9..129fb50861 100644 --- a/lerna.json +++ b/lerna.json @@ -2,9 +2,7 @@ "version": "1.6.0-alpha.9", "npmClient": "yarn", "useWorkspaces": true, - "npmClientArgs": [ - "--ignore-engines" - ], + "npmClientArgs": ["--ignore-engines"], "command": { "version": { "forcePublish": true, diff --git a/packages/core/auth/src/auth.ts b/packages/core/auth/src/auth.ts index 7c1de54a4e..b0622576ee 100644 --- a/packages/core/auth/src/auth.ts +++ b/packages/core/auth/src/auth.ts @@ -31,6 +31,10 @@ interface IAuth { } export abstract class Auth implements IAuth { + /** + * options keys that are not allowed to use environment variables + */ + public static optionsKeysNotAllowedInEnv: string[]; abstract user: Model; protected authenticator: Authenticator; protected options: { diff --git a/packages/core/client/src/application/Application.tsx b/packages/core/client/src/application/Application.tsx index f66810ace7..f4062bda93 100644 --- a/packages/core/client/src/application/Application.tsx +++ b/packages/core/client/src/application/Application.tsx @@ -99,6 +99,7 @@ export class Application { public schemaSettingsManager: SchemaSettingsManager; public dataSourceManager: DataSourceManager; public name: string; + public globalVars: Record = {}; loading = true; maintained = false; @@ -465,4 +466,12 @@ export class Application { componentOption, ); } + + addGlobalVar(key: string, value: any) { + set(this.globalVars, key, value); + } + + getGlobalVar(key) { + return get(this.globalVars, key); + } } diff --git a/packages/core/client/src/application/hooks/index.ts b/packages/core/client/src/application/hooks/index.ts index e7d4f06ee0..57fb89faff 100644 --- a/packages/core/client/src/application/hooks/index.ts +++ b/packages/core/client/src/application/hooks/index.ts @@ -11,3 +11,4 @@ export * from './useApp'; export * from './useAppSpin'; export * from './usePlugin'; export * from './useRouter'; +export * from './useGlobalVariable'; diff --git a/packages/core/client/src/application/hooks/useGlobalVariable.ts b/packages/core/client/src/application/hooks/useGlobalVariable.ts new file mode 100644 index 0000000000..1ef7ef76d1 --- /dev/null +++ b/packages/core/client/src/application/hooks/useGlobalVariable.ts @@ -0,0 +1,31 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { isFunction } from 'lodash'; +import { useMemo } from 'react'; +import { useApp } from './'; + +export const useGlobalVariable = (key: string) => { + const app = useApp(); + + const variable = useMemo(() => { + return app.getGlobalVar(key); + }, [app, key]); + + if (isFunction(variable)) { + try { + return variable(); + } catch (error) { + console.error(`Error calling global variable function for key: ${key}`, error); + return undefined; + } + } + + return variable; +}; diff --git a/packages/core/client/src/modules/fields/component/Input/inputComponentSettings.tsx b/packages/core/client/src/modules/fields/component/Input/inputComponentSettings.tsx index deae4f1a29..8f53455a71 100644 --- a/packages/core/client/src/modules/fields/component/Input/inputComponentSettings.tsx +++ b/packages/core/client/src/modules/fields/component/Input/inputComponentSettings.tsx @@ -72,7 +72,7 @@ export const enableLinkSettingsItem: SchemaSettingsItemType = { const { fieldSchema: columnSchema } = useColumnSchema(); const schema = useFieldSchema(); const fieldSchema = columnSchema || schema; - const { name } = useBlockContext(); + const { name } = useBlockContext() || {}; return name !== 'kanban' && (fieldSchema?.['x-read-pretty'] || field.readPretty); }, useComponentProps() { diff --git a/packages/core/client/src/schema-component/antd/input/Json.tsx b/packages/core/client/src/schema-component/antd/input/Json.tsx index 5d5a5510c2..eb1a66c9b0 100644 --- a/packages/core/client/src/schema-component/antd/input/Json.tsx +++ b/packages/core/client/src/schema-component/antd/input/Json.tsx @@ -7,13 +7,13 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ +import { css, cx } from '@emotion/css'; import { Field } from '@formily/core'; import { useField } from '@formily/react'; import { Input } from 'antd'; import { TextAreaProps } from 'antd/es/input'; -import React, { useState, useEffect, Ref } from 'react'; -import { cx, css } from '@emotion/css'; import JSON5 from 'json5'; +import React, { Ref, useEffect, useState } from 'react'; export type JSONTextAreaProps = TextAreaProps & { value?: string; space?: number; json5?: boolean }; @@ -30,7 +30,16 @@ export const Json = React.forwardRef( useEffect(() => { try { if (value != null) { - setText(_JSON.stringify(value, null, space)); + if (typeof value === 'string') { + try { + _JSON.parse(value); + setText(value); + } catch (error) { + setText(_JSON.stringify(value, null, space)); + } + } else { + setText(_JSON.stringify(value, null, space)); + } } else { setText(undefined); } diff --git a/packages/core/client/src/schema-component/antd/variable/TextArea.tsx b/packages/core/client/src/schema-component/antd/variable/TextArea.tsx index 519e814866..0c0b127468 100644 --- a/packages/core/client/src/schema-component/antd/variable/TextArea.tsx +++ b/packages/core/client/src/schema-component/antd/variable/TextArea.tsx @@ -223,7 +223,7 @@ function useVariablesFromValue(value: string, delimiters: [string, string] = ['{ export function TextArea(props) { const { wrapSSR, hashId, componentCls } = useStyles(); - const { scope, onChange, changeOnSelect, style, fieldNames, delimiters = ['{{', '}}'] } = props; + const { scope, onChange, changeOnSelect, style, fieldNames, delimiters = ['{{', '}}'], addonBefore } = props; const value = typeof props.value === 'string' ? props.value : props.value == null ? '' : props.value.toString(); const variables = useVariablesFromValue(value, delimiters); const inputRef = useRef(null); @@ -397,7 +397,6 @@ export function TextArea(props) { }, [onChange, delimitersString], ); - const disabled = props.disabled || form.disabled; return wrapSSR( + {addonBefore && ( +
+ {addonBefore} +
+ )}
{ + const environmentVariables = useGlobalVariable('$env'); + return useMemo(() => { + if (environmentVariables) { + return [environmentVariables].filter(Boolean); + } + return scope; + }, [environmentVariables, scope]); +}; + +const isVariable = (value) => { + const regex = /{{.*?}}/; + return regex.test(value); +}; +interface TextAreaWithGlobalScopeProps { + supportsLineBreak?: boolean; + password?: boolean; + number?: boolean; + boolean?: boolean; + value?: any; + scope?: string | object; + [key: string]: any; +} + +export const TextAreaWithGlobalScope = connect((props: TextAreaWithGlobalScopeProps) => { + const { supportsLineBreak, password, number, boolean, ...others } = props; + const scope = useEnvironmentVariableOptions(props.scope); + const fieldNames = { value: 'name', label: 'title' }; + + if (supportsLineBreak) { + return ; + } + if (number) { + return ; + } + if (password && props.value && !isVariable(props.value)) { + return ; + } + if (boolean) { + return ; + } + return