UI fixing
This commit is contained in:
+33
-1
@@ -1,19 +1,49 @@
|
|||||||
|
# Build context exclusions for deploy/unraid/Dockerfile.all-in-one.
|
||||||
|
# Everything listed here is NOT sent to the Docker daemon.
|
||||||
|
# Keep this aggressive: the all-in-one image only needs
|
||||||
|
# backend/, frontend/, fixtures/, scripts/, deploy/ and VERSION.
|
||||||
|
|
||||||
.git
|
.git
|
||||||
|
.gitea
|
||||||
|
.github
|
||||||
.venv
|
.venv
|
||||||
venv
|
venv
|
||||||
__pycache__
|
__pycache__
|
||||||
*.pyc
|
*.pyc
|
||||||
.pytest_cache
|
.pytest_cache
|
||||||
|
.mypy_cache
|
||||||
|
.ruff_cache
|
||||||
|
|
||||||
frontend/node_modules
|
# Node dependencies at every level (root test harness + frontend).
|
||||||
|
node_modules
|
||||||
|
**/node_modules
|
||||||
frontend/dist
|
frontend/dist
|
||||||
frontend/*.tsbuildinfo
|
frontend/*.tsbuildinfo
|
||||||
|
**/*.tsbuildinfo
|
||||||
|
|
||||||
backend/.pytest_cache
|
backend/.pytest_cache
|
||||||
backend/**/*.pyc
|
backend/**/*.pyc
|
||||||
backend/**/__pycache__
|
backend/**/__pycache__
|
||||||
|
|
||||||
|
# Accidental self-copy of the repository into itself.
|
||||||
|
# Without this the build context is duplicated and the build stalls.
|
||||||
|
/geointel
|
||||||
|
|
||||||
|
# Local work products, never needed inside the image.
|
||||||
|
.codex-input
|
||||||
|
test-results
|
||||||
|
playwright-report
|
||||||
|
artifacts
|
||||||
|
RELEASE_NOTES
|
||||||
|
adr
|
||||||
|
docs
|
||||||
|
checklists
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Runtime data and secrets: mounted at runtime, never baked in.
|
||||||
storage
|
storage
|
||||||
postgres-data
|
postgres-data
|
||||||
|
backups
|
||||||
datasets/raw
|
datasets/raw
|
||||||
datasets/processed
|
datasets/processed
|
||||||
datasets/cache
|
datasets/cache
|
||||||
@@ -21,3 +51,5 @@ exports
|
|||||||
models
|
models
|
||||||
|
|
||||||
.env
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|||||||
+1
-1
@@ -167,7 +167,7 @@ GEOINTEL_POSTGIS_DATA_PATH=./postgres-data
|
|||||||
GEOINTEL_POSTGRES_DB=geointel
|
GEOINTEL_POSTGRES_DB=geointel
|
||||||
GEOINTEL_POSTGRES_USER=geointel
|
GEOINTEL_POSTGRES_USER=geointel
|
||||||
GEOINTEL_POSTGRES_PASSWORD=geointel
|
GEOINTEL_POSTGRES_PASSWORD=geointel
|
||||||
GEOINTEL_CORS_ORIGINS=http://localhost:1202,http://127.0.0.1:1202
|
GEOINTEL_CORS_ORIGINS=https://geointel.itworx.tech,http://geointel.itworx.tech,http://localhost:1202,http://127.0.0.1:1202,http://192.168.10.150:1202
|
||||||
GEOINTEL_MAX_UPLOAD_MB=500
|
GEOINTEL_MAX_UPLOAD_MB=500
|
||||||
GEOINTEL_AOI_WORKER_ENABLED=false
|
GEOINTEL_AOI_WORKER_ENABLED=false
|
||||||
GEOINTEL_AOI_WORKER_POLL_SECONDS=2
|
GEOINTEL_AOI_WORKER_POLL_SECONDS=2
|
||||||
|
|||||||
@@ -0,0 +1,210 @@
|
|||||||
|
# Handmatige deploy naar Unraid (1 container, poort 1202)
|
||||||
|
|
||||||
|
Voor als de bestaande deploy stuk is en je de code met de hand kopieert
|
||||||
|
(zip/scp/SMB) in plaats van via `git clone`. Eindresultaat: **één** container
|
||||||
|
met de naam `geointel`, bereikbaar op `http://192.168.10.150:1202`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Kopiëren
|
||||||
|
|
||||||
|
Kopieer de **volledige** map `C:\Projects\geointel` naar
|
||||||
|
`/mnt/user/appdata/geointel` op de server. Verder niets uitzoeken.
|
||||||
|
|
||||||
|
`.dockerignore` regelt de rest: `node_modules/`, `.git/`, de dubbele
|
||||||
|
`geointel/`-map, `docs/`, `artifacts/` en testoutput gaan niet mee de
|
||||||
|
build-context in, ook al staan ze in de map.
|
||||||
|
|
||||||
|
Eén waarschuwing bij het overschrijven: laat `storage/`, `postgres-data/`,
|
||||||
|
`backups/` en `models/` op de server **staan**. Dat is je bestaande data, en
|
||||||
|
die staat niet in je Windows-map. Kopieer dus overschrijvend, niet met een
|
||||||
|
"mirror"/"purge"-optie die bestanden aan de doelzijde weggooit.
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Vanaf Windows, veilig: kopieert en overschrijft, verwijdert niets aan de doelkant.
|
||||||
|
robocopy C:\Projects\geointel \\192.168.10.150\appdata\geointel /E
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Rechten en regeleindes goedzetten
|
||||||
|
|
||||||
|
Na een kopie via Windows zijn de execute-bits weg. Op de Unraid-shell:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /mnt/user/appdata/geointel
|
||||||
|
find . -name '*.sh' -exec sed -i 's/\r$//' {} +
|
||||||
|
sed -i 's/\r$//' deploy/unraid/gosu-setpriv
|
||||||
|
chmod +x deploy/unraid/*.sh scripts/*.sh deploy/unraid/gosu-setpriv
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. `.env` aanmaken
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /mnt/user/appdata/geointel
|
||||||
|
cp deploy/unraid/geointel.env.example .env
|
||||||
|
nano .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Verplicht aan te passen:
|
||||||
|
|
||||||
|
- `GEOINTEL_POSTGRES_PASSWORD` — de deploy **weigert** te starten met een lege
|
||||||
|
of standaardwaarde (`geointel`, `postgres`, `changeme`,
|
||||||
|
`change-me-before-shared-use`). Gebruik hier dezelfde waarde als je vorige
|
||||||
|
installatie, anders kan de bestaande PostGIS-data niet worden geopend.
|
||||||
|
|
||||||
|
Controleer dat deze op de bestaande data wijzen:
|
||||||
|
|
||||||
|
```env
|
||||||
|
GEOINTEL_FRONTEND_PORT=1202
|
||||||
|
GEOINTEL_STORAGE_PATH=/mnt/user/appdata/geointel/storage
|
||||||
|
GEOINTEL_POSTGIS_DATA_PATH=/mnt/user/appdata/geointel/postgres-data
|
||||||
|
GEOINTEL_MODELS_PATH=/mnt/user/appdata/geointel/models
|
||||||
|
GEOINTEL_BACKUPS_PATH=/mnt/user/appdata/geointel/backups
|
||||||
|
GEOINTEL_CORS_ORIGINS=https://geointel.itworx.tech,http://geointel.itworx.tech,http://localhost:1202,http://127.0.0.1:1202,http://192.168.10.150:1202
|
||||||
|
```
|
||||||
|
|
||||||
|
Staat Nginx Proxy Manager voor poort 1202 op `geointel.itworx.tech`, dan moet
|
||||||
|
die hostname in `GEOINTEL_CORS_ORIGINS` staan — anders blokkeert de browser de
|
||||||
|
API-calls vanaf het publieke adres. De backend doet geen host-validatie, dus
|
||||||
|
verder is er niets nodig aan applicatiekant.
|
||||||
|
|
||||||
|
Voor GPU-inferentie (optioneel, kan ook later):
|
||||||
|
|
||||||
|
```env
|
||||||
|
GEOINTEL_INSTALL_AI=true
|
||||||
|
YOLO_ENABLED=true
|
||||||
|
YOLO_DEVICE=cuda:0
|
||||||
|
YOLO_REQUIRE_CUDA=true
|
||||||
|
YOLO_MODEL_PATH=/app/models/<jouw-model>.pt
|
||||||
|
```
|
||||||
|
|
||||||
|
Laat `GEOINTEL_INSTALL_AI=false` staan als je eerst gewoon wilt dat de app
|
||||||
|
draait — dat scheelt een paar GB aan PyTorch-lagen in de build.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Login instellen
|
||||||
|
|
||||||
|
De app slaat geen plaintext wachtwoord op: `.env` bevat alleen een
|
||||||
|
pbkdf2_sha256-hash. Dit script maakt die hash plus een sessiegeheim en zet ze
|
||||||
|
in `.env`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /mnt/user/appdata/geointel
|
||||||
|
bash scripts/configure_operator_login.sh --username jens@itworx.tech --guest-access true
|
||||||
|
```
|
||||||
|
|
||||||
|
`--guest-access true` is nodig om de demo-ingang te behouden; zonder die vlag
|
||||||
|
zet het script `GEOINTEL_GUEST_ACCESS_ENABLED=false`.
|
||||||
|
|
||||||
|
Het script vraagt het wachtwoord tweemaal (je typt het niet zichtbaar en het
|
||||||
|
komt niet in je shell-history), en zet:
|
||||||
|
|
||||||
|
```env
|
||||||
|
GEOINTEL_AUTH_ENABLED=true
|
||||||
|
GEOINTEL_AUTH_USERNAME=jens@itworx.tech
|
||||||
|
GEOINTEL_AUTH_PASSWORD_HASH=pbkdf2_sha256$600000$...
|
||||||
|
GEOINTEL_AUTH_SESSION_SECRET=<48 willekeurige tekens>
|
||||||
|
GEOINTEL_GUEST_ACCESS_ENABLED=false
|
||||||
|
```
|
||||||
|
|
||||||
|
Gastentoegang wordt bewust uitgezet — met auth aan zou anders de publieke
|
||||||
|
demo-ingang "Als gast verkennen" op de loginpagina blijven staan. Wil je die
|
||||||
|
wel: `--guest-access true`.
|
||||||
|
|
||||||
|
Wachtwoord later wijzigen: draai hetzelfde commando opnieuw en deploy. Er
|
||||||
|
wordt dan ook een nieuw sessiegeheim gemaakt, dus openstaande browsersessies
|
||||||
|
worden ongeldig.
|
||||||
|
|
||||||
|
> Het wachtwoord dat je me in de chat gaf staat nu in die chatgeschiedenis.
|
||||||
|
> Draai het script met een nieuw wachtwoord zodra de deploy staat.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Deployen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /mnt/user/appdata/geointel
|
||||||
|
docker compose -f docker-compose.unraid.yml config >/dev/null # syntaxcheck
|
||||||
|
bash deploy/unraid/deploy-release.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Het script:
|
||||||
|
|
||||||
|
1. ruimt een eventueel achtergebleven Compose-stack op (ook de oude
|
||||||
|
3-container dev-stack die óók poort 1202 pakt);
|
||||||
|
2. bewaart de huidige image als `geointel-all-in-one:previous`;
|
||||||
|
3. bouwt `deploy/unraid/Dockerfile.all-in-one`;
|
||||||
|
4. start één container `geointel` met `-p 1202:80` en `--gpus all`;
|
||||||
|
5. rolt automatisch terug naar `:previous` als de healthcheck of smoke faalt.
|
||||||
|
|
||||||
|
De eerste build duurt lang (PostGIS + GDAL + npm build). Volgende deploys
|
||||||
|
hergebruiken de Docker-layercache.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Controleren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker ps --filter name=geointel # exact 1 regel verwacht
|
||||||
|
curl -fsS http://127.0.0.1:1202/health/live
|
||||||
|
curl -fsS http://127.0.0.1:1202/health/ready
|
||||||
|
curl -fsS http://127.0.0.1:1202/api/v1/system/capabilities
|
||||||
|
```
|
||||||
|
|
||||||
|
Browser: `http://192.168.10.150:1202`
|
||||||
|
|
||||||
|
Bij problemen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker logs --tail 200 geointel
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Belangrijk: dubbele projectmap lokaal
|
||||||
|
|
||||||
|
In `C:\Projects\geointel` staat een tweede, volledige kopie van het project
|
||||||
|
onder `C:\Projects\geointel\geointel\`. Die bevat dezelfde bestanden en
|
||||||
|
dezelfde datum, maar staat buiten git. Zolang die er staat:
|
||||||
|
|
||||||
|
- wordt de Docker build-context onnodig verdubbeld;
|
||||||
|
- weet je bij het bewerken van bijvoorbeeld `docker-compose.unraid.yml` niet
|
||||||
|
welke versie je te pakken hebt.
|
||||||
|
|
||||||
|
`.dockerignore` sluit hem nu uit, maar ruim hem op zodra je zeker weet dat er
|
||||||
|
niets unieks in staat. Vergelijk eerst:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
robocopy C:\Projects\geointel\geointel C:\Projects\geointel /L /E /NJH /NJS /NDL /XF *.pyc
|
||||||
|
```
|
||||||
|
|
||||||
|
Regels die als `New File` verschijnen bestaan alleen in de kopie.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Terugrollen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /mnt/user/appdata/geointel
|
||||||
|
bash deploy/unraid/rollback-dockerman-container.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Rollback hergebruikt dezelfde PostGIS- en storage-paden en draait nooit een
|
||||||
|
Alembic-downgrade.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Wat er is aangepast om dit mogelijk te maken
|
||||||
|
|
||||||
|
| Bestand | Aanpassing |
|
||||||
|
|---|---|
|
||||||
|
| `.dockerignore` | Sluit root-`node_modules`, de dubbele `geointel/`-map, `.git`, `docs/`, `artifacts/` en testoutput uit de build-context |
|
||||||
|
| `deploy/unraid/deploy-release.sh` | `git rev-parse HEAD` crashte op een kopie zonder `.git`. Valt nu terug op `GEOINTEL_BUILD_SHA`, een `RELEASE_SHA`-bestand of een content-hash van de broncode |
|
||||||
|
| `deploy/unraid/deploy-release.sh` | Smoke-scripts worden op bestaan getest in plaats van op de execute-bit, die bij een Windows-kopie verloren gaat |
|
||||||
|
| `deploy/unraid/run-dockerman-container.sh` | Ruimt expliciet zowel `docker-compose.yml` (3 containers) als `docker-compose.unraid.yml` op, zodat poort 1202 gegarandeerd vrij is |
|
||||||
|
| `deploy/unraid/geointel.env.example` | `COMPOSE_FILE=docker-compose.unraid.yml`, zodat een kaal `docker compose up -d` nooit per ongeluk de 3-container dev-stack start |
|
||||||
|
| `scripts/configure_operator_login.sh` | Nieuw. Zet de operator-login in `.env` met een correcte pbkdf2_sha256-hash (600.000 iteraties, identiek aan `AuthService.hash_password`) en een vers sessiegeheim |
|
||||||
@@ -32,7 +32,58 @@ if ! [[ "$GEOINTEL_APP_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]
|
|||||||
echo "Invalid semantic version in VERSION: ${GEOINTEL_APP_VERSION}" >&2
|
echo "Invalid semantic version in VERSION: ${GEOINTEL_APP_VERSION}" >&2
|
||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
GEOINTEL_BUILD_SHA="$(git rev-parse HEAD)"
|
resolve_build_sha() {
|
||||||
|
# 1. Explicit override wins.
|
||||||
|
if [ -n "${GEOINTEL_BUILD_SHA:-}" ]; then
|
||||||
|
printf '%s' "$GEOINTEL_BUILD_SHA"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Real git checkout.
|
||||||
|
if command -v git >/dev/null 2>&1 && git rev-parse --git-dir >/dev/null 2>&1; then
|
||||||
|
git rev-parse HEAD
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Manually copied tree with a RELEASE_SHA marker file.
|
||||||
|
if [ -f RELEASE_SHA ]; then
|
||||||
|
tr -d '[:space:]' < RELEASE_SHA
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Manually copied tree without git: derive a stable content hash so
|
||||||
|
# an unchanged redeploy reuses the existing immutable image tag.
|
||||||
|
if command -v sha1sum >/dev/null 2>&1; then
|
||||||
|
local tree_hash
|
||||||
|
tree_hash="$(
|
||||||
|
find backend frontend deploy scripts fixtures VERSION \
|
||||||
|
-type f \
|
||||||
|
! -path '*/node_modules/*' \
|
||||||
|
! -path '*/__pycache__/*' \
|
||||||
|
! -path '*/.pytest_cache/*' \
|
||||||
|
! -name '*.pyc' \
|
||||||
|
-print0 2>/dev/null \
|
||||||
|
| sort -z \
|
||||||
|
| xargs -0 sha1sum 2>/dev/null \
|
||||||
|
| sha1sum \
|
||||||
|
| cut -c1-40
|
||||||
|
)" || tree_hash=""
|
||||||
|
if [ -n "$tree_hash" ]; then
|
||||||
|
printf '%s' "$tree_hash"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5. Last resort: unique per deploy.
|
||||||
|
printf 'manual%s' "$(date -u +%Y%m%d%H%M%S)"
|
||||||
|
}
|
||||||
|
|
||||||
|
GEOINTEL_BUILD_SHA="$(resolve_build_sha)"
|
||||||
|
if [ -z "$GEOINTEL_BUILD_SHA" ]; then
|
||||||
|
echo "Could not determine a build revision for this deployment." >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
echo "Build revision: ${GEOINTEL_BUILD_SHA}"
|
||||||
GEOINTEL_BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
GEOINTEL_BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
GEOINTEL_IMAGE_REPOSITORY="${GEOINTEL_IMAGE_REPOSITORY:-geointel-all-in-one}"
|
GEOINTEL_IMAGE_REPOSITORY="${GEOINTEL_IMAGE_REPOSITORY:-geointel-all-in-one}"
|
||||||
if [ "$GEOINTEL_INSTALL_AI" = "true" ]; then
|
if [ "$GEOINTEL_INSTALL_AI" = "true" ]; then
|
||||||
@@ -138,14 +189,14 @@ if ! start_image "$GEOINTEL_RELEASE_IMAGE"; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -x scripts/live_migration_smoke.sh ]; then
|
if [ -f scripts/live_migration_smoke.sh ]; then
|
||||||
if ! LIVE_SMOKE_CONTAINER=geointel bash scripts/live_migration_smoke.sh; then
|
if ! LIVE_SMOKE_CONTAINER=geointel bash scripts/live_migration_smoke.sh; then
|
||||||
rollback_previous || true
|
rollback_previous || true
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -x scripts/verify_browser_runtime.sh ]; then
|
if [ -f scripts/verify_browser_runtime.sh ]; then
|
||||||
if ! bash scripts/verify_browser_runtime.sh "$FRONTEND_URL"; then
|
if ! bash scripts/verify_browser_runtime.sh "$FRONTEND_URL"; then
|
||||||
rollback_previous || true
|
rollback_previous || true
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
# GeoIntel Unraid all-in-one environment template.
|
# GeoIntel Unraid all-in-one environment template.
|
||||||
# Copy this file to /mnt/user/appdata/geointel/.env and edit values there.
|
# Copy this file to /mnt/user/appdata/geointel/.env and edit values there.
|
||||||
|
|
||||||
|
# GeoIntel runs as ONE container. This makes a bare `docker compose ...` in this
|
||||||
|
# directory use the single-container file instead of the multi-container
|
||||||
|
# development stack in docker-compose.yml.
|
||||||
|
COMPOSE_FILE=docker-compose.unraid.yml
|
||||||
|
|
||||||
# Browser URL: http://<unraid-ip>:<GEOINTEL_FRONTEND_PORT>
|
# Browser URL: http://<unraid-ip>:<GEOINTEL_FRONTEND_PORT>
|
||||||
GEOINTEL_FRONTEND_PORT=1202
|
GEOINTEL_FRONTEND_PORT=1202
|
||||||
|
|
||||||
@@ -24,7 +29,8 @@ GEOINTEL_POSTGRES_USER=geointel
|
|||||||
GEOINTEL_POSTGRES_PASSWORD=change-me-before-shared-use
|
GEOINTEL_POSTGRES_PASSWORD=change-me-before-shared-use
|
||||||
|
|
||||||
# Browser origins allowed when directly calling the backend API.
|
# Browser origins allowed when directly calling the backend API.
|
||||||
GEOINTEL_CORS_ORIGINS=http://localhost:1202,http://127.0.0.1:1202,http://192.168.10.150:1202
|
# Neem hier ook de publieke hostname op zodra Nginx Proxy Manager ervoor staat.
|
||||||
|
GEOINTEL_CORS_ORIGINS=https://geointel.itworx.tech,http://geointel.itworx.tech,http://localhost:1202,http://127.0.0.1:1202,http://192.168.10.150:1202
|
||||||
|
|
||||||
# Upload guard in MiB. The same 1-2048 limit is applied by nginx and FastAPI.
|
# Upload guard in MiB. The same 1-2048 limit is applied by nginx and FastAPI.
|
||||||
GEOINTEL_MAX_UPLOAD_MB=500
|
GEOINTEL_MAX_UPLOAD_MB=500
|
||||||
@@ -43,6 +49,8 @@ GEOINTEL_AUTH_SESSION_TTL_SECONDS=43200
|
|||||||
# Guest access is enabled by default whenever operator authentication is active.
|
# Guest access is enabled by default whenever operator authentication is active.
|
||||||
# It opens the seeded GeoIntel demo in a temporary, API-enforced restricted
|
# It opens the seeded GeoIntel demo in a temporary, API-enforced restricted
|
||||||
# session. Set this to false on installations containing private project data.
|
# session. Set this to false on installations containing private project data.
|
||||||
|
# LET OP: scripts/configure_operator_login.sh zet dit op false tenzij je
|
||||||
|
# expliciet --guest-access true meegeeft.
|
||||||
GEOINTEL_GUEST_ACCESS_ENABLED=true
|
GEOINTEL_GUEST_ACCESS_ENABLED=true
|
||||||
GEOINTEL_GUEST_DISPLAY_NAME=Gast
|
GEOINTEL_GUEST_DISPLAY_NAME=Gast
|
||||||
GEOINTEL_GUEST_SESSION_TTL_SECONDS=7200
|
GEOINTEL_GUEST_SESSION_TTL_SECONDS=7200
|
||||||
|
|||||||
@@ -269,7 +269,14 @@ migrate_compose_volume_if_needed() {
|
|||||||
validate_runtime_config
|
validate_runtime_config
|
||||||
install_dockerman_metadata
|
install_dockerman_metadata
|
||||||
|
|
||||||
docker compose down --remove-orphans || true
|
# GeoIntel runs as ONE container started by docker run below.
|
||||||
|
# Tear down any leftover Compose stack first, including the multi-container
|
||||||
|
# development stack (db + backend + frontend) that also claims port 1202.
|
||||||
|
for compose_file in docker-compose.yml docker-compose.unraid.yml; do
|
||||||
|
if [ -f "$compose_file" ]; then
|
||||||
|
docker compose -f "$compose_file" down --remove-orphans || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
if docker ps -a --format '{{.Names}}' | grep -qx geointel; then
|
if docker ps -a --format '{{.Names}}' | grep -qx geointel; then
|
||||||
docker rm -f geointel >/dev/null 2>&1 || true
|
docker rm -f geointel >/dev/null 2>&1 || true
|
||||||
|
|||||||
@@ -5623,15 +5623,26 @@ section {
|
|||||||
padding: 0.78rem;
|
padding: 0.78rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Het icoon is optioneel. Een vast tweekolommenraster dwong een kop zonder
|
||||||
|
icoon in de 1.65rem-icoonkolom, waardoor de tekst per letter afbrak.
|
||||||
|
Flex laat de tekst altijd de resterende breedte nemen, met of zonder icoon. */
|
||||||
.geo-panel-heading {
|
.geo-panel-heading {
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-template-columns: 1.65rem minmax(0, 1fr);
|
|
||||||
gap: 0.55rem;
|
gap: 0.55rem;
|
||||||
align-items: start;
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.geo-panel-heading > * {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.geo-panel-heading > div {
|
||||||
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.geo-panel-heading > span {
|
.geo-panel-heading > span {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
flex: 0 0 auto;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
width: 1.65rem;
|
width: 1.65rem;
|
||||||
height: 1.65rem;
|
height: 1.65rem;
|
||||||
|
|||||||
@@ -0,0 +1,174 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Configure the GeoIntel operator login in .env.
|
||||||
|
#
|
||||||
|
# Writes GEOINTEL_AUTH_ENABLED, GEOINTEL_AUTH_USERNAME, a pbkdf2_sha256
|
||||||
|
# password hash and a fresh random session secret. The plaintext password is
|
||||||
|
# never written to disk, never printed and never passed as a command argument.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# bash scripts/configure_operator_login.sh --username jens@itworx.tech
|
||||||
|
# -> prompts for the password (nothing lands in shell history)
|
||||||
|
#
|
||||||
|
# GEOINTEL_OPERATOR_PASSWORD='...' bash scripts/configure_operator_login.sh \
|
||||||
|
# --username jens@itworx.tech --non-interactive
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# --username <value> Operator login name. Required.
|
||||||
|
# --env-file <path> Target env file. Default: .env
|
||||||
|
# --guest-access <bool> Keep the public guest demo. Default: false
|
||||||
|
# --non-interactive Read the password from GEOINTEL_OPERATOR_PASSWORD.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$ROOT"
|
||||||
|
|
||||||
|
USERNAME=""
|
||||||
|
ENV_FILE=".env"
|
||||||
|
GUEST_ACCESS="false"
|
||||||
|
NON_INTERACTIVE="false"
|
||||||
|
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--username) USERNAME="${2:-}"; shift 2 ;;
|
||||||
|
--env-file) ENV_FILE="${2:-}"; shift 2 ;;
|
||||||
|
--guest-access) GUEST_ACCESS="${2:-}"; shift 2 ;;
|
||||||
|
--non-interactive) NON_INTERACTIVE="true"; shift ;;
|
||||||
|
-h|--help) sed -n '2,22p' "$0"; exit 0 ;;
|
||||||
|
*) echo "Unknown argument: $1" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$USERNAME" ]; then
|
||||||
|
echo "--username is required." >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$GUEST_ACCESS" in
|
||||||
|
true|false) ;;
|
||||||
|
*) echo "--guest-access must be true or false." >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ ! -f "$ENV_FILE" ]; then
|
||||||
|
if [ -f deploy/unraid/geointel.env.example ]; then
|
||||||
|
echo "Creating ${ENV_FILE} from deploy/unraid/geointel.env.example."
|
||||||
|
cp deploy/unraid/geointel.env.example "$ENV_FILE"
|
||||||
|
else
|
||||||
|
echo "${ENV_FILE} does not exist and no template was found." >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$NON_INTERACTIVE" = "true" ]; then
|
||||||
|
if [ -z "${GEOINTEL_OPERATOR_PASSWORD:-}" ]; then
|
||||||
|
echo "GEOINTEL_OPERATOR_PASSWORD must be set for --non-interactive." >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
printf 'Password for %s: ' "$USERNAME" >&2
|
||||||
|
read -r -s GEOINTEL_OPERATOR_PASSWORD
|
||||||
|
printf '\n' >&2
|
||||||
|
printf 'Repeat password: ' >&2
|
||||||
|
read -r -s password_repeat
|
||||||
|
printf '\n' >&2
|
||||||
|
if [ "$GEOINTEL_OPERATOR_PASSWORD" != "$password_repeat" ]; then
|
||||||
|
echo "Passwords do not match." >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
unset password_repeat
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${#GEOINTEL_OPERATOR_PASSWORD}" -lt 12 ]; then
|
||||||
|
echo "Refusing to configure an operator password shorter than 12 characters." >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
export GEOINTEL_OPERATOR_PASSWORD
|
||||||
|
|
||||||
|
HASH_SCRIPT="$(mktemp)"
|
||||||
|
trap 'rm -f "$HASH_SCRIPT"' EXIT
|
||||||
|
cat >"$HASH_SCRIPT" <<'PY'
|
||||||
|
import base64
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
import secrets
|
||||||
|
|
||||||
|
# Must stay identical to AuthService.hash_password in
|
||||||
|
# backend/app/services/auth_service.py.
|
||||||
|
HASH_NAME = "pbkdf2_sha256"
|
||||||
|
ITERATIONS = 600_000
|
||||||
|
|
||||||
|
|
||||||
|
def b64(value: bytes) -> str:
|
||||||
|
return base64.urlsafe_b64encode(value).decode("ascii").rstrip("=")
|
||||||
|
|
||||||
|
|
||||||
|
password = os.environ["GEOINTEL_OPERATOR_PASSWORD"]
|
||||||
|
salt = secrets.token_bytes(18)
|
||||||
|
digest = hashlib.pbkdf2_hmac("sha256", password.encode("utf-8"), salt, ITERATIONS)
|
||||||
|
|
||||||
|
print("$".join((HASH_NAME, str(ITERATIONS), b64(salt), b64(digest))))
|
||||||
|
print(secrets.token_urlsafe(48))
|
||||||
|
PY
|
||||||
|
|
||||||
|
generated=""
|
||||||
|
if command -v python3 >/dev/null 2>&1; then
|
||||||
|
generated="$(python3 "$HASH_SCRIPT")"
|
||||||
|
elif command -v docker >/dev/null 2>&1; then
|
||||||
|
echo "No local python3 found; deriving the hash in a throwaway python container."
|
||||||
|
generated="$(
|
||||||
|
docker run --rm -i \
|
||||||
|
-e GEOINTEL_OPERATOR_PASSWORD \
|
||||||
|
python:3.11-slim python - <"$HASH_SCRIPT"
|
||||||
|
)"
|
||||||
|
else
|
||||||
|
echo "Need either python3 or docker to derive the password hash." >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
unset GEOINTEL_OPERATOR_PASSWORD
|
||||||
|
|
||||||
|
PASSWORD_HASH="$(printf '%s\n' "$generated" | sed -n '1p')"
|
||||||
|
SESSION_SECRET="$(printf '%s\n' "$generated" | sed -n '2p')"
|
||||||
|
|
||||||
|
case "$PASSWORD_HASH" in
|
||||||
|
pbkdf2_sha256\$*) ;;
|
||||||
|
*) echo "Password hash generation failed." >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
if [ "${#SESSION_SECRET}" -lt 32 ]; then
|
||||||
|
echo "Session secret generation failed." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
backup="${ENV_FILE}.bak.$(date -u +%Y%m%d%H%M%S)"
|
||||||
|
cp "$ENV_FILE" "$backup"
|
||||||
|
|
||||||
|
tmp_env="$(mktemp)"
|
||||||
|
grep -v -E '^(GEOINTEL_AUTH_ENABLED|GEOINTEL_AUTH_USERNAME|GEOINTEL_AUTH_PASSWORD_HASH|GEOINTEL_AUTH_SESSION_SECRET|GEOINTEL_GUEST_ACCESS_ENABLED)=' \
|
||||||
|
"$ENV_FILE" >"$tmp_env" || true
|
||||||
|
|
||||||
|
{
|
||||||
|
printf '\n'
|
||||||
|
printf '# Operator login, written by scripts/configure_operator_login.sh on %s.\n' \
|
||||||
|
"$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||||
|
# Single quotes zijn verplicht: de pbkdf2-hash bevat '$'-tekens. Ongequote
|
||||||
|
# zou Docker Compose ze als variabelen interpoleren en `source .env` onder
|
||||||
|
# `set -u` afbreken met "unbound variable".
|
||||||
|
printf 'GEOINTEL_AUTH_ENABLED=true\n'
|
||||||
|
printf "GEOINTEL_AUTH_USERNAME='%s'\n" "$USERNAME"
|
||||||
|
printf "GEOINTEL_AUTH_PASSWORD_HASH='%s'\n" "$PASSWORD_HASH"
|
||||||
|
printf "GEOINTEL_AUTH_SESSION_SECRET='%s'\n" "$SESSION_SECRET"
|
||||||
|
printf 'GEOINTEL_GUEST_ACCESS_ENABLED=%s\n' "$GUEST_ACCESS"
|
||||||
|
} >>"$tmp_env"
|
||||||
|
|
||||||
|
cat "$tmp_env" >"$ENV_FILE"
|
||||||
|
rm -f "$tmp_env"
|
||||||
|
chmod 600 "$ENV_FILE"
|
||||||
|
|
||||||
|
echo "Operator login configured in ${ENV_FILE}."
|
||||||
|
echo " username: ${USERNAME}"
|
||||||
|
echo " password hash: pbkdf2_sha256, 600000 iterations"
|
||||||
|
echo " session secret: regenerated (existing browser sessions are invalidated)"
|
||||||
|
echo " guest access: ${GUEST_ACCESS}"
|
||||||
|
echo " backup of the previous env file: ${backup}"
|
||||||
|
echo
|
||||||
|
echo "Apply with: bash deploy/unraid/deploy-release.sh"
|
||||||
Reference in New Issue
Block a user