diff --git a/templates/sparkyfitness/.felhom.yml b/templates/sparkyfitness/.felhom.yml new file mode 100644 index 0000000..9b58d64 --- /dev/null +++ b/templates/sparkyfitness/.felhom.yml @@ -0,0 +1,87 @@ +# ============================================================================= +# .felhom.yml - App metadata for felhom-controller +# ============================================================================= + +display_name: "SparkyFitness" +description: "Táplálkozás- és edzéskövető" +category: "home" +subdomain: "sparky" +slug: "sparkyfitness" + +resources: + mem_request: "400M" + mem_limit: "1792M" + pi_compatible: false + needs_hdd: false + +deploy_fields: + - env_var: DOMAIN + label: "Domain" + type: domain + description: "A szerver domain neve" + locked_after_deploy: true + + - env_var: SUBDOMAIN + label: "Aldomain" + type: subdomain + default: "sparky" + required: true + locked_after_deploy: true + description: "Az alkalmazás aldomainje" + + - env_var: DB_PASSWORD + label: "Adatbázis jelszó" + type: secret + generate: "password:24" + locked_after_deploy: true + + - env_var: APP_DB_PASSWORD + label: "Alkalmazás adatbázis jelszó" + type: secret + generate: "password:24" + locked_after_deploy: true + + - env_var: API_ENCRYPTION_KEY + label: "Adattitkosítási kulcs" + type: secret + generate: "hex:32" + locked_after_deploy: true + # Encrypts stored data (e.g. external-integration credentials). Must NEVER be + # regenerated after first boot — that would make existing encrypted data + # unrecoverable. At restore the controller RECOVERS (never regenerates) it. + data_key: true + + - env_var: BETTER_AUTH_SECRET + label: "Munkamenet aláíró kulcs" + type: secret + generate: "hex:32" + locked_after_deploy: true + # Signs sessions and encrypts 2FA/TOTP secrets. Must NEVER change after first + # boot — changing it locks out any user who enabled two-factor auth. + data_key: true + +app_info: + tagline: "Táplálkozási napló, kalóriaszámláló és edzéskövető – önállóan üzemeltetve" + docs_url: "https://github.com/CodeWithCJ/SparkyFitness" + + use_cases: + - 'Napi étkezések és kalóriabevitel naplózása' + - 'Testsúly és testméretek követése grafikonokkal' + - 'Edzések és mozgás rögzítése' + - 'Tápanyag-célok (fehérje, szénhidrát, zsír) beállítása és követése' + - 'Haladás áttekintése idővonalon és statisztikákon' + + first_steps: + - 'Nyisd meg a sparky.DOMAIN címet a böngészőben' + - 'Regisztrálj egy fiókot e-mail címmel és jelszóval' + - 'Állítsd be a profilodat és a napi céljaidat' + - 'Kezdd el naplózni az étkezéseidet és edzéseidet' + + prerequisites: + - 'Az első indításkor a szerver lefuttatja az adatbázis-migrációkat – ez akár 1-2 percig is eltarthat, mire az alkalmazás elérhetővé válik' + +# --- Controller-side health probe (same form as wger) --- +healthcheck: + checks: + - type: http + port: 80 diff --git a/templates/sparkyfitness/docker-compose.yml b/templates/sparkyfitness/docker-compose.yml new file mode 100644 index 0000000..1ab38e3 --- /dev/null +++ b/templates/sparkyfitness/docker-compose.yml @@ -0,0 +1,131 @@ +# SparkyFitness - Táplálkozás- és edzéskövető +# Domain: ${SUBDOMAIN}.${DOMAIN} +# Database: PostgreSQL 15 (dedicated) +# RAM: ~400M (mem_limit: 1792M) | Pi-compatible: No +# +# Topology: frontend (nginx SPA, :80) proxies /api,/uploads internally to +# server (Node, :3010) -> PostgreSQL. Only the frontend is exposed via Traefik; +# the server and DB stay on the internal network. +# +# Environment variables: +# DOMAIN - Customer domain (auto) +# SUBDOMAIN - App subdomain (default: sparky) +# DB_PASSWORD - PostgreSQL superuser password (auto-generated) +# APP_DB_PASSWORD - Limited app DB role password (auto-generated) +# API_ENCRYPTION_KEY - Data-encryption key, 64-char hex (auto; NEVER changes) +# BETTER_AUTH_SECRET - Session/2FA signing secret (auto; NEVER changes) + +services: + sparkyfitness-db: + image: postgres:15-alpine + container_name: sparkyfitness-db + restart: unless-stopped + environment: + - POSTGRES_USER=sparky + - POSTGRES_PASSWORD=${DB_PASSWORD} + - POSTGRES_DB=sparkyfitness_db + - PGDATA=/var/lib/postgresql/data/pgdata + - TZ=Europe/Budapest + volumes: + - sparkyfitness_db_data:/var/lib/postgresql/data + networks: + - sparkyfitness-internal + deploy: + resources: + limits: + memory: 512M + healthcheck: + test: ["CMD-SHELL", "pg_isready -U sparky -d sparkyfitness_db"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 20s + + sparkyfitness-server: + image: codewithcj/sparkyfitness_server:v0.17.2 + container_name: sparkyfitness-server + restart: unless-stopped + depends_on: + sparkyfitness-db: + condition: service_healthy + environment: + - SPARKY_FITNESS_DB_HOST=sparkyfitness-db + - SPARKY_FITNESS_DB_PORT=5432 + - SPARKY_FITNESS_DB_NAME=sparkyfitness_db + - SPARKY_FITNESS_DB_USER=sparky + - SPARKY_FITNESS_DB_PASSWORD=${DB_PASSWORD} + - SPARKY_FITNESS_APP_DB_USER=sparkyapp + - SPARKY_FITNESS_APP_DB_PASSWORD=${APP_DB_PASSWORD} + - SPARKY_FITNESS_API_ENCRYPTION_KEY=${API_ENCRYPTION_KEY} + - BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET} + - SPARKY_FITNESS_FRONTEND_URL=https://${SUBDOMAIN}.${DOMAIN} + - SPARKY_FITNESS_DISABLE_SIGNUP=false + - ALLOW_PRIVATE_NETWORK_CORS=true + - SPARKY_FITNESS_LOG_LEVEL=INFO + - NODE_ENV=production + - TZ=Europe/Budapest + - PUID=1000 + - GUID=1000 + volumes: + - sparkyfitness_uploads:/app/SparkyFitnessServer/uploads + - sparkyfitness_backup:/app/SparkyFitnessServer/backup + networks: + - sparkyfitness-internal + deploy: + resources: + limits: + memory: 1024M + healthcheck: + # VERIFY-BEFORE-FINALIZE (see 1.3): node-exec probe, no wget/curl dependency. + test: ["CMD", "node", "-e", "require('http').get('http://127.0.0.1:3010/api/health',r=>process.exit(r.statusCode<400?0:1)).on('error',()=>process.exit(1))"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 120s + + sparkyfitness-frontend: + image: codewithcj/sparkyfitness:v0.17.2 + container_name: sparkyfitness-frontend + restart: unless-stopped + depends_on: + sparkyfitness-server: + condition: service_healthy + environment: + - SPARKY_FITNESS_SERVER_HOST=sparkyfitness-server + - SPARKY_FITNESS_SERVER_PORT=3010 + - SPARKY_FITNESS_FRONTEND_URL=https://${SUBDOMAIN}.${DOMAIN} + - NGINX_LISTEN_PORT=80 + - TZ=Europe/Budapest + - PUID=1000 + - GUID=1000 + networks: + - traefik-public + - sparkyfitness-internal + labels: + - "traefik.enable=true" + - "traefik.http.routers.sparkyfitness.rule=Host(`${SUBDOMAIN}.${DOMAIN}`)" + - "traefik.http.routers.sparkyfitness.entrypoints=websecure" + - "traefik.http.routers.sparkyfitness.tls=true" + - "traefik.http.routers.sparkyfitness.tls.certresolver=letsencrypt" + - "traefik.http.services.sparkyfitness.loadbalancer.server.port=80" + deploy: + resources: + limits: + memory: 256M + healthcheck: + # VERIFY-BEFORE-FINALIZE (see 1.3): confirm wget exists in this image; else curl; else omit. + test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:80/"] + interval: 30s + timeout: 10s + retries: 5 + start_period: 30s + +volumes: + sparkyfitness_db_data: + sparkyfitness_uploads: + sparkyfitness_backup: + +networks: + traefik-public: + external: true + sparkyfitness-internal: