diff --git a/.gitignore b/.gitignore index ac0fc82..eb5512e 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,6 @@ npm-debug.log* # Release build output (scripts/release/make_release.sh). /dist/ + +# getlibreportal assembled docroot (built by containers/getlibreportal/publish.sh). +containers/getlibreportal/data/ diff --git a/containers/getlibreportal/docker-compose.yml b/containers/getlibreportal/docker-compose.yml new file mode 100644 index 0000000..868535f --- /dev/null +++ b/containers/getlibreportal/docker-compose.yml @@ -0,0 +1,41 @@ +networks: + DOCKER_NETWORK_DATA: #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA + external: true + +services: + getlibreportal-service: #LIBREPORTAL|SERVICE_TAG_1|getlibreportal-service + container_name: getlibreportal-service + image: nginx:alpine + restart: unless-stopped + hostname: getlibreportal + # ./data is the docroot (website + install.sh + release channels), assembled + # by publish.sh. Read-only into nginx. ./nginx.conf sets content-types/caching. + volumes: + - ./data:/usr/share/nginx/html:ro + - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro + # GLUETUN_OFF_BEGIN + ports: + - "PORTS_DATA_1" #LIBREPORTAL|PORTS_TAG_1|PORTS_DATA_1 + # GLUETUN_OFF_END + labels: + libreportal.category: "CATEGORY_DATA" #LIBREPORTAL|CATEGORY_TAG|CATEGORY_DATA + libreportal.title: "TITLE_DATA" #LIBREPORTAL|TITLE_TAG|TITLE_DATA + traefik.enable: TRAEFIK_ENABLE_DATA #LIBREPORTAL|TRAEFIK_ENABLE_TAG|TRAEFIK_ENABLE_DATA + # TRAEFIK_PORT_1_BEGIN + traefik.http.routers.getlibreportal-service.entrypoints: web,websecure + traefik.http.routers.getlibreportal-service.rule: Host(`DOMAINSUBNAME_DATA_1`) #LIBREPORTAL|DOMAINSUBNAME_TAG_1|DOMAINSUBNAME_DATA_1 + traefik.http.routers.getlibreportal-service.tls: true + traefik.http.routers.getlibreportal-service.tls.certresolver: production + traefik.http.services.getlibreportal-service.loadbalancer.server.port: PORT_INTERNAL_DATA_1 #LIBREPORTAL|PORT_INTERNAL_TAG_1|PORT_INTERNAL_DATA_1 + traefik.http.routers.getlibreportal-service.middlewares: MIDDLEWARE_DATA_1 #LIBREPORTAL|MIDDLEWARE_TAG_1|MIDDLEWARE_DATA_1 + # TRAEFIK_PORT_1_END + healthcheck: + disable: HEALTHCHECK_DATA #LIBREPORTAL|HEALTHCHECK_TAG|HEALTHCHECK_DATA + # GLUETUN_OFF_BEGIN + networks: + DOCKER_NETWORK_DATA: #LIBREPORTAL|DOCKER_NETWORK_TAG|DOCKER_NETWORK_DATA + ipv4_address: IP_DATA_1 #LIBREPORTAL|IP_TAG_1|IP_DATA_1 + # GLUETUN_OFF_END + # GLUETUN_ON_BEGIN + # network_mode: "container:gluetun-service" + # GLUETUN_ON_END diff --git a/containers/getlibreportal/getlibreportal.config b/containers/getlibreportal/getlibreportal.config new file mode 100644 index 0000000..b160168 --- /dev/null +++ b/containers/getlibreportal/getlibreportal.config @@ -0,0 +1,46 @@ +# +# ============================================================================= +# GENERAL CONFIGURATION +# ============================================================================= +# APP_NAME = name of application for use in scripts +# COMPOSE_FILE = default for no app_name in docker-compose file name, app if there is +# BACKUP = if true, include this application in backup operations +# HEALTHCHECK = if true, default docker health checks for that container will be enabled +# AUTHELIA = if true, use Authelia authentication, if false turned off. +# HEADSCALE = options : false, local, remote (see general config). e.g false or local,remote +# +CFG_GETLIBREPORTAL_APP_NAME=getlibreportal +CFG_GETLIBREPORTAL_BACKUP=false +CFG_GETLIBREPORTAL_BACKUP_STRATEGY=auto +CFG_GETLIBREPORTAL_COMPOSE_FILE=default +CFG_GETLIBREPORTAL_HEALTHCHECK=true +CFG_GETLIBREPORTAL_AUTHELIA=false +CFG_GETLIBREPORTAL_HEADSCALE=false +# +# ============================================================================= +# METADATA +# ============================================================================= +CFG_GETLIBREPORTAL_CATEGORY="features" +CFG_GETLIBREPORTAL_TITLE="LibrePortal Downloads" +CFG_GETLIBREPORTAL_DESCRIPTION="Website + signed-release host" +CFG_GETLIBREPORTAL_LONG_DESCRIPTION="Serves the LibrePortal website, the install.sh bootstrap, and signed/checksummed release artifacts — i.e. the get.libreportal.org host. Populate its docroot with containers/getlibreportal/publish.sh." +CFG_GETLIBREPORTAL_URL="https://get.libreportal.org" +CFG_GETLIBREPORTAL_ACTIONS="configure|install|restart|shutdown|uninstall" +# +# ============================================================================= +# NETWORK CONFIGURATION +# ============================================================================= +# DOMAIN = number of domain from the general config, useful when using multiple domains +# WHITELIST = if true only allow whitelisted ips on traefik, if false allow all +# +CFG_GETLIBREPORTAL_DOMAIN=1 +CFG_GETLIBREPORTAL_WHITELIST=false +CFG_GETLIBREPORTAL_NETWORK=default +# +# ============================================================================= +# PORT CONFIGURATION +# ============================================================================= +# PORT_ = app|name|external:internal|access|protocol|login|traefik|webui|title|path|slug +# login MUST stay false — this is a public download host (no basic-auth). +# +CFG_GETLIBREPORTAL_PORT_1="getlibreportal-service|webui|random:80|public|tcp|false|true|true|Downloads|/|getlibreportal" diff --git a/containers/getlibreportal/getlibreportal.sh b/containers/getlibreportal/getlibreportal.sh new file mode 100644 index 0000000..791feba --- /dev/null +++ b/containers/getlibreportal/getlibreportal.sh @@ -0,0 +1,108 @@ +#!/bin/bash + +# Category : Features +# Description : getlibreportal - Website + signed-release host (c/u/s/r/i): + +installGetlibreportal() +{ + local config_variables="$1" + + if [[ "$getlibreportal" == *[cCtTuUsSrRiI]* ]]; then + dockerConfigSetupToContainer silent getlibreportal; + local app_name=$CFG_GETLIBREPORTAL_APP_NAME + initializeAppVariables $app_name; + fi + + if [[ "$getlibreportal" == *[cC]* ]]; then + editAppConfig $app_name; + fi + + if [[ "$getlibreportal" == *[uU]* ]]; then + dockerUninstallApp $app_name; + fi + + if [[ "$getlibreportal" == *[sS]* ]]; then + dockerComposeDown $app_name; + fi + + if [[ "$getlibreportal" == *[rR]* ]]; then + dockerComposeRestart $app_name; + fi + + if [[ "$getlibreportal" == *[iI]* ]]; then + isHeader "Install $app_name" + + ((menu_number++)) + echo "" + echo "---- $menu_number. Setting up install folder and config file for $app_name." + echo "" + + dockerConfigSetupToContainer "loud" "$app_name" "install" "$config_variables"; + isSuccessful "Install folders and Config files have been setup for $app_name." + + ((menu_number++)) + echo "" + echo "---- $menu_number. Setting up the $app_name docker-compose.yml file." + echo "" + + dockerComposeSetupFile $app_name; + + ((menu_number++)) + echo "" + echo "---- $menu_number. Updating file permissions before starting." + echo "" + + fixPermissionsBeforeStart $app_name; + + ((menu_number++)) + echo "" + echo "---- $menu_number. Running the docker-compose.yml to install and start $app_name" + echo "" + + dockerComposeUpdateAndStartApp $app_name install; + + ((menu_number++)) + echo "" + echo "---- $menu_number. Running Application specific updates (if required)" + echo "" + + appUpdateSpecifics $app_name; + + ((menu_number++)) + echo "" + echo "---- $menu_number. Running Headscale setup (if required)" + echo "" + + setupHeadscale $app_name; + + ((menu_number++)) + echo "" + echo "---- $menu_number. Adding $app_name to the Apps Database table." + echo "" + + databaseInstallApp $app_name; + + ((menu_number++)) + echo "" + echo "---- $menu_number. Updating WebUI config file." + echo "" + + webuiContainerSetup $app_name install; + + ((menu_number++)) + echo "" + echo "---- $menu_number. Populate the docroot, then browse $app_name." + echo "" + echo " The container serves an EMPTY docroot until you publish content." + echo " From a full repo checkout (build/release machine) run:" + echo " containers/getlibreportal/publish.sh $containers_dir$app_name/data" + echo " (builds the site + copies install.sh + the dist/ releases)." + echo "" + + menuShowFinalMessages $app_name; + + menu_number=0 + cd + fi + getlibreportal=n +} diff --git a/containers/getlibreportal/getlibreportal.svg b/containers/getlibreportal/getlibreportal.svg new file mode 100644 index 0000000..a1222f7 --- /dev/null +++ b/containers/getlibreportal/getlibreportal.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/containers/getlibreportal/nginx.conf b/containers/getlibreportal/nginx.conf new file mode 100644 index 0000000..8ffd9aa --- /dev/null +++ b/containers/getlibreportal/nginx.conf @@ -0,0 +1,27 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # The bootstrap installer: served as a shell script, never cached, so + # `curl … | sudo bash` always gets the current one. + location = /install.sh { + default_type text/x-shellscript; + add_header Cache-Control "no-cache, must-revalidate"; + } + + # Channel manifests change every release — don't cache. + location ~ /latest\.json$ { + default_type application/json; + add_header Cache-Control "no-cache, must-revalidate"; + } + + # Release artifacts are immutable (version-pinned names) — cache hard. + location ~ \.tar\.gz$ { default_type application/gzip; add_header Cache-Control "public, max-age=31536000, immutable"; } + location ~ \.sha256$ { default_type text/plain; add_header Cache-Control "public, max-age=31536000, immutable"; } + location ~ \.minisig$ { default_type text/plain; add_header Cache-Control "public, max-age=31536000, immutable"; } + + # The static website. + location / { try_files $uri $uri/ =404; } +} diff --git a/containers/getlibreportal/publish.sh b/containers/getlibreportal/publish.sh new file mode 100644 index 0000000..f5d1cc4 --- /dev/null +++ b/containers/getlibreportal/publish.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# +# Assemble the getlibreportal docroot — the website + install.sh + release +# channels — into the served data dir. Run on a FULL repo checkout (build/release +# machine), since it needs ../../site and ../../dist (both export-ignored, i.e. +# not in release tarballs). +# +# Usage: publish.sh [TARGET_DIR] +# TARGET_DIR defaults to ./data (next to this script). On a host where the app +# is installed, pass its live data dir, e.g.: +# containers/getlibreportal/publish.sh /libreportal-containers/getlibreportal/data +# +# Layout produced: +# / ← built website (repo: site/) +# /install.sh ← bootstrap installer (repo: install.sh) +# //… ← release manifests + tarballs (repo: dist//) +set -euo pipefail + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$HERE/../.." && pwd)" # repo root (containers/getlibreportal -> ../../) +TARGET="${1:-$HERE/data}" + +echo "Assembling getlibreportal docroot -> $TARGET" +mkdir -p "$TARGET" +# Clean only the things we manage (keep the dir itself / its mount). +rm -rf "${TARGET:?}"/* "${TARGET:?}"/.??* 2>/dev/null || true + +# 1. Website (optional — skip cleanly without the toolchain). +if [[ -d "$ROOT/site" ]] && command -v npm >/dev/null 2>&1; then + ( cd "$ROOT/site" && npm install --silent && npm run build ) + cp -a "$ROOT/site/dist/." "$TARGET/" 2>/dev/null || true + echo " ✓ website" +else + echo " ! site build skipped (no site/ or no npm) — install.sh + releases still published" +fi + +# 2. Bootstrap installer. +if [[ -f "$ROOT/install.sh" ]]; then cp -f "$ROOT/install.sh" "$TARGET/install.sh"; echo " ✓ install.sh"; fi + +# 3. Release channels (from scripts/release/make_release.sh). +if [[ -d "$ROOT/dist" ]]; then + for ch in "$ROOT/dist"/*/; do + [[ -d "$ch" ]] || continue + n="$(basename "$ch")"; mkdir -p "$TARGET/$n"; cp -f "$ch"/* "$TARGET/$n/" 2>/dev/null || true + echo " ✓ channel $n" + done +else + echo " ! no dist/ — run scripts/release/make_release.sh first" +fi + +echo "Done. Restart the container to serve it."