mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-11-03 19:17:13 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			408 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			408 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env bash
 | 
						|
 | 
						|
ask() {
 | 
						|
	while true ; do
 | 
						|
		if [[ -z $3 ]] ; then
 | 
						|
			read -r -p "$1 [$2]: " result
 | 
						|
		else
 | 
						|
			read -r -p "$1 ($3) [$2]: " result
 | 
						|
		fi
 | 
						|
		if [[ -z $result ]]; then
 | 
						|
			ask_result=$2
 | 
						|
			return
 | 
						|
		fi
 | 
						|
		array=$3
 | 
						|
		if [[ -z $3 || " ${array[*]} " =~ ${result} ]]; then
 | 
						|
			ask_result=$result
 | 
						|
			return
 | 
						|
		else
 | 
						|
			echo "Invalid option: $result"
 | 
						|
		fi
 | 
						|
	done
 | 
						|
}
 | 
						|
 | 
						|
ask_docker_folder() {
 | 
						|
	while true ; do
 | 
						|
 | 
						|
		read -r -p "$1 [$2]: " result
 | 
						|
 | 
						|
		if [[ -z $result ]]; then
 | 
						|
			ask_result=$2
 | 
						|
			return
 | 
						|
		fi
 | 
						|
 | 
						|
		if [[ $result == /* || $result == ./* ]]; then
 | 
						|
			ask_result=$result
 | 
						|
			return
 | 
						|
		else
 | 
						|
			echo "Invalid folder: $result"
 | 
						|
		fi
 | 
						|
 | 
						|
	done
 | 
						|
}
 | 
						|
 | 
						|
if [[ $(id -u) == "0" ]] ; then
 | 
						|
	echo "Do not run this script as root."
 | 
						|
	exit 1
 | 
						|
fi
 | 
						|
 | 
						|
if ! command -v wget &> /dev/null ; then
 | 
						|
	echo "wget executable not found. Is wget installed?"
 | 
						|
	exit 1
 | 
						|
fi
 | 
						|
 | 
						|
if ! command -v docker &> /dev/null ; then
 | 
						|
	echo "docker executable not found. Is Docker installed?"
 | 
						|
	exit 1
 | 
						|
fi
 | 
						|
 | 
						|
if ! docker compose &> /dev/null ; then
 | 
						|
	echo "docker compose plugin not found. Is Docker Compose installed?"
 | 
						|
	exit 1
 | 
						|
fi
 | 
						|
 | 
						|
# Check if user has permissions to run Docker by trying to get the status of Docker (docker status).
 | 
						|
# If this fails, the user probably does not have permissions for Docker.
 | 
						|
if ! docker stats --no-stream &> /dev/null ; then
 | 
						|
	echo ""
 | 
						|
	echo "WARN: It look like the current user does not have Docker permissions."
 | 
						|
	echo "WARN: Use 'sudo usermod -aG docker $USER' to assign Docker permissions to the user (may require restarting the shell)."
 | 
						|
	echo ""
 | 
						|
	sleep 3
 | 
						|
fi
 | 
						|
 | 
						|
# Added handling for timezone for busybox based linux, not having timedatectl available (i.e. QNAP QTS)
 | 
						|
# if neither timedatectl nor /etc/TZ is succeeding, defaulting to GMT.
 | 
						|
if  command -v timedatectl &> /dev/null ; then
 | 
						|
	default_time_zone=$(timedatectl show -p Timezone --value)
 | 
						|
elif [ -f /etc/TZ ] && [ -f /etc/tzlist ] ; then
 | 
						|
	TZ=$(cat /etc/TZ)
 | 
						|
	default_time_zone=$(grep -B 1 -m 1 "$TZ" /etc/tzlist | head -1 | cut -f 2 -d =)
 | 
						|
else
 | 
						|
	echo "WARN: unable to detect timezone, defaulting to Etc/UTC"
 | 
						|
	default_time_zone="Etc/UTC"
 | 
						|
fi
 | 
						|
 | 
						|
set -e
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "#############################################"
 | 
						|
echo "###   paperless-ngx docker installation   ###"
 | 
						|
echo "#############################################"
 | 
						|
echo ""
 | 
						|
echo "This script will download, configure and start paperless-ngx."
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "1. Application configuration"
 | 
						|
echo "============================"
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "The URL paperless will be available at. This is required if the"
 | 
						|
echo "installation will be accessible via the web, otherwise can be left blank."
 | 
						|
echo "Example: https://paperless.example.com"
 | 
						|
echo ""
 | 
						|
 | 
						|
ask "URL" ""
 | 
						|
URL=$ask_result
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "The port on which the paperless webserver will listen for incoming"
 | 
						|
echo "connections."
 | 
						|
echo ""
 | 
						|
 | 
						|
ask "Port" "8000"
 | 
						|
PORT=$ask_result
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "Paperless requires you to configure the current time zone correctly."
 | 
						|
echo "Otherwise, the dates of your documents may appear off by one day,"
 | 
						|
echo "depending on where you are on earth."
 | 
						|
echo "Example: Europe/Berlin"
 | 
						|
echo "See here for a list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones"
 | 
						|
echo ""
 | 
						|
 | 
						|
ask "Current time zone" "$default_time_zone"
 | 
						|
TIME_ZONE=$ask_result
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "Database backend: PostgreSQL, MariaDB, and SQLite are available. Use PostgreSQL"
 | 
						|
echo "if unsure. If you're running on a low-power device such as Raspberry"
 | 
						|
echo "Pi, use SQLite to save resources."
 | 
						|
echo ""
 | 
						|
 | 
						|
ask "Database backend" "postgres" "postgres sqlite mariadb"
 | 
						|
DATABASE_BACKEND=$ask_result
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "Paperless is able to use Apache Tika to support Office documents such as"
 | 
						|
echo "Word, Excel, PowerPoint, and LibreOffice equivalents. This feature"
 | 
						|
echo "requires more resources due to the required services."
 | 
						|
echo ""
 | 
						|
 | 
						|
ask "Enable Apache Tika?" "no" "yes no"
 | 
						|
TIKA_ENABLED=$ask_result
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "Specify the default language that most of your documents are written in."
 | 
						|
echo "Use ISO 639-2, (T) variant language codes: "
 | 
						|
echo "https://www.loc.gov/standards/iso639-2/php/code_list.php"
 | 
						|
echo "Common values: eng (English) deu (German) nld (Dutch) fra (French)"
 | 
						|
echo "This can be a combination of multiple languages such as deu+eng"
 | 
						|
echo ""
 | 
						|
 | 
						|
ask "OCR language" "eng"
 | 
						|
OCR_LANGUAGE=$ask_result
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "Specify the user id and group id you wish to run paperless as."
 | 
						|
echo "Paperless will also change ownership on the data, media and consume"
 | 
						|
echo "folder to the specified values, so it's a good idea to supply the user id"
 | 
						|
echo "and group id of your Unix user account."
 | 
						|
echo "If unsure, leave default."
 | 
						|
echo ""
 | 
						|
 | 
						|
ask "User ID" "$(id -u)"
 | 
						|
USERMAP_UID=$ask_result
 | 
						|
 | 
						|
ask "Group ID" "$(id -g)"
 | 
						|
USERMAP_GID=$ask_result
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "2. Folder configuration"
 | 
						|
echo "======================="
 | 
						|
echo ""
 | 
						|
echo "The target folder is used to store the configuration files of "
 | 
						|
echo "paperless. You can move this folder around after installing paperless."
 | 
						|
echo "You will need this folder whenever you want to start, stop, update or "
 | 
						|
echo "maintain your paperless instance."
 | 
						|
echo ""
 | 
						|
 | 
						|
ask "Target folder" "$(pwd)/paperless-ngx"
 | 
						|
TARGET_FOLDER=$ask_result
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "The consume folder is where paperless will search for new documents."
 | 
						|
echo "Point this to a folder where your scanner is able to put your scanned"
 | 
						|
echo "documents."
 | 
						|
echo ""
 | 
						|
echo "CAUTION: You must specify an absolute path starting with / or a relative "
 | 
						|
echo "path starting with ./ here. Examples:"
 | 
						|
echo "  /mnt/consume"
 | 
						|
echo "  ./consume"
 | 
						|
echo ""
 | 
						|
 | 
						|
ask_docker_folder "Consume folder" "$TARGET_FOLDER/consume"
 | 
						|
CONSUME_FOLDER=$ask_result
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "The media folder is where paperless stores your documents."
 | 
						|
echo "Leave empty and docker will manage this folder for you."
 | 
						|
echo "Docker usually stores managed folders in /var/lib/docker/volumes."
 | 
						|
echo ""
 | 
						|
echo "CAUTION: If specified, you must specify an absolute path starting with /"
 | 
						|
echo "or a relative path starting with ./ here."
 | 
						|
echo ""
 | 
						|
 | 
						|
ask_docker_folder "Media folder" ""
 | 
						|
MEDIA_FOLDER=$ask_result
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "The data folder is where paperless stores other data, such as your"
 | 
						|
if [[ "$DATABASE_BACKEND" == "sqlite" ]] ; then
 | 
						|
	echo -n "SQLite database, the "
 | 
						|
fi
 | 
						|
echo "search index and other data."
 | 
						|
echo "As with the media folder, leave empty to have this managed by Docker."
 | 
						|
echo ""
 | 
						|
echo "CAUTION: If specified, you must specify an absolute path starting with /"
 | 
						|
echo "or a relative path starting with ./ here."
 | 
						|
echo ""
 | 
						|
 | 
						|
ask_docker_folder "Data folder" ""
 | 
						|
DATA_FOLDER=$ask_result
 | 
						|
 | 
						|
if [[ "$DATABASE_BACKEND" == "postgres" || "$DATABASE_BACKEND" == "mariadb" ]] ; then
 | 
						|
	echo ""
 | 
						|
	echo "The database folder, where your database stores its data."
 | 
						|
	echo "Leave empty to have this managed by Docker."
 | 
						|
	echo ""
 | 
						|
	echo "CAUTION: If specified, you must specify an absolute path starting with /"
 | 
						|
	echo "or a relative path starting with ./ here."
 | 
						|
	echo ""
 | 
						|
 | 
						|
	ask_docker_folder "Database folder" ""
 | 
						|
	DATABASE_FOLDER=$ask_result
 | 
						|
fi
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "3. Login credentials"
 | 
						|
echo "===================="
 | 
						|
echo ""
 | 
						|
echo "Specify initial login credentials. You can change these later."
 | 
						|
echo "A mail address is required, however it is not used in paperless. You don't"
 | 
						|
echo "need to provide an actual mail address."
 | 
						|
echo ""
 | 
						|
 | 
						|
ask "Paperless username" "$(whoami)"
 | 
						|
USERNAME=$ask_result
 | 
						|
 | 
						|
while true; do
 | 
						|
	read -r -sp "Paperless password: " PASSWORD
 | 
						|
	echo ""
 | 
						|
 | 
						|
	if [[ -z $PASSWORD ]] ; then
 | 
						|
		echo "Password cannot be empty."
 | 
						|
		continue
 | 
						|
	fi
 | 
						|
 | 
						|
	read -r -sp "Paperless password (again): " PASSWORD_REPEAT
 | 
						|
	echo ""
 | 
						|
 | 
						|
	if [[ ! "$PASSWORD" == "$PASSWORD_REPEAT" ]] ; then
 | 
						|
		echo "Passwords did not match"
 | 
						|
	else
 | 
						|
		break
 | 
						|
	fi
 | 
						|
done
 | 
						|
 | 
						|
ask "Email" "$USERNAME@localhost"
 | 
						|
EMAIL=$ask_result
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "Summary"
 | 
						|
echo "======="
 | 
						|
echo ""
 | 
						|
 | 
						|
echo "Target folder: $TARGET_FOLDER"
 | 
						|
echo "Consume folder: $CONSUME_FOLDER"
 | 
						|
if [[ -z $MEDIA_FOLDER ]] ; then
 | 
						|
	echo "Media folder: Managed by Docker"
 | 
						|
else
 | 
						|
	echo "Media folder: $MEDIA_FOLDER"
 | 
						|
fi
 | 
						|
if [[ -z $DATA_FOLDER ]] ; then
 | 
						|
	echo "Data folder: Managed by Docker"
 | 
						|
else
 | 
						|
	echo "Data folder: $DATA_FOLDER"
 | 
						|
fi
 | 
						|
if [[ "$DATABASE_BACKEND" == "postgres" || "$DATABASE_BACKEND" == "mariadb" ]] ; then
 | 
						|
	if [[ -z $DATABASE_FOLDER ]] ; then
 | 
						|
		echo "Database folder: Managed by Docker"
 | 
						|
	else
 | 
						|
		echo "Database folder: $DATABASE_FOLDER"
 | 
						|
	fi
 | 
						|
fi
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "URL: $URL"
 | 
						|
echo "Port: $PORT"
 | 
						|
echo "Database: $DATABASE_BACKEND"
 | 
						|
echo "Tika enabled: $TIKA_ENABLED"
 | 
						|
echo "OCR language: $OCR_LANGUAGE"
 | 
						|
echo "User id: $USERMAP_UID"
 | 
						|
echo "Group id: $USERMAP_GID"
 | 
						|
echo ""
 | 
						|
echo "Paperless username: $USERNAME"
 | 
						|
echo "Paperless email: $EMAIL"
 | 
						|
 | 
						|
echo ""
 | 
						|
read -r -p "Press any key to install."
 | 
						|
 | 
						|
echo ""
 | 
						|
echo "Installing paperless..."
 | 
						|
echo ""
 | 
						|
 | 
						|
mkdir -p "$TARGET_FOLDER"
 | 
						|
 | 
						|
cd "$TARGET_FOLDER"
 | 
						|
 | 
						|
DOCKER_COMPOSE_VERSION=$DATABASE_BACKEND
 | 
						|
 | 
						|
if [[ $TIKA_ENABLED == "yes" ]] ; then
 | 
						|
	DOCKER_COMPOSE_VERSION="$DOCKER_COMPOSE_VERSION-tika"
 | 
						|
fi
 | 
						|
 | 
						|
wget "https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/docker-compose.$DOCKER_COMPOSE_VERSION.yml" -O docker-compose.yml
 | 
						|
wget "https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/main/docker/compose/.env" -O .env
 | 
						|
 | 
						|
SECRET_KEY=$(LC_ALL=C tr -dc 'a-zA-Z0-9!#$%&()*+,-./:;<=>?@[\]^_`{|}~' < /dev/urandom | dd bs=1 count=64 2>/dev/null)
 | 
						|
 | 
						|
 | 
						|
DEFAULT_LANGUAGES=("deu eng fra ita spa")
 | 
						|
 | 
						|
# OCR_LANG requires underscores, replace dashes if the user gave them with underscores
 | 
						|
readonly ocr_langs=${OCR_LANGUAGE//-/_}
 | 
						|
# OCR_LANGS (the install version) uses dashes, not underscores, so convert underscore to dash and plus to space
 | 
						|
install_langs=${OCR_LANGUAGE//_/-}    # First convert any underscores to dashes
 | 
						|
install_langs=${install_langs//+/ }    # Then convert plus signs to spaces
 | 
						|
 | 
						|
read -r -a install_langs_array <<< "${install_langs}"
 | 
						|
 | 
						|
{
 | 
						|
	if [[ ! $URL == "" ]] ; then
 | 
						|
		echo "PAPERLESS_URL=$URL"
 | 
						|
	fi
 | 
						|
	if [[ ! $USERMAP_UID == "1000" ]] ; then
 | 
						|
		echo "USERMAP_UID=$USERMAP_UID"
 | 
						|
	fi
 | 
						|
	if [[ ! $USERMAP_GID == "1000" ]] ; then
 | 
						|
		echo "USERMAP_GID=$USERMAP_GID"
 | 
						|
	fi
 | 
						|
	echo "PAPERLESS_TIME_ZONE=$TIME_ZONE"
 | 
						|
	echo "PAPERLESS_OCR_LANGUAGE=$ocr_langs"
 | 
						|
	echo "PAPERLESS_SECRET_KEY='$SECRET_KEY'"
 | 
						|
	if [[ ! ${DEFAULT_LANGUAGES[*]} =~ ${install_langs_array[*]} ]] ; then
 | 
						|
		echo "PAPERLESS_OCR_LANGUAGES=${install_langs_array[*]}"
 | 
						|
	fi
 | 
						|
} > docker-compose.env
 | 
						|
 | 
						|
sed -i "s/- \"8000:8000\"/- \"$PORT:8000\"/g" docker-compose.yml
 | 
						|
 | 
						|
sed -i "s#- \./consume:/usr/src/paperless/consume#- $CONSUME_FOLDER:/usr/src/paperless/consume#g" docker-compose.yml
 | 
						|
 | 
						|
if [[ -n $MEDIA_FOLDER ]] ; then
 | 
						|
	sed -i "s#- media:/usr/src/paperless/media#- $MEDIA_FOLDER:/usr/src/paperless/media#g" docker-compose.yml
 | 
						|
	sed -i "/^\s*media:/d" docker-compose.yml
 | 
						|
fi
 | 
						|
 | 
						|
if [[ -n $DATA_FOLDER ]] ; then
 | 
						|
	sed -i "s#- data:/usr/src/paperless/data#- $DATA_FOLDER:/usr/src/paperless/data#g" docker-compose.yml
 | 
						|
	sed -i "/^\s*data:/d" docker-compose.yml
 | 
						|
fi
 | 
						|
 | 
						|
# If the database folder was provided (not blank), replace the pgdata/dbdata volume with a bind mount
 | 
						|
# of the provided folder
 | 
						|
if [[ -n $DATABASE_FOLDER ]] ; then
 | 
						|
	if [[ "$DATABASE_BACKEND" == "postgres" ]] ; then
 | 
						|
		sed -i "s#- pgdata:/var/lib/postgresql/data#- $DATABASE_FOLDER:/var/lib/postgresql/data#g" docker-compose.yml
 | 
						|
		sed -i "/^\s*pgdata:/d" docker-compose.yml
 | 
						|
	elif [[ "$DATABASE_BACKEND" == "mariadb" ]]; then
 | 
						|
		sed -i "s#- dbdata:/var/lib/mysql#- $DATABASE_FOLDER:/var/lib/mysql#g" docker-compose.yml
 | 
						|
		sed -i "/^\s*dbdata:/d" docker-compose.yml
 | 
						|
	fi
 | 
						|
fi
 | 
						|
 | 
						|
# remove trailing blank lines from end of file
 | 
						|
sed -i -e :a -e '/^\n*$/{$d;N;};/\n$/ba' docker-compose.yml
 | 
						|
# if last line in file contains "volumes:", remove that line since no more named volumes are left
 | 
						|
l1=$(grep -n '^volumes:' docker-compose.yml | cut -d : -f 1)  # get line number containing volume: at begin of line
 | 
						|
l2=$(wc -l < docker-compose.yml)  # get total number of lines
 | 
						|
if [ "$l1" -eq "$l2" ] ; then
 | 
						|
	sed -i "/^volumes:/d" docker-compose.yml
 | 
						|
fi
 | 
						|
 | 
						|
 | 
						|
docker compose pull
 | 
						|
 | 
						|
if [ "$DATABASE_BACKEND" == "postgres" ] || [ "$DATABASE_BACKEND" == "mariadb" ] ; then
 | 
						|
	echo "Starting DB first for initialization"
 | 
						|
	docker compose up --detach db
 | 
						|
	# hopefully enough time for even the slower systems
 | 
						|
	sleep 15
 | 
						|
	docker compose stop
 | 
						|
fi
 | 
						|
 | 
						|
docker compose run --rm -e DJANGO_SUPERUSER_PASSWORD="$PASSWORD" webserver createsuperuser --noinput --username "$USERNAME" --email "$EMAIL"
 | 
						|
 | 
						|
docker compose up --detach
 |