From ca700f7c2120989a1709b3c1d6c821c62303c8cb Mon Sep 17 00:00:00 2001 From: Simon Knittel Date: Sat, 7 Mar 2026 08:12:51 +0100 Subject: [PATCH 01/14] fix(ESLint): add better version tag for react-compiler plugin and update ESLint config --- app/eslint.config.mjs | 2 +- app/package-lock.json | 2 +- app/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/eslint.config.mjs b/app/eslint.config.mjs index 8ae5445b6..5cd647f5b 100644 --- a/app/eslint.config.mjs +++ b/app/eslint.config.mjs @@ -20,6 +20,7 @@ const eslintConfig = defineConfig([ ...tanstackQuery.configs["flat/recommended"], reactYouMightNotNeedAnEffect.configs.recommended, prettier, + reactCompiler.configs.recommended, globalIgnores([ ".next/**", @@ -39,7 +40,6 @@ const eslintConfig = defineConfig([ name: "custom-rules", plugins: { "@typescript-eslint": tseslint.plugin, - "react-compiler": reactCompiler, }, languageOptions: { parserOptions: { diff --git a/app/package-lock.json b/app/package-lock.json index 5c20809d9..1f8ab3615 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -109,7 +109,7 @@ "csv": "6.4.1", "eslint": "9.39.3", "eslint-config-next": "16.1.6", - "eslint-plugin-react-compiler": "latest", + "eslint-plugin-react-compiler": "19.1.0-rc.2", "eslint-plugin-react-you-might-not-need-an-effect": "0.9.1", "postcss": "8.5.6", "prettier": "3.8.1", diff --git a/app/package.json b/app/package.json index a1a980be4..9d664a4c7 100644 --- a/app/package.json +++ b/app/package.json @@ -113,7 +113,7 @@ "csv": "6.4.1", "eslint": "9.39.3", "eslint-config-next": "16.1.6", - "eslint-plugin-react-compiler": "latest", + "eslint-plugin-react-compiler": "19.1.0-rc.2", "eslint-plugin-react-you-might-not-need-an-effect": "0.9.1", "postcss": "8.5.6", "prettier": "3.8.1", From 2b085ce26bc28fffd013463e48ae53c5ab3db9dd Mon Sep 17 00:00:00 2001 From: Simon Knittel Date: Sat, 7 Mar 2026 08:13:19 +0100 Subject: [PATCH 02/14] refactor: remove unused route --- app/src/app/api/health/throw/route.ts | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 app/src/app/api/health/throw/route.ts diff --git a/app/src/app/api/health/throw/route.ts b/app/src/app/api/health/throw/route.ts deleted file mode 100644 index ed6fbbba5..000000000 --- a/app/src/app/api/health/throw/route.ts +++ /dev/null @@ -1,17 +0,0 @@ -import apiErrorHandler from "@/modules/common/utils/apiErrorHandler"; -import { type NextRequest } from "next/server"; -import { z } from "zod"; - -const bodySchema = z.object({ - message: z.string(), -}); - -export const POST = async (request: NextRequest) => { - try { - const body = (await request.json()) as unknown; - const data = bodySchema.parse(body); - throw new Error(data.message); - } catch (error) { - return apiErrorHandler(error); - } -}; From 398a3877b52be435f78309e6f228cd598e4d6c23 Mon Sep 17 00:00:00 2001 From: Simon Knittel Date: Sat, 7 Mar 2026 08:14:14 +0100 Subject: [PATCH 03/14] fix(Zod): replace deprecations --- app/src/env.ts | 2 +- app/src/modules/career/nodes/Markdown/client/schema.ts | 2 +- .../modules/career/nodes/Markdown/server/updateFlowSchema.ts | 2 +- app/src/modules/career/nodes/Role/client/schema.ts | 2 +- app/src/modules/career/nodes/Role/server/updateFlowSchema.ts | 2 +- app/src/modules/career/nodes/RoleCitizens/client/schema.ts | 2 +- .../career/nodes/RoleCitizens/server/updateFlowSchema.ts | 2 +- app/src/modules/tasks/actions/createTask.ts | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/src/env.ts b/app/src/env.ts index a5b510724..2f3797517 100644 --- a/app/src/env.ts +++ b/app/src/env.ts @@ -50,7 +50,7 @@ export const env = createEnv({ return "http://localhost:3000"; }, - z.string().url(), + z.url(), ), COMMIT_SHA: z.preprocess( // Uses VERCEL_GIT_COMMIT_SHA if COMMIT_SHA is not set diff --git a/app/src/modules/career/nodes/Markdown/client/schema.ts b/app/src/modules/career/nodes/Markdown/client/schema.ts index b58bf4787..195f2bb1e 100644 --- a/app/src/modules/career/nodes/Markdown/client/schema.ts +++ b/app/src/modules/career/nodes/Markdown/client/schema.ts @@ -5,7 +5,7 @@ import z from "zod"; export const schema = z.object({ id: z.cuid2(), markdown: z.string(), - markdownPosition: z.nativeEnum(FlowNodeMarkdownPosition), + markdownPosition: z.enum(FlowNodeMarkdownPosition), backgroundColor: z.string(), backgroundTransparency: z.coerce.number().min(0).max(1), }); diff --git a/app/src/modules/career/nodes/Markdown/server/updateFlowSchema.ts b/app/src/modules/career/nodes/Markdown/server/updateFlowSchema.ts index 27434c749..ed8029dd0 100644 --- a/app/src/modules/career/nodes/Markdown/server/updateFlowSchema.ts +++ b/app/src/modules/career/nodes/Markdown/server/updateFlowSchema.ts @@ -12,7 +12,7 @@ export const updateFlowSchema = z.object({ height: z.number(), data: z.object({ markdown: z.string(), - markdownPosition: z.nativeEnum(FlowNodeMarkdownPosition), + markdownPosition: z.enum(FlowNodeMarkdownPosition), backgroundColor: z.string().optional(), backgroundTransparency: z.number().min(0).max(1).optional(), }), diff --git a/app/src/modules/career/nodes/Role/client/schema.ts b/app/src/modules/career/nodes/Role/client/schema.ts index c42b3bb86..154f2401e 100644 --- a/app/src/modules/career/nodes/Role/client/schema.ts +++ b/app/src/modules/career/nodes/Role/client/schema.ts @@ -6,7 +6,7 @@ import z from "zod"; export const schema = z.object({ id: z.cuid2(), roleId: z.string(), - roleImage: z.nativeEnum(FlowNodeRoleImage), + roleImage: z.enum(FlowNodeRoleImage), backgroundColor: z.string(), backgroundTransparency: z.coerce.number().min(0).max(1), showUnlocked: z.preprocess((value) => value === "true", z.boolean()), diff --git a/app/src/modules/career/nodes/Role/server/updateFlowSchema.ts b/app/src/modules/career/nodes/Role/server/updateFlowSchema.ts index e5f0c91a8..faad04ace 100644 --- a/app/src/modules/career/nodes/Role/server/updateFlowSchema.ts +++ b/app/src/modules/career/nodes/Role/server/updateFlowSchema.ts @@ -14,7 +14,7 @@ export const updateFlowSchema = z.object({ role: z.object({ id: z.cuid(), }), - roleImage: z.nativeEnum(FlowNodeRoleImage), + roleImage: z.enum(FlowNodeRoleImage), backgroundColor: z.string().optional(), backgroundTransparency: z.number().min(0).max(1).optional(), showUnlocked: z.boolean().nullish(), diff --git a/app/src/modules/career/nodes/RoleCitizens/client/schema.ts b/app/src/modules/career/nodes/RoleCitizens/client/schema.ts index e94c03fda..b5e9d51c8 100644 --- a/app/src/modules/career/nodes/RoleCitizens/client/schema.ts +++ b/app/src/modules/career/nodes/RoleCitizens/client/schema.ts @@ -6,7 +6,7 @@ import z from "zod"; export const schema = z.object({ id: z.cuid2(), roleId: z.string(), - roleCitizensAlignment: z.nativeEnum(FlowNodeRoleCitizensAlignment), + roleCitizensAlignment: z.enum(FlowNodeRoleCitizensAlignment), roleCitizensHideRole: z.preprocess((value) => value === "true", z.boolean()), backgroundColor: z.string(), backgroundTransparency: z.coerce.number().min(0).max(1), diff --git a/app/src/modules/career/nodes/RoleCitizens/server/updateFlowSchema.ts b/app/src/modules/career/nodes/RoleCitizens/server/updateFlowSchema.ts index 8bece926c..16fd6a1bc 100644 --- a/app/src/modules/career/nodes/RoleCitizens/server/updateFlowSchema.ts +++ b/app/src/modules/career/nodes/RoleCitizens/server/updateFlowSchema.ts @@ -14,7 +14,7 @@ export const updateFlowSchema = z.object({ role: z.object({ id: z.cuid(), }), - roleCitizensAlignment: z.nativeEnum(FlowNodeRoleCitizensAlignment), + roleCitizensAlignment: z.enum(FlowNodeRoleCitizensAlignment), roleCitizensHideRole: z.boolean(), backgroundColor: z.string().optional(), backgroundTransparency: z.number().min(0).max(1).optional(), diff --git a/app/src/modules/tasks/actions/createTask.ts b/app/src/modules/tasks/actions/createTask.ts index 0dc6766bd..4ff05952f 100644 --- a/app/src/modules/tasks/actions/createTask.ts +++ b/app/src/modules/tasks/actions/createTask.ts @@ -12,13 +12,13 @@ import { serializeError } from "serialize-error"; import { z } from "zod"; const schema = z.object({ - visibility: z.nativeEnum(TaskVisibility), + visibility: z.enum(TaskVisibility), assignmentLimit: z.coerce.number().min(1).nullable(), assignedToIds: z.array(z.cuid()).max(250).optional(), // Arbitrary (untested) limit to prevent DDoS title: z.string().trim().max(64), description: z.string().trim().max(2048).optional(), expiresAt: z.coerce.date().optional(), - rewardType: z.nativeEnum(TaskRewardType), + rewardType: z.enum(TaskRewardType), rewardTypeTextValue: z.string().trim().max(2048).optional(), rewardTypeSilcValue: z.coerce.number().optional(), rewardTypeNewSilcValue: z.coerce.number().optional(), From c9125b02b53db4f6f611df3d2b4f6edc2d5d574e Mon Sep 17 00:00:00 2001 From: Simon Knittel Date: Sat, 7 Mar 2026 08:14:31 +0100 Subject: [PATCH 04/14] fix: add samesite=lax to enable_admin cookie --- app/src/modules/auth/components/AdminEnabler.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/modules/auth/components/AdminEnabler.tsx b/app/src/modules/auth/components/AdminEnabler.tsx index 7664c36cd..8d22c0344 100644 --- a/app/src/modules/auth/components/AdminEnabler.tsx +++ b/app/src/modules/auth/components/AdminEnabler.tsx @@ -13,9 +13,9 @@ export const AdminEnabler = ({ className, enabled = false }: Props) => { const handleClick = () => { if (enabled) { - document.cookie = `enable_admin=; path=/; max-age=0;`; + document.cookie = `enable_admin=; path=/; samesite=lax; max-age=0;`; } else { - document.cookie = `enable_admin=1; path=/; max-age=${60 * 60 * 24 * 7};`; + document.cookie = `enable_admin=1; path=/; samesite=lax; max-age=${60 * 60 * 24 * 7};`; } router.refresh(); From dfe64943b05ddbd92e0d291f535e81d3dc729532 Mon Sep 17 00:00:00 2001 From: Simon Knittel Date: Sat, 7 Mar 2026 08:14:58 +0100 Subject: [PATCH 05/14] fix: add some limits on endpoints --- app/src/modules/career/nodes/Markdown/client/schema.ts | 5 +++-- .../career/nodes/Markdown/server/updateFlowSchema.ts | 2 +- app/src/modules/citizen/actions/updateRoleAssignment.ts | 9 +++++++++ .../actions/updateMyNotificationSettings.ts | 9 +++++++++ .../actions/updateParticipantAttribute.ts | 9 +++++++++ 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/app/src/modules/career/nodes/Markdown/client/schema.ts b/app/src/modules/career/nodes/Markdown/client/schema.ts index 195f2bb1e..2cc16895e 100644 --- a/app/src/modules/career/nodes/Markdown/client/schema.ts +++ b/app/src/modules/career/nodes/Markdown/client/schema.ts @@ -1,11 +1,12 @@ "use client"; + import { FlowNodeMarkdownPosition } from "@prisma/client"; import z from "zod"; export const schema = z.object({ id: z.cuid2(), - markdown: z.string(), + markdown: z.string().max(5000), markdownPosition: z.enum(FlowNodeMarkdownPosition), - backgroundColor: z.string(), + backgroundColor: z.string().optional(), backgroundTransparency: z.coerce.number().min(0).max(1), }); diff --git a/app/src/modules/career/nodes/Markdown/server/updateFlowSchema.ts b/app/src/modules/career/nodes/Markdown/server/updateFlowSchema.ts index ed8029dd0..f657446df 100644 --- a/app/src/modules/career/nodes/Markdown/server/updateFlowSchema.ts +++ b/app/src/modules/career/nodes/Markdown/server/updateFlowSchema.ts @@ -11,7 +11,7 @@ export const updateFlowSchema = z.object({ width: z.number(), height: z.number(), data: z.object({ - markdown: z.string(), + markdown: z.string().max(5000), markdownPosition: z.enum(FlowNodeMarkdownPosition), backgroundColor: z.string().optional(), backgroundTransparency: z.number().min(0).max(1).optional(), diff --git a/app/src/modules/citizen/actions/updateRoleAssignment.ts b/app/src/modules/citizen/actions/updateRoleAssignment.ts index 61b53e6ea..16b0cc74c 100644 --- a/app/src/modules/citizen/actions/updateRoleAssignment.ts +++ b/app/src/modules/citizen/actions/updateRoleAssignment.ts @@ -28,6 +28,15 @@ export const updateRoleAssignments = createAuthenticatedAction( requestPayload: formData, }; + /** + * Further validate the request + */ + if (formData.keys.length > 500) + return { + error: t("Common.badRequest"), + requestPayload: formData, + }; + /** * */ diff --git a/app/src/modules/notifications/actions/updateMyNotificationSettings.ts b/app/src/modules/notifications/actions/updateMyNotificationSettings.ts index 3da7049ac..d7edbfb70 100644 --- a/app/src/modules/notifications/actions/updateMyNotificationSettings.ts +++ b/app/src/modules/notifications/actions/updateMyNotificationSettings.ts @@ -30,6 +30,15 @@ export const updateMyNotificationSettings = createAuthenticatedAction( requestPayload: formData, }; + /** + * Further validate the request + */ + if (formData.keys.length > 100) + return { + error: t("Common.badRequest"), + requestPayload: formData, + }; + /** * */ diff --git a/app/src/modules/profit-distribution/actions/updateParticipantAttribute.ts b/app/src/modules/profit-distribution/actions/updateParticipantAttribute.ts index a188bcc67..f1dbe8217 100644 --- a/app/src/modules/profit-distribution/actions/updateParticipantAttribute.ts +++ b/app/src/modules/profit-distribution/actions/updateParticipantAttribute.ts @@ -33,6 +33,15 @@ export const updateParticipantAttribute = createAuthenticatedAction( requestPayload: formData, }; + /** + * Further validate the request + */ + if (formData.keys.length > 100) + return { + error: t("Common.badRequest"), + requestPayload: formData, + }; + /** * */ From af62c34439c5d02239e442f7f464089081bce109 Mon Sep 17 00:00:00 2001 From: Simon Knittel Date: Sat, 7 Mar 2026 08:15:16 +0100 Subject: [PATCH 06/14] fix: improve env usage --- app/src/modules/common/utils/api.ts | 4 ++-- app/src/server/api/routers/ai.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/modules/common/utils/api.ts b/app/src/modules/common/utils/api.ts index 43159f51c..e34cbd4d3 100644 --- a/app/src/modules/common/utils/api.ts +++ b/app/src/modules/common/utils/api.ts @@ -4,6 +4,7 @@ * * We also create a few inference helpers for input and output types. */ +import { env } from "@/env"; import type { AppRouter } from "@/server/api/root"; import { httpBatchLink, loggerLink } from "@trpc/client"; import { createTRPCNext } from "@trpc/next"; @@ -12,8 +13,7 @@ import superjson from "superjson"; const getBaseUrl = () => { if (typeof window !== "undefined") return ""; // browser should use relative url - if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url - return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost + return env.BASE_URL; }; /** A set of type-safe react-query hooks for your tRPC API. */ diff --git a/app/src/server/api/routers/ai.ts b/app/src/server/api/routers/ai.ts index 4fe7acf88..0ceaa2782 100644 --- a/app/src/server/api/routers/ai.ts +++ b/app/src/server/api/routers/ai.ts @@ -1,10 +1,10 @@ +import { env } from "@/env"; import { isOpenAIEnabled } from "@/modules/common/utils/isOpenAIEnabled"; import { log } from "@/modules/logging"; import { getRoles } from "@/modules/roles/queries"; import { TRPCError } from "@trpc/server"; import OpenAI from "openai"; import { type ChatCompletionMessageParam } from "openai/resources/index.mjs"; -import { env } from "process"; import { serializeError } from "serialize-error"; import { z } from "zod"; import { createTRPCRouter, protectedProcedure } from "../trpc"; From 9d985cf29296f54858954151fe33507817889556 Mon Sep 17 00:00:00 2001 From: Simon Knittel Date: Sat, 7 Mar 2026 08:15:43 +0100 Subject: [PATCH 07/14] docs: improve readability --- app/src/proxy.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/proxy.ts b/app/src/proxy.ts index 9a0930b31..519a4af08 100644 --- a/app/src/proxy.ts +++ b/app/src/proxy.ts @@ -7,7 +7,11 @@ export function proxy(request: NextRequest) { request.cookies.get("next-auth.session-token") || request.cookies.get("__Secure-next-auth.session-token"); - // Early return. Make sure to use `authenticatePage()` on individual pages in order to fully authenticate the user. + /** + * This is only an early return. Actual verification of the session is done + * on the individual pages. Use `authenticatePage()` on in order to fully + * authenticate the user. + */ if (pathname.startsWith("/app") && !sessionCookie) { return NextResponse.redirect(new URL(`/`, request.url)); } From c949953c3c9da454d6fb059362251a5a1282b2dd Mon Sep 17 00:00:00 2001 From: Simon Knittel Date: Sat, 7 Mar 2026 08:29:32 +0100 Subject: [PATCH 08/14] fix: add zod-validation-error v4 Fixes `Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './v4' is not defined by "exports" in /Users/simon.knittel/Documents/personal/projects/sam/app/node_modules/zod-validation-error/package.json` caused by `eslint-config-next` and `eslint-plugin-react-hooks` --- app/package-lock.json | 11 ++++++----- app/package.json | 6 ++++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/package-lock.json b/app/package-lock.json index 1f8ab3615..2915d69fe 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -119,7 +119,8 @@ "tailwindcss": "4.2.1", "typescript": "5.9.3", "vercel": "50.22.1", - "vitest": "3.2.4" + "vitest": "3.2.4", + "zod-validation-error": "4.0.2" } }, "node_modules/@algolia/autocomplete-core": { @@ -24492,16 +24493,16 @@ } }, "node_modules/zod-validation-error": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-3.5.4.tgz", - "integrity": "sha512-+hEiRIiPobgyuFlEojnqjJnhFvg4r/i3cqgcm67eehZf/WBaK3g6cD02YU9mtdVxZjv8CzCA9n/Rhrs3yAAvAw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", "dev": true, "license": "MIT", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "zod": "^3.24.4" + "zod": "^3.25.0 || ^4.0.0" } }, "node_modules/zrender": { diff --git a/app/package.json b/app/package.json index 9d664a4c7..94706c09a 100644 --- a/app/package.json +++ b/app/package.json @@ -123,7 +123,8 @@ "tailwindcss": "4.2.1", "typescript": "5.9.3", "vercel": "50.22.1", - "vitest": "3.2.4" + "vitest": "3.2.4", + "zod-validation-error": "4.0.2" }, "prisma": { "schema": "./prisma/schema" @@ -131,6 +132,7 @@ "packageManager": "npm@10.9.4", "overrides": { "@types/react-dom": "19.2.3", - "@types/react": "19.2.14" + "@types/react": "19.2.14", + "zod-validation-error": "4.0.2" } } From add4bbda7abd3b2ee1be478f5d726e9cefbab68c Mon Sep 17 00:00:00 2001 From: Simon Knittel Date: Sat, 7 Mar 2026 08:33:06 +0100 Subject: [PATCH 09/14] style: run Prettier --- app/.devcontainer/devcontainer.json | 5 +-- app/postcss.config.cjs | 2 +- .../app/dogfight-trainer/opengraph-image.tsx | 14 +++---- app/src/app/icon.tsx | 38 ++++++++++--------- app/src/app/manifest.ts | 4 +- app/src/app/opengraph-image.tsx | 10 ++--- .../app/preview-channel/_opengraph-image.tsx | 14 +++---- .../common/components/CreateContext.tsx | 24 ++++++------ 8 files changed, 52 insertions(+), 59 deletions(-) diff --git a/app/.devcontainer/devcontainer.json b/app/.devcontainer/devcontainer.json index 1f65d98ad..376796cdd 100644 --- a/app/.devcontainer/devcontainer.json +++ b/app/.devcontainer/devcontainer.json @@ -23,10 +23,7 @@ ] } }, - "forwardPorts": [ - 3000, - "db:5432" - ], + "forwardPorts": [3000, "db:5432"], "postCreateCommand": "./.devcontainer/postCreateCommand.sh", "remoteUser": "node" } diff --git a/app/postcss.config.cjs b/app/postcss.config.cjs index ca54d4ad3..19afca407 100644 --- a/app/postcss.config.cjs +++ b/app/postcss.config.cjs @@ -1,6 +1,6 @@ const config = { plugins: { - '@tailwindcss/postcss': {}, + "@tailwindcss/postcss": {}, }, }; diff --git a/app/src/app/dogfight-trainer/opengraph-image.tsx b/app/src/app/dogfight-trainer/opengraph-image.tsx index 96a32a3de..c2a6db90f 100644 --- a/app/src/app/dogfight-trainer/opengraph-image.tsx +++ b/app/src/app/dogfight-trainer/opengraph-image.tsx @@ -14,16 +14,14 @@ export default async function og() { ).then((res) => res.arrayBuffer()); return new ImageResponse( - ( -
-

Dogfight Trainer

+
+

Dogfight Trainer

-
-

Sinister Inc

-

Hoist the Black

-
+
+

Sinister Inc

+

Hoist the Black

- ), +
, { ...size, fonts: [ diff --git a/app/src/app/icon.tsx b/app/src/app/icon.tsx index cc9807962..8bbbd3c7e 100644 --- a/app/src/app/icon.tsx +++ b/app/src/app/icon.tsx @@ -9,25 +9,27 @@ export const contentType = "image/png"; export default function icon() { return new ImageResponse( - ( - + + - - - - - - - - - - - - ), + + + + + + + + + , size, ); } diff --git a/app/src/app/manifest.ts b/app/src/app/manifest.ts index e72683625..1ad460751 100644 --- a/app/src/app/manifest.ts +++ b/app/src/app/manifest.ts @@ -1,10 +1,10 @@ import { env } from "@/env"; import { type MetadataRoute } from "next"; import faviconSrc from "../assets/favicon.svg"; -import screenshotDashboardMobileSrc from "../assets/screenshots/screenshot-dashboard-mobile.avif"; -import screenshotDashboardSrc from "../assets/screenshots/screenshot-dashboard.avif"; import screenshotAppsMobileSrc from "../assets/screenshots/screenshot-apps-mobile.avif"; import screenshotAppsSrc from "../assets/screenshots/screenshot-apps.avif"; +import screenshotDashboardMobileSrc from "../assets/screenshots/screenshot-dashboard-mobile.avif"; +import screenshotDashboardSrc from "../assets/screenshots/screenshot-dashboard.avif"; export default function manifest(): MetadataRoute.Manifest { return { diff --git a/app/src/app/opengraph-image.tsx b/app/src/app/opengraph-image.tsx index 1c88b7d98..283fecb01 100644 --- a/app/src/app/opengraph-image.tsx +++ b/app/src/app/opengraph-image.tsx @@ -14,12 +14,10 @@ export default async function og() { ).then((res) => res.arrayBuffer()); return new ImageResponse( - ( -
-

SAM

-

Sinister Incorporated

-
- ), +
+

SAM

+

Sinister Incorporated

+
, { ...size, fonts: [ diff --git a/app/src/app/preview-channel/_opengraph-image.tsx b/app/src/app/preview-channel/_opengraph-image.tsx index 48c15638a..35aaf62c0 100644 --- a/app/src/app/preview-channel/_opengraph-image.tsx +++ b/app/src/app/preview-channel/_opengraph-image.tsx @@ -14,16 +14,14 @@ export default async function og() { ).then((res) => res.arrayBuffer()); return new ImageResponse( - ( -
-

Preview Channel

+
+

Preview Channel

-
-

Sinister Inc

-

Hoist the Black

-
+
+

Sinister Inc

+

Hoist the Black

- ), +
, { ...size, fonts: [ diff --git a/app/src/modules/common/components/CreateContext.tsx b/app/src/modules/common/components/CreateContext.tsx index ea4ece630..08f0ae3fe 100644 --- a/app/src/modules/common/components/CreateContext.tsx +++ b/app/src/modules/common/components/CreateContext.tsx @@ -20,9 +20,9 @@ const CreateCitizenForm = dynamic(() => ); const CreateOrganizationForm = dynamic(() => - import( - "@/modules/spynet/components/CreateOrganization/CreateOrganizationForm" - ).then((mod) => mod.CreateOrganizationForm), + import("@/modules/spynet/components/CreateOrganization/CreateOrganizationForm").then( + (mod) => mod.CreateOrganizationForm, + ), ); const CreateRoleForm = dynamic(() => @@ -32,9 +32,9 @@ const CreateRoleForm = dynamic(() => ); const CreatePenaltyEntryForm = dynamic(() => - import( - "@/modules/penalty-points/components/CreatePenaltyEntry/CreatePenaltyEntryForm" - ).then((mod) => mod.CreatePenaltyEntryForm), + import("@/modules/penalty-points/components/CreatePenaltyEntry/CreatePenaltyEntryForm").then( + (mod) => mod.CreatePenaltyEntryForm, + ), ); const CreateTaskForm = dynamic(() => @@ -44,15 +44,15 @@ const CreateTaskForm = dynamic(() => ); const CreateProfitDistributionCycleForm = dynamic(() => - import( - "@/modules/profit-distribution/components/CreateProfitDistributionCycleForm" - ).then((mod) => mod.CreateProfitDistributionCycleForm), + import("@/modules/profit-distribution/components/CreateProfitDistributionCycleForm").then( + (mod) => mod.CreateProfitDistributionCycleForm, + ), ); const CreateSilcTransactionForm = dynamic(() => - import( - "@/modules/silc/components/CreateSilcTransactionForm" - ).then((mod) => mod.CreateSilcTransactionForm), + import("@/modules/silc/components/CreateSilcTransactionForm").then( + (mod) => mod.CreateSilcTransactionForm, + ), ); export const createForms = { From 0d511358723a265f905e1a1be71d546f0d408573 Mon Sep 17 00:00:00 2001 From: Simon Knittel Date: Sat, 7 Mar 2026 08:34:14 +0100 Subject: [PATCH 10/14] ci: disable continue-on-error for Prettier --- .github/workflows/validate-app.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/validate-app.yml b/.github/workflows/validate-app.yml index ae8b21fad..2f6be746e 100644 --- a/.github/workflows/validate-app.yml +++ b/.github/workflows/validate-app.yml @@ -76,7 +76,6 @@ jobs: - name: Prettier run: npx prettier --check . working-directory: app - continue-on-error: true test: runs-on: ubuntu-24.04 From c3b4606ca8f854ae824f4a3236275025b5e5da8b Mon Sep 17 00:00:00 2001 From: Simon Knittel Date: Sat, 7 Mar 2026 08:48:47 +0100 Subject: [PATCH 11/14] fix: BASE_URL -> NEXT_PUBLIC_BASE_URL --- app/src/app/app/help/contributing/page.tsx | 26 ++++++++--------- app/src/app/app/help/support/page.tsx | 2 +- app/src/app/layout.tsx | 2 +- app/src/app/manifest.ts | 4 +-- app/src/env.ts | 28 +++++++++---------- app/src/modules/common/utils/api.ts | 2 +- .../utils/type-handlers/email_confirmation.ts | 2 +- 7 files changed, 33 insertions(+), 33 deletions(-) diff --git a/app/src/app/app/help/contributing/page.tsx b/app/src/app/app/help/contributing/page.tsx index 86a14b10a..f61c0945e 100644 --- a/app/src/app/app/help/contributing/page.tsx +++ b/app/src/app/app/help/contributing/page.tsx @@ -94,7 +94,7 @@ export default async function Page() {
@@ -117,7 +117,7 @@ export default async function Page() {
Arten der Mithilfe @@ -156,7 +156,7 @@ export default async function Page() {
Integrierte Apps @@ -180,7 +180,7 @@ export default async function Page() {
Externe Apps @@ -238,7 +238,7 @@ export default async function Page() { @@ -296,7 +296,7 @@ export default async function Page() { @@ -335,7 +335,7 @@ export default async function Page() { @@ -364,7 +364,7 @@ export default async function Page() { @@ -388,14 +388,14 @@ export default async function Page() {
Frequently Asked Questions (FAQ) @@ -411,7 +411,7 @@ export default async function Page() { @@ -434,7 +434,7 @@ export default async function Page() { @@ -452,7 +452,7 @@ export default async function Page() { diff --git a/app/src/app/app/help/support/page.tsx b/app/src/app/app/help/support/page.tsx index d24abbb2a..3d086e2e0 100644 --- a/app/src/app/app/help/support/page.tsx +++ b/app/src/app/app/help/support/page.tsx @@ -35,7 +35,7 @@ export default async function Page() {
diff --git a/app/src/app/layout.tsx b/app/src/app/layout.tsx index f351dc9c2..dd2abdd8b 100644 --- a/app/src/app/layout.tsx +++ b/app/src/app/layout.tsx @@ -14,7 +14,7 @@ const robotMono = Roboto_Mono({ }); export const metadata: Metadata = { - metadataBase: new URL(env.BASE_URL), + metadataBase: new URL(env.NEXT_PUBLIC_BASE_URL), title: { default: "SAM - Sinister Incorporated", template: "%s | SAM - Sinister Incorporated", diff --git a/app/src/app/manifest.ts b/app/src/app/manifest.ts index 1ad460751..d688553b7 100644 --- a/app/src/app/manifest.ts +++ b/app/src/app/manifest.ts @@ -13,8 +13,8 @@ export default function manifest(): MetadataRoute.Manifest { description: "Sinister Administration Module (SAM) for the Star Citizen organization Sinister Incorporated", categories: ["entertainment", "games"], // https://github.com/w3c/manifest/wiki/Categories - scope: env.BASE_URL, // Will open links outside the app in the browser - start_url: `${env.BASE_URL}/app`, + scope: env.NEXT_PUBLIC_BASE_URL, // Will open links outside the app in the browser + start_url: `${env.NEXT_PUBLIC_BASE_URL}/app`, shortcuts: [ // Can't be individualized based on permissions { diff --git a/app/src/env.ts b/app/src/env.ts index 2f3797517..498d06833 100644 --- a/app/src/env.ts +++ b/app/src/env.ts @@ -39,19 +39,6 @@ export const env = createEnv({ UNLEASH_SERVER_API_URL: z.url().optional(), /** Unleash (or any other Unleash-compatible feature flag provider like GitLab) */ UNLEASH_SERVER_API_TOKEN: z.string().optional(), - BASE_URL: z.preprocess( - // Uses VERCEL_URL if BASE_URL is not set, e.g. on Vercel's preview deployments - (str) => { - if (str) { - return str; - } else if (process.env.VERCEL_URL) { - return `https://${process.env.VERCEL_URL}`; - } - - return "http://localhost:3000"; - }, - z.url(), - ), COMMIT_SHA: z.preprocess( // Uses VERCEL_GIT_COMMIT_SHA if COMMIT_SHA is not set (str) => str || process.env.VERCEL_GIT_COMMIT_SHA, @@ -111,6 +98,19 @@ export const env = createEnv({ }, z.string(), ), + NEXT_PUBLIC_BASE_URL: z.preprocess( + // Uses VERCEL_URL if BASE_URL is not set, e.g. on Vercel's preview deployments + (str) => { + if (str) { + return str; + } else if (process.env.VERCEL_URL) { + return `https://${process.env.VERCEL_URL}`; + } + + return "http://localhost:3000"; + }, + z.url(), + ), }, /* @@ -143,7 +143,7 @@ export const env = createEnv({ COMMIT_SHA: process.env.COMMIT_SHA, NEXT_PUBLIC_CARE_BEAR_SHOOTER_BUILD_URL: process.env.NEXT_PUBLIC_CARE_BEAR_SHOOTER_BUILD_URL, - BASE_URL: process.env.BASE_URL, + NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL, AWS_ACCESS_KEY_ID: process.env.AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY, AWS_EVENT_BUS_ARN: process.env.AWS_EVENT_BUS_ARN, diff --git a/app/src/modules/common/utils/api.ts b/app/src/modules/common/utils/api.ts index e34cbd4d3..f70228f41 100644 --- a/app/src/modules/common/utils/api.ts +++ b/app/src/modules/common/utils/api.ts @@ -13,7 +13,7 @@ import superjson from "superjson"; const getBaseUrl = () => { if (typeof window !== "undefined") return ""; // browser should use relative url - return env.BASE_URL; + return env.NEXT_PUBLIC_BASE_URL; // SSR should use the provided BASE_URL }; /** A set of type-safe react-query hooks for your tRPC API. */ diff --git a/app/src/modules/notifications/utils/type-handlers/email_confirmation.ts b/app/src/modules/notifications/utils/type-handlers/email_confirmation.ts index 4227d8bce..d95410b93 100644 --- a/app/src/modules/notifications/utils/type-handlers/email_confirmation.ts +++ b/app/src/modules/notifications/utils/type-handlers/email_confirmation.ts @@ -30,7 +30,7 @@ export const emailConfirmationHandler = async (payload: Payload) => { { to: payload.userEmail, templateProps: { - baseUrl: env.BASE_URL, + baseUrl: env.NEXT_PUBLIC_BASE_URL, host: env.NEXT_PUBLIC_HOST, token: emailConfirmationToken, }, From f97d9cfd6a3e9aee49ab761f0bfca385e75c838f Mon Sep 17 00:00:00 2001 From: Simon Knittel Date: Sat, 7 Mar 2026 08:48:53 +0100 Subject: [PATCH 12/14] docs: typo --- app/src/proxy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/proxy.ts b/app/src/proxy.ts index 519a4af08..8edb0fcd4 100644 --- a/app/src/proxy.ts +++ b/app/src/proxy.ts @@ -9,7 +9,7 @@ export function proxy(request: NextRequest) { /** * This is only an early return. Actual verification of the session is done - * on the individual pages. Use `authenticatePage()` on in order to fully + * on the individual pages. Use `authenticatePage()` in order to fully * authenticate the user. */ if (pathname.startsWith("/app") && !sessionCookie) { From 335a324f11e419201aaa82cd2944794bcc750329 Mon Sep 17 00:00:00 2001 From: Simon Knittel Date: Sat, 7 Mar 2026 08:49:07 +0100 Subject: [PATCH 13/14] fix: use Array.from() for formData.keys() --- app/src/modules/citizen/actions/updateRoleAssignment.ts | 2 +- .../notifications/actions/updateMyNotificationSettings.ts | 2 +- .../profit-distribution/actions/updateParticipantAttribute.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/modules/citizen/actions/updateRoleAssignment.ts b/app/src/modules/citizen/actions/updateRoleAssignment.ts index 16b0cc74c..d60a3c754 100644 --- a/app/src/modules/citizen/actions/updateRoleAssignment.ts +++ b/app/src/modules/citizen/actions/updateRoleAssignment.ts @@ -31,7 +31,7 @@ export const updateRoleAssignments = createAuthenticatedAction( /** * Further validate the request */ - if (formData.keys.length > 500) + if (Array.from(formData.keys()).length > 500) return { error: t("Common.badRequest"), requestPayload: formData, diff --git a/app/src/modules/notifications/actions/updateMyNotificationSettings.ts b/app/src/modules/notifications/actions/updateMyNotificationSettings.ts index d7edbfb70..6f37104af 100644 --- a/app/src/modules/notifications/actions/updateMyNotificationSettings.ts +++ b/app/src/modules/notifications/actions/updateMyNotificationSettings.ts @@ -33,7 +33,7 @@ export const updateMyNotificationSettings = createAuthenticatedAction( /** * Further validate the request */ - if (formData.keys.length > 100) + if (Array.from(formData.keys()).length > 100) return { error: t("Common.badRequest"), requestPayload: formData, diff --git a/app/src/modules/profit-distribution/actions/updateParticipantAttribute.ts b/app/src/modules/profit-distribution/actions/updateParticipantAttribute.ts index f1dbe8217..f8dd2bcb9 100644 --- a/app/src/modules/profit-distribution/actions/updateParticipantAttribute.ts +++ b/app/src/modules/profit-distribution/actions/updateParticipantAttribute.ts @@ -36,7 +36,7 @@ export const updateParticipantAttribute = createAuthenticatedAction( /** * Further validate the request */ - if (formData.keys.length > 100) + if (Array.from(formData.keys()).length > 100) return { error: t("Common.badRequest"), requestPayload: formData, From c8bea8441d2da48b64dc29042f722d2073eaa68c Mon Sep 17 00:00:00 2001 From: Simon Knittel Date: Sat, 7 Mar 2026 08:56:43 +0100 Subject: [PATCH 14/14] fix: BASE_URL -> NEXT_PUBLIC_BASE_URL --- app/src/env.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/env.ts b/app/src/env.ts index 498d06833..a69fa6f0b 100644 --- a/app/src/env.ts +++ b/app/src/env.ts @@ -88,8 +88,8 @@ export const env = createEnv({ (str) => { if (str) { return str; - } else if (process.env.BASE_URL) { - return process.env.BASE_URL.replace(/https?:\/\//, ""); + } else if (process.env.NEXT_PUBLIC_BASE_URL) { + return process.env.NEXT_PUBLIC_BASE_URL.replace(/https?:\/\//, ""); } else if (process.env.VERCEL_URL) { return process.env.VERCEL_URL; }