aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJan T <jans.tuomi@gmail.com>2024-02-14 17:18:30 +0200
committerGitHub <noreply@github.com>2024-02-14 10:18:30 -0500
commit2af066038351ab0634c359a688fe712a539992b4 (patch)
tree3ca4bb30f4813ff7e7d87354c5fdd83dcb19cb59 /src
parent50525ad8810de356856eb098a1e0882ce3607b9f (diff)
Optimize docker image size (#91)
* Move prisma to runtime dependencies * Optimize Dockerfile and build script * Fix: remove mention of generated next-env.d.ts in Dockerfile * Add missing reset.d.ts file to Dockerfile * Remove compression steps from Dockerfile and entrypoint script * Add an env file with mocked env vars added for Docker production builds * Use server actions to get runtime env vars * Refactor types and names * Rollback serverActions, use parsed Zod object for runtime env * Reintroduce featureFlags object to avoid passing secret envs to the frontend * Improve string to boolean coercion Co-authored-by: Sebastien Castiel <sebastien@castiel.me> * Run prettier autoformat * Fix type issue, rename function to match behaviour better --------- Co-authored-by: Lauri Vuorela <lauri.vuorela@gmail.com> Co-authored-by: Sebastien Castiel <sebastien@castiel.me>
Diffstat (limited to 'src')
-rw-r--r--src/app/groups/[groupId]/expenses/[expenseId]/edit/page.tsx2
-rw-r--r--src/app/groups/[groupId]/expenses/create/page.tsx2
-rw-r--r--src/components/expense-form.tsx7
-rw-r--r--src/lib/env.ts20
-rw-r--r--src/lib/featureFlags.ts15
5 files changed, 41 insertions, 5 deletions
diff --git a/src/app/groups/[groupId]/expenses/[expenseId]/edit/page.tsx b/src/app/groups/[groupId]/expenses/[expenseId]/edit/page.tsx
index 65b2b50..2514a77 100644
--- a/src/app/groups/[groupId]/expenses/[expenseId]/edit/page.tsx
+++ b/src/app/groups/[groupId]/expenses/[expenseId]/edit/page.tsx
@@ -6,6 +6,7 @@ import {
getExpense,
updateExpense,
} from '@/lib/api'
+import { getRuntimeFeatureFlags } from '@/lib/featureFlags'
import { expenseFormSchema } from '@/lib/schemas'
import { Metadata } from 'next'
import { notFound, redirect } from 'next/navigation'
@@ -47,6 +48,7 @@ export default async function EditExpensePage({
categories={categories}
onSubmit={updateExpenseAction}
onDelete={deleteExpenseAction}
+ runtimeFeatureFlags={await getRuntimeFeatureFlags()}
/>
</Suspense>
)
diff --git a/src/app/groups/[groupId]/expenses/create/page.tsx b/src/app/groups/[groupId]/expenses/create/page.tsx
index 2d580d3..6398fd1 100644
--- a/src/app/groups/[groupId]/expenses/create/page.tsx
+++ b/src/app/groups/[groupId]/expenses/create/page.tsx
@@ -1,6 +1,7 @@
import { cached } from '@/app/cached-functions'
import { ExpenseForm } from '@/components/expense-form'
import { createExpense, getCategories } from '@/lib/api'
+import { getRuntimeFeatureFlags } from '@/lib/featureFlags'
import { expenseFormSchema } from '@/lib/schemas'
import { Metadata } from 'next'
import { notFound, redirect } from 'next/navigation'
@@ -32,6 +33,7 @@ export default async function ExpensePage({
group={group}
categories={categories}
onSubmit={createExpenseAction}
+ runtimeFeatureFlags={await getRuntimeFeatureFlags()}
/>
</Suspense>
)
diff --git a/src/components/expense-form.tsx b/src/components/expense-form.tsx
index fd3f6e2..a7e549c 100644
--- a/src/components/expense-form.tsx
+++ b/src/components/expense-form.tsx
@@ -35,6 +35,7 @@ import {
SelectValue,
} from '@/components/ui/select'
import { getCategories, getExpense, getGroup, randomId } from '@/lib/api'
+import { RuntimeFeatureFlags } from '@/lib/featureFlags'
import { ExpenseFormValues, expenseFormSchema } from '@/lib/schemas'
import { cn } from '@/lib/utils'
import { zodResolver } from '@hookform/resolvers/zod'
@@ -52,6 +53,7 @@ export type Props = {
categories: NonNullable<Awaited<ReturnType<typeof getCategories>>>
onSubmit: (values: ExpenseFormValues) => Promise<void>
onDelete?: () => Promise<void>
+ runtimeFeatureFlags: RuntimeFeatureFlags
}
export function ExpenseForm({
@@ -60,6 +62,7 @@ export function ExpenseForm({
categories,
onSubmit,
onDelete,
+ runtimeFeatureFlags,
}: Props) {
const isCreate = expense === undefined
const searchParams = useSearchParams()
@@ -161,7 +164,7 @@ export function ExpenseForm({
{...field}
onBlur={async () => {
field.onBlur() // avoid skipping other blur event listeners since we overwrite `field`
- if (process.env.NEXT_PUBLIC_ENABLE_CATEGORY_EXTRACT) {
+ if (runtimeFeatureFlags.enableCategoryExtract) {
setCategoryLoading(true)
const { categoryId } = await extractCategoryFromTitle(
field.value,
@@ -541,7 +544,7 @@ export function ExpenseForm({
</CardContent>
</Card>
- {process.env.NEXT_PUBLIC_ENABLE_EXPENSE_DOCUMENTS && (
+ {runtimeFeatureFlags.enableExpenseDocuments && (
<Card className="mt-4">
<CardHeader>
<CardTitle className="flex justify-between">
diff --git a/src/lib/env.ts b/src/lib/env.ts
index 927e756..920df14 100644
--- a/src/lib/env.ts
+++ b/src/lib/env.ts
@@ -1,5 +1,10 @@
import { ZodIssueCode, z } from 'zod'
+const interpretEnvVarAsBool = (val: unknown): boolean => {
+ if (typeof val !== 'string') return false
+ return ['true', 'yes', '1', 'on'].includes(val.toLowerCase())
+}
+
const envSchema = z
.object({
POSTGRES_URL_NON_POOLING: z.string().url(),
@@ -12,14 +17,23 @@ const envSchema = z
? `https://${process.env.VERCEL_URL}`
: 'http://localhost:3000',
),
- NEXT_PUBLIC_ENABLE_EXPENSE_DOCUMENTS: z.coerce.boolean().default(false),
+ NEXT_PUBLIC_ENABLE_EXPENSE_DOCUMENTS: z.preprocess(
+ interpretEnvVarAsBool,
+ z.boolean().default(false),
+ ),
S3_UPLOAD_KEY: z.string().optional(),
S3_UPLOAD_SECRET: z.string().optional(),
S3_UPLOAD_BUCKET: z.string().optional(),
S3_UPLOAD_REGION: z.string().optional(),
S3_UPLOAD_ENDPOINT: z.string().optional(),
- NEXT_PUBLIC_ENABLE_RECEIPT_EXTRACT: z.coerce.boolean().default(false),
- NEXT_PUBLIC_ENABLE_CATEGORY_EXTRACT: z.coerce.boolean().default(false),
+ NEXT_PUBLIC_ENABLE_RECEIPT_EXTRACT: z.preprocess(
+ interpretEnvVarAsBool,
+ z.boolean().default(false),
+ ),
+ NEXT_PUBLIC_ENABLE_CATEGORY_EXTRACT: z.preprocess(
+ interpretEnvVarAsBool,
+ z.boolean().default(false),
+ ),
OPENAI_API_KEY: z.string().optional(),
})
.superRefine((env, ctx) => {
diff --git a/src/lib/featureFlags.ts b/src/lib/featureFlags.ts
new file mode 100644
index 0000000..e57ca9c
--- /dev/null
+++ b/src/lib/featureFlags.ts
@@ -0,0 +1,15 @@
+'use server'
+
+import { env } from './env'
+
+export async function getRuntimeFeatureFlags() {
+ return {
+ enableExpenseDocuments: env.NEXT_PUBLIC_ENABLE_EXPENSE_DOCUMENTS,
+ enableReceiptExtract: env.NEXT_PUBLIC_ENABLE_RECEIPT_EXTRACT,
+ enableCategoryExtract: env.NEXT_PUBLIC_ENABLE_CATEGORY_EXTRACT,
+ }
+}
+
+export type RuntimeFeatureFlags = Awaited<
+ ReturnType<typeof getRuntimeFeatureFlags>
+>