Skip to content

Recipicity

Production

Domain: recipicity.com Stack: app-recipicity-production (5 services) Stack file: /opt/swarm/stacks/recipicity-production/stack.recipicity-production.yml

Service Image Replicas Port
frontend recipicity-production-frontend 2 80
api recipicity-production-api 2 3000
admin recipicity-production-admin 1 80
scheduler recipicity-production-api 1 --
images nginx:stable-alpine 1 80

Tech stack: Next.js (frontend), Node.js/Express (API), PostgreSQL, Redis, Prisma ORM

Routes:

  • recipicity.com -> frontend
  • recipicity.com/api -> api
  • recipicity.com/admin -> admin (stripprefix)

Payment integration: Paddle (webhook route mounted before express.json() for raw body signature verification). Credentials encrypted with AES-256-GCM, key from Docker secret.

Build Script Required

Always use the build script on apps-dev1. Never run docker build manually -- the frontend requires --build-arg REACT_APP_API_URL=https://recipicity.com/api.

ssh john@192.168.51.40
cd /opt/development/recipicity/prod
./build.sh all --deploy           # Build, push, deploy everything
./build.sh frontend --deploy      # Frontend only
./build.sh api --deploy           # API only
./build.sh admin --deploy         # Admin only

The build script uses digest-pinned deploys -- it captures the SHA256 digest from each docker push and passes it to docker service update, eliminating stale image cache issues.

Staging

Domain: staging.recipicity.com Stack: app-recipicity-staging (4 services) + app-recipicity-staging-admin (1 service) Stack file: /opt/swarm/stacks/app-recipicity/stack.recipicity.yml

Service Image Replicas Port
frontend recipicity-staging-frontend 2 3000
api recipicity-staging-api 2 3000
scheduler recipicity-staging-api 1 --
images nginx:stable-alpine 1 80
admin (separate stack) recipicity-staging-admin 1 80

Tech stack: Next.js 16.1.6 (frontend), React 19.2.3, Node.js/Express (API), PostgreSQL, Tailwind CSS v4

Key architecture decisions:

  • Zero UUIDs in URLs -- recipes at /{username}/{slug}
  • BFF auth pattern with httpOnly cookies
  • CSS masonry via column-count (SSR-safe, zero JS)
  • Native fetch (no axios) -- server-client.ts for SSR, client.ts for browser
  • Internal API: http://app-recipicity-staging_api:3000/api (direct service mesh)

Build:

ssh john@192.168.51.40
cd /opt/development/recipicity/staging
./build.sh all --deploy    # Build, push, and deploy (digest-pinned)

Staging-Specific Notes

  • Frontend needs data_net network for SSR API calls (direct service mesh, not through Traefik)
  • Internal API URL set via INTERNAL_API_URL environment variable
  • Memory: 512MB limit / 256MB reservation (higher than production due to SSR)

AI Integration

Provider-agnostic AI client supporting both Anthropic (Claude) and OpenAI. Provider is auto-detected from model name (e.g., claude-* routes to Anthropic). Configurable via admin panel.

AI Features

Feature Description
Recipe Generation AI generates complete recipes from user prompts
Recipe Import (URL) Schema.org parsing + AI enrichment (description, tags)
Recipe Import (Image) Vision model extracts recipe from photos
Recipe Import (Text) AI structures pasted recipe text
Conversational Recipes Chat-based recipe creation and modification

Recipe Import Pipeline

  1. Fetch and parse -- Downloads page, extracts Schema.org JSON-LD or microdata
  2. Schema.org extraction -- Parses structured recipe data (ingredients, steps, times)
  3. AI enrichment -- Generates factual description, suggests tags
  4. Unit normalization -- Standardizes ingredient units (cups->cup, tablespoons->tbsp, pounds->lb)
  5. Drink detection -- Heuristic analysis classifies recipe as food or drink
  6. Save as draft -- User reviews before publishing

All AI calls are logged to the ai_generation_logs table with provider, model, token usage, and cost.

Copyright Policy

Recipe imports never copy copyrightable content (personal stories, creative descriptions, images). Only factual data is extracted: ingredient lists, cooking steps, times, servings. AI generates original descriptions. Source URL is always attributed.

Payment System (Paddle)

  • Webhook route mounted BEFORE express.json() middleware (needs raw body for signature verification)
  • Credentials stored encrypted in SiteConfig DB table (AES-256-GCM)
  • Encryption key from Docker secret: recipicity_staging_encryption_key
  • Paddle SDK v1.10: uses paddle.transactions.create() (not checkouts)