From 0bf0312956c532714f927441f47f10c0cf22cd6b Mon Sep 17 00:00:00 2001 From: ildyria Date: Fri, 31 Jul 2026 09:02:18 +0200 Subject: [PATCH 1/5] WIP --- package-lock.json | 9 + package.json | 2 + src/data/wizard/docker-compose.yaml | 648 ++++++++++++++++++++++++++ src/data/wizard/env.example | 371 +++++++++++++++ src/navigation.js | 18 +- src/pages/index.astro | 21 +- src/pages/wizard.astro | 692 ++++++++++++++++++++++++++++ src/utils/wizard/answers.ts | 78 ++++ src/utils/wizard/dbCompose.ts | 104 +++++ src/utils/wizard/dockerSecrets.ts | 164 +++++++ src/utils/wizard/generator.ts | 223 +++++++++ src/utils/wizard/nsfwService.ts | 99 ++++ src/utils/wizard/secrets.ts | 31 ++ src/utils/wizard/templates.ts | 33 ++ src/utils/wizard/validate.ts | 26 ++ 15 files changed, 2500 insertions(+), 19 deletions(-) create mode 100644 src/data/wizard/docker-compose.yaml create mode 100644 src/data/wizard/env.example create mode 100644 src/pages/wizard.astro create mode 100644 src/utils/wizard/answers.ts create mode 100644 src/utils/wizard/dbCompose.ts create mode 100644 src/utils/wizard/dockerSecrets.ts create mode 100644 src/utils/wizard/generator.ts create mode 100644 src/utils/wizard/nsfwService.ts create mode 100644 src/utils/wizard/secrets.ts create mode 100644 src/utils/wizard/templates.ts create mode 100644 src/utils/wizard/validate.ts diff --git a/package-lock.json b/package-lock.json index 258acfbe..50a72faa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "astro-seo": "^1.1.0", "limax": "4.2.3", "lodash.merge": "^4.6.2", + "prismjs": "^1.30.0", "unpic": "^4.2.2" }, "devDependencies": { @@ -33,6 +34,7 @@ "@types/js-yaml": "^4.0.9", "@types/lodash.merge": "^4.6.9", "@types/mdx": "^2.0.13", + "@types/prismjs": "^1.26.5", "@typescript-eslint/eslint-plugin": "^8.60.0", "@typescript-eslint/parser": "^8.60.0", "astro-compress": "^2.4.1", @@ -3513,6 +3515,13 @@ "undici-types": "~8.3.0" } }, + "node_modules/@types/prismjs": { + "version": "1.26.6", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.6.tgz", + "integrity": "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/sax": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", diff --git a/package.json b/package.json index 5dc5d8a7..f07a7748 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "astro-seo": "^1.1.0", "limax": "4.2.3", "lodash.merge": "^4.6.2", + "prismjs": "^1.30.0", "unpic": "^4.2.2" }, "devDependencies": { @@ -47,6 +48,7 @@ "@types/js-yaml": "^4.0.9", "@types/lodash.merge": "^4.6.9", "@types/mdx": "^2.0.13", + "@types/prismjs": "^1.26.5", "@typescript-eslint/eslint-plugin": "^8.60.0", "@typescript-eslint/parser": "^8.60.0", "astro-compress": "^2.4.1", diff --git a/src/data/wizard/docker-compose.yaml b/src/data/wizard/docker-compose.yaml new file mode 100644 index 00000000..7f22bbfc --- /dev/null +++ b/src/data/wizard/docker-compose.yaml @@ -0,0 +1,648 @@ +# 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 + +########################################################################################## +# 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) + # - mapping .env file to /app/.env (not necessary anymore if you use env_file) + 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_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_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_ENABLED: "${AI_VISION_ENABLED:-true}" + AI_VISION_FACE_API_KEY: "${AI_VISION_API_KEY:-changeme}" + AI_VISION_FACE_URL: "http://lychee_facial_recognition:8000" + +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: + expose: + - "${APP_PORT_AI_FACE:-8001}" + 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:-changeme}" + # 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: "0.6" + + # --- 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: 1 + # Number of Uvicorn worker processes + VISION_FACE_WORKERS: "${AI_VISION_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: 10 + + # --- Quality filtering --- + # Minimum face size in pixels (longest side of bounding box); 0 : disabled + VISION_FACE_MIN_FACE_SIZE_PIXELS: 0 + # Laplacian variance threshold for blur detection; faces below this are discarded + 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 + +networks: + lychee: + +volumes: + mysql: + name: lychee_prod_mysql + driver: local + ai_vision_embeddings: + name: lychee_ai_vision_embeddings + driver: local diff --git a/src/data/wizard/env.example b/src/data/wizard/env.example new file mode 100644 index 00000000..ac2b6623 --- /dev/null +++ b/src/data/wizard/env.example @@ -0,0 +1,371 @@ +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= + +# 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. + +# 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_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_NSFW_URL= +# AI_VISION_NSFW_API_KEY= + +################################################################### +# 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. +# 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..9406ffab 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -42,9 +42,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 +81,7 @@ const metadata = { > - Screenshot of Lychee + Screenshot of Lychee @@ -179,7 +173,6 @@ const metadata = { ]} /> -
@@ -247,6 +240,12 @@ const metadata = { target: '_blank', icon: 'tabler:download', }, + { + variant: 'secondary', + text: 'Docker Compose Wizard', + href: '/wizard/', + icon: 'tabler:wand', + }, ]} > Lychee on Docker diff --git a/src/pages/wizard.astro b/src/pages/wizard.astro new file mode 100644 index 00000000..afbb6c02 --- /dev/null +++ b/src/pages/wizard.astro @@ -0,0 +1,692 @@ +--- +import Layout from '~/layouts/Layout.astro'; +import Footer from '~/components/widgets/Footer.astro'; +import { footerData } from '~/navigation'; + +const metadata = { + title: 'Docker Compose Wizard', + description: + 'Generate a ready-to-run docker-compose.yaml and .env for Lychee, right in your browser. Same questions as the Lychee Wizard CLI, no install required.', +}; + +const inputClass = + 'py-2.5 px-4 block w-full text-sm rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-900 focus:border-primary focus:ring-primary disabled:opacity-50'; +const selectClass = inputClass; +const labelClass = 'block text-sm font-medium mb-1'; +const descClass = 'text-xs text-gray-500 dark:text-gray-400 mt-1'; +const fieldsetClass = 'rounded-xl border border-gray-200 dark:border-gray-700 p-6 bg-white dark:bg-slate-900'; +const legendClass = 'px-2 text-lg font-semibold flex items-center'; +const checkboxRowClass = 'flex items-start gap-3'; +const checkboxClass = + 'mt-1 h-4 w-4 shrink-0 cursor-pointer rounded border-gray-300 dark:border-gray-600 text-primary focus:ring-primary'; +const badgeClass = + 'ml-2 px-2 py-0.5 rounded-full text-xs font-semibold bg-emerald-100 text-emerald-800 dark:bg-emerald-900 dark:text-emerald-200'; +--- + + +
+
+
+

+ Docker Compose Wizard +

+

+ Generates docker-compose.yaml and .env for Lychee, entirely in your browser — nothing + is sent to any server. Prefer a terminal? Same wizard as a + standalone CLI. +

+
+

+ Loading the latest templates… +

+
+
+ +
+
+ +
+
+ General +
+
+ + +
+
+ + +

The host name up to the TLD, e.g. https://photos.example.com

+
+
+ + +

+
+
+ + +
+
+ + +

A named timezone identifier, e.g. Europe/Paris

+
+
+
+ +
+ Database +
+
+ + +
+ +
+ + +
+

+ Lychee's official Docker packaging only ships a MariaDB container. For PostgreSQL, connect to a server you + already run. +

+ + + +
+
+ + +
+
+ + +
+
+ + +
+ + +
+
+
+ +
+ Secrets handling Recommended +
+
+ + +
+

Recommended: keeps sensitive values out of .env, in ./secrets/ files instead.

+
+
+ +
+ Optional services +
+
+ + +
+ +
+ + +
+
+ + +
+ + +
+ + +
+

+ Flags or blocks explicit photos using a separate classification service. +

+
+ + +
+ +
+
+ +
+ System +
+
+ + +

+ User ID that will own files written by the container. +

+
+
+ + +

+ Group ID that will own files written by the container. +

+
+
+
+
+ + +
+ + +
+ + +
+
+ docker-compose.yaml +
+ + +
+
+
+
+ + + + +
+ +
+

Next steps

+
    +
+
+
+
+
+ + + + + +