wrolpi/scripts/start_kiwix_serve.sh

85 lines
3.4 KiB
Bash
Raw Permalink Normal View History

#! /usr/bin/env bash
# This file imports all *.zim files in the Zim media directory, then starts kiwix-serve.
set -u
zim: ship a default fallback Zim and honor the configured Zim directory Two related gaps in how WROLPi serves Kiwix content: 1. A fresh WROLPi (or one whose zims are all corrupt/absent) showed a blank Kiwix. Every image now ships a small built-in default Zim that Kiwix falls back to, telling the user to download real Zim files. 2. The startup scripts hardcoded the `/zims` directory, ignoring wrolpi.yaml's `zims_destination` (which the Python side already honors). Changes: - New wrolpi/scripts/read_config_value.sh: a zero-dependency (awk) reader so non-Python scripts can reliably read top-level wrolpi.yaml scalars. Also a config_value() wrapper in wrolpi/scripts/lib.sh. - New wrolpi/scripts/default_zim/: HTML source + a reproducible builder (ghcr.io/openzim/zim-tools) that outputs the committed wrolpi/blobs/default.zim. - New wrolpi/blobs/: any committed blob here is copied to /opt/wrolpi-blobs by the pi-gen and debian-live builds; the Docker zim image bakes it in. - start_kiwix_serve.sh honors zims_destination and falls back to the default Zim when no functioning zim is found. - docker/zim/entrypoint.sh gains the same default-Zim fallback. It needs no config read: it already scans the whole mounted media dir, and zims_destination is always a subdirectory of that mount. - Changing zims_destination via the settings API now restarts Kiwix so it serves the new directory without a reboot. Tests: new test_read_config_value.py, extended test_start_kiwix_serve.py (configured dir + default-Zim fallback), and a root_api test for the restart. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 18:24:03 -06:00
# The media directory and project directory are overridable for testing;
# they default to the production locations.
MEDIA_DIRECTORY="${MEDIA_DIRECTORY:-/media/wrolpi}"
zim: ship a default fallback Zim and honor the configured Zim directory Two related gaps in how WROLPi serves Kiwix content: 1. A fresh WROLPi (or one whose zims are all corrupt/absent) showed a blank Kiwix. Every image now ships a small built-in default Zim that Kiwix falls back to, telling the user to download real Zim files. 2. The startup scripts hardcoded the `/zims` directory, ignoring wrolpi.yaml's `zims_destination` (which the Python side already honors). Changes: - New wrolpi/scripts/read_config_value.sh: a zero-dependency (awk) reader so non-Python scripts can reliably read top-level wrolpi.yaml scalars. Also a config_value() wrapper in wrolpi/scripts/lib.sh. - New wrolpi/scripts/default_zim/: HTML source + a reproducible builder (ghcr.io/openzim/zim-tools) that outputs the committed wrolpi/blobs/default.zim. - New wrolpi/blobs/: any committed blob here is copied to /opt/wrolpi-blobs by the pi-gen and debian-live builds; the Docker zim image bakes it in. - start_kiwix_serve.sh honors zims_destination and falls back to the default Zim when no functioning zim is found. - docker/zim/entrypoint.sh gains the same default-Zim fallback. It needs no config read: it already scans the whole mounted media dir, and zims_destination is always a subdirectory of that mount. - Changing zims_destination via the settings API now restarts Kiwix so it serves the new directory without a reboot. Tests: new test_read_config_value.py, extended test_start_kiwix_serve.py (configured dir + default-Zim fallback), and a root_api test for the restart. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 18:24:03 -06:00
PROJECT_DIR="${PROJECT_DIR:-/opt/wrolpi}"
# Default Zim shipped in /opt/wrolpi-blobs; served when no real zim is available.
DEFAULT_ZIM="${DEFAULT_ZIM:-/opt/wrolpi-blobs/default.zim}"
[ ! -d "${MEDIA_DIRECTORY}" ] && echo "Cannot start kiwix without media directory mounted" && exit 1
zim: ship a default fallback Zim and honor the configured Zim directory Two related gaps in how WROLPi serves Kiwix content: 1. A fresh WROLPi (or one whose zims are all corrupt/absent) showed a blank Kiwix. Every image now ships a small built-in default Zim that Kiwix falls back to, telling the user to download real Zim files. 2. The startup scripts hardcoded the `/zims` directory, ignoring wrolpi.yaml's `zims_destination` (which the Python side already honors). Changes: - New wrolpi/scripts/read_config_value.sh: a zero-dependency (awk) reader so non-Python scripts can reliably read top-level wrolpi.yaml scalars. Also a config_value() wrapper in wrolpi/scripts/lib.sh. - New wrolpi/scripts/default_zim/: HTML source + a reproducible builder (ghcr.io/openzim/zim-tools) that outputs the committed wrolpi/blobs/default.zim. - New wrolpi/blobs/: any committed blob here is copied to /opt/wrolpi-blobs by the pi-gen and debian-live builds; the Docker zim image bakes it in. - start_kiwix_serve.sh honors zims_destination and falls back to the default Zim when no functioning zim is found. - docker/zim/entrypoint.sh gains the same default-Zim fallback. It needs no config read: it already scans the whole mounted media dir, and zims_destination is always a subdirectory of that mount. - Changing zims_destination via the settings API now restarts Kiwix so it serves the new directory without a reboot. Tests: new test_read_config_value.py, extended test_start_kiwix_serve.py (configured dir + default-Zim fallback), and a root_api test for the restart. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 18:24:03 -06:00
# Honor the configured Zim directory (wrolpi.yaml `zims_destination`, always
# relative to the media directory), defaulting to `zims`.
ZIMS_DEST="$(MEDIA_DIRECTORY="${MEDIA_DIRECTORY}" "${PROJECT_DIR}/wrolpi/scripts/read_config_value.sh" zims_destination zims)"
# Defense in depth: never let an absolute path, traversal, or empty value escape
# the media root (the settings API also rejects these). Fall back to `zims`.
case "${ZIMS_DEST}" in
''|/*|*..*)
echo "Ignoring unsafe zims_destination '${ZIMS_DEST}'; using 'zims'" >&2
ZIMS_DEST=zims
;;
esac
zim: ship a default fallback Zim and honor the configured Zim directory Two related gaps in how WROLPi serves Kiwix content: 1. A fresh WROLPi (or one whose zims are all corrupt/absent) showed a blank Kiwix. Every image now ships a small built-in default Zim that Kiwix falls back to, telling the user to download real Zim files. 2. The startup scripts hardcoded the `/zims` directory, ignoring wrolpi.yaml's `zims_destination` (which the Python side already honors). Changes: - New wrolpi/scripts/read_config_value.sh: a zero-dependency (awk) reader so non-Python scripts can reliably read top-level wrolpi.yaml scalars. Also a config_value() wrapper in wrolpi/scripts/lib.sh. - New wrolpi/scripts/default_zim/: HTML source + a reproducible builder (ghcr.io/openzim/zim-tools) that outputs the committed wrolpi/blobs/default.zim. - New wrolpi/blobs/: any committed blob here is copied to /opt/wrolpi-blobs by the pi-gen and debian-live builds; the Docker zim image bakes it in. - start_kiwix_serve.sh honors zims_destination and falls back to the default Zim when no functioning zim is found. - docker/zim/entrypoint.sh gains the same default-Zim fallback. It needs no config read: it already scans the whole mounted media dir, and zims_destination is always a subdirectory of that mount. - Changing zims_destination via the settings API now restarts Kiwix so it serves the new directory without a reboot. Tests: new test_read_config_value.py, extended test_start_kiwix_serve.py (configured dir + default-Zim fallback), and a root_api test for the restart. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 18:24:03 -06:00
ZIM_DIRECTORY="${MEDIA_DIRECTORY}/${ZIMS_DEST}"
LIBRARY="${ZIM_DIRECTORY}/library.xml"
# Wait up to 30 seconds for the zims directory to be mounted.
COUNT=0
while [ ${COUNT} -lt 30 ]; do
if [ -d "${ZIM_DIRECTORY}" ]; then
break
fi
COUNT=$((COUNT + 1))
sleep 1
done
# Create the zim directory, if necessary.
[ ! -d "${ZIM_DIRECTORY}" ] && mkdir -p "${ZIM_DIRECTORY}"
# We will replace the library file.
rm -f "${LIBRARY}"
# Import every *.zim. A corrupt or partial zim is rejected by kiwix-manage
# ("Cannot add zim ..."); it must NOT prevent the remaining valid zims from
# being served, nor stop the server from starting. So we add each zim
# independently and never abort on a single failure.
found_zim=false
added_zim=false
while IFS= read -r -d '' zim; do
found_zim=true
if kiwix-manage "${LIBRARY}" add "${zim}"; then
added_zim=true
else
echo "Skipping zim which could not be added (corrupt or incomplete?): ${zim}" >&2
fi
done < <(find "${ZIM_DIRECTORY}" -iname '*.zim' -print0)
if [ "${found_zim}" = false ]; then
echo "Could not find any Zim files to import"
elif [ "${added_zim}" = false ]; then
zim: ship a default fallback Zim and honor the configured Zim directory Two related gaps in how WROLPi serves Kiwix content: 1. A fresh WROLPi (or one whose zims are all corrupt/absent) showed a blank Kiwix. Every image now ships a small built-in default Zim that Kiwix falls back to, telling the user to download real Zim files. 2. The startup scripts hardcoded the `/zims` directory, ignoring wrolpi.yaml's `zims_destination` (which the Python side already honors). Changes: - New wrolpi/scripts/read_config_value.sh: a zero-dependency (awk) reader so non-Python scripts can reliably read top-level wrolpi.yaml scalars. Also a config_value() wrapper in wrolpi/scripts/lib.sh. - New wrolpi/scripts/default_zim/: HTML source + a reproducible builder (ghcr.io/openzim/zim-tools) that outputs the committed wrolpi/blobs/default.zim. - New wrolpi/blobs/: any committed blob here is copied to /opt/wrolpi-blobs by the pi-gen and debian-live builds; the Docker zim image bakes it in. - start_kiwix_serve.sh honors zims_destination and falls back to the default Zim when no functioning zim is found. - docker/zim/entrypoint.sh gains the same default-Zim fallback. It needs no config read: it already scans the whole mounted media dir, and zims_destination is always a subdirectory of that mount. - Changing zims_destination via the settings API now restarts Kiwix so it serves the new directory without a reboot. Tests: new test_read_config_value.py, extended test_start_kiwix_serve.py (configured dir + default-Zim fallback), and a root_api test for the restart. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 18:24:03 -06:00
echo "Found Zim files but none could be added (all corrupt or incomplete?)" >&2
fi
# When no functioning zim was added, fall back to the default Zim shipped in
# /opt/wrolpi-blobs so the user sees a helpful placeholder ("download some Zim
# files") instead of a blank Kiwix. A single corrupt zim must never take down
# the server.
if [ "${added_zim}" = false ] && [ -f "${DEFAULT_ZIM}" ]; then
echo "No usable Zim files found; serving the default WROLPi Zim: ${DEFAULT_ZIM}" >&2
kiwix-manage "${LIBRARY}" add "${DEFAULT_ZIM}" || echo "Failed to add the default Zim" >&2
fi
# Ensure a library file always exists so kiwix-serve can start even when there
zim: ship a default fallback Zim and honor the configured Zim directory Two related gaps in how WROLPi serves Kiwix content: 1. A fresh WROLPi (or one whose zims are all corrupt/absent) showed a blank Kiwix. Every image now ships a small built-in default Zim that Kiwix falls back to, telling the user to download real Zim files. 2. The startup scripts hardcoded the `/zims` directory, ignoring wrolpi.yaml's `zims_destination` (which the Python side already honors). Changes: - New wrolpi/scripts/read_config_value.sh: a zero-dependency (awk) reader so non-Python scripts can reliably read top-level wrolpi.yaml scalars. Also a config_value() wrapper in wrolpi/scripts/lib.sh. - New wrolpi/scripts/default_zim/: HTML source + a reproducible builder (ghcr.io/openzim/zim-tools) that outputs the committed wrolpi/blobs/default.zim. - New wrolpi/blobs/: any committed blob here is copied to /opt/wrolpi-blobs by the pi-gen and debian-live builds; the Docker zim image bakes it in. - start_kiwix_serve.sh honors zims_destination and falls back to the default Zim when no functioning zim is found. - docker/zim/entrypoint.sh gains the same default-Zim fallback. It needs no config read: it already scans the whole mounted media dir, and zims_destination is always a subdirectory of that mount. - Changing zims_destination via the settings API now restarts Kiwix so it serves the new directory without a reboot. Tests: new test_read_config_value.py, extended test_start_kiwix_serve.py (configured dir + default-Zim fallback), and a root_api test for the restart. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 18:24:03 -06:00
# are no valid zims and the default Zim is missing.
if [ ! -f "${LIBRARY}" ]; then
printf '<?xml version="1.0" encoding="UTF-8"?>\n<library version="20110515">\n</library>\n' > "${LIBRARY}"
fi
# Serve HTTP kiwix on 9085. Caddy will serve HTTPS on 8085.
# Bind all interfaces by omitting --address: kiwix-serve defaults to all
# interfaces, and trixie's kiwix-tools 3.7.0 rejects the literal
# "--address 0.0.0.0" ("IP address is not available on this system").
exec kiwix-serve --library "${LIBRARY}" --port 9085