Skip to content

Commit

Permalink
refactor(env): use Remix default and zod runtime validation (#347)
Browse files Browse the repository at this point in the history
* Thomas/remove hardcoded env vars (#342)

* refactor(env): getClientEnv for SSR and CSR

* chore(dotenv): remove explicit dep for built in remix one

* refactor(env): use zod to validate env vars

* docs(app-builder): update README.md with env vars

* docs(README): fix typo

* refactor(chatlio): optionnal chatlio & segment from builder (#343)

* refactor(chatlio): optionnal chatlio from builder

* refactor(segment): optionnal segment from builder (#344)

* chore(envs): let segment key
  • Loading branch information
balzdur authored Jan 23, 2024
1 parent 5825ffc commit 91b3ba4
Show file tree
Hide file tree
Showing 24 changed files with 939 additions and 890 deletions.
36 changes: 36 additions & 0 deletions packages/app-builder/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# This file is a template, and might need editing before it works on your project.
# Duplicate this file as .env, and replace the values with your own.
# The .env file should never be committed to git.
# .env is only used in development, and ignored in production.
# More information about environment variables can be found at: https://remix.run/docs/en/main/guides/envvars

ENV=development
NODE_ENV=development

SESSION_SECRET=SESSION_SECRET
SESSION_MAX_AGE=43200

MARBLE_API_DOMAIN=http://localhost:8080
MARBLE_APP_DOMAIN=http://localhost:3000

FIREBASE_AUTH_EMULATOR=true
FIREBASE_AUTH_EMULATOR_HOST=http://localhost:9099

# if you make use of the auth emulator, you can enter dummy values
FIREBASE_API_KEY=dummy
FIREBASE_AUTH_DOMAIN=dummy
FIREBASE_PROJECT_ID=dummy
FIREBASE_STORAGE_BUCKET=dummy
FIREBASE_MESSAGING_SENDER_ID=dummy
FIREBASE_APP_ID=dummy


# we use it for analytics
SEGMENT_WRITE_KEY=hC8qrY2OLhUpl1Xycw523tbuClxlQR6u

# we use it for error tracking
# SENTRY_DSN=
# SENTRY_ENVIRONMENT=

# we use it for our support chat
# CHATLIO_WIDGET_ID=
23 changes: 0 additions & 23 deletions packages/app-builder/.env.local

This file was deleted.

26 changes: 16 additions & 10 deletions packages/app-builder/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,29 @@ This is the app builder package.

## Getting started

The application is built using the Remax framework. You can find the documentation [here](https://remaxjs.org/).
The application is built using the Remix framework. You can find the documentation [here](https://remix.run/).

### Development

#### VSCode users
#### Run the project

We recommand to use the launch task: "Launch app-builder" to start the app in dev mode. The debugger should be automatically attached (work for both SSR and client parts).
1. Create you own `.env` file based on `.env.example`.

#### From the CLI
> You can fill it with your own values but it should work locally with the default values.
```bash
# Start the builder app in dev mode
pnpm --filter app-builder run dev
2. launch the app in dev mode

# Start the builder app in dev mode with debug
pnpm --filter app-builder run dev --debug
```
2.1. **VSCode users:** we recommand to use the launch task: "Launch app-builder" to start the app in dev mode. The debugger should be automatically attached (work for both SSR and client parts).

2.2. **CLI users:** you can use the following command to start the app in dev mode:

```bash
# Start the builder app in dev mode
pnpm --filter app-builder run dev

# Start the builder app in dev mode with debug
pnpm --filter app-builder run dev --debug
```

#### Add a new route

Expand Down
18 changes: 8 additions & 10 deletions packages/app-builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"scripts": {
"lint": "eslint .",
"type-check": "npx tsc --noEmit",
"dev": "dotenv -e .env.local -- remix dev",
"dev": "remix dev",
"build": "remix build",
"build-with-sourcemaps": "remix build --sourcemap && sentry-upload-sourcemaps --org checkmarble --project marble-frontend",
"start": "remix-serve build/index.js",
Expand All @@ -19,15 +19,14 @@
"license": "ISC",
"devDependencies": {
"@marble/eslint-config": "workspace:*",
"@remix-run/dev": "^2.4.1",
"@remix-run/dev": "^2.5.1",
"@segment/analytics-next": "^1.62.0",
"@sentry/cli": "^2.24.1",
"@types/autosuggest-highlight": "^3.2.3",
"@types/qs": "^6.9.11",
"@types/react": "18.2.47",
"@types/react": "18.2.48",
"@types/react-dom": "18.2.18",
"@types/swagger-ui-react": "^4.18.3",
"dotenv-cli": "^7.3.0",
"eslint-plugin-tailwindcss": "^3.13.1",
"jsdom": "23.2.0",
"ora": "^8.0.1",
Expand Down Expand Up @@ -56,9 +55,9 @@
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
"@radix-ui/react-tooltip": "^1.0.7",
"@remix-run/node": "^2.4.1",
"@remix-run/react": "^2.4.1",
"@remix-run/serve": "^2.4.1",
"@remix-run/node": "^2.5.1",
"@remix-run/react": "^2.5.1",
"@remix-run/serve": "^2.5.1",
"@segment/snippet": "^5.2.0",
"@sentry/remix": "^7.92.0",
"@tanstack/react-table": "^8.11.3",
Expand All @@ -69,13 +68,12 @@
"cronstrue": "^2.47.0",
"crypto-js": "^4.2.0",
"date-fns": "^3.1.0",
"dotenv": "^16.3.1",
"firebase": "^10.7.1",
"i18next": "^23.7.16",
"i18next-browser-languagedetector": "^7.2.0",
"i18next-fs-backend": "^2.3.1",
"i18next-http-backend": "^2.4.2",
"isbot": "^4.3.0",
"isbot": "^4.4.0",
"marble-api": "workspace:*",
"match-sorter": "^6.3.1",
"nanoid": "^5.0.4",
Expand All @@ -89,7 +87,7 @@
"react-hot-toast": "^2.4.1",
"react-i18next": "^14.0.0",
"remeda": "^1.34.0",
"remix": "^2.4.1",
"remix": "^2.5.1",
"remix-flat-routes": "^0.6.4",
"remix-i18next": "^5.5.0",
"remix-utils": "^7.5.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/app-builder/src/components/MarbleToaster.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
toastMessageScema,
type ToastSession,
} from '@app-builder/models/toast-session';
import { getClientEnv } from '@app-builder/utils/environment.client';
import { getClientEnv } from '@app-builder/utils/environment';
import { useEffect } from 'react';
import { toast, ToastBar, Toaster } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
Expand Down
2 changes: 1 addition & 1 deletion packages/app-builder/src/entry.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { hydrateRoot } from 'react-dom/client';
import { I18nextProvider } from 'react-i18next';

import { clientServices } from './services/init.client';
import { getClientEnv } from './utils/environment.client';
import { getClientEnv } from './utils/environment';

Sentry.init({
dsn: getClientEnv('SENTRY_DSN'),
Expand Down
2 changes: 1 addition & 1 deletion packages/app-builder/src/entry.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { I18nextProvider } from 'react-i18next';
import { PassThrough } from 'stream';

import { serverServices } from './services/init.server';
import { getServerEnv } from './utils/environment.server';
import { getServerEnv } from './utils/environment';

const ABORT_DELAY = 5000;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type FirebaseClientWrapper } from '@app-builder/infra/firebase';
import { getClientEnv } from '@app-builder/utils/environment.client';
import { getClientEnv } from '@app-builder/utils/environment';
import { getRoute } from '@app-builder/utils/routes';

export interface AuthenticationClientRepository {
Expand Down
10 changes: 7 additions & 3 deletions packages/app-builder/src/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { useSegmentPageTracking } from './services/segment';
import { getSegmentScript } from './services/segment/segment.server';
import { SegmentScript } from './services/segment/SegmentScript';
import tailwindStyles from './tailwind.css';
import { getClientEnvVars } from './utils/environment.server';
import { getClientEnvVars, getServerEnv } from './utils/environment';
import { getRoute } from './utils/routes';

export const links: LinksFunction = () => [
Expand Down Expand Up @@ -79,13 +79,17 @@ export async function loader({ request }: LoaderFunctionArgs) {
);
if (csrfCookieHeader) headers.append('set-cookie', csrfCookieHeader);

const segmentApiKey = getServerEnv('SEGMENT_WRITE_KEY');

return json(
{
ENV,
locale,
csrf: csrfToken,
toastMessage,
segmentScript: getSegmentScript(),
segmentScript: segmentApiKey
? getSegmentScript(segmentApiKey)
: undefined,
},
{
headers,
Expand Down Expand Up @@ -163,7 +167,7 @@ function App() {
<head>
<Meta />
<Links />
<SegmentScript script={segmentScript} />
{segmentScript ? <SegmentScript script={segmentScript} /> : null}
<ExternalScripts />
</head>
<body className="selection:text-grey-00 h-screen w-full overflow-hidden antialiased selection:bg-purple-100">
Expand Down
18 changes: 14 additions & 4 deletions packages/app-builder/src/routes/_builder+/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
useSegmentIdentification,
} from '@app-builder/services/segment';
import { getFullName } from '@app-builder/services/user';
import { getClientEnv } from '@app-builder/utils/environment';
import { getRoute } from '@app-builder/utils/routes';
import * as Popover from '@radix-ui/react-popover';
import { json, type LoaderFunctionArgs } from '@remix-run/node';
Expand Down Expand Up @@ -45,7 +46,9 @@ export async function loader({ request }: LoaderFunctionArgs) {

export const handle = {
i18n: ['common', ...navigationI18n] satisfies Namespace,
scripts: () => [chatlioScript],
scripts: () => [
...(getClientEnv('CHATLIO_WIDGET_ID') ? [chatlioScript] : []),
],
};

export default function Builder() {
Expand All @@ -59,6 +62,7 @@ export default function Builder() {
useRefreshToken();

const [expanded, setExpanded] = useState(true);
const chatlioWidgetId = getClientEnv('CHATLIO_WIDGET_ID');

return (
<PermissionsProvider userPermissions={user.permissions}>
Expand Down Expand Up @@ -217,9 +221,15 @@ export default function Builder() {
/>
</li>
) : null}
<li>
<ChatlioWidget user={user} organization={organization} />
</li>
{chatlioWidgetId ? (
<li>
<ChatlioWidget
user={user}
organization={organization}
widgetid={chatlioWidgetId}
/>
</li>
) : null}
<li>
<SidebarButton
onClick={() => {
Expand Down
2 changes: 1 addition & 1 deletion packages/app-builder/src/services/auth/auth.client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type AuthenticationClientRepository } from '@app-builder/repositories/AuthenticationRepository';
import { getClientEnv } from '@app-builder/utils/environment.client';
import { getClientEnv } from '@app-builder/utils/environment';
import { FirebaseError } from 'firebase/app';
import { AuthErrorCodes } from 'firebase/auth';
import { marbleApi } from 'marble-api';
Expand Down
2 changes: 1 addition & 1 deletion packages/app-builder/src/services/auth/auth.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { type MarbleAPIRepository } from '@app-builder/repositories/MarbleAPIRep
import { type OrganizationRepository } from '@app-builder/repositories/OrganizationRepository';
import { type ScenarioRepository } from '@app-builder/repositories/ScenarioRepository';
import { type UserRepository } from '@app-builder/repositories/UserRepository';
import { getServerEnv } from '@app-builder/utils/environment.server';
import { getServerEnv } from '@app-builder/utils/environment';
import { parseForm } from '@app-builder/utils/input-validation';
import { type RoutePath } from '@app-builder/utils/routes/types';
import { json, redirect } from '@remix-run/node';
Expand Down
7 changes: 3 additions & 4 deletions packages/app-builder/src/services/chatlio/ChatlioWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import { getFullName } from '../user';
export function ChatlioWidget({
user,
organization,
widgetid,
}: {
user: CurrentUser;
organization: Organization;
widgetid: string;
}) {
return (
<>
Expand All @@ -34,10 +36,7 @@ export function ChatlioWidget({
data-chatlio-widget-button
/>
<div className="absolute">
<chatlio-widget
widgetid="4aef4109-4ac2-4499-590d-511078df07fd"
data-start-hidden
></chatlio-widget>
<chatlio-widget widgetid={widgetid} data-start-hidden></chatlio-widget>
</div>
</>
);
Expand Down
4 changes: 2 additions & 2 deletions packages/app-builder/src/services/init.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
type ClientRepositories,
makeClientRepositories,
} from '@app-builder/repositories/init.client';
import { getClientEnv } from '@app-builder/utils/environment.client';
import { getClientEnv } from '@app-builder/utils/environment';

import { makeAuthenticationClientService } from './auth/auth.client';
import { makeI18nextClientService } from './i18n/i18next.client';
Expand All @@ -20,7 +20,7 @@ function makeClientServices(repositories: ClientRepositories) {
function initClientServices() {
const firebaseClient = initializeFirebaseClient({
firebaseOptions: getClientEnv('FIREBASE_OPTIONS'),
authEmulatorHost: getClientEnv('AUTH_EMULATOR_HOST', ''),
authEmulatorHost: getClientEnv('AUTH_EMULATOR_HOST'),
});
const clientRepositories = makeClientRepositories(firebaseClient);
return makeClientServices(clientRepositories);
Expand Down
3 changes: 2 additions & 1 deletion packages/app-builder/src/services/init.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
makeServerRepositories,
type ServerRepositories,
} from '@app-builder/repositories/init.server';
import { getServerEnv } from '@app-builder/utils/environment.server';
import { checkServerEnv, getServerEnv } from '@app-builder/utils/environment';
import { CSRF } from 'remix-utils/csrf/server';

import { makeAuthenticationServerService } from './auth/auth.server';
Expand Down Expand Up @@ -46,6 +46,7 @@ function makeServerServices(repositories: ServerRepositories) {
}

function initServerServices() {
checkServerEnv();
const getMarbleAPIClient = initializeGetMarbleAPIClient({
baseUrl: getServerEnv('MARBLE_API_DOMAIN'),
});
Expand Down
8 changes: 4 additions & 4 deletions packages/app-builder/src/services/segment/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ export function useSegmentIdentification(user: CurrentUser) {
const isHydrated = useHydrated();
useEffect(() => {
if (isHydrated) {
void window.analytics.identify(user.actorIdentity.userId);
void window.analytics?.identify(user.actorIdentity.userId);
if (user.actorIdentity.userId) {
void window.analytics.track('Logged In');
void window.analytics?.track('Logged In');
}
}
}, [user.actorIdentity.userId, user.organizationId, isHydrated]);
Expand All @@ -27,7 +27,7 @@ export function useSegmentPageTracking() {
const tracking = getPageViewNameAndProps(thisPage);
if (!tracking) return;
const { name, properties } = tracking;
void window.analytics.page(name, properties);
void window.analytics?.page(name, properties);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location, thisPage.id, isHydrated]);
Expand All @@ -36,5 +36,5 @@ export function useSegmentPageTracking() {
}

export const segment = {
reset: () => window.analytics.reset(),
reset: () => window.analytics?.reset(),
};
5 changes: 2 additions & 3 deletions packages/app-builder/src/services/segment/segment.server.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { getServerEnv } from '@app-builder/utils/environment.server';
import { min } from '@segment/snippet';

export function getSegmentScript() {
export function getSegmentScript(apiKey: string) {
return min({
apiKey: getServerEnv('SEGMENT_WRITE_KEY'),
apiKey,

// TODO(GDPR): uncomment to lazy load segment after GDPR consent
// Ressource to implement in house cookie consent banner: https://github.com/remix-run/examples/tree/main/gdpr-cookie-consent
Expand Down
Loading

0 comments on commit 91b3ba4

Please sign in to comment.