diff --git a/.dockerignore b/.dockerignore index c33ee6452..8c39dd615 100644 --- a/.dockerignore +++ b/.dockerignore @@ -26,3 +26,5 @@ ./dist ./scripts ./resources +# Other stuff +**/*.drawio.png diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d00a8cb43..5e090a227 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -131,7 +131,7 @@ jobs: - name: Configure ImageMagick run: | - sudo cp docker/imagemagick-policy.xml /etc/ImageMagick-6/policy.xml + sudo cp docker/rootfs/etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml - name: Install Python dependencies run: | diff --git a/.ruff.toml b/.ruff.toml index a29b471c5..ae1bed609 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -76,8 +76,12 @@ ignore = ["DJ001", "SIM105", "RUF012"] "src/paperless_tesseract/tests/test_parser.py" = ["RUF001", "PTH"] # TODO PTH Enable & remove "src/paperless_tika/tests/test_live_tika.py" = ["PTH"] # TODO Enable & remove "src/paperless_tika/tests/test_tika_parser.py" = ["PTH"] # TODO Enable & remove +# Testing "*/tests/*.py" = ["E501", "SIM117"] +# Migrations "*/migrations/*.py" = ["E501", "SIM", "T201"] +# Docker specific +"docker/rootfs/usr/local/bin/wait-for-redis.py" = ["INP001", "T201"] [lint.isort] force-single-line = true diff --git a/Dockerfile b/Dockerfile index 3eab1dc2f..6a575be74 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,11 +43,66 @@ RUN set -eux \ && echo "Generating requirement.txt" \ && pipenv requirements > requirements.txt +# Stage: s6-overlay-base +# Purpose: Installs s6-overlay and rootfs +# Comments: +# - Don't leave anything extra in here either +FROM docker.io/python:3.12-slim-bookworm AS s6-overlay-base + +WORKDIR /usr/src/s6 + +# https://github.com/just-containers/s6-overlay#customizing-s6-overlay-behaviour +ENV \ + S6_BEHAVIOUR_IF_STAGE2_FAILS=2 \ + S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \ + S6_VERBOSITY=1 \ + PATH=/command:$PATH + +# Buildx provided, must be defined to use though +ARG TARGETARCH +ARG TARGETVARIANT +# Lock this version +ARG S6_OVERLAY_VERSION=3.2.0.2 + +ARG S6_BUILD_TIME_PKGS="curl \ + xz-utils" + +RUN set -eux \ + && echo "Installing build time packages" \ + && apt-get update \ + && apt-get install --yes --quiet --no-install-recommends ${S6_BUILD_TIME_PKGS} \ + && echo "Determining arch" \ + && S6_ARCH="" \ + && if [ "${TARGETARCH}${TARGETVARIANT}" = "amd64" ]; then S6_ARCH="x86_64"; \ + elif [ "${TARGETARCH}${TARGETVARIANT}" = "arm64" ]; then S6_ARCH="aarch64"; fi\ + && if [ -z "${S6_ARCH}" ]; then { echo "Error: Not able to determine arch"; exit 1; }; fi \ + && echo "Installing s6-overlay for ${S6_ARCH}" \ + && curl --fail --silent --no-progress-meter --show-error --location --remote-name-all --parallel --parallel-max 4 \ + "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz" \ + "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz.sha256" \ + "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-${S6_ARCH}.tar.xz" \ + "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-${S6_ARCH}.tar.xz.sha256" \ + && echo "Validating s6-archive checksums" \ + && sha256sum --check ./*.sha256 \ + && echo "Unpacking archives" \ + && tar --directory / -Jxpf s6-overlay-noarch.tar.xz \ + && tar --directory / -Jxpf s6-overlay-${S6_ARCH}.tar.xz \ + && echo "Removing downloaded archives" \ + && rm ./*.tar.xz \ + && rm ./*.sha256 \ + && echo "Cleaning up image" \ + && apt-get --yes purge ${S6_BUILD_TIME_PKGS} \ + && apt-get --yes autoremove --purge \ + && rm -rf /var/lib/apt/lists/* + +# Copy our service defs and filesystem +COPY ./docker/rootfs / + # Stage: main-app # Purpose: The final image # Comments: # - Don't leave anything extra in here -FROM docker.io/python:3.12-slim-bookworm AS main-app +FROM s6-overlay-base AS main-app LABEL org.opencontainers.image.authors="paperless-ngx team " LABEL org.opencontainers.image.documentation="https://docs.paperless-ngx.com/" @@ -143,64 +198,23 @@ RUN set -eux \ && dpkg --install ./ghostscript_${GS_VERSION}.dfsg-1_${TARGETARCH}.deb \ && echo "Installing jbig2enc" \ && dpkg --install ./jbig2enc_${JBIG2ENC_VERSION}-1_${TARGETARCH}.deb \ + && echo "Configuring imagemagick" \ + && cp /etc/ImageMagick-6/paperless-policy.xml /etc/ImageMagick-6/policy.xml \ && echo "Cleaning up image layer" \ && rm --force --verbose *.deb \ - && rm --recursive --force --verbose /var/lib/apt/lists/* \ - && echo "Installing supervisor" \ - && python3 -m pip install --default-timeout=1000 --upgrade --no-cache-dir supervisor==4.2.5 + && rm --recursive --force --verbose /var/lib/apt/lists/* # Copy gunicorn config # Changes very infrequently WORKDIR /usr/src/paperless/ -COPY gunicorn.conf.py . - -# setup docker-specific things -# These change sometimes, but rarely -WORKDIR /usr/src/paperless/src/docker/ - -COPY [ \ - "docker/imagemagick-policy.xml", \ - "docker/supervisord.conf", \ - "docker/docker-entrypoint.sh", \ - "docker/docker-prepare.sh", \ - "docker/paperless_cmd.sh", \ - "docker/wait-for-redis.py", \ - "docker/env-from-file.sh", \ - "docker/management_script.sh", \ - "docker/flower-conditional.sh", \ - "docker/install_management_commands.sh", \ - "/usr/src/paperless/src/docker/" \ -] - -RUN set -eux \ - && echo "Configuring ImageMagick" \ - && mv imagemagick-policy.xml /etc/ImageMagick-6/policy.xml \ - && echo "Configuring supervisord" \ - && mkdir /var/log/supervisord /var/run/supervisord \ - && mv supervisord.conf /etc/supervisord.conf \ - && echo "Setting up Docker scripts" \ - && mv docker-entrypoint.sh /sbin/docker-entrypoint.sh \ - && chmod 755 /sbin/docker-entrypoint.sh \ - && mv docker-prepare.sh /sbin/docker-prepare.sh \ - && chmod 755 /sbin/docker-prepare.sh \ - && mv wait-for-redis.py /sbin/wait-for-redis.py \ - && chmod 755 /sbin/wait-for-redis.py \ - && mv env-from-file.sh /sbin/env-from-file.sh \ - && chmod 755 /sbin/env-from-file.sh \ - && mv paperless_cmd.sh /usr/local/bin/paperless_cmd.sh \ - && chmod 755 /usr/local/bin/paperless_cmd.sh \ - && mv flower-conditional.sh /usr/local/bin/flower-conditional.sh \ - && chmod 755 /usr/local/bin/flower-conditional.sh \ - && echo "Installing management commands" \ - && chmod +x install_management_commands.sh \ - && ./install_management_commands.sh +COPY --chown=1000:1000 gunicorn.conf.py /usr/src/paperless/gunicorn.conf.py WORKDIR /usr/src/paperless/src/ # Python dependencies # Change pretty frequently -COPY --from=pipenv-base /usr/src/pipenv/requirements.txt ./ +COPY --chown=1000:1000 --from=pipenv-base /usr/src/pipenv/requirements.txt ./ # Packages needed only for building a few quick Python # dependencies @@ -222,7 +236,7 @@ RUN --mount=type=cache,target=/root/.cache/pip/,id=pip-cache \ && echo "Installing build system packages" \ && apt-get update \ && apt-get install --yes --quiet --no-install-recommends ${BUILD_PACKAGES} \ - && python3 -m pip install --no-cache-dir --upgrade wheel \ + && python3 -m pip install --upgrade wheel \ && echo "Installing Python requirements" \ && curl --fail --silent --no-progress-meter --show-error --location --remote-name-all --parallel --parallel-max 4 \ https://github.com/paperless-ngx/builder/releases/download/psycopg-${PSYCOPG_VERSION}/psycopg_c-${PSYCOPG_VERSION}-cp312-cp312-linux_x86_64.whl \ @@ -267,18 +281,16 @@ RUN set -eux \ && echo "Adjusting all permissions" \ && chown --from root:root --changes --recursive paperless:paperless /usr/src/paperless \ && echo "Collecting static files" \ - && gosu paperless python3 manage.py collectstatic --clear --no-input --link \ - && gosu paperless python3 manage.py compilemessages + && s6-setuidgid paperless python3 manage.py collectstatic --clear --no-input --link \ + && s6-setuidgid paperless python3 manage.py compilemessages VOLUME ["/usr/src/paperless/data", \ "/usr/src/paperless/media", \ "/usr/src/paperless/consume", \ "/usr/src/paperless/export"] -ENTRYPOINT ["/sbin/docker-entrypoint.sh"] +ENTRYPOINT ["/init"] EXPOSE 8000 -CMD ["/usr/local/bin/paperless_cmd.sh"] - HEALTHCHECK --interval=30s --timeout=10s --retries=5 CMD [ "curl", "-fs", "-S", "--max-time", "2", "http://localhost:8000" ] diff --git a/docker/docker-prepare.sh b/docker/docker-prepare.sh deleted file mode 100755 index e3d924535..000000000 --- a/docker/docker-prepare.sh +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env bash - -set -e - -wait_for_postgres() { - local attempt_num=1 - local -r max_attempts=5 - - echo "Waiting for PostgreSQL to start..." - - local -r host="${PAPERLESS_DBHOST:-localhost}" - local -r port="${PAPERLESS_DBPORT:-5432}" - - # Disable warning, host and port can't have spaces - # shellcheck disable=SC2086 - while [ ! "$(pg_isready --host ${host} --port ${port})" ]; do - - if [ $attempt_num -eq $max_attempts ]; then - echo "Unable to connect to database." - exit 1 - else - echo "Attempt $attempt_num failed! Trying again in 5 seconds..." - fi - - attempt_num=$(("$attempt_num" + 1)) - sleep 5 - done - echo "Connected to PostgreSQL" -} - -wait_for_mariadb() { - echo "Waiting for MariaDB to start..." - - local -r host="${PAPERLESS_DBHOST:=localhost}" - local -r port="${PAPERLESS_DBPORT:=3306}" - - local attempt_num=1 - local -r max_attempts=5 - - # Disable warning, host and port can't have spaces - # shellcheck disable=SC2086 - while ! true > /dev/tcp/$host/$port; do - - if [ $attempt_num -eq $max_attempts ]; then - echo "Unable to connect to database." - exit 1 - else - echo "Attempt $attempt_num failed! Trying again in 5 seconds..." - - fi - - attempt_num=$(("$attempt_num" + 1)) - sleep 5 - done - echo "Connected to MariaDB" -} - -wait_for_redis() { - # We use a Python script to send the Redis ping - # instead of installing redis-tools just for 1 thing - if ! python3 /sbin/wait-for-redis.py; then - exit 1 - fi -} - -migrations() { - ( - # flock is in place to prevent multiple containers from doing migrations - # simultaneously. This also ensures that the db is ready when the command - # of the current container starts. - flock 200 - echo "Apply database migrations..." - python3 manage.py migrate --skip-checks --no-input - ) 200>"${DATA_DIR}/migration_lock" -} - -django_checks() { - # Explicitly run the Django system checks - echo "Running Django checks" - python3 manage.py check -} - -search_index() { - - local -r index_version=9 - local -r index_version_file=${DATA_DIR}/.index_version - - if [[ (! -f "${index_version_file}") || $(<"${index_version_file}") != "$index_version" ]]; then - echo "Search index out of date. Updating..." - python3 manage.py document_index reindex --no-progress-bar - echo ${index_version} | tee "${index_version_file}" >/dev/null - fi -} - -superuser() { - if [[ -n "${PAPERLESS_ADMIN_USER}" ]]; then - python3 manage.py manage_superuser - fi -} - -do_work() { - if [[ "${PAPERLESS_DBENGINE}" == "mariadb" ]]; then - wait_for_mariadb - elif [[ -n "${PAPERLESS_DBHOST}" ]]; then - wait_for_postgres - fi - - wait_for_redis - - migrations - - django_checks - - search_index - - superuser - -} - -do_work diff --git a/docker/env-from-file.sh b/docker/env-from-file.sh deleted file mode 100644 index 41f51d065..000000000 --- a/docker/env-from-file.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash - -# Scans the environment variables for those with the suffix _FILE -# When located, checks the file exists, and exports the contents -# of the file as the same name, minus the suffix -# This allows the use of Docker secrets or mounted files -# to fill in any of the settings configurable via environment -# variables - -set -eu - -for line in $(printenv) -do - # Extract the name of the environment variable - env_name=${line%%=*} - # Check if it starts with "PAPERLESS_" and ends in "_FILE" - if [[ ${env_name} == PAPERLESS_*_FILE ]]; then - # This should have been named different.. - if [[ ${env_name} == "PAPERLESS_OCR_SKIP_ARCHIVE_FILE" || ${env_name} == "PAPERLESS_MODEL_FILE" ]]; then - continue - fi - # Extract the value of the environment - env_value=${line#*=} - - # Check the file exists - if [[ -f ${env_value} ]]; then - - # Trim off the _FILE suffix - non_file_env_name=${env_name%"_FILE"} - echo "Setting ${non_file_env_name} from file" - - # Reads the value from th file - val="$(< "${!env_name}")" - - # Sets the normal name to the read file contents - export "${non_file_env_name}"="${val}" - - else - echo "File ${env_value} referenced by ${env_name} doesn't exist" - fi - fi -done diff --git a/docker/flower-conditional.sh b/docker/flower-conditional.sh deleted file mode 100644 index f8719e0fd..000000000 --- a/docker/flower-conditional.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -echo "Checking if we should start flower..." - -if [[ -n "${PAPERLESS_ENABLE_FLOWER}" ]]; then - # Small delay to allow celery to be up first - echo "Starting flower in 5s" - sleep 5 - celery --app paperless flower --conf=/usr/src/paperless/src/paperless/flowerconfig.py -else - echo "Not starting flower" -fi diff --git a/docker/init-flow.drawio.png b/docker/init-flow.drawio.png new file mode 100644 index 000000000..2e19ac16a Binary files /dev/null and b/docker/init-flow.drawio.png differ diff --git a/docker/install_management_commands.sh b/docker/install_management_commands.sh index 37c17058a..c7c65bfbb 100755 --- a/docker/install_management_commands.sh +++ b/docker/install_management_commands.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash +# Run this script to generate the management commands again (for example if a new command is create or the template is updated) + set -eu for command in decrypt_documents \ @@ -19,6 +21,6 @@ for command in decrypt_documents \ prune_audit_logs; do echo "installing $command..." - sed "s/management_command/$command/g" management_script.sh > /usr/local/bin/$command - chmod +x /usr/local/bin/$command + sed "s/management_command/$command/g" management_script.sh >"$PWD/rootfs/usr/local/bin/$command" + chmod +x "$PWD/rootfs/usr/local/bin/$command" done diff --git a/docker/management_script.sh b/docker/management_script.sh index 996435745..1fa31c372 100755 --- a/docker/management_script.sh +++ b/docker/management_script.sh @@ -1,17 +1,13 @@ -#!/usr/bin/env bash +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash set -e -cd /usr/src/paperless/src/ -# This ensures environment is setup -# shellcheck disable=SC1091 -source /sbin/env-from-file.sh +cd "${PAPERLESS_SRC_DIR}" -if [[ $(id -u) == 0 ]] ; -then - gosu paperless python3 manage.py management_command "$@" -elif [[ $(id -un) == "paperless" ]] ; -then +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py management_command "$@" +elif [[ $(id -un) == "paperless" ]]; then python3 manage.py management_command "$@" else echo "Unknown user." diff --git a/docker/paperless_cmd.sh b/docker/paperless_cmd.sh deleted file mode 100755 index afedb1599..000000000 --- a/docker/paperless_cmd.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -SUPERVISORD_WORKING_DIR="${PAPERLESS_SUPERVISORD_WORKING_DIR:-$PWD}" -rootless_args=() -if [ "$(id -u)" == "$(id -u paperless)" ]; then - rootless_args=( - --user - paperless - --logfile - "${SUPERVISORD_WORKING_DIR}/supervisord.log" - --pidfile - "${SUPERVISORD_WORKING_DIR}/supervisord.pid" - ) -fi - -exec /usr/local/bin/supervisord -c /etc/supervisord.conf "${rootless_args[@]}" diff --git a/docker/imagemagick-policy.xml b/docker/rootfs/etc/ImageMagick-6/paperless-policy.xml similarity index 100% rename from docker/imagemagick-policy.xml rename to docker/rootfs/etc/ImageMagick-6/paperless-policy.xml diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-custom-init b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-custom-init new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-env-file b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-env-file new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-folders b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-folders new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-migrations b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-migrations new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-modify-user b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-modify-user new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-search-index b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-search-index new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-start b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-start new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-superuser b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-superuser new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-system-checks b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-system-checks new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-tesseract-langs b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-tesseract-langs new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-wait-for-db b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-wait-for-db new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-wait-for-redis b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/dependencies.d/init-wait-for-redis new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/run new file mode 100755 index 000000000..83c83df9d --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/run @@ -0,0 +1,8 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash +declare -r log_prefix="[init-complete]" +declare -r end_time=$(date +%s) +declare -r start_time=${PAPERLESS_START_TIME_S} + +echo "${log_prefix} paperless-ngx docker container init completed in $(($end_time-$start_time)) seconds" +echo "${log_prefix} Starting services" diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/type new file mode 100644 index 000000000..bdd22a185 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/up new file mode 100644 index 000000000..9fb31e420 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-complete/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-complete/run diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-search-index b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-search-index new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-system-checks b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-system-checks new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-tesseract-langs b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-tesseract-langs new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-wait-for-db b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-wait-for-db new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-wait-for-redis b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/dependencies.d/init-wait-for-redis new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/run new file mode 100755 index 000000000..50cc4c241 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/run @@ -0,0 +1,44 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +declare -r log_prefix="[custom-init]" + +# Mostly borrowed from the LinuxServer.io base image +# https://github.com/linuxserver/docker-baseimage-ubuntu/tree/bionic/root/etc/cont-init.d +declare -r custom_script_dir="/custom-cont-init.d" + +# Tamper checking. +# Don't run files which are owned by anyone except root +# Don't run files which are writeable by others +if [ -d "${custom_script_dir}" ]; then + if [ -n "$(/usr/bin/find "${custom_script_dir}" -maxdepth 1 ! -user root)" ]; then + echo "${log_prefix} **** Potential tampering with custom scripts detected ****" + echo "${log_prefix} **** The folder '${custom_script_dir}' must be owned by root ****" + exit 0 + fi + if [ -n "$(/usr/bin/find "${custom_script_dir}" -maxdepth 1 -perm -o+w)" ]; then + echo "${log_prefix} **** The folder '${custom_script_dir}' or some of contents have write permissions for others, which is a security risk. ****" + echo "${log_prefix} **** Please review the permissions and their contents to make sure they are owned by root, and can only be modified by root. ****" + exit 0 + fi + + # Make sure custom init directory has files in it + if [ -n "$(/bin/ls --almost-all "${custom_script_dir}" 2>/dev/null)" ]; then + echo "${log_prefix} files found in ${custom_script_dir} executing" + # Loop over files in the directory + for SCRIPT in "${custom_script_dir}"/*; do + NAME="$(basename "${SCRIPT}")" + if [ -f "${SCRIPT}" ]; then + echo "${log_prefix} ${NAME}: executing..." + /command/with-contenv /bin/bash "${SCRIPT}" + echo "${log_prefix} ${NAME}: exited $?" + elif [ ! -f "${SCRIPT}" ]; then + echo "${log_prefix} ${NAME}: is not a file" + fi + done + else + echo "${log_prefix} no custom files found exiting..." + fi +else + echo "${log_prefix} ${custom_script_dir} doesn't exist, nothing to do" +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/type new file mode 100644 index 000000000..bdd22a185 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/up new file mode 100644 index 000000000..69a261a39 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-custom-init/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-custom-init/run diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/dependencies.d/init-start b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/dependencies.d/init-start new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/run new file mode 100755 index 000000000..08b7635f8 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/run @@ -0,0 +1,30 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +declare -r log_prefix="[env-init]" + +echo "${log_prefix} Checking for environment from files" + +if find /run/s6/container_environment/*"_FILE" -maxdepth 1 > /dev/null 2>&1; then + for FILENAME in /run/s6/container_environment/*; do + if [[ "${FILENAME##*/}" == PAPERLESS_*_FILE ]]; then + # This should have been named different.. + if [[ ${FILENAME} == "PAPERLESS_OCR_SKIP_ARCHIVE_FILE" || ${FILENAME} == "PAPERLESS_MODEL_FILE" ]]; then + continue + fi + SECRETFILE=$(cat "${FILENAME}") + # Check the file exists + if [[ -f ${SECRETFILE} ]]; then + # Trim off trailing _FILE + FILESTRIP=${FILENAME//_FILE/} + # Set environment variable + cat "${SECRETFILE}" > "${FILESTRIP}" + echo "${log_prefix} ${FILESTRIP##*/} set from ${FILENAME##*/}" + else + echo "${log_prefix} cannot find secret in ${FILENAME##*/}" + fi + fi + done +else + echo "${log_prefix} No *_FILE environment found" +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/type new file mode 100644 index 000000000..bdd22a185 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/up new file mode 100644 index 000000000..0bb4c22fc --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-env-file/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-env-file/run diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/dependencies.d/init-modify-user b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/dependencies.d/init-modify-user new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/run new file mode 100755 index 000000000..5f731ceae --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/run @@ -0,0 +1,33 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +declare -r log_prefix="[init-folders]" + +declare -r export_dir="/usr/src/paperless/export" +declare -r data_dir="${PAPERLESS_DATA_DIR:-/usr/src/paperless/data}" +declare -r media_root_dir="${PAPERLESS_MEDIA_ROOT:-/usr/src/paperless/media}" +declare -r consume_dir="${PAPERLESS_CONSUMPTION_DIR:-/usr/src/paperless/consume}" +declare -r tmp_dir="${PAPERLESS_SCRATCH_DIR:=/tmp/paperless}" + +echo "${log_prefix} Checking for folder existence" + +for dir in \ + "${export_dir}" \ + "${data_dir}" "${data_dir}/index" \ + "${media_root_dir}" "${media_root_dir}/documents" "${media_root_dir}/documents/originals" "${media_root_dir}/documents/thumbnails" \ + "${consume_dir}" \ + "${tmp_dir}"; do + if [[ ! -d "${dir}" ]]; then + mkdir --parents --verbose "${dir}" + fi +done + +echo "${log_prefix} Adjusting file and folder permissions" +for dir in \ + "${export_dir}" \ + "${data_dir}" \ + "${media_root_dir}" \ + "${consume_dir}" \ + "${tmp_dir}"; do + find "${dir}" -not \( -user paperless -and -group paperless \) -exec chown --changes paperless:paperless {} + +done diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/type new file mode 100644 index 000000000..bdd22a185 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/up new file mode 100644 index 000000000..0fb7dc7de --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-folders/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-folders/run diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/dependencies.d/init-folders b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/dependencies.d/init-folders new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/dependencies.d/init-wait-for-db b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/dependencies.d/init-wait-for-db new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/run new file mode 100755 index 000000000..db0dc26d3 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/run @@ -0,0 +1,20 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash +declare -r log_prefix="[init-migrations]" +declare -r data_dir="${PAPERLESS_DATA_DIR:-/usr/src/paperless/data}" + +( + # flock is in place to prevent multiple containers from doing migrations + # simultaneously. This also ensures that the db is ready when the command + # of the current container starts. + flock 200 + echo "${log_prefix} Apply database migrations..." + cd "${PAPERLESS_SRC_DIR}" + + if [[ -n "${USER_IS_NON_ROOT}" ]]; then + exec python3 manage.py migrate --skip-checks --no-input + else + exec s6-setuidgid paperless python3 manage.py migrate --skip-checks --no-input + fi + +) 200>"${data_dir}/migration_lock" diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/type new file mode 100644 index 000000000..bdd22a185 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/up new file mode 100644 index 000000000..7c4cbcf6f --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-migrations/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-migrations/run diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/dependencies.d/init-env-file b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/dependencies.d/init-env-file new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/run new file mode 100755 index 000000000..aa617355d --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/run @@ -0,0 +1,22 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash +declare -r log_prefix="[init-user]" + +declare -r usermap_original_uid=$(id -u paperless) +declare -r usermap_original_gid=$(id -g paperless) +declare -r usermap_new_uid=${USERMAP_UID:-$usermap_original_uid} +declare -r usermap_new_gid=${USERMAP_GID:-${usermap_original_gid:-$usermap_new_uid}} + +if [[ ${usermap_new_uid} != "${usermap_original_uid}" ]]; then + echo "${log_prefix} Mapping UID for paperless to $usermap_new_uid" + usermod --non-unique --uid "${usermap_new_uid}" paperless +else + echo "${log_prefix} No UID changes for paperless" +fi + +if [[ ${usermap_new_gid} != "${usermap_original_gid}" ]]; then + echo "${log_prefix} Mapping GID for paperless to $usermap_new_gid" + groupmod --non-unique --gid "${usermap_new_gid}" paperless +else + echo "${log_prefix} No GID changes for paperless" +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/type new file mode 100644 index 000000000..bdd22a185 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/up new file mode 100644 index 000000000..4c22ce857 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-modify-user/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-modify-user/run diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/dependencies.d/init-migrations b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/dependencies.d/init-migrations new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/run new file mode 100755 index 000000000..2208faf67 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/run @@ -0,0 +1,28 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +declare -r log_prefix="[init-index]" + +declare -r index_version=9 +declare -r data_dir="${PAPERLESS_DATA_DIR:-/usr/src/paperless/data}" +declare -r index_version_file="${data_dir}/.index_version" + +update_index () { + echo "${log_prefix} Search index out of date. Updating..." + cd "${PAPERLESS_SRC_DIR}" + if [[ -n "${USER_IS_NON_ROOT}" ]]; then + python3 manage.py document_index reindex --no-progress-bar + echo ${index_version} | tee "${index_version_file}" > /dev/null + else + s6-setuidgid paperless python3 manage.py document_index reindex --no-progress-bar + echo ${index_version} | s6-setuidgid paperless tee "${index_version_file}" > /dev/null + fi +} + +if [[ (! -f "${index_version_file}") ]]; then + echo "${log_prefix} No index version file found" + update_index +elif [[ $(<"${index_version_file}") != "$index_version" ]]; then + echo "${log_prefix} index version updated" + update_index +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/type new file mode 100644 index 000000000..bdd22a185 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/up new file mode 100644 index 000000000..372763add --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-search-index/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-search-index/run diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/dependencies.d/base b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/dependencies.d/base new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/run new file mode 100755 index 000000000..b6a26fae7 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/run @@ -0,0 +1,19 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +declare -r log_prefix="[init-start]" + +echo "${log_prefix} paperless-ngx docker container starting..." + +# Set some directories into environment for other steps to access via environment +# Sort of like variables for later +printf "/usr/src/paperless/src" > /var/run/s6/container_environment/PAPERLESS_SRC_DIR +echo $(date +%s) > /var/run/s6/container_environment/PAPERLESS_START_TIME_S + +# Check if we're starting as a non-root user +if [ $(id -u) == $(id -u paperless) ]; then + printf "true" > /var/run/s6/container_environment/USER_IS_NON_ROOT + echo "${log_prefix} paperless-ngx docker container running under a user" +else + echo "${log_prefix} paperless-ngx docker container starting init as root" +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/type new file mode 100644 index 000000000..bdd22a185 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/up new file mode 100644 index 000000000..3a6a26d81 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-start/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-start/run diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/dependencies.d/init-migrations b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/dependencies.d/init-migrations new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/run new file mode 100755 index 000000000..625185181 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/run @@ -0,0 +1,20 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +declare -r log_prefix="[init-superuser]" + +if [[ -n "${PAPERLESS_ADMIN_USER}" ]]; then + echo "${log_prefix} Creating superuser..." + cd "${PAPERLESS_SRC_DIR}" + + if [[ -n "${USER_IS_NON_ROOT}" ]]; then + python3 manage.py manage_superuser + else + s6-setuidgid paperless python3 manage.py manage_superuser + fi + + echo "${log_prefix} Superuser creation done" + +else + echo "${log_prefix} Not creating superuser" +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/type new file mode 100644 index 000000000..bdd22a185 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/up new file mode 100644 index 000000000..055f10a50 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-superuser/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-superuser/run diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/dependencies.d/init-superuser b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/dependencies.d/init-superuser new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/dependencies.d/init-tesseract-langs b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/dependencies.d/init-tesseract-langs new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/run new file mode 100755 index 000000000..d024765cc --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/run @@ -0,0 +1,15 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +declare -r log_prefix="[init-checks]" + +# Explicitly run the Django system checks +echo "${log_prefix} Running Django checks" + +cd "${PAPERLESS_SRC_DIR}" + +if [[ -n "${USER_IS_NON_ROOT}" ]]; then + python3 manage.py check +else + s6-setuidgid paperless python3 manage.py check +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/type new file mode 100644 index 000000000..bdd22a185 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/up new file mode 100644 index 000000000..7403acc2f --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-system-checks/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-system-checks/run diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/dependencies.d/init-env-file b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/dependencies.d/init-env-file new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/run new file mode 100755 index 000000000..7ab464583 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/run @@ -0,0 +1,65 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +declare -r log_prefix="[init-tesseract-langs]" + +install_languages() { + echo "Installing languages..." + + read -ra langs <<<"$1" + + # Check that it is not empty + if [ ${#langs[@]} -eq 0 ]; then + return + fi + + # Build list of packages to install + to_install=() + for lang in "${langs[@]}"; do + pkg="tesseract-ocr-$lang" + + if dpkg --status "$pkg" &>/dev/null; then + echo "${log_prefix} Package $pkg already installed!" + continue + else + to_install+=("$pkg") + fi + done + + # Use apt only when we install packages + if [ ${#to_install[@]} -gt 0 ]; then + + # Warn the user if they're not root, but try anyway + if [[ -n "${USER_IS_NON_ROOT}" ]]; then + echo "${log_prefix} ERROR: Unable to install language ${pkg} as non-root, startup may fail" + fi + + apt-get --quiet update &>/dev/null + + for pkg in "${to_install[@]}"; do + if ! apt-cache --quiet show "$pkg" &>/dev/null; then + echo "${log_prefix} Skipped $pkg: Package not found! :(" + continue + fi + echo "${log_prefix} Installing package $pkg..." + if ! apt-get --quiet --assume-yes install "$pkg" &>/dev/null; then + echo "${log_prefix} Could not install $pkg" + exit 1 + else + echo "${log_prefix} Installed $pkg" + fi + done + + fi +} + +echo "${log_prefix} Checking if additional teseract languages needed" + +# Install additional languages if specified +if [[ -n "$PAPERLESS_OCR_LANGUAGES" ]]; then + + install_languages "$PAPERLESS_OCR_LANGUAGES" + echo "${log_prefix} Additional packages installed" +else + echo "${log_prefix} No additional installs requested" +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/type new file mode 100644 index 000000000..bdd22a185 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/up new file mode 100644 index 000000000..16ef2f4d1 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-tesseract-langs/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-tesseract-langs/run diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/dependencies.d/init-env-file b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/dependencies.d/init-env-file new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/run new file mode 100755 index 000000000..ede8a654a --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/run @@ -0,0 +1,70 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +declare -r log_prefix="[init-db-wait]" + +wait_for_postgres() { + local attempt_num=1 + local -r max_attempts=5 + + echo "${log_prefix} Waiting for PostgreSQL to start..." + + local -r host="${PAPERLESS_DBHOST:-localhost}" + local -r port="${PAPERLESS_DBPORT:-5432}" + local -r user="${PAPERLESS_DBUSER:-paperless}" + + # Disable warning, host and port can't have spaces + # shellcheck disable=SC2086 + while [ ! "$(pg_isready -h ${host} -p ${port} --username ${user})" ]; do + + if [ $attempt_num -eq $max_attempts ]; then + echo "${log_prefix} Unable to connect to database." + exit 1 + else + echo "${log_prefix} Attempt $attempt_num failed! Trying again in 5 seconds..." + fi + + attempt_num=$(("$attempt_num" + 1)) + sleep 5 + done + # Extra in case this is a first start + sleep 5 + echo "Connected to PostgreSQL" +} + +wait_for_mariadb() { + echo "${log_prefix} Waiting for MariaDB to start..." + + local -r host="${PAPERLESS_DBHOST:=localhost}" + local -r port="${PAPERLESS_DBPORT:=3306}" + + local attempt_num=1 + local -r max_attempts=5 + + # Disable warning, host and port can't have spaces + # shellcheck disable=SC2086 + while ! true > /dev/tcp/$host/$port; do + + if [ $attempt_num -eq $max_attempts ]; then + echo "${log_prefix} Unable to connect to database." + exit 1 + else + echo "${log_prefix} Attempt $attempt_num failed! Trying again in 5 seconds..." + + fi + + attempt_num=$(("$attempt_num" + 1)) + sleep 5 + done + echo "Connected to MariaDB" +} + +if [[ "${PAPERLESS_DBENGINE}" == "mariadb" ]]; then + echo "${log_prefix} Waiting for MariaDB to report ready" + wait_for_mariadb +elif [[ -n "${PAPERLESS_DBHOST}" ]]; then + echo "${log_prefix} Waiting for postgresql to report ready" + wait_for_postgres +fi + + echo "${log_prefix} Database is ready" diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/type new file mode 100644 index 000000000..bdd22a185 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/up new file mode 100644 index 000000000..6cbecf5f4 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-db/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-wait-for-db/run diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/dependencies.d/init-env-file b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/dependencies.d/init-env-file new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/run new file mode 100755 index 000000000..407536aa2 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/run @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +declare -r log_prefix="[init-redis-wait]" + +echo "${log_prefix} Waiting for Redis to report ready" + +# We use a Python script to send the Redis ping +# instead of installing redis-tools just for 1 thing +if ! python3 /usr/local/bin/wait-for-redis.py; then + exit 1 +else + echo "${log_prefix} Redis ready" +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/type new file mode 100644 index 000000000..bdd22a185 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/type @@ -0,0 +1 @@ +oneshot diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/up b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/up new file mode 100644 index 000000000..8a5fa1601 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/init-wait-for-redis/up @@ -0,0 +1 @@ +/etc/s6-overlay/s6-rc.d/init-wait-for-redis/run diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/dependencies.d/init-complete b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/dependencies.d/init-complete new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/run new file mode 100755 index 000000000..3e1c0472b --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/run @@ -0,0 +1,10 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +cd ${PAPERLESS_SRC_DIR} + +if [[ -n "${USER_IS_NON_ROOT}" ]]; then + exec python3 manage.py document_consumer +else + exec s6-setuidgid paperless python3 manage.py document_consumer +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/type new file mode 100644 index 000000000..5883cff0c --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-consumer/type @@ -0,0 +1 @@ +longrun diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/init-complete b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/init-complete new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/svc-scheduler b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/svc-scheduler new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/svc-worker b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/dependencies.d/svc-worker new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/run new file mode 100755 index 000000000..a3e4b6a3e --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/run @@ -0,0 +1,24 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +declare -r log_prefix="[svc-flower]" + +echo "${log_prefix} Checking if we should start flower..." + +if [[ -n "${PAPERLESS_ENABLE_FLOWER}" ]]; then + # Small delay to allow celery to be up first + echo "${log_prefix} Starting flower in 5s" + sleep 5 + cd ${PAPERLESS_SRC_DIR} + + if [[ -n "${USER_IS_NON_ROOT}" ]]; then + exec /usr/local/bin/celery --app paperless flower --conf=${PAPERLESS_SRC_DIR}/paperless/flowerconfig.py + else + exec s6-setuidgid paperless /usr/local/bin/celery --app paperless flower --conf=${PAPERLESS_SRC_DIR}/paperless/flowerconfig.py + fi + +else + echo "${log_prefix} Not starting flower" + # https://skarnet.org/software/s6/s6-svc.html + s6-svc -Od . +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/type new file mode 100644 index 000000000..5883cff0c --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-flower/type @@ -0,0 +1 @@ +longrun diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/dependencies.d/init-complete b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/dependencies.d/init-complete new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/dependencies.d/svc-worker b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/dependencies.d/svc-worker new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/run new file mode 100755 index 000000000..396a4d2e0 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/run @@ -0,0 +1,10 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +cd ${PAPERLESS_SRC_DIR} + +if [[ -n "${USER_IS_NON_ROOT}" ]]; then + exec /usr/local/bin/celery --app paperless beat --loglevel INFO +else + exec s6-setuidgid paperless /usr/local/bin/celery --app paperless beat --loglevel INFO +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/type new file mode 100644 index 000000000..5883cff0c --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-scheduler/type @@ -0,0 +1 @@ +longrun diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/init-complete b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/init-complete new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-consumer b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-consumer new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-scheduler b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-scheduler new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-worker b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/dependencies.d/svc-worker new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/run new file mode 100755 index 000000000..423b17531 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/run @@ -0,0 +1,10 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +cd ${PAPERLESS_SRC_DIR} + +if [[ -n "${USER_IS_NON_ROOT}" ]]; then + exec /usr/local/bin/gunicorn -c /usr/src/paperless/gunicorn.conf.py paperless.asgi:application +else + exec s6-setuidgid paperless /usr/local/bin/gunicorn -c /usr/src/paperless/gunicorn.conf.py paperless.asgi:application +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/type new file mode 100644 index 000000000..5883cff0c --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-webserver/type @@ -0,0 +1 @@ +longrun diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/dependencies.d/init-complete b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/dependencies.d/init-complete new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/run b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/run new file mode 100755 index 000000000..0bf833c56 --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/run @@ -0,0 +1,10 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +cd ${PAPERLESS_SRC_DIR} + +if [[ -n "${USER_IS_NON_ROOT}" ]]; then + exec /usr/local/bin/celery --app paperless worker --loglevel INFO --without-mingle --without-gossip +else + exec s6-setuidgid paperless /usr/local/bin/celery --app paperless worker --loglevel INFO --without-mingle --without-gossip +fi diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/type b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/type new file mode 100644 index 000000000..5883cff0c --- /dev/null +++ b/docker/rootfs/etc/s6-overlay/s6-rc.d/svc-worker/type @@ -0,0 +1 @@ +longrun diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/init-complete b/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/init-complete new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-consumer b/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-consumer new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-flower b/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-flower new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-scheduler b/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-scheduler new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-webserver b/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-webserver new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-worker b/docker/rootfs/etc/s6-overlay/s6-rc.d/user/contents.d/svc-worker new file mode 100644 index 000000000..e69de29bb diff --git a/docker/rootfs/usr/local/bin/convert_mariadb_uuid b/docker/rootfs/usr/local/bin/convert_mariadb_uuid new file mode 100755 index 000000000..806a98f3b --- /dev/null +++ b/docker/rootfs/usr/local/bin/convert_mariadb_uuid @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py convert_mariadb_uuid "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py convert_mariadb_uuid "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/decrypt_documents b/docker/rootfs/usr/local/bin/decrypt_documents new file mode 100755 index 000000000..4da1549ee --- /dev/null +++ b/docker/rootfs/usr/local/bin/decrypt_documents @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py decrypt_documents "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py decrypt_documents "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/document_archiver b/docker/rootfs/usr/local/bin/document_archiver new file mode 100755 index 000000000..383acfcc6 --- /dev/null +++ b/docker/rootfs/usr/local/bin/document_archiver @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py document_archiver "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py document_archiver "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/document_create_classifier b/docker/rootfs/usr/local/bin/document_create_classifier new file mode 100755 index 000000000..72dc33d6f --- /dev/null +++ b/docker/rootfs/usr/local/bin/document_create_classifier @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py document_create_classifier "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py document_create_classifier "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/document_exporter b/docker/rootfs/usr/local/bin/document_exporter new file mode 100755 index 000000000..7f48215d7 --- /dev/null +++ b/docker/rootfs/usr/local/bin/document_exporter @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py document_exporter "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py document_exporter "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/document_fuzzy_match b/docker/rootfs/usr/local/bin/document_fuzzy_match new file mode 100755 index 000000000..5b9548557 --- /dev/null +++ b/docker/rootfs/usr/local/bin/document_fuzzy_match @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py document_fuzzy_match "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py document_fuzzy_match "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/document_importer b/docker/rootfs/usr/local/bin/document_importer new file mode 100755 index 000000000..2286e89f7 --- /dev/null +++ b/docker/rootfs/usr/local/bin/document_importer @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py document_importer "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py document_importer "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/document_index b/docker/rootfs/usr/local/bin/document_index new file mode 100755 index 000000000..2d518b5c5 --- /dev/null +++ b/docker/rootfs/usr/local/bin/document_index @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py document_index "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py document_index "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/document_renamer b/docker/rootfs/usr/local/bin/document_renamer new file mode 100755 index 000000000..326317a73 --- /dev/null +++ b/docker/rootfs/usr/local/bin/document_renamer @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py document_renamer "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py document_renamer "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/document_retagger b/docker/rootfs/usr/local/bin/document_retagger new file mode 100755 index 000000000..3bab3e790 --- /dev/null +++ b/docker/rootfs/usr/local/bin/document_retagger @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py document_retagger "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py document_retagger "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/document_sanity_checker b/docker/rootfs/usr/local/bin/document_sanity_checker new file mode 100755 index 000000000..5c0c29ef2 --- /dev/null +++ b/docker/rootfs/usr/local/bin/document_sanity_checker @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py document_sanity_checker "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py document_sanity_checker "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/document_thumbnails b/docker/rootfs/usr/local/bin/document_thumbnails new file mode 100755 index 000000000..c1000c31a --- /dev/null +++ b/docker/rootfs/usr/local/bin/document_thumbnails @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py document_thumbnails "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py document_thumbnails "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/mail_fetcher b/docker/rootfs/usr/local/bin/mail_fetcher new file mode 100755 index 000000000..2ae1d1dfb --- /dev/null +++ b/docker/rootfs/usr/local/bin/mail_fetcher @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py mail_fetcher "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py mail_fetcher "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/manage_superuser b/docker/rootfs/usr/local/bin/manage_superuser new file mode 100755 index 000000000..9f7f37ecf --- /dev/null +++ b/docker/rootfs/usr/local/bin/manage_superuser @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py manage_superuser "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py manage_superuser "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/prune_audit_logs b/docker/rootfs/usr/local/bin/prune_audit_logs new file mode 100755 index 000000000..b9142e98e --- /dev/null +++ b/docker/rootfs/usr/local/bin/prune_audit_logs @@ -0,0 +1,14 @@ +#!/command/with-contenv /usr/bin/bash +# shellcheck shell=bash + +set -e + +cd "${PAPERLESS_SRC_DIR}" + +if [[ $(id -u) == 0 ]]; then + s6-setuidgid paperless python3 manage.py prune_audit_logs "$@" +elif [[ $(id -un) == "paperless" ]]; then + python3 manage.py prune_audit_logs "$@" +else + echo "Unknown user." +fi diff --git a/docker/rootfs/usr/local/bin/wait-for-redis.py b/docker/rootfs/usr/local/bin/wait-for-redis.py new file mode 100755 index 000000000..9ae4a35a5 --- /dev/null +++ b/docker/rootfs/usr/local/bin/wait-for-redis.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +""" +Simple script which attempts to ping the Redis broker as set in the environment for +a certain number of times, waiting a little bit in between + +""" + +import os +import sys +import time + +import click +from redis import Redis + + +@click.command(context_settings={"show_default": True}) +@click.option( + "--retry-count", + default=5, + type=int, + help="Count of times to retry the Redis connection", +) +@click.option( + "--retry-sleep", + default=5, + type=int, + help="Seconds to wait between Redis connection retries", +) +@click.argument( + "redis_url", + type=str, + envvar="PAPERLESS_REDIS", + default="redis://localhost:6379", +) +def wait(redis_url: str, retry_count: int, retry_sleep: int) -> None: + click.echo("Waiting for Redis...") + + attempt = 0 + with Redis.from_url(url=redis_url) as client: + while attempt < retry_count: + try: + client.ping() + break + except Exception as e: + click.echo( + f"Redis ping #{attempt} failed.\n" + f"Error: {e!s}.\n" + f"Waiting {retry_sleep}s", + ) + time.sleep(retry_sleep) + attempt += 1 + + if attempt >= retry_count: + click.echo( + "Failed to connect to redis using environment variable PAPERLESS_REDIS.", + ) + sys.exit(os.EX_UNAVAILABLE) + else: + click.echo("Connected to Redis broker.") + sys.exit(os.EX_OK) + + +if __name__ == "__main__": + wait() diff --git a/docker/supervisord.conf b/docker/supervisord.conf deleted file mode 100644 index 009760771..000000000 --- a/docker/supervisord.conf +++ /dev/null @@ -1,65 +0,0 @@ -[supervisord] -nodaemon=true ; start in foreground if true; default false -logfile=/var/log/supervisord/supervisord.log ; main log file; default $CWD/supervisord.log -pidfile=/var/run/supervisord/supervisord.pid ; supervisord pidfile; default supervisord.pid -logfile_maxbytes=50MB ; max main logfile bytes b4 rotation; default 50MB -logfile_backups=10 ; # of main logfile backups; 0 means none, default 10 -loglevel=info ; log level; default info; others: debug,warn,trace -user=root - -[program:gunicorn] -command=gunicorn -c /usr/src/paperless/gunicorn.conf.py paperless.asgi:application -user=paperless -priority = 1 -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -environment = HOME="/usr/src/paperless",USER="paperless" - -[program:consumer] -command=python3 manage.py document_consumer -user=paperless -stopsignal=INT -priority = 20 -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -environment = HOME="/usr/src/paperless",USER="paperless" - -[program:celery] - -command = celery --app paperless worker --loglevel INFO --without-mingle --without-gossip -user=paperless -stopasgroup = true -stopwaitsecs = 60 -priority = 5 -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -environment = HOME="/usr/src/paperless",USER="paperless" - -[program:celery-beat] - -command = celery --app paperless beat --loglevel INFO -user=paperless -stopasgroup = true -priority = 10 -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -environment = HOME="/usr/src/paperless",USER="paperless" - -[program:celery-flower] -command = /usr/local/bin/flower-conditional.sh -user = paperless -startsecs = 0 -priority = 40 -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 -environment = HOME="/usr/src/paperless",USER="paperless" diff --git a/docker/wait-for-redis.py b/docker/wait-for-redis.py deleted file mode 100755 index c3e4f1d59..000000000 --- a/docker/wait-for-redis.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python3 -""" -Simple script which attempts to ping the Redis broker as set in the environment for -a certain number of times, waiting a little bit in between - -""" - -import os -import sys -import time -from typing import Final - -from redis import Redis - -if __name__ == "__main__": - MAX_RETRY_COUNT: Final[int] = 5 - RETRY_SLEEP_SECONDS: Final[int] = 5 - - REDIS_URL: Final[str] = os.getenv("PAPERLESS_REDIS", "redis://localhost:6379") - - print("Waiting for Redis...", flush=True) - - attempt = 0 - with Redis.from_url(url=REDIS_URL) as client: - while attempt < MAX_RETRY_COUNT: - try: - client.ping() - break - except Exception as e: - print( - f"Redis ping #{attempt} failed.\n" - f"Error: {e!s}.\n" - f"Waiting {RETRY_SLEEP_SECONDS}s", - flush=True, - ) - time.sleep(RETRY_SLEEP_SECONDS) - attempt += 1 - - if attempt >= MAX_RETRY_COUNT: - print("Failed to connect to redis using environment variable PAPERLESS_REDIS.") - sys.exit(os.EX_UNAVAILABLE) - else: - print("Connected to Redis broker.") - sys.exit(os.EX_OK) diff --git a/docs/configuration.md b/docs/configuration.md index b81c10b0d..3724c792d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1596,9 +1596,11 @@ started by the container. #### [`PAPERLESS_SUPERVISORD_WORKING_DIR=`](#PAPERLESS_SUPERVISORD_WORKING_DIR) {#PAPERLESS_SUPERVISORD_WORKING_DIR} -: If this environment variable is defined, the `supervisord.log` and `supervisord.pid` file will be created under the specified path in `PAPERLESS_SUPERVISORD_WORKING_DIR`. Setting `PAPERLESS_SUPERVISORD_WORKING_DIR=/tmp` and `PYTHONPYCACHEPREFIX=/tmp/pycache` would allow paperless to work on a read-only filesystem. +!!! warning - Please take note that the `PAPERLESS_DATA_DIR` and `PAPERLESS_MEDIA_ROOT` paths still have to be writable, just like the `PAPERLESS_SUPERVISORD_WORKING_DIR`. The can be archived by using bind or volume mounts. Only works in the container is run as user *paperless* + This option is deprecated and has no effect. For read only file system support, + see [S6_READ_ONLY_ROOT](https://github.com/just-containers/s6-overlay#customizing-s6-overlay-behaviour) + from s6-overlay. ## Frontend Settings