Sign inSign up

iemperor/constellation

By iemperor

•Updated 22 days ago

Image
0

343

iemperor/constellation repository overview

⁠Constellation

Constellation compile des fiches de célébrités à partir de plusieurs sources : Wikipedia, Wikidata, API Ninjas et Google Knowledge Graph. Chaque fiche a une galerie photo, une filmographie avec le casting et une discographie. Une page d'accueil montre le graphe des liens entre célébrités, par les films et séries qu'elles ont en commun.

L'image contient l'application complète, front Angular et API Symfony, servie par FrankenPHP sur le port 8080. Il suffit d'y ajouter PostgreSQL ; le docker-compose.yml ci-dessous fournit tout.

⁠Fonctionnalités

  • Fiches de célébrités : compilées depuis Wikipedia, Wikidata, API Ninjas et Google, avec la source de chaque information.
  • Galerie photo : photos libres (Wikimedia Commons, Openverse) choisies une à une, ou synchronisation automatique d'albums Immich. Les photos sont copiées et converties en WebP, sans métadonnées EXIF ni GPS.
  • Filmographie : films et séries TMDB, casting de chaque œuvre, ajout de fiches depuis le casting.
  • Musique : albums et singles MusicBrainz, clips YouTube (lecteur chargé seulement au clic).
  • Graphe des liens : célébrités reliées par leurs œuvres communes, avec recherche, filtres et navigation au clavier.
  • Administration :
    • un tableau de bord (disque, processeur, mémoire, contenu) ;
    • les utilisateurs ;
    • le suivi des galeries et des albums Immich ;
    • les clés d'API et le serveur mail, chiffrés en base.
  • Comptes : inscription avec vérification de l'email, mot de passe oublié, protection contre la force brute, authentification JWT.
  • Interface : thème néon clair et sombre, en français, adaptée au mobile.

⁠Tags

TagContenu
1.1.0, latestVersion actuelle
1.0.0Première version

Plateforme : linux/amd64.


⁠Installation

⁠Prérequis
  • Un serveur Linux avec Docker 24+ et Docker Compose v2 (le mode rootless est pris en charge).
  • Pour un accès depuis internet :
    • un nom de domaine qui pointe vers le serveur ;
    • les ports 80 et 443 ouverts (la variante A gère le certificat HTTPS toute seule).
  • Environ 1 Go de RAM, et de l'espace disque pour les photos : environ 110 Ko par photo.
⁠1. Créer le dossier
mkdir constellation && cd constellation
⁠2. Créer docker-compose.yml

Deux variantes : choisissez celle qui correspond à votre serveur.

⁠Variante A : serveur sans reverse proxy (HTTPS automatique)

Caddy obtient et renouvelle le certificat Let's Encrypt. C'est la seule porte d'entrée du serveur.

name: constellation

x-app: &app
  image: docker.io/iemperor/constellation:${CONSTELLATION_VERSION:-1.1.0}
  restart: unless-stopped
  read_only: true
  tmpfs:
    - /tmp
    - /config:uid=1000,gid=1000,mode=0700
  security_opt:
    - no-new-privileges:true
  cap_drop: [ALL]
  environment: &app-env
    FRONTEND_URL: https://${DOMAIN:?DOMAIN manquant}
    APP_SECRET: ${APP_SECRET:?APP_SECRET manquant}
    JWT_PASSPHRASE: ${JWT_PASSPHRASE:?JWT_PASSPHRASE manquant}
    SETTINGS_ENCRYPTION_KEY: ${SETTINGS_ENCRYPTION_KEY:?SETTINGS_ENCRYPTION_KEY manquant}
    DATABASE_URL: postgresql://constellation:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD manquant}@db:5432/constellation?serverVersion=17&charset=utf8
    MAILER_DSN: ${MAILER_DSN:-null://null}
    MAILER_FROM: ${MAILER_FROM:-}
    SOURCES_CONTACT: ${SOURCES_CONTACT:-}
    API_NINJAS_KEY: ${API_NINJAS_KEY:-}
    GOOGLE_KG_API_KEY: ${GOOGLE_KG_API_KEY:-}
    TMDB_API_READ_ACCESS_TOKEN: ${TMDB_API_READ_ACCESS_TOKEN:-}
    TMDB_API_KEY: ${TMDB_API_KEY:-}
    IMMICH_URL: ${IMMICH_URL:-}
    IMMICH_API_KEY: ${IMMICH_API_KEY:-}
    WIKIPEDIA_LANG: ${WIKIPEDIA_LANG:-fr}
    TMDB_LANGUAGE: ${TMDB_LANGUAGE:-fr-FR}
  volumes:
    - app-data:/data
    - app-var:/app/var
    - photos:/app/public/media/photos
  networks: [web, data]

services:
  # Reverse proxy HTTPS (Let's Encrypt) : sa configuration est écrite au démarrage
  proxy:
    image: caddy:2-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    environment:
      DOMAIN: ${DOMAIN}
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        cat > /etc/caddy/Caddyfile <<'CADDYFILE'
        {$$DOMAIN} {
        	encode zstd gzip
        	header Strict-Transport-Security "max-age=31536000; includeSubDomains"
        	reverse_proxy app:8080 {
        		transport http {
        			read_timeout 120s
        		}
        	}
        }
        CADDYFILE
        exec caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
    volumes:
      - proxy-data:/data
      - proxy-config:/config
    security_opt:
      - no-new-privileges:true
    networks: [web]
    depends_on:
      app:
        condition: service_healthy

  # Front Angular + API Symfony
  app:
    <<: *app
    depends_on:
      db:
        condition: service_healthy

  # Tâches quotidiennes : rafraîchissement TMDB, synchronisation Immich
  scheduler:
    <<: *app
    command: ["constellation-scheduler"]
    healthcheck:
      disable: true
    environment:
      <<: *app-env
      CONSTELLATION_INIT: "0"
      SCHEDULER_INTERVAL: ${SCHEDULER_INTERVAL:-86400}
      IMMICH_AUTO_LINK: ${IMMICH_AUTO_LINK:-0}
    depends_on:
      app:
        condition: service_healthy

  db:
    image: postgres:17-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: constellation
      POSTGRES_USER: constellation
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - db-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U constellation -d constellation"]
      interval: 10s
      timeout: 5s
      retries: 10
    security_opt:
      - no-new-privileges:true
    networks: [data]

networks:
  web: {}
  # Sans accès extérieur : la base n'est joignable que par l'application
  data:
    internal: true

volumes:
  app-data: {}
  app-var: {}
  photos: {}
  db-data: {}
  proxy-data: {}
  proxy-config: {}
⁠Variante B : vous avez déjà un reverse proxy (Nginx Proxy Manager, Traefik…)

Reprenez la variante A, puis :

  1. Supprimez le service proxy et les volumes proxy-data et proxy-config.
  2. Publiez le port de l'application dans le service app :
      app:
        <<: *app
        ports:
          - "127.0.0.1:8080:8080"   # 0.0.0.0:8080:8080 si le proxy tourne sur une autre machine
        depends_on:
          db:
            condition: service_healthy
    
  3. Remplacez FRONTEND_URL: https://${DOMAIN:?DOMAIN manquant} par l'adresse publique complète du site, par exemple FRONTEND_URL: https://constellation.mon-domaine.fr.
  4. Dans votre proxy, créez un hôte HTTPS qui pointe vers http://<ip-du-serveur>:8080. Le proxy doit remplacer l'en-tête X-Forwarded-For par l'IP du visiteur, comme le fait Nginx Proxy Manager par défaut. L'application s'en sert pour limiter les tentatives de connexion.

Test sur le réseau local, sans HTTPS : FRONTEND_URL: http://<ip-du-serveur>:8080, et ajoutez COOKIE_SECURE: "0" à l'environnement. Sinon, le navigateur refuse le cookie de session. N'exposez jamais ce mode sur internet.

⁠3. Créer le fichier .env
cat > .env <<'EOF'
# Nom de domaine du site (doit pointer vers ce serveur)
DOMAIN=constellation.mon-domaine.fr
CONSTELLATION_VERSION=1.1.0

# Secrets (générés à l'étape suivante)
APP_SECRET=
JWT_PASSPHRASE=
SETTINGS_ENCRYPTION_KEY=
POSTGRES_PASSWORD=

# Email ou URL transmis à Wikimedia et MusicBrainz (exigé par leurs règles d'usage)
SOURCES_CONTACT=

# Serveur mail (modifiable ensuite dans l'administration)
# ex. smtp://utilisateur:[email protected]:587
MAILER_DSN=null://null
MAILER_FROM=

# Clés des sources : facultatives ici, saisissables dans l'administration
API_NINJAS_KEY=
GOOGLE_KG_API_KEY=
TMDB_API_READ_ACCESS_TOKEN=
TMDB_API_KEY=
IMMICH_URL=
IMMICH_API_KEY=
EOF
chmod 600 .env
⁠4. Générer les secrets
sed -i "s|^APP_SECRET=.*|APP_SECRET=$(openssl rand -hex 32)|; s|^JWT_PASSPHRASE=.*|JWT_PASSPHRASE=$(openssl rand -hex 32)|; s|^SETTINGS_ENCRYPTION_KEY=.*|SETTINGS_ENCRYPTION_KEY=$(openssl rand -hex 32)|; s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$(openssl rand -hex 24)|" .env

Ouvrez ensuite .env et renseignez DOMAIN et SOURCES_CONTACT.

Sauvegardez .env. SETTINGS_ENCRYPTION_KEY chiffre les clés d'API et le mot de passe SMTP saisis dans l'administration : sans elle, ils deviennent illisibles. Ne changez pas POSTGRES_PASSWORD après le premier démarrage, car la base est créée avec ce mot de passe.

⁠5. Démarrer
docker compose up -d
docker compose ps

Le service app doit être healthy, généralement en moins d'une minute. Au premier démarrage, l'application vérifie sa configuration, attend la base, crée les tables et génère ses clés JWT.

⁠6. Créer le premier administrateur
docker compose exec app bin/console app:create-admin [email protected]

Le mot de passe est demandé de façon masquée, avec 12 caractères au minimum.

⁠7. Terminer la configuration

Ouvrez https://<votre-domaine>, connectez-vous, puis allez dans Administration › Paramètres :

  1. Serveur mail : renseignez votre serveur SMTP, puis cliquez sur M'envoyer un email de test. Sans serveur mail, les emails d'inscription et de mot de passe oublié sont perdus.
  2. Clés d'API : collez les clés des sources que vous utilisez. Le bouton Tester vérifie chacune.
SourceUtilitéOù obtenir la clé
TMDBFilmographies, castingthemoviedb.org › Paramètres › API (« API Read Access Token »)
API NinjasTaille, fortune, naissanceapi-ninjas.com › Profil
Google Knowledge GraphDescriptions, imagesGoogle Cloud › API « Knowledge Graph Search »
ImmichAlbums photoImmich › Paramètres du compte › Clés d'API (permissions album.read, asset.read, asset.view, et album.create pour créer des albums)

Wikipedia, Wikidata, Wikimedia Commons, Openverse et MusicBrainz ne demandent pas de clé.


⁠Exploitation

⁠Mise à jour
# Dans .env : CONSTELLATION_VERSION=<nouvelle version>
docker compose pull
docker compose up -d

Les migrations de base sont appliquées automatiquement au démarrage. Faites une sauvegarde avant chaque mise à jour.

⁠Sauvegarde
# Base de données
docker compose exec -T db pg_dump -U constellation -Fc constellation > constellation-$(date +%F).dump
# Photos
docker run --rm -v constellation_photos:/photos:ro -v "$PWD":/backup alpine tar czf /backup/photos-$(date +%F).tgz -C /photos .

Restauration de la base :

docker compose exec -T db pg_restore -U constellation -d constellation --clean --if-exists < constellation-AAAA-MM-JJ.dump
VolumeContenuÀ sauvegarder
db-dataBase PostgreSQLOui, avec pg_dump
photosPhotos des galeriesOui
app-dataClés JWT, données CaddyFacultatif
app-varCache, verrousNon
⁠Tâches périodiques

Le service scheduler lance, 5 minutes après le démarrage puis une fois par jour :

  • app:tmdb:refresh : rafraîchit les filmographies. Les conditions TMDB interdisent de garder leurs données plus de 6 mois.
  • app:immich:sync : synchronise les galeries avec les albums Immich.

Avec IMMICH_AUTO_LINK=1, chaque fiche sans album est d'abord liée à l'album Immich de même nom.

⁠Commandes utiles
docker compose logs -f app                                  # journaux (JSON)
docker compose exec app bin/console app:create-admin email  # créer ou promouvoir un administrateur
docker compose exec scheduler bin/console app:immich:sync --auto-link
docker compose exec db psql -U constellation constellation  # console SQL
⁠Variables d'environnement
VariableObligatoireRôle
DOMAINoui (variante A)Nom de domaine ; l'adresse du site devient https://DOMAIN
FRONTEND_URLoui (variante B)Adresse publique du site
APP_SECRETouiSecret Symfony (openssl rand -hex 32)
JWT_PASSPHRASEouiProtège la clé privée JWT
SETTINGS_ENCRYPTION_KEYoui64 caractères hexadécimaux (openssl rand -hex 32)
POSTGRES_PASSWORDouiMot de passe de la base
SOURCES_CONTACTconseilléEmail ou URL transmis à Wikimedia et MusicBrainz
MAILER_DSN / MAILER_FROMnonServeur SMTP et expéditeur (aussi dans l'administration)
TMDB_API_READ_ACCESS_TOKEN, TMDB_API_KEY, API_NINJAS_KEY, GOOGLE_KG_API_KEY, IMMICH_URL, IMMICH_API_KEYnonClés des sources (aussi dans l'administration)
WIKIPEDIA_LANG / TMDB_LANGUAGEnonfr / fr-FR par défaut
COOKIE_SECUREnon1 par défaut ; 0 uniquement pour un test local en HTTP
SCHEDULER_INTERVALnonIntervalle des tâches périodiques, en secondes (86400)
IMMICH_AUTO_LINKnon1 : lie automatiquement les albums Immich de même nom

⁠Sécurité

  • Conteneurs : ils tournent avec un utilisateur sans privilège (uid 1000), un système de fichiers en lecture seule, toutes les capacités Linux retirées et no-new-privileges.
  • Base de données : elle se trouve sur un réseau interne, sans accès extérieur, et n'expose aucun port.
  • En-têtes : Content-Security-Policy stricte (aucun script en ligne), X-Frame-Options: DENY, nosniff, Referrer-Policy et Permissions-Policy. Les photos sont servies avec sandbox.
  • Sessions : le JWT est gardé en mémoire et le jeton de rafraîchissement dans un cookie HttpOnly, Secure et SameSite=Strict. Les tentatives de connexion, d'inscription et d'email sont limitées.
  • Secrets en base : les clés d'API et le mot de passe SMTP sont chiffrés (libsodium). Ils ne sont jamais réaffichés, et leur modification demande le mot de passe de l'administrateur.
  • Appels sortants : ils sont protégés contre la SSRF ; les adresses privées et locales sont refusées pour les sources et Immich.

⁠Dépannage

SymptômeCause probable
la variable … est obligatoire dans les journauxVariable absente de .env
base de données injoignablePOSTGRES_PASSWORD modifié après le premier démarrage. Remettez l'ancien, ou supprimez le volume constellation_db-data si la base est vide
app reste unhealthyConsultez docker compose logs app. Vérifiez aussi l'architecture : l'image est amd64
Déconnexion juste après la connexionSite servi en HTTP avec COOKIE_SECURE=1
Aucun email reçuServeur mail non configuré (MAILER_DSN=null://null)
« Trop de tentatives » pour tout le mondeLe proxy ne transmet pas l'IP du visiteur (X-Forwarded-For)
Certificat HTTPS non obtenu (variante A)Le domaine ne pointe pas vers le serveur, ou les ports 80 et 443 sont fermés

⁠Mentions

Les données de filmographie viennent de l'API TMDB, sans être approuvées ni certifiées par TMDB : leur usage est réservé à un cadre non commercial. Les données MusicBrainz et Wikidata sont sous licence CC0. Les photos Commons et Openverse sont affichées avec leur auteur et leur licence. Les photos importées depuis Immich doivent être des photos que vous avez le droit de diffuser.

Tag summary

Content type

Image

Digest

sha256:fcfec38f9…

Size

129.2 MB

Last updated

22 days ago

docker pull iemperor/constellation