From b028d9fd2a048ff3cb2f8743dfa7cb6fab6b5763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Malfait?= Date: Mon, 31 Jul 2023 14:36:04 -0700 Subject: [PATCH] Add deploy buttons and clean environment variables (#974) * add render.yaml * Clean environment variables --------- Co-authored-by: Charles Bochet --- .ergomake/docker-compose.yml | 19 +++------ .github/workflows/ci-chromatic.yaml | 8 +--- .github/workflows/ci-front.yaml | 4 +- docs/docs/developer/local-setup.mdx | 2 +- docs/docs/hosting/self-hosting.mdx | 17 +++++--- docs/docs/others/CLI.mdx | 2 +- docs/src/theme/DocSidebarItem/Link/index.js | 5 ++- front/.env.example | 9 ++-- front/codegen.js | 2 +- front/src/generated/graphql.tsx | 6 +-- .../modules/apollo/hooks/useApolloFactory.ts | 2 +- front/src/modules/auth/hooks/useAuth.ts | 5 ++- .../auth/sign-in-up/hooks/useSignInUp.tsx | 8 ++-- .../components/ClientConfigProvider.tsx | 8 ++-- .../modules/client-config/queries/index.tsx | 2 +- .../client-config/states/isDemoModeState.ts | 6 --- .../states/isSignInPrefilledState.ts | 6 +++ .../utils/getProfilePictureAbsoluteURI.ts | 6 ++- front/src/testing/graphqlMocks.ts | 2 +- front/src/testing/mockedClient.ts | 2 +- infra/prod/front/Dockerfile | 7 ++-- render.yaml | 42 +++++++++++++++++++ server/.env.example | 25 ++++++----- server/.env.test | 28 ++++++++----- server/scripts/set-env-test.sh | 9 +++- .../activity-target.ability-handler.ts | 14 ++++--- .../client-config/client-config.entity.ts | 2 +- .../client-config/client-config.resolver.ts | 10 ++--- .../environment/environment.service.ts | 37 ++++++++++------ .../environment/environment.validation.ts | 10 ++++- .../src/integrations/integrations.module.ts | 1 - 31 files changed, 194 insertions(+), 112 deletions(-) delete mode 100644 front/src/modules/client-config/states/isDemoModeState.ts create mode 100644 front/src/modules/client-config/states/isSignInPrefilledState.ts create mode 100644 render.yaml diff --git a/.ergomake/docker-compose.yml b/.ergomake/docker-compose.yml index a3136fde540a..e16ded42a5b7 100644 --- a/.ergomake/docker-compose.yml +++ b/.ergomake/docker-compose.yml @@ -5,15 +5,11 @@ services: context: .. dockerfile: ./infra/prod/front/Dockerfile args: - REACT_APP_API_URL: "http://localhost:3000/graphql" - REACT_APP_AUTH_URL: "http://localhost:3000/auth" - REACT_APP_FILES_URL: "http://localhost:3000/files" + REACT_APP_SERVER_BASE_URL: "http://localhost:3000" ports: - "3001:3000" labels: - dev.ergomake.env.replace-arg.REACT_APP_API_URL: "https://{{ services.server.url }}/graphql" - dev.ergomake.env.replace-arg.REACT_APP_AUTH_URL: "https://{{ services.server.url }}/auth" - dev.ergomake.env.replace-arg.REACT_APP_FILES_URL: "https://{{ services.server.url }}/files" + dev.ergomake.env.replace-arg.REACT_APP_SERVER_BASE_URL: "https://{{ services.server.url }}" server: build: context: .. @@ -23,19 +19,14 @@ services: - "3000:3000" environment: DEBUG_MODE: false - DEMO_MODE: true + IS_SIGN_IN_PREFILLED: true ACCESS_TOKEN_SECRET: "secret_jwt" - ACCESS_TOKEN_EXPIRES_IN: "30m" LOGIN_TOKEN_SECRET: "secret_login_token" - LOGIN_TOKEN_EXPIRES_IN: "15m" REFRESH_TOKEN_SECRET: "secret_refresh_token" - REFRESH_TOKEN_EXPIRES_IN: "90d" PG_DATABASE_URL: "postgres://postgres:postgrespassword@postgres:5432/default?connection_limit=1" - FRONT_AUTH_CALLBACK_URL: "http://localhost:3000/verify" - STORAGE_TYPE: "local" - STORAGE_LOCAL_PATH: ".local-storage" + FRONT_BASE_URL: "http://localhost:3000" labels: - dev.ergomake.env.replace-env.FRONT_AUTH_CALLBACK_URL: "https://{{ services.server.url }}/verify" + dev.ergomake.env.replace-env.FRONT_BASE_URL: "https://{{ services.server.url }}" postgres: build: ../infra/dev/postgres environment: diff --git a/.github/workflows/ci-chromatic.yaml b/.github/workflows/ci-chromatic.yaml index d5ef046639cc..f1f0b3999e2b 100644 --- a/.github/workflows/ci-chromatic.yaml +++ b/.github/workflows/ci-chromatic.yaml @@ -11,9 +11,7 @@ jobs: if: ${{ contains(github.event.*.labels.*.name, 'run-chromatic') }} || github.event_name == 'push' }} runs-on: ubuntu-latest env: - REACT_APP_API_URL: http://127.0.0.1:3000/graphql - REACT_APP_AUTH_URL: http://127.0.0.1:3000/auth - REACT_APP_FILES_URL: http://127.0.0.1:3000/files + REACT_APP_SERVER_BASE_URL: http://127.0.0.1:3000 steps: - uses: actions/checkout@v3 if: github.event_name == 'push' @@ -33,9 +31,7 @@ jobs: run: | cd front touch .env - echo "REACT_APP_API_URL: $REACT_APP_API_URL" >> .env - echo "REACT_APP_AUTH_URL: $REACT_APP_AUTH_URL" >> .env - echo "REACT_APP_FILES_URL: $REACT_APP_FILES_URL" >> .env + echo "REACT_APP_SERVER_BASE_URL: $REACT_APP_SERVER_BASE_URL" >> .env - name: Front / Install Dependencies run: cd front && yarn - name: Publish to Chromatic diff --git a/.github/workflows/ci-front.yaml b/.github/workflows/ci-front.yaml index 76eb041fb886..a5b149a5d817 100644 --- a/.github/workflows/ci-front.yaml +++ b/.github/workflows/ci-front.yaml @@ -8,9 +8,7 @@ jobs: front-test: runs-on: ubuntu-latest env: - REACT_APP_API_URL: http://localhost:3000/graphql - REACT_APP_AUTH_URL: http://localhost:3000/auth - REACT_APP_FILES_URL: http://localhost:3000/files + REACT_APP_SERVER_BASE_URL: http://localhost:3000 steps: - uses: actions/checkout@v3 if: github.event_name == 'push' diff --git a/docs/docs/developer/local-setup.mdx b/docs/docs/developer/local-setup.mdx index c42d2ac00e73..5a096f42b995 100644 --- a/docs/docs/developer/local-setup.mdx +++ b/docs/docs/developer/local-setup.mdx @@ -1,7 +1,7 @@ --- sidebar_position: 0 sidebar_custom_props: - icon: TbArrowBigRight + icon: TbDeviceDesktop --- # Local Setup diff --git a/docs/docs/hosting/self-hosting.mdx b/docs/docs/hosting/self-hosting.mdx index cb15efdcb88e..6b48e5adc2b7 100644 --- a/docs/docs/hosting/self-hosting.mdx +++ b/docs/docs/hosting/self-hosting.mdx @@ -19,9 +19,7 @@ You will find these in the [infra/prod/front/Dockerfile](https://github.com/twen ```bash docker build \ - --build-arg REACT_APP_API_URL=REPLACE_ME \ - --build-arg REACT_APP_AUTH_URL=REPLACE_ME \ - --build-arg REACT_APP_FILES_URL=REPLACE_ME \ + --build-arg REACT_APP_SERVER_BASE_URL=REPLACE_ME \ -t twenty-front:latest \ -f ./infra/prod/front/Dockerfile . ``` @@ -36,10 +34,19 @@ docker build \ -f ./infra/prod/server/Dockerfile . ``` +## Render + +[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/twentyhq/twenty) + + ## AWS Elastic Beanstalk (soon) We are working on providing a joint Docker image - containing both the Twenty frontend and server - that you can deploy using [AWS Elastic Beanstalk](https://aws.amazon.com/elasticbeanstalk/). -## Railway (soon) -[Railway](https://railway.app) is an infrastructure platform that lets you deploy to the cloud in one click. We are currently working on making it available. \ No newline at end of file + \ No newline at end of file diff --git a/docs/docs/others/CLI.mdx b/docs/docs/others/CLI.mdx index 0279037d6423..275e8024c898 100644 --- a/docs/docs/others/CLI.mdx +++ b/docs/docs/others/CLI.mdx @@ -4,6 +4,6 @@ sidebar_custom_props: icon: TbTerminal2 --- -# CLI (soon) +# CLI Available soon! \ No newline at end of file diff --git a/docs/src/theme/DocSidebarItem/Link/index.js b/docs/src/theme/DocSidebarItem/Link/index.js index aaa820801cb2..5cc231aeed94 100644 --- a/docs/src/theme/DocSidebarItem/Link/index.js +++ b/docs/src/theme/DocSidebarItem/Link/index.js @@ -6,7 +6,7 @@ import Link from '@docusaurus/Link'; import isInternalUrl from '@docusaurus/isInternalUrl'; import IconExternalLink from '@theme/Icon/ExternalLink'; import styles from './styles.module.css'; -import { TbFaceIdError, TbTerminal2, TbCloud, TbServer, TbBolt, TbApps, TbTopologyStar, TbChartDots, TbBug, TbVocabulary, TbArrowBigRight } from "react-icons/tb"; +import { TbFaceIdError, TbTerminal2, TbCloud, TbServer, TbBolt, TbApps, TbTopologyStar, TbChartDots, TbBug, TbVocabulary, TbArrowBigRight, TbDeviceDesktop } from "react-icons/tb"; export default function DocSidebarItemLink({ @@ -30,7 +30,8 @@ export default function DocSidebarItemLink({ 'TbTopologyStar': TbTopologyStar, 'TbChartDots': TbChartDots, 'TbBug': TbBug, - 'TbVocabulary': TbVocabulary + 'TbVocabulary': TbVocabulary, + 'TbDeviceDesktop': TbDeviceDesktop, }; let IconComponent = customProps && customProps.icon ? icons[customProps.icon] : TbFaceIdError; diff --git a/front/.env.example b/front/.env.example index 33094934828b..02a33b5371a2 100644 --- a/front/.env.example +++ b/front/.env.example @@ -1,5 +1,6 @@ -REACT_APP_API_URL=http://localhost:3000/graphql -REACT_APP_AUTH_URL=http://localhost:3000/auth -REACT_APP_FILES_URL=http://localhost:3000/files +REACT_APP_SERVER_BASE_URL=http://localhost:3000 -CHROMATIC_PROJECT_TOKEN=REPLACE_ME \ No newline at end of file +# ———————— Optional ———————— +# REACT_APP_SERVER_AUTH_URL=http://localhost:3000/auth +# REACT_APP_SERVER_FILES_URL=http://localhost:3000/files +# CHROMATIC_PROJECT_TOKEN= diff --git a/front/codegen.js b/front/codegen.js index 3d4151371103..818affe05a6f 100644 --- a/front/codegen.js +++ b/front/codegen.js @@ -1,5 +1,5 @@ module.exports = { - schema: process.env.REACT_APP_API_URL, + schema: process.env.REACT_APP_SERVER_BASE_URL + "/graphql", documents: ['./src/**/*.tsx', './src/**/*.ts'], overwrite: true, generates: { diff --git a/front/src/generated/graphql.tsx b/front/src/generated/graphql.tsx index a98084a53a95..2459a2534c23 100644 --- a/front/src/generated/graphql.tsx +++ b/front/src/generated/graphql.tsx @@ -547,7 +547,7 @@ export type ClientConfig = { __typename?: 'ClientConfig'; authProviders: AuthProviders; debugMode: Scalars['Boolean']; - demoMode: Scalars['Boolean']; + signInPrefilled: Scalars['Boolean']; telemetry: Telemetry; }; @@ -2333,7 +2333,7 @@ export type RenewTokenMutation = { __typename?: 'Mutation', renewToken: { __type export type GetClientConfigQueryVariables = Exact<{ [key: string]: never; }>; -export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', demoMode: boolean, debugMode: boolean, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean }, telemetry: { __typename?: 'Telemetry', enabled: boolean, anonymizationEnabled: boolean } } }; +export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, debugMode: boolean, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean }, telemetry: { __typename?: 'Telemetry', enabled: boolean, anonymizationEnabled: boolean } } }; export type GetCompaniesQueryVariables = Exact<{ orderBy?: InputMaybe | CompanyOrderByWithRelationInput>; @@ -3369,7 +3369,7 @@ export const GetClientConfigDocument = gql` google password } - demoMode + signInPrefilled debugMode telemetry { enabled diff --git a/front/src/modules/apollo/hooks/useApolloFactory.ts b/front/src/modules/apollo/hooks/useApolloFactory.ts index a4c7e6aa011c..054bff601b1d 100644 --- a/front/src/modules/apollo/hooks/useApolloFactory.ts +++ b/front/src/modules/apollo/hooks/useApolloFactory.ts @@ -22,7 +22,7 @@ export function useApolloFactory() { const apolloClient = useMemo(() => { apolloRef.current = new ApolloFactory({ - uri: `${process.env.REACT_APP_API_URL}`, + uri: `${process.env.REACT_APP_SERVER_BASE_URL}/graphql`, cache: new InMemoryCache({ typePolicies: { Activity: { diff --git a/front/src/modules/auth/hooks/useAuth.ts b/front/src/modules/auth/hooks/useAuth.ts index 208019071d11..e9ec0769b291 100644 --- a/front/src/modules/auth/hooks/useAuth.ts +++ b/front/src/modules/auth/hooks/useAuth.ts @@ -118,8 +118,11 @@ export function useAuth() { ); const handleGoogleLogin = useCallback((workspaceInviteHash?: string) => { + const authServerUrl = + process.env.REACT_APP_SERVER_AUTH_URL ?? + process.env.REACT_APP_SERVER_BASE_URL + '/auth'; window.location.href = - `${process.env.REACT_APP_AUTH_URL}/google/${ + `${authServerUrl}/google/${ workspaceInviteHash ? '?inviteHash=' + workspaceInviteHash : '' }` || ''; }, []); diff --git a/front/src/modules/auth/sign-in-up/hooks/useSignInUp.tsx b/front/src/modules/auth/sign-in-up/hooks/useSignInUp.tsx index e882190e571a..ef29692b5b7f 100644 --- a/front/src/modules/auth/sign-in-up/hooks/useSignInUp.tsx +++ b/front/src/modules/auth/sign-in-up/hooks/useSignInUp.tsx @@ -6,7 +6,7 @@ import { useRecoilState, useRecoilValue } from 'recoil'; import * as Yup from 'yup'; import { authProvidersState } from '@/client-config/states/authProvidersState'; -import { isDemoModeState } from '@/client-config/states/isDemoModeState'; +import { isSignInPrefilledState } from '@/client-config/states/isSignInPrefilledState'; import { AppPath } from '@/types/AppPath'; import { PageHotkeyScope } from '@/types/PageHotkeyScope'; import { useSnackBar } from '@/ui/snack-bar/hooks/useSnackBar'; @@ -45,7 +45,7 @@ export function useSignInUp() { const { enqueueSnackBar } = useSnackBar(); const isMatchingLocation = useIsMatchingLocation(); const [authProviders] = useRecoilState(authProvidersState); - const isDemoMode = useRecoilValue(isDemoModeState); + const isSignInPrefilled = useRecoilValue(isSignInPrefilledState); const workspaceInviteHash = useParams().workspaceInviteHash; const [signInUpStep, setSignInUpStep] = useState( SignInUpStep.Init, @@ -61,8 +61,8 @@ export function useSignInUp() { mode: 'onChange', defaultValues: { exist: false, - email: isDemoMode ? 'tim@apple.dev' : '', - password: isDemoMode ? 'Applecar2025' : '', + email: isSignInPrefilled ? 'tim@apple.dev' : '', + password: isSignInPrefilled ? 'Applecar2025' : '', }, resolver: yupResolver(validationSchema), }); diff --git a/front/src/modules/client-config/components/ClientConfigProvider.tsx b/front/src/modules/client-config/components/ClientConfigProvider.tsx index ca29bfe8932d..5963ac59c5f7 100644 --- a/front/src/modules/client-config/components/ClientConfigProvider.tsx +++ b/front/src/modules/client-config/components/ClientConfigProvider.tsx @@ -3,7 +3,7 @@ import { useRecoilState } from 'recoil'; import { authProvidersState } from '@/client-config/states/authProvidersState'; import { isDebugModeState } from '@/client-config/states/isDebugModeState'; -import { isDemoModeState } from '@/client-config/states/isDemoModeState'; +import { isSignInPrefilledState } from '@/client-config/states/isSignInPrefilledState'; import { telemetryState } from '@/client-config/states/telemetryState'; import { useGetClientConfigQuery } from '~/generated/graphql'; @@ -12,7 +12,7 @@ export const ClientConfigProvider: React.FC = ({ }) => { const [, setAuthProviders] = useRecoilState(authProvidersState); const [, setDebugMode] = useRecoilState(isDebugModeState); - const [, setDemoMode] = useRecoilState(isDemoModeState); + const [, setSignInPrefilled] = useRecoilState(isSignInPrefilledState); const [, setTelemetry] = useRecoilState(telemetryState); const [isLoading, setIsLoading] = useState(true); @@ -29,14 +29,14 @@ export const ClientConfigProvider: React.FC = ({ magicLink: false, }); setDebugMode(data?.clientConfig.debugMode); - setDemoMode(data?.clientConfig.demoMode); + setSignInPrefilled(data?.clientConfig.signInPrefilled); setTelemetry(data?.clientConfig.telemetry); } }, [ data, setAuthProviders, setDebugMode, - setDemoMode, + setSignInPrefilled, setTelemetry, setIsLoading, loading, diff --git a/front/src/modules/client-config/queries/index.tsx b/front/src/modules/client-config/queries/index.tsx index cd227da93078..d53e84c65f8d 100644 --- a/front/src/modules/client-config/queries/index.tsx +++ b/front/src/modules/client-config/queries/index.tsx @@ -7,7 +7,7 @@ export const GET_CLIENT_CONFIG = gql` google password } - demoMode + signInPrefilled debugMode telemetry { enabled diff --git a/front/src/modules/client-config/states/isDemoModeState.ts b/front/src/modules/client-config/states/isDemoModeState.ts deleted file mode 100644 index 753ed61cef1e..000000000000 --- a/front/src/modules/client-config/states/isDemoModeState.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { atom } from 'recoil'; - -export const isDemoModeState = atom({ - key: 'isDemoModeState', - default: false, -}); diff --git a/front/src/modules/client-config/states/isSignInPrefilledState.ts b/front/src/modules/client-config/states/isSignInPrefilledState.ts new file mode 100644 index 000000000000..649f42d25c05 --- /dev/null +++ b/front/src/modules/client-config/states/isSignInPrefilledState.ts @@ -0,0 +1,6 @@ +import { atom } from 'recoil'; + +export const isSignInPrefilledState = atom({ + key: 'isSignInPrefilledState', + default: false, +}); diff --git a/front/src/modules/users/utils/getProfilePictureAbsoluteURI.ts b/front/src/modules/users/utils/getProfilePictureAbsoluteURI.ts index 0ebf7bb76b9d..b221047ab49d 100644 --- a/front/src/modules/users/utils/getProfilePictureAbsoluteURI.ts +++ b/front/src/modules/users/utils/getProfilePictureAbsoluteURI.ts @@ -11,5 +11,9 @@ export function getImageAbsoluteURIOrBase64(imageUrl?: string | null) { return imageUrl; } - return `${process.env.REACT_APP_FILES_URL}/${imageUrl}`; + const serverFilesUrl = + process.env.REACT_APP_SERVER_FILES_URL ?? + process.env.REACT_APP_SERVER_BASE_URL + '/files'; + + return `${serverFilesUrl}/${imageUrl}`; } diff --git a/front/src/testing/graphqlMocks.ts b/front/src/testing/graphqlMocks.ts index c10376906b73..e21a3ce1e024 100644 --- a/front/src/testing/graphqlMocks.ts +++ b/front/src/testing/graphqlMocks.ts @@ -198,7 +198,7 @@ export const graphqlMocks = [ return res( ctx.data({ clientConfig: { - demoMode: true, + signInPrefilled: true, debugMode: false, authProviders: { google: true, password: true, magicLink: false }, telemetry: { enabled: false, anonymizationEnabled: true }, diff --git a/front/src/testing/mockedClient.ts b/front/src/testing/mockedClient.ts index 64448ab4773b..acf40cb231f2 100644 --- a/front/src/testing/mockedClient.ts +++ b/front/src/testing/mockedClient.ts @@ -1,6 +1,6 @@ import { ApolloClient, InMemoryCache } from '@apollo/client'; export const mockedClient = new ApolloClient({ - uri: process.env.REACT_APP_API_URL, + uri: process.env.REACT_APP_SERVER_BASE_URL + '/graphql', cache: new InMemoryCache(), }); diff --git a/infra/prod/front/Dockerfile b/infra/prod/front/Dockerfile index 1a52b4957588..d5f353d8446d 100644 --- a/infra/prod/front/Dockerfile +++ b/infra/prod/front/Dockerfile @@ -1,8 +1,9 @@ FROM node:18.16.0-alpine as build -ARG REACT_APP_API_URL -ARG REACT_APP_AUTH_URL -ARG REACT_APP_FILES_URL + +ARG REACT_APP_SERVER_BASE_URL +ARG REACT_APP_SERVER_AUTH_URL +ARG REACT_APP_SERVER_FILES_URL COPY ./packages/ /app/packages diff --git a/render.yaml b/render.yaml new file mode 100644 index 000000000000..d6961e5dbe2b --- /dev/null +++ b/render.yaml @@ -0,0 +1,42 @@ +services: +- type: web + name: front + env: docker + dockerfilePath: ./infra/prod/front/Dockerfile + autoDeploy: false + envVars: + - key: REACT_APP_SERVER_BASE_URL + fromService: + name: server + type: web + envVarKey: RENDER_EXTERNAL_URL +- type: web + name: server + env: docker + dockerfilePath: ./infra/prod/server/Dockerfile + dockerCommand: "sh -c yarn prisma:migrate && node dist/src/main" + autoDeploy: false + envVars: + - key: ACCESS_TOKEN_SECRET + generateValue: true + - key: LOGIN_TOKEN_SECRET + generateValue: true + - key: REFRESH_TOKEN_SECRET + generateValue: true + - key: PG_DATABASE_URL + fromDatabase: + name: twenty-db + property: connectionString + - key: FRONT_BASE_URL + fromService: + name: front + type: web + envVarKey: RENDER_EXTERNAL_URL + disk: + name: twenty-disk + mountPath: /.local-storage + sizeGB: 5 + +databases: +- name: twenty-db + plan: starter diff --git a/server/.env.example b/server/.env.example index 4e9efd98497a..f3f95f6afb7c 100644 --- a/server/.env.example +++ b/server/.env.example @@ -1,13 +1,16 @@ -DEBUG_MODE=false -DEMO_MODE=true -ACCESS_TOKEN_SECRET=secret_jwt -ACCESS_TOKEN_EXPIRES_IN=30m -LOGIN_TOKEN_SECRET=secret_login_token -LOGIN_TOKEN_EXPIRES_IN=15m -REFRESH_TOKEN_SECRET=secret_refresh_token -REFRESH_TOKEN_EXPIRES_IN=90d PG_DATABASE_URL=postgres://postgres:postgrespassword@localhost:5432/default?connection_limit=1 -FRONT_AUTH_CALLBACK_URL=http://localhost:3001/verify -STORAGE_TYPE=local -STORAGE_LOCAL_PATH=.local-storage +FRONT_BASE_URL=http://localhost:3001 +ACCESS_TOKEN_SECRET=replace_me_with_a_random_string +LOGIN_TOKEN_SECRET=replace_me_with_a_random_string +REFRESH_TOKEN_SECRET=replace_me_with_a_random_string +IS_SIGN_IN_PREFILLED=true +# ———————— Optional ———————— +# DEBUG_MODE=true +# ACCESS_TOKEN_EXPIRES_IN=30m +# LOGIN_TOKEN_EXPIRES_IN=15m +# REFRESH_TOKEN_EXPIRES_IN=90d +# FRONT_AUTH_CALLBACK_URL=http://localhost:3001/verify +# AUTH_GOOGLE_ENABLED=false +# STORAGE_TYPE=local +# STORAGE_LOCAL_PATH=.local-storage \ No newline at end of file diff --git a/server/.env.test b/server/.env.test index ddf328f4e6c3..7706883a1b45 100644 --- a/server/.env.test +++ b/server/.env.test @@ -1,12 +1,20 @@ DEBUG_MODE=true -AUTH_GOOGLE_ENABLED=false -ACCESS_TOKEN_SECRET=secret_jwt -ACCESS_TOKEN_EXPIRES_IN=1d -REFRESH_TOKEN_SECRET=secret_refresh_token -REFRESH_TOKEN_EXPIRES_IN=30d -LOGIN_TOKEN_SECRET=secret_login_token -LOGIN_TOKEN_EXPIRES_IN=15m -FRONT_AUTH_CALLBACK_URL=http://localhost:3001/auth/callback PG_DATABASE_URL=postgres://postgres:postgrespassword@localhost:5432/test?connection_limit=1 -STORAGE_TYPE=local -STORAGE_LOCAL_PATH=.local-storage +# the URL of the front-end app +FRONT_BASE_URL=http://localhost:3001 +# random keys used to generate JWT tokens +ACCESS_TOKEN_SECRET=secret_jwt +LOGIN_TOKEN_SECRET=secret_login_tokens +REFRESH_TOKEN_SECRET=secret_refresh_token + + +# ———————— Optional ———————— +# DEBUG_MODE=false +# IS_SIGN_IN_PREFILLED=false +# ACCESS_TOKEN_EXPIRES_IN=30m +# LOGIN_TOKEN_EXPIRES_IN=15m +# REFRESH_TOKEN_EXPIRES_IN=90d +# FRONT_AUTH_CALLBACK_URL=http://localhost:3001/verify +# AUTH_GOOGLE_ENABLED=false +# STORAGE_TYPE=local +# STORAGE_LOCAL_PATH=.local-storage \ No newline at end of file diff --git a/server/scripts/set-env-test.sh b/server/scripts/set-env-test.sh index 29d74b2f4fd1..8eb4eee9d239 100755 --- a/server/scripts/set-env-test.sh +++ b/server/scripts/set-env-test.sh @@ -11,7 +11,14 @@ ENV_PATH="${SCRIPT_DIR}/../.env.test" if [ -f "${ENV_PATH}" ]; then echo "🔵 - Loading environment variables from "${ENV_PATH}"..." # Export env vars - export $(grep -v '^#' ${ENV_PATH} | xargs) + while IFS= read -r line || [ -n "$line" ]; do + if echo "$line" | grep -F = &>/dev/null + then + varname=$(echo "$line" | cut -d '=' -f 1) + varvalue=$(echo "$line" | cut -d '=' -f 2- | cut -d '#' -f 1) + export "$varname"="$varvalue" + fi + done < <(grep -v '^#' "${ENV_PATH}") else echo "Error: ${ENV_PATH} does not exist." exit 1 diff --git a/server/src/ability/handlers/activity-target.ability-handler.ts b/server/src/ability/handlers/activity-target.ability-handler.ts index 633c7ac153e7..694fee47fe88 100644 --- a/server/src/ability/handlers/activity-target.ability-handler.ts +++ b/server/src/ability/handlers/activity-target.ability-handler.ts @@ -47,9 +47,10 @@ export class UpdateActivityTargetAbilityHandler implements IAbilityHandler { async handle(ability: AppAbility, context: ExecutionContext) { const gqlContext = GqlExecutionContext.create(context); const args = gqlContext.getArgs(); - const ActivityTarget = await this.prismaService.client.activityTarget.findFirst({ - where: args.where, - }); + const ActivityTarget = + await this.prismaService.client.activityTarget.findFirst({ + where: args.where, + }); assert(ActivityTarget, '', NotFoundException); return ability.can( @@ -66,9 +67,10 @@ export class DeleteActivityTargetAbilityHandler implements IAbilityHandler { async handle(ability: AppAbility, context: ExecutionContext) { const gqlContext = GqlExecutionContext.create(context); const args = gqlContext.getArgs(); - const ActivityTarget = await this.prismaService.client.activityTarget.findFirst({ - where: args.where, - }); + const ActivityTarget = + await this.prismaService.client.activityTarget.findFirst({ + where: args.where, + }); assert(ActivityTarget, '', NotFoundException); return ability.can( diff --git a/server/src/core/client-config/client-config.entity.ts b/server/src/core/client-config/client-config.entity.ts index d8405dce2bac..8c5ae60e6cb9 100644 --- a/server/src/core/client-config/client-config.entity.ts +++ b/server/src/core/client-config/client-config.entity.ts @@ -30,7 +30,7 @@ export class ClientConfig { telemetry: Telemetry; @Field(() => Boolean) - demoMode: boolean; + signInPrefilled: boolean; @Field(() => Boolean) debugMode: boolean; diff --git a/server/src/core/client-config/client-config.resolver.ts b/server/src/core/client-config/client-config.resolver.ts index eaa9a0d6f541..1fa00b94f0ec 100644 --- a/server/src/core/client-config/client-config.resolver.ts +++ b/server/src/core/client-config/client-config.resolver.ts @@ -12,17 +12,17 @@ export class ClientConfigResolver { async clientConfig(): Promise { const clientConfig: ClientConfig = { authProviders: { - google: this.environmentService.isAuthGoogleEnabled() ?? false, + google: this.environmentService.isAuthGoogleEnabled(), magicLink: false, password: true, }, telemetry: { - enabled: this.environmentService.isTelemetryEnabled() ?? false, + enabled: this.environmentService.isTelemetryEnabled(), anonymizationEnabled: - this.environmentService.isTelemetryAnonymizationEnabled() ?? false, + this.environmentService.isTelemetryAnonymizationEnabled(), }, - demoMode: this.environmentService.isDemoMode() ?? false, - debugMode: this.environmentService.isDebugMode() ?? false, + signInPrefilled: this.environmentService.isSignInPrefilled(), + debugMode: this.environmentService.isDebugMode(), }; return Promise.resolve(clientConfig); diff --git a/server/src/integrations/environment/environment.service.ts b/server/src/integrations/environment/environment.service.ts index 8f5ab247f457..b5dd52f0d5eb 100644 --- a/server/src/integrations/environment/environment.service.ts +++ b/server/src/integrations/environment/environment.service.ts @@ -13,15 +13,15 @@ export class EnvironmentService { return this.configService.get('DEBUG_MODE') ?? false; } - isDemoMode(): boolean { - return this.configService.get('DEMO_MODE') ?? false; + isSignInPrefilled(): boolean { + return this.configService.get('IS_SIGN_IN_PREFILLED') ?? false; } isTelemetryEnabled(): boolean { return this.configService.get('TELEMETRY_ENABLED') ?? true; } - isTelemetryAnonymizationEnabled(): boolean | undefined { + isTelemetryAnonymizationEnabled(): boolean { return ( this.configService.get('TELEMETRY_ANONYMIZATION_ENABLED') ?? true ); @@ -31,12 +31,16 @@ export class EnvironmentService { return this.configService.get('PG_DATABASE_URL')!; } + getFrontBaseUrl(): string { + return this.configService.get('FRONT_BASE_URL')!; + } + getAccessTokenSecret(): string { return this.configService.get('ACCESS_TOKEN_SECRET')!; } getAccessTokenExpiresIn(): string { - return this.configService.get('ACCESS_TOKEN_EXPIRES_IN')!; + return this.configService.get('ACCESS_TOKEN_EXPIRES_IN') ?? '30m'; } getRefreshTokenSecret(): string { @@ -44,7 +48,7 @@ export class EnvironmentService { } getRefreshTokenExpiresIn(): string { - return this.configService.get('REFRESH_TOKEN_EXPIRES_IN')!; + return this.configService.get('REFRESH_TOKEN_EXPIRES_IN') ?? '90d'; } getLoginTokenSecret(): string { @@ -52,15 +56,18 @@ export class EnvironmentService { } getLoginTokenExpiresIn(): string { - return this.configService.get('LOGIN_TOKEN_EXPIRES_IN')!; + return this.configService.get('LOGIN_TOKEN_EXPIRES_IN') ?? '15m'; } getFrontAuthCallbackUrl(): string { - return this.configService.get('FRONT_AUTH_CALLBACK_URL')!; + return ( + this.configService.get('FRONT_AUTH_CALLBACK_URL') ?? + this.getFrontBaseUrl() + '/auth/callback' + ); } - isAuthGoogleEnabled(): boolean | undefined { - return this.configService.get('AUTH_GOOGLE_ENABLED'); + isAuthGoogleEnabled(): boolean { + return this.configService.get('AUTH_GOOGLE_ENABLED') ?? false; } getAuthGoogleClientId(): string | undefined { @@ -75,8 +82,10 @@ export class EnvironmentService { return this.configService.get('AUTH_GOOGLE_CALLBACK_URL'); } - getStorageType(): StorageType | undefined { - return this.configService.get('STORAGE_TYPE'); + getStorageType(): StorageType { + return ( + this.configService.get('STORAGE_TYPE') ?? StorageType.Local + ); } getStorageS3Region(): AwsRegion | undefined { @@ -87,7 +96,9 @@ export class EnvironmentService { return this.configService.get('STORAGE_S3_NAME'); } - getStorageLocalPath(): string | undefined { - return this.configService.get('STORAGE_LOCAL_PATH')!; + getStorageLocalPath(): string { + return ( + this.configService.get('STORAGE_LOCAL_PATH') ?? '.local-storage' + ); } } diff --git a/server/src/integrations/environment/environment.validation.ts b/server/src/integrations/environment/environment.validation.ts index a2d847d8cb99..270e2387a171 100644 --- a/server/src/integrations/environment/environment.validation.ts +++ b/server/src/integrations/environment/environment.validation.ts @@ -27,7 +27,7 @@ export class EnvironmentVariables { @CastToBoolean() @IsOptional() @IsBoolean() - DEMO_MODE?: boolean; + IS_SIGN_IN_PREFILLED?: boolean; @CastToBoolean() @IsOptional() @@ -43,24 +43,32 @@ export class EnvironmentVariables { @IsUrl({ protocols: ['postgres'], require_tld: false }) PG_DATABASE_URL: string; + // Frontend URL + @IsUrl({ require_tld: false }) + FRONT_BASE_URL: string; + // Json Web Token @IsString() ACCESS_TOKEN_SECRET: string; @IsDuration() + @IsOptional() ACCESS_TOKEN_EXPIRES_IN: string; @IsString() REFRESH_TOKEN_SECRET: string; @IsDuration() + @IsOptional() REFRESH_TOKEN_EXPIRES_IN: string; @IsString() LOGIN_TOKEN_SECRET: string; @IsDuration() + @IsOptional() LOGIN_TOKEN_EXPIRES_IN: string; // Auth @IsUrl({ require_tld: false }) + @IsOptional() FRONT_AUTH_CALLBACK_URL: string; @CastToBoolean() diff --git a/server/src/integrations/integrations.module.ts b/server/src/integrations/integrations.module.ts index 4e0750a166ea..24330fbef89d 100644 --- a/server/src/integrations/integrations.module.ts +++ b/server/src/integrations/integrations.module.ts @@ -19,7 +19,6 @@ const fileStorageModuleFactory = async ( const type = environmentService.getStorageType(); switch (type) { - case undefined: case StorageType.Local: { const storagePath = environmentService.getStorageLocalPath();