Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,31 @@ The easiest way to start exploring the different eoAPI services is with *Docker*
```
git clone https://github.com/developmentseed/eoAPI.git
cd eoAPI
docker compose up
docker compose up --build
```

`--build` picks up changes to the local STAC Browser Dockerfile.

When upgrading PgSTAC, migrate an existing `.pgdata` before starting the stack:

```
docker compose run --rm --build pgstac-migrate
docker compose up --build
```

A version check blocks the services when its schema version does not match the configured PgSTAC image version and prints the migration command.

Set `PGSTAC_VERSION` before running Compose to select a PgSTAC image and build a matching migration helper. It defaults to `0.9.11`:

```
export PGSTAC_VERSION=0.9.11
docker compose run --rm --build pgstac-migrate
docker compose up --build
```

> [!NOTE]
> You cannot downgrade the installed version of pgstac!

Once the applications are *up*, you'll need to add STAC **Collections** and **Items** to the PgSTAC database. If you don't have these available, you can follow the [MAXAR open data demo](https://github.com/vincentsarago/MAXAR_opendata_to_pgstac) (or get inspired by the other [demos](https://github.com/developmentseed/eoAPI/tree/main/demo)).

Then you can start exploring your dataset with:
Expand Down
114 changes: 73 additions & 41 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,52 +1,54 @@
services:

# change to official image when available https://github.com/radiantearth/stac-browser/pull/386
# Build from a pinned STAC Browser release so eoAPI can provide a custom buildTileUrlTemplate config.
stac-browser:
build:
context: dockerfiles
dockerfile: Dockerfile.browser
args:
STAC_BROWSER_VERSION: ${STAC_BROWSER_VERSION:-v5.0.0}
ports:
- "${MY_DOCKER_IP:-127.0.0.1}:8085:8085"
- "${MY_DOCKER_IP:-127.0.0.1}:8085:8080"
depends_on:
- stac-fastapi
- titiler-pgstac
- database
stac-fastapi:
condition: service_started
titiler-pgstac:
condition: service_started
pgstac-version-check:
condition: service_completed_successfully

stac-fastapi:
image: ghcr.io/stac-utils/stac-fastapi-pgstac:5.0.2
image: ghcr.io/stac-utils/stac-fastapi-pgstac:6.3.1
ports:
- "${MY_DOCKER_IP:-127.0.0.1}:8081:8081"
environment:
# Postgres connection
- POSTGRES_USER=username
- POSTGRES_PASS=password
- POSTGRES_DBNAME=postgis
- POSTGRES_HOST_READER=database
- POSTGRES_HOST_WRITER=database
- POSTGRES_PORT=5432
- PGUSER=username
- PGPASSWORD=password
- PGDATABASE=postgis
- PGHOST=database
- PGPORT=5432
- DB_MIN_CONN_SIZE=1
- DB_MAX_CONN_SIZE=1
# stac-fastapi-pgstac 6 disables transaction endpoints by default.
- ENABLE_TRANSACTIONS_EXTENSIONS=TRUE
depends_on:
- database
command:
bash -c "bash /tmp/scripts/wait-for-it.sh -t 120 -h database -p 5432 && uvicorn stac_fastapi.pgstac.app:app --host 0.0.0.0 --port 8081"
pgstac-version-check:
condition: service_completed_successfully
command: bash -c "bash /tmp/scripts/wait-for-it.sh -t 120 -h database -p 5432 && uvicorn stac_fastapi.pgstac.app:app --host 0.0.0.0 --port 8081"
volumes:
- ./dockerfiles/scripts:/tmp/scripts

titiler-pgstac:
# At the time of writing, rasterio and psycopg wheels are not available for arm64 arch
# so we force the image to be built with linux/amd64
platform: linux/amd64
image: ghcr.io/stac-utils/titiler-pgstac:1.7.2
image: ghcr.io/stac-utils/titiler-pgstac:3.0.0
ports:
- "${MY_DOCKER_IP:-127.0.0.1}:8082:8082"
environment:
# Postgres connection
- POSTGRES_USER=username
- POSTGRES_PASS=password
- POSTGRES_DBNAME=postgis
- POSTGRES_HOST=database
- POSTGRES_PORT=5432
- PGUSER=username
- PGPASSWORD=password
- PGDATABASE=postgis
- PGHOST=database
- PGPORT=5432
- DB_MIN_CONN_SIZE=1
- DB_MAX_CONN_SIZE=10
# - DB_MAX_QUERIES=10
Expand All @@ -63,42 +65,37 @@ services:
- VSI_CACHE_SIZE=536870912
# TiTiler Config
- MOSAIC_CONCURRENCY=1
- TITILER_PGSTAC_API_ENABLE_EXTERNAL_DATASET_ENDPOINTS=TRUE
# AWS S3 endpoint config
- AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
- AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
depends_on:
- database
command:
bash -c "bash /tmp/scripts/wait-for-it.sh -t 120 -h database -p 5432 && uvicorn titiler.pgstac.main:app --host 0.0.0.0 --port 8082"
pgstac-version-check:
condition: service_completed_successfully
command: bash -c "bash /tmp/scripts/wait-for-it.sh -t 120 -h database -p 5432 && uvicorn titiler.pgstac.main:app --host 0.0.0.0 --port 8082"
volumes:
- ./dockerfiles/scripts:/tmp/scripts

tipg:
image: ghcr.io/developmentseed/tipg:1.0.1
image: ghcr.io/developmentseed/tipg:1.4.0
ports:
- "${MY_DOCKER_IP:-127.0.0.1}:8083:8083"
environment:
# Postgres connection
- POSTGRES_USER=username
- POSTGRES_PASS=password
- POSTGRES_DBNAME=postgis
- POSTGRES_HOST=database
- POSTGRES_PORT=5432
- DATABASE_URL=postgresql://username:password@database:5432/postgis
- DB_MIN_CONN_SIZE=1
- DB_MAX_CONN_SIZE=10
command:
bash -c "bash /tmp/scripts/wait-for-it.sh -t 120 -h database -p 5432 && uvicorn tipg.main:app --host 0.0.0.0 --port 8083"
command: bash -c "bash /tmp/scripts/wait-for-it.sh -t 120 -h database -p 5432 && uvicorn tipg.main:app --host 0.0.0.0 --port 8083"
depends_on:
- database
pgstac-version-check:
condition: service_completed_successfully
volumes:
- ./dockerfiles/scripts:/tmp/scripts

database:
# The PgSTAC image does not publish an arm64 manifest for this tag, so run
# the amd64 image under emulation on Apple Silicon and other arm64 hosts.
platform: linux/amd64
image: ghcr.io/stac-utils/pgstac:v0.9.6
image: ghcr.io/stac-utils/pgstac:v${PGSTAC_VERSION:-0.9.11}
environment:
- PGSTAC_VERSION=${PGSTAC_VERSION:-0.9.11}
- POSTGRES_USER=username
- POSTGRES_PASSWORD=password
- POSTGRES_DB=postgis
Expand All @@ -110,6 +107,41 @@ services:
command: postgres -N 500
volumes:
- ./.pgdata:/var/lib/postgresql/data
- ./dockerfiles/scripts:/tmp/scripts:ro

pgstac-version-check:
image: ghcr.io/stac-utils/pgstac:v${PGSTAC_VERSION:-0.9.11}
entrypoint: ["/tmp/scripts/check-pgstac-version.sh"]
environment:
- PGSTAC_VERSION=${PGSTAC_VERSION:-0.9.11}
- PGUSER=username
- PGPASSWORD=password
- PGDATABASE=postgis
- PGHOST=database
- PGPORT=5432
volumes:
- ./dockerfiles/scripts:/tmp/scripts:ro
depends_on:
database:
condition: service_started

pgstac-migrate:
profiles: ["migration"]
build:
context: dockerfiles
dockerfile: Dockerfile.pgstac-migrate
args:
PGSTAC_VERSION: ${PGSTAC_VERSION:-0.9.11}
environment:
- PGSTAC_VERSION=${PGSTAC_VERSION:-0.9.11}
- PGUSER=username
- PGPASSWORD=password
- PGDATABASE=postgis
- PGHOST=database
- PGPORT=5432
depends_on:
database:
condition: service_started

networks:
default:
Expand Down
39 changes: 23 additions & 16 deletions dockerfiles/Dockerfile.browser
Original file line number Diff line number Diff line change
@@ -1,34 +1,41 @@
# Copyright Radiant Earth Foundation

FROM node:lts-alpine3.18 AS build-step
FROM node:lts-alpine AS build-step
ARG DYNAMIC_CONFIG=true
ARG STAC_BROWSER_VERSION=v5.0.0-rc.1
ARG historyMode="history"
ARG pathPrefix="/"
ARG SB_CONFIG="./config.eoapi.js"
ENV SB_historyMode="${historyMode}"
ENV SB_pathPrefix="${pathPrefix}"
ENV SB_CONFIG="${SB_CONFIG}"

WORKDIR /app

RUN apk add --no-cache git
RUN git clone https://github.com/radiantearth/stac-browser.git .
# remove the default config.js
RUN rm config.js
RUN git clone --depth 1 --branch "${STAC_BROWSER_VERSION}" https://github.com/radiantearth/stac-browser.git .
RUN npm install
# replace the default config.js with our config file
COPY ./browser_config.js ./config.js
RUN \[ "${DYNAMIC_CONFIG}" == "true" \] && sed -i 's/<!-- <script defer="defer" src=".\/config.js"><\/script> -->/<script defer="defer" src=".\/config.js"><\/script>/g' public/index.html
COPY ./browser_config.js ./config.eoapi.js
RUN if [ "${DYNAMIC_CONFIG}" = "true" ]; then sed -i 's/<!--RC//;s/RC-->//' index.html; fi
RUN npm run build

FROM nginxinc/nginx-unprivileged:1-alpine
ARG pathPrefix="/"

FROM nginx:1-alpine-slim
USER root
RUN apk add --no-cache jq pcre-tools

COPY --from=build-step /app/dist /usr/share/nginx/html
COPY --from=build-step /app/config.schema.json /etc/nginx/conf.d/config.schema.json
COPY --from=build-step /app/dist /usr/share/nginx/html
COPY --from=build-step /app/docker/default.conf /etc/nginx/conf.d/default.conf
COPY --from=build-step /app/docker/docker-entrypoint.sh /docker-entrypoint.d/40-stac-browser-entrypoint.sh

# change default port to 8085
RUN apk add jq pcre-tools && \
sed -i 's/\s*listen\s*80;/ listen 8085;/' /etc/nginx/conf.d/default.conf && \
sed -i 's/\s*location \/ {/ location \/ {\n try_files $uri $uri\/ \/index.html;/' /etc/nginx/conf.d/default.conf
RUN sed -i "s|<pathPrefix>|${pathPrefix}|" /etc/nginx/conf.d/default.conf && \
chown -R nginx:nginx /usr/share/nginx/html && \
chmod +x /docker-entrypoint.d/40-stac-browser-entrypoint.sh

EXPOSE 8085
EXPOSE 8080

STOPSIGNAL SIGTERM

# override entrypoint, which calls nginx-entrypoint underneath
COPY --from=build-step /app/docker/docker-entrypoint.sh ./docker-entrypoint.d/40-stac-browser-entrypoint.sh
USER nginx
9 changes: 9 additions & 0 deletions dockerfiles/Dockerfile.pgstac-migrate
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM python:3.13-slim

ARG PGSTAC_VERSION

RUN pip install --no-cache-dir "pypgstac[psycopg]==${PGSTAC_VERSION}"

COPY migrate-pgstac.py /usr/local/bin/migrate-pgstac

ENTRYPOINT ["migrate-pgstac"]
104 changes: 66 additions & 38 deletions dockerfiles/browser_config.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,67 @@
module.exports = {
catalogUrl: "http://0.0.0.0:8081",
catalogTitle: "eoAPI STAC Browser",
allowExternalAccess: true, // Must be true if catalogUrl is not given
allowedDomains: [],
detectLocaleFromBrowser: true,
storeLocale: true,
locale: "en",
fallbackLocale: "en",
supportedLocales: [
"de",
"es",
"en",
"fr",
"it",
"ro"
],
apiCatalogPriority: null,
useTileLayerAsFallback: true,
displayGeoTiffByDefault: false,
buildTileUrlTemplate: ({href, asset}) => "http://0.0.0.0:8082/cog/tiles/{z}/{x}/{y}@2x?url=" + encodeURIComponent(asset.href.startsWith("/vsi") ? asset.href : href),
stacProxyUrl: null,
pathPrefix: "/",
historyMode: "history",
cardViewMode: "cards",
cardViewSort: "asc",
showThumbnailsAsAssets: false,
stacLint: true,
geoTiffResolution: 128,
redirectLegacyUrls: false,
itemsPerPage: 12,
defaultThumbnailSize: null,
maxPreviewsOnMap: 50,
crossOriginMedia: null,
requestHeaders: {},
requestQueryParameters: {},
preprocessSTAC: null,
authConfig: null
export default {
catalogUrl: "http://0.0.0.0:8081",
catalogTitle: "eoAPI STAC Browser",
catalogTitleAfterImage: null,
catalogImage: null,
allowExternalAccess: true, // Must be true if catalogUrl is not given
allowedDomains: [],
enforcedColorMode: "auto",
detectLocaleFromBrowser: true,
storeLocale: true,
locale: "en",
fallbackLocale: "en",
supportedLocales: [
"ar",
"de",
"es",
"en",
"fr",
"it",
"ro",
"ru",
"ja",
"pt",
"id",
"pl",
"sv",
],
apiCatalogPriority: null,
useTileLayerAsFallback: true,
displayGeoTiffByDefault: false,
displayPreview: true,
displayOverview: true,
displayOverviewsForChildren: false,
buildTileUrlTemplate: (asset) => {
const href = asset.getAbsoluteUrl();
const assetHref = asset.href || href;
const tileHref = assetHref.startsWith("/vsi") ? assetHref : href;

return (
"http://0.0.0.0:8082/external/tiles/WebMercatorQuad/{z}/{x}/{y}?url=" +
encodeURIComponent(tileHref)
);
},
getMapSourceOptions: null,
pathPrefix: "/",
historyMode: "history",
cardViewMode: "cards",
defaultCollectionSort: "title",
defaultItemSort: null,
showKeywordsInItemCards: false,
showKeywordsInCatalogCards: false,
preferredAssets: true,
showThumbnailsAsAssets: false,
searchResultsPerPage: null,
itemsPerPage: 12,
collectionsPerPage: null,
maxEntriesPerPage: 1000,
defaultThumbnailSize: null,
crossOriginMedia: null,
requestHeaders: {},
requestQueryParameters: {},
socialSharing: ["email", "bsky", "mastodon", "x"],
preprocessSTAC: null,
authConfig: null,
crs: {},
footerLinks: null,
};
Loading