feat(aether): initial commit – self-hosted Cloud, Phasen 1–7
Go-Backend (chi, pgx, golang-migrate) + React/TS-Frontend als PWA. Enthalten: Auth inkl. OIDC, Dateien mit Versionen und Papierkorb, Freigaben (Links + intern), WebDAV, Gruppenordner, Admin/Theming, Volltextsuche mit OCR, eigener PDF-Betrachter und WOPI-Office (Collabora/OnlyOffice). Nicht im Repo: deploy/.env (Sitzungsschlüssel, DB- und Admin-Passwort) sowie alle Daten- und Build-Verzeichnisse – siehe .gitignore. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
commit
31a027c93a
225 files changed
+38042
No files matched your search
@@ -0,0 +1,137 @@
|
||||
services:
|
||||
app:
|
||||
container_name: aether-app
|
||||
# Fertiges Abbild aus der eigenen Gitea-Registry.
|
||||
#
|
||||
# Ein Tag mit Datum **und** `latest`: Zum Aufbauen nimmt man `latest`, zum
|
||||
# Zurückrollen den datierten. Wer nur `latest` fährt, hat im Fehlerfall
|
||||
# nichts, worauf er zurück kann – der Name zeigt dann schon auf die
|
||||
# Fassung, die gerade Ärger macht.
|
||||
#
|
||||
# Anmelden vor dem ersten Zug:
|
||||
# docker login gitea.honeyy.duckdns.org
|
||||
#
|
||||
# Aktualisieren:
|
||||
# docker compose pull app && docker compose up -d app
|
||||
image: gitea.honeyy.duckdns.org/honey/aether:latest
|
||||
# Selbst bauen statt ziehen? Dann die Zeile darüber auskommentieren und
|
||||
# diese drei einkommentieren. Der Kontext ist das **Wurzelverzeichnis** des
|
||||
# Projekts, nicht dieser Ordner – das Abbild braucht backend/ und frontend/.
|
||||
#
|
||||
# docker compose build app
|
||||
#
|
||||
# build:
|
||||
# context: ..
|
||||
# dockerfile: deploy/Dockerfile
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
# Als der Nutzer laufen, dem das Datenverzeichnis auf dem Wirt gehört.
|
||||
# Ohne das legt der Container seine Dateien als uid 100 an, und du kämst
|
||||
# per SFTP nur als root daran.
|
||||
user: "${APP_UID:-1000}:${APP_GID:-1000}"
|
||||
# ocrmypdf und ghostscript legen Arbeitsdateien unter $HOME ab. Bei einem
|
||||
# abweichenden Nutzer ist das Heimverzeichnis aus dem Image nicht mehr
|
||||
# beschreibbar – /tmp im Container ist es immer.
|
||||
environment:
|
||||
HOME: /tmp
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
# Bind-Mount statt eines benannten Volumes: Deine Dateien liegen damit in
|
||||
# einem gewöhnlichen Verzeichnis auf dem Wirt, mit echten Ordnern und
|
||||
# echten Namen (users/<name>/home/…). Du kommst per SFTP, rsync oder
|
||||
# einfach im Dateimanager daran – auch dann noch, wenn diese Anwendung
|
||||
# nie wieder startet. Bei einem benannten Volume läge alles unter
|
||||
# /var/lib/docker/volumes/ und gehörte root.
|
||||
#
|
||||
# Nur lesen. Wer hier hineinschreibt, hat eine Datei auf der Platte, die
|
||||
# in der Datenbank nicht existiert – die Cloud sieht sie nicht, und der
|
||||
# GC räumt sie irgendwann weg. Zum Hineinlegen ist WebDAV der Weg.
|
||||
- ${DATA_PATH:-./data}:/data
|
||||
networks:
|
||||
- internal
|
||||
ports:
|
||||
# Expose on loopback only — your external reverse proxy connects here.
|
||||
# Host port via APP_PORT in .env (default 8080), container port stays 8080.
|
||||
- "127.0.0.1:${APP_PORT:-8080}:8080"
|
||||
|
||||
postgres:
|
||||
container_name: aether-db
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: ${DB_NAME}
|
||||
POSTGRES_USER: ${DB_USER}
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
networks:
|
||||
- internal
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
|
||||
# ── Office (optional) ──────────────────────────────────────────────────────
|
||||
# Laeuft nur mit `--profile office`. Ohne das Profil startet der Container
|
||||
# nicht – wer kein Office will, soll nicht 1 GB Arbeitsspeicher dafuer
|
||||
# abgeben, nur weil die Zeilen hier stehen.
|
||||
#
|
||||
# docker compose --profile office up -d
|
||||
#
|
||||
# Der Office-Server holt die Datei **selbst** beim Backend ab. Er muss Aether
|
||||
# also unter PUBLIC_URL erreichen, nicht unter localhost. Im selben
|
||||
# Compose-Netz erreicht er es unter `app:8080` – dafuer steht `extra_hosts`
|
||||
# bzw. ein PUBLIC_URL, das auf den Wirt zeigt.
|
||||
collabora:
|
||||
container_name: aether-collabora
|
||||
image: collabora/code:latest
|
||||
profiles: ["office"]
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
# Welche Hosts ihre Dateien hier oeffnen duerfen. Punkte maskiert, weil
|
||||
# Collabora den Wert als regulaeren Ausdruck liest.
|
||||
aliasgroup1: ${OFFICE_ALIASGROUP:-http://host.docker.internal:8090}
|
||||
# Zwei getrennte Schalter, und beide zaehlen:
|
||||
#
|
||||
# ssl.enable – spricht Collabora selbst HTTPS?
|
||||
# ssl.termination – endet HTTPS davor, an einem Reverse Proxy?
|
||||
#
|
||||
# Steht termination auf true, sagt Collabora dem Browser „verbinde dich
|
||||
# per wss://" – auch dann, wenn davor gar kein HTTPS liegt. Im reinen
|
||||
# HTTP-Testaufbau scheitert damit die WebSocket-Verbindung, und der
|
||||
# Editor bleibt leer stehen, obwohl alles andere laedt (gemessen).
|
||||
#
|
||||
# Produktiv hinter deinem Reverse Proxy: OFFICE_SSL_TERMINATION=true.
|
||||
#
|
||||
# mount_jail_tree=false: Collabora sperrt jedes Dokument sonst in einen
|
||||
# eigenen Einhaengepunkt und braucht dafuer CAP_SYS_ADMIN. Die Rechte
|
||||
# dem Container zu geben waere der teurere Weg – ohne den Schalter
|
||||
# scheitert der Start eines Dokuments mit „Failed to exec coolmount".
|
||||
extra_params: >-
|
||||
--o:ssl.enable=${OFFICE_SSL:-false}
|
||||
--o:ssl.termination=${OFFICE_SSL_TERMINATION:-false}
|
||||
--o:mount_jail_tree=false
|
||||
dictionaries: de_DE en_US
|
||||
cap_add:
|
||||
- MKNOD
|
||||
extra_hosts:
|
||||
# Damit der Container den Wirt erreicht – dort haengt der Reverse Proxy
|
||||
# bzw. der veroeffentlichte Port von Aether.
|
||||
- "host.docker.internal:host-gateway"
|
||||
networks:
|
||||
- internal
|
||||
ports:
|
||||
- "127.0.0.1:${OFFICE_PORT:-9980}:9980"
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
# "data" ist bewusst weg – die Dateien liegen jetzt unter DATA_PATH auf dem
|
||||
# Wirt (siehe oben). Die Datenbank bleibt ein benanntes Volume: An ihre
|
||||
# Dateien geht man nicht von Hand, und Postgres mag fremde Besitzer nicht.
|
||||
|
||||
networks:
|
||||
internal:
|
||||
driver: bridge
|
||||
Reference in new issue
Block a user