- Add --force-recreate to podman compose up so stale containers are never
reused across deploys when the image tag (staging) is reused
- Inject CLERK_SECRET_KEY and ADMIN_CLERK_SECRET_KEY from Gitea secrets into
~/staging/.env on the VPS via printf (variables expand on the runner before
SSH, so secrets never touch VPS shell history; file gets chmod 600)
- Update compose.yml: storefront gets CLERK_SECRET_KEY, admin gets
CLERK_SECRET_KEY mapped from ADMIN_CLERK_SECRET_KEY
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
With outputFileTracingRoot set to the repo root, Next.js standalone mirrors
the full monorepo directory tree inside .next/standalone/. server.js lands at
apps/storefront/server.js (not at the root), so the CMD must reflect that.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The compose file was written via a bash << 'COMPOSE' heredoc nested inside
the YAML run: | block scalar. Lines like "name: petloft-staging" at column 0
cause the YAML parser to break out of the block scalar early, making the
entire workflow file invalid YAML — Gitea silently drops invalid workflows,
so no jobs triggered at all.
Fix: move compose.yml to deploy/staging/compose.yml in the repo, substitute
${REGISTRY} on the runner, base64-encode the result, and decode it on the VPS
inside the SSH session. No inner heredoc needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The VPS had no /opt/staging directory or compose file, causing the deploy
step to fail with "No such file or directory". Now the workflow:
- Creates /opt/staging if missing
- Writes compose.yml on every deploy (keeps it in sync with CI)
- Touches .env so podman compose doesn't error if no secrets file exists yet
Also adds deploy/staging/.env.example documenting runtime secrets that must
be set manually on the VPS after first deploy.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
turbo prune storefront --docker excludes admin, so tailwind-merge was
not installed at root in Docker (it was only hoisted because of admin's dep).
@heroui/styles/node_modules/tailwind-variants requires tailwind-merge >=3.0.0
and walks up to root to find it. Adding it as a root-level dep ensures npm ci
always installs it regardless of which workspace is being built.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- apps/admin: tailwind-merge ^2.6.1 → ^3.4.0 so root resolves to v3.x,
satisfying @heroui/styles/node_modules/tailwind-variants peer dep (>=3.0.0)
- apps/storefront: add lucide-react ^0.400.0 as explicit dep (used in
SearchEmptyState and SearchResultsPanel but was previously undeclared)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
turbo prune cannot fully parse the npm 11 lockfile format, causing it to
generate an incomplete out/package-lock.json that drops non-hoisted workspace
entries (apps/storefront/node_modules/@heroui/react and related packages).
Replacing it with the full root lockfile ensures npm ci in the Docker deps
stage installs all packages including non-hoisted ones.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@heroui/react cannot be hoisted to the root by npm (peer dep constraints)
and is installed at apps/storefront/node_modules/ instead. The builder stage
was only copying /app/node_modules, leaving @heroui/react absent when
next build ran.
Switch to COPY --from=deps /app/ ./ so both root and workspace-level
node_modules are present, then COPY full/ . layers the source on top.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
docker build --push uses buildkit's internal push which connects directly
to the registry over HTTPS, bypassing the Podman daemon. Since the Gitea
registry is HTTP-only, this fails with "server gave HTTP response to HTTPS client".
Switch to --load (exports image into Podman daemon) then docker push (goes
through the daemon which has insecure=true in registries.conf → uses HTTP).
Tag the SHA variant with docker tag before pushing both.
Also:
- Add NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME ARG/ENV to admin Dockerfile
- Add STAGING_ prefix note to both Dockerfiles builder stage
- Add STAGING_NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME to workflow env and
pass it as --build-arg for admin builds only
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Added NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME to both admin and storefront Dockerfiles to ensure it is available during the build process.
- Updated deploy-staging.yml to pass the new Cloudinary variable as a build argument.
- Clarified comments regarding the handling of NEXT_PUBLIC_* variables and Gitea secret prefixes.
This change enhances the build configuration for both applications, ensuring all necessary environment variables are correctly passed during the Docker build process.
Two issues in the admin (and upcoming storefront) build:
1. Missing Clerk publishableKey during prerender
NEXT_PUBLIC_* vars are baked into the client bundle at build time. If absent,
Next.js SSG fails with "@clerk/clerk-react: Missing publishableKey".
Added ARG + ENV in both Dockerfiles builder stage and pass them via
--build-arg in the workflow. Admin and storefront use different Clerk
instances so the key is selected per matrix.app with a shell conditional.
2. "No output specified with docker-container driver" warning
setup-buildx-action with driver:docker was not switching the driver in the
Podman environment. Removed the step and switched to docker build --push
which pushes directly during the build, eliminating the separate push steps
and the missing-output warning.
New secrets required:
STAGING_NEXT_PUBLIC_CONVEX_URL
STAGING_NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY (storefront)
STAGING_ADMIN_NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY (admin)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two root causes for the Docker build failures:
1. convex/_generated/api not found (both apps)
turbo prune only traces npm workspace packages; the root convex/ directory
is not a workspace package so it is excluded from out/full/. Copy it
manually into the prune output after turbo prune runs.
2. @heroui/react not found (storefront)
package-lock.json was generated with npm@11 but node:20-alpine ships
npm@10. turbo warns it cannot parse the npm 11 lockfile and generates an
incomplete out/package-lock.json, causing npm ci inside Docker to miss
packages. Upgrade npm to 11 in the deps stage of both Dockerfiles.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove top-level env.REGISTRY — Gitea does not expand secrets in
workflow-level env blocks; reference secrets.STAGING_REGISTRY directly
- Add docker/setup-buildx-action with driver: docker to avoid the
docker-container driver which requires --privileged on rootless Podman
- Update secret names comment to clarify STAGING_ prefix convention
(Gitea has no environment-level secrets, so prefixes distinguish staging/prod)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Introduced Dockerfiles for both admin and storefront applications to streamline the build and deployment process using multi-stage builds.
- Configured the Dockerfiles to install dependencies, build the applications, and set up a minimal runtime environment.
- Updated next.config.js for both applications to enable standalone output and set the outputFileTracingRoot for proper file tracing in a monorepo setup.
This commit enhances the containerization of the applications, improving deployment efficiency and reducing image sizes.
- Introduced a new workflow in deploy-staging.yml to automate the deployment process for the staging environment.
- The workflow includes steps for CI tasks (linting, type checking, testing), building and pushing Docker images for storefront and admin applications, and deploying to a VPS.
- Configured environment variables and secrets for secure access to the Docker registry and VPS.
This commit enhances the CI/CD pipeline by streamlining the deployment process to the staging environment.
Add onUnhandledError to filter 'Write outside of transaction ...
_scheduled_functions' errors so CI passes. These occur when
order/fulfillment mutations schedule email sends and convex-test
runs them after the transaction closes.
Made-with: Cursor
- carts.test: add required product fields (parentCategorySlug, childCategorySlug)
and variant fields (weight, weightUnit)
- stripeActions.test: use price in cents (2499) for variant/cart and expect
unit_amount: 2499 in line_items assertion
- useShippingRate.test: expect fallback error message for plain Error rejections
- scaffold.test: enable @ alias in root vitest.config for storefront imports
- useCartSession.test: mock useConvexAuth instead of ConvexProviderWithClerk
for reliable unit tests
Made-with: Cursor
- Allow require() in next.config.js (eslint-disable) for both apps
- Replace all catch (e: any) with catch (e: unknown) and proper error handling
- Remove no-explicit-any: add types (PreviewProduct, ProductImage, Id<addresses>,
ProductDetailReview, error payloads) across admin and storefront
- Admin: use next/image in ImageUploadSection and ProductImageCarousel; remove
unused layout fonts and sidebar imports; fix products page useMemo deps
- Storefront: use Link for /sign-in in header actions; fix useAddressMutations
and product detail types; remove unused imports/vars and fix useMemo deps
Made-with: Cursor
- Introduced eslint.config.mjs files for both admin and storefront to extend Next.js linting rules.
- Updated package.json files to replace the default Next.js lint command with a direct ESLint command for improved linting control.
- Add .gitea/workflows/ci.yml — runs lint, typecheck, and tests on every push
- Remove convex/_generated from .gitignore and commit the generated files so CI
has the type information it needs without requiring a live Convex backend
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Added .env.staging files for admin and storefront applications to .gitignore.
- Updated package.json and package-lock.json to include @convex-dev/resend and its dependencies.
- Introduced new Convex-related modules and updated peer dependencies for better compatibility.
This commit enhances the environment configuration and integrates the Resend service into the project.
- Added RequestReturnDialog component for initiating return requests.
- Enhanced OrderDetailPageView to handle return requests and display order timeline.
- Updated OrderActions to include return request button based on order status.
- Introduced OrderTimeline component to visualize order events.
- Modified order-related types and constants to support return functionality.
- Updated UI components for better styling and user experience.
This commit improves the order management system by allowing users to request returns and view the timeline of their orders.
- Introduced a comprehensive markdown document outlining implementation rules for Convex functions, including syntax, registration, HTTP endpoints, and TypeScript usage.
- Created a new configuration file for the Convex app, integrating the Resend service.
- Added a new HTTP route for handling Shippo webhooks to ensure proper response handling.
- Implemented integration tests for order timeline events, covering various scenarios including order fulfillment and status changes.
- Enhanced existing functions with type safety improvements and additional validation logic.
This commit establishes clear guidelines for backend development and improves the overall structure and reliability of the Convex application.
- Removed .env.example file as it is no longer needed.
- Updated next.config.js to include turbopack configuration.
- Added detailed implementation plans for order processing and UI design in new markdown files.
- Introduced API usage guide for background removal service.
This commit enhances the order processing backend and UI design documentation, ensuring clarity and improved configuration management.
- Extend addVariant with dimension fields and SKU uniqueness check; expand updateVariant to full field set; update getByIdForAdmin to return all variants (active + inactive)
- Add generateSku utility to @repo/utils; auto-generates SKU from brand, product name, attributes, and weight with manual-override support
- Move ProductSearchSection to components/shared and fix nav link /variants → /variant
- Variants page: product search, loading skeleton, variants table, toolbar with create button
- VariantsTable: 8 columns, activate/deactivate toggle, delete with AlertDialog confirmation
- VariantPreviewDialog: read-only full variant details with sections for pricing, inventory, shipping, attributes
- VariantForm: zod schema with superRefine for dimension and on-sale validation, auto-SKU generation
- CreateVariantDialog and EditVariantDialog wiring dollarsToCents on submit
- Install sonner and add Toaster to root layout; install ShadCN Switch component
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Next.js API route for server-side Cloudinary upload with structured
public_id (the-pet-loft/products/{id}/main|gallery-n) and asset_folder
for portal folder visibility in dynamic folder mode
- Add background removal flow via Image Processing API with side-by-side
original vs processed preview (Skeleton while loading)
- Dual upload buttons: processed (background removed) or original file
- Horizontal drag-and-drop image gallery using @dnd-kit/sortable with
horizontalListSortingStrategy; reorder persisted via reorderImages mutation
- Per-image delete with AlertDialog confirmation
- 180° rotation technique for card layout: drag handle top, image center,
delete bottom
- Debounced product search (300 ms) with inline results (max 3); clears
gallery state when search input is cleared
- Install: cloudinary, @dnd-kit/core/sortable/utilities, embla-carousel-react,
ShadCN carousel component
- Configure next.config.js with Cloudinary remote image pattern
- Mark checklist items 3.5 and 3.6 complete
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>