Conversation
- Add SiteSettings.enabledStorefrontLocales and CMS migration for junction table - resolvePublishedLocales(): PUBLISHED_LOCALES env > Medusa stub > Payload > da - Middleware redirects unpublished locales; internal /api/internal/published-locales - hreflang, sitemap, and generateStaticParams follow published locales only Made-with: Cursor
📝 WalkthroughWalkthroughImplements dynamic published locale management by adding CMS configuration field and database migration for Changes
Sequence DiagramsequenceDiagram
participant Client as Browser/Client
participant Middleware as Middleware
participant API as Published Locales API
participant CMS as Payload CMS
participant Page as Page Component
Client->>Middleware: GET /en/products/item
Middleware->>Middleware: Check URL locale
Middleware->>API: Fetch published locales
API->>CMS: Fetch site_settings.enabledStorefrontLocales
CMS-->>API: Returns published locales
API-->>Middleware: Returns {locales: ['da','en']}
alt Locale not published
Middleware-->>Client: 302 redirect to /da/products/item
else Locale published
Middleware-->>Client: Next response (allow)
Client->>Page: GET /en/products/item
Page->>Page: generateMetadata()
Page->>API: buildLocaleAlternates (async)
API->>API: resolvePublishedLocales()
API-->>Page: Returns {alternates with langs}
Page-->>Client: HTML with hreflang tags
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~65 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
apps/storefront/src/app/api/internal/published-locales/route.ts (1)
8-17: "Internal" endpoint is publicly reachable — consider documenting or gating.The path
/api/internal/published-localessuggests private use, but there's no auth gate andCache-Control: publicis set. The payload (locale codes) is non-sensitive, so this is low risk, but consider either:
- adding a short comment clarifying it is deliberately public (since middleware on Edge consumes it), or
- restricting via an internal header/secret check if you ever extend it to return more than public locale codes.
No change required right now; flagging for future-proofing.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/storefront/src/app/api/internal/published-locales/route.ts` around lines 8 - 17, The endpoint GET that calls resolvePublishedLocales and returns NextResponse.json is currently publicly reachable with "Cache-Control: public"; either add a brief inline comment above the GET function clarifying this is intentionally public because Edge middleware/other services consume locale codes, or implement a simple internal gating check (e.g., validate a specific internal header/secret before calling resolvePublishedLocales and return 401 if missing) so future expansions returning sensitive data are protected; reference the GET handler and resolvePublishedLocales when making the change.apps/storefront/src/app/[locale]/layout.tsx (1)
67-70: LGTM — runtime-resolved static params.Deriving
generateStaticParamsfromresolvePublishedLocales()is consistent with the rest of the PR. One operational note: duringnext build, ifPAYLOAD_API_URLis unreachable andPUBLISHED_LOCALESis unset, this falls back to[defaultLocale]only — so enablingenlater in the CMS without redeploying/revalidating won't pre-render/enroutes. Given middleware handles redirects for unpublished locales and routes can render dynamically on demand, this should be acceptable, but worth keeping in mind for the rollout.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/storefront/src/app/`[locale]/layout.tsx around lines 67 - 70, generateStaticParams relies on resolvePublishedLocales() at build time which can return only [defaultLocale] if PAYLOAD_API_URL is unreachable or PUBLISHED_LOCALES is unset, causing new locales added in the CMS to not be statically generated until redeploy; update generateStaticParams to explicitly fall back to include the canonical defaultLocale plus any runtime-resolved locales and emit a clear warning when resolvePublishedLocales() fails (use the same defaultLocale variable and reference resolvePublishedLocales() inside generateStaticParams) so that builds always pre-render at least the default locale and surface the failure reason in logs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@apps/storefront/src/app/`[locale]/layout.tsx:
- Around line 67-70: generateStaticParams relies on resolvePublishedLocales() at
build time which can return only [defaultLocale] if PAYLOAD_API_URL is
unreachable or PUBLISHED_LOCALES is unset, causing new locales added in the CMS
to not be statically generated until redeploy; update generateStaticParams to
explicitly fall back to include the canonical defaultLocale plus any
runtime-resolved locales and emit a clear warning when resolvePublishedLocales()
fails (use the same defaultLocale variable and reference
resolvePublishedLocales() inside generateStaticParams) so that builds always
pre-render at least the default locale and surface the failure reason in logs.
In `@apps/storefront/src/app/api/internal/published-locales/route.ts`:
- Around line 8-17: The endpoint GET that calls resolvePublishedLocales and
returns NextResponse.json is currently publicly reachable with "Cache-Control:
public"; either add a brief inline comment above the GET function clarifying
this is intentionally public because Edge middleware/other services consume
locale codes, or implement a simple internal gating check (e.g., validate a
specific internal header/secret before calling resolvePublishedLocales and
return 401 if missing) so future expansions returning sensitive data are
protected; reference the GET handler and resolvePublishedLocales when making the
change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 131f6162-6b18-4c52-b9e2-0460eaa1ee8e
📒 Files selected for processing (20)
apps/cms/migrations/20260420_site_settings_enabled_storefront_locales.tsapps/cms/migrations/index.tsapps/cms/src/app/api/storefront/globals/site-settings/route.tsapps/cms/src/globals/SiteSettings.tsapps/storefront/env.templateapps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/app/[locale]/brands/[handle]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/app/[locale]/layout.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/api/internal/published-locales/route.tsapps/storefront/src/i18n/config.tsapps/storefront/src/i18n/published-locales.tsapps/storefront/src/lib/payload-site-settings.tsapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/lib/sitemap-build.tsapps/storefront/src/middleware.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (17)
apps/storefront/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
apps/storefront/**/*.{ts,tsx,js,jsx}: Storefront app inapps/storefrontshould be a Next.js customer UI with i18n routes that composes data from both Medusa and Payload CMS
Customer-facing URLs must follow the Storefront routing pattern with locale prefixes (/da/...,/en/...)
Files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/i18n/config.tsapps/storefront/src/app/api/internal/published-locales/route.tsapps/storefront/src/app/[locale]/layout.tsxapps/storefront/src/lib/sitemap-build.tsapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/i18n/published-locales.tsapps/storefront/src/lib/payload-site-settings.tsapps/storefront/src/middleware.tsapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/code-structure.mdc)
**/*.{ts,tsx}: Treat files > ~400 lines in one .tsx as tech debt — split before adding features
Use interface for object shapes in TypeScript; keep near usage or in *.types.ts files
Avoid any type in TypeScript; use unknown + narrowing or Zod-inferred types where validated
Prefer as const objects + union type over enums unless enum is required by an external API
Prefer direct imports from the module you need (e.g.@/components/ui/button) to avoid pulling large barrels into client bundlesDefine a type for const functions when possible
**/*.{ts,tsx}: Use interfaces for object shapes in TypeScript; write functional components with typed props
Avoid enums; preferas constobjects combined with union types
Do not useanytype; useunknownwith type narrowing or Zod-inferred types for validated data
Files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/i18n/config.tsapps/storefront/src/app/api/internal/published-locales/route.tsapps/storefront/src/app/[locale]/layout.tsxapps/cms/src/app/api/storefront/globals/site-settings/route.tsapps/cms/src/globals/SiteSettings.tsapps/storefront/src/lib/sitemap-build.tsapps/cms/migrations/index.tsapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/i18n/published-locales.tsapps/cms/migrations/20260420_site_settings_enabled_storefront_locales.tsapps/storefront/src/lib/payload-site-settings.tsapps/storefront/src/middleware.tsapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/front-end-cursor-rules.mdc)
**/*.{js,jsx,ts,tsx}: Use early returns whenever possible to make the code more readable
Use descriptive variable and function/const names
Use const instead of function declarations, for example 'const toggle = () =>'
Don't use semicolons
Files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/i18n/config.tsapps/storefront/src/app/api/internal/published-locales/route.tsapps/storefront/src/app/[locale]/layout.tsxapps/cms/src/app/api/storefront/globals/site-settings/route.tsapps/cms/src/globals/SiteSettings.tsapps/storefront/src/lib/sitemap-build.tsapps/cms/migrations/index.tsapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/i18n/published-locales.tsapps/cms/migrations/20260420_site_settings_enabled_storefront_locales.tsapps/storefront/src/lib/payload-site-settings.tsapps/storefront/src/middleware.tsapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
**/*.{jsx,tsx,html}
📄 CodeRabbit inference engine (.cursor/rules/front-end-cursor-rules.mdc)
**/*.{jsx,tsx,html}: Always use Tailwind classes for styling HTML elements; avoid using CSS or tags
Implement accessibility features on elements such as tabindex='0', aria-label, on:click, and on:keydown attributes
Files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/app/[locale]/layout.tsxapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
**/*.{svelte,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/front-end-cursor-rules.mdc)
Use class: instead of the ternary operator in class tags whenever possible
Files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/app/[locale]/layout.tsxapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/front-end-cursor-rules.mdc)
Event functions should be named with a 'handle' prefix, like 'handleClick' for onClick and 'handleKeyDown' for onKeyDown
Files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/app/[locale]/layout.tsxapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-react-typescript-cursor-rules.mdc)
**/*.{ts,tsx,js,jsx}: Use concise, technical TypeScript; write functional and declarative code; avoid classes
Prefer modularization over duplication; use descriptive names likeisLoading,hasError
Use thefunctionkeyword for pure functions; write declarative JSX
Files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/i18n/config.tsapps/storefront/src/app/api/internal/published-locales/route.tsapps/storefront/src/app/[locale]/layout.tsxapps/cms/src/app/api/storefront/globals/site-settings/route.tsapps/cms/src/globals/SiteSettings.tsapps/storefront/src/lib/sitemap-build.tsapps/cms/migrations/index.tsapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/i18n/published-locales.tsapps/cms/migrations/20260420_site_settings_enabled_storefront_locales.tsapps/storefront/src/lib/payload-site-settings.tsapps/storefront/src/middleware.tsapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
**/*.{tsx,ts}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-react-typescript-cursor-rules.mdc)
**/*.{tsx,ts}: File layout: place exported component first, then subcomponents, helpers, static content, types. Followcode-structure.mdcfor file size and splitting decisions
Favor named exports for components
Default to Server Components in Next.js; use'use client'only for interactivity, browser APIs, or third-party widgets like Stripe; keep client boundaries small
Usenext/dynamicfor heavy or below-the-fold components
Usenuqslibrary for managing URL state and search parameters
Do not fetch data on the client when the server can perform the fetch; avoid duplicating fetches in layouts and pages without usingReact.cache()
Files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/i18n/config.tsapps/storefront/src/app/api/internal/published-locales/route.tsapps/storefront/src/app/[locale]/layout.tsxapps/cms/src/app/api/storefront/globals/site-settings/route.tsapps/cms/src/globals/SiteSettings.tsapps/storefront/src/lib/sitemap-build.tsapps/cms/migrations/index.tsapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/i18n/published-locales.tsapps/cms/migrations/20260420_site_settings_enabled_storefront_locales.tsapps/storefront/src/lib/payload-site-settings.tsapps/storefront/src/middleware.tsapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
**/*.{tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/nextjs-react-typescript-cursor-rules.mdc)
**/*.{tsx,jsx}: Use simple, concise conditionals in JSX; avoid unnecessary braces
Use Shadcn UI, Radix UI, and Tailwind for UI and styling; implement mobile-first responsive design
Wrap client trees in Suspense with appropriate fallbacks
Usenext/imagefor images with WebP format where possible, include sizes attribute, enable lazy loading, and mark LCP images with priority
Files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/app/[locale]/layout.tsxapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
Do not log or echo secrets; redact secrets in examples and documentation
Files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/i18n/config.tsapps/storefront/src/app/api/internal/published-locales/route.tsapps/storefront/src/app/[locale]/layout.tsxapps/cms/src/app/api/storefront/globals/site-settings/route.tsapps/cms/src/globals/SiteSettings.tsapps/storefront/src/lib/sitemap-build.tsapps/cms/migrations/index.tsapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/i18n/published-locales.tsapps/cms/migrations/20260420_site_settings_enabled_storefront_locales.tsapps/storefront/src/lib/payload-site-settings.tsapps/storefront/src/middleware.tsapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
apps/storefront/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/storefront-next.mdc)
apps/storefront/src/**/*.{ts,tsx}: Talk to Medusa via@medusajs/js-sdkand the shared client insrc/lib/medusa.ts(publishable key,NEXT_PUBLIC_MEDUSA_BACKEND_URL)
Custom store endpoints: usemedusa.client.fetchor SDK extensions consistently; do not scatter rawfetchURLs without a single base pattern
Payload/content: use existinglib/payload-*.tshelpers; passlocale(da/en) for localized APIs
Parallelize independent loads: start async work thenPromise.allto avoid sequential waterfalls
Choose cache explicitly:cache: 'no-store'vsrevalidatevs static per data freshness needs
Followcode-structure.mdcfor file structure and organization: line budgets, splitting steps/hooks/types, React/TypeScript hygiene
Files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/i18n/config.tsapps/storefront/src/app/api/internal/published-locales/route.tsapps/storefront/src/app/[locale]/layout.tsxapps/storefront/src/lib/sitemap-build.tsapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/i18n/published-locales.tsapps/storefront/src/lib/payload-site-settings.tsapps/storefront/src/middleware.tsapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
apps/storefront/src/**/*.tsx
📄 CodeRabbit inference engine (.cursor/rules/storefront-next.mdc)
apps/storefront/src/**/*.tsx: Default to Server Components in Next.js. Use'use client'only for interactivity, browser APIs, Stripe, or local state
Heavy or rare UI (modals, checkout sub-steps, maps): prefernext/dynamicwith loading fallback
Minimize props passed from server to client components; serialize only what the client needs
Files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/app/[locale]/layout.tsxapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
apps/storefront/src/app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/storefront-next.mdc)
Shared server loaders used from layout + page: wrap with
React.cache()so one request does not duplicate the same fetch
Files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/app/api/internal/published-locales/route.tsapps/storefront/src/app/[locale]/layout.tsxapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
apps/storefront/src/**/*.{tsx,css,scss}
📄 CodeRabbit inference engine (.cursor/rules/storefront-next.mdc)
Focus states on inputs: use border states, not focus rings (see project design spec)
Files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/app/[locale]/layout.tsxapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
**/api/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/security.mdc)
**/api/**/*.{js,ts}: Assume Medusa store API receives untrusted clients; validate input, enforce customer/session rules on protected routes, avoid leaking internal IDs or stack traces in production errors
Require authentication for destructive or sensitive operations in Admin/Payload interfaces; do not expose admin-only fields on public REST/GraphQL responses
Files:
apps/storefront/src/app/api/internal/published-locales/route.tsapps/cms/src/app/api/storefront/globals/site-settings/route.ts
apps/cms/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)
CMS app in
apps/cmsshould implement Payload for managing pages, articles, navigation, homepage, and editorial product copy with live preview to storefront
Files:
apps/cms/src/app/api/storefront/globals/site-settings/route.tsapps/cms/src/globals/SiteSettings.tsapps/cms/migrations/index.tsapps/cms/migrations/20260420_site_settings_enabled_storefront_locales.ts
**/lib/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/code-structure.mdc)
**/lib/**/*.{ts,tsx}: Pure utilities / mappers (lib/.ts) should be ≤ ~400 lines; split by domain (cart-.ts, checkout-*.ts)
Write pure helpers (formatting, mapping) using function keyword with single responsibility for easy unit testing
Files:
apps/storefront/src/lib/sitemap-build.tsapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/lib/payload-site-settings.ts
🧠 Learnings (17)
📓 Common learnings
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/architecture.mdc:0-0
Timestamp: 2026-04-15T16:58:24.041Z
Learning: Applies to apps/storefront/**/*.{ts,tsx,js,jsx} : Customer-facing URLs must follow the Storefront routing pattern with locale prefixes (`/da/...`, `/en/...`)
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/storefront-next.mdc:0-0
Timestamp: 2026-04-15T16:59:32.095Z
Learning: Applies to apps/storefront/src/**/*.{ts,tsx} : Payload/content: use existing `lib/payload-*.ts` helpers; pass `locale` (`da` / `en`) for localized APIs
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/payload-cms.mdc:0-0
Timestamp: 2026-04-15T16:59:15.305Z
Learning: Applies to apps/cms/**/payload.config.ts : Configure locales `da` (default) and `en` with fallback matching `payload.config.ts`. Localized fields belong in collection/global configs; API consumers use `?locale=` query parameter as documented.
📚 Learning: 2026-04-15T16:59:32.095Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/storefront-next.mdc:0-0
Timestamp: 2026-04-15T16:59:32.095Z
Learning: Applies to apps/storefront/src/**/*.{ts,tsx} : Payload/content: use existing `lib/payload-*.ts` helpers; pass `locale` (`da` / `en`) for localized APIs
Applied to files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/env.templateapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/i18n/config.tsapps/storefront/src/app/api/internal/published-locales/route.tsapps/storefront/src/app/[locale]/layout.tsxapps/cms/src/app/api/storefront/globals/site-settings/route.tsapps/cms/src/globals/SiteSettings.tsapps/storefront/src/lib/sitemap-build.tsapps/cms/migrations/index.tsapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/i18n/published-locales.tsapps/cms/migrations/20260420_site_settings_enabled_storefront_locales.tsapps/storefront/src/lib/payload-site-settings.tsapps/storefront/src/middleware.tsapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
📚 Learning: 2026-04-15T16:58:38.430Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/code-structure.mdc:0-0
Timestamp: 2026-04-15T16:58:38.430Z
Learning: Applies to **/(page|layout).{ts,tsx} : Place async loaders in lib/ or getXxxData.ts; keep page.tsx as composition + generateMetadata, not 200 lines of fetch/transform
Applied to files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/app/[locale]/layout.tsxapps/storefront/src/lib/sitemap-build.tsapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/middleware.tsapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
📚 Learning: 2026-04-15T16:58:24.041Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/architecture.mdc:0-0
Timestamp: 2026-04-15T16:58:24.041Z
Learning: Applies to apps/storefront/**/*.{ts,tsx,js,jsx} : Customer-facing URLs must follow the Storefront routing pattern with locale prefixes (`/da/...`, `/en/...`)
Applied to files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/env.templateapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/i18n/config.tsapps/storefront/src/app/api/internal/published-locales/route.tsapps/storefront/src/app/[locale]/layout.tsxapps/cms/src/app/api/storefront/globals/site-settings/route.tsapps/cms/src/globals/SiteSettings.tsapps/storefront/src/lib/sitemap-build.tsapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/app/[locale]/blog/[slug]/page.tsxapps/storefront/src/i18n/published-locales.tsapps/cms/migrations/20260420_site_settings_enabled_storefront_locales.tsapps/storefront/src/lib/payload-site-settings.tsapps/storefront/src/middleware.tsapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
📚 Learning: 2026-04-15T16:59:32.095Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/storefront-next.mdc:0-0
Timestamp: 2026-04-15T16:59:32.095Z
Learning: Applies to apps/storefront/src/**/*.{ts,tsx} : Parallelize independent loads: start async work then `Promise.all` to avoid sequential waterfalls
Applied to files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/app/[locale]/layout.tsxapps/storefront/src/lib/sitemap-build.tsapps/storefront/src/app/[locale]/concerns/[handle]/page.tsxapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/middleware.tsapps/storefront/src/app/[locale]/brands/[handle]/page.tsx
📚 Learning: 2026-04-15T16:59:32.095Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/storefront-next.mdc:0-0
Timestamp: 2026-04-15T16:59:32.095Z
Learning: Applies to apps/storefront/src/**/*.{ts,tsx} : Follow `code-structure.mdc` for file structure and organization: line budgets, splitting steps/hooks/types, React/TypeScript hygiene
Applied to files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/app/[locale]/layout.tsxapps/storefront/src/i18n/published-locales.ts
📚 Learning: 2026-04-15T16:58:24.041Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/architecture.mdc:0-0
Timestamp: 2026-04-15T16:58:24.041Z
Learning: Applies to apps/storefront/**/*.{ts,tsx,js,jsx} : Storefront app in `apps/storefront` should be a Next.js customer UI with i18n routes that composes data from both Medusa and Payload CMS
Applied to files:
apps/storefront/src/app/[locale]/[...path]/page.tsxapps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/env.templateapps/storefront/src/app/[locale]/categories/[handle]/page.tsxapps/storefront/src/app/api/internal/published-locales/route.tsapps/storefront/src/app/[locale]/layout.tsxapps/cms/src/app/api/storefront/globals/site-settings/route.tsapps/storefront/src/lib/sitemap-build.tsapps/cms/migrations/index.tsapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/i18n/published-locales.tsapps/cms/migrations/20260420_site_settings_enabled_storefront_locales.tsapps/storefront/src/lib/payload-site-settings.tsapps/storefront/src/middleware.ts
📚 Learning: 2026-04-15T16:59:32.095Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/storefront-next.mdc:0-0
Timestamp: 2026-04-15T16:59:32.095Z
Learning: Applies to apps/storefront/src/**/*.tsx : Heavy or rare UI (modals, checkout sub-steps, maps): prefer `next/dynamic` with loading fallback
Applied to files:
apps/storefront/src/app/[locale]/products/[handle]/page.tsxapps/storefront/src/app/[locale]/layout.tsx
📚 Learning: 2026-04-15T16:59:15.305Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/payload-cms.mdc:0-0
Timestamp: 2026-04-15T16:59:15.305Z
Learning: Applies to apps/cms/**/payload.config.ts : Configure locales `da` (default) and `en` with fallback matching `payload.config.ts`. Localized fields belong in collection/global configs; API consumers use `?locale=` query parameter as documented.
Applied to files:
apps/storefront/env.templateapps/storefront/src/i18n/config.tsapps/storefront/src/app/[locale]/layout.tsxapps/cms/src/app/api/storefront/globals/site-settings/route.tsapps/cms/src/globals/SiteSettings.tsapps/cms/migrations/index.tsapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/i18n/published-locales.tsapps/cms/migrations/20260420_site_settings_enabled_storefront_locales.tsapps/storefront/src/lib/payload-site-settings.tsapps/storefront/src/middleware.ts
📚 Learning: 2026-04-15T16:58:57.364Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/medusa-commerce.mdc:0-0
Timestamp: 2026-04-15T16:58:57.364Z
Learning: Applies to apps/commerce/**/*.{ts,tsx,js,jsx} : For simple reads (list/get), resolving module or core services in a GET route is acceptable.
Applied to files:
apps/storefront/src/app/api/internal/published-locales/route.ts
📚 Learning: 2026-04-15T16:59:32.095Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/storefront-next.mdc:0-0
Timestamp: 2026-04-15T16:59:32.095Z
Learning: Applies to apps/storefront/src/app/**/*.{ts,tsx} : Shared server loaders used from layout + page: wrap with `React.cache()` so one request does not duplicate the same fetch
Applied to files:
apps/storefront/src/app/[locale]/layout.tsxapps/storefront/src/middleware.ts
📚 Learning: 2026-04-15T16:59:08.330Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/nextjs-react-typescript-cursor-rules.mdc:0-0
Timestamp: 2026-04-15T16:59:08.330Z
Learning: Applies to **/*.{tsx,ts} : File layout: place exported component first, then subcomponents, helpers, static content, types. Follow `code-structure.mdc` for file size and splitting decisions
Applied to files:
apps/storefront/src/app/[locale]/layout.tsx
📚 Learning: 2026-04-15T16:58:38.430Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/code-structure.mdc:0-0
Timestamp: 2026-04-15T16:58:38.430Z
Learning: Applies to **/(page|layout).{ts,tsx} : In Next.js App Router, page.tsx / layout.tsx should be async server components when possible; delegate to small components and shared loaders
Applied to files:
apps/storefront/src/app/[locale]/layout.tsx
📚 Learning: 2026-04-15T16:58:24.041Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/architecture.mdc:0-0
Timestamp: 2026-04-15T16:58:24.041Z
Learning: Applies to apps/cms/**/*.{ts,tsx,js,jsx} : CMS app in `apps/cms` should implement Payload for managing pages, articles, navigation, homepage, and editorial product copy with live preview to storefront
Applied to files:
apps/cms/src/globals/SiteSettings.tsapps/cms/migrations/20260420_site_settings_enabled_storefront_locales.tsapps/storefront/src/lib/payload-site-settings.ts
📚 Learning: 2026-04-15T16:59:32.095Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/storefront-next.mdc:0-0
Timestamp: 2026-04-15T16:59:32.095Z
Learning: Applies to apps/storefront/src/**/*.{ts,tsx} : Talk to Medusa via `medusajs/js-sdk` and the shared client in `src/lib/medusa.ts` (publishable key, `NEXT_PUBLIC_MEDUSA_BACKEND_URL`)
Applied to files:
apps/storefront/src/lib/sitemap-build.tsapps/storefront/src/lib/seo-locale-alternates.tsapps/storefront/src/i18n/published-locales.tsapps/storefront/src/middleware.ts
📚 Learning: 2026-04-15T16:59:32.095Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/storefront-next.mdc:0-0
Timestamp: 2026-04-15T16:59:32.095Z
Learning: Applies to apps/storefront/src/**/*.{ts,tsx} : Custom store endpoints: use `medusa.client.fetch` or SDK extensions consistently; do not scatter raw `fetch` URLs without a single base pattern
Applied to files:
apps/storefront/src/middleware.ts
📚 Learning: 2026-04-15T16:58:57.364Z
Learnt from: CR
Repo: eskoubar95/guapo PR: 0
File: .cursor/rules/medusa-commerce.mdc:0-0
Timestamp: 2026-04-15T16:58:57.364Z
Learning: Applies to apps/commerce/**/*.{ts,tsx,js,jsx} : Protect store/admin routes: use authenticated request types and enforce customer/admin scope.
Applied to files:
apps/storefront/src/middleware.ts
🔇 Additional comments (11)
apps/storefront/env.template (1)
24-28: LGTM — env documentation is clear.The new comments accurately describe the precedence (
PUBLISHED_LOCALESenv overrides Payload'senabledStorefrontLocales) and note the futureSTOREFRONT_LOCALE_SOURCE=medusahook as not-yet-implemented.apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx (1)
46-46: LGTM.Awaiting
buildLocaleAlternates(...)correctly aligns with the helper becoming async.apps/cms/migrations/index.ts (1)
15-15: LGTM.Migration is appended at the end of the array, preserving ordering.
Also applies to: 88-92
apps/storefront/src/lib/sitemap-build.ts (1)
161-171: LGTM — parallelized fetch and locale set derivation look good.
resolvePublishedLocales()is kicked off alongside the other independent fetches viaPromise.all, andsitemapLocaleSetcorrectly filters Payload rows to only published locales. Based on learnings: "Parallelize independent loads: start async work thenPromise.allto avoid sequential waterfalls" — compliant.apps/cms/src/globals/SiteSettings.ts (1)
20-42: LGTM — field + invariant validation is well-scoped.Select field with
hasMany,defaultValue: ['da'], and thevalidateguard enforcing thatdaremains enabled is a sensible guardrail and matches the normalization done inapps/cms/src/app/api/storefront/globals/site-settings/route.ts. Admin description clearly communicates the CMS-wide scope and thePUBLISHED_LOCALESescape hatch.apps/cms/src/app/api/storefront/globals/site-settings/route.ts (1)
30-42: LGTM — defensive normalization mirrors the CMS field contract.Filtering to
'da' | 'en', defaulting to['da']when the source isn't an array, and prepending'da'when missing gives the storefront a stable contract even if admin-side validation is ever bypassed or the underlying data drifts.apps/storefront/src/lib/seo-locale-alternates.ts (1)
27-46: LGTM — alternates now follow the published locale set.The canonical fallback and
languagesmap are correctly derived fromresolvePublishedLocales(), so unpublished locales are excluded from SEO metadata.apps/cms/migrations/20260420_site_settings_enabled_storefront_locales.ts (1)
7-58: LGTM — migration is safely reversible and seeds the default locale.The
uppath creates the backing enum/table and seedsda; thedownpath removes both artifacts.apps/storefront/src/i18n/published-locales.ts (1)
8-76: LGTM — locale resolution has safe fallbacks.The resolver validates inputs, de-dupes values, preserves
defaultLocale, and falls back to[defaultLocale]when external sources are unavailable.apps/storefront/src/middleware.ts (2)
57-94: LGTM — unpublished known locales redirect to the default locale.The cookie preference is clamped to published locales, and known-but-unpublished locale paths preserve the suffix/query while redirecting under
defaultLocale.
14-22: Removecache: "no-store"from middleware fetch for clarity, but middleware always executes per request.The
cacheoption in fetch() controls Next.js Data Cache, which is unavailable in Edge Runtime where middleware runs. Removingcache: "no-store"won't enable response caching because middleware itself executes per request regardless. The endpoint'sCache-Controlheaders govern CDN-level caching, but middleware must still fetch on every request. If the concern is performance, consider using the 1500ms timeout and fallback strategy (already in place) to avoid slowdowns, or pre-setPUBLISHED_LOCALESenvironment variable to skip the fetch entirely.Suggested change
const url = new URL("/api/internal/published-locales", request.nextUrl.origin); const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), PUBLISHED_LOCALES_FETCH_MS); try { const res = await fetch(url.toString(), { signal: controller.signal, headers: { Accept: "application/json" }, - cache: "no-store", });> Likely an incorrect or invalid review comment.
Formål
Merge
staging→mainmed styring af hvilke storefront-URL-lokaler (/da,/en) der er live.Ændringer (højdepunkter)
SiteSettings.enabledStorefrontLocales+ migration for junction/enum.resolvePublishedLocales()— prioritering:PUBLISHED_LOCALESenv → Medusa-stub → Payload → fallbackda.GET /api/internal/published-locales.hreflangog sitemap følger publicerede lokaler;generateStaticParamstilpasset.Drift
20260420_site_settings_enabled_storefront_locales.PUBLISHED_LOCALES=dapå storefront for nød-override.Test
/enredirecter til/danår engelsk ikke er slået til i Payload/envMade with Cursor
Summary by CodeRabbit
Release Notes
New Features
Improvements