Add Unraid all-in-one runtime
This commit is contained in:
@@ -0,0 +1,23 @@
|
|||||||
|
.git
|
||||||
|
.venv
|
||||||
|
venv
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
.pytest_cache
|
||||||
|
|
||||||
|
frontend/node_modules
|
||||||
|
frontend/dist
|
||||||
|
frontend/*.tsbuildinfo
|
||||||
|
backend/.pytest_cache
|
||||||
|
backend/**/*.pyc
|
||||||
|
backend/**/__pycache__
|
||||||
|
|
||||||
|
storage
|
||||||
|
postgres-data
|
||||||
|
datasets/raw
|
||||||
|
datasets/processed
|
||||||
|
datasets/cache
|
||||||
|
exports
|
||||||
|
models
|
||||||
|
|
||||||
|
.env
|
||||||
@@ -25,6 +25,7 @@ VITE_MAP_STYLE_URL=https://demotiles.maplibre.org/style.json
|
|||||||
GEOINTEL_FRONTEND_PORT=1202
|
GEOINTEL_FRONTEND_PORT=1202
|
||||||
GEOINTEL_BACKEND_PORT=8000
|
GEOINTEL_BACKEND_PORT=8000
|
||||||
GEOINTEL_STORAGE_PATH=./storage
|
GEOINTEL_STORAGE_PATH=./storage
|
||||||
|
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
|
||||||
|
|||||||
@@ -7,6 +7,16 @@
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Sprint 32 Unraid all-in-one runtime (2026-06-17)
|
||||||
|
|
||||||
|
- Added `docker-compose.unraid.yml` for a single `geointel` container on Unraid.
|
||||||
|
- Added `deploy/unraid/Dockerfile.all-in-one`, embedding PostGIS, FastAPI, nginx and the built React frontend in one image.
|
||||||
|
- Added `deploy/unraid/all-in-one-start.sh` to start embedded PostGIS, apply Alembic migrations, start the backend and serve nginx.
|
||||||
|
- Added `deploy/unraid/nginx-all-in-one.conf` with localhost backend proxying inside the same container.
|
||||||
|
- Updated Tower deploy scripts to stop the old multi-container stack without removing volumes and start the all-in-one stack.
|
||||||
|
- Updated the Unraid template so the Docker can be edited from Unraid with one web port, storage path, PostGIS data path and app icon.
|
||||||
|
- No API contracts, migrations, product features, provider fetching or AI behavior were introduced.
|
||||||
|
|
||||||
## Sprint 31 Unraid deployment template (2026-06-17)
|
## Sprint 31 Unraid deployment template (2026-06-17)
|
||||||
|
|
||||||
- Made Docker Compose ports, storage path, PostGIS credentials, CORS origins and upload limit configurable through `.env` defaults.
|
- Made Docker Compose ports, storage path, PostGIS credentials, CORS origins and upload limit configurable through `.env` defaults.
|
||||||
|
|||||||
@@ -69,32 +69,33 @@ make readiness
|
|||||||
|
|
||||||
## Unraid / Tower deployment
|
## Unraid / Tower deployment
|
||||||
|
|
||||||
GeoIntel runs on Unraid as a Docker Compose stack with PostGIS, backend and frontend services. The frontend is the browser entrypoint and proxies `/api` and `/health` to the backend.
|
GeoIntel runs on Unraid as an all-in-one Docker container. The container embeds PostGIS, runs the FastAPI backend internally, and serves the frontend through nginx on one editable web port.
|
||||||
|
|
||||||
Unraid template assets live in:
|
Unraid template assets live in:
|
||||||
|
|
||||||
- `deploy/unraid/geointel.env.example`
|
- `deploy/unraid/geointel.env.example`
|
||||||
- `deploy/unraid/geointel-unraid-template.xml`
|
- `deploy/unraid/geointel-unraid-template.xml`
|
||||||
- `deploy/unraid/geointel-icon.svg`
|
- `deploy/unraid/geointel-icon.svg`
|
||||||
|
- `docker-compose.unraid.yml`
|
||||||
|
|
||||||
Copy the Unraid env template to `.env` in the checkout and edit ports there:
|
Copy the Unraid env template to `.env` in the checkout and edit ports/paths there:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /mnt/user/appdata/geointel
|
cd /mnt/user/appdata/geointel
|
||||||
cp deploy/unraid/geointel.env.example .env
|
cp deploy/unraid/geointel.env.example .env
|
||||||
nano .env
|
nano .env
|
||||||
docker compose up -d --build
|
docker compose -f docker-compose.unraid.yml up -d --build
|
||||||
```
|
```
|
||||||
|
|
||||||
Common editable values:
|
Common editable values:
|
||||||
|
|
||||||
```env
|
```env
|
||||||
GEOINTEL_FRONTEND_PORT=1202
|
GEOINTEL_FRONTEND_PORT=1202
|
||||||
GEOINTEL_BACKEND_PORT=8000
|
|
||||||
GEOINTEL_STORAGE_PATH=/mnt/user/appdata/geointel/storage
|
GEOINTEL_STORAGE_PATH=/mnt/user/appdata/geointel/storage
|
||||||
|
GEOINTEL_POSTGIS_DATA_PATH=/mnt/user/appdata/geointel/postgres-data
|
||||||
```
|
```
|
||||||
|
|
||||||
The database port is intentionally not exposed to the LAN. See `deploy/unraid/README.md` for full setup, port-change and cleanup notes.
|
The backend and PostGIS ports are intentionally not exposed to the LAN in the all-in-one runtime. See `deploy/unraid/README.md` for full setup, port-change and cleanup notes.
|
||||||
|
|
||||||
## Sprint 2 quick start
|
## Sprint 2 quick start
|
||||||
|
|
||||||
|
|||||||
@@ -10,24 +10,25 @@ def test_unraid_template_documents_editable_runtime_settings() -> None:
|
|||||||
template = (ROOT / "deploy" / "unraid" / "geointel-unraid-template.xml").read_text(encoding="utf-8")
|
template = (ROOT / "deploy" / "unraid" / "geointel-unraid-template.xml").read_text(encoding="utf-8")
|
||||||
|
|
||||||
assert "<Name>GeoIntel Kempen</Name>" in template
|
assert "<Name>GeoIntel Kempen</Name>" in template
|
||||||
assert "GeoIntel is a multi-container Docker Compose stack" in template
|
assert "GeoIntel all-in-one runs the complete GeoIntel Kempen V1 stack in one Docker container" in template
|
||||||
assert "<WebUI>http://[IP]:[PORT:1202]/</WebUI>" in template
|
assert "<Repository>geointel-all-in-one:latest</Repository>" in template
|
||||||
assert "<Icon>http://[IP]:[PORT:1202]/geointel-icon.svg</Icon>" in template
|
assert "<WebUI>http://[IP]:[PORT:80]/</WebUI>" in template
|
||||||
assert 'Target="GEOINTEL_FRONTEND_PORT"' in template
|
assert "<Icon>http://[IP]:[PORT:80]/geointel-icon.svg</Icon>" in template
|
||||||
assert 'Target="GEOINTEL_BACKEND_PORT"' in template
|
assert 'Target="80"' in template
|
||||||
assert 'Target="GEOINTEL_STORAGE_PATH"' in template
|
assert 'Target="/app/storage"' in template
|
||||||
|
assert 'Target="/var/lib/postgresql/data"' in template
|
||||||
assert 'Target="GEOINTEL_POSTGRES_PASSWORD"' in template
|
assert 'Target="GEOINTEL_POSTGRES_PASSWORD"' in template
|
||||||
assert 'Mask="true">change-me-before-shared-use</Config>' in template
|
assert 'Mask="true">change-me-before-shared-use</Config>' in template
|
||||||
|
|
||||||
|
|
||||||
def test_unraid_env_template_matches_compose_variables() -> None:
|
def test_unraid_env_template_matches_single_container_compose_variables() -> None:
|
||||||
compose = (ROOT / "docker-compose.yml").read_text(encoding="utf-8")
|
compose = (ROOT / "docker-compose.unraid.yml").read_text(encoding="utf-8")
|
||||||
env_template = (ROOT / "deploy" / "unraid" / "geointel.env.example").read_text(encoding="utf-8")
|
env_template = (ROOT / "deploy" / "unraid" / "geointel.env.example").read_text(encoding="utf-8")
|
||||||
|
|
||||||
for key in (
|
for key in (
|
||||||
"GEOINTEL_FRONTEND_PORT",
|
"GEOINTEL_FRONTEND_PORT",
|
||||||
"GEOINTEL_BACKEND_PORT",
|
|
||||||
"GEOINTEL_STORAGE_PATH",
|
"GEOINTEL_STORAGE_PATH",
|
||||||
|
"GEOINTEL_POSTGIS_DATA_PATH",
|
||||||
"GEOINTEL_POSTGRES_DB",
|
"GEOINTEL_POSTGRES_DB",
|
||||||
"GEOINTEL_POSTGRES_USER",
|
"GEOINTEL_POSTGRES_USER",
|
||||||
"GEOINTEL_POSTGRES_PASSWORD",
|
"GEOINTEL_POSTGRES_PASSWORD",
|
||||||
@@ -39,6 +40,14 @@ def test_unraid_env_template_matches_compose_variables() -> None:
|
|||||||
|
|
||||||
assert "GEOINTEL_FRONTEND_PORT=1202" in env_template
|
assert "GEOINTEL_FRONTEND_PORT=1202" in env_template
|
||||||
assert "GEOINTEL_STORAGE_PATH=/mnt/user/appdata/geointel/storage" in env_template
|
assert "GEOINTEL_STORAGE_PATH=/mnt/user/appdata/geointel/storage" in env_template
|
||||||
|
assert "GEOINTEL_POSTGIS_DATA_PATH=/mnt/user/appdata/geointel/postgres-data" in env_template
|
||||||
|
assert "${GEOINTEL_POSTGIS_DATA_PATH:-geointel_postgis}:/var/lib/postgresql/data" in compose
|
||||||
|
assert "geointel_postgis:" in compose
|
||||||
|
assert "geointel:" in compose
|
||||||
|
assert "db:" not in compose
|
||||||
|
assert "backend:" not in compose
|
||||||
|
assert "frontend:" not in compose
|
||||||
|
assert '"${GEOINTEL_FRONTEND_PORT:-1202}:80"' in compose
|
||||||
|
|
||||||
|
|
||||||
def test_unraid_readme_explains_port_changes_and_safe_cleanup() -> None:
|
def test_unraid_readme_explains_port_changes_and_safe_cleanup() -> None:
|
||||||
@@ -46,12 +55,44 @@ def test_unraid_readme_explains_port_changes_and_safe_cleanup() -> None:
|
|||||||
|
|
||||||
assert "cp deploy/unraid/geointel.env.example .env" in readme
|
assert "cp deploy/unraid/geointel.env.example .env" in readme
|
||||||
assert "GEOINTEL_FRONTEND_PORT=1203" in readme
|
assert "GEOINTEL_FRONTEND_PORT=1203" in readme
|
||||||
assert "docker compose up -d --build" in readme
|
assert "docker compose -f docker-compose.unraid.yml up -d --build" in readme
|
||||||
assert "bash scripts/live_migration_smoke.sh" in readme
|
assert "curl -fsS" in readme
|
||||||
assert "docker builder prune -af" in readme
|
assert "docker builder prune -af" in readme
|
||||||
assert "Avoid broad volume pruning" in readme
|
assert "Avoid broad volume pruning" in readme
|
||||||
|
|
||||||
|
|
||||||
|
def test_unraid_all_in_one_runtime_starts_embedded_postgis_backend_and_nginx() -> None:
|
||||||
|
dockerfile = (ROOT / "deploy" / "unraid" / "Dockerfile.all-in-one").read_text(encoding="utf-8")
|
||||||
|
start_script = (ROOT / "deploy" / "unraid" / "all-in-one-start.sh").read_text(encoding="utf-8")
|
||||||
|
nginx_config = (ROOT / "deploy" / "unraid" / "nginx-all-in-one.conf").read_text(encoding="utf-8")
|
||||||
|
dockerignore = (ROOT / ".dockerignore").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert "FROM postgis/postgis:16-3.4 AS runtime" in dockerfile
|
||||||
|
assert "COPY --from=frontend-build /frontend/dist/ /usr/share/nginx/html/" in dockerfile
|
||||||
|
assert 'CMD ["/usr/local/bin/geointel-all-in-one-start"]' in dockerfile
|
||||||
|
assert "/usr/local/bin/docker-entrypoint.sh postgres &" in start_script
|
||||||
|
assert "python -m alembic upgrade head" in start_script
|
||||||
|
assert "uvicorn app.main:app --host 127.0.0.1 --port 8000 &" in start_script
|
||||||
|
assert 'exec nginx -g "daemon off;"' in start_script
|
||||||
|
assert "proxy_pass http://127.0.0.1:8000/api/" in nginx_config
|
||||||
|
assert "proxy_pass http://127.0.0.1:8000/health" in nginx_config
|
||||||
|
assert "frontend/node_modules" in dockerignore
|
||||||
|
assert "storage" in dockerignore
|
||||||
|
assert "postgres-data" in dockerignore
|
||||||
|
|
||||||
|
|
||||||
|
def test_tower_deploy_uses_single_container_unraid_compose() -> None:
|
||||||
|
powershell = (ROOT / "scripts" / "deploy_tower.ps1").read_text(encoding="utf-8")
|
||||||
|
bash = (ROOT / "scripts" / "deploy_tower.sh").read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
for script in (powershell, bash):
|
||||||
|
assert "docker compose down --remove-orphans || true" in script
|
||||||
|
assert "docker compose -f docker-compose.unraid.yml config" in script
|
||||||
|
assert "docker compose -f docker-compose.unraid.yml build geointel" in script
|
||||||
|
assert "docker compose -f docker-compose.unraid.yml up -d" in script
|
||||||
|
assert "COMPOSE_FILE=docker-compose.unraid.yml bash scripts/live_migration_smoke.sh" in script
|
||||||
|
|
||||||
|
|
||||||
def test_frontend_and_unraid_icon_assets_are_present() -> None:
|
def test_frontend_and_unraid_icon_assets_are_present() -> None:
|
||||||
deploy_icon = (ROOT / "deploy" / "unraid" / "geointel-icon.svg").read_text(encoding="utf-8")
|
deploy_icon = (ROOT / "deploy" / "unraid" / "geointel-icon.svg").read_text(encoding="utf-8")
|
||||||
frontend_icon = (ROOT / "frontend" / "public" / "geointel-icon.svg").read_text(encoding="utf-8")
|
frontend_icon = (ROOT / "frontend" / "public" / "geointel-icon.svg").read_text(encoding="utf-8")
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
FROM node:20-alpine AS frontend-build
|
||||||
|
|
||||||
|
WORKDIR /frontend
|
||||||
|
COPY frontend/package.json frontend/package-lock.json* ./
|
||||||
|
RUN npm install
|
||||||
|
COPY frontend/ ./
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM postgis/postgis:16-3.4 AS runtime
|
||||||
|
|
||||||
|
ENV GEOINTEL_ENV=production \
|
||||||
|
GEOINTEL_API_PREFIX=/api/v1 \
|
||||||
|
GEOINTEL_STORAGE_ROOT=/app/storage \
|
||||||
|
STORAGE_ROOT=/app/storage \
|
||||||
|
GEOINTEL_POSTGRES_DB=geointel \
|
||||||
|
GEOINTEL_POSTGRES_USER=geointel \
|
||||||
|
GEOINTEL_POSTGRES_PASSWORD=geointel \
|
||||||
|
GEOINTEL_ALL_IN_ONE=1 \
|
||||||
|
PATH="/opt/geointel/venv/bin:${PATH}"
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential \
|
||||||
|
curl \
|
||||||
|
gdal-bin \
|
||||||
|
libgdal-dev \
|
||||||
|
libgeos-dev \
|
||||||
|
libpq-dev \
|
||||||
|
libproj-dev \
|
||||||
|
nginx \
|
||||||
|
proj-bin \
|
||||||
|
python3 \
|
||||||
|
python3-pip \
|
||||||
|
python3-venv \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY backend/ /app/
|
||||||
|
COPY fixtures/ /app/fixtures/
|
||||||
|
COPY deploy/unraid/nginx-all-in-one.conf /etc/nginx/conf.d/default.conf
|
||||||
|
COPY deploy/unraid/all-in-one-start.sh /usr/local/bin/geointel-all-in-one-start
|
||||||
|
COPY --from=frontend-build /frontend/dist/ /usr/share/nginx/html/
|
||||||
|
|
||||||
|
RUN python3 -m venv /opt/geointel/venv \
|
||||||
|
&& pip install --no-cache-dir --upgrade pip setuptools \
|
||||||
|
&& pip install --no-cache-dir ".[gis]" \
|
||||||
|
&& python scripts/gis_import_smoke.py \
|
||||||
|
&& chmod +x /usr/local/bin/geointel-all-in-one-start \
|
||||||
|
&& mkdir -p /app/storage /run/nginx /var/log/nginx
|
||||||
|
|
||||||
|
VOLUME ["/var/lib/postgresql/data", "/app/storage"]
|
||||||
|
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=5s --retries=10 --start-period=60s \
|
||||||
|
CMD curl -fsS http://127.0.0.1/health >/dev/null || exit 1
|
||||||
|
|
||||||
|
CMD ["/usr/local/bin/geointel-all-in-one-start"]
|
||||||
+48
-50
@@ -1,30 +1,38 @@
|
|||||||
# GeoIntel Unraid template
|
# GeoIntel Unraid all-in-one container
|
||||||
|
|
||||||
GeoIntel is a multi-container Docker Compose stack:
|
GeoIntel can run on Unraid as one Docker container.
|
||||||
|
|
||||||
- `db`: PostGIS 16 / PostGIS 3.4
|
Inside that single container:
|
||||||
- `backend`: FastAPI/GIS runtime
|
|
||||||
- `frontend`: nginx-served React app with `/api` and `/health` proxying to the backend
|
|
||||||
|
|
||||||
The normal browser entrypoint is:
|
- embedded PostGIS stores the application database
|
||||||
|
- Alembic migrations run at startup
|
||||||
|
- FastAPI runs on internal `127.0.0.1:8000`
|
||||||
|
- nginx serves the React/MapLibre frontend on container port `80`
|
||||||
|
- nginx proxies `/api` and `/health` to the internal backend
|
||||||
|
|
||||||
|
The browser entrypoint is:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
http://<unraid-ip>:${GEOINTEL_FRONTEND_PORT}
|
http://<unraid-ip>:${GEOINTEL_FRONTEND_PORT}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Files
|
The app icon is served from the same container:
|
||||||
|
|
||||||
- `geointel.env.example`: copy this to the repository checkout as `.env`.
|
|
||||||
- `geointel-unraid-template.xml`: Unraid/DockerMan-style metadata for the editable settings.
|
|
||||||
- `geointel-icon.svg`: icon source for template/app metadata.
|
|
||||||
|
|
||||||
The frontend also serves the same icon at:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
http://<unraid-ip>:${GEOINTEL_FRONTEND_PORT}/geointel-icon.svg
|
http://<unraid-ip>:${GEOINTEL_FRONTEND_PORT}/geointel-icon.svg
|
||||||
```
|
```
|
||||||
|
|
||||||
## First setup on Unraid
|
## Files
|
||||||
|
|
||||||
|
- `docker-compose.unraid.yml`: recommended single-container Compose stack.
|
||||||
|
- `deploy/unraid/Dockerfile.all-in-one`: builds the single container.
|
||||||
|
- `deploy/unraid/all-in-one-start.sh`: starts embedded PostGIS, backend and nginx.
|
||||||
|
- `deploy/unraid/nginx-all-in-one.conf`: frontend and API proxy config for one container.
|
||||||
|
- `deploy/unraid/geointel.env.example`: copy to `.env` and edit ports/paths.
|
||||||
|
- `deploy/unraid/geointel-unraid-template.xml`: Unraid/DockerMan metadata for editable fields.
|
||||||
|
- `deploy/unraid/geointel-icon.svg`: icon source.
|
||||||
|
|
||||||
|
## First setup with Compose Manager
|
||||||
|
|
||||||
From the Unraid shell:
|
From the Unraid shell:
|
||||||
|
|
||||||
@@ -33,59 +41,46 @@ cd /mnt/user/appdata
|
|||||||
git clone gitea-widefrog:NuklearRabbit/geointel.git geointel
|
git clone gitea-widefrog:NuklearRabbit/geointel.git geointel
|
||||||
cd /mnt/user/appdata/geointel
|
cd /mnt/user/appdata/geointel
|
||||||
cp deploy/unraid/geointel.env.example .env
|
cp deploy/unraid/geointel.env.example .env
|
||||||
```
|
|
||||||
|
|
||||||
Edit `.env` before starting the stack:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nano .env
|
nano .env
|
||||||
```
|
docker compose -f docker-compose.unraid.yml config
|
||||||
|
docker compose -f docker-compose.unraid.yml up -d --build
|
||||||
Common values:
|
|
||||||
|
|
||||||
```env
|
|
||||||
GEOINTEL_FRONTEND_PORT=1202
|
|
||||||
GEOINTEL_BACKEND_PORT=8000
|
|
||||||
GEOINTEL_STORAGE_PATH=/mnt/user/appdata/geointel/storage
|
|
||||||
GEOINTEL_POSTGRES_PASSWORD=change-me-before-shared-use
|
|
||||||
GEOINTEL_CORS_ORIGINS=http://localhost:1202,http://127.0.0.1:1202,http://192.168.10.150:1202
|
|
||||||
```
|
|
||||||
|
|
||||||
Start or rebuild:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker compose config
|
|
||||||
docker compose up -d --build
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Validate:
|
Validate:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bash scripts/live_migration_smoke.sh
|
curl -fsS "http://192.168.10.150:${GEOINTEL_FRONTEND_PORT:-1202}/health"
|
||||||
bash scripts/verify_browser_runtime.sh "http://192.168.10.150:${GEOINTEL_FRONTEND_PORT:-1202}"
|
curl -fsS "http://192.168.10.150:${GEOINTEL_FRONTEND_PORT:-1202}/api/v1/projects"
|
||||||
|
curl -I "http://192.168.10.150:${GEOINTEL_FRONTEND_PORT:-1202}/geointel-icon.svg"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Change ports
|
## Change the browser port
|
||||||
|
|
||||||
Change the web UI port:
|
Edit `.env`:
|
||||||
|
|
||||||
```env
|
```env
|
||||||
GEOINTEL_FRONTEND_PORT=1203
|
GEOINTEL_FRONTEND_PORT=1203
|
||||||
```
|
|
||||||
|
|
||||||
If you expose a different web port, also update CORS origins:
|
|
||||||
|
|
||||||
```env
|
|
||||||
GEOINTEL_CORS_ORIGINS=http://localhost:1203,http://127.0.0.1:1203,http://192.168.10.150:1203
|
GEOINTEL_CORS_ORIGINS=http://localhost:1203,http://127.0.0.1:1203,http://192.168.10.150:1203
|
||||||
```
|
```
|
||||||
|
|
||||||
Apply:
|
Apply:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose up -d --build
|
docker compose -f docker-compose.unraid.yml up -d --build
|
||||||
```
|
```
|
||||||
|
|
||||||
The database port is intentionally not published to the LAN. The backend reaches it through the internal Compose service name `db`.
|
## Persistent paths
|
||||||
|
|
||||||
|
Recommended Unraid paths:
|
||||||
|
|
||||||
|
```env
|
||||||
|
GEOINTEL_STORAGE_PATH=/mnt/user/appdata/geointel/storage
|
||||||
|
GEOINTEL_POSTGIS_DATA_PATH=/mnt/user/appdata/geointel/postgres-data
|
||||||
|
```
|
||||||
|
|
||||||
|
`GEOINTEL_STORAGE_PATH` contains uploads, tiles, masks, reports and exports.
|
||||||
|
|
||||||
|
`GEOINTEL_POSTGIS_DATA_PATH` contains the embedded PostGIS database files.
|
||||||
|
|
||||||
## Update from Gitea
|
## Update from Gitea
|
||||||
|
|
||||||
@@ -93,11 +88,10 @@ The database port is intentionally not published to the LAN. The backend reaches
|
|||||||
cd /mnt/user/appdata/geointel
|
cd /mnt/user/appdata/geointel
|
||||||
git fetch origin main
|
git fetch origin main
|
||||||
git reset --hard origin/main
|
git reset --hard origin/main
|
||||||
docker compose up -d --build
|
docker compose -f docker-compose.unraid.yml up -d --build
|
||||||
bash scripts/live_migration_smoke.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Cleanup notes
|
## Safe cleanup
|
||||||
|
|
||||||
Safe cache cleanup if Docker build cache fills the Unraid Docker image:
|
Safe cache cleanup if Docker build cache fills the Unraid Docker image:
|
||||||
|
|
||||||
@@ -106,3 +100,7 @@ docker builder prune -af
|
|||||||
```
|
```
|
||||||
|
|
||||||
Avoid broad volume pruning unless you explicitly intend to remove persisted PostGIS data or GeoIntel artifacts.
|
Avoid broad volume pruning unless you explicitly intend to remove persisted PostGIS data or GeoIntel artifacts.
|
||||||
|
|
||||||
|
## Multi-container development stack
|
||||||
|
|
||||||
|
The root `docker-compose.yml` remains available for development and CI-like validation with separate `db`, `backend` and `frontend` services. For Unraid app-style operation, prefer `docker-compose.unraid.yml`.
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
export POSTGRES_DB="${GEOINTEL_POSTGRES_DB:-${POSTGRES_DB:-geointel}}"
|
||||||
|
export POSTGRES_USER="${GEOINTEL_POSTGRES_USER:-${POSTGRES_USER:-geointel}}"
|
||||||
|
export POSTGRES_PASSWORD="${GEOINTEL_POSTGRES_PASSWORD:-${POSTGRES_PASSWORD:-geointel}}"
|
||||||
|
export PGDATA="${PGDATA:-/var/lib/postgresql/data}"
|
||||||
|
export STORAGE_ROOT="${STORAGE_ROOT:-${GEOINTEL_STORAGE_ROOT:-/app/storage}}"
|
||||||
|
export DATABASE_URL="${DATABASE_URL:-postgresql+psycopg://${POSTGRES_USER}:${POSTGRES_PASSWORD}@127.0.0.1:5432/${POSTGRES_DB}}"
|
||||||
|
export CORS_ORIGINS="${GEOINTEL_CORS_ORIGINS:-${CORS_ORIGINS:-http://localhost:1202,http://127.0.0.1:1202}}"
|
||||||
|
export MAX_UPLOAD_MB="${GEOINTEL_MAX_UPLOAD_MB:-${MAX_UPLOAD_MB:-500}}"
|
||||||
|
|
||||||
|
mkdir -p "$PGDATA" "$STORAGE_ROOT" /run/nginx /var/log/nginx
|
||||||
|
chown -R postgres:postgres "$PGDATA"
|
||||||
|
|
||||||
|
postgres_pid=""
|
||||||
|
backend_pid=""
|
||||||
|
|
||||||
|
shutdown() {
|
||||||
|
if [ -n "$backend_pid" ] && kill -0 "$backend_pid" 2>/dev/null; then
|
||||||
|
kill "$backend_pid" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
if [ -n "$postgres_pid" ] && kill -0 "$postgres_pid" 2>/dev/null; then
|
||||||
|
kill "$postgres_pid" 2>/dev/null || true
|
||||||
|
wait "$postgres_pid" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
trap shutdown INT TERM EXIT
|
||||||
|
|
||||||
|
echo "Starting embedded PostGIS..."
|
||||||
|
/usr/local/bin/docker-entrypoint.sh postgres &
|
||||||
|
postgres_pid="$!"
|
||||||
|
|
||||||
|
echo "Waiting for embedded PostGIS..."
|
||||||
|
for attempt in $(seq 1 60); do
|
||||||
|
if pg_isready -h 127.0.0.1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" >/dev/null 2>&1; then
|
||||||
|
echo "PostGIS is ready after attempt ${attempt}."
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if ! kill -0 "$postgres_pid" 2>/dev/null; then
|
||||||
|
echo "PostGIS process exited before becoming ready."
|
||||||
|
wait "$postgres_pid"
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
if ! pg_isready -h 127.0.0.1 -U "$POSTGRES_USER" -d "$POSTGRES_DB" >/dev/null 2>&1; then
|
||||||
|
echo "PostGIS did not become ready."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Applying Alembic migrations..."
|
||||||
|
python -m alembic upgrade head
|
||||||
|
|
||||||
|
echo "Starting GeoIntel backend..."
|
||||||
|
uvicorn app.main:app --host 127.0.0.1 --port 8000 &
|
||||||
|
backend_pid="$!"
|
||||||
|
|
||||||
|
echo "Waiting for GeoIntel backend..."
|
||||||
|
python - <<'PY'
|
||||||
|
import time
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
last_error = None
|
||||||
|
for attempt in range(1, 61):
|
||||||
|
try:
|
||||||
|
urllib.request.urlopen("http://127.0.0.1:8000/health", timeout=3).read()
|
||||||
|
print(f"Backend is ready after attempt {attempt}.")
|
||||||
|
break
|
||||||
|
except Exception as exc:
|
||||||
|
last_error = exc
|
||||||
|
print(f"Backend not ready yet ({attempt}/60): {exc}")
|
||||||
|
time.sleep(1)
|
||||||
|
else:
|
||||||
|
raise SystemExit(f"Backend did not become ready: {last_error}")
|
||||||
|
PY
|
||||||
|
|
||||||
|
echo "Starting nginx frontend on container port 80..."
|
||||||
|
exec nginx -g "daemon off;"
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Container version="2">
|
<Container version="2">
|
||||||
<Name>GeoIntel Kempen</Name>
|
<Name>GeoIntel Kempen</Name>
|
||||||
<Repository>Local Docker Compose stack</Repository>
|
<Repository>geointel-all-in-one:latest</Repository>
|
||||||
<Registry>gitea-widefrog:NuklearRabbit/geointel.git</Registry>
|
<Registry>gitea-widefrog:NuklearRabbit/geointel.git</Registry>
|
||||||
<Network>bridge</Network>
|
<Network>bridge</Network>
|
||||||
<Shell>sh</Shell>
|
<Shell>bash</Shell>
|
||||||
<Privileged>false</Privileged>
|
<Privileged>false</Privileged>
|
||||||
<Support>http://192.168.10.150:1202</Support>
|
<Support>http://192.168.10.150:1202</Support>
|
||||||
<Project>GeoIntel Kempen</Project>
|
<Project>GeoIntel Kempen</Project>
|
||||||
<Overview>GeoIntel is a multi-container Docker Compose stack for the GeoIntel Kempen GeoAI Workbench: PostGIS, FastAPI and React/MapLibre. Use this template together with deploy/unraid/geointel.env.example and docker-compose.yml; it documents the editable Unraid settings for the Compose stack.</Overview>
|
<Overview>GeoIntel all-in-one runs the complete GeoIntel Kempen V1 stack in one Docker container: embedded PostGIS, FastAPI backend, nginx frontend and MapLibre UI. Use docker-compose.unraid.yml or this template so the web port, storage path and database path can be edited from Unraid.</Overview>
|
||||||
<Category>Productivity: Tools: GIS:</Category>
|
<Category>Productivity: Tools: GIS:</Category>
|
||||||
<WebUI>http://[IP]:[PORT:1202]/</WebUI>
|
<WebUI>http://[IP]:[PORT:80]/</WebUI>
|
||||||
<TemplateURL>deploy/unraid/geointel-unraid-template.xml</TemplateURL>
|
<TemplateURL>deploy/unraid/geointel-unraid-template.xml</TemplateURL>
|
||||||
<Icon>http://[IP]:[PORT:1202]/geointel-icon.svg</Icon>
|
<Icon>http://[IP]:[PORT:80]/geointel-icon.svg</Icon>
|
||||||
<ExtraParams/>
|
<ExtraParams/>
|
||||||
<PostArgs/>
|
<PostArgs/>
|
||||||
<CPUset/>
|
<CPUset/>
|
||||||
@@ -20,14 +20,14 @@
|
|||||||
<DonateText/>
|
<DonateText/>
|
||||||
<DonateLink/>
|
<DonateLink/>
|
||||||
<Description>
|
<Description>
|
||||||
GeoIntel Kempen runs as a Docker Compose stack with services db, backend and frontend. Edit the matching .env variables to change ports and paths, then run docker compose up -d --build from /mnt/user/appdata/geointel.
|
Single-container GeoIntel runtime for Unraid. The container starts embedded PostGIS, runs Alembic migrations, starts the FastAPI backend on localhost and serves the frontend through nginx on container port 80.
|
||||||
</Description>
|
</Description>
|
||||||
<Config Name="Web UI Port" Target="GEOINTEL_FRONTEND_PORT" Default="1202" Mode="" Description="Host port mapped to the frontend nginx container port 80." Type="Variable" Display="always" Required="true" Mask="false">1202</Config>
|
<Config Name="Web UI Port" Target="80" Default="1202" Mode="tcp" Description="Host port mapped to the GeoIntel all-in-one web UI. Change this to edit the browser port." Type="Port" Display="always" Required="true" Mask="false">1202</Config>
|
||||||
<Config Name="Backend API Port" Target="GEOINTEL_BACKEND_PORT" Default="8000" Mode="" Description="Optional host port mapped to backend container port 8000. Normal browser traffic uses the frontend /api proxy." Type="Variable" Display="advanced" Required="true" Mask="false">8000</Config>
|
<Config Name="Storage Path" Target="/app/storage" Default="/mnt/user/appdata/geointel/storage" Mode="rw" Description="Persistent GeoIntel artifact storage for uploads, tiles, masks, reports and exports." Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/geointel/storage</Config>
|
||||||
<Config Name="Storage Path" Target="GEOINTEL_STORAGE_PATH" Default="/mnt/user/appdata/geointel/storage" Mode="rw" Description="Persistent GeoIntel artifact storage for uploads, tiles, masks, reports and exports." Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/geointel/storage</Config>
|
<Config Name="PostGIS Data Path" Target="/var/lib/postgresql/data" Default="/mnt/user/appdata/geointel/postgres-data" Mode="rw" Description="Persistent embedded PostGIS data directory for the all-in-one container." Type="Path" Display="always" Required="true" Mask="false">/mnt/user/appdata/geointel/postgres-data</Config>
|
||||||
<Config Name="Postgres Database" Target="GEOINTEL_POSTGRES_DB" Default="geointel" Mode="" Description="Internal PostGIS database name." Type="Variable" Display="advanced" Required="true" Mask="false">geointel</Config>
|
<Config Name="Postgres Database" Target="GEOINTEL_POSTGRES_DB" Default="geointel" Mode="" Description="Embedded PostGIS database name." Type="Variable" Display="advanced" Required="true" Mask="false">geointel</Config>
|
||||||
<Config Name="Postgres User" Target="GEOINTEL_POSTGRES_USER" Default="geointel" Mode="" Description="Internal PostGIS database user." Type="Variable" Display="advanced" Required="true" Mask="false">geointel</Config>
|
<Config Name="Postgres User" Target="GEOINTEL_POSTGRES_USER" Default="geointel" Mode="" Description="Embedded PostGIS database user." Type="Variable" Display="advanced" Required="true" Mask="false">geointel</Config>
|
||||||
<Config Name="Postgres Password" Target="GEOINTEL_POSTGRES_PASSWORD" Default="change-me-before-shared-use" Mode="" Description="Internal PostGIS database password. Change before shared use." Type="Variable" Display="advanced" Required="true" Mask="true">change-me-before-shared-use</Config>
|
<Config Name="Postgres Password" Target="GEOINTEL_POSTGRES_PASSWORD" Default="change-me-before-shared-use" Mode="" Description="Embedded PostGIS database password. Change before shared use." Type="Variable" Display="advanced" Required="true" Mask="true">change-me-before-shared-use</Config>
|
||||||
<Config Name="CORS Origins" Target="GEOINTEL_CORS_ORIGINS" Default="http://localhost:1202,http://127.0.0.1:1202,http://192.168.10.150:1202" Mode="" Description="Comma-separated browser origins allowed to call the backend directly." Type="Variable" Display="advanced" Required="false" Mask="false">http://localhost:1202,http://127.0.0.1:1202,http://192.168.10.150:1202</Config>
|
<Config Name="CORS Origins" Target="GEOINTEL_CORS_ORIGINS" Default="http://localhost:1202,http://127.0.0.1:1202,http://192.168.10.150:1202" Mode="" Description="Comma-separated browser origins allowed to call the backend directly." Type="Variable" Display="advanced" Required="false" Mask="false">http://localhost:1202,http://127.0.0.1:1202,http://192.168.10.150:1202</Config>
|
||||||
<Config Name="Max Upload MB" Target="GEOINTEL_MAX_UPLOAD_MB" Default="500" Mode="" Description="Maximum upload size in MiB enforced by the backend settings." Type="Variable" Display="advanced" Required="true" Mask="false">500</Config>
|
<Config Name="Max Upload MB" Target="GEOINTEL_MAX_UPLOAD_MB" Default="500" Mode="" Description="Maximum upload size in MiB enforced by the backend settings." Type="Variable" Display="advanced" Required="true" Mask="false">500</Config>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
# GeoIntel Unraid/Compose 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.
|
||||||
|
|
||||||
# Browser URL: http://<unraid-ip>:<GEOINTEL_FRONTEND_PORT>
|
# Browser URL: http://<unraid-ip>:<GEOINTEL_FRONTEND_PORT>
|
||||||
GEOINTEL_FRONTEND_PORT=1202
|
GEOINTEL_FRONTEND_PORT=1202
|
||||||
|
|
||||||
# Optional direct backend API port. The frontend proxies /api and /health, so this
|
|
||||||
# does not need to be exposed to normal browser users.
|
|
||||||
GEOINTEL_BACKEND_PORT=8000
|
|
||||||
|
|
||||||
# Persisted application artifacts: uploads, tiles, masks, reports and exports.
|
# Persisted application artifacts: uploads, tiles, masks, reports and exports.
|
||||||
GEOINTEL_STORAGE_PATH=/mnt/user/appdata/geointel/storage
|
GEOINTEL_STORAGE_PATH=/mnt/user/appdata/geointel/storage
|
||||||
|
|
||||||
# Internal PostGIS database settings. The database is not published to the LAN.
|
# Embedded PostGIS data directory for the all-in-one container.
|
||||||
|
GEOINTEL_POSTGIS_DATA_PATH=/mnt/user/appdata/geointel/postgres-data
|
||||||
|
|
||||||
|
# Internal embedded PostGIS settings. The database is not published to the LAN.
|
||||||
GEOINTEL_POSTGRES_DB=geointel
|
GEOINTEL_POSTGRES_DB=geointel
|
||||||
GEOINTEL_POSTGRES_USER=geointel
|
GEOINTEL_POSTGRES_USER=geointel
|
||||||
GEOINTEL_POSTGRES_PASSWORD=change-me-before-shared-use
|
GEOINTEL_POSTGRES_PASSWORD=change-me-before-shared-use
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
root /usr/share/nginx/html;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location = /index.html {
|
||||||
|
add_header Cache-Control "no-cache";
|
||||||
|
try_files /index.html =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /geointel-icon.svg {
|
||||||
|
add_header Cache-Control "public, max-age=3600";
|
||||||
|
try_files /geointel-icon.svg =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /assets/ {
|
||||||
|
add_header Cache-Control "no-cache";
|
||||||
|
try_files $uri =404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /api/ {
|
||||||
|
proxy_pass http://127.0.0.1:8000/api/;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /health {
|
||||||
|
proxy_pass http://127.0.0.1:8000/health;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
services:
|
||||||
|
geointel:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: deploy/unraid/Dockerfile.all-in-one
|
||||||
|
image: geointel-all-in-one:latest
|
||||||
|
container_name: geointel
|
||||||
|
environment:
|
||||||
|
GEOINTEL_POSTGRES_DB: ${GEOINTEL_POSTGRES_DB:-geointel}
|
||||||
|
GEOINTEL_POSTGRES_USER: ${GEOINTEL_POSTGRES_USER:-geointel}
|
||||||
|
GEOINTEL_POSTGRES_PASSWORD: ${GEOINTEL_POSTGRES_PASSWORD:-geointel}
|
||||||
|
GEOINTEL_STORAGE_ROOT: /app/storage
|
||||||
|
GEOINTEL_CORS_ORIGINS: ${GEOINTEL_CORS_ORIGINS:-http://localhost:1202,http://127.0.0.1:1202}
|
||||||
|
GEOINTEL_MAX_UPLOAD_MB: ${GEOINTEL_MAX_UPLOAD_MB:-500}
|
||||||
|
ports:
|
||||||
|
- "${GEOINTEL_FRONTEND_PORT:-1202}:80"
|
||||||
|
volumes:
|
||||||
|
- ${GEOINTEL_POSTGIS_DATA_PATH:-geointel_postgis}:/var/lib/postgresql/data
|
||||||
|
- ${GEOINTEL_STORAGE_PATH:-./storage}:/app/storage
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
geointel_postgis:
|
||||||
@@ -1,3 +1,32 @@
|
|||||||
|
## Sprint 32 Unraid all-in-one runtime (2026-06-17)
|
||||||
|
|
||||||
|
Changed:
|
||||||
|
- Added `docker-compose.unraid.yml` for a single editable `geointel` container on Unraid.
|
||||||
|
- Added `deploy/unraid/Dockerfile.all-in-one` to build one image containing embedded PostGIS, backend GIS runtime, nginx and frontend static assets.
|
||||||
|
- Added `deploy/unraid/all-in-one-start.sh` to start embedded PostGIS, apply Alembic migrations, start FastAPI on internal localhost and serve nginx on container port 80.
|
||||||
|
- Added `deploy/unraid/nginx-all-in-one.conf` so `/api` and `/health` proxy to `127.0.0.1:8000` inside the same container.
|
||||||
|
- Updated `deploy/unraid/geointel.env.example`, Unraid XML template and README for one-container operation and editable web/storage/database paths.
|
||||||
|
- Updated Tower deploy scripts to stop the old multi-container stack without deleting volumes and start `docker-compose.unraid.yml`.
|
||||||
|
- Updated `scripts/live_migration_smoke.sh` to support both all-in-one `geointel` and legacy `backend` Compose services.
|
||||||
|
- Added root `.dockerignore` for all-in-one builds.
|
||||||
|
- Updated Sprint 31 tests to cover the all-in-one Dockerfile, startscript, nginx config, deploy scripts and template metadata.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- `python -m pytest backend/tests/test_sprint31_unraid_template.py backend/tests/test_live_migration_smoke_script.py backend/tests/test_docker_runtime_config.py` passed: 26 tests.
|
||||||
|
- `cd frontend && npm run typecheck` passed.
|
||||||
|
- `cd frontend && npm run build` passed.
|
||||||
|
- `python -m compileall backend/app` passed.
|
||||||
|
- `cd backend && python -m pytest` passed: 179 tests.
|
||||||
|
- `bash scripts/run_readiness_check.sh` passed: 179 backend tests, frontend typecheck/build, Alembic head check and script syntax checks.
|
||||||
|
- `cd backend && python -m alembic heads` passed: single head `202606120900`.
|
||||||
|
- `cd backend && python -m alembic upgrade head --sql` passed.
|
||||||
|
- `bash -n scripts/live_migration_smoke.sh` passed.
|
||||||
|
- `bash -n deploy/unraid/all-in-one-start.sh` passed.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- No API contracts, migrations, product features, provider fetching or AI behavior changed.
|
||||||
|
- Local Windows environment does not have `docker` in PATH; Tower deployment should provide the live all-in-one Docker validation.
|
||||||
|
|
||||||
## Sprint 31 Unraid deployment template (2026-06-17)
|
## Sprint 31 Unraid deployment template (2026-06-17)
|
||||||
|
|
||||||
Changed:
|
Changed:
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ This file now starts with the current implementation status. Older preparation/b
|
|||||||
- [x] Add Docker build context ignores for backend and frontend.
|
- [x] Add Docker build context ignores for backend and frontend.
|
||||||
- [x] Run Docker/PostGIS live validation on Tower/Unraid.
|
- [x] Run Docker/PostGIS live validation on Tower/Unraid.
|
||||||
- [x] Add Unraid Compose template assets with editable ports, storage path and app icon.
|
- [x] Add Unraid Compose template assets with editable ports, storage path and app icon.
|
||||||
|
- [x] Add single-container Unraid runtime with embedded PostGIS, backend and frontend.
|
||||||
|
|
||||||
## Current implementation status
|
## Current implementation status
|
||||||
|
|
||||||
@@ -43,6 +44,7 @@ This file now starts with the current implementation status. Older preparation/b
|
|||||||
- [x] Dataset detail, raster controls and vector controls component decomposition.
|
- [x] Dataset detail, raster controls and vector controls component decomposition.
|
||||||
- [x] QA/QC results and map workspace component decomposition.
|
- [x] QA/QC results and map workspace component decomposition.
|
||||||
- [x] Docker Compose port/storage/database configuration via `.env` defaults for Unraid.
|
- [x] Docker Compose port/storage/database configuration via `.env` defaults for Unraid.
|
||||||
|
- [x] Single-container `geointel` Unraid compose/template runtime.
|
||||||
- [ ] Further frontend component decomposition for export preview and shared workbench orchestration.
|
- [ ] Further frontend component decomposition for export preview and shared workbench orchestration.
|
||||||
|
|
||||||
## Sprint 8 status
|
## Sprint 8 status
|
||||||
|
|||||||
@@ -30,16 +30,17 @@ fi
|
|||||||
git remote get-url origin >/dev/null 2>&1 || git remote add origin '$RemoteRepo'
|
git remote get-url origin >/dev/null 2>&1 || git remote add origin '$RemoteRepo'
|
||||||
git fetch origin '$RemoteBranch'
|
git fetch origin '$RemoteBranch'
|
||||||
git reset --hard 'origin/$RemoteBranch'
|
git reset --hard 'origin/$RemoteBranch'
|
||||||
git branch -M '$RemoteBranch'
|
git branch -M '$RemoteBranch'
|
||||||
chmod +x scripts/*.sh backend/docker_start.sh || true
|
chmod +x scripts/*.sh backend/docker_start.sh deploy/unraid/*.sh || true
|
||||||
|
|
||||||
docker compose config >/dev/null
|
docker compose down --remove-orphans || true
|
||||||
docker compose build backend frontend
|
docker compose -f docker-compose.unraid.yml config >/dev/null
|
||||||
docker compose up -d
|
docker compose -f docker-compose.unraid.yml build geointel
|
||||||
docker compose ps
|
docker compose -f docker-compose.unraid.yml up -d
|
||||||
|
docker compose -f docker-compose.unraid.yml ps
|
||||||
|
|
||||||
if [ -x scripts/live_migration_smoke.sh ]; then
|
if [ -x scripts/live_migration_smoke.sh ]; then
|
||||||
bash scripts/live_migration_smoke.sh
|
COMPOSE_FILE=docker-compose.unraid.yml bash scripts/live_migration_smoke.sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -x scripts/verify_browser_runtime.sh ]; then
|
if [ -x scripts/verify_browser_runtime.sh ]; then
|
||||||
|
|||||||
@@ -32,14 +32,16 @@ fi
|
|||||||
|
|
||||||
git fetch origin "$REMOTE_BRANCH"
|
git fetch origin "$REMOTE_BRANCH"
|
||||||
git checkout -B "$REMOTE_BRANCH" "origin/$REMOTE_BRANCH"
|
git checkout -B "$REMOTE_BRANCH" "origin/$REMOTE_BRANCH"
|
||||||
|
chmod +x scripts/*.sh backend/docker_start.sh deploy/unraid/*.sh || true
|
||||||
|
|
||||||
docker compose config >/dev/null
|
docker compose down --remove-orphans || true
|
||||||
docker compose build backend frontend
|
docker compose -f docker-compose.unraid.yml config >/dev/null
|
||||||
docker compose up -d
|
docker compose -f docker-compose.unraid.yml build geointel
|
||||||
docker compose ps
|
docker compose -f docker-compose.unraid.yml up -d
|
||||||
|
docker compose -f docker-compose.unraid.yml ps
|
||||||
|
|
||||||
if [[ -x scripts/live_migration_smoke.sh ]]; then
|
if [[ -x scripts/live_migration_smoke.sh ]]; then
|
||||||
bash scripts/live_migration_smoke.sh
|
COMPOSE_FILE=docker-compose.unraid.yml bash scripts/live_migration_smoke.sh
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -x scripts/verify_browser_runtime.sh ]]; then
|
if [[ -x scripts/verify_browser_runtime.sh ]]; then
|
||||||
|
|||||||
@@ -4,14 +4,16 @@ set -euo pipefail
|
|||||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
PYTHON_BIN="${PYTHON_BIN:-}"
|
PYTHON_BIN="${PYTHON_BIN:-}"
|
||||||
LIVE_SMOKE_USE_DOCKER="${LIVE_SMOKE_USE_DOCKER:-auto}"
|
LIVE_SMOKE_USE_DOCKER="${LIVE_SMOKE_USE_DOCKER:-auto}"
|
||||||
|
COMPOSE_FILE_PATH="${COMPOSE_FILE:-docker-compose.yml}"
|
||||||
|
|
||||||
run_docker_smoke() {
|
run_docker_smoke() {
|
||||||
|
service_name="$1"
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
|
|
||||||
echo "== GeoIntel live migration smoke =="
|
echo "== GeoIntel live migration smoke =="
|
||||||
echo "Using Docker Compose backend service."
|
echo "Using Docker Compose service: ${service_name}."
|
||||||
|
|
||||||
docker compose exec -T backend sh <<'CONTAINER_SCRIPT'
|
docker compose -f "$COMPOSE_FILE_PATH" exec -T "$service_name" sh <<'CONTAINER_SCRIPT'
|
||||||
set -eu
|
set -eu
|
||||||
cd /app
|
cd /app
|
||||||
|
|
||||||
@@ -76,13 +78,18 @@ echo "== Live migration smoke passed =="
|
|||||||
CONTAINER_SCRIPT
|
CONTAINER_SCRIPT
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ "$LIVE_SMOKE_USE_DOCKER" != "0" ] && command -v docker >/dev/null 2>&1 && [ -f "$ROOT/docker-compose.yml" ]; then
|
if [ "$LIVE_SMOKE_USE_DOCKER" != "0" ] && command -v docker >/dev/null 2>&1 && [ -f "$ROOT/$COMPOSE_FILE_PATH" ]; then
|
||||||
if docker compose ps -q backend >/dev/null 2>&1 && [ -n "$(docker compose ps -q backend 2>/dev/null)" ]; then
|
cd "$ROOT"
|
||||||
if [ "$LIVE_SMOKE_USE_DOCKER" = "1" ] || docker compose ps --status running backend | grep -q backend; then
|
for service_name in geointel backend; do
|
||||||
run_docker_smoke
|
if docker compose -f "$COMPOSE_FILE_PATH" ps -q "$service_name" >/dev/null 2>&1 \
|
||||||
exit 0
|
&& [ -n "$(docker compose -f "$COMPOSE_FILE_PATH" ps -q "$service_name" 2>/dev/null)" ]; then
|
||||||
|
if [ "$LIVE_SMOKE_USE_DOCKER" = "1" ] \
|
||||||
|
|| docker compose -f "$COMPOSE_FILE_PATH" ps --status running "$service_name" | grep -q "$service_name"; then
|
||||||
|
run_docker_smoke "$service_name"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$PYTHON_BIN" ]; then
|
if [ -z "$PYTHON_BIN" ]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user