11 KiB
Flyer URL Configuration
Overview
Flyer image and icon URLs are environment-specific to ensure they point to the correct server for each deployment. Images are served as static files by NGINX from the /flyer-images/ path with 7-day browser caching enabled.
Environment-Specific URLs
| Environment | Base URL | Example |
|---|---|---|
| Dev Container | https://127.0.0.1 |
https://127.0.0.1/flyer-images/safeway-flyer.jpg |
| Test | https://flyer-crawler-test.projectium.com |
https://flyer-crawler-test.projectium.com/flyer-images/safeway-flyer.jpg |
| Production | https://flyer-crawler.projectium.com |
https://flyer-crawler.projectium.com/flyer-images/safeway-flyer.jpg |
Note: The dev container accepts connections to both https://localhost/ and https://127.0.0.1/ thanks to the SSL certificate and NGINX configuration. See SSL Certificate Configuration below.
SSL Certificate Configuration (Dev Container)
The dev container uses self-signed certificates generated by mkcert to enable HTTPS locally. This configuration solves a common mixed-origin SSL issue.
The Problem
When users access the site via https://localhost/ but image URLs in the database use https://127.0.0.1/..., browsers treat these as different origins. This causes ERR_CERT_AUTHORITY_INVALID errors when loading images, even though both hostnames point to the same server.
The Solution
-
Certificate Generation (
Dockerfile.dev):mkcert localhost 127.0.0.1 ::1This creates a certificate with Subject Alternative Names (SANs) for all three hostnames.
-
NGINX Configuration (
docker/nginx/dev.conf):server_name localhost 127.0.0.1;NGINX accepts requests to both hostnames using the same SSL certificate.
How It Works
| Component | Configuration |
|---|---|
| SSL Certificate SANs | localhost, 127.0.0.1, ::1 |
NGINX server_name |
localhost 127.0.0.1 |
| Seed Script URLs | Uses https://127.0.0.1 (works with DB constraints) |
| User Access | Either https://localhost/ or https://127.0.0.1/ |
Why This Matters
- Database Constraints: The
flyerstable has CHECK constraints requiring URLs to start withhttp://orhttps://. Relative URLs are not allowed. - Consistent Behavior: Users can access the site using either hostname without SSL warnings.
- Same Certificate: Both hostnames use the same self-signed certificate, eliminating mixed-content errors.
Verifying the Configuration
# Check certificate SANs
podman exec flyer-crawler-dev openssl x509 -in /app/certs/localhost.crt -text -noout | grep -A1 "Subject Alternative Name"
# Expected output:
# X509v3 Subject Alternative Name:
# DNS:localhost, IP Address:127.0.0.1, IP Address:0:0:0:0:0:0:0:1
# Test both hostnames respond
curl -k https://localhost/health
curl -k https://127.0.0.1/health
Troubleshooting SSL Issues
If you encounter ERR_CERT_AUTHORITY_INVALID:
- Check NGINX is running:
podman exec flyer-crawler-dev nginx -t - Verify certificate exists:
podman exec flyer-crawler-dev ls -la /app/certs/ - Ensure both hostnames are in server_name: Check
/etc/nginx/sites-available/default - Rebuild container if needed: The certificate is generated at build time
Permanent Fix: Install CA Certificate (Recommended)
To permanently eliminate SSL certificate warnings, install the mkcert CA certificate on your system. This is optional but provides a better development experience.
The CA certificate is located at certs/mkcert-ca.crt in the project root. See certs/README.md for platform-specific installation instructions (Windows, macOS, Linux, Firefox).
After installation:
- Your browser will trust all mkcert certificates without warnings
- Both
https://localhost/andhttps://127.0.0.1/will work without SSL errors - Flyer images will load without
ERR_CERT_AUTHORITY_INVALIDerrors
See also: Debugging Guide - SSL Issues
NGINX Static File Serving
All environments serve flyer images as static files with browser caching:
# Serve flyer images from static storage (7-day cache)
location /flyer-images/ {
alias /path/to/flyer-images/;
expires 7d;
add_header Cache-Control "public, immutable";
}
Directory Paths by Environment
| Environment | NGINX Alias Path |
|---|---|
| Dev Container | /app/public/flyer-images/ |
| Test | /var/www/flyer-crawler-test.projectium.com/flyer-images/ |
| Production | /var/www/flyer-crawler.projectium.com/flyer-images/ |
Configuration
Environment Variable
Set FLYER_BASE_URL in your environment configuration:
# Dev container (.env)
FLYER_BASE_URL=https://localhost
# Test environment
FLYER_BASE_URL=https://flyer-crawler-test.projectium.com
# Production
FLYER_BASE_URL=https://flyer-crawler.projectium.com
Seed Script
The seed script (src/db/seed.ts) automatically uses the correct base URL based on:
FLYER_BASE_URLenvironment variable (if set)NODE_ENVvalue:production→https://flyer-crawler.projectium.comtest→https://flyer-crawler-test.projectium.com- Default →
https://localhost
The seed script also copies test images from src/tests/assets/ to public/flyer-images/:
test-flyer-image.jpg- Sample flyer imagetest-flyer-icon.png- Sample 64x64 icon
Updating Existing Data
If you need to update existing flyer URLs in the database, use the provided SQL script:
Dev Container
# Connect to dev database
podman exec -it flyer-crawler-postgres psql -U postgres -d flyer_crawler_dev
# Run the update (dev container uses HTTPS with self-signed certs)
UPDATE flyers
SET
image_url = REPLACE(image_url, 'example.com', 'localhost'),
icon_url = REPLACE(icon_url, 'example.com', 'localhost')
WHERE
image_url LIKE '%example.com%'
OR icon_url LIKE '%example.com%';
# Verify
SELECT flyer_id, image_url, icon_url FROM flyers;
Test Environment
# Via SSH
ssh root@projectium.com "psql -U flyer_crawler_test -d flyer-crawler-test -c \"
UPDATE flyers
SET
image_url = REPLACE(image_url, 'example.com', 'flyer-crawler-test.projectium.com'),
icon_url = REPLACE(icon_url, 'example.com', 'flyer-crawler-test.projectium.com')
WHERE
image_url LIKE '%example.com%'
OR icon_url LIKE '%example.com%';
\""
Production
# Via SSH
ssh root@projectium.com "psql -U flyer_crawler_prod -d flyer-crawler-prod -c \"
UPDATE flyers
SET
image_url = REPLACE(image_url, 'example.com', 'flyer-crawler.projectium.com'),
icon_url = REPLACE(icon_url, 'example.com', 'flyer-crawler.projectium.com')
WHERE
image_url LIKE '%example.com%'
OR icon_url LIKE '%example.com%';
\""
Test Data Updates
Test Helper Function
A helper function getFlyerBaseUrl() is available in src/tests/utils/testHelpers.ts that automatically detects the correct base URL for tests:
export const getFlyerBaseUrl = (): string => {
if (process.env.FLYER_BASE_URL) {
return process.env.FLYER_BASE_URL;
}
// Check if we're in dev container (DB_HOST=postgres is typical indicator)
// Use 'localhost' instead of '127.0.0.1' to match the hostname users access
// This avoids SSL certificate mixed-origin issues in browsers
if (process.env.DB_HOST === 'postgres' || process.env.DB_HOST === '127.0.0.1') {
return 'https://localhost';
}
if (process.env.NODE_ENV === 'production') {
return 'https://flyer-crawler.projectium.com';
}
if (process.env.NODE_ENV === 'test') {
return 'https://flyer-crawler-test.projectium.com';
}
// Default for unit tests
return 'https://example.com';
};
Updated Test Files
The following test files now use getFlyerBaseUrl() for environment-aware URL generation:
- src/db/seed.ts - Main seed script (uses
FLYER_BASE_URL) - src/tests/utils/testHelpers.ts -
getFlyerBaseUrl()helper function - src/hooks/useDataExtraction.test.ts - Mock flyer factory
- src/schemas/flyer.schemas.test.ts - Schema validation tests
- src/services/flyerProcessingService.server.test.ts - Processing service tests
- src/tests/integration/flyer-processing.integration.test.ts - Integration tests
This approach ensures tests work correctly in all environments (dev container, CI/CD, local development, test, production).
Files Changed
| File | Change |
|---|---|
src/db/seed.ts |
Added FLYER_BASE_URL environment variable support, copies test images to public/flyer-images/ |
docker/nginx/dev.conf |
Added /flyer-images/ location block for static file serving |
.env.example |
Added FLYER_BASE_URL variable |
sql/update_flyer_urls.sql |
SQL script for updating existing data |
| Test files | Updated mock data to use https://localhost |
Summary
- Seed script now uses environment-specific HTTPS URLs
- Seed script copies test images from
src/tests/assets/topublic/flyer-images/ - NGINX serves
/flyer-images/as static files with 7-day cache - Test files updated with
https://localhost(not127.0.0.1to avoid SSL mixed-origin issues) - SQL script provided for updating existing data
- Documentation updated for each environment