Skip to content

Import Herbatica article widgets into Payload rich text - #511

Closed
tomas-cm1 wants to merge 9 commits into
masterfrom
feat/payload-richtext-widget-seed-flow
Closed

tomas-cm1 wants to merge 9 commits into
masterfrom
feat/payload-richtext-widget-seed-flow

Conversation

@tomas-cm1

@tomas-cm1 tomas-cm1 commented Jul 15, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Convert Herbatica blog XLSX HTML into Payload Lexical rich text seed files
  • Convert ProductWidgets embeds into product/article carousel blocks storing slugs
  • Add Payload admin slug pickers for Medusa products and Payload articles
  • Wire Herbatica seed flow to run Payload seed from herbatica-seed.ts
  • Keep seedHerbatica as a plain medusa exec command and keep local backend prepare on migrate

Usage

  1. Convert XLSX:
    pnpm --filter @nmit/payload exec payload run src/scripts/convert-article-xlsx-html-to-richtext.ts /var/www/local/seed-files/blog_clanky.xlsx /var/www/local/seed-files/blog_clanky.richtext.xlsx
  2. Ensure PAYLOAD_SEED_* env points to Payload DB/S3 and richtext XLSX.
  3. Run:
    pnpm --filter medusa-be run seedHerbatica

This runs the Herbatica Medusa seed and then invokes the Payload seed from the TypeScript seed script. Migrations remain a separate explicit step.

Summary by CodeRabbit

  • New Features
    • Added searchable article and Medusa product selectors to the CMS admin.
    • Added article and product carousel blocks for rich-text content.
    • Enhanced article import to support rich-text media (image fetching/uploading, link/carousel handling, and media URL hydration).
    • Expanded configurable Herbatica product and blog seeding (locales, overwrite/update controls, limits) and added support for seeding via Docker environment variables.
  • Documentation
    • Expanded “Seed local data” guidance, including repeatable/overwrite behaviour and seeding prerequisites.
  • Chores
    • Automatic data seeding on startup is now disabled by default.

Add a Payload XLSX conversion flow for Herbatica blog content. Run the converter with:\n\n  pnpm --filter @nmit/payload exec payload run src/scripts/convert-article-xlsx-html-to-richtext.ts /var/www/local/seed-files/blog_clanky.xlsx /var/www/local/seed-files/blog_clanky.richtext.xlsx\n\nThe converter writes a new richtext XLSX plus media and links manifests, gzip/base64 encodes Payload Lexical JSON for Excel cells, imports image references via media markers, strips hyperlink nodes while exporting link text/URLs, and converts ProductWidgets embeds into Lexical blocks. Product widgets become productCarousel blocks with productSlug values; article widgets become articleCarousel blocks with articleSlug values. Mixed widgets emit both blocks.\n\nAdd Payload blocks and admin pickers for productCarousel.products[].productSlug and articleCarousel.articles[].articleSlug. The pickers search Medusa products and Payload articles through protected endpoints while still storing only slug strings.\n\nUpdate Payload seeding so richtext XLSX content is decoded/imported, media manifests can prepare upload nodes, Herbatica product article seeding remains optional, and Payload container startup no longer seeds by default. The Herbatica Medusa seed script now runs migrations, executes the Medusa Herbatica seed, then invokes the Payload seed with PAYLOAD_SEED_* env mapped to the Payload runtime vars.\n\nTypical local flow:\n\n  1. Convert the XLSX to /var/www/local/seed-files/blog_clanky.richtext.xlsx.\n  2. Ensure PAYLOAD_SEED_* env points at the Payload DB/S3 config and richtext XLSX.\n  3. Run pnpm --filter medusa-be run seedHerbatica.\n\nThat runs Medusa migrations, seeds Herbatica Medusa data, then seeds Payload articles from the converted richtext workbook.
@semanticdiff-com

semanticdiff-com Bot commented Jul 15, 2026 •

Copy link
Copy Markdown

@vercel

vercel Bot commented Jul 15, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
new-engine-ui-storybook Ready Ready Preview, Comment Jul 17, 2026 12:00pm

@coderabbitai

coderabbitai Bot commented Jul 15, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c54cd18a-cfd1-4acf-85c1-77f133ff3dc1

📥 Commits

Reviewing files that changed from the base of the PR and between 1c9cef4 and cb1cc49.

📒 Files selected for processing (4)
  • apps/medusa-be/src/scripts/herbatica-seed.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/payload/src/seed.ts
  • docker-compose.yaml
📜 Recent review details
⚠️ CI failures not shown inline (1)

GitHub Check: Kilo Code Review: Kilo Code Review failed

Conclusion: failure

View job details

Review failed: The message could not be delivered
🧰 Additional context used
📓 Path-based instructions (10)
apps/medusa-be/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/medusa-be/CLAUDE.md)

apps/medusa-be/**/*.{ts,tsx}: Use TypeScript for type checking - run npx tsc --noEmit for validation
Forbidden: Non-null assertions (!) - always use type guards and validation instead
Annotate generic field access with explicit unknown type before type guards - const v: unknown = result[field]
Don't use as Type without validation - always validate before casting
Use Modules.* and ContainerRegistrationKeys.* constants instead of hardcoding strings
Batch operations with CHUNK_SIZE to avoid unbounded operations
Extract pure functions to separate files for testability without runtime dependencies

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
apps/medusa-be/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/medusa-be/CLAUDE.md)

apps/medusa-be/**/*.{ts,tsx,js,jsx}: Use Biome linter with ultracite preset - run bunx biome check --write . and always use braces in conditionals
Comments should explain 'why', never 'what' - use self-documenting code via clear naming
Always use const per declaration - const a = 1; const b = 2 is correct, one variable per line
Use nullish coalescing (??) operator instead of logical OR (||) for default values

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
apps/medusa-be/src/scripts/*.ts

📄 CodeRabbit inference engine (apps/medusa-be/CLAUDE.md)

apps/medusa-be/src/scripts/*.ts: Use medusa exec ./src/scripts/script-name.ts [args] to run one-off scripts with container access
Scripts receive { container, args } - parse args manually or use container.resolve() for services

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Import UI components using the @techsio/ui-kit namespace, not @libs/ui, for runtime apps

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Run Biome linting and formatting only on changed files using 'bunx biome check --write path/to/file'

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
apps/medusa-be/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Medusa backend custom logic should be organized in api/, modules/, workflows/, admin/, subscribers/, and jobs/ directories under apps/medusa-be/src/

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
apps/**/!(medusa-be)/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use modern React patterns and React 19 for frontend applications in the monorepo

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
apps/payload/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/access-control-advanced.md)

apps/payload/**/*.{ts,tsx}: Implement locale-specific access control using Access function to check user authentication and locale, allowing authenticated users all locales and public users only English content
Implement device-specific access control using Access function to check user-agent headers, distinguishing between mobile and desktop access
Create IP-based access restriction using factory function that returns an Access function checking x-forwarded-for or x-real-ip headers against allowed IP list
Implement time-based access control to restrict records to today's date range using Access function with createdAt date comparisons
Create time-based access factory function that returns recent records (last N days) for regular users while granting admins full access
Implement scheduled content access control distinguishing editors/admins who see all content from public users who see only content within publish/unpublish date windows
Implement subscription-based access control using async Access function to verify active subscription status, with admin bypass
Create tier-based subscription access factory function that checks subscription tier hierarchy and returns false on inactive subscriptions or errors
Implement role-based access factory function createRoleBasedAccess that checks if user has any of the specified roles
Implement organization-scoped access factory function createOrgScopedAccess that restricts documents to user's organization IDs with optional admin bypass
Implement team-based access factory function createTeamBasedAccess that filters documents by user's team IDs with admin bypass
Implement time-limited access factory function createTimeLimitedAccess that restricts documents to last N days with admin bypass
Implement public + authenticated collection pattern where create/update/delete are restricted to admins/editors, read shows published content to public and all content to authenticated users
Implement self-service collection...

Files:

  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
apps/payload/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/access-control.md)

apps/payload/**/*.{ts,tsx,js,jsx}: Implement the 'admin only' access pattern in Payload CMS by checking if user roles include 'admin' and returning a boolean result
Implement the 'admin or self' access pattern in Payload CMS by checking admin role first, then falling back to self-referential query constraints (e.g., { id: { equals: user?.id } })
Implement the 'authenticated or published' access pattern in Payload CMS by returning true for authenticated users and applying query constraints for public access to published documents
In Payload CMS Local API calls, set overrideAccess: false when passing a user parameter to ensure access control is enforced rather than bypassed
Do NOT rely on the default Local API behavior in Payload CMS, as access control is skipped by default (overrideAccess: true) - always explicitly set overrideAccess: false when implementing access control

apps/payload/**/*.{ts,tsx,js,jsx}: When performing nested operations in hooks, always pass req parameter to maintain transaction context across all-or-nothing database operations
Pass req to req.payload.find() calls within hooks to maintain atomic transaction context across MongoDB, PostgreSQL, and SQLite databases
Pass req to req.payload.update() calls within hooks to ensure changes are included in the same transaction and can be rolled back together on errors
Use payload.db.beginTransaction(), payload.db.commitTransaction(), and payload.db.rollbackTransaction() for manual transaction control when needed

apps/payload/**/*.{ts,tsx,js,jsx}: Custom endpoints in Payload are not authenticated by default. Always check req.user to verify user authentication before processing requests.
Use req.payload for database operations instead of direct database access to ensure access control and hooks execute properly.
Use the addDataAndFileToRequest helper from Payload to handle both JSON parsing and file uploads in POST endpoints.
Use headersWithCors helper from Payload ...

Files:

  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
apps/payload/**/*.{ts,tsx,js,jsx,graphql}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/queries.md)

Use GraphQL queries with proper field selection, filtering (where), limiting (limit), and sorting (sort) when querying Payload CMS via GraphQL

Files:

  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
🧠 Learnings (1)
📚 Learning: 2026-02-25T14:46:02.729Z
Learnt from: redeyecz
Repo: TechsioCZ/new-engine PR: 335
File: apps/zane-operator/src/db.ts:205-207
Timestamp: 2026-02-25T14:46:02.729Z
Learning: Enforce PostgreSQL 18+ as the minimum version across docker-compose files. Since the repo uses postgres:18.1-alpine (evidence in docker-compose.yaml), ensure all docker-compose service images for PostgreSQL use 18.1-alpine or newer. When reviewing, look for postgres images with a tag below 18 (e.g., postgres:<older-version>) and update to 18.1-alpine or a newer compatible tag. This guideline applies to all docker-compose YAML files that define PostgreSQL services.

Applied to files:

  • docker-compose.yaml
🪛 ast-grep (0.44.1)
apps/medusa-be/src/scripts/herbatica-seed.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from "node:child_process"
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🔇 Additional comments (4)
apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts (1)

569-570: LGTM!

apps/payload/src/seed.ts (1)

10-10: LGTM!

Also applies to: 80-80, 677-680

docker-compose.yaml (1)

49-49: LGTM!

Also applies to: 265-265

apps/medusa-be/src/scripts/herbatica-seed.ts (1)

3506-3506: LGTM!


Walkthrough

Payload adds article and product carousel editing, authenticated admin pickers, XLSX-to-Lexical conversion with media hydration, repeatable Herbatica seeding, and Docker/Medusa wiring for configurable Payload seed execution.

Changes

Payload content and seeding

Layer / File(s) Summary
Carousel blocks and editor registration
apps/payload/src/lib/blocks/*, apps/payload/src/lib/editors/lexical.ts
Lexical registers article and product carousel blocks with custom slug fields and article slug validation.
Admin slug pickers and lookup endpoints
apps/payload/src/components/admin/*, apps/payload/src/lib/endpoints/*, apps/payload/src/lib/utils/endpoint.ts, apps/payload/src/payload.config.ts, apps/payload/src/app/(payload)/importMap.js
Authenticated article and Medusa product lookup endpoints support debounced searchable admin selectors.
Rich-text conversion and media import
apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts, apps/payload/src/scripts/import-articles.ts
XLSX HTML is converted to compressed Lexical content with carousel and media manifests, which the importer uploads and hydrates into Payload records.
Herbatica seed ingestion
apps/payload/src/seed.ts, apps/payload/README.md
Repeatable slug-based seeding imports Herbatica product XML and optional blog article XLSX content.
Seed configuration and execution wiring
.env.docker, docker-compose.yaml, docker/development/payload/payload-runtime.sh, apps/medusa-be/src/scripts/herbatica-seed.ts
Environment blocks define seed inputs and controls, startup seeding defaults to disabled, and the Herbatica workflow invokes Payload seeding as a child process.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant XLSXConverter
  participant importArticles
  participant PayloadMedia
  XLSXConverter->>importArticles: Pass compressed Lexical content and media manifest
  importArticles->>PayloadMedia: Fetch and upsert referenced images
  PayloadMedia-->>importArticles: Return media IDs
  importArticles-->>importArticles: Replace media URL tokens with relations
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: importing Herbatica article widgets into Payload rich text.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ 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 feat/payload-richtext-widget-seed-flow
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/payload-richtext-widget-seed-flow

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


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.

@greptile-apps

greptile-apps Bot commented Jul 15, 2026 •

Copy link
Copy Markdown

Greptile Summary

This PR adds a Payload rich-text import and Herbatica seed flow. The main changes are:

  • Converts XLSX HTML into Lexical content with media and carousel blocks.
  • Adds product and article carousel pickers in Payload admin.
  • Imports media manifests during article seeding.
  • Runs the Payload seed after the Herbatica Medusa seed.
  • Aligns default locale handling across the seed flow.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Important Files Changed

Filename Overview
apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts Converts article HTML into Lexical rich text with media and carousel nodes.
apps/payload/src/scripts/import-articles.ts Imports converted content and hydrates referenced media uploads.
apps/payload/src/seed.ts Adds Herbatica product and XLSX article seed support.
apps/medusa-be/src/scripts/herbatica-seed.ts Runs the Payload seed with the required environment mapping.

Reviews (9): Last reviewed commit: "fix(payload): forward seed locale config..." | Re-trigger Greptile

Comment thread apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts Outdated
Comment thread apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts Outdated
Comment thread apps/payload/src/payload-types.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 15

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/payload/src/lib/blocks/article-carousel.ts`:
- Around line 13-29: Add validation to the articleSlug field within the articles
array so submitted slugs are checked against existing articles during save,
using the Payload validation context to query the articles collection and return
a clear validation error when no match exists. Preserve the existing required
text-field and ArticleSlugField admin configuration.

In `@apps/payload/src/lib/endpoints/article-options.ts`:
- Around line 15-34: Move DEFAULT_LIMIT, MAX_LIMIT, parseLimit, and isAuthorized
from article-options.ts into ../utils/endpoint and export them alongside the
existing endpoint utilities. In
apps/payload/src/lib/endpoints/article-options.ts (lines 15-34), import and use
the shared symbols; in apps/payload/src/lib/endpoints/medusa-products.ts (lines
15-16 and 31-47), remove the duplicate constants and helper implementations and
import the shared symbols instead.
- Around line 66-75: Update the articles query in the find call to include a
select configuration containing only id, slug, title, and featuredImage.url,
while preserving the existing filters, sorting, localization, and pagination
options.

In `@apps/payload/src/lib/endpoints/medusa-products.ts`:
- Around line 70-75: Update the Medusa product fetch around the fetch call to
add a fallback timeout when the provided signal is absent or invalid. Preserve
req.signal cancellation when it is a valid AbortSignal, and ensure the request
is aborted after a bounded timeout so an unresponsive Medusa backend cannot hang
indefinitely.

In `@apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts`:
- Around line 234-240: Update fetchProductWidgetData and its callers to bound
remote requests with an explicit timeout or abort signal, and handle resolution
failures without replacing the widget with empty content: either propagate the
error to fail the cell conversion or preserve the original widget markup for
retry. Apply the same behavior to the related conversion flow around the
referenced widget-processing logic.
- Around line 472-479: Update the record.type === "link" branch in
sanitizeLexicalNode to preserve the hyperlink node in the returned Lexical
structure, or implement the corresponding link hydration path consumed by
import-articles.ts; do not return only sanitized children while merely recording
the URL in context.linkManifest. Ensure seeded articles retain functional links
rather than plain text.
- Around line 18-25: Replace the ad-hoc FieldLike type and recursive editor
traversal with Payload’s fieldAffectsData and fieldHasSubFields guards in the
editor lookup logic. Preserve the explicit traversal of tab fields, and use the
guards to safely identify data-affecting fields and recurse through supported
subfields in the existing conversion flow.

In `@apps/payload/src/scripts/import-articles.ts`:
- Around line 303-310: Update decodeRichTextValue and the import flow around its
callers so prefixed values that fail Base64, gzip, or JSON decoding are treated
as malformed and reported or skipped, never returned as visible plain text.
Preserve the existing plain-text fallback only for values without
RICH_TEXT_GZIP_PREFIX, and ensure malformed encoded rows do not continue through
article import.
- Around line 1051-1066: Update the excerpt fallback in the article data
construction to decode the compressed prefixed content once, extract its plain
Lexical text, and use the first 300 characters of that decoded text when no
excerpt column is present. Keep the existing column-based excerpt precedence and
pass the decoded content through the existing rich-text conversion flow without
repeatedly decoding it.
- Around line 344-375: Harden fetchMediaBuffer before downloading manifest
media: validate the initial URL and every redirect, reject loopback, private,
link-local, and cloud-metadata hosts or resolved addresses, and enforce a
request timeout. Stream the response with a strict maximum byte limit instead of
fully buffering unbounded data, and accept only supported image content types
after validating the final response.
- Around line 420-468: Update hydrateRichTextMedia to detect every media URL
absent from mediaUrlMap instead of substituting fallbackMediaId, and expose
those unresolved URLs to its caller. In the article import flow, use that result
to report each unresolved URL and fail or skip the affected article before
persistence. Preserve successful hydration for mapped media and the existing
placeholder behavior only where it is not an unresolved manifest URL.

In `@apps/payload/src/seed.ts`:
- Around line 557-560: Update getProductDescription so an empty decoded
DESCRIPTION is treated as missing and falls back to SHORT_DESCRIPTION before
returning the final empty string. Preserve the existing HTML extraction and
decoding behavior while ensuring the fallback occurs for both nullish and empty
DESCRIPTION results.
- Around line 283-305: Update upsertBySlug to use Payload’s CollectionSlug and
DataFromCollectionSlug generics, typing collection and data from the collection
slug so payload.update and payload.create no longer require as never casts.
Preserve the existing find, update, and create behavior while enforcing
generated schema types for the collection and seed data.
- Around line 260-274: Limit lookup projections to the fields actually consumed
by adding depth: 0 and a minimal id-only select to findOne in
apps/payload/src/seed.ts (lines 260-274), findSeedMedia in
apps/payload/src/seed.ts (lines 356-370), and the media filename lookup in
apps/payload/src/scripts/import-articles.ts (lines 383-393); preserve each
lookup’s existing filtering and result handling.

In `@docker-compose.yaml`:
- Around line 45-59: Complete Payload seed configuration propagation across
docker-compose.yaml lines 45-59, .env.docker lines 97-100, and
apps/medusa-be/package.json line 54. Add the missing article status, overwrite,
translate, sheet, product-enable, and limit mappings/templates, then explicitly
forward them in seedHerbatica or remove redundant inline assignments so
identically named PAYLOAD_SEED_* variables are inherited automatically.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a3da424a-2fe8-4710-a5ca-b1d8710ffe66

📥 Commits

Reviewing files that changed from the base of the PR and between 2da270d and b34ce67.

📒 Files selected for processing (19)
  • .env.docker
  • .mise.toml
  • apps/medusa-be/package.json
  • apps/payload/README.md
  • apps/payload/src/app/(payload)/importMap.js
  • apps/payload/src/components/admin/article-slug-field.tsx
  • apps/payload/src/components/admin/medusa-product-slug-field.tsx
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/blocks/product-carousel.ts
  • apps/payload/src/lib/editors/lexical.ts
  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/payload-types.ts
  • apps/payload/src/payload.config.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
  • docker-compose.yaml
  • docker/development/payload/payload-runtime.sh
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Greptile Review
  • GitHub Check: main
  • GitHub Check: Analyze (javascript-typescript)
⚠️ CI failures not shown inline (1)

GitHub Check: Kilo Code Review: Kilo Code Review failed

Conclusion: failure

View job details

Review failed: The message could not be delivered
🧰 Additional context used
📓 Path-based instructions (14)
apps/payload/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/access-control.md)

apps/payload/**/*.{ts,tsx,js,jsx}: Implement the 'admin only' access pattern in Payload CMS by checking if user roles include 'admin' and returning a boolean result
Implement the 'admin or self' access pattern in Payload CMS by checking admin role first, then falling back to self-referential query constraints (e.g., { id: { equals: user?.id } })
Implement the 'authenticated or published' access pattern in Payload CMS by returning true for authenticated users and applying query constraints for public access to published documents
In Payload CMS Local API calls, set overrideAccess: false when passing a user parameter to ensure access control is enforced rather than bypassed
Do NOT rely on the default Local API behavior in Payload CMS, as access control is skipped by default (overrideAccess: true) - always explicitly set overrideAccess: false when implementing access control

apps/payload/**/*.{ts,tsx,js,jsx}: When performing nested operations in hooks, always pass req parameter to maintain transaction context across all-or-nothing database operations
Pass req to req.payload.find() calls within hooks to maintain atomic transaction context across MongoDB, PostgreSQL, and SQLite databases
Pass req to req.payload.update() calls within hooks to ensure changes are included in the same transaction and can be rolled back together on errors
Use payload.db.beginTransaction(), payload.db.commitTransaction(), and payload.db.rollbackTransaction() for manual transaction control when needed

apps/payload/**/*.{ts,tsx,js,jsx}: Custom endpoints in Payload are not authenticated by default. Always check req.user to verify user authentication before processing requests.
Use req.payload for database operations instead of direct database access to ensure access control and hooks execute properly.
Use the addDataAndFileToRequest helper from Payload to handle both JSON parsing and file uploads in POST endpoints.
Use headersWithCors helper from Payload ...

Files:

  • apps/payload/src/app/(payload)/importMap.js
  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/lib/editors/lexical.ts
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/blocks/product-carousel.ts
  • apps/payload/src/components/admin/article-slug-field.tsx
  • apps/payload/src/payload.config.ts
  • apps/payload/src/components/admin/medusa-product-slug-field.tsx
  • apps/payload/src/payload-types.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/import-articles.ts
apps/payload/**/*.{ts,tsx,js,jsx,graphql}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/queries.md)

Use GraphQL queries with proper field selection, filtering (where), limiting (limit), and sorting (sort) when querying Payload CMS via GraphQL

Files:

  • apps/payload/src/app/(payload)/importMap.js
  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/lib/editors/lexical.ts
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/blocks/product-carousel.ts
  • apps/payload/src/components/admin/article-slug-field.tsx
  • apps/payload/src/payload.config.ts
  • apps/payload/src/components/admin/medusa-product-slug-field.tsx
  • apps/payload/src/payload-types.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/import-articles.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Run Biome linting and formatting only on changed files using 'bunx biome check --write path/to/file'

Files:

  • apps/payload/src/app/(payload)/importMap.js
  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/lib/editors/lexical.ts
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/blocks/product-carousel.ts
  • apps/payload/src/components/admin/article-slug-field.tsx
  • apps/payload/src/payload.config.ts
  • apps/payload/src/components/admin/medusa-product-slug-field.tsx
  • apps/payload/src/payload-types.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/import-articles.ts
apps/payload/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/access-control-advanced.md)

apps/payload/**/*.{ts,tsx}: Implement locale-specific access control using Access function to check user authentication and locale, allowing authenticated users all locales and public users only English content
Implement device-specific access control using Access function to check user-agent headers, distinguishing between mobile and desktop access
Create IP-based access restriction using factory function that returns an Access function checking x-forwarded-for or x-real-ip headers against allowed IP list
Implement time-based access control to restrict records to today's date range using Access function with createdAt date comparisons
Create time-based access factory function that returns recent records (last N days) for regular users while granting admins full access
Implement scheduled content access control distinguishing editors/admins who see all content from public users who see only content within publish/unpublish date windows
Implement subscription-based access control using async Access function to verify active subscription status, with admin bypass
Create tier-based subscription access factory function that checks subscription tier hierarchy and returns false on inactive subscriptions or errors
Implement role-based access factory function createRoleBasedAccess that checks if user has any of the specified roles
Implement organization-scoped access factory function createOrgScopedAccess that restricts documents to user's organization IDs with optional admin bypass
Implement team-based access factory function createTeamBasedAccess that filters documents by user's team IDs with admin bypass
Implement time-limited access factory function createTimeLimitedAccess that restricts documents to last N days with admin bypass
Implement public + authenticated collection pattern where create/update/delete are restricted to admins/editors, read shows published content to public and all content to authenticated users
Implement self-service collection...

Files:

  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/lib/editors/lexical.ts
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/blocks/product-carousel.ts
  • apps/payload/src/components/admin/article-slug-field.tsx
  • apps/payload/src/payload.config.ts
  • apps/payload/src/components/admin/medusa-product-slug-field.tsx
  • apps/payload/src/payload-types.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/import-articles.ts
apps/payload/**/endpoints/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/payload/AGENTS.md)

apps/payload/**/endpoints/**/*.{ts,tsx}: Always check authentication in custom endpoints before performing operations
Use APIError for proper error responses in custom endpoints
Use req.payload for database operations within custom endpoints

Files:

  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Import UI components using the @techsio/ui-kit namespace, not @libs/ui, for runtime apps

Files:

  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/lib/editors/lexical.ts
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/blocks/product-carousel.ts
  • apps/payload/src/components/admin/article-slug-field.tsx
  • apps/payload/src/payload.config.ts
  • apps/payload/src/components/admin/medusa-product-slug-field.tsx
  • apps/payload/src/payload-types.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/import-articles.ts
apps/**/!(medusa-be)/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use modern React patterns and React 19 for frontend applications in the monorepo

Files:

  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/lib/editors/lexical.ts
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/blocks/product-carousel.ts
  • apps/payload/src/components/admin/article-slug-field.tsx
  • apps/payload/src/payload.config.ts
  • apps/payload/src/components/admin/medusa-product-slug-field.tsx
  • apps/payload/src/payload-types.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/import-articles.ts
**/package.json

📄 CodeRabbit inference engine (CLAUDE.md)

Use pnpm CLI to add dependencies; never edit package.json directly

Files:

  • apps/medusa-be/package.json
apps/payload/**/components/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/components.md)

apps/payload/**/components/**/*.{ts,tsx,js,jsx}: Use 'use client' directive for components requiring interactivity, hooks, state management, or event handlers in Payload CMS admin panel
Server Components (default) can use Local API directly, perform async operations, and access full Payload instance in Payload CMS custom components
Client Components in Payload CMS cannot receive non-serializable props (functions, class instances, etc.); use clientProps for serializable data and serverProps for non-serializable data
Use 'useFormFields' for optimized re-renders when accessing specific field values in Payload CMS, instead of useForm which re-renders on any field change
In Admin Panel UI, import from '@payloadcms/ui' for Payload components; in frontend code, use specific paths from '@payloadcms/ui/elements' to minimize bundle size
Use Server Components by default for Payload CMS custom components; only use Client Components when needing state, effects, event handlers, or browser APIs
Use useDocumentInfo hook in Payload CMS Client Components to access current document info (id, slug, collection)
Use useAuth hook in Payload CMS Client Components to access current user information within the admin panel
Use useTranslation hook in Payload CMS Client Components to access i18n translations and language information
Use getTranslation utility in Payload CMS Server Components to translate content using the i18n object
Use React.memo() for expensive Payload CMS custom components, implement proper key props in lists, and avoid inline function definitions in renders
All custom components receive default props (payload, i18n, locale) automatically from Payload CMS; access via component parameters or hooks

Files:

  • apps/payload/src/components/admin/article-slug-field.tsx
  • apps/payload/src/components/admin/medusa-product-slug-field.tsx
apps/payload/**/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/components.md)

Use TypeScript types from Payload (TextFieldServerComponent, TextFieldClientComponent, TextFieldCellComponent) to ensure type safety for custom components

apps/payload/**/components/**/*.{ts,tsx}: All components in the Admin Panel are Server Components by default; use 'use client' directive only when state or browser APIs are needed
Optimize re-renders in Client Components by using useFormFields instead of useForm for single field values
Import Payload UI components from '@payloadcms/ui' in Admin Panel components

Files:

  • apps/payload/src/components/admin/article-slug-field.tsx
  • apps/payload/src/components/admin/medusa-product-slug-field.tsx
apps/payload/**/*payload*.config.{ts,js}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/adapters.md)

apps/payload/**/*payload*.config.{ts,js}: Use mongooseAdapter from '@payloadcms/db-mongodb' for MongoDB database configuration in Payload CMS with DATABASE_URL environment variable
Use postgresAdapter from '@payloadcms/db-postgres' for PostgreSQL configuration with pool.connectionString, setting push to false, and specifying migrationDir
Use sqliteAdapter from '@payloadcms/db-sqlite' for SQLite configuration with client.url and enable transactions via transactionOptions property
Use s3Storage from '@payloadcms/storage-s3' for AWS S3 file storage configuration with bucket name and AWS credentials from environment variables
Consider available storage adapters: @payloadcms/storage-s3, @payloadcms/storage-azure, @payloadcms/storage-gcs, @payloadcms/storage-r2, @payloadcms/storage-vercel-blob, @payloadcms/storage-uploadthing
Use nodemailerAdapter from '@payloadcms/email-nodemailer' for SMTP-based email configuration with defaultFromAddress, defaultFromName, and transportOptions (host, port, auth)
Use resendAdapter from '@payloadcms/email-resend' for email configuration via Resend service with apiKey from environment variable
SQLite transactions are disabled by default and must be explicitly enabled via transactionOptions configuration

Files:

  • apps/payload/src/payload.config.ts
apps/payload/**/payload.config.{ts,js}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/components.md)

apps/payload/**/payload.config.{ts,js}: Define custom components using file paths (not direct imports) in Payload CMS admin config to keep the config lightweight and Node.js compatible
Component paths must be relative to project root or config.admin.importMap.baseDir, with named exports using '#ExportName' suffix or exportName property
Set baseDir in admin.importMap config to establish a custom base directory for resolving component paths in Payload CMS
Use clientProps in component config for serializable custom props and serverProps for non-serializable custom props in Payload CMS
Verify component file paths are correct relative to baseDir and use named export syntax '#ExportName' when needed to resolve component loading issues in Payload CMS

Files:

  • apps/payload/src/payload.config.ts
apps/payload/**/payload.config.ts

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/payload-overview.md)

apps/payload/**/payload.config.ts: Use storage adapter plugins for cloud storage
Use buildConfig from 'payload' to configure the Payload CMS instance
Use mongooseAdapter from '@payloadcms/db-mongodb' for MongoDB database configuration
Use lexicalEditor from '@payloadcms/richtext-lexical' for rich text editing
Configure typescript.outputFile to generate payload-types.ts for type safety
Configure admin.importMap.baseDir in payload config to enable proper component imports

Files:

  • apps/payload/src/payload.config.ts
apps/payload/**/payload.config.{ts,tsx}

📄 CodeRabbit inference engine (apps/payload/AGENTS.md)

apps/payload/**/payload.config.{ts,tsx}: Use file paths (not direct imports) to define components in the Payload config, with proper export suffixes
Enable transactions in SQLite using transactionOptions: {}

Files:

  • apps/payload/src/payload.config.ts
🧠 Learnings (6)
📚 Learning: 2026-07-13T10:43:17.745Z
Learnt from: Luko248
Repo: TechsioCZ/new-engine PR: 510
File: libs/ui/agent-plugin/scripts/pre-push-validate-gate.mjs:0-0
Timestamp: 2026-07-13T10:43:17.745Z
Learning: In Node scripts, treat command strings built with `child_process.execSync(...)` as a potential shell-injection risk when any part of the command comes from untrusted or agent/LLM/remote input (e.g., ref/branch names like `tool_input.command`). Do not interpolate untrusted values into an `execSync` shell command string (and avoid relying on `shellQuote` workarounds). Instead, execute the program directly with argv arrays (e.g., `execFileSync('git', ['rev-parse', ref, ...], { stdio: ... })` or `spawn/execFile` with `args`), passing each argument as a separate array element at every call site so no shell parsing is involved.

Applied to files:

  • apps/payload/src/app/(payload)/importMap.js
📚 Learning: 2026-02-05T14:43:17.404Z
Learnt from: KaiUweCZE
Repo: NMIT-WR/new-engine PR: 324
File: apps/medusa-be/package.json:0-0
Timestamp: 2026-02-05T14:43:17.404Z
Learning: Validate and enforce React 19 compatibility across monorepo workspaces. Since Medusa UI supports React 19 via root package.json overrides and Medusa Cloud prerequisites show React 19 overrides for npm workspaces, ensure workspace root and all relevant package.json files align with React 19 (18+ requirement is satisfied). When reviewing, verify that overrides exist in the root package.json and that dependent packages in apps or packages directories declare React 19 (or compatible) in their peerDependencies or dependencies as appropriate for workspace usage.

Applied to files:

  • apps/medusa-be/package.json
📚 Learning: 2026-05-07T19:05:58.339Z
Learnt from: redeyecz
Repo: TechsioCZ/new-engine PR: 390
File: apps/medusa-be/package.json:78-81
Timestamp: 2026-05-07T19:05:58.339Z
Learning: When reviewing changes to `package.json`, do not automatically flag dependency additions/removals as "manually edited" or as "bypassing the pnpm lockfile" just because the `package.json` diff shows only that file changed. First verify whether `pnpm-lock.yaml` is missing the corresponding entries. Since `pnpm add` updates both `package.json` and `pnpm-lock.yaml` together, legitimate changes can appear in the `package.json` diff while still being properly tracked in the lockfile.

Applied to files:

  • apps/medusa-be/package.json
📚 Learning: 2026-05-20T15:58:53.048Z
Learnt from: redeyecz
Repo: TechsioCZ/new-engine PR: 413
File: apps/medusa-be/package.json:77-80
Timestamp: 2026-05-20T15:58:53.048Z
Learning: When reviewing monorepo `package.json` files, treat any dependencies/devDependencies using the `paykit-sdk/*` scope (e.g., `paykit-sdk/core`, `paykit-sdk/stripe`, `paykit-sdk/comgate`, `paykit-sdk/gopay`) as coming from the TechsioCZ/new-engine private npm registry. Do not flag dependency version constraints (e.g., `^1.2.0`) as invalid merely because those packages/versions are not found on the public npm registry. Public-web/private-web availability checks against the public npm API are not applicable for these packages; if validation is needed, rely on the private registry/CI install behavior instead.

Applied to files:

  • apps/medusa-be/package.json
📚 Learning: 2025-12-16T19:45:17.746Z
Learnt from: BleedingDev
Repo: NMIT-WR/new-engine PR: 207
File: libs/ui/src/molecules/select.tsx:50-50
Timestamp: 2025-12-16T19:45:17.746Z
Learning: When reviewing Tailwind classes in TSX/TS files, prefer using square brackets for arbitrary CSS values and complex expressions. Specifically: - Do not use the parentheses syntax (z-(--z-index)) for anything beyond simple CSS variable references; this syntax auto-wraps in var() and cannot handle calc or complex functions. - Use the square brackets syntax (e.g., h-[calc(var(--available-height)-var(--spacing-content))]) for calc expressions, var with calc, and any complex CSS expressions. This rule applies broadly to Tailwind v4 usage in TSX code across the project.

Applied to files:

  • apps/payload/src/components/admin/article-slug-field.tsx
  • apps/payload/src/components/admin/medusa-product-slug-field.tsx
📚 Learning: 2026-02-25T14:46:02.729Z
Learnt from: redeyecz
Repo: TechsioCZ/new-engine PR: 335
File: apps/zane-operator/src/db.ts:205-207
Timestamp: 2026-02-25T14:46:02.729Z
Learning: Enforce PostgreSQL 18+ as the minimum version across docker-compose files. Since the repo uses postgres:18.1-alpine (evidence in docker-compose.yaml), ensure all docker-compose service images for PostgreSQL use 18.1-alpine or newer. When reviewing, look for postgres images with a tag below 18 (e.g., postgres:<older-version>) and update to 18.1-alpine or a newer compatible tag. This guideline applies to all docker-compose YAML files that define PostgreSQL services.

Applied to files:

  • docker-compose.yaml
🪛 ast-grep (0.44.1)
apps/payload/src/seed.ts

[warning] 201-201: Do not use weak hash functions (MD5/SHA1)
Context: createHash("sha1")
Note: [CWE-328] Use of Weak Hash.

(insecure-hash-typescript)


[warning] 201-201: Avoid SHA1 security protocol
Context: createHash("sha1")
Note: [CWE-327] Use of a Broken or Risky Cryptographic Algorithm (SHA-1).

(avoid-crypto-sha1-typescript)


[warning] 165-165: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(<${tag}(\\s[^>]*)?>([\\s\\S]*?)<\\/${tag}>, "g")
Note: [CWE-1333] Inefficient Regular Expression Complexity

(regexp-from-variable)


[warning] 173-173: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(<${tag}(?:\\s[^>]*)?>([\\s\\S]*?)<\\/${tag}>)
Note: [CWE-1333] Inefficient Regular Expression Complexity

(regexp-from-variable)

apps/payload/src/scripts/import-articles.ts

[warning] 336-336: Avoid SHA1 security protocol
Context: createHash("sha1")
Note: [CWE-327] Use of a Broken or Risky Cryptographic Algorithm (SHA-1).

(avoid-crypto-sha1-typescript)


[warning] 336-336: Do not use weak hash functions (MD5/SHA1)
Context: createHash("sha1")
Note: [CWE-328] Use of Weak Hash.

(insecure-hash-typescript)

🪛 dotenv-linter (4.0.0)
.env.docker

[warning] 97-97: [UnorderedKey] The DC_HERBATICA_BLOG_ARTICLES_XLSX_PATH key should go before the DC_HERBATICA_CATEGORIES_XML_PATH key

(UnorderedKey)


[warning] 100-100: [UnorderedKey] The DC_PAYLOAD_SEED_ARTICLES_OVERWRITE key should go before the DC_PAYLOAD_SEED_ARTICLES_STATUS key

(UnorderedKey)

🔇 Additional comments (17)
.mise.toml (1)

270-270: LGTM!

apps/payload/README.md (1)

62-89: LGTM!

docker-compose.yaml (1)

245-261: LGTM!

docker/development/payload/payload-runtime.sh (1)

28-28: LGTM!

apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts (1)

1-17: LGTM!

Also applies to: 26-131, 154-233, 241-268, 318-471, 480-653

apps/payload/src/scripts/import-articles.ts (1)

1-296: LGTM!

Also applies to: 297-302, 311-343, 376-382, 394-419, 469-493, 511-1050, 1067-1221

apps/payload/src/seed.ts (1)

2-259: LGTM!

Also applies to: 275-282, 306-355, 371-556, 561-780

apps/payload/src/components/admin/article-slug-field.tsx (2)

65-116: Extract shared logic with MedusaProductSlugField.

See consolidated comment for the cross-file DRY refactor covering this file and medusa-product-slug-field.tsx.


70-72: Verify potentiallyStalePath is a valid useField option.

See consolidated comment for the cross-file verification covering this file and medusa-product-slug-field.tsx.

apps/payload/src/components/admin/medusa-product-slug-field.tsx (2)

60-109: Extract shared logic with ArticleSlugField.

See consolidated comment for the cross-file DRY refactor covering this file and article-slug-field.tsx.


65-67: Verify potentiallyStalePath is a valid useField option.

See consolidated comment for the cross-file verification covering this file and article-slug-field.tsx.

apps/payload/src/app/(payload)/importMap.js (1)

9-11: LGTM!

Also applies to: 49-51

apps/payload/src/lib/editors/lexical.ts (1)

4-4: LGTM!

Also applies to: 26-27, 58-60

apps/payload/src/lib/blocks/product-carousel.ts (1)

1-32: LGTM!

apps/payload/src/payload.config.ts (1)

29-31: LGTM!

Also applies to: 83-84

apps/payload/src/payload-types.ts (1)

101-108: LGTM!

apps/payload/src/lib/endpoints/article-options.ts (1)

27-34: 🔒 Security & Privacy

Access control already matches the collection rules

article-options is already restricted to authenticated users/admin API-key callers, and articles read access is requireAuth, so this does not bypass collection access control.

			> Likely an incorrect or invalid review comment.

Comment thread apps/payload/src/lib/blocks/article-carousel.ts
Comment thread apps/payload/src/lib/endpoints/article-options.ts Outdated
Comment thread apps/payload/src/lib/endpoints/article-options.ts
Comment thread apps/payload/src/lib/endpoints/medusa-products.ts
Comment thread apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts Outdated
Comment thread apps/payload/src/scripts/import-articles.ts Outdated
Comment thread apps/payload/src/seed.ts
Comment thread apps/payload/src/seed.ts Outdated
Comment thread apps/payload/src/seed.ts Outdated
Comment thread docker-compose.yaml
Comment thread apps/payload/src/scripts/import-articles.ts Outdated
Comment thread apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
Comment thread apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts (1)

319-325: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

fix(converter): abort when a widget cannot be resolved

The catch retains markup only in htmlWithCarouselTokens; the cell is still overwritten with converted Lexical content, so the original widget cannot actually be retried. A successful response with no recognised slugs also replaces it with an empty string. Throw or skip the cell whenever resolution produces no carousel.

Proposed fix
+      if (replacementParts.length === 0) {
+        throw new Error("Widget contained no recognised product or article links")
+      }
+
       output = output.replace(match[0], replacementParts.join(""))
     } catch (error) {
-      console.warn(
-        `Failed to import product widget ${widgetUrl}: ${error instanceof Error ? error.message : String(error)}`
-      )
-      // Keep the original widget markup so the import can be retried later.
+      throw new Error(
+        `Failed to import product widget ${widgetUrl}: ${error instanceof Error ? error.message : String(error)}`
+      )
     }

Also applies to: 664-684

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts` around
lines 319 - 325, Update the widget conversion flow around the catch handling and
carousel resolution so unresolved widgets abort or skip replacement of the
affected cell instead of writing converted Lexical content. Preserve the
original widget markup when resolution fails, including successful responses
with no recognized slugs, and only overwrite the cell when a carousel is
actually produced; use the surrounding conversion function and its
htmlWithCarouselTokens handling to locate both paths.
♻️ Duplicate comments (1)
apps/payload/src/scripts/import-articles.ts (1)

410-426: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

fix(import): bind the fetch to the validated DNS result

lookup() validates one resolution, but fetch(currentUrl) resolves the hostname again. An attacker-controlled hostname can use DNS rebinding to return a public address during validation and a private address during connection. Pin the validated address through a custom resolver/dispatcher, or restrict imports to an explicit host allow-list.

#!/bin/bash
set -euo pipefail

file='apps/payload/src/scripts/import-articles.ts'
sed -n '383,519p' "$file"
rg -n -C3 '"undici"|dispatcher|connect:|lookup:' apps/payload package.json bun.lock 2>/dev/null || true

Also applies to: 472-487

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/payload/src/scripts/import-articles.ts` around lines 410 - 426, The
media import flow around assertSafeMediaUrl and the fetch call must prevent DNS
rebinding by connecting to the address validated during lookup. Configure the
request with a custom resolver/dispatcher that pins the validated address, or
enforce an explicit host allow-list, and apply the same protection to the
additional fetch path around the referenced import logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/medusa-be/src/scripts/herbatica-seed.ts`:
- Around line 3530-3552: Update buildPayloadSeedEnv and the
PAYLOAD_SEED_ENV_MAPPINGS loop so mapped variables are copied only when their
source values exist in process.env, rather than calling
getRequiredPayloadSeedEnv for every entry. Preserve omission of undefined
optional values so Payload applies its defaults, while retaining
required-variable validation such as DATABASE_URL through the appropriate native
seed handling.

In `@apps/payload/src/lib/blocks/article-carousel.ts`:
- Around line 11-22: Update the req.payload.find configuration in the article
lookup to include the current req object, preserving the existing query filters
and options so the operation inherits the active transaction, locale, and user
context.

In `@apps/payload/src/lib/endpoints/medusa-products.ts`:
- Around line 48-50: Update resolveFetchSignal so a provided valid AbortSignal
is combined with the 10-second timeout signal via AbortSignal.any() where
supported, ensuring either signal aborts the fetch; retain the timeout-only
fallback for invalid or absent signals and preserve compatibility when
AbortSignal.any() is unavailable.

In `@apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts`:
- Around line 365-369: Update the fallback logic across all listed sites to
preserve empty-value semantics: in
apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts lines 365-369,
explicitly check for a non-empty image alt value before using the fallback; in
apps/payload/src/scripts/import-articles.ts lines 461-464, replace the
unreachable logical-OR default with nullish coalescing; in
apps/payload/src/scripts/import-articles.ts lines 1244-1252, explicitly fall
back when the extracted excerpt is empty; and in apps/payload/src/seed.ts lines
567-570, test the full description before using the short description.

In `@apps/payload/src/scripts/import-articles.ts`:
- Around line 647-676: Update getRichTextPlainText to join block-level sibling
content with a separator so adjacent paragraphs remain distinct in excerpts,
while preserving direct concatenation for inline or adjacent text nodes. Keep
excerptFromContent and decodeRichTextJson behavior unchanged.
- Around line 691-700: Update the unresolved-media error handling after
hydrateRichTextMedia to avoid interpolating complete URLs; transform each
unresolved URL into a redacted path or short stable hash before joining them in
the thrown Error message, while preserving the unresolved count and diagnostic
usefulness.

In `@docker-compose.yaml`:
- Around line 54-60: Add fallback defaults to the PAYLOAD_SEED_ARTICLES_* and
PAYLOAD_SEED_HERBATICA_PRODUCTS_* mappings in the compose environment block,
matching the established fallback values used by their counterparts in
x-payload-env. Preserve the existing DC_ variable references while ensuring
unset variables do not trigger Docker Compose warnings.

---

Outside diff comments:
In `@apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts`:
- Around line 319-325: Update the widget conversion flow around the catch
handling and carousel resolution so unresolved widgets abort or skip replacement
of the affected cell instead of writing converted Lexical content. Preserve the
original widget markup when resolution fails, including successful responses
with no recognized slugs, and only overwrite the cell when a carousel is
actually produced; use the surrounding conversion function and its
htmlWithCarouselTokens handling to locate both paths.

---

Duplicate comments:
In `@apps/payload/src/scripts/import-articles.ts`:
- Around line 410-426: The media import flow around assertSafeMediaUrl and the
fetch call must prevent DNS rebinding by connecting to the address validated
during lookup. Configure the request with a custom resolver/dispatcher that pins
the validated address, or enforce an explicit host allow-list, and apply the
same protection to the additional fetch path around the referenced import logic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 312bd97e-689d-4e52-a48d-93e98b1b5195

📥 Commits

Reviewing files that changed from the base of the PR and between b34ce67 and 2f6d4e5.

📒 Files selected for processing (11)
  • .env.docker
  • apps/medusa-be/src/scripts/herbatica-seed.ts
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/lib/utils/endpoint.ts
  • apps/payload/src/payload.config.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
  • docker-compose.yaml
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Greptile Review
  • GitHub Check: main
  • GitHub Check: Analyze (javascript-typescript)
⚠️ CI failures not shown inline (1)

GitHub Check: Kilo Code Review: Kilo Code Review failed

Conclusion: failure

View job details

Review failed: The message could not be delivered
🧰 Additional context used
📓 Path-based instructions (15)
apps/payload/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/access-control-advanced.md)

apps/payload/**/*.{ts,tsx}: Implement locale-specific access control using Access function to check user authentication and locale, allowing authenticated users all locales and public users only English content
Implement device-specific access control using Access function to check user-agent headers, distinguishing between mobile and desktop access
Create IP-based access restriction using factory function that returns an Access function checking x-forwarded-for or x-real-ip headers against allowed IP list
Implement time-based access control to restrict records to today's date range using Access function with createdAt date comparisons
Create time-based access factory function that returns recent records (last N days) for regular users while granting admins full access
Implement scheduled content access control distinguishing editors/admins who see all content from public users who see only content within publish/unpublish date windows
Implement subscription-based access control using async Access function to verify active subscription status, with admin bypass
Create tier-based subscription access factory function that checks subscription tier hierarchy and returns false on inactive subscriptions or errors
Implement role-based access factory function createRoleBasedAccess that checks if user has any of the specified roles
Implement organization-scoped access factory function createOrgScopedAccess that restricts documents to user's organization IDs with optional admin bypass
Implement team-based access factory function createTeamBasedAccess that filters documents by user's team IDs with admin bypass
Implement time-limited access factory function createTimeLimitedAccess that restricts documents to last N days with admin bypass
Implement public + authenticated collection pattern where create/update/delete are restricted to admins/editors, read shows published content to public and all content to authenticated users
Implement self-service collection...

Files:

  • apps/payload/src/lib/utils/endpoint.ts
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/payload.config.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
apps/payload/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/access-control.md)

apps/payload/**/*.{ts,tsx,js,jsx}: Implement the 'admin only' access pattern in Payload CMS by checking if user roles include 'admin' and returning a boolean result
Implement the 'admin or self' access pattern in Payload CMS by checking admin role first, then falling back to self-referential query constraints (e.g., { id: { equals: user?.id } })
Implement the 'authenticated or published' access pattern in Payload CMS by returning true for authenticated users and applying query constraints for public access to published documents
In Payload CMS Local API calls, set overrideAccess: false when passing a user parameter to ensure access control is enforced rather than bypassed
Do NOT rely on the default Local API behavior in Payload CMS, as access control is skipped by default (overrideAccess: true) - always explicitly set overrideAccess: false when implementing access control

apps/payload/**/*.{ts,tsx,js,jsx}: When performing nested operations in hooks, always pass req parameter to maintain transaction context across all-or-nothing database operations
Pass req to req.payload.find() calls within hooks to maintain atomic transaction context across MongoDB, PostgreSQL, and SQLite databases
Pass req to req.payload.update() calls within hooks to ensure changes are included in the same transaction and can be rolled back together on errors
Use payload.db.beginTransaction(), payload.db.commitTransaction(), and payload.db.rollbackTransaction() for manual transaction control when needed

apps/payload/**/*.{ts,tsx,js,jsx}: Custom endpoints in Payload are not authenticated by default. Always check req.user to verify user authentication before processing requests.
Use req.payload for database operations instead of direct database access to ensure access control and hooks execute properly.
Use the addDataAndFileToRequest helper from Payload to handle both JSON parsing and file uploads in POST endpoints.
Use headersWithCors helper from Payload ...

Files:

  • apps/payload/src/lib/utils/endpoint.ts
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/payload.config.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
apps/payload/**/*.{ts,tsx,js,jsx,graphql}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/queries.md)

Use GraphQL queries with proper field selection, filtering (where), limiting (limit), and sorting (sort) when querying Payload CMS via GraphQL

Files:

  • apps/payload/src/lib/utils/endpoint.ts
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/payload.config.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Import UI components using the @techsio/ui-kit namespace, not @libs/ui, for runtime apps

Files:

  • apps/payload/src/lib/utils/endpoint.ts
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/payload.config.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/medusa-be/src/scripts/herbatica-seed.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Run Biome linting and formatting only on changed files using 'bunx biome check --write path/to/file'

Files:

  • apps/payload/src/lib/utils/endpoint.ts
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/payload.config.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/medusa-be/src/scripts/herbatica-seed.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
apps/**/!(medusa-be)/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use modern React patterns and React 19 for frontend applications in the monorepo

Files:

  • apps/payload/src/lib/utils/endpoint.ts
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/payload.config.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/medusa-be/src/scripts/herbatica-seed.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
apps/payload/**/endpoints/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/payload/AGENTS.md)

apps/payload/**/endpoints/**/*.{ts,tsx}: Always check authentication in custom endpoints before performing operations
Use APIError for proper error responses in custom endpoints
Use req.payload for database operations within custom endpoints

Files:

  • apps/payload/src/lib/endpoints/article-options.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
apps/payload/**/*payload*.config.{ts,js}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/adapters.md)

apps/payload/**/*payload*.config.{ts,js}: Use mongooseAdapter from '@payloadcms/db-mongodb' for MongoDB database configuration in Payload CMS with DATABASE_URL environment variable
Use postgresAdapter from '@payloadcms/db-postgres' for PostgreSQL configuration with pool.connectionString, setting push to false, and specifying migrationDir
Use sqliteAdapter from '@payloadcms/db-sqlite' for SQLite configuration with client.url and enable transactions via transactionOptions property
Use s3Storage from '@payloadcms/storage-s3' for AWS S3 file storage configuration with bucket name and AWS credentials from environment variables
Consider available storage adapters: @payloadcms/storage-s3, @payloadcms/storage-azure, @payloadcms/storage-gcs, @payloadcms/storage-r2, @payloadcms/storage-vercel-blob, @payloadcms/storage-uploadthing
Use nodemailerAdapter from '@payloadcms/email-nodemailer' for SMTP-based email configuration with defaultFromAddress, defaultFromName, and transportOptions (host, port, auth)
Use resendAdapter from '@payloadcms/email-resend' for email configuration via Resend service with apiKey from environment variable
SQLite transactions are disabled by default and must be explicitly enabled via transactionOptions configuration

Files:

  • apps/payload/src/payload.config.ts
apps/payload/**/payload.config.{ts,js}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/components.md)

apps/payload/**/payload.config.{ts,js}: Define custom components using file paths (not direct imports) in Payload CMS admin config to keep the config lightweight and Node.js compatible
Component paths must be relative to project root or config.admin.importMap.baseDir, with named exports using '#ExportName' suffix or exportName property
Set baseDir in admin.importMap config to establish a custom base directory for resolving component paths in Payload CMS
Use clientProps in component config for serializable custom props and serverProps for non-serializable custom props in Payload CMS
Verify component file paths are correct relative to baseDir and use named export syntax '#ExportName' when needed to resolve component loading issues in Payload CMS

Files:

  • apps/payload/src/payload.config.ts
apps/payload/**/payload.config.ts

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/payload-overview.md)

apps/payload/**/payload.config.ts: Use storage adapter plugins for cloud storage
Use buildConfig from 'payload' to configure the Payload CMS instance
Use mongooseAdapter from '@payloadcms/db-mongodb' for MongoDB database configuration
Use lexicalEditor from '@payloadcms/richtext-lexical' for rich text editing
Configure typescript.outputFile to generate payload-types.ts for type safety
Configure admin.importMap.baseDir in payload config to enable proper component imports

Files:

  • apps/payload/src/payload.config.ts
apps/payload/**/payload.config.{ts,tsx}

📄 CodeRabbit inference engine (apps/payload/AGENTS.md)

apps/payload/**/payload.config.{ts,tsx}: Use file paths (not direct imports) to define components in the Payload config, with proper export suffixes
Enable transactions in SQLite using transactionOptions: {}

Files:

  • apps/payload/src/payload.config.ts
apps/medusa-be/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/medusa-be/CLAUDE.md)

apps/medusa-be/**/*.{ts,tsx}: Use TypeScript for type checking - run npx tsc --noEmit for validation
Forbidden: Non-null assertions (!) - always use type guards and validation instead
Annotate generic field access with explicit unknown type before type guards - const v: unknown = result[field]
Don't use as Type without validation - always validate before casting
Use Modules.* and ContainerRegistrationKeys.* constants instead of hardcoding strings
Batch operations with CHUNK_SIZE to avoid unbounded operations
Extract pure functions to separate files for testability without runtime dependencies

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
apps/medusa-be/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/medusa-be/CLAUDE.md)

apps/medusa-be/**/*.{ts,tsx,js,jsx}: Use Biome linter with ultracite preset - run bunx biome check --write . and always use braces in conditionals
Comments should explain 'why', never 'what' - use self-documenting code via clear naming
Always use const per declaration - const a = 1; const b = 2 is correct, one variable per line
Use nullish coalescing (??) operator instead of logical OR (||) for default values

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
apps/medusa-be/src/scripts/*.ts

📄 CodeRabbit inference engine (apps/medusa-be/CLAUDE.md)

apps/medusa-be/src/scripts/*.ts: Use medusa exec ./src/scripts/script-name.ts [args] to run one-off scripts with container access
Scripts receive { container, args } - parse args manually or use container.resolve() for services

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
apps/medusa-be/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Medusa backend custom logic should be organized in api/, modules/, workflows/, admin/, subscribers/, and jobs/ directories under apps/medusa-be/src/

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
🧠 Learnings (1)
📚 Learning: 2026-02-25T14:46:02.729Z
Learnt from: redeyecz
Repo: TechsioCZ/new-engine PR: 335
File: apps/zane-operator/src/db.ts:205-207
Timestamp: 2026-02-25T14:46:02.729Z
Learning: Enforce PostgreSQL 18+ as the minimum version across docker-compose files. Since the repo uses postgres:18.1-alpine (evidence in docker-compose.yaml), ensure all docker-compose service images for PostgreSQL use 18.1-alpine or newer. When reviewing, look for postgres images with a tag below 18 (e.g., postgres:<older-version>) and update to 18.1-alpine or a newer compatible tag. This guideline applies to all docker-compose YAML files that define PostgreSQL services.

Applied to files:

  • docker-compose.yaml
🪛 ast-grep (0.44.1)
apps/medusa-be/src/scripts/herbatica-seed.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from "node:child_process"
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from "node:child_process"
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)


[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from "node:child_process"
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🪛 dotenv-linter (4.0.0)
.env.docker

[warning] 99-99: [UnorderedKey] The DC_PAYLOAD_SEED_ARTICLES_LOCALE key should go before the DC_PAYLOAD_SEED_ARTICLES_XLSX_PATH key

(UnorderedKey)


[warning] 100-100: [UnorderedKey] The DC_PAYLOAD_SEED_ARTICLES_OVERWRITE key should go before the DC_PAYLOAD_SEED_ARTICLES_XLSX_PATH key

(UnorderedKey)


[warning] 101-101: [UnorderedKey] The DC_PAYLOAD_SEED_ARTICLES_SHEET key should go before the DC_PAYLOAD_SEED_ARTICLES_XLSX_PATH key

(UnorderedKey)


[warning] 102-102: [UnorderedKey] The DC_PAYLOAD_SEED_ARTICLES_STATUS key should go before the DC_PAYLOAD_SEED_ARTICLES_XLSX_PATH key

(UnorderedKey)


[warning] 103-103: [UnorderedKey] The DC_PAYLOAD_SEED_ARTICLES_TRANSLATE key should go before the DC_PAYLOAD_SEED_ARTICLES_XLSX_PATH key

(UnorderedKey)

🪛 OpenGrep (1.25.0)
apps/payload/src/scripts/import-articles.ts

[ERROR] 451-451: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

🔇 Additional comments (8)
apps/payload/src/lib/utils/endpoint.ts (1)

3-64: LGTM!

apps/payload/src/lib/endpoints/article-options.ts (1)

34-74: LGTM!

apps/payload/src/payload.config.ts (1)

47-51: LGTM!

apps/payload/src/seed.ts (3)

264-304: refactor(seed): remove the remaining Payload never casts

where as never and data as never still bypass generated collection types despite the generic helper. This is the same type-safety concern raised previously.

#!/bin/bash
set -euo pipefail

rg -n -C4 'RequiredDataFromCollectionSlug|Where|where as never|data as never' \
  apps/payload/src/seed.ts apps/payload/src/payload-types.ts

As per coding guidelines, “Don't use as Type without validation.”

Source: Coding guidelines


365-379: perf(seed): add the remaining id-only projection

depth: 0 is now present, but this lookup still retrieves every scalar field although only id is consumed. This was already raised in the earlier projection review.

As per coding guidelines, use select in Payload Local API queries to limit returned fields.

Source: Coding guidelines


5-8: LGTM!

Also applies to: 253-253

apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts (1)

8-78: LGTM!

Also applies to: 244-318, 326-364, 370-410, 491-530, 586-663, 685-726

apps/payload/src/scripts/import-articles.ts (1)

2-59: LGTM!

Also applies to: 317-330, 428-460, 465-646, 677-690, 701-740, 1253-1273

Comment thread apps/medusa-be/src/scripts/herbatica-seed.ts Outdated
Comment thread apps/payload/src/lib/blocks/article-carousel.ts
Comment thread apps/payload/src/lib/endpoints/medusa-products.ts Outdated
Comment thread apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
Comment thread apps/payload/src/scripts/import-articles.ts
Comment thread apps/payload/src/scripts/import-articles.ts
Comment thread docker-compose.yaml Outdated
Comment thread apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts Outdated
Comment thread apps/payload/src/seed.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
apps/payload/src/lib/blocks/article-carousel.ts (1)

15-27: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

perf: add select parameter to optimise the existence check

Hello! To improve query performance and adhere to our coding guidelines, could we please add the select option to this Local API query? Because we are only checking if the article exists, selecting just the id field will prevent the database from fetching the entire document, which is particularly beneficial for articles containing large amounts of rich text.

As per coding guidelines: Use the select parameter in Payload Local API queries to limit returned fields and improve query performance.

♻️ Proposed refactor
   const result = await req.payload.find({
     req,
     collection: "articles",
     where: {
       slug: {
         equals: value.trim(),
       },
     },
     depth: 0,
     limit: 1,
     pagination: false,
     overrideAccess: true,
+    select: {
+      id: true,
+    },
   })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/payload/src/lib/blocks/article-carousel.ts` around lines 15 - 27, Add a
select option to the Payload query in the article existence check, limiting the
returned fields to only the article id while preserving the existing slug filter
and query behavior.

Source: Coding guidelines

apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts (1)

566-589: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

fix(converter): link nodes are still stripped down to bare children.

shouldStripLink still returns the flattened children array instead of nextRecord, discarding the link node's type/url/attributes — functionally identical to the behaviour flagged previously (marked "Addressed in commit fabb41a"), just refactored behind a boolean. Since LinkFeature is enabled for articles.content, every hyperlink is still permanently converted to plain text on import.

🐛 Proposed fix to preserve link nodes
-  const shouldStripLink = record.type === "link"
-  if (shouldStripLink) {
+  if (record.type === "link") {
     addLinkManifestEntry(record, context.linkManifest)
   }
 
   const root = sanitizeLexicalRoot(record, context)
   if (root !== undefined) {
     nextRecord.root = root
   }
 
   if (Array.isArray(record.children)) {
     const children = record.children.flatMap((child) => {
       const sanitized = sanitizeLexicalNode(child, context)
       return sanitized === undefined ? [] : sanitized
     })
 
-    if (shouldStripLink) {
-      return children.length > 0 ? children : undefined
-    }
-
     if (children.length === 0 && record.type !== "root") {
       return
     }
 
     nextRecord.children = children
   }
 
   return nextRecord
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts` around
lines 566 - 589, Update the shouldStripLink branch in sanitizeLexicalNode so
link records return nextRecord with their sanitized children assigned, rather
than returning the flattened children array. Preserve the link node’s type, URL,
and other attributes while retaining the existing behavior for links with no
children.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@apps/payload/src/lib/blocks/article-carousel.ts`:
- Around line 15-27: Add a select option to the Payload query in the article
existence check, limiting the returned fields to only the article id while
preserving the existing slug filter and query behavior.

In `@apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts`:
- Around line 566-589: Update the shouldStripLink branch in sanitizeLexicalNode
so link records return nextRecord with their sanitized children assigned, rather
than returning the flattened children array. Preserve the link node’s type, URL,
and other attributes while retaining the existing behavior for links with no
children.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 00bac613-e15c-4c4d-82c4-5a9622ac7bbb

📥 Commits

Reviewing files that changed from the base of the PR and between 2f6d4e5 and 1c9cef4.

📒 Files selected for processing (7)
  • apps/medusa-be/src/scripts/herbatica-seed.ts
  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
  • docker-compose.yaml
📜 Review details
⚠️ CI failures not shown inline (1)

GitHub Check: Kilo Code Review: Kilo Code Review failed

Conclusion: failure

View job details

Review failed: The message could not be delivered
🧰 Additional context used
📓 Path-based instructions (11)
apps/payload/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/access-control-advanced.md)

apps/payload/**/*.{ts,tsx}: Implement locale-specific access control using Access function to check user authentication and locale, allowing authenticated users all locales and public users only English content
Implement device-specific access control using Access function to check user-agent headers, distinguishing between mobile and desktop access
Create IP-based access restriction using factory function that returns an Access function checking x-forwarded-for or x-real-ip headers against allowed IP list
Implement time-based access control to restrict records to today's date range using Access function with createdAt date comparisons
Create time-based access factory function that returns recent records (last N days) for regular users while granting admins full access
Implement scheduled content access control distinguishing editors/admins who see all content from public users who see only content within publish/unpublish date windows
Implement subscription-based access control using async Access function to verify active subscription status, with admin bypass
Create tier-based subscription access factory function that checks subscription tier hierarchy and returns false on inactive subscriptions or errors
Implement role-based access factory function createRoleBasedAccess that checks if user has any of the specified roles
Implement organization-scoped access factory function createOrgScopedAccess that restricts documents to user's organization IDs with optional admin bypass
Implement team-based access factory function createTeamBasedAccess that filters documents by user's team IDs with admin bypass
Implement time-limited access factory function createTimeLimitedAccess that restricts documents to last N days with admin bypass
Implement public + authenticated collection pattern where create/update/delete are restricted to admins/editors, read shows published content to public and all content to authenticated users
Implement self-service collection...

Files:

  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
apps/payload/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/access-control.md)

apps/payload/**/*.{ts,tsx,js,jsx}: Implement the 'admin only' access pattern in Payload CMS by checking if user roles include 'admin' and returning a boolean result
Implement the 'admin or self' access pattern in Payload CMS by checking admin role first, then falling back to self-referential query constraints (e.g., { id: { equals: user?.id } })
Implement the 'authenticated or published' access pattern in Payload CMS by returning true for authenticated users and applying query constraints for public access to published documents
In Payload CMS Local API calls, set overrideAccess: false when passing a user parameter to ensure access control is enforced rather than bypassed
Do NOT rely on the default Local API behavior in Payload CMS, as access control is skipped by default (overrideAccess: true) - always explicitly set overrideAccess: false when implementing access control

apps/payload/**/*.{ts,tsx,js,jsx}: When performing nested operations in hooks, always pass req parameter to maintain transaction context across all-or-nothing database operations
Pass req to req.payload.find() calls within hooks to maintain atomic transaction context across MongoDB, PostgreSQL, and SQLite databases
Pass req to req.payload.update() calls within hooks to ensure changes are included in the same transaction and can be rolled back together on errors
Use payload.db.beginTransaction(), payload.db.commitTransaction(), and payload.db.rollbackTransaction() for manual transaction control when needed

apps/payload/**/*.{ts,tsx,js,jsx}: Custom endpoints in Payload are not authenticated by default. Always check req.user to verify user authentication before processing requests.
Use req.payload for database operations instead of direct database access to ensure access control and hooks execute properly.
Use the addDataAndFileToRequest helper from Payload to handle both JSON parsing and file uploads in POST endpoints.
Use headersWithCors helper from Payload ...

Files:

  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
apps/payload/**/*.{ts,tsx,js,jsx,graphql}

📄 CodeRabbit inference engine (apps/payload/.cursor/rules/queries.md)

Use GraphQL queries with proper field selection, filtering (where), limiting (limit), and sorting (sort) when querying Payload CMS via GraphQL

Files:

  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Import UI components using the @techsio/ui-kit namespace, not @libs/ui, for runtime apps

Files:

  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/medusa-be/src/scripts/herbatica-seed.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Run Biome linting and formatting only on changed files using 'bunx biome check --write path/to/file'

Files:

  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/medusa-be/src/scripts/herbatica-seed.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
apps/**/!(medusa-be)/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use modern React patterns and React 19 for frontend applications in the monorepo

Files:

  • apps/payload/src/lib/blocks/article-carousel.ts
  • apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts
  • apps/medusa-be/src/scripts/herbatica-seed.ts
  • apps/payload/src/lib/endpoints/medusa-products.ts
  • apps/payload/src/scripts/import-articles.ts
  • apps/payload/src/seed.ts
apps/medusa-be/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/medusa-be/CLAUDE.md)

apps/medusa-be/**/*.{ts,tsx}: Use TypeScript for type checking - run npx tsc --noEmit for validation
Forbidden: Non-null assertions (!) - always use type guards and validation instead
Annotate generic field access with explicit unknown type before type guards - const v: unknown = result[field]
Don't use as Type without validation - always validate before casting
Use Modules.* and ContainerRegistrationKeys.* constants instead of hardcoding strings
Batch operations with CHUNK_SIZE to avoid unbounded operations
Extract pure functions to separate files for testability without runtime dependencies

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
apps/medusa-be/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (apps/medusa-be/CLAUDE.md)

apps/medusa-be/**/*.{ts,tsx,js,jsx}: Use Biome linter with ultracite preset - run bunx biome check --write . and always use braces in conditionals
Comments should explain 'why', never 'what' - use self-documenting code via clear naming
Always use const per declaration - const a = 1; const b = 2 is correct, one variable per line
Use nullish coalescing (??) operator instead of logical OR (||) for default values

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
apps/medusa-be/src/scripts/*.ts

📄 CodeRabbit inference engine (apps/medusa-be/CLAUDE.md)

apps/medusa-be/src/scripts/*.ts: Use medusa exec ./src/scripts/script-name.ts [args] to run one-off scripts with container access
Scripts receive { container, args } - parse args manually or use container.resolve() for services

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
apps/medusa-be/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Medusa backend custom logic should be organized in api/, modules/, workflows/, admin/, subscribers/, and jobs/ directories under apps/medusa-be/src/

Files:

  • apps/medusa-be/src/scripts/herbatica-seed.ts
apps/payload/**/endpoints/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/payload/AGENTS.md)

apps/payload/**/endpoints/**/*.{ts,tsx}: Always check authentication in custom endpoints before performing operations
Use APIError for proper error responses in custom endpoints
Use req.payload for database operations within custom endpoints

Files:

  • apps/payload/src/lib/endpoints/medusa-products.ts
🧠 Learnings (1)
📚 Learning: 2026-02-25T14:46:02.729Z
Learnt from: redeyecz
Repo: TechsioCZ/new-engine PR: 335
File: apps/zane-operator/src/db.ts:205-207
Timestamp: 2026-02-25T14:46:02.729Z
Learning: Enforce PostgreSQL 18+ as the minimum version across docker-compose files. Since the repo uses postgres:18.1-alpine (evidence in docker-compose.yaml), ensure all docker-compose service images for PostgreSQL use 18.1-alpine or newer. When reviewing, look for postgres images with a tag below 18 (e.g., postgres:<older-version>) and update to 18.1-alpine or a newer compatible tag. This guideline applies to all docker-compose YAML files that define PostgreSQL services.

Applied to files:

  • docker-compose.yaml
🪛 ast-grep (0.44.1)
apps/medusa-be/src/scripts/herbatica-seed.ts

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn } from "node:child_process"
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🔇 Additional comments (8)
apps/payload/src/lib/endpoints/medusa-products.ts (1)

48-128: LGTM!

docker-compose.yaml (1)

52-60: LGTM!

apps/medusa-be/src/scripts/herbatica-seed.ts (1)

3537-3540: LGTM!

apps/payload/src/scripts/convert-article-xlsx-html-to-richtext.ts (2)

353-381: fix(converter): alt-text fallback now correctly handles empty/whitespace values.

Explicit truthy check (imageAlt ? imageAlt : "...") replaces the earlier unreachable ?? fallback — nicely done.


534-548: LGTM!

apps/payload/src/scripts/import-articles.ts (2)

450-465: LGTM!

Also applies to: 682-694, 701-753, 1303-1313


923-935: 🗄️ Data Integrity & Integration

Keep skipArticleSlugValidation scoped to the import path. If this flag is only meant to bypass the import-time slug check, wire it to the articles validation hook rather than making it a general bypass, since findExistingBySlug already controls the update/create branch here.

apps/payload/src/seed.ts (1)

567-580: LGTM!

Comment thread apps/medusa-be/src/scripts/herbatica-seed.ts
@andrey-hrinchenko

Copy link
Copy Markdown
Contributor

Closing this in favor of #547, which already includes all the work from this PR plus the newer blog changes.

This branch was successfully deployed

1 active deployment
Preview — cb1cc492 Deployed Jul 17, 2026 by vercel[bot]
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.

3 participants