Skip to content

feat(storefront,cms): Styring af publicerede storefront-lokaler (kun /da som standard)#34

Merged
eskobar95 merged 2 commits into
mainfrom
staging
Apr 20, 2026
Merged

feat(storefront,cms): Styring af publicerede storefront-lokaler (kun /da som standard)#34
eskobar95 merged 2 commits into
mainfrom
staging

Conversation

@eskobar95
Copy link
Copy Markdown
Owner

@eskobar95 eskobar95 commented Apr 20, 2026

Formål

Merge stagingmain med styring af hvilke storefront-URL-lokaler (/da, /en) der er live.

Ændringer (højdepunkter)

  • Payload: SiteSettings.enabledStorefrontLocales + migration for junction/enum.
  • Storefront: resolvePublishedLocales() — prioritering: PUBLISHED_LOCALES env → Medusa-stub → Payload → fallback da.
  • Middleware: redirect fra ikke-publicerede lokaler; intern GET /api/internal/published-locales.
  • SEO: hreflang og sitemap følger publicerede lokaler; generateStaticParams tilpasset.

Drift

  • Efter merge: kør CMS-migration på miljøer der ikke allerede har kørt 20260420_site_settings_enabled_storefront_locales.
  • Valgfrit: sæt PUBLISHED_LOCALES=da på storefront for nød-override.

Test

  • CI grøn
  • Manuelt: /en redirecter til /da når engelsk ikke er slået til i Payload/env

Made with Cursor

Summary by CodeRabbit

Release Notes

  • New Features

    • Added CMS configuration for enabled storefront locales (Danish and English support).
    • Storefront now dynamically resolves active locales from CMS settings or environment override.
  • Improvements

    • Enhanced SEO metadata with proper locale alternate generation across pages.
    • Improved multi-locale routing and redirect handling based on published locales.

- 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
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 20, 2026

📝 Walkthrough

Walkthrough

Implements dynamic published locale management by adding CMS configuration field and database migration for enabledStorefrontLocales, creating an internal API endpoint for fetching published locales, and updating middleware and page generation to resolve locales dynamically from CMS settings or environment overrides instead of static configuration.

Changes

Cohort / File(s) Summary
CMS Database & Globals
apps/cms/migrations/20260420_site_settings_enabled_storefront_locales.ts, apps/cms/migrations/index.ts, apps/cms/src/globals/SiteSettings.ts, apps/cms/src/app/api/storefront/globals/site-settings/route.ts
Adds migration creating site_settings_enabled_storefront_locales junction table with enum type and seeding; registers migration in index; adds multi-select enabledStorefrontLocales field to SiteSettings with required 'da' validation; updates site-settings API endpoint to include normalized enabledStorefrontLocales in response.
Storefront Locale Resolution
apps/storefront/src/i18n/published-locales.ts, apps/storefront/src/lib/payload-site-settings.ts, apps/storefront/env.template
New module implementing resolvePublishedLocales() with precedence chain: environment override (PUBLISHED_LOCALES), Medusa source (stub), Payload site settings, then default locale fallback; adds helper functions parsePublishedLocalesFromEnv() and coercePublishedLocales() with normalization/deduplication; updates payload site settings type to include optional enabledStorefrontLocales field; documents environment variables in template.
Storefront Page Metadata
apps/storefront/src/app/[locale]/page.tsx, apps/storefront/src/app/[locale]/[...path]/page.tsx, apps/storefront/src/app/[locale]/blog/[slug]/page.tsx, apps/storefront/src/app/[locale]/brands/[handle]/page.tsx, apps/storefront/src/app/[locale]/categories/[handle]/page.tsx, apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx, apps/storefront/src/app/[locale]/products/[handle]/page.tsx, apps/storefront/src/lib/seo-locale-alternates.ts
Converts buildLocaleAlternates() to async function returning promise; updates all generateMetadata functions to await buildLocaleAlternates() call; changes alternate locale computation to depend on dynamic resolvePublishedLocales() instead of static locales list; updates canonical/x-default locale selection logic.
Storefront Static Generation & Routing
apps/storefront/src/app/[locale]/layout.tsx, apps/storefront/src/app/api/internal/published-locales/route.ts, apps/storefront/src/i18n/config.ts, apps/storefront/src/lib/sitemap-build.ts, apps/storefront/src/middleware.ts
Makes generateStaticParams() async, deriving params from resolvePublishedLocales() instead of static locales; adds internal /api/internal/published-locales route returning cached published locales; removes sitemapLocales export; updates sitemap builder to use resolvePublishedLocales(); converts middleware to async with dynamic published locale fetching, request-specific locale validation, and redirect logic for unpublished locales to default locale.

Sequence Diagram

sequenceDiagram
    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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

Poem

🐰 Through verdant fields of locale code we hop,
Dynamic locales now from CMS we drop,
No static lists to cage our storefront flight,
da leads the way, with en shining bright,
Middleware keeps watch as requests flow through! 🌍

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.04% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main feature: managing published storefront locales with /da as default, matching the comprehensive changeset across CMS and storefront.
Description check ✅ Passed The description includes purpose, highlights of key changes, operational instructions, and testing checkpoints, covering most template sections but lacks formal task ID and acceptance criteria structure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch staging

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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-locales suggests private use, but there's no auth gate and Cache-Control: public is 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 generateStaticParams from resolvePublishedLocales() is consistent with the rest of the PR. One operational note: during next build, if PAYLOAD_API_URL is unreachable and PUBLISHED_LOCALES is unset, this falls back to [defaultLocale] only — so enabling en later in the CMS without redeploying/revalidating won't pre-render /en routes. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 17da5b3 and 21f7111.

📒 Files selected for processing (20)
  • apps/cms/migrations/20260420_site_settings_enabled_storefront_locales.ts
  • apps/cms/migrations/index.ts
  • apps/cms/src/app/api/storefront/globals/site-settings/route.ts
  • apps/cms/src/globals/SiteSettings.ts
  • apps/storefront/env.template
  • apps/storefront/src/app/[locale]/[...path]/page.tsx
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/storefront/src/app/[locale]/brands/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/api/internal/published-locales/route.ts
  • apps/storefront/src/i18n/config.ts
  • apps/storefront/src/i18n/published-locales.ts
  • apps/storefront/src/lib/payload-site-settings.ts
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/lib/sitemap-build.ts
  • apps/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 in apps/storefront should 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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/i18n/config.ts
  • apps/storefront/src/app/api/internal/published-locales/route.ts
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/storefront/src/lib/sitemap-build.ts
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/storefront/src/i18n/published-locales.ts
  • apps/storefront/src/lib/payload-site-settings.ts
  • apps/storefront/src/middleware.ts
  • apps/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 bundles

Define a type for const functions when possible

**/*.{ts,tsx}: Use interfaces for object shapes in TypeScript; write functional components with typed props
Avoid enums; prefer as const objects combined with union types
Do not use any type; use unknown with type narrowing or Zod-inferred types for validated data

Files:

  • apps/storefront/src/app/[locale]/[...path]/page.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/i18n/config.ts
  • apps/storefront/src/app/api/internal/published-locales/route.ts
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/cms/src/app/api/storefront/globals/site-settings/route.ts
  • apps/cms/src/globals/SiteSettings.ts
  • apps/storefront/src/lib/sitemap-build.ts
  • apps/cms/migrations/index.ts
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/storefront/src/i18n/published-locales.ts
  • apps/cms/migrations/20260420_site_settings_enabled_storefront_locales.ts
  • apps/storefront/src/lib/payload-site-settings.ts
  • apps/storefront/src/middleware.ts
  • apps/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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/i18n/config.ts
  • apps/storefront/src/app/api/internal/published-locales/route.ts
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/cms/src/app/api/storefront/globals/site-settings/route.ts
  • apps/cms/src/globals/SiteSettings.ts
  • apps/storefront/src/lib/sitemap-build.ts
  • apps/cms/migrations/index.ts
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/storefront/src/i18n/published-locales.ts
  • apps/cms/migrations/20260420_site_settings_enabled_storefront_locales.ts
  • apps/storefront/src/lib/payload-site-settings.ts
  • apps/storefront/src/middleware.ts
  • apps/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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/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 like isLoading, hasError
Use the function keyword for pure functions; write declarative JSX

Files:

  • apps/storefront/src/app/[locale]/[...path]/page.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/i18n/config.ts
  • apps/storefront/src/app/api/internal/published-locales/route.ts
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/cms/src/app/api/storefront/globals/site-settings/route.ts
  • apps/cms/src/globals/SiteSettings.ts
  • apps/storefront/src/lib/sitemap-build.ts
  • apps/cms/migrations/index.ts
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/storefront/src/i18n/published-locales.ts
  • apps/cms/migrations/20260420_site_settings_enabled_storefront_locales.ts
  • apps/storefront/src/lib/payload-site-settings.ts
  • apps/storefront/src/middleware.ts
  • apps/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. Follow code-structure.mdc for 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
Use next/dynamic for heavy or below-the-fold components
Use nuqs library 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 using React.cache()

Files:

  • apps/storefront/src/app/[locale]/[...path]/page.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/i18n/config.ts
  • apps/storefront/src/app/api/internal/published-locales/route.ts
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/cms/src/app/api/storefront/globals/site-settings/route.ts
  • apps/cms/src/globals/SiteSettings.ts
  • apps/storefront/src/lib/sitemap-build.ts
  • apps/cms/migrations/index.ts
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/storefront/src/i18n/published-locales.ts
  • apps/cms/migrations/20260420_site_settings_enabled_storefront_locales.ts
  • apps/storefront/src/lib/payload-site-settings.ts
  • apps/storefront/src/middleware.ts
  • apps/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
Use next/image for 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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/i18n/config.ts
  • apps/storefront/src/app/api/internal/published-locales/route.ts
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/cms/src/app/api/storefront/globals/site-settings/route.ts
  • apps/cms/src/globals/SiteSettings.ts
  • apps/storefront/src/lib/sitemap-build.ts
  • apps/cms/migrations/index.ts
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/storefront/src/i18n/published-locales.ts
  • apps/cms/migrations/20260420_site_settings_enabled_storefront_locales.ts
  • apps/storefront/src/lib/payload-site-settings.ts
  • apps/storefront/src/middleware.ts
  • apps/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-sdk and the shared client in src/lib/medusa.ts (publishable key, NEXT_PUBLIC_MEDUSA_BACKEND_URL)
Custom store endpoints: use medusa.client.fetch or SDK extensions consistently; do not scatter raw fetch URLs without a single base pattern
Payload/content: use existing lib/payload-*.ts helpers; pass locale (da / en) for localized APIs
Parallelize independent loads: start async work then Promise.all to avoid sequential waterfalls
Choose cache explicitly: cache: 'no-store' vs revalidate vs static per data freshness needs
Follow code-structure.mdc for file structure and organization: line budgets, splitting steps/hooks/types, React/TypeScript hygiene

Files:

  • apps/storefront/src/app/[locale]/[...path]/page.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/i18n/config.ts
  • apps/storefront/src/app/api/internal/published-locales/route.ts
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/storefront/src/lib/sitemap-build.ts
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/storefront/src/i18n/published-locales.ts
  • apps/storefront/src/lib/payload-site-settings.ts
  • apps/storefront/src/middleware.ts
  • apps/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): prefer next/dynamic with loading fallback
Minimize props passed from server to client components; serialize only what the client needs

Files:

  • apps/storefront/src/app/[locale]/[...path]/page.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/app/api/internal/published-locales/route.ts
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/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.ts
  • apps/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/cms should 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.ts
  • apps/cms/src/globals/SiteSettings.ts
  • apps/cms/migrations/index.ts
  • apps/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.ts
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/env.template
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/i18n/config.ts
  • apps/storefront/src/app/api/internal/published-locales/route.ts
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/cms/src/app/api/storefront/globals/site-settings/route.ts
  • apps/cms/src/globals/SiteSettings.ts
  • apps/storefront/src/lib/sitemap-build.ts
  • apps/cms/migrations/index.ts
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/storefront/src/i18n/published-locales.ts
  • apps/cms/migrations/20260420_site_settings_enabled_storefront_locales.ts
  • apps/storefront/src/lib/payload-site-settings.ts
  • apps/storefront/src/middleware.ts
  • apps/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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/storefront/src/lib/sitemap-build.ts
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/storefront/src/middleware.ts
  • apps/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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/env.template
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/i18n/config.ts
  • apps/storefront/src/app/api/internal/published-locales/route.ts
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/cms/src/app/api/storefront/globals/site-settings/route.ts
  • apps/cms/src/globals/SiteSettings.ts
  • apps/storefront/src/lib/sitemap-build.ts
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/app/[locale]/blog/[slug]/page.tsx
  • apps/storefront/src/i18n/published-locales.ts
  • apps/cms/migrations/20260420_site_settings_enabled_storefront_locales.ts
  • apps/storefront/src/lib/payload-site-settings.ts
  • apps/storefront/src/middleware.ts
  • apps/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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/storefront/src/lib/sitemap-build.ts
  • apps/storefront/src/app/[locale]/concerns/[handle]/page.tsx
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/middleware.ts
  • apps/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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/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.tsx
  • apps/storefront/src/app/[locale]/products/[handle]/page.tsx
  • apps/storefront/env.template
  • apps/storefront/src/app/[locale]/categories/[handle]/page.tsx
  • apps/storefront/src/app/api/internal/published-locales/route.ts
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/cms/src/app/api/storefront/globals/site-settings/route.ts
  • apps/storefront/src/lib/sitemap-build.ts
  • apps/cms/migrations/index.ts
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/i18n/published-locales.ts
  • apps/cms/migrations/20260420_site_settings_enabled_storefront_locales.ts
  • apps/storefront/src/lib/payload-site-settings.ts
  • apps/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.tsx
  • apps/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.template
  • apps/storefront/src/i18n/config.ts
  • apps/storefront/src/app/[locale]/layout.tsx
  • apps/cms/src/app/api/storefront/globals/site-settings/route.ts
  • apps/cms/src/globals/SiteSettings.ts
  • apps/cms/migrations/index.ts
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/i18n/published-locales.ts
  • apps/cms/migrations/20260420_site_settings_enabled_storefront_locales.ts
  • apps/storefront/src/lib/payload-site-settings.ts
  • 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} : 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.tsx
  • apps/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.ts
  • apps/cms/migrations/20260420_site_settings_enabled_storefront_locales.ts
  • apps/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.ts
  • apps/storefront/src/lib/seo-locale-alternates.ts
  • apps/storefront/src/i18n/published-locales.ts
  • apps/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_LOCALES env overrides Payload's enabledStorefrontLocales) and note the future STOREFRONT_LOCALE_SOURCE=medusa hook 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 via Promise.all, and sitemapLocaleSet correctly filters Payload rows to only published locales. Based on learnings: "Parallelize independent loads: start async work then Promise.all to 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 the validate guard enforcing that da remains enabled is a sensible guardrail and matches the normalization done in apps/cms/src/app/api/storefront/globals/site-settings/route.ts. Admin description clearly communicates the CMS-wide scope and the PUBLISHED_LOCALES escape 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 languages map are correctly derived from resolvePublishedLocales(), 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 up path creates the backing enum/table and seeds da; the down path 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: Remove cache: "no-store" from middleware fetch for clarity, but middleware always executes per request.

The cache option in fetch() controls Next.js Data Cache, which is unavailable in Edge Runtime where middleware runs. Removing cache: "no-store" won't enable response caching because middleware itself executes per request regardless. The endpoint's Cache-Control headers 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-set PUBLISHED_LOCALES environment 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.

@railway-app railway-app Bot temporarily deployed to guapo / staging April 20, 2026 21:25 Inactive
@eskobar95 eskobar95 merged commit 283a706 into main Apr 20, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant