Skip to content

Commit 1dfbeb6

Browse files
authored
Remove public env vars that cannot be set at build time (#614)
* Remove public env vars that cannot be set at build time * Also declare the arg and narrow down the scope of variables
1 parent 8a4b587 commit 1dfbeb6

14 files changed

Lines changed: 53 additions & 143 deletions

File tree

Dockerfile

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ ARG NODE_VERSION=22.16.0
33

44
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS base
55

6+
ARG APP_VERSION
7+
ARG GIT_SHA
8+
69
ENV SKIP_ENV_VALIDATION="true"
710
ENV DOCKER_OUTPUT=1
811
ENV NEXT_TELEMETRY_DISABLED=1
912
ENV COREPACK_ENABLE_DOWNLOAD_PROMPT=0
10-
ENV APP_VERSION=${APP_VERSION}
11-
ENV GIT_SHA=${GIT_SHA}
13+
14+
ENV NEXT_PUBLIC_APP_VERSION=${APP_VERSION}
15+
ENV NEXT_PUBLIC_GIT_SHA=${GIT_SHA}
1216

1317
RUN apk update && apk add --no-cache libc6-compat
1418

@@ -24,9 +28,6 @@ RUN pnpm build
2428

2529
FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS release
2630

27-
ARG APP_VERSION
28-
ARG GIT_SHA
29-
3031
ENV NODE_ENV=production
3132
ENV DOCKER_OUTPUT=1
3233

@@ -43,8 +44,6 @@ COPY --from=base /app/prisma/migrations ./prisma/migrations
4344

4445
# set this so it throws error where starting server
4546
ENV SKIP_ENV_VALIDATION="false"
46-
ENV APP_VERSION=${APP_VERSION}
47-
ENV GIT_SHA=${GIT_SHA}
4847

4948
COPY ./start.sh ./start.sh
5049

src/components/Account/DebugInfo.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const DebugInfo: React.FC<React.PropsWithChildren> = ({ children }) => {
3535
}
3636
};
3737

38-
if (env.NEXT_PUBLIC_VERSION) {
38+
if (env.NEXT_PUBLIC_APP_VERSION) {
3939
void fetchLatestVersion();
4040
}
4141
}, []);
@@ -46,8 +46,8 @@ export const DebugInfo: React.FC<React.PropsWithChildren> = ({ children }) => {
4646
if (env.NEXT_PUBLIC_GIT_SHA) {
4747
debugInfo.push(`${t('account.debug_info_details.git')}: ${env.NEXT_PUBLIC_GIT_SHA}`);
4848
}
49-
if (env.NEXT_PUBLIC_VERSION) {
50-
debugInfo.push(`${t('account.debug_info_details.version')} ${env.NEXT_PUBLIC_VERSION}`);
49+
if (env.NEXT_PUBLIC_APP_VERSION) {
50+
debugInfo.push(`${t('account.debug_info_details.version')} ${env.NEXT_PUBLIC_APP_VERSION}`);
5151
}
5252
try {
5353
void navigator.clipboard.writeText(debugInfo.join('\n'));
@@ -78,10 +78,12 @@ export const DebugInfo: React.FC<React.PropsWithChildren> = ({ children }) => {
7878
/>
7979
<DebugInfoRow
8080
label={t('account.debug_info_details.version')}
81-
value={env.NEXT_PUBLIC_VERSION}
81+
value={env.NEXT_PUBLIC_APP_VERSION}
8282
className="mt-4"
8383
/>
84-
{newVersion && env.NEXT_PUBLIC_VERSION && newVersion !== env.NEXT_PUBLIC_VERSION ? (
84+
{newVersion &&
85+
env.NEXT_PUBLIC_APP_VERSION &&
86+
newVersion !== env.NEXT_PUBLIC_APP_VERSION ? (
8587
<p className="mt-4 text-sm text-yellow-600">
8688
{t('account.debug_info_details.new_version_available')}: {newVersion}
8789
</p>

src/components/Account/UpdateDetails.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { zodResolver } from '@hookform/resolvers/zod';
22
import { Camera, Pencil, X } from 'lucide-react';
33
import Cropper, { type Area } from 'react-easy-crop';
4-
import React, { useCallback, useMemo, useRef, useState } from 'react';
4+
import React, { useCallback, useMemo, useState } from 'react';
55
import { useForm } from 'react-hook-form';
66
import { type TFunction, useTranslation } from 'next-i18next';
77
import { toast } from 'sonner';
88
import { z } from 'zod';
99

10-
import { env } from '~/env';
1110
import { prepareImageForUpload, uploadImage, validateUploadSize } from '~/utils/imageUpload';
1211

1312
import { AppDrawer } from '../ui/drawer';
@@ -17,6 +16,7 @@ import { Input } from '../ui/input';
1716
import { Label } from '../ui/label';
1817
import { Slider } from '../ui/slider';
1918
import { Button } from '../ui/button';
19+
import { useAppStore } from '~/store/appStore';
2020

2121
const createImage = async (url: string) => {
2222
const image = new Image();
@@ -91,6 +91,7 @@ export const UpdateName: React.FC<{
9191
const [crop, setCrop] = useState({ x: 0, y: 0 });
9292
const [zoom, setZoom] = useState(1);
9393
const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);
94+
const maxUploadFileSizeMB = useAppStore((s) => s.maxUploadFileSizeMB);
9495

9596
const { t } = useTranslation();
9697

@@ -151,14 +152,14 @@ export const UpdateName: React.FC<{
151152
let croppedFile = new File([croppedBlob], 'avatar.jpg', { type: 'image/jpeg' });
152153

153154
try {
154-
croppedFile = await prepareImageForUpload(croppedFile);
155+
croppedFile = await prepareImageForUpload(croppedFile, maxUploadFileSizeMB);
155156
} catch (error) {
156157
console.error('Compression failed:', error);
157158
toast.error(t('errors.image_compression_failed'));
158159
}
159160

160-
if (!validateUploadSize(croppedFile)) {
161-
toast.error(t('errors.less_than', { size: env.NEXT_PUBLIC_UPLOAD_MAX_FILE_SIZE_MB }));
161+
if (!validateUploadSize(croppedFile, maxUploadFileSizeMB)) {
162+
toast.error(t('errors.less_than', { size: maxUploadFileSizeMB }));
162163
return;
163164
}
164165

@@ -183,7 +184,7 @@ export const UpdateName: React.FC<{
183184
setZoom(1);
184185
setCroppedAreaPixels(null);
185186
})();
186-
}, [croppedAreaPixels, detailForm, imageSrc, onNameSubmit, t]);
187+
}, [croppedAreaPixels, detailForm, imageSrc, maxUploadFileSizeMB, onNameSubmit, t]);
187188

188189
const handleFileChange = useCallback(
189190
(event: React.ChangeEvent<HTMLInputElement>) => {

src/components/AddExpense/CurrencyPicker.tsx

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,20 @@
11
import { memo, useCallback, useMemo } from 'react';
22

3-
import {
4-
CURRENCIES,
5-
type CurrencyCode,
6-
FRANKFURTER_CURRENCIES,
7-
parseCurrencyCode,
8-
} from '~/lib/currency';
3+
import { CURRENCIES, type CurrencyCode, parseCurrencyCode } from '~/lib/currency';
94

105
import { useTranslationWithUtils } from '~/hooks/useTranslationWithUtils';
116
import { GeneralPicker } from '../GeneralPicker';
127
import { Button } from '../ui/button';
138
import { useCurrencyPreferenceStore } from '~/store/currencyPreferenceStore';
149

15-
const FRANKFURTER_FILTERED_CURRENCIES = Object.fromEntries(
16-
Object.entries(CURRENCIES).filter(([code]) => FRANKFURTER_CURRENCIES.includes(code)),
17-
);
18-
1910
function CurrencyPickerInner({
2011
className,
2112
currentCurrency = 'USD',
2213
onCurrencyPick,
23-
showOnlyFrankfurter = false,
2414
}: {
2515
className?: string;
2616
currentCurrency: CurrencyCode;
2717
onCurrencyPick: (currency: CurrencyCode) => void;
28-
showOnlyFrankfurter?: boolean;
2918
}) {
3019
const { t, getCurrencyName } = useTranslationWithUtils(['currencies']);
3120
const { recentCurrencies, addToRecentCurrencies } = useCurrencyPreferenceStore();
@@ -70,15 +59,13 @@ function CurrencyPickerInner({
7059
[recentCurrencies],
7160
);
7261
const items = useMemo(() => {
73-
const baseItems = showOnlyFrankfurter
74-
? Object.values(FRANKFURTER_FILTERED_CURRENCIES)
75-
: Object.values(CURRENCIES);
62+
const baseItems = Object.values(CURRENCIES);
7663
const uniqueItems = [
7764
...recentCurrencyObjects,
7865
...baseItems.filter((c) => !recentCurrencies.includes(c.code as CurrencyCode)),
7966
];
8067
return uniqueItems;
81-
}, [showOnlyFrankfurter, recentCurrencyObjects, recentCurrencies]);
68+
}, [recentCurrencyObjects, recentCurrencies]);
8269

8370
return (
8471
<GeneralPicker

src/components/AddExpense/UploadFile.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ import React, { useState } from 'react';
33
import { toast } from 'sonner';
44
import { useTranslation } from 'next-i18next';
55

6-
import { env } from '~/env';
76
import { useAddExpenseStore } from '~/store/addStore';
87
import { prepareImageForUpload, uploadImage, validateUploadSize } from '~/utils/imageUpload';
98

109
import { Input } from '../ui/input';
1110
import { Label } from '../ui/label';
11+
import { useAppStore } from '~/store/appStore';
1212

1313
export const UploadFile: React.FC = () => {
1414
const { t } = useTranslation();
1515
const [file, setFile] = useState<File | null>(null);
16+
const maxUploadFileSizeMB = useAppStore((s) => s.maxUploadFileSizeMB);
1617
const fileKey = useAddExpenseStore((s) => s.fileKey);
1718
const { setFileUploading, setFileKey } = useAddExpenseStore((s) => s.actions);
1819

@@ -28,14 +29,14 @@ export const UploadFile: React.FC = () => {
2829

2930
try {
3031
try {
31-
file = await prepareImageForUpload(file);
32+
file = await prepareImageForUpload(file, maxUploadFileSizeMB);
3233
} catch (error) {
3334
console.error('Compression failed:', error);
3435
toast.error(t('errors.image_compression_failed'));
3536
}
3637

37-
if (!validateUploadSize(file)) {
38-
toast.error(t('errors.less_than', { size: env.NEXT_PUBLIC_UPLOAD_MAX_FILE_SIZE_MB }));
38+
if (!validateUploadSize(file, maxUploadFileSizeMB)) {
39+
toast.error(t('errors.less_than', { size: maxUploadFileSizeMB }));
3940
return;
4041
}
4142

@@ -54,7 +55,7 @@ export const UploadFile: React.FC = () => {
5455
setFileUploading(false);
5556
}
5657
},
57-
[setFileUploading, setFileKey, t],
58+
[setFileUploading, setFileKey, maxUploadFileSizeMB, t],
5859
);
5960

6061
return (

src/components/Friend/CurrencyConversion.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { api } from '~/utils/api';
44
import { MAX_RATE_PRECISION, currencyConversion, getRatePrecision } from '~/utils/numbers';
55

66
import { toast } from 'sonner';
7-
import { env } from '~/env';
87
import { type CurrencyCode, isCurrencyCode } from '~/lib/currency';
98
import { useAddExpenseStore } from '~/store/addStore';
109
import { CurrencyPicker } from '../AddExpense/CurrencyPicker';
@@ -216,8 +215,6 @@ export const CurrencyConversion: React.FC<{
216215
className="mx-auto"
217216
currentCurrency={targetCurrency}
218217
onCurrencyPick={onChangeTargetCurrency}
219-
// Client env vars with pages router only work after next build :/
220-
showOnlyFrankfurter={env.NEXT_PUBLIC_FRANKFURTER_USED}
221218
/>
222219
)}
223220
</div>

src/components/group/AddMembers.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { api } from '~/utils/api';
1212

1313
import { EntityAvatar } from '../ui/avatar';
1414
import { Input } from '../ui/input';
15-
import { env } from '~/env';
1615

1716
const AddMembers: React.FC<{
1817
enableSendingInvites: boolean;
@@ -34,13 +33,10 @@ const AddMembers: React.FC<{
3433
return null;
3534
}
3635

37-
const groupUserMap = group.groupUsers.reduce(
38-
(acc, gu) => {
39-
acc[gu.userId] = true;
40-
return acc;
41-
},
42-
{} as Record<string, boolean>,
43-
);
36+
const groupUserMap = group.groupUsers.reduce<Record<string, boolean>>((acc, gu) => {
37+
acc[gu.userId] = true;
38+
return acc;
39+
}, {});
4440

4541
const filteredUsers = friendsQuery.data?.filter(
4642
(friend) =>
@@ -126,7 +122,7 @@ const AddMembers: React.FC<{
126122
onChange={(e) => setInputValue(e.target.value)}
127123
/>
128124
<div>
129-
{enableSendingInvites && !env.NEXT_PUBLIC_IS_CLOUD_DEPLOYMENT ? (
125+
{enableSendingInvites ? (
130126
<div className="mt-1 text-orange-600">
131127
{t('group_details.no_members.add_members_details.warning')}
132128
</div>

src/env.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,10 @@ export const env = createEnv({
7777
/**
7878
* Specify your client-side environment variables schema here. This way you can ensure the app
7979
* isn't built with invalid env vars. To expose them to the client, prefix them with
80-
* `NEXT_PUBLIC_`.
80+
* `NEXT_PUBLIC_`. Note that these are only evaluated at build time!
8181
*/
8282
client: {
83-
NEXT_PUBLIC_FRANKFURTER_USED: z.boolean().default(false),
84-
NEXT_PUBLIC_IS_CLOUD_DEPLOYMENT: z.boolean().default(false),
85-
NEXT_PUBLIC_UPLOAD_MAX_FILE_SIZE_MB: z.coerce.number().int().positive().default(10),
86-
NEXT_PUBLIC_VERSION: z.string().optional(),
83+
NEXT_PUBLIC_APP_VERSION: z.string().optional(),
8784
NEXT_PUBLIC_GIT_SHA: z.string().optional(),
8885
},
8986

@@ -143,16 +140,11 @@ export const env = createEnv({
143140
OIDC_CLIENT_SECRET: process.env.OIDC_CLIENT_SECRET,
144141
OIDC_WELL_KNOWN_URL: process.env.OIDC_WELL_KNOWN_URL,
145142
OIDC_ALLOW_DANGEROUS_EMAIL_LINKING: Boolean(process.env.OIDC_ALLOW_DANGEROUS_EMAIL_LINKING),
146-
NEXT_PUBLIC_FRANKFURTER_USED: process.env.CURRENCY_RATE_PROVIDER === 'frankfurter',
147-
NEXT_PUBLIC_IS_CLOUD_DEPLOYMENT: process.env.NEXTAUTH_URL?.includes('splitpro.app') ?? false,
148-
NEXT_PUBLIC_UPLOAD_MAX_FILE_SIZE_MB: process.env.UPLOAD_MAX_FILE_SIZE_MB
149-
? Number(process.env.UPLOAD_MAX_FILE_SIZE_MB)
150-
: 10,
151143
UPLOAD_MAX_FILE_SIZE_MB: process.env.UPLOAD_MAX_FILE_SIZE_MB
152144
? Number(process.env.UPLOAD_MAX_FILE_SIZE_MB)
153145
: 10,
154-
NEXT_PUBLIC_VERSION: process.env.APP_VERSION,
155-
NEXT_PUBLIC_GIT_SHA: process.env.GIT_SHA,
146+
NEXT_PUBLIC_APP_VERSION: process.env.NEXT_PUBLIC_APP_VERSION,
147+
NEXT_PUBLIC_GIT_SHA: process.env.NEXT_PUBLIC_GIT_SHA,
156148
},
157149
/**
158150
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially

src/lib/currency.ts

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -840,38 +840,3 @@ export const isCurrencyCode = (value: string): value is CurrencyCode => value in
840840

841841
export const parseCurrencyCode = (code: string): CurrencyCode =>
842842
isCurrencyCode(code) ? code : 'USD';
843-
844-
// Check with https://api.frankfurter.dev/v1/currencies
845-
export const FRANKFURTER_CURRENCIES = [
846-
'AUD',
847-
'BGN',
848-
'BRL',
849-
'CAD',
850-
'CHF',
851-
'CNY',
852-
'CZK',
853-
'DKK',
854-
'EUR',
855-
'GBP',
856-
'HKD',
857-
'HUF',
858-
'IDR',
859-
'ILS',
860-
'INR',
861-
'ISK',
862-
'JPY',
863-
'KRW',
864-
'MXN',
865-
'MYR',
866-
'NOK',
867-
'NZD',
868-
'PHP',
869-
'PLN',
870-
'RON',
871-
'SEK',
872-
'SGD',
873-
'THB',
874-
'TRY',
875-
'USD',
876-
'ZAR',
877-
];

src/pages/_app.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ import '~/styles/globals.css';
2424
const poppins = Poppins({ weight: ['200', '300', '400', '500', '600', '700'], subsets: ['latin'] });
2525
const toastOptions = { duration: 1500 };
2626

27-
const MyApp: AppType<{ session: Session | null; baseUrl: string }> = ({
27+
const MyApp: AppType<{ session: Session | null; baseUrl: string; maxUploadFileSizeMB: number }> = ({
2828
Component,
29-
pageProps: { session, baseUrl, ...pageProps },
29+
pageProps: { session, baseUrl, maxUploadFileSizeMB, ...pageProps },
3030
}) => {
3131
const { t, ready } = useTranslation();
32+
const { setMaxUploadFileSizeMB } = useAppStore((s) => s.actions);
33+
setMaxUploadFileSizeMB(maxUploadFileSizeMB);
3234

3335
if (!ready) {
3436
return (
@@ -181,6 +183,7 @@ const Auth: React.FC<{ Page: NextPageWithUser; pageProps: any }> = ({ Page, page
181183
export const getServerSideProps = () => ({
182184
props: {
183185
baseUrl: env.NEXTAUTH_URL,
186+
maxUploadFileSizeMB: env.UPLOAD_MAX_FILE_SIZE_MB,
184187
},
185188
});
186189

0 commit comments

Comments
 (0)