158 lines
6.3 KiB
Plaintext
158 lines
6.3 KiB
Plaintext
# Copy this file to .env and fill in your own values.
|
|
# Nothing here is a real credential.
|
|
#
|
|
# The host-side values below are for running the backend directly with uvicorn
|
|
# (python run_project.py). When the backend runs INSIDE Docker via the root
|
|
# docker-compose.yml, four of them are overridden by compose and you do not
|
|
# need to change them here:
|
|
#
|
|
# DB_HOST -> postgres (service name)
|
|
# DB_PORT -> 5432
|
|
# DB_PASSWORD -> POSTGRES_PASSWORD from the root .env
|
|
# OLLAMA_BASE_URL -> http://ollama:11434
|
|
#
|
|
# The reason is worth internalising: inside a container, `localhost` is the
|
|
# container itself, not the host and not a sibling container. A container-bound
|
|
# localhost URL points the backend at its own empty ports. Containers reach
|
|
# each other by service name over the compose network instead.
|
|
|
|
# --- Authentication (REQUIRED) -------------------------------------------
|
|
# The backend will not start without these while AUTH_ENABLED=true. Generate
|
|
# all four lines, plus sign-in passwords, with:
|
|
#
|
|
# python scripts/make_auth_secrets.py
|
|
#
|
|
# They guard the 18 write/compute endpoints - catalog generation, ML training,
|
|
# the upload endpoints and chat. CORS is not a substitute: browsers enforce it,
|
|
# curl ignores it entirely.
|
|
#
|
|
# AUTH_ENABLED=false turns every guard off and makes the whole API open again.
|
|
# It exists so a fresh checkout runs before you have generated secrets. Never
|
|
# set it false on a host reachable from the internet.
|
|
AUTH_ENABLED=true
|
|
|
|
# Signs access tokens. Changing it signs everybody out, which is how you revoke
|
|
# every issued token at once. Use a DIFFERENT value in production from the one
|
|
# on your laptop - a secret that has been on a dev machine is not a secret.
|
|
AUTH_SECRET_KEY=
|
|
|
|
# Only PBKDF2 digests are stored, never passwords. `make_auth_secrets.py`
|
|
# prints the password once and the hash to paste here; it cannot be reversed,
|
|
# so rerun the script to change a password.
|
|
AUTH_ADMIN_USERNAME=admin
|
|
AUTH_ADMIN_PASSWORD_HASH=
|
|
AUTH_USER_USERNAME=user
|
|
AUTH_USER_PASSWORD_HASH=
|
|
|
|
# Token lifetime in minutes. 12h by default: one sign-in per working day, and
|
|
# a leaked token expires by itself.
|
|
AUTH_TOKEN_TTL_MINUTES=720
|
|
|
|
# Failed-login throttle, per username+IP. Stops the login endpoint being an
|
|
# unlimited password oracle once it is on the internet.
|
|
AUTH_MAX_LOGIN_ATTEMPTS=10
|
|
AUTH_LOCKOUT_SECONDS=300
|
|
|
|
# Local development only: accept ANY password at /api/auth/login. The username
|
|
# still picks the role (`admin` -> admin pages, `user` -> user pages), and the
|
|
# token issued is a normal one, so every other guard behaves normally. Unlike
|
|
# AUTH_ENABLED=false it leaves the login page working - it just stops checking
|
|
# the password. Anyone who can reach the port becomes admin: keep it false here.
|
|
AUTH_ALLOW_ANY_LOGIN=false
|
|
|
|
# Machine consumers of api.<domain> - scripts, partner integrations, your own
|
|
# backends. Format: name:role:secret, comma-separated, role is admin or user.
|
|
# Callers send the secret as an X-API-Key header.
|
|
#
|
|
# One entry per consumer, always: a shared key cannot be revoked for one caller
|
|
# without breaking every other. Mint them with:
|
|
# python scripts/make_auth_secrets.py --api-key partner-x:user
|
|
#
|
|
# Leave empty if only the web app calls the API - it signs in through
|
|
# /api/auth/login instead, and a key nobody needs is only risk.
|
|
API_KEYS=
|
|
|
|
USE_OLLAMA=true
|
|
OLLAMA_BASE_URL=http://localhost:11434
|
|
OLLAMA_MODEL_NAME=qwen2.5:1.5b
|
|
|
|
USE_EMBEDDINGS=true
|
|
EMBEDDINGS_MODEL=sentence-transformers/all-MiniLM-L6-v2
|
|
|
|
USE_PGVECTOR=true
|
|
DB_HOST=localhost
|
|
DB_PORT=5432
|
|
DB_NAME=pgvector
|
|
DB_USER=postgres
|
|
DB_PASSWORD=changeme
|
|
|
|
USE_S3=true
|
|
S3_ACCESS_KEY=your-do-spaces-key
|
|
S3_SECRET_KEY=your-do-spaces-secret
|
|
S3_ENDPOINT=https://your-space.region.digitaloceanspaces.com
|
|
S3_BUCKET=your-bucket
|
|
S3_REGION=region
|
|
|
|
# Optional - leave USE_GOOGLE_CSE=false if you don't have a Google CSE key
|
|
USE_GOOGLE_CSE=false
|
|
GOOGLE_API_KEY=
|
|
GOOGLE_CSE_ID=
|
|
|
|
USE_DDG_IMAGES=true
|
|
USE_OPEN_FACTS=true
|
|
USE_WIKIMEDIA=true
|
|
USE_PLAYWRIGHT_FALLBACK=true
|
|
|
|
MIN_IMAGE_BYTES=3000
|
|
|
|
# Product SKU: try a live web search for a real marketplace product ID
|
|
# (Amazon ASIN, Flipkart PID, etc.) before falling back to an internal SKU.
|
|
# Set to false to always generate internal SKUs only (faster, offline-safe).
|
|
ENABLE_SKU_WEB_LOOKUP=true
|
|
|
|
# Deterministic product validation (see app/services/product_validator.py).
|
|
# Final gate applied to every catalog row before it's kept; rows scoring
|
|
# below VALIDATION_REJECT_THRESHOLD are dropped (with reasons recorded),
|
|
# rows below VALIDATION_REVIEW_THRESHOLD are kept but flagged for human review.
|
|
ENABLE_PRODUCT_VALIDATION=true
|
|
VALIDATION_REJECT_THRESHOLD=0.35
|
|
VALIDATION_REVIEW_THRESHOLD=0.70
|
|
|
|
# Per-pack-size product images (see docs/PER_VARIANT_IMAGES.md). When true,
|
|
# each size variant of a product (e.g. 100g/200g/500g) gets its own
|
|
# size-targeted image search/upload instead of sharing one generic image
|
|
# set. Falls back to the old shared behaviour automatically whenever no
|
|
# size-specific match is found, so this is safe to leave on. Set to
|
|
# "false" to fully restore the old shared-image behaviour.
|
|
ENABLE_PER_VARIANT_IMAGES=true
|
|
# Max image candidates fetched per size variant (kept smaller than the
|
|
# ~24 used for the general product-level search, since a per-size search
|
|
# only needs a handful of genuinely matching candidates).
|
|
PER_VARIANT_IMAGE_MAX_RESULTS=10
|
|
|
|
# Barcode Retrieval & Product Enrichment (see
|
|
# app/services/enrichment/barcode/ and docs/BARCODE_ENRICHMENT.md). Set to
|
|
# false to disable barcode lookup entirely (rows are stored with
|
|
# barcode=NULL, barcode_lookup_status="disabled").
|
|
ENABLE_BARCODE_LOOKUP=true
|
|
BARCODE_LOOKUP_TIMEOUT_SECONDS=10
|
|
# 30 days, in seconds
|
|
BARCODE_LOOKUP_CACHE_TTL_SECONDS=2592000
|
|
BARCODE_LOOKUP_MAX_CONCURRENCY=5
|
|
BARCODE_COUNTRY_TAG=india
|
|
|
|
# GS1 India tier - leave both blank to skip (no free public API exists at
|
|
# time of writing; the cascade falls through to Open Food Facts). Fill in
|
|
# if/when real GS1 India ("Verified by GS1") API access is provisioned.
|
|
GS1_INDIA_API_BASE_URL=
|
|
GS1_INDIA_API_KEY=
|
|
|
|
# UPCItemDB - works out of the box on the free trial tier (no key needed,
|
|
# rate limited ~100 req/day). Set to switch to the paid production endpoint.
|
|
UPC_DATABASE_API_KEY=
|
|
|
|
# Lowest-trust fallback tier: DuckDuckGo search + manufacturer page text
|
|
# scan for a labelled barcode (uses the same `ddgs` dependency as SKU
|
|
# lookup). Set to false to skip this tier.
|
|
ENABLE_MANUFACTURER_SITE_LOOKUP=true
|