katalon-cli 0.2.1__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/PKG-INFO +26 -1
  2. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/README.md +25 -0
  3. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/pyproject.toml +1 -1
  4. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/pyproject.toml.orig +1 -1
  5. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/main.py +42 -0
  6. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/templates/compose.yaml.j2 +28 -3
  7. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/templates/nginx.conf.j2 +18 -0
  8. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/__init__.py +0 -0
  9. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/core/__init__.py +0 -0
  10. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/core/backup.py +0 -0
  11. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/core/checks.py +0 -0
  12. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/core/compose_gen.py +0 -0
  13. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/core/docker.py +0 -0
  14. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/core/paths.py +0 -0
  15. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/core/permissions.py +0 -0
  16. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/core/reindex.py +0 -0
  17. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/core/release.py +0 -0
  18. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/core/selfupdate.py +0 -0
  19. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/core/state.py +0 -0
  20. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/py.typed +0 -0
  21. {katalon_cli-0.2.1 → katalon_cli-0.3.0}/src/katalon_cli/templates/Caddyfile.j2 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: katalon-cli
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: Installer & Updater CLI für Katalon Production-Instanzen
5
5
  Author: Karl Krägelin
6
6
  Author-email: Karl Krägelin <karlkraegelin@gmail.com>
@@ -83,6 +83,31 @@ unter `MEDIA_ROOT`, die noch einem anderen Eigentümer gehören. Dateiinhalte we
83
83
  verändert; ein erneuter Lauf ändert nichts. `katalon doctor` meldet ein nicht beschreibbares
84
84
  `MEDIA_ROOT`, bevor Uploads mit HTTP 500 scheitern.
85
85
 
86
+ ## Bild-Cache (Cantaloupe)
87
+
88
+ Vorschaubilder und Viewer-Kacheln rechnet Cantaloupe aus den Originalen und legt sie im
89
+ Instanzverzeichnis unter `cantaloupe-cache/` ab (abweichend: `CANTALOUPE_CACHE_DIR` in `.env`).
90
+ Der Cache übersteht Neustarts und Updates; ein kurzlebiger Init-Container übergibt das
91
+ Verzeichnis vor jedem Start dem Cantaloupe-User. Abgelaufene Derivate
92
+ (`CANTALOUPE_CACHE_SERVER_DERIVATIVE_TTL_SECONDS`, Standard 30 Tage) löscht Cantaloupe selbst.
93
+
94
+ Eine Größenbegrenzung kennt Cantaloupe nicht. Als Richtwert ~25 KB pro Vorschaubild plus
95
+ Kacheln der aufgerufenen Detailansichten einplanen; wer den Platz hart begrenzen muss, legt
96
+ `CANTALOUPE_CACHE_DIR` auf ein eigenes Dateisystem bzw. Quota-Verzeichnis. Hosts ohne
97
+ Plattenplatz nutzen mit `CANTALOUPE_FILESYSTEMCACHE_PATHNAME=/tmpfs/cache` einen RAM-Cache
98
+ (`CANTALOUPE_CACHE_TMPFS_SIZE`, Standard `512m`), der nach jedem Neustart leer ist.
99
+
100
+ Vorschaubilder lassen sich vorab berechnen, damit schon der erste Aufruf der Trefferliste
101
+ schnell ist: `katalon media warm-thumbnails` (Hauptbilder zuerst, dann die übrigen; mit
102
+ `--primary-only` nur Hauptbilder, `--status` zeigt den Fortschritt). Der Lauf arbeitet im
103
+ Worker in kleinen Paketen, belegt höchstens `THUMBNAIL_WARM_CONCURRENCY` (Standard 3)
104
+ Cantaloupe-Kerne und setzt nach einer Unterbrechung beim letzten Stand fort. Neu hochgeladene
105
+ Bilder werden automatisch vorberechnet.
106
+
107
+ Bestehende Instanzen stellt `katalon update` automatisch um (neue `compose.yaml`); der bisherige
108
+ RAM-Cache entfällt dabei. Releases, die eine neuere CLI voraussetzen, bricht `katalon update`
109
+ mit einem Hinweis auf `uv tool upgrade katalon-cli` ab.
110
+
86
111
  ## Domain und HTTPS
87
112
 
88
113
  Für eine lokale Installation bleibt `http://localhost` mit TLS-Modus `none` der Standard.
@@ -69,6 +69,31 @@ unter `MEDIA_ROOT`, die noch einem anderen Eigentümer gehören. Dateiinhalte we
69
69
  verändert; ein erneuter Lauf ändert nichts. `katalon doctor` meldet ein nicht beschreibbares
70
70
  `MEDIA_ROOT`, bevor Uploads mit HTTP 500 scheitern.
71
71
 
72
+ ## Bild-Cache (Cantaloupe)
73
+
74
+ Vorschaubilder und Viewer-Kacheln rechnet Cantaloupe aus den Originalen und legt sie im
75
+ Instanzverzeichnis unter `cantaloupe-cache/` ab (abweichend: `CANTALOUPE_CACHE_DIR` in `.env`).
76
+ Der Cache übersteht Neustarts und Updates; ein kurzlebiger Init-Container übergibt das
77
+ Verzeichnis vor jedem Start dem Cantaloupe-User. Abgelaufene Derivate
78
+ (`CANTALOUPE_CACHE_SERVER_DERIVATIVE_TTL_SECONDS`, Standard 30 Tage) löscht Cantaloupe selbst.
79
+
80
+ Eine Größenbegrenzung kennt Cantaloupe nicht. Als Richtwert ~25 KB pro Vorschaubild plus
81
+ Kacheln der aufgerufenen Detailansichten einplanen; wer den Platz hart begrenzen muss, legt
82
+ `CANTALOUPE_CACHE_DIR` auf ein eigenes Dateisystem bzw. Quota-Verzeichnis. Hosts ohne
83
+ Plattenplatz nutzen mit `CANTALOUPE_FILESYSTEMCACHE_PATHNAME=/tmpfs/cache` einen RAM-Cache
84
+ (`CANTALOUPE_CACHE_TMPFS_SIZE`, Standard `512m`), der nach jedem Neustart leer ist.
85
+
86
+ Vorschaubilder lassen sich vorab berechnen, damit schon der erste Aufruf der Trefferliste
87
+ schnell ist: `katalon media warm-thumbnails` (Hauptbilder zuerst, dann die übrigen; mit
88
+ `--primary-only` nur Hauptbilder, `--status` zeigt den Fortschritt). Der Lauf arbeitet im
89
+ Worker in kleinen Paketen, belegt höchstens `THUMBNAIL_WARM_CONCURRENCY` (Standard 3)
90
+ Cantaloupe-Kerne und setzt nach einer Unterbrechung beim letzten Stand fort. Neu hochgeladene
91
+ Bilder werden automatisch vorberechnet.
92
+
93
+ Bestehende Instanzen stellt `katalon update` automatisch um (neue `compose.yaml`); der bisherige
94
+ RAM-Cache entfällt dabei. Releases, die eine neuere CLI voraussetzen, bricht `katalon update`
95
+ mit einem Hinweis auf `uv tool upgrade katalon-cli` ab.
96
+
72
97
  ## Domain und HTTPS
73
98
 
74
99
  Für eine lokale Installation bleibt `http://localhost` mit TLS-Modus `none` der Standard.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "katalon-cli"
3
- version = "0.2.1"
3
+ version = "0.3.0"
4
4
  description = "Installer & Updater CLI für Katalon Production-Instanzen"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "katalon-cli"
3
- version = "0.2.1"
3
+ version = "0.3.0"
4
4
  description = "Installer & Updater CLI für Katalon Production-Instanzen"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -456,6 +456,21 @@ def _media_root(dir: Path) -> str:
456
456
  return str(path if path.is_absolute() else dir / path)
457
457
 
458
458
 
459
+ def _require_installer_version(minimum: str) -> None:
460
+ """Bricht ab, wenn das Release eine neuere CLI braucht (z. B. neues Compose-Template).
461
+
462
+ Sonst würde eine alte CLI ihr altes compose.yaml-Template schreiben und das Release
463
+ liefe ohne die Topologie, für die es gebaut ist."""
464
+ current = version("katalon-cli")
465
+ if selfupdate.is_newer(minimum, current):
466
+ console.print(
467
+ f"[red]✖ Dieses Release braucht katalon-cli ≥ {minimum} (installiert: {current}).[/]\n"
468
+ " Zuerst aktualisieren: [bold]uv tool upgrade katalon-cli[/], dann erneut "
469
+ "[bold]katalon update[/]."
470
+ )
471
+ raise typer.Exit(1)
472
+
473
+
459
474
  def _ensure_media_permissions(dir: Path) -> bool:
460
475
  """Macht MEDIA_ROOT für den Container-User von API/Worker beschreibbar (idempotent).
461
476
 
@@ -559,6 +574,32 @@ def manage(
559
574
  raise typer.Exit(result.returncode)
560
575
 
561
576
 
577
+ media_app = typer.Typer(no_args_is_help=True, help="Medien: Vorschaubilder vorberechnen.")
578
+ app.add_typer(media_app, name="media")
579
+
580
+
581
+ @media_app.command("warm-thumbnails")
582
+ def media_warm_thumbnails(
583
+ dir: Path | None = typer.Option(None, "--dir"),
584
+ primary_only: bool = typer.Option(False, "--primary-only", help="Nur das Hauptbild jedes Objekts."),
585
+ restart: bool = typer.Option(False, "--restart", help="Von vorn beginnen statt fortsetzen."),
586
+ status: bool = typer.Option(False, "--status", help="Nur den Fortschritt anzeigen."),
587
+ ):
588
+ """Vorschaubilder im Cantaloupe-Cache vorberechnen (läuft im Worker, fortsetzbar).
589
+
590
+ Sinnvoll nach Installation, Massenimport oder Umstellung des Bild-Caches."""
591
+ dir = instance_dir_or_raise(dir)
592
+ args = ["warm-thumbnails"]
593
+ if primary_only:
594
+ args.append("--primary-only")
595
+ if restart:
596
+ args.append("--restart")
597
+ if status:
598
+ args.append("--status")
599
+ result = docker.compose(dir, "exec", "api", "katalon-manage", *args, check=False)
600
+ raise typer.Exit(result.returncode)
601
+
602
+
562
603
  def _ensure_db_running(dir: Path, yes: bool = False) -> None:
563
604
  if docker.is_running(dir, "db"):
564
605
  return
@@ -630,6 +671,7 @@ def check_updates(dir: Path | None = typer.Option(None, "--dir")):
630
671
  if meta.version == state.version:
631
672
  console.print(f"[green]✔[/] Bereits auf aktueller Version {meta.version}.")
632
673
  return
674
+ _require_installer_version(meta.minimum_installer_version)
633
675
  console.print(f"Update verfügbar: [bold]{state.version}[/] → [bold]{meta.version}[/]")
634
676
 
635
677
 
@@ -39,15 +39,31 @@ services:
39
39
  timeout: 10s
40
40
  retries: 20
41
41
 
42
+ # Legt den Derivat-Cache an und übergibt ihn dem Cantaloupe-User des Images.
43
+ # Ändert nur Einträge mit falschem Eigentümer (idempotent, schnell bei vollem Cache).
44
+ cantaloupe-cache-init:
45
+ image: islandora/cantaloupe:main
46
+ user: "0:0"
47
+ entrypoint: ["sh", "-c", "mkdir -p /data/cache && find /data ! -user cantaloupe -exec chown cantaloupe:cantaloupe {} +"]
48
+ volumes:
49
+ - ${CANTALOUPE_CACHE_DIR:-./cantaloupe-cache}:/data
50
+ restart: "no"
51
+
42
52
  cantaloupe:
43
53
  image: islandora/cantaloupe:main
44
54
  restart: unless-stopped
45
55
  volumes:
46
56
  - ${MEDIA_ROOT:-./media}:/var/lib/cantaloupe/images:ro
57
+ # Derivat-Cache auf Platte, übersteht Neustarts. Ohne Plattenplatz:
58
+ # CANTALOUPE_FILESYSTEMCACHE_PATHNAME=/tmpfs/cache in .env (RAM, leer nach Neustart).
59
+ - ${CANTALOUPE_CACHE_DIR:-./cantaloupe-cache}:/data
47
60
  - type: tmpfs
48
- target: /var/lib/cantaloupe/cache
61
+ target: /tmpfs
49
62
  tmpfs:
50
63
  size: ${CANTALOUPE_CACHE_TMPFS_SIZE:-512m}
64
+ mode: 1023 # = 0o1777 (Compose liest den Wert dezimal)
65
+ depends_on:
66
+ cantaloupe-cache-init: { condition: service_completed_successfully }
51
67
  environment:
52
68
  CANTALOUPE_SOURCE_STATIC: FilesystemSource
53
69
  CANTALOUPE_FILESYSTEMSOURCE_LOOKUP_STRATEGY: BasicLookupStrategy
@@ -55,10 +71,19 @@ services:
55
71
  CANTALOUPE_BASE_URI: ${CANTALOUPE_PUBLIC_URL:-http://localhost}
56
72
  CANTALOUPE_CACHE_SERVER_DERIVATIVE: FilesystemCache
57
73
  CANTALOUPE_CACHE_SERVER_DERIVATIVE_ENABLED: "true"
58
- CANTALOUPE_FILESYSTEMCACHE_PATHNAME: /var/lib/cantaloupe/cache
74
+ CANTALOUPE_FILESYSTEMCACHE_PATHNAME: ${CANTALOUPE_FILESYSTEMCACHE_PATHNAME:-/data/cache}
75
+ # Der Cache-Worker löscht Derivate älter als die TTL; ohne ihn wächst der Cache unbegrenzt.
76
+ CANTALOUPE_CACHE_SERVER_WORKER_ENABLED: "true"
77
+ CANTALOUPE_CACHE_SERVER_DERIVATIVE_TTL_SECONDS: ${CANTALOUPE_CACHE_SERVER_DERIVATIVE_TTL_SECONDS:-2592000}
59
78
  CANTALOUPE_PROCESSOR_MANUALSELECTIONSTRATEGY_JPG: Java2dProcessor
79
+ # Fallback ohne nginx davor: nie "public". nginx setzt Cache-Control für /iiif/
80
+ # nach Sichtbarkeit (X-Media-Visibility aus /v1/media/_authorize).
81
+ CANTALOUPE_CACHE_CLIENT_PUBLIC: "false"
82
+ CANTALOUPE_CACHE_CLIENT_PRIVATE: "true"
60
83
  healthcheck:
61
- test: ["CMD-SHELL", "curl -sf http://localhost:8182/iiif/3"]
84
+ # Prüft neben der Erreichbarkeit, dass der Cantaloupe-Prozess in seinen Cache
85
+ # schreiben kann — sonst liefert er Bilder mit 0 Bytes bei HTTP 200.
86
+ test: ["CMD-SHELL", "curl -sf http://localhost:8182/iiif/3 && runuser -u cantaloupe -- sh -c 'd=$${CANTALOUPE_FILESYSTEMCACHE_PATHNAME:-/data/cache}; mkdir -p \"$$d\" && touch \"$$d/.healthcheck\" && rm -f \"$$d/.healthcheck\"'"]
62
87
  interval: 10s
63
88
  timeout: 5s
64
89
  start_period: 300s
@@ -1,5 +1,13 @@
1
1
  resolver 127.0.0.11 valid=10s ipv6=off;
2
2
 
3
+ # Cache-Control for /iiif/ from the media's visibility (set by /v1/media/_authorize via
4
+ # auth_request_set): shared caches (CDN, proxies) may keep only what anonymous
5
+ # visitors may see; derivatives of internal media stay in the requesting browser.
6
+ map $media_visibility $iiif_cache_control {
7
+ public "public, max-age=2592000";
8
+ default "private, max-age=86400";
9
+ }
10
+
3
11
  server {
4
12
  {% if tls_mode == "standalone" %}
5
13
  listen 80;
@@ -73,12 +81,22 @@ server {
73
81
 
74
82
  location /iiif/ {
75
83
  auth_request /internal/media-authorize;
84
+ auth_request_set $media_visibility $upstream_http_x_media_visibility;
76
85
  set $upstream_cantaloupe cantaloupe:8182;
77
86
  proxy_pass http://$upstream_cantaloupe;
78
87
  proxy_set_header Host $host;
79
88
  proxy_set_header X-Real-IP $remote_addr;
80
89
  proxy_read_timeout 300s;
81
90
  proxy_buffering off;
91
+ # Cantaloupe marks every derivative public; decide by visibility instead.
92
+ proxy_hide_header Cache-Control;
93
+ add_header Cache-Control $iiif_cache_control;
94
+ # An add_header here drops the server-level ones: repeat them.
95
+ add_header Content-Security-Policy "default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'; form-action 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data: blob: https:; media-src 'self' blob: https:; connect-src 'self' https:; frame-src 'self' https://www.openstreetmap.org; worker-src 'self' blob:" always;
96
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
97
+ add_header X-Content-Type-Options "nosniff" always;
98
+ add_header X-Frame-Options "SAMEORIGIN" always;
99
+ add_header Permissions-Policy "camera=(), geolocation=(), microphone=()" always;
82
100
  }
83
101
 
84
102
  location = /internal/media-authorize {