))
}
diff --git a/src/content/docs/docs/features/facial-recognition.md b/src/content/docs/docs/features/facial-recognition.md
index f67c49dc..cc7c0d89 100644
--- a/src/content/docs/docs/features/facial-recognition.md
+++ b/src/content/docs/docs/features/facial-recognition.md
@@ -115,7 +115,7 @@ The service is configured independently, via its own `.env` file (copy `.env.exa
| `VISION_FACE_MAX_FACES_PER_PHOTO` | `10` | Maximum faces included in a callback payload. |
| `VISION_FACE_MIN_FACE_SIZE_PIXELS` | `0` | Minimum face size in pixels; `0` disables this filter. |
| `VISION_FACE_BLUR_THRESHOLD` | `0.5` | Laplacian variance threshold; blurry faces below this are discarded. |
-| `VISION_FACE_CLUSTER_EPS` | `0.6` | DBSCAN epsilon (max cosine distance) used for face clustering. |
+| `VISION_FACE_CLUSTER_EPS` | `0.3` | DBSCAN epsilon (max cosine distance) used for face clustering. |
### Storage
@@ -193,7 +193,7 @@ The container exposes interactive API docs at `/docs` and a health check at `/he
- Try a different `VISION_FACE_DETECTOR_BACKEND` (`retinaface`, `mtcnn`, `opencv`, `ssd`) — detectors vary in recall depending on pose, lighting, and image resolution.
**Clustering groups unrelated faces together, or fails to group similar faces:**
-- Clustering is controlled by `VISION_FACE_CLUSTER_EPS` (default `0.6`), the DBSCAN epsilon (maximum cosine distance) allowed within a cluster. Lower it if dissimilar people are being grouped together; raise it if the same person is being split across multiple clusters.
+- Clustering is controlled by `VISION_FACE_CLUSTER_EPS` (default `0.3`), the DBSCAN epsilon (maximum cosine distance) allowed within a cluster. Lower it if dissimilar people are being grouped together; raise it if the same person is being split across multiple clusters.
- To apply a new value: update `VISION_FACE_CLUSTER_EPS` in the microservice's `.env`, restart the microservice so it picks up the change, then re-trigger clustering from _Settings ⇒ Maintenance ⇒ Run Clustering_ in Lychee. This re-clusters every currently unassigned face with the new epsilon — repeat with a different value if the result still isn't right.
- The `VISION_FACE_MODEL_NAME` (default `ArcFace`) recognition model determines embedding quality — a different model may produce more consistent embeddings for your photo set, but changing it does not retroactively update existing embeddings.
- Poor detections (blurry, low-confidence, or partial faces — see above) produce noisy embeddings that cluster poorly; fixing detection quality often improves clustering as a side effect.
diff --git a/src/data/wizard/docker-compose.yaml b/src/data/wizard/docker-compose.yaml
new file mode 100644
index 00000000..7e4b21d1
--- /dev/null
+++ b/src/data/wizard/docker-compose.yaml
@@ -0,0 +1,759 @@
+# Docker Compose configuration for Lychee application with FrankenPHP backend
+# Version: 2026-01-10
+
+# You can set up secrets files for sensitive data like database passwords.
+# Create a 'secrets' directory and add files 'db_password' and 'db_master_password'
+# with appropriate permissions (readable only by the owner).
+# Then uncomment the 'secrets' section below.
+
+# secrets:
+# db_password:
+# file: ./secrets/db_password
+# db_master_password:
+# file: ./secrets/db_master_password
+# app_key:
+# file: ./secrets/app_key
+
+# More generally, EVERY confidential value backing config/services.php (the
+# third-party OAuth/AWS/mail/AI-vision credentials further below) also supports
+# the same Docker/Kubernetes secrets convention: set "_FILE" to the path of
+# a mounted file instead of setting "" directly, and leave "" itself
+# unset. This is resolved by docker/scripts/01-validate-env.sh at container
+# startup, before Lychee/PHP ever runs. See the commented "_FILE" examples next
+# to each credential below.
+
+##########################################################################################
+# Common settings for Lychee services
+##########################################################################################
+x-base-lychee-setup:
+ # There are two main images: `latest` and `edge`.
+ # `latest` is mapped to the last published version
+ # `edge` is mapped to the last build on master branch (may be unstable)
+ #
+ # There are also other images available which uses nginx as a base instead of FrankenPHP:
+ # - ghcr.io/lycheeorg/lychee:latest-legacy
+ # - ghcr.io/lycheeorg/lychee:edge-legacy
+ &base-lychee-setup
+ image: ghcr.io/lycheeorg/lychee:latest
+
+ # The following lines are for development purposes only.
+ # image: lychee-frankenphp
+ # image: lychee-legacy:latest
+ # build:
+ # context: ./app
+ # dockerfile: Dockerfile
+ # args:
+ # NODE_ENV: "${NODE_ENV:-production}"
+ restart: unless-stopped # Auto-restart at container level (outer layer)
+
+ # Security hardening
+ security_opt:
+ - no-new-privileges:true
+ - seccomp:unconfined # FrankenPHP may need this; consider custom seccomp profile
+ cap_drop:
+ - ALL
+ cap_add:
+ - CHOWN
+ - SETGID
+ - SETUID
+ - DAC_OVERRIDE
+ - NET_BIND_SERVICE
+ read_only: false # Laravel needs write access to storage/cache
+ tmpfs:
+ - /tmp:noexec,nosuid,nodev,size=100m
+
+ # Resource limits
+ deploy:
+ resources:
+ limits:
+ cpus: '2'
+ memory: 2G
+ reservations:
+ cpus: '0.5'
+ memory: 512M
+
+ # To configure Lychee, you can do it via:
+ # - environment variables (see the environment section below)
+ # - .env file (recommended for sensitive data)
+ env_file:
+ - path: ./.env
+
+ # If you use docker secrets, uncomment the following lines and
+ # create the secrets files as described at the top of this file.
+ # secrets:
+ # - db_password
+ # - app_key
+ volumes:
+ # Mount local directories for persistent storage
+ # Uploads : where your photos are going to be stored
+ - ./lychee/uploads:/app/public/uploads
+ # Logs: to see what went wrong (or right)
+ - ./lychee/logs:/app/storage/logs
+ # Temporary files: where your uploads are stored temporarily
+ # while waiting for jobs to process them.
+ - ./lychee/tmp:/app/storage/tmp
+
+ networks:
+ - lychee
+
+x-common-env:
+ # User and Group IDs for the www-data user inside the container
+ # Note that this will impact your permissions on mounted volumes.
+ # By default Lychee does not run as root inside the container.
+ #
+ # Ensure that the PUID and PGID match the owner of the mounted volumes
+ # to avoid permission issues.
+ #
+ # You can also set it to 33 (www-data) if your files are owned by www-data.
+ &common-env
+ PUID: "${PUID:-1000}"
+ PGID: "${PGID:-1000}"
+
+ # Or you can activate the following to run as root inside the container. (not recommended)
+ # RUN_AS_ROOT: "yes"
+
+ # Application Key
+ # You can either set it here directly (not recommended for security reasons)
+ # Or load it from .env file
+ # or use Docker secrets as shown above.
+ APP_KEY: "${APP_KEY:-}"
+ # APP_KEY_FILE: "/run/secrets/app_key"
+
+ # Application
+ APP_NAME: "${APP_NAME:-Lychee}"
+ APP_ENV: "${APP_ENV:-production}"
+ APP_DEBUG: "${APP_DEBUG:-false}"
+ APP_TIMEZONE: "${TIMEZONE:-UTC}"
+ APP_URL: "${APP_URL:-http://localhost:8000}"
+ APP_FORCE_HTTPS: "${APP_FORCE_HTTPS:-false}"
+ APP_MAINTENANCE_DRIVER: "${APP_MAINTENANCE_DRIVER:-file}"
+
+ # enable or disable debug bar. By default it is disabled.
+ # Do note that this disable CSP!!
+ # DEBUGBAR_ENABLED: "${DEBUGBAR_ENABLED:-false}"
+
+ # enable or disable log viewer. By default it is enabled.
+ # Unfortunately, log viewer is not available in production due to an upstream bug. :(
+ # you will need to set APP_ENV=local to enable it.
+ LOG_VIEWER_ENABLED: "${LOG_VIEWER_ENABLED:-true}"
+
+ # Sometimes 404 errors are very noisy in the logs.
+ # You can disable logging them by setting this to false.
+ LOG_404_ERRORS: "${LOG_404_ERRORS:-true}"
+
+ # enable or disable clockwork. By default it is disabled (and not provided on non-dev build).
+ CLOCKWORK_ENABLE: "${CLOCKWORK_ENABLE:-false}"
+
+ # enable or disable latency debug: adds a specific amount of time in milliseconds to wait before processing requests.
+ # Always disabled on production environment.
+ # APP_DEBUG_LATENCY: 0
+
+ # All API requests to have the header "content-type: application/json"
+ # or "content-type: multipart/form-data" depending on the type.
+ #
+ # If you want to disable this requirement, set this to false.
+ #
+ # This requirement prevents the use of the API from the API documentation page.
+ # REQUIRE_CONTENT_TYPE_ENABLED: "${REQUIRE_CONTENT_TYPE_ENABLED:-true}"
+
+ # enable s3 bucket (required in addition to needing AWS_ACCESS_KEY_ID)
+ # S3_ENABLED: true
+
+ # If you spread old links of to your albums in your Lychee instance starting with
+ # https://lychee.text/#albumID/PhotoId
+ # Set this value to true to enable redirection.
+ # LEGACY_V4_REDIRECT: "${LEGACY_V4_REDIRECT:-false}"
+
+ ##############################################################################
+ # IMPORTANT: To migrate from Lychee v3 you *MUST* use the same MySQL/MariaDB #
+ # server as v3. #
+ ##############################################################################
+
+ # Table prefix (e.g. lychee_) of a Lychee v3 instance for migration
+ # DB_OLD_LYCHEE_PREFIX:
+
+ # DB_CONNECTION can be sqlite, mysql or pgsql. For sqlite the other entries are
+ # not required, but an existing sqlite3 database may be specified if desired.
+ # In this case, please use an absolute path. DB_DATABASE may be omitted but should
+ # *not* be left blank.
+ #
+ # Note that if DB_PASSWORD includes special characters, it must be enclosed in quotes.
+ # e.g. DB_PASSWORD: "lychee!@#$%^&"
+ DB_CONNECTION: "${DB_CONNECTION:-mysql}"
+ DB_HOST: "${DB_HOST:-lychee_db}"
+ DB_PORT: "${DB_PORT:-3306}"
+ DB_DATABASE: "${DB_DATABASE:-lychee}"
+ DB_USERNAME: "${DB_USERNAME:-lychee}"
+
+ # Use secrets for sensitive data - see Docker secrets or vault integration
+ # DB_PASSWORD should come from .env file only
+ DB_PASSWORD: "${DB_PASSWORD:-password}"
+ #
+ # Or you can uncomment the following line to use DB_PASSWORD_FILE from secrets
+ # DB_PASSWORD_FILE: "/run/secrets/db_password"
+
+ ###################################################################
+ # Keygen License Management #
+ ###################################################################
+
+ # API token obtained from keygen.lycheeorg.dev.
+ # When set, Lychee will automatically rotate an expired license key
+ # on admin login and check the token health on the diagnostics page.
+ KEYGEN_API_KEY: "${KEYGEN_API_KEY:-}"
+ # Or you can uncomment the following line to use KEYGEN_API_KEY_FILE from secrets
+ # KEYGEN_API_KEY_FILE: "/run/secrets/keygen_api_key"
+
+ # Session configuration
+ SESSION_DRIVER: "${SESSION_DRIVER:-file}"
+ SESSION_LIFETIME: "${SESSION_LIFETIME:-120}"
+ # SESSION_DRIVER: "${SESSION_DRIVER:-database}"
+ # SESSION_LIFETIME: "${SESSION_LIFETIME:-120}"
+ # SESSION_ENCRYPT: "${SESSION_ENCRYPT:-false}"
+ # SESSION_PATH: "${SESSION_PATH:-/}"
+ # SESSION_DOMAIN: "${SESSION_DOMAIN:-null}"
+
+ # Cache
+ CACHE_STORE: "${CACHE_STORE:-file}"
+ CACHE_PREFIX: "${CACHE_PREFIX:-lychee_cache}"
+ # REDIS_HOST: "lychee_cache"
+ # REDIS_PASSWORD: "null"
+ # REDIS_PORT: "6379"
+ # REDIS_URL=redis://:@:
+
+ # If you use Redis as cache driver, we strongly recommend
+ # to disable it for your Log Viewer.
+ # Should redis crash, you will no longer be able to access your logs.
+ LOG_VIEWER_CACHE_DRIVER: "file"
+
+ # When booting the application, if you have a lot of photos,
+ # Lychee will check the permissions of all files and folders.
+ # This may take a while (even hours depending on the number of photos...)
+ # If you are sure that your permissions are correct, you can skip these checks
+ # by setting the following to "yes".
+ # SKIP_PERMISSIONS_CHECKS: "yes"
+
+ # Queue
+ # The default value for QUEUE_CONNECTION is 'sync', this means that jobs are
+ # executed immediately (synchronously) when dispatched.
+ #
+ # For improved reactivity of Lychee we recommend to use a worker.
+ # You can use either 'database' or 'redis' as queue driver (but in the later you need to enable redis).
+ QUEUE_CONNECTION: "${QUEUE_CONNECTION:-database}"
+
+ # Logging Stuff.
+ # LOG_CHANNEL: "${LOG_CHANNEL:-stack}"
+ # LOG_STACK: "${LOG_STACK:-single}"
+ # LOG_DEPRECATIONS_CHANNEL: "${LOG_DEPRECATIONS_CHANNEL:-null}"
+ # LOG_LEVEL: "${LOG_LEVEL:-debug}"
+ # LOG_STDOUT: "${LOG_STDOUT:-true}"
+
+ # SECURITY_HEADER_HSTS_ENABLE:false
+ # SECURITY_HEADER_CSP_CONNECT_SRC:
+ # SECURITY_HEADER_SCRIPT_SRC_ALLOW:
+ # SECURITY_HEADER_CSP_CHILD_SRC:
+ # SECURITY_HEADER_CSP_FONT_SRC:
+ # SECURITY_HEADER_CSP_FORM_ACTION:
+ # SECURITY_HEADER_CSP_FRAME_ANCESTORS:
+ # SECURITY_HEADER_CSP_FRAME_SRC:
+ # SECURITY_HEADER_CSP_IMG_SRC:
+ # SECURITY_HEADER_CSP_MEDIA_SRC:
+ # SESSION_SECURE_COOKIE:false
+
+ # MAIL_DRIVER:smtp
+ # MAIL_HOST:
+ # MAIL_PORT:
+ # MAIL_USERNAME:
+ # MAIL_PASSWORD:
+ # MAIL_ENCRYPTION:
+ # MAIL_FROM_NAME:
+ # MAIL_FROM_ADDRESS:
+
+ # TRUSTED_PROXIES=null
+
+ # Disable Basic Auth. This means that the only way to authenticate is via the API token or Oauth.
+ # This should only be toggled AFTER having set up the admin account and bound the Oauth client.
+ # DISABLE_BASIC_AUTH=false
+
+ # Disable WebAuthn. This means that the only way to authenticate is via the API token, Basic Auth or Oauth.
+ # DISABLE_WEBAUTHN=false
+
+ ###################################################################
+ # LDAP Authentication (enterprise directory integration) #
+ ###################################################################
+
+ # Enable LDAP authentication alongside or instead of basic auth
+ # LDAP_ENABLED=false
+
+ # LDAP Server connection settings
+ # LDAP_HOST=ldap.example.com
+ # LDAP_PORT=389
+ # For LDAPS (LDAP over SSL), use port 636
+ # LDAP_PORT=636
+
+ # Base DN for LDAP searches (e.g., dc=example,dc=com or dc=corp,dc=example,dc=com)
+ # LDAP_BASE_DN=dc=example,dc=com
+
+ # Service account credentials for LDAP bind
+ # This account needs read-only access to user and group attributes
+ # LDAP_BIND_DN=cn=lychee-service,ou=services,dc=example,dc=com
+ # LDAP_BIND_PASSWORD=securepassword
+
+ # LDAP user search filter (%s is replaced with username)
+ # For OpenLDAP:
+ # LDAP_USER_FILTER=(&(objectClass=person)(uid=%s))
+ # For Active Directory:
+ # LDAP_USER_FILTER=(&(objectClass=user)(sAMAccountName=%s))
+
+ # LDAP attribute mapping (maps LDAP attributes to Lychee user fields)
+ # OpenLDAP defaults:
+ # LDAP_ATTR_USERNAME=uid
+ # LDAP_ATTR_EMAIL=mail
+ # LDAP_ATTR_DISPLAY_NAME=displayName
+ # Active Directory alternatives:
+ # LDAP_ATTR_USERNAME=sAMAccountName
+ # LDAP_ATTR_EMAIL=userPrincipalName
+ # LDAP_ATTR_DISPLAY_NAME=displayName
+
+ # Admin role mapping via LDAP group
+ # Users in this group will have may_administrate=true
+ # LDAP_ADMIN_GROUP_DN=cn=lychee-admins,ou=groups,dc=example,dc=com
+
+ # Auto-provision users on first LDAP login
+ # If false, users must be pre-created in Lychee before they can log in via LDAP
+ # LDAP_AUTO_PROVISION=true
+
+ # TLS/SSL settings for secure LDAP connections
+ # LDAP_USE_TLS=true
+ # LDAP_TLS_VERIFY_PEER=true
+
+ # Connection timeout in seconds
+ # LDAP_CONNECTION_TIMEOUT=5
+
+ # Oauth token data
+ # XXX_REDIRECT_URI should be left as default unless you know exactly what you do.
+
+ # AMAZON_SIGNIN_CLIENT_ID=
+ # AMAZON_SIGNIN_SECRET=
+ # AMAZON_SIGNIN_SECRET_FILE=/run/secrets/amazon_signin_secret
+ # AMAZON_SIGNIN_REDIRECT_URI=/auth/amazon/redirect
+
+ # https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple
+ # Note: the client secret used for "Sign In with Apple" is a JWT token that can have a maximum lifetime of 6 months.
+ # The article above explains how to generate the client secret on demand and you'll need to update this every 6 months.
+ # To generate the client secret for each request, see Generating A Client Secret For Sign In With Apple On Each Request.
+ # https://bannister.me/blog/generating-a-client-secret-for-sign-in-with-apple-on-each-request
+ # APPLE_CLIENT_ID=
+ # APPLE_CLIENT_SECRET=
+ # APPLE_CLIENT_SECRET_FILE=/run/secrets/apple_client_secret
+ # APPLE_REDIRECT_URI=/auth/apple/redirect
+
+ # FACEBOOK_CLIENT_ID=
+ # FACEBOOK_CLIENT_SECRET=
+ # FACEBOOK_CLIENT_SECRET_FILE=/run/secrets/facebook_client_secret
+ # FACEBOOK_REDIRECT_URI=/auth/facebook/redirect
+
+ # GITHUB_CLIENT_ID=
+ # GITHUB_CLIENT_SECRET=
+ # GITHUB_CLIENT_SECRET_FILE=/run/secrets/github_client_secret
+ # GITHUB_REDIRECT_URI=/auth/github/redirect
+
+ # GOOGLE_CLIENT_ID=
+ # GOOGLE_CLIENT_SECRET=
+ # GOOGLE_CLIENT_SECRET_FILE=/run/secrets/google_client_secret
+ # GOOGLE_REDIRECT_URI=/auth/google/redirect
+
+ # MASTODON_DOMAIN=https://mastodon.social
+ # MASTODON_ID=
+ # MASTODON_SECRET=
+ # MASTODON_SECRET_FILE=/run/secrets/mastodon_secret
+ # MASTODON_REDIRECT_URI=/auth/mastodon/redirect
+
+ # MICROSOFT_CLIENT_ID=
+ # MICROSOFT_CLIENT_SECRET=
+ # MICROSOFT_CLIENT_SECRET_FILE=/run/secrets/microsoft_client_secret
+ # MICROSOFT_REDIRECT_URI=/auth/microsoft/redirect
+ # MICROSOFT_TENANT_ID=
+
+ # NEXTCLOUD_CLIENT_ID=
+ # NEXTCLOUD_CLIENT_SECRET=
+ # NEXTCLOUD_CLIENT_SECRET_FILE=/run/secrets/nextcloud_client_secret
+ # NEXTCLOUD_REDIRECT_URI=/auth/nextcloud/redirect
+ # NEXTCLOUD_BASE_URI=
+
+ # KEYCLOAK_CLIENT_ID=
+ # KEYCLOAK_CLIENT_SECRET=
+ # KEYCLOAK_CLIENT_SECRET_FILE=/run/secrets/keycloak_client_secret
+ # KEYCLOAK_REDIRECT_URI=/auth/keycloak/redirect
+ # KEYCLOAK_BASE_URL=
+ # KEYCLOAK_REALM=
+
+ # AUTHENTIK_BASE_URL=
+ # AUTHENTIK_CLIENT_ID=
+ # AUTHENTIK_CLIENT_SECRET=
+ # AUTHENTIK_CLIENT_SECRET_FILE=/run/secrets/authentik_client_secret
+ # AUTHENTIK_REDIRECT_URI=/auth/authentik/redirect
+
+ # AUTHELIA_BASE_URL=
+ # AUTHELIA_CLIENT_ID=
+ # AUTHELIA_CLIENT_SECRET=
+ # AUTHELIA_CLIENT_SECRET_FILE=/run/secrets/authelia_client_secret
+ # AUTHELIA_REDIRECT_URI=/auth/authelia/redirect
+
+ # AWS support data
+
+ # AWS_ACCESS_KEY_ID=
+ # AWS_ACCESS_KEY_ID_FILE=/run/secrets/aws_access_key_id
+ # AWS_SECRET_ACCESS_KEY=
+ # AWS_SECRET_ACCESS_KEY_FILE=/run/secrets/aws_secret_access_key
+ # AWS_DEFAULT_REGION=
+ # AWS_BUCKET=
+ # AWS_URL=
+ # AWS_ENDPOINT=
+ # AWS_IMAGE_VISIBILITY=
+ # AWS_USE_PATH_STYLE_ENDPOINT=
+
+ # DISABLE_IMPORT_FROM_SERVER=false
+
+ ###################################################################
+ # Payment integration (requires SE) #
+ ###################################################################
+
+ # Enable test mode (Sandbox mode) for payment gateways.
+ # In test mode, no real money transactions are done.
+ # We set it to true by default for safety. Make sure to set it to false
+ # when you go live.
+ # OMNIPAY_TEST_MODE=true
+
+ # Configuration values for Mollie integration
+ # MOLLIE_API_KEY=
+ # MOLLIE_PROFILE_ID=
+
+ # Configuration values for Stripe integration (NOT WORKING YET, MAYBE LATER)
+ # STRIPE_API_KEY=
+ # STRIPE_PUBLISHABLE_KEY=
+
+ # Configuration values for PayPal integration
+ # PAYPAL_CLIENT_ID=
+ # PAYPAL_SECRET=
+
+ ###################################################################
+ # Facial recognition #
+ ###################################################################
+ AI_VISION_FACE_URL: "http://lychee_facial_recognition:8000"
+ AI_VISION_FACE_API_KEY: "${AI_VISION_API_KEY:-}"
+ # AI_VISION_FACE_API_KEY_FILE: "/run/secrets/ai_vision_face_api_key"
+
+ ###################################################################
+ # NSFW classification (requires SE)
+ ###################################################################
+ # AI_VISION_NSFW_URL: "http://lychee_nsfw_classification:8000"
+ # AI_VISION_NSFW_API_KEY: "${AI_VISION_NSFW_API_KEY:-}"
+ # AI_VISION_NSFW_API_KEY_FILE: "/run/secrets/ai_vision_nsfw_api_key"
+
+
+services:
+ ##########################################################################################
+ # Lychee API Service and frontend.
+ ##########################################################################################
+ lychee_api:
+ <<: *base-lychee-setup
+ # Set up a container name for easier identification
+ container_name: lychee-api
+ expose:
+ - "${APP_PORT:-8000}"
+ ports:
+ - "${APP_PORT:-8000}:8000"
+ environment:
+ <<: *common-env
+ # Performance tuning for FRANKENPHP
+ # For the legacy setup using the -legacy tags, these values are not used.
+
+ # Increase PHP max execution time (in seconds) for long running operations like imports.
+ # We recommend you leave those as is and use database/redis queue with a worker for long operations.
+ # PHP_MAX_EXECUTION_TIME: 3000
+ # LYCHEE_MAX_EXECUTION_TIME: 30
+
+ depends_on:
+ lychee_db:
+ condition: service_healthy
+
+ healthcheck:
+ test: [ "CMD", "curl", "-f", "http://localhost:8000/up" ]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ start_period: 30s
+
+ ##########################################################################################
+ # Queue Worker Service (disabled by default)
+ ##########################################################################################
+ #
+ # Uncomment to enable horizontal scaling of background job processing
+ # THIS IS PRETTY MUCH THE SAME CONFIGURATION AS lychee_api WITH A SINGLE MINOR MODIFICATIONS
+ lychee_worker:
+ <<: *base-lychee-setup
+ container_name: lychee-worker
+ environment:
+ <<: *common-env
+ ##########################################################################################
+ # Enable WORKER MODE
+ ##########################################################################################
+ #
+ # THIS VALUE HERE IS THE MOST IMPORTANT ONE.
+ # IF LYCHEE_MODE IS NOT SET TO WORKER, THE CONTAINER WILL BE A DUPLICATE OF lychee_api.
+ # Set LYCHEE_MODE=worker to enable queue worker mode
+ LYCHEE_MODE: worker
+
+ depends_on:
+ lychee_db:
+ condition: service_healthy
+ lychee_api:
+ condition: service_healthy
+
+ # Worker health check
+ # Verifies queue:work process is running
+ healthcheck:
+ test: [ "CMD-SHELL", "pgrep -f 'queue:work' || exit 1" ]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 60s # Give worker time to start up
+
+ phpmyadmin:
+ image: phpmyadmin
+ restart: always
+ ports:
+ - 8080:80
+ environment:
+ - PMA_HOST=lychee_db
+ - PMA_PORT=3306
+ depends_on:
+ lychee_db:
+ condition: service_healthy
+ networks:
+ - lychee
+ profiles:
+ - phpmyadmin
+
+ lychee_db:
+ image: mariadb:11
+ security_opt:
+ - no-new-privileges:true
+ cap_drop:
+ - ALL
+ cap_add:
+ - SETGID
+ - SETUID
+ - DAC_OVERRIDE
+ - CHOWN
+ read_only: false # MariaDB needs write access
+ tmpfs:
+ - /tmp:noexec,nosuid,nodev,size=200m
+ - /var/run/mysqld:noexec,nosuid,nodev,size=10m
+ env_file:
+ - path: ./.env
+ required: false
+ # secrets:
+ # - db_master_password
+ # - db_password
+ environment:
+ - MYSQL_ROOT_PASSWORD=${DB_ROOT_PASSWORD:-rootpassword}
+ # - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/db_master_password
+ - MYSQL_DATABASE=${DB_DATABASE:-lychee}
+ - MYSQL_USER=${DB_USERNAME:-lychee}
+ - MYSQL_PASSWORD=${DB_PASSWORD:-password}
+ # - MYSQL_PASSWORD_FILE=/run/secrets/db_password
+ expose:
+ - 3306
+ # Removed host port binding - only accessible within Docker network
+ # For debugging, temporarily uncomment:
+ # ports:
+ # - 127.0.0.1:33061:3306
+ volumes:
+ - mysql:/var/lib/mysql
+ networks:
+ - lychee
+ restart: unless-stopped
+ healthcheck:
+ test: [ "CMD", "healthcheck.sh", "--connect", "--innodb_initialized" ]
+ interval: 5s
+ timeout: 3s
+ retries: 10
+ start_period: 10s
+
+ lychee_facial_recognition:
+ # ports:
+ # - "${APP_PORT_AI_FACE:-8001}:8000"
+ image: ghcr.io/lycheeorg/lychee-facial-recognition:latest
+ restart: unless-stopped
+ security_opt:
+ - no-new-privileges:true
+ cap_drop:
+ - ALL
+ environment:
+ # Lychee instance base URL (no trailing slash)
+ VISION_FACE_LYCHEE_API_URL: "http://lychee_api:8000"
+ # Shared API key — must match AI_VISION_FACE_API_KEY in Lychee's .env
+ VISION_FACE_API_KEY: "${AI_VISION_API_KEY:-}"
+ # Set to false for development environments with self-signed certificates
+ VISION_FACE_VERIFY_SSL: "${AI_VISION_VERIFY_SSL:-true}"
+ # Skip the Lychee connectivity check at startup (useful for local dev)
+ VISION_FACE_SKIP_LYCHEE_CHECK: "${VISION_FACE_SKIP_LYCHEE_CHECK:-false}"
+
+ # --- Logging ---
+ # Uvicorn/application log level (debug, info, warning, error, critical)
+ VISION_FACE_LOG_LEVEL: "info"
+
+ # --- Clustering ---
+ # DBSCAN epsilon (max cosine distance); lower : tighter clusters
+ VISION_FACE_CLUSTER_EPS: "${VISION_FACE_CLUSTER_EPS:-0.3}"
+
+ # --- Photo volume ---
+ # Shared Docker-volume mount point for photo files
+ VISION_FACE_PHOTOS_PATH: "/data/photos"
+
+ # --- Embedding storage ---
+ # Storage engine: sqlite or pgvector
+ VISION_FACE_STORAGE_BACKEND: sqlite
+ # SQLite DB directory (used when storage_backend : sqlite)
+ VISION_FACE_STORAGE_PATH: "/data/embeddings"
+
+ # --- Concurrency ---
+ # Number of threads in the ThreadPoolExecutor used for CPU-bound inference
+ VISION_FACE_THREAD_POOL_SIZE: "${VISION_FACE_THREAD_POOL_SIZE:-1}"
+ # Number of Uvicorn worker processes
+ VISION_FACE_WORKERS: "${VISION_FACE_WORKERS:-1}"
+
+ # Check the following for more env variables
+ # https://github.com/LycheeOrg/Lychee-Facial-Recognition/blob/master/.env.example
+
+ VISION_FACE_QUEUE_BACKEND: "${VISION_FACE_QUEUE_BACKEND:-database}"
+
+ # Maximum pending jobs; requests beyond this are rejected with 429, 0 = unlimited
+ VISION_FACE_QUEUE_MAX_SIZE: "${VISION_FACE_QUEUE_MAX_SIZE:-0}"
+
+ # --- Detection thresholds ---
+ # Bounding-box confidence filter (0–1); faces below this score are excluded
+ VISION_FACE_DETECTION_THRESHOLD: 0.5
+ # Cosine-similarity cutoff for selfie match results and suggestion candidates
+ VISION_FACE_MATCH_THRESHOLD: 0.5
+ # IoU threshold for bounding-box matching on re-scan (preserves person_id)
+ VISION_FACE_RESCAN_IOU_THRESHOLD: 0.5
+ # Maximum faces included in a callback payload (top-N by confidence)
+ VISION_FACE_MAX_FACES_PER_PHOTO: "${VISION_FACE_MAX_FACES_PER_PHOTO:-10}"
+
+ # --- Quality filtering ---
+ # Minimum face size in pixels (longest side of bounding box); 0 : disabled
+ VISION_FACE_MIN_FACE_SIZE_PIXELS: "${VISION_FACE_MIN_FACE_SIZE_PIXELS:-0}"
+ # Laplacian variance threshold for blur detection; faces below this are discarded
+ VISION_FACE_BLUR_THRESHOLD: "${VISION_FACE_BLUR_THRESHOLD:-0.5}"
+ volumes:
+ - ./lychee/uploads:/data/photos:ro
+ - ai_vision_embeddings:/data/embeddings
+ networks:
+ - lychee
+ depends_on:
+ lychee_api:
+ condition: service_healthy
+ healthcheck:
+ test: [ "CMD", "curl", "-f", "http://localhost:8000/health" ]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 60s
+
+ ##########################################################################################
+ # NSFW Classification Service (disabled by default - opt in with --profile nsfw)
+ ##########################################################################################
+ #
+ # Sidecar for https://github.com/LycheeOrg/Lychee-NSFW-Classification, following the same
+ # REST + webhook architecture as lychee_facial_recognition above.
+ # Note: unlike every other LycheeOrg repo (MIT), this one is AGPL-3.0-licensed because it
+ # bundles NudeNet, which is itself AGPL-3.0.
+ lychee_nsfw_classification:
+ # ports:
+ # - "${APP_PORT_AI_NSFW:-8002}:8000"
+ image: ghcr.io/lycheeorg/lychee-nsfw-classification:latest
+ restart: unless-stopped
+ security_opt:
+ - no-new-privileges:true
+ cap_drop:
+ - ALL
+ profiles:
+ - nsfw
+ environment:
+ # Lychee instance base URL used for result callbacks (no trailing slash)
+ VISION_NSFW_LYCHEE_API_URL: "http://lychee_api:8000"
+ # Shared API key — must match AI_VISION_NSFW_API_KEY in Lychee's .env
+ VISION_NSFW_API_KEY: "${AI_VISION_NSFW_API_KEY:-}"
+ # Set to false for development environments with self-signed certificates
+ VISION_NSFW_VERIFY_SSL: "${AI_VISION_VERIFY_SSL:-true}"
+ # Skip the Lychee connectivity check at startup (useful for local dev)
+ VISION_NSFW_SKIP_LYCHEE_CHECK: "false"
+ # Shared Docker-volume mount point for photo files
+ VISION_NSFW_PHOTOS_PATH: "/data/photos"
+ # SQLite job-queue storage directory (used when queue backend = database, the default)
+ VISION_NSFW_STORAGE_PATH: "/data/queue"
+ # Uvicorn/application log level (debug, info, warning, error, critical)
+ VISION_NSFW_LOG_LEVEL: "info"
+
+ # Check the following for more env variables (presets, per-tier thresholds, etc.)
+ # https://github.com/LycheeOrg/Lychee-NSFW-Classification/blob/master/.env.example
+ volumes:
+ - ./lychee/uploads:/data/photos:ro
+ - nsfw_queue:/data/queue
+ networks:
+ - lychee
+ depends_on:
+ lychee_api:
+ condition: service_healthy
+ healthcheck:
+ test: [ "CMD", "curl", "-f", "http://localhost:8000/api/nsfw/health" ]
+ interval: 30s
+ timeout: 10s
+ retries: 3
+ start_period: 60s
+
+ ##########################################################################################
+ # Local Reverse Geo-Decoding Service (disabled by default - opt in with --profile geo-decoding)
+ ##########################################################################################
+ #
+ # Sidecar for https://github.com/LycheeOrg/Lychee-Reverse-Geo-Coding: a self-contained,
+ # Nominatim-"/reverse"-compatible service backed by data embedded in the binary (Natural
+ # Earth country/province/city polygons via rgeo). No API key, no external data download, no
+ # outbound network calls, no persistent volume - country/province/city resolution only (not
+ # street-level). config/services.php -> local-geo-decoding.base_url (LOCAL_GEO_DECODING_URL)
+ # points Lychee at it instead of the public nominatim.openstreetmap.org server.
+ lychee_geo_decoding:
+ image: ghcr.io/lycheeorg/lychee-reverse-geo-coding:latest
+ restart: unless-stopped
+ security_opt:
+ - no-new-privileges:true
+ cap_drop:
+ - ALL
+ profiles:
+ - geo-decoding
+ ports:
+ - "${APP_PORT_GEO_DECODING:-8003}:8080"
+ networks:
+ - lychee
+ healthcheck:
+ test: [ "CMD", "wget", "-qO-", "http://localhost:8080/health" ]
+ interval: 30s
+ timeout: 5s
+ retries: 3
+ start_period: 15s
+
+networks:
+ lychee:
+
+volumes:
+ mysql:
+ name: lychee_prod_mysql
+ driver: local
+ ai_vision_embeddings:
+ name: lychee_ai_vision_embeddings
+ driver: local
+ nsfw_queue:
+ name: lychee_nsfw_queue
+ driver: local
diff --git a/src/data/wizard/env.example b/src/data/wizard/env.example
new file mode 100644
index 00000000..fe8e3f7d
--- /dev/null
+++ b/src/data/wizard/env.example
@@ -0,0 +1,397 @@
+APP_NAME=Lychee
+APP_ENV=production
+APP_KEY=
+APP_DEBUG=false
+# This MUST contain the host name up to the Top Level Domain (tld) e.g. .com, .org etc.
+APP_URL=http://localhost
+APP_FORCE_HTTPS=false
+
+# If using Lychee in a sub folder, specify the path after the tld here.
+# For example for https://lychee.test/path/to/lychee
+# Set APP_URL=https://lychee.test
+# and APP_DIR=/path/to/lychee
+# We (LycheeOrg) do not recommend the use of APP_DIR.
+# APP_DIR=
+
+# enable or disable debug bar. By default it is disabled.
+# Do note that this disable CSP!!
+DEBUGBAR_ENABLED=false
+
+# enable or disable log viewer. By default it is disabled
+# Unfortunately, it is not possible to enable Log Viewer in production.
+# If you wish to enable it, also switch your APP_ENV to 'local'
+LOG_VIEWER_ENABLED=false
+
+# disable logging 404 errors
+# LOG_404_ERRORS=false
+
+# enable or disable clockwork. By default it is disabled (and not provided on non-dev build).
+CLOCKWORK_ENABLE=false
+CLOCKWORK_DRIVER=laravel
+CLOCKWORK_STORAGE_FILES_PATH=storage/clockwork
+
+# enable or disable latency debug: adds a specific amount of time in milliseconds to wait before processing requests.
+# Always disabled on production environment.
+# APP_DEBUG_LATENCY=0
+
+# All API requests to have the header "content-type: application/json"
+# or "content-type: multipart/form-data" depending on the type.
+#
+# If you want to disable this requirement, set this to false.
+#
+# This requirement prevents the use of the API from the API documentation page.
+REQUIRE_CONTENT_TYPE_ENABLED=true
+
+# enable s3 bucket (required in addition to needing AWS_ACCESS_KEY_ID)
+# S3_ENABLED=true
+
+# If you spread old links of to your albums in your Lychee instance starting with
+# https://lychee.text/#albumID/PhotoId
+# Set this value to true to enable redirection.
+LEGACY_V4_REDIRECT=false
+
+##############################################################################
+# IMPORTANT: To migrate from Lychee v3 you *MUST* use the same MySQL/MariaDB #
+# server as v3. #
+##############################################################################
+
+# Table prefix (e.g. lychee_) of a Lychee v3 instance for migration
+DB_OLD_LYCHEE_PREFIX=
+
+# DB_CONNECTION can be sqlite, mysql or pgsql. For sqlite the other entries are
+# not required, but an existing sqlite3 database may be specified if desired.
+# In this case, please use an absolute path. DB_DATABASE may be omitted but should
+# *not* be left blank.
+# Note that if DB_PASSWORD includes special characters, it must be enclosed in quotes.
+# e.g. DB_PASSWORD="lychee!@#$%^&"
+DB_CONNECTION=sqlite
+DB_HOST=
+DB_PORT=
+#DB_DATABASE=
+DB_USERNAME=
+DB_PASSWORD=
+DB_LOG_SQL=false
+DB_LOG_SQL_EXPLAIN=false #only for MySQL
+
+# List foreign keys in diagnostic page
+DB_LIST_FOREIGN_KEYS=false
+
+# Application timezone. If not specified, the server's default timezone is used.
+# Requires a named timezone identifier.
+# See https://www.php.net/manual/en/timezones.php for the list of supported timezones.
+# Don't use a timezone offset (like +01:00) or a timezone abbreviation (like CEST)
+# TIMEZONE=Europe/Paris
+
+# Visibility of directories and (media) files in LYCHEE_UPLOADS
+# Possible values are:
+#
+# - private: world group has neither read nor write access
+# - public: world group has read access but no write access (the default)
+# - world: world group has read and write access
+#
+# The default should suffice for most installations.
+# For improved security, change this setting to "private".
+# Some rare setups may require directories and files to be world writeable.
+# In this case, use "world" here.
+# USE WITH PRECAUTIONS: world writeable files and folders may be a SECURITY RISK.
+# LYCHEE_IMAGE_VISIBILITY=public
+
+# folders in which the files will be stored
+# LYCHEE_UPLOADS="/var/www/html/Lychee-Laravel/public/uploads/"
+# LYCHEE_DIST="/var/www/html/Lychee-Laravel/public/dist/"
+# LYCHEE_SYM="/var/www/html/Lychee-Laravel/public/sym/"
+# url to access those files
+# LYCHEE_UPLOADS_URL="uploads/"
+# LYCHEE_DIST_URL="dist/"
+# LYCHEE_SYM_URL="sym/"
+
+# Support for token based authentication used by API requests. Enabled by default.
+# ENABLE_TOKEN_AUTH=true
+
+# Lychee supports both Redis and file caching.
+# To use Redis, set CACHE_DRIVER to redis and configure the Redis connection.
+CACHE_DRIVER=file
+# CACHE_FILE_PATH=storage/framework/cache/data
+REDIS_HOST=127.0.0.1
+REDIS_PASSWORD=null
+REDIS_PORT=6379
+# REDIS_URL=redis://:@:
+
+# If you use Redis as cache driver, we strongly recommend
+# to disable it for your Log Viewer.
+# Should redis crash, you will no longer be able to access your logs.
+LOG_VIEWER_CACHE_DRIVER=file
+LOG_STDOUT=false
+
+# Session configuration
+SESSION_DRIVER=file
+SESSION_LIFETIME=120
+# Duration (in minutes) for the "Remember Me" cookie. Default: 40320 (4 weeks)
+# REMEMBER_LIFETIME=40320
+
+# `sync` if jobs need to be executed live (default) or `database` if they can be deferred.
+QUEUE_CONNECTION=sync
+# Choose this mode only if you have set up a queue worker (strongly recommended though).
+# QUEUE_CONNECTION=database
+
+SECURITY_HEADER_HSTS_ENABLE=false
+SECURITY_HEADER_CSP_CONNECT_SRC=
+SECURITY_HEADER_SCRIPT_SRC_ALLOW=
+SECURITY_HEADER_CSP_CHILD_SRC=
+SECURITY_HEADER_CSP_FONT_SRC=
+SECURITY_HEADER_CSP_FORM_ACTION=
+SECURITY_HEADER_CSP_FRAME_ANCESTORS=
+SECURITY_HEADER_CSP_FRAME_SRC=
+SECURITY_HEADER_CSP_IMG_SRC=
+SECURITY_HEADER_CSP_MEDIA_SRC=
+SESSION_SECURE_COOKIE=false
+
+MAIL_DRIVER=smtp
+MAIL_HOST=
+MAIL_PORT=
+MAIL_USERNAME=
+MAIL_PASSWORD=
+MAIL_ENCRYPTION=
+MAIL_FROM_NAME=
+MAIL_FROM_ADDRESS=
+
+# Only used when MAIL_DRIVER is set to "mailgun" or "postmark" respectively.
+# Both secrets also support the "_FILE" convention described above.
+# MAILGUN_DOMAIN=
+# MAILGUN_SECRET=
+# MAILGUN_SECRET_FILE=/run/secrets/mailgun_secret
+# MAILGUN_ENDPOINT=api.mailgun.net
+# POSTMARK_TOKEN=
+# POSTMARK_TOKEN_FILE=/run/secrets/postmark_token
+
+# The trusted proxies if Lychee is behind a reverse proxy
+# Accepted values:
+# - `null`: no proxy
+# - `*`: any proxy
+# - [,]: a comma-seperated list of IP addresses
+TRUSTED_PROXIES=null
+
+# Comma-separated list of class names of diagnostics checks that should be skipped.
+#SKIP_DIAGNOSTICS_CHECKS=
+
+VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
+VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
+
+# Disable Basic Auth. This means that the only way to authenticate is via the API token or Oauth.
+# This should only be toggled AFTER having set up the admin account and bound the Oauth client.
+# DISABLE_BASIC_AUTH=false
+
+# Disable WebAuthn. This means that the only way to authenticate is via the API token, Basic Auth or Oauth.
+# DISABLE_WEBAUTHN=false
+
+# White-label mode — hides all Lychee branding from the UI (footer link, generator meta tag,
+# misconfiguration warning, left-menu "Lychee" section, and login-form branding).
+# NOTE: This setting only takes effect when a valid Lychee Supporter Edition (SE) licence is active.
+# On non-SE installations the flag is ignored and Lychee branding remains visible.
+# WHITE_LABEL_ENABLED=false
+
+###################################################################
+# Keygen License Management #
+###################################################################
+
+# API token obtained from keygen.lycheeorg.dev.
+# When set, Lychee will automatically rotate an expired license key
+# on admin login and check the token health on the diagnostics page.
+# KEYGEN_API_KEY=
+
+###################################################################
+# LDAP Authentication (enterprise directory integration) #
+###################################################################
+
+# Enable LDAP authentication alongside or instead of basic auth
+# LDAP_ENABLED=false
+
+# LDAP Server connection settings
+# LDAP_HOST=ldap.example.com
+# LDAP_PORT=389
+# For LDAPS (LDAP over SSL), use port 636
+# LDAP_PORT=636
+
+# Base DN for LDAP searches (e.g., dc=example,dc=com or dc=corp,dc=example,dc=com)
+# LDAP_BASE_DN=dc=example,dc=com
+
+# Service account credentials for LDAP bind
+# This account needs read-only access to user and group attributes
+# LDAP_BIND_DN=cn=lychee-service,ou=services,dc=example,dc=com
+# LDAP_BIND_PASSWORD=securepassword
+
+# LDAP user search filter (%s is replaced with username)
+# For OpenLDAP:
+# LDAP_USER_FILTER=(&(objectClass=person)(uid=%s))
+# For Active Directory:
+# LDAP_USER_FILTER=(&(objectClass=user)(sAMAccountName=%s))
+
+# LDAP attribute mapping (maps LDAP attributes to Lychee user fields)
+# OpenLDAP defaults:
+# LDAP_ATTR_USERNAME=uid
+# LDAP_ATTR_EMAIL=mail
+# LDAP_ATTR_DISPLAY_NAME=displayName
+# Active Directory alternatives:
+# LDAP_ATTR_USERNAME=sAMAccountName
+# LDAP_ATTR_EMAIL=userPrincipalName
+# LDAP_ATTR_DISPLAY_NAME=displayName
+
+# Admin role mapping via LDAP group
+# Users in this group will have may_administrate=true
+# LDAP_ADMIN_GROUP_DN=cn=lychee-admins,ou=groups,dc=example,dc=com
+
+# Auto-provision users on first LDAP login
+# If false, users must be pre-created in Lychee before they can log in via LDAP
+# LDAP_AUTO_PROVISION=true
+
+# TLS/SSL settings for secure LDAP connections
+# LDAP_USE_TLS=true
+# LDAP_TLS_VERIFY_PEER=true
+
+# Connection timeout in seconds
+# LDAP_CONNECTION_TIMEOUT=5
+
+# Oauth token data
+# XXX_REDIRECT_URI should be left as default unless you know exactly what you do.
+#
+# Docker/Kubernetes secrets: when running the official Docker image, every confidential
+# value below - as well as AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY, MAILGUN_SECRET,
+# POSTMARK_TOKEN, AI_VISION_FACE_API_KEY and AI_VISION_NSFW_API_KEY further down - can be
+# provided via a file instead of in plaintext here: set "_FILE" to the file's path
+# (e.g. AMAZON_SIGNIN_SECRET_FILE=/run/secrets/amazon_signin_secret) and leave ""
+# itself unset. This is resolved by docker/scripts/01-validate-env.sh at container
+# startup. See docker-compose.yaml for ready-to-use examples.
+
+# AMAZON_SIGNIN_CLIENT_ID=
+# AMAZON_SIGNIN_SECRET=
+# AMAZON_SIGNIN_REDIRECT_URI=/auth/amazon/redirect
+
+# https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple
+# Note: the client secret used for "Sign In with Apple" is a JWT token that can have a maximum lifetime of 6 months.
+# The article above explains how to generate the client secret on demand and you'll need to update this every 6 months.
+# To generate the client secret for each request, see Generating A Client Secret For Sign In With Apple On Each Request.
+# https://bannister.me/blog/generating-a-client-secret-for-sign-in-with-apple-on-each-request
+# APPLE_CLIENT_ID=
+# APPLE_CLIENT_SECRET=
+# APPLE_REDIRECT_URI=/auth/apple/redirect
+
+# FACEBOOK_CLIENT_ID=
+# FACEBOOK_CLIENT_SECRET=
+# FACEBOOK_REDIRECT_URI=/auth/facebook/redirect
+
+# GITHUB_CLIENT_ID=
+# GITHUB_CLIENT_SECRET=
+# GITHUB_REDIRECT_URI=/auth/github/redirect
+
+# GOOGLE_CLIENT_ID=
+# GOOGLE_CLIENT_SECRET=
+# GOOGLE_REDIRECT_URI=/auth/google/redirect
+
+# MASTODON_DOMAIN=https://mastodon.social
+# MASTODON_ID=
+# MASTODON_SECRET=
+# MASTODON_REDIRECT_URI=/auth/mastodon/redirect
+
+# MICROSOFT_CLIENT_ID=
+# MICROSOFT_CLIENT_SECRET=
+# MICROSOFT_REDIRECT_URI=/auth/microsoft/redirect
+# MICROSOFT_TENANT_ID=
+
+# NEXTCLOUD_CLIENT_ID=
+# NEXTCLOUD_CLIENT_SECRET=
+# NEXTCLOUD_REDIRECT_URI=/auth/nextcloud/redirect
+# NEXTCLOUD_BASE_URI=
+
+# KEYCLOAK_CLIENT_ID=
+# KEYCLOAK_CLIENT_SECRET=
+# KEYCLOAK_REDIRECT_URI=/auth/keycloak/redirect
+# KEYCLOAK_BASE_URL=
+# KEYCLOAK_REALM=
+
+# AUTHENTIK_BASE_URL=
+# AUTHENTIK_CLIENT_ID=
+# AUTHENTIK_CLIENT_SECRET=
+# AUTHENTIK_REDIRECT_URI=/auth/authentik/redirect
+
+# AUTHELIA_BASE_URL=
+# AUTHELIA_CLIENT_ID=
+# AUTHELIA_CLIENT_SECRET=
+# AUTHELIA_REDIRECT_URI=/auth/authelia/redirect
+
+# AWS support data
+
+# AWS_ACCESS_KEY_ID=
+# AWS_ACCESS_KEY_ID_FILE=/run/secrets/aws_access_key_id
+# AWS_SECRET_ACCESS_KEY=
+# AWS_SECRET_ACCESS_KEY_FILE=/run/secrets/aws_secret_access_key
+# AWS_DEFAULT_REGION=
+# AWS_BUCKET=
+# AWS_URL=
+# AWS_ENDPOINT=
+# AWS_IMAGE_VISIBILITY=
+# AWS_USE_PATH_STYLE_ENDPOINT=
+
+###################################################################
+# Vite local development without running a server. #
+# set VITE_LOCAL_DEV to true #
+# set VITE_HTTP_PROXY_TARGET to the rediction for the API calls. #
+###################################################################
+# VITE_LOCAL_DEV=true
+# VITE_HTTP_PROXY_TARGET=http://localhost:8000
+
+# DISABLE_IMPORT_FROM_SERVER=false
+
+# On shared hosting where sys_get_temp_dir() is not readable/writable,
+# set to false to use storage/tmp/uploads_parts instead.
+# USE_SYSTEM_TEMP_DIR=true
+
+# When enabled, the request caching settings (cache_enabled, cache_ttl,
+# cache_event_logging) become visible in the admin settings panel.
+# ENABLE_REQUEST_CACHING=false
+
+###################################################################
+# Payment integration (requires SE) #
+###################################################################
+
+# Enable test mode (Sandbox mode) for payment gateways.
+# In test mode, no real money transactions are done.
+# We set it to true by default for safety. Make sure to set it to false
+# when you go live.
+# OMNIPAY_TEST_MODE=true
+
+# Configuration values for Mollie integration
+# MOLLIE_API_KEY=
+# MOLLIE_PROFILE_ID=
+
+# Configuration values for Stripe integration (NOT WORKING YET, MAYBE LATER)
+# STRIPE_API_KEY=
+# STRIPE_PUBLISHABLE_KEY=
+
+# Configuration values for PayPal integration
+# PAYPAL_CLIENT_ID=
+# PAYPAL_SECRET=
+
+###################################################################
+# AI Vision (facial recognition & NSFW classification) #
+###################################################################
+
+# AI_VISION_FACE_URL=
+# AI_VISION_FACE_API_KEY=
+# AI_VISION_FACE_API_KEY_FILE=/run/secrets/ai_vision_face_api_key
+# AI_VISION_NSFW_URL=
+# AI_VISION_NSFW_API_KEY=
+# AI_VISION_NSFW_API_KEY_FILE=/run/secrets/ai_vision_nsfw_api_key
+#
+# See the "lychee_facial_recognition" and "lychee_nsfw_classification" services
+# in docker-compose.yaml for a ready-to-use sidecar setup for both.
+
+###################################################################
+# Local reverse geo-decoding #
+###################################################################
+
+# Nominatim-API-compatible endpoint (e.g. a self-hosted Nominatim
+# instance). When set, it is used instead of the public
+# nominatim.openstreetmap.org server.
+# This is a bring-your-own-service integration - see the commented
+# "lychee_geo_decoding" example service in docker-compose.yaml.
+# LOCAL_GEO_DECODING_URL=
diff --git a/src/navigation.js b/src/navigation.js
index 08e030be..9e573b07 100644
--- a/src/navigation.js
+++ b/src/navigation.js
@@ -57,12 +57,13 @@ export const footerData = {
{ text: 'Release Notes', href: getPermalink('/docs/getting-started/releases/') },
// { text: 'PR Dashboard', href: 'https://pr.lycheeorg.dev/' },
{ text: 'Issue trakcer', href: 'https://github.com/LycheeOrg/Lychee/issues' },
- ]
+ ],
},
{
title: 'Need help?',
links: [
{ text: 'Read the Docs', href: '/docs/' },
+ { text: 'Docker Compose Wizard', href: '/wizard/' },
{ text: 'Community Forum', href: 'https://github.com/LycheeOrg/Lychee/discussions' },
{ text: 'Join our discord', href: 'https://discord.gg/JMPvuRQcTf' },
],
@@ -70,7 +71,10 @@ export const footerData = {
{
title: 'Support Lychee',
links: [
- { text: 'Get Lychee SE', href: 'https://lycheeorg.dev/get-supporter-edition' },
+ {
+ text: 'Get Lychee SE',
+ href: 'https://lycheeorg.dev/get-supporter-edition',
+ },
{ text: 'GitHub sponsor', href: 'https://github.com/sponsors/LycheeOrg' },
{ text: 'Open Collective', href: 'https://opencollective.com/LycheeOrg' },
{ text: 'Translations', href: 'https://weblate.lycheeorg.dev' },
@@ -78,18 +82,16 @@ export const footerData = {
},
{
title: 'Security',
- links: [
- { text: 'Cosign key', href: getAsset('lychee-cosign.pub') },
- ]
- }
+ links: [{ text: 'Cosign key', href: getAsset('lychee-cosign.pub') }],
+ },
],
secondaryLinks: [
{ text: 'License', href: getPermalink('/license') },
{ text: 'Privacy Policy', href: getPermalink('/privacy-policy') },
],
socialLinks: [
- { ariaLabel: 'RSS', icon: 'tabler:rss', href: getAsset('/rss.xml') },
- { ariaLabel: 'Github', icon: 'tabler:brand-github', href: 'https://github.com/LycheeOrg/Lychee' },
+ { ariaLabel: 'RSS', icon: 'tabler:rss', href: getAsset('/rss.xml') },
+ { ariaLabel: 'Github', icon: 'tabler:brand-github', href: 'https://github.com/LycheeOrg/Lychee' },
],
footNote: `Maintained by LycheeOrg — Built with Astro & Tailwind CSS`,
};
diff --git a/src/pages/index.astro b/src/pages/index.astro
index 7cfd54c8..a6dbe11d 100644
--- a/src/pages/index.astro
+++ b/src/pages/index.astro
@@ -13,11 +13,14 @@ import show0 from '~/assets/images/showcase/0.jpg';
import show1 from '~/assets/images/showcase/1.jpg';
import show2 from '~/assets/images/showcase/2.jpg';
import show3 from '~/assets/images/showcase/3.jpg';
+import { getRepoStats } from '~/utils/repoStats';
const metadata = {
title: 'LycheeOrg — Self-hosted photo-management done right.',
ignoreTitleTemplate: true,
};
+
+const repoStats = await getRepoStats();
---
@@ -42,9 +45,9 @@ const metadata = {
- Lychee is a free photo-management tool, which runs on your server or web-space.
- Installing is a matter of seconds. Upload, manage and share photos like from a native application. Lychee comes with
- everything you need and all your photos are stored securely.
+ Lychee is a free photo-management tool, which runs on your server or web-space. Installing
+ is a matter of seconds. Upload, manage and share photos like from a native application. Lychee comes with everything
+ you need and all your photos are stored securely.
@@ -81,13 +84,7 @@ const metadata = {
>