Playwright Chromium is not available in node:24-alpine, causing the vitest 'client' project (browser tests) to fail with an unhandled browserType.launch error and exit code 1. - Dockerfile: switch tester stage command to 'npm run test:unit -- --run --project=server' so only Node.js unit tests run during Docker builds - page.svelte.spec.ts: update stale 'renders h1' assertion to match the new InstaChef design (no h1; check for 'InstaChef' logo text) Browser component tests still run locally when Playwright/Chromium is available. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
InstaRecipe - Async Instagram Recipe Extractor
A modern web application that extracts recipes from Instagram posts and saves them to Tandoor Recipe Manager using an async queue-based processing system.
🚀 Features
Core Functionality
- Async Queue Processing: Fire-and-forget recipe extraction with background processing
- Real-time Updates: Server-Sent Events for live progress tracking
- Push Notifications: Background notifications when recipes complete
- Instagram Integration: Extract recipes from Instagram posts and stories
- Tandoor Integration: Automatic upload to Tandoor Recipe Manager
- PWA Support: Installable Progressive Web App with offline capabilities
User Experience
- Queue Dashboard: Monitor all recipe extractions in real-time
- Share Integration: Browser share target for easy URL submission
- Responsive Design: Works on desktop, tablet, and mobile
- Error Recovery: Retry failed extractions with one click
- Progress Tracking: Visual progress through extraction phases
Technical Architecture
- SvelteKit Frontend: Modern reactive UI with TypeScript
- Hexagonal Architecture: Clean separation of concerns
- In-Memory Queue: High-performance processing with configurable concurrency
- Three-Phase Pipeline: Extraction → Parsing → Uploading
- Comprehensive Testing: 138 tests covering all components
📋 API Endpoints
Queue Management
POST /api/queue- Enqueue Instagram URL for processingGET /api/queue- List queue items with filtering and paginationGET /api/queue/{id}- Get specific queue item detailsPOST /api/queue/{id}/retry- Retry failed itemGET /api/queue/stream- Server-Sent Events for real-time updates
Push Notifications
POST /api/notifications/subscribe- Subscribe to push notificationsDELETE /api/notifications/subscribe- Unsubscribe from notificationsGET /api/notifications/vapid-key- Get VAPID public key
Legacy Endpoints (Deprecated)
- UsePOST /api/extract/api/queueinstead- UseGET /api/extract-stream/api/queue/streaminstead
🛠 Development Setup
Prerequisites
- Node.js 18+
- npm or pnpm
- Tandoor Recipe Manager instance (optional)
- LLM API access (OpenAI, Anthropic, or local)
Installation
# Clone the repository
git clone <repository-url>
cd insta-recipe
# Install dependencies
npm install
# Copy environment template
cp .env.example .env
# Configure your environment variables (see Configuration section)
Local Development
# Start development server with HTTPS
npm run dev
# Open in browser (certificate must be trusted)
open https://localhost:5173
The app runs on HTTPS by default for:
- Service worker support (required for PWA)
- Push notifications
- Browser share target API
- Instagram cookie handling
SSL Certificate Setup
The application uses HTTPS in development with SSL certificates signed by an external Caddy CA container. The current certificate is valid until December 20, 2035 (10 years).
Certificate Information:
- Location:
.ssl/directory - CA Certificate:
.ssl/root.crt(already trusted on the system) - Server Certificate:
.ssl/localhost.crt - Server Private Key:
.ssl/localhost.key
Since the Caddy CA is already trusted on the system, the certificate should work without additional trust steps. If you encounter browser warnings:
Linux (Ubuntu/Debian):
sudo cp .ssl/root.crt /usr/local/share/ca-certificates/caddy-local.crt
sudo update-ca-certificates
Chrome/Chromium:
- Go to
chrome://settings/certificates - Click "Authorities" → "Import"
- Select
.ssl/root.crt - Check "Trust this certificate for identifying websites"
Checking Certificate Expiration:
openssl x509 -enddate -noout -in .ssl/localhost.crt
Regenerating the Certificate (if needed):
If the certificate expires or needs to be regenerated:
# Identify the Caddy container (usually named caddy-local)
CADDY_CONTAINER="caddy-local"
# Copy Caddy's CA certificate and private key
docker cp $CADDY_CONTAINER:/data/caddy/pki/authorities/local/root.crt .ssl/root.crt
docker cp $CADDY_CONTAINER:/data/caddy/pki/authorities/local/root.key .ssl/caddy-ca.key
# Generate new server private key
openssl genrsa -out .ssl/localhost.key 2048
# Generate Certificate Signing Request (CSR)
openssl req -new \
-key .ssl/localhost.key \
-out .ssl/localhost.csr \
-subj "/O=Caddy Local Authority/CN=localhost"
# Create OpenSSL config for Subject Alternative Names
cat > .ssl/localhost.ext << 'EOF'
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
DNS.2 = *.localhost
IP.1 = 127.0.0.1
IP.2 = ::1
EOF
# Sign certificate with Caddy's CA (10 years = 3650 days)
openssl x509 -req \
-in .ssl/localhost.csr \
-CA .ssl/root.crt \
-CAkey .ssl/caddy-ca.key \
-CAcreateserial \
-out .ssl/localhost.crt \
-days 3650 \
-sha256 \
-extfile .ssl/localhost.ext
# Cleanup temporary files and set permissions
rm .ssl/localhost.csr .ssl/localhost.ext .ssl/caddy-ca.key .ssl/root.srl
chmod 600 .ssl/localhost.key
chmod 644 .ssl/localhost.crt .ssl/root.crt
# Verify the certificate
openssl verify -CAfile .ssl/root.crt .ssl/localhost.crt
⚙️ Configuration
Environment Variables
Create a .env file with the following variables:
# LLM Configuration
LLM_API_BASE_URL=https://api.openai.com/v1
LLM_API_KEY=your-api-key
LLM_MODEL=gpt-4o-mini
# Tandoor Integration (optional)
TANDOOR_BASE_URL=https://your-tandoor.com
TANDOOR_API_KEY=your-tandoor-token
# Queue Processing
QUEUE_CONCURRENCY=2
QUEUE_TIMEOUT_MS=30000
# Push Notifications (optional)
VAPID_PUBLIC_KEY=your-vapid-public-key
VAPID_PRIVATE_KEY=your-vapid-private-key
# Instagram Authentication (optional)
AUTH_SCHEDULER_ENABLED=true
AUTH_SCHEDULER_INTERVAL_MINUTES=720
Tandoor Setup
To automatically upload extracted recipes to Tandoor:
- Create an API token in your Tandoor instance
- Set
TANDOOR_BASE_URLandTANDOOR_API_KEYin.env - Recipes will be automatically uploaded after successful extraction
Push Notifications
To enable web push notifications:
- Generate VAPID keys:
npx web-push generate-vapid-keys - Set
VAPID_PUBLIC_KEYandVAPID_PRIVATE_KEYin.env - Users can enable notifications in the dashboard settings
🏗 Architecture Overview
Queue System
User submits URL → Queue Manager → Queue Processor
↓
Extraction Phase ← → Parsing Phase ← → Upload Phase
↓
Push Notifications ← → SSE Updates ← → Dashboard Updates
Processing Pipeline
- Extraction Phase: Browser automation extracts text and images
- Parsing Phase: LLM converts text to structured recipe data
- Upload Phase: Automatic upload to Tandoor (if configured)
Each phase tracks progress and can fail independently with proper error handling.
Error Classification
- Recoverable Errors (
unhealthy): Temporary issues, can be retried - Non-recoverable Errors (
error): Invalid URLs, parsing failures, etc.
🧪 Testing
# Run all tests
npm test
# Run specific test suites
npm run test:unit # Unit tests only
npm run test:client # Browser tests only
npm run test:server # Server tests only
# Run tests in watch mode
npm run test:watch
Test Coverage:
- 138 total tests covering all major components
- Queue Manager: 28 tests
- Queue Processor: 5 integration tests
- API Endpoints: 17 tests
- SSE Streaming: 6 tests
- Frontend Components: Browser tests
📦 Building & Deployment
Production Build
# Build for production
npm run build
# Preview production build locally
npm run preview
Deployment
The app is built as a Node.js application with the following outputs:
/.svelte-kit/output/server/- Server bundle/.svelte-kit/output/client/- Static assets/build/- Adapter output
Deploy the server bundle with:
node build/index.js
Docker Deployment
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY build ./build
EXPOSE 3000
CMD ["node", "build"]
🔄 Migration from Synchronous System
What Changed
The app was migrated from a synchronous extraction system to an async queue-based system:
Before (Synchronous):
- User waited for entire extraction process to complete
- No progress tracking during processing
- No retry capability for failures
- Single-threaded processing
- Limited error handling
After (Async Queue):
- Fire-and-forget: submit URL and redirect immediately
- Real-time progress tracking via SSE
- Comprehensive retry system for failures
- Concurrent processing (configurable)
- Detailed error classification and reporting
- Push notifications for background updates
API Migration
Old Synchronous Endpoints (Deprecated):
POST /api/extract # Submit URL and wait for completion
GET /api/extract-stream # Long-polling for progress
New Queue Endpoints:
POST /api/queue # Submit URL, get queue ID immediately
GET /api/queue # List all queue items
GET /api/queue/{id} # Get specific item status
POST /api/queue/{id}/retry # Retry failed items
GET /api/queue/stream # Real-time SSE updates
Migration Steps
If migrating from the old system:
- Update Client Code: Replace
/api/extractcalls with/api/queue - Handle Async Responses: Process queue ID instead of waiting for completion
- Add Progress Tracking: Implement SSE listeners for real-time updates
- Update Error Handling: Handle new error classification system
- Add Retry Logic: Implement retry functionality for failed items
Backward Compatibility
The legacy endpoints are still available but deprecated:
- They will return
410 Gonestatus with migration instructions - Support will be removed in a future version
- All new development should use the queue endpoints
🤝 Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes with tests
- Run the test suite (
npm test) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Guidelines
- Follow TypeScript strict mode
- Add tests for all new functionality
- Use the existing architecture patterns (Hexagonal Architecture)
- Update documentation for API changes
- Ensure PWA functionality remains intact
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments
- SvelteKit - Application framework
- Tandoor Recipe Manager - Recipe management system
- Workbox - PWA capabilities
- fastq - High-performance queue processing