mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-05 06:35:59 +00:00
commit
3407abbad1
@ -39,13 +39,6 @@ if [[ $version == *SNAPSHOT* ]]; then
|
||||
echo ">>>> Building nightly images for $version <<<<<"
|
||||
url_base="https://github.com/eikek/docspell/releases/download/nightly"
|
||||
|
||||
echo "============ Building Tools ============"
|
||||
docker buildx build \
|
||||
--platform="$platforms" $push \
|
||||
--build-arg tools_url="$url_base/docspell-tools-$version.zip" \
|
||||
--tag docspell/tools:nightly \
|
||||
-f tools.dockerfile .
|
||||
|
||||
echo "============ Building Restserver ============"
|
||||
docker buildx build \
|
||||
--platform="$platforms" $push \
|
||||
@ -61,14 +54,6 @@ if [[ $version == *SNAPSHOT* ]]; then
|
||||
-f joex.dockerfile .
|
||||
else
|
||||
echo ">>>> Building release images for $version <<<<<"
|
||||
echo "============ Building Tools ============"
|
||||
docker buildx build \
|
||||
--platform="$platforms" $push \
|
||||
--build-arg version=$version \
|
||||
--tag docspell/tools:v$version \
|
||||
--tag docspell/tools:latest \
|
||||
-f tools.dockerfile .
|
||||
|
||||
echo "============ Building Restserver ============"
|
||||
docker buildx build \
|
||||
--platform="$platforms" $push \
|
||||
|
@ -1,35 +0,0 @@
|
||||
FROM alpine:latest
|
||||
|
||||
# Builds an image where all scripts in tools/ are in PATH. There are
|
||||
# no assumptions what script to run, so there are no CMD or
|
||||
# ENTRYPOINTS defined.
|
||||
#
|
||||
# The scripts are named is in tools/ only prefixed by `ds-`
|
||||
#
|
||||
# Run the export-files script, for example:
|
||||
#
|
||||
# docker run -e DS_USER=demo -e DS_PASS=test docspell/tools:dev ds-export-files "http://localhost" .
|
||||
#
|
||||
# The build requires to either specify a version build argument or a
|
||||
# tools_url build argument. If a tools_url argument is given, then
|
||||
# this url is used to download the tools zip file. Otherwise the
|
||||
# version argument is used to download from github.
|
||||
|
||||
LABEL maintainer="eikek0 <eike@docspell.org>"
|
||||
|
||||
ARG version=
|
||||
ARG tools_url=
|
||||
|
||||
RUN apk add --no-cache curl bash inotify-tools jq sqlite
|
||||
|
||||
WORKDIR /opt
|
||||
RUN wget ${tools_url:-https://github.com/eikek/docspell/releases/download/v$version/docspell-tools-$version.zip} && \
|
||||
unzip docspell-tools-*.zip && \
|
||||
rm docspell-tools-*.zip
|
||||
|
||||
RUN bash -c 'while read f; do \
|
||||
target="ds-$(basename "$f" ".sh")"; \
|
||||
echo "Installing $f -> $target"; \
|
||||
cp "$f" "/usr/local/bin/$target"; \
|
||||
chmod 755 "/usr/local/bin/$target"; \
|
||||
done < <(find /opt/docspell-tools-* -name "*.sh" -mindepth 2 -not -path "*webextension*")'
|
@ -1,181 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
echo "##################### START #####################"
|
||||
|
||||
echo " Docspell Consumedir Cleaner - v0.1 beta"
|
||||
echo " by totti4ever" && echo
|
||||
echo " $(date)"
|
||||
echo
|
||||
echo "#################################################"
|
||||
echo && echo
|
||||
|
||||
CURL_CMD="curl"
|
||||
JQ_CMD="jq"
|
||||
|
||||
"$JQ_CMD" --version > /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "please install 'jq'"
|
||||
exit -4
|
||||
fi
|
||||
|
||||
ds_url=${1%/}
|
||||
ds_user_param=$2
|
||||
ds_user=${ds_user_param#*/}
|
||||
ds_collective=${ds_user_param%%/*}
|
||||
ds_password=$3
|
||||
ds_consumedir_path=${4%/}
|
||||
ds_archive_path=$ds_consumedir_path/_archive/$ds_collective
|
||||
|
||||
|
||||
if [ $# -ne 4 ]; then
|
||||
echo "FATAL Exactly four parameters needed"
|
||||
exit -3
|
||||
elif [ "$1" == "" ] || [ "$2" == "" ] || [ "$3" == "" ] || [ "$4" == "" ]; then
|
||||
echo "FATAL Parameter missing"
|
||||
echo " ds_url: $ds_url"
|
||||
echo " ds_user: $ds_user"
|
||||
echo " ds_password: $ds_password"
|
||||
echo " ds_consumedir_path: $ds_consumedir_path"
|
||||
exit -2
|
||||
elif [ "$ds_collective" == "_archive" ]; then
|
||||
echo "FATAL collective name '_archive' is not supported by this script"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
|
||||
############# FUNCTIONS
|
||||
function curl_call() {
|
||||
curl_cmd="$CURL_CMD $1 -H 'X-Docspell-Auth: $ds_token'"
|
||||
curl_result=$(eval $curl_cmd)
|
||||
curl_code=$?
|
||||
|
||||
if [ "$curl_result" == '"Authentication failed."' ] || [ "$curl_result" == 'Response timed out' ]; then
|
||||
printf "\nNew login required ($curl_result)... "
|
||||
login
|
||||
printf "%${#len_resultset}s" " "; printf " .."
|
||||
curl_call $1
|
||||
|
||||
elif [ "$curl_result" == "Bad Gateway" ] || [ "$curl_result" == '404 page not found' ]; then
|
||||
echo "FATAL Connection to server failed"
|
||||
exit -1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
function login() {
|
||||
curl_call "-s -X POST -d '{\"account\": \"$ds_collective/$ds_user\", \"password\": \"$ds_password\"}' ${ds_url}/api/v1/open/auth/login"
|
||||
|
||||
curl_status=$(echo $curl_result | $JQ_CMD -r ".success")
|
||||
|
||||
if [ "$curl_status" == "true" ]; then
|
||||
ds_token=$(echo $curl_result | $JQ_CMD -r ".token")
|
||||
echo "Login successfull ( Token: $ds_token )"
|
||||
|
||||
else
|
||||
echo "FATAL Login not succesfull"
|
||||
exit 1
|
||||
|
||||
fi
|
||||
}
|
||||
|
||||
############# END
|
||||
|
||||
echo "Settings:"
|
||||
if [ "$DS_CC_REMOVE" == "true" ]; then
|
||||
echo " ### !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ###"
|
||||
echo " - DELETE files? YES"
|
||||
echo " when already existing in Docspell. This cannot be undone!"
|
||||
echo " ### !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ###"
|
||||
else
|
||||
echo " - DELETE files? no"
|
||||
echo " moving already uploaded files to archive"
|
||||
fi
|
||||
echo
|
||||
if [ "$DS_CC_UPLOAD_MISSING" == true ]; then
|
||||
echo " - UPLOAD files? YES"
|
||||
echo " files not existing in Docspell will be uploaded and will be re-checked in the next run."
|
||||
else
|
||||
echo " - UPLOAD files? no"
|
||||
echo " files not existing in Docspell will NOT be uploaded and stay where they are."
|
||||
fi
|
||||
echo && echo
|
||||
echo "Press 'ctrl+c' to cancel"
|
||||
for ((i=9;i>=0;i--)); do
|
||||
printf "\r waiting $i seconds "
|
||||
sleep 1s
|
||||
done
|
||||
echo && echo
|
||||
|
||||
# login, get token
|
||||
login
|
||||
|
||||
echo "Scanning folder for collective '$ds_collective' ($ds_consumedir_path/$ds_collective)"
|
||||
echo && echo
|
||||
|
||||
while read -r line
|
||||
do
|
||||
tmp_filepath=$line
|
||||
|
||||
if [ "$tmp_filepath" == "" ]; then
|
||||
echo "no files found" && echo
|
||||
exit 0 #no results
|
||||
elif [ ! -f "$tmp_filepath" ]; then
|
||||
echo "FATAL no access to file: $tmp_filepath"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
echo "Checking '$tmp_filepath'"
|
||||
printf "%${#len_resultset}s" " "; printf " "
|
||||
|
||||
# check for checksum
|
||||
tmp_checksum=$(sha256sum "$tmp_filepath" | awk '{print $1}')
|
||||
|
||||
curl_call "-s -X GET '$ds_url/api/v1/sec/checkfile/$tmp_checksum'"
|
||||
curl_status=$(echo $curl_result | $JQ_CMD -r ".exists")
|
||||
|
||||
if [ $curl_code -ne 0 ]; then
|
||||
# error
|
||||
echo "ERROR $curl_result // $curl_status"
|
||||
|
||||
# file exists in Docspell
|
||||
elif [ "$curl_status" == "true" ]; then
|
||||
item_name=$(echo $curl_result | $JQ_CMD -r ".items[0].name")
|
||||
item_id=$(echo $curl_result | $JQ_CMD -r ".items[0].id")
|
||||
echo "File already exists: '$item_name (ID: $item_id)'"
|
||||
|
||||
printf "%${#len_resultset}s" " "; printf " "
|
||||
if [ "$DS_CC_REMOVE" == "true" ]; then
|
||||
echo "... removing file"
|
||||
rm "$tmp_filepath"
|
||||
else
|
||||
created=$(echo $curl_result | $JQ_CMD -r ".items[0].created")
|
||||
cur_dir="$ds_archive_path/$(date -d @$(expr \( $created + 500 \) / 1000) +%Y-%m)"
|
||||
echo "... moving to archive by month added ('$cur_dir')"
|
||||
mkdir -p "$cur_dir"
|
||||
mv "$tmp_filepath" "$cur_dir/"
|
||||
fi
|
||||
|
||||
# file does not exist in Docspell
|
||||
else
|
||||
|
||||
echo "Files does not exist, yet"
|
||||
if [ "$DS_CC_UPLOAD_MISSING" == true ]; then
|
||||
printf "%${#len_resultset}s" " "; printf " "
|
||||
printf "...uploading file.."
|
||||
curl_call "-s -X POST '$ds_url/api/v1/sec/upload/item' -H 'Content-Type: multipart/form-data' -F 'file=@$tmp_filepath'"
|
||||
curl_status=$(echo $curl_result | $JQ_CMD -r ".success")
|
||||
if [ "$curl_status" == "true" ]; then
|
||||
echo ". done"
|
||||
else
|
||||
echo -e "\nERROR $curl_result"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo
|
||||
done \
|
||||
<<< $(find $ds_consumedir_path/$ds_collective -type f)
|
||||
|
||||
|
||||
echo ################# DONE #################
|
||||
date
|
@ -1,439 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This script watches a directory for new files and uploads them to
|
||||
# docspell. Or it uploads all files currently in the directory.
|
||||
#
|
||||
# It requires inotifywait, curl and sha256sum if the `-m' option is
|
||||
# used.
|
||||
|
||||
# saner programming env: these switches turn some bugs into errors
|
||||
set -o errexit -o pipefail -o noclobber -o nounset
|
||||
|
||||
CURL_CMD="curl"
|
||||
INOTIFY_CMD="inotifywait"
|
||||
SHA256_CMD="sha256sum"
|
||||
MKTEMP_CMD="mktemp"
|
||||
CURL_OPTS=${CURL_OPTS:-}
|
||||
|
||||
! getopt --test > /dev/null
|
||||
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
|
||||
echo 'I’m sorry, `getopt --test` failed in this environment.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OPTIONS=omhdp:vrmi
|
||||
LONGOPTS=once,distinct,help,delete,path:,verbose,recursive,dry,integration,iuser:,iheader:,poll:,exclude:,include:
|
||||
|
||||
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
|
||||
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
|
||||
# e.g. return value is 1
|
||||
# then getopt has complained about wrong arguments to stdout
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# read getopt’s output this way to handle the quoting right:
|
||||
eval set -- "$PARSED"
|
||||
|
||||
declare -a watchdir
|
||||
help=n verbose=n delete=n once=n distinct=n recursive=n dryrun=n
|
||||
integration=n iuser="" iheader="" poll="" exclude="" include=""
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
help=y
|
||||
shift
|
||||
;;
|
||||
-v|--verbose)
|
||||
verbose=y
|
||||
shift
|
||||
;;
|
||||
-d|--delete)
|
||||
delete=y
|
||||
shift
|
||||
;;
|
||||
-o|--once)
|
||||
once=y
|
||||
shift
|
||||
;;
|
||||
-p|--path)
|
||||
watchdir+=("$2")
|
||||
shift 2
|
||||
;;
|
||||
-m|--distinct)
|
||||
distinct=y
|
||||
shift
|
||||
;;
|
||||
-r|--recursive)
|
||||
recursive=y
|
||||
shift
|
||||
;;
|
||||
--dry)
|
||||
dryrun=y
|
||||
shift
|
||||
;;
|
||||
-i|--integration)
|
||||
integration=y
|
||||
recursive=y
|
||||
shift
|
||||
;;
|
||||
--iuser)
|
||||
iuser="$2"
|
||||
shift 2
|
||||
;;
|
||||
--iheader)
|
||||
iheader="$2"
|
||||
shift 2
|
||||
;;
|
||||
--poll)
|
||||
poll="$2"
|
||||
shift 2
|
||||
;;
|
||||
--exclude)
|
||||
exclude="$2"
|
||||
shift 2
|
||||
;;
|
||||
--include)
|
||||
include="$2"
|
||||
shift 2
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo "Programming error"
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
showUsage() {
|
||||
echo "Upload files in a directory"
|
||||
echo ""
|
||||
echo "Usage: $0 [options] url url ..."
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " -v | --verbose Print more to stdout. (value: $verbose)"
|
||||
echo " -d | --delete Delete the file if successfully uploaded. (value: $delete)"
|
||||
echo " -p | --path <dir> The directories to watch. This is required. (value: ${watchdir[@]})"
|
||||
echo " -h | --help Prints this help text. (value: $help)"
|
||||
echo " -m | --distinct Optional. Upload only if the file doesn't already exist. (value: $distinct)"
|
||||
echo " -o | --once Instead of watching, upload all files in that dir. (value: $once)"
|
||||
echo " --poll <sec> Run the script periodically instead of watching a directory. This can be"
|
||||
echo " used if watching via inotify is not possible. (value: $poll)"
|
||||
echo " -r | --recursive Traverse the directory(ies) recursively (value: $recursive)"
|
||||
echo " -i | --integration Upload to the integration endpoint. It implies -r. This puts the script in"
|
||||
echo " a different mode, where the first subdirectory of any given starting point"
|
||||
echo " is read as the collective name. The url(s) are completed with this name in"
|
||||
echo " order to upload files to the respective collective. So each directory"
|
||||
echo " given is expected to contain one subdirectory per collective and the urls"
|
||||
echo " are expected to identify the integration endpoint, which is"
|
||||
echo " /api/v1/open/integration/item/<collective-name>. (value: $integration)"
|
||||
echo " --iheader The header name and value to use with the integration endpoint. This must be"
|
||||
echo " in form 'headername:value'. Only used if '-i' is supplied."
|
||||
echo " (value: $iheader)"
|
||||
echo " --iuser The username and password for basic auth to use with the integration"
|
||||
echo " endpoint. This must be of form 'user:pass'. Only used if '-i' is supplied."
|
||||
echo " (value: $iuser)"
|
||||
echo " --exclude <glob> A shell glob pattern that is used to skip files that match (value: $exclude)."
|
||||
echo " --include <glob> A shell glob pattern that is used to find files to upload (value: $include)."
|
||||
echo " If --exclude and --include is given, both apply."
|
||||
echo " --dry Do a 'dry run', not uploading anything only printing to stdout (value: $dryrun)"
|
||||
echo ""
|
||||
echo "Arguments:"
|
||||
echo " A list of URLs to upload the files to."
|
||||
echo ""
|
||||
echo "Example: Watch directory"
|
||||
echo "$0 --path ~/Downloads -m -dv http://localhost:7880/api/v1/open/upload/item/abcde-12345-abcde-12345"
|
||||
echo ""
|
||||
echo "Example: Upload all files in a directory"
|
||||
echo "$0 --path ~/Downloads -m -dv --once http://localhost:7880/api/v1/open/upload/item/abcde-12345-abcde-12345"
|
||||
echo ""
|
||||
echo "Example: Integration Endpoint"
|
||||
echo "$0 -i --iheader 'Docspell-Integration:test123' -m -p ~/Downloads/ http://localhost:7880/api/v1/open/integration/item"
|
||||
echo ""
|
||||
}
|
||||
|
||||
if [ "$help" = "y" ]; then
|
||||
showUsage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# handle non-option arguments
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "$0: No upload URLs given."
|
||||
exit 4
|
||||
fi
|
||||
urls=$@
|
||||
|
||||
if [ ! -d "$watchdir" ]; then
|
||||
echo "The path '$watchdir' is not a directory."
|
||||
exit 4
|
||||
fi
|
||||
|
||||
|
||||
trace() {
|
||||
if [ "$verbose" = "y" ]; then
|
||||
>&2 echo "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
info() {
|
||||
>&2 echo $1
|
||||
}
|
||||
|
||||
getCollective() {
|
||||
file=$(realpath "$1")
|
||||
dir=$(realpath "$2")
|
||||
collective=${file#"$dir"}
|
||||
coll=$(echo $collective | cut -d'/' -f1)
|
||||
if [ -z "$coll" ]; then
|
||||
coll=$(echo $collective | cut -d'/' -f2)
|
||||
fi
|
||||
echo $coll
|
||||
}
|
||||
|
||||
|
||||
upload() {
|
||||
dir=$(realpath "$1")
|
||||
file=$(realpath "$2")
|
||||
url="$3"
|
||||
OPTS="$CURL_OPTS"
|
||||
if [ "$integration" = "y" ]; then
|
||||
collective=$(getCollective "$file" "$dir")
|
||||
trace "- upload: collective = $collective"
|
||||
url="$url/$collective"
|
||||
if [ $iuser ]; then
|
||||
OPTS="$OPTS --user $iuser"
|
||||
fi
|
||||
if [ $iheader ]; then
|
||||
OPTS="$OPTS -H $iheader"
|
||||
fi
|
||||
fi
|
||||
if [ "$dryrun" = "y" ]; then
|
||||
info "- Not uploading (dry-run) $file to $url with opts $OPTS"
|
||||
else
|
||||
META1=""
|
||||
META2=""
|
||||
if [ "$distinct" = "y" ]; then
|
||||
META1="-F"
|
||||
META2="meta={\"multiple\": false, \"skipDuplicates\": true}"
|
||||
fi
|
||||
trace "- Uploading $file to $url with options $OPTS"
|
||||
tf1=$($MKTEMP_CMD) tf2=$($MKTEMP_CMD) rc=0
|
||||
$CURL_CMD --fail -# -o "$tf1" --stderr "$tf2" $OPTS -XPOST $META1 "$META2" -F file=@"$file" "$url"
|
||||
if [ $? -ne 0 ]; then
|
||||
info "Upload failed. Exit code: $rc"
|
||||
cat "$tf1"
|
||||
cat "$tf2"
|
||||
echo ""
|
||||
rm "$tf1" "$tf2"
|
||||
return $rc
|
||||
else
|
||||
if cat $tf1 | grep -q '{"success":false'; then
|
||||
echo "Upload failed. Message from server:"
|
||||
cat "$tf1"
|
||||
echo ""
|
||||
rm "$tf1" "$tf2"
|
||||
return 1
|
||||
else
|
||||
info "- Upload done."
|
||||
rm "$tf1" "$tf2"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
checksum() {
|
||||
$SHA256_CMD "$1" | cut -d' ' -f1 | xargs
|
||||
}
|
||||
|
||||
checkFile() {
|
||||
local url="$1"
|
||||
local file="$2"
|
||||
local dir="$3"
|
||||
OPTS="$CURL_OPTS"
|
||||
if [ "$integration" = "y" ]; then
|
||||
collective=$(getCollective "$file" "$dir")
|
||||
url="$url/$collective"
|
||||
url=$(echo "$url" | sed 's,/item/,/checkfile/,g')
|
||||
if [ $iuser ]; then
|
||||
OPTS="$OPTS --user $iuser"
|
||||
fi
|
||||
if [ $iheader ]; then
|
||||
OPTS="$OPTS -H $iheader"
|
||||
fi
|
||||
else
|
||||
url=$(echo "$1" | sed 's,upload/item,checkfile,g')
|
||||
fi
|
||||
url=$url/$(checksum "$file")
|
||||
trace "- Check file via $OPTS: $url"
|
||||
tf1=$($MKTEMP_CMD) tf2=$($MKTEMP_CMD)
|
||||
$CURL_CMD --fail -v -o "$tf1" --stderr "$tf2" $OPTS -XGET -s "$url"
|
||||
if [ $? -ne 0 ]; then
|
||||
info "Checking file failed!"
|
||||
cat "$tf1" >&2
|
||||
cat "$tf2" >&2
|
||||
info ""
|
||||
rm "$tf1" "$tf2"
|
||||
echo "failed"
|
||||
return 1
|
||||
else
|
||||
if cat "$tf1" | grep -q '{"exists":true'; then
|
||||
rm "$tf1" "$tf2"
|
||||
echo "y"
|
||||
else
|
||||
rm "$tf1" "$tf2"
|
||||
echo "n"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
process() {
|
||||
file=$(realpath "$1")
|
||||
dir="$2"
|
||||
info "---- Processing $file ----------"
|
||||
declare -i curlrc=0
|
||||
set +e
|
||||
for url in $urls; do
|
||||
if [ "$distinct" = "y" ]; then
|
||||
trace "- Checking if $file has been uploaded to $url already"
|
||||
res=$(checkFile "$url" "$file" "$dir")
|
||||
rc=$?
|
||||
curlrc=$(expr $curlrc + $rc)
|
||||
trace "- Result from checkfile: $res"
|
||||
if [ "$res" = "y" ]; then
|
||||
info "- Skipping file '$file' because it has been uploaded in the past."
|
||||
continue
|
||||
elif [ "$res" != "n" ]; then
|
||||
info "- Checking file failed, skipping the file."
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
trace "- Uploading '$file' to '$url'."
|
||||
upload "$dir" "$file" "$url"
|
||||
rc=$?
|
||||
curlrc=$(expr $curlrc + $rc)
|
||||
if [ $rc -ne 0 ]; then
|
||||
trace "Upload to '$url' failed!"
|
||||
fi
|
||||
done
|
||||
set -e
|
||||
if [ $curlrc -ne 0 ]; then
|
||||
info "-> Some uploads failed."
|
||||
else
|
||||
trace "= File processed for all URLs"
|
||||
if [ "$delete" = "y" ]; then
|
||||
info "- Deleting file '$file'"
|
||||
set +e
|
||||
rm "$file"
|
||||
if [ $? -ne 0 ]; then
|
||||
info "- Deleting failed!"
|
||||
fi
|
||||
set -e
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
findDir() {
|
||||
path="$1"
|
||||
for dir in "${watchdir[@]}"; do
|
||||
if [[ $path = ${dir}* ]]
|
||||
then
|
||||
echo $dir
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
checkSetup() {
|
||||
for dir in "${watchdir[@]}"; do
|
||||
find "$dir" -mindepth 1 -maxdepth 1 -type d -print0 | while IFS= read -d '' -r collective; do
|
||||
for url in $urls; do
|
||||
if [ "$integration" = "y" ]; then
|
||||
url="$url/$(basename $collective)"
|
||||
OPTS="$CURL_OPTS -i -s -o /dev/null -w %{http_code}"
|
||||
if [ $iuser ]; then
|
||||
OPTS="$OPTS --user $iuser"
|
||||
fi
|
||||
if [ $iheader ]; then
|
||||
OPTS="$OPTS -H $iheader"
|
||||
fi
|
||||
trace "Checking integration endpoint: $CURL_CMD $OPTS "$url""
|
||||
status=$($CURL_CMD $OPTS "$url")
|
||||
if [ "$status" != "200" ]; then
|
||||
echo "[WARN] Collective '$(basename $collective)' failed the setup check."
|
||||
echo "[WARN] $status response, command: $CURL_CMD $OPTS $url"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
runOnce() {
|
||||
info "Uploading all files (except hidden) in '$watchdir'."
|
||||
MD="-maxdepth 1"
|
||||
if [ "$recursive" = "y" ]; then
|
||||
MD=""
|
||||
fi
|
||||
EXCL=""
|
||||
if [ -n "$exclude" ]; then
|
||||
EXCL="-not -name $exclude"
|
||||
fi
|
||||
INCL=""
|
||||
if [ -n "$include" ]; then
|
||||
INCL="-name $include"
|
||||
fi
|
||||
for dir in "${watchdir[@]}"; do
|
||||
find "$dir" $MD -type f $INCL $EXCL -not -name ".*" -print0 | while IFS= read -d '' -r file; do
|
||||
process "$file" "$dir"
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
includeFile() {
|
||||
file="$1"
|
||||
if [ -n "$include" ] && [[ $file != $include ]]; then
|
||||
trace "Skip $file due to include filter"
|
||||
return 1
|
||||
elif [ -n "$exclude" ] && [[ $file == $exclude ]]; then
|
||||
trace "Skip $file due to exclude filter"
|
||||
return 1
|
||||
else
|
||||
[[ "$file" != .* ]]
|
||||
fi
|
||||
}
|
||||
|
||||
# warn if something seems not correctly configured
|
||||
checkSetup
|
||||
|
||||
if [ "$once" = "y" ]; then
|
||||
runOnce
|
||||
else
|
||||
REC=""
|
||||
if [ "$recursive" = "y" ]; then
|
||||
REC="-r"
|
||||
fi
|
||||
if [ -z "$poll" ]; then
|
||||
$INOTIFY_CMD $REC -m --format '%w%f' -e close_write -e moved_to "${watchdir[@]}" |
|
||||
while read pathfile; do
|
||||
if includeFile "$(basename "$pathfile")"; then
|
||||
dir=$(findDir "$pathfile")
|
||||
trace "The file '$pathfile' appeared below '$dir'"
|
||||
sleep 1
|
||||
process "$(realpath "$pathfile")" "$dir"
|
||||
else
|
||||
trace "Skip file $(realpath "$pathfile")"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "Running in polling mode: ${poll}s"
|
||||
while [ : ]
|
||||
do
|
||||
runOnce
|
||||
sleep $poll
|
||||
done
|
||||
fi
|
||||
fi
|
@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Simple script to authenticate with docspell and trigger the "convert
|
||||
# all pdf" route that submits a task to convert all pdf files using
|
||||
# ocrmypdf.
|
||||
|
||||
set -e
|
||||
|
||||
CURL_CMD="curl"
|
||||
JQ_CMD="jq"
|
||||
|
||||
BASE_URL="${1:-http://localhost:7880}"
|
||||
LOGIN_URL="$BASE_URL/api/v1/open/auth/login"
|
||||
TRIGGER_URL="$BASE_URL/api/v1/sec/item/convertallpdfs"
|
||||
|
||||
echo "Login to trigger converting all pdfs."
|
||||
echo "Using url: $BASE_URL"
|
||||
echo -n "Account: "
|
||||
read USER
|
||||
echo -n "Password: "
|
||||
read -s PASS
|
||||
echo
|
||||
|
||||
auth=$("$CURL_CMD" --fail -XPOST --silent --data-binary "{\"account\":\"$USER\", \"password\":\"$PASS\"}" "$LOGIN_URL")
|
||||
|
||||
if [ "$(echo $auth | "$JQ_CMD" .success)" == "true" ]; then
|
||||
echo "Login successful"
|
||||
auth_token=$(echo $auth | "$JQ_CMD" -r .token)
|
||||
"$CURL_CMD" --fail -XPOST -H "X-Docspell-Auth: $auth_token" "$TRIGGER_URL"
|
||||
else
|
||||
echo "Login failed."
|
||||
fi
|
@ -1,213 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Script for downloading files (the PDF versions) flat in the current
|
||||
# directory. It takes a search query for selecting what to download.
|
||||
# Metadata is not downloaded, only the files.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# download-files.sh <docspell-base-url> <query>
|
||||
#
|
||||
# The docspell base url is required as well as a search query. The
|
||||
# output directory is the current directory, and can be defined via
|
||||
# env variable "TARGET_DIR".
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# download-files.sh http://localhost:7880 "tag:todo folder:work"
|
||||
#
|
||||
# The script then asks for username and password and starts
|
||||
# downloading. For more details about the query, please see the docs
|
||||
# here: https://docspell.org/docs/query/
|
||||
|
||||
CURL_CMD="curl"
|
||||
JQ_CMD="jq"
|
||||
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "The base-url to docspell is required."
|
||||
exit 1
|
||||
else
|
||||
BASE_URL="$1"
|
||||
shift
|
||||
fi
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
errout "A search query is required"
|
||||
exit 1
|
||||
else
|
||||
QUERY="$1"
|
||||
shift
|
||||
fi
|
||||
|
||||
set -o errexit -o pipefail -o noclobber -o nounset
|
||||
|
||||
LOGIN_URL="$BASE_URL/api/v1/open/auth/login"
|
||||
SEARCH_URL="$BASE_URL/api/v1/sec/item/search"
|
||||
DETAIL_URL="$BASE_URL/api/v1/sec/item"
|
||||
ATTACH_URL="$BASE_URL/api/v1/sec/attachment"
|
||||
|
||||
OVERWRITE_FILE=${OVERWRITE_FILE:-n}
|
||||
TARGET=${TARGET_DIR:-"$(pwd)"}
|
||||
|
||||
errout() {
|
||||
>&2 echo "$@"
|
||||
}
|
||||
|
||||
trap "{ rm -f ${TMPDIR-:/tmp}/ds-download.*; }" EXIT
|
||||
|
||||
mcurl() {
|
||||
tmpfile1=$(mktemp -t "ds-download.XXXXX")
|
||||
tmpfile2=$(mktemp -t "ds-download.XXXXX")
|
||||
set +e
|
||||
"$CURL_CMD" -# --fail --stderr "$tmpfile1" -o "$tmpfile2" -H "X-Docspell-Auth: $auth_token" "$@"
|
||||
status=$?
|
||||
set -e
|
||||
if [ $status -ne 0 ]; then
|
||||
errout "$CURL_CMD -H 'X-Docspell-Auth: …' $@"
|
||||
errout "curl command failed (rc=$status)! Output is below."
|
||||
cat "$tmpfile1" >&2
|
||||
cat "$tmpfile2" >&2
|
||||
rm -f "$tmpfile1" "$tmpfile2"
|
||||
return 2
|
||||
else
|
||||
ret=$(cat "$tmpfile2")
|
||||
rm "$tmpfile2" "$tmpfile1"
|
||||
echo $ret
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
errout "Login to Docspell."
|
||||
errout "Using url: $BASE_URL"
|
||||
if [ -z "${DS_USER:-}" ]; then
|
||||
errout -n "Account: "
|
||||
read DS_USER
|
||||
fi
|
||||
if [ -z "${DS_PASS:-}" ]; then
|
||||
errout -n "Password: "
|
||||
read -s DS_PASS
|
||||
fi
|
||||
echo
|
||||
|
||||
declare auth
|
||||
declare auth_token
|
||||
declare auth_time
|
||||
|
||||
|
||||
login() {
|
||||
auth=$("$CURL_CMD" -s --fail -XPOST \
|
||||
--data-binary "{\"account\":\"$DS_USER\", \"password\":\"$DS_PASS\"}" "$LOGIN_URL")
|
||||
|
||||
if [ "$(echo $auth | "$JQ_CMD" .success)" == "true" ]; then
|
||||
errout "Login successful"
|
||||
auth_token=$(echo $auth | "$JQ_CMD" -r .token)
|
||||
auth_time=$(date +%s)
|
||||
else
|
||||
errout "Login failed."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
checkLogin() {
|
||||
elapsed=$((1000 * ($(date +%s) - $auth_time)))
|
||||
maxtime=$(echo $auth | "$JQ_CMD" .validMs)
|
||||
|
||||
elapsed=$(($elapsed + 1000))
|
||||
if [ $elapsed -gt $maxtime ]; then
|
||||
errout "Need to re-login $elapsed > $maxtime"
|
||||
login
|
||||
fi
|
||||
}
|
||||
|
||||
listItems() {
|
||||
OFFSET="${1:-0}"
|
||||
LIMIT="${2:-50}"
|
||||
QUERY="$3"
|
||||
errout "Get next items with offset=$OFFSET, limit=$LIMIT"
|
||||
REQ="{\"offset\":$OFFSET, \"limit\":$LIMIT, \"query\":\" $QUERY \"}"
|
||||
|
||||
mcurl -XPOST -H 'ContentType: application/json' -d "$REQ" "$SEARCH_URL" | "$JQ_CMD" -r '.groups[].items[]|.id'
|
||||
}
|
||||
|
||||
|
||||
fetchItem() {
|
||||
mcurl -XGET "$DETAIL_URL/$1"
|
||||
}
|
||||
|
||||
downloadAttachment() {
|
||||
attachId="$1"
|
||||
errout " - Download '$attachName' ($attachId)"
|
||||
|
||||
if [ -f "$attachOut" ] && [ "$SKIP_FILE" == "y" ]; then
|
||||
errout " - Skipping file '$attachOut' since it already exists"
|
||||
else
|
||||
if [ -f "$attachOut" ] && [ "$OVERWRITE_FILE" == "y" ]; then
|
||||
errout " - Removing attachment file as requested: $attachOut"
|
||||
rm -f "$attachOut"
|
||||
fi
|
||||
|
||||
DL_URL="$ATTACH_URL/$attachId"
|
||||
|
||||
checksum1=$("$CURL_CMD" -s -I -H "X-Docspell-Auth: $auth_token" "$DL_URL" | \
|
||||
grep -i 'etag' | cut -d' ' -f2 | xargs | tr -d '\r')
|
||||
"$CURL_CMD" -s -o "$attachOut" -H "X-Docspell-Auth: $auth_token" "$DL_URL"
|
||||
checksum2=$(sha256sum "$attachOut" | cut -d' ' -f1 | xargs)
|
||||
if [ "$checksum1" == "$checksum2" ]; then
|
||||
errout " - Checksum ok."
|
||||
else
|
||||
errout " - WARNING: Checksum mismatch! Server: $checksum1 Downloaded: $checksum2"
|
||||
return 3
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
downloadItem() {
|
||||
checkLogin
|
||||
itemData=$(fetchItem "$1")
|
||||
errout "Get item $(echo $itemData | "$JQ_CMD" -r .id)"
|
||||
created=$(echo $itemData|"$JQ_CMD" '.created')
|
||||
created=$((($(echo $itemData|"$JQ_CMD" '.created') + 500) / 1000))
|
||||
itemId=$(echo $itemData | "$JQ_CMD" -r '.id')
|
||||
# out="$TARGET/$(date -d @$created +%Y-%m)/$itemId"
|
||||
out="$TARGET"
|
||||
|
||||
if [ -d "$out" ] && [ "${DROP_ITEM:-}" == "y" ]; then
|
||||
errout "Removing item folder as requested: $out"
|
||||
rm -rf "$out"
|
||||
fi
|
||||
|
||||
mkdir -p "$out"
|
||||
|
||||
while read attachId attachName; do
|
||||
attachOut="$out/$attachName"
|
||||
checkLogin
|
||||
downloadAttachment "$attachId"
|
||||
done < <(echo $itemData | "$JQ_CMD" -r '.attachments[] | [.id,.name] | join(" ")')
|
||||
}
|
||||
|
||||
login
|
||||
|
||||
errout "Downloading files…"
|
||||
|
||||
allCounter=0 innerCounter=0 limit=100 offset=0 done=n
|
||||
|
||||
while [ "$done" = "n" ]; do
|
||||
checkLogin
|
||||
|
||||
innerCounter=0
|
||||
while read id; do
|
||||
downloadItem "$id"
|
||||
innerCounter=$(($innerCounter + 1))
|
||||
done < <(listItems $offset $limit "$QUERY")
|
||||
|
||||
allCounter=$(($allCounter + $innerCounter))
|
||||
offset=$(($offset + $limit))
|
||||
|
||||
|
||||
if [ $innerCounter -lt $limit ]; then
|
||||
done=y
|
||||
fi
|
||||
|
||||
done
|
||||
errout "Downloaded $allCounter items"
|
199
tools/ds.sh
199
tools/ds.sh
@ -1,199 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# A simple bash script that reads a configuration file to know where
|
||||
# to upload a given file.
|
||||
#
|
||||
# The config file contains anonymous upload urls to docspell. All
|
||||
# files given to this script are uploaded to all those urls.
|
||||
#
|
||||
# The default location for the config file is
|
||||
# `~/.config/docspell/ds.conf'.
|
||||
#
|
||||
# The config file must contain lines of the form:
|
||||
#
|
||||
# url.1=http://localhost:7880/api/v1/open/upload/item/<source-id>
|
||||
# url.2=...
|
||||
#
|
||||
# Lines starting with a `#' are ignored.
|
||||
#
|
||||
# The `-e|--exists' option allows to skip uploading and only check
|
||||
# whether a given file exists in docspell.
|
||||
|
||||
# saner programming env: these switches turn some bugs into errors
|
||||
set -o errexit -o pipefail -o noclobber -o nounset
|
||||
|
||||
CURL_CMD="curl"
|
||||
GREP_CMD="grep"
|
||||
MKTEMP_CMD="mktemp"
|
||||
SHA256_CMD="sha256sum"
|
||||
|
||||
! getopt --test > /dev/null
|
||||
if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
|
||||
echo 'I’m sorry, `getopt --test` failed in this environment.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OPTIONS=c:hsde
|
||||
LONGOPTS=config:,help,skip,delete,exists,allow-duplicates
|
||||
|
||||
! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
|
||||
if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
|
||||
# e.g. return value is 1
|
||||
# then getopt has complained about wrong arguments to stdout
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# read getopt’s output this way to handle the quoting right:
|
||||
eval set -- "$PARSED"
|
||||
|
||||
exists=n delete=n help=n config="${XDG_CONFIG_HOME:-$HOME/.config}/docspell/ds.conf" dupes=n
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
help=y
|
||||
shift
|
||||
;;
|
||||
-c|--config)
|
||||
config="$2"
|
||||
shift 2
|
||||
;;
|
||||
-d|--delete)
|
||||
delete="y"
|
||||
shift
|
||||
;;
|
||||
-e|--exists)
|
||||
exists=y
|
||||
shift
|
||||
;;
|
||||
--allow-duplicates)
|
||||
dupes=y
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo "Programming error"
|
||||
exit 3
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
info() {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
checksum() {
|
||||
$SHA256_CMD "$1" | cut -d' ' -f1 | xargs
|
||||
}
|
||||
|
||||
checkFile() {
|
||||
local url=$(echo "$1" | sed 's,upload/item,checkfile,g')
|
||||
local file="$2"
|
||||
$CURL_CMD -XGET -s "$url/$(checksum "$file")" | (2>&1 1>/dev/null grep '"exists":true')
|
||||
}
|
||||
|
||||
upload_file() {
|
||||
tf=$($MKTEMP_CMD) rc=0
|
||||
META1=""
|
||||
META2=""
|
||||
if [ "$dupes" = "y" ]; then
|
||||
META1="-F"
|
||||
META2="meta={\"multiple\": false, \"skipDuplicates\": false}"
|
||||
else
|
||||
META1="-F"
|
||||
META2="meta={\"multiple\": false, \"skipDuplicates\": true}"
|
||||
fi
|
||||
$CURL_CMD -# -o "$tf" --stderr "$tf" -w "%{http_code}" -XPOST $META1 "$META2" -F file=@"$1" "$2" | (2>&1 1>/dev/null grep 200)
|
||||
rc=$(expr $rc + $?)
|
||||
cat $tf | (2>&1 1>/dev/null grep '{"success":true')
|
||||
rc=$(expr $rc + $?)
|
||||
if [ $rc -ne 0 ]; then
|
||||
info "Upload failed. Exit code: $rc"
|
||||
cat "$tf"
|
||||
echo ""
|
||||
rm "$tf"
|
||||
return $rc
|
||||
else
|
||||
rm "$tf"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
upload() {
|
||||
if [ "$dupes" == "y" ]; then
|
||||
upload_file "$1" "$2"
|
||||
else
|
||||
checkFile "$2" "$1"
|
||||
if [ $? -eq 0 ]; then
|
||||
info "File already exists at url $2"
|
||||
return 0
|
||||
else
|
||||
upload_file "$1" "$2"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
showUsage() {
|
||||
info "Upload files to docspell"
|
||||
info ""
|
||||
info "Usage: $0 [options] file [file ...]"
|
||||
info ""
|
||||
info "Options:"
|
||||
info " -c | --config Provide a config file. (value: $config)"
|
||||
info " -d | --delete Delete the files when successfully uploaded (value: $delete)"
|
||||
info " -h | --help Prints this help text. (value: $help)"
|
||||
info " -e | --exists Checks for the existence of a file instead of uploading (value: $exists)"
|
||||
info " --allow-duplicates Do not skip existing files in docspell (value: $dupes)"
|
||||
info ""
|
||||
info "Arguments:"
|
||||
info " One or more files to check for existence or upload."
|
||||
info ""
|
||||
}
|
||||
|
||||
if [ "$help" = "y" ]; then
|
||||
showUsage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# handle non-option arguments
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "$0: No files given."
|
||||
exit 4
|
||||
fi
|
||||
|
||||
|
||||
## Read the config file
|
||||
declare -a urls
|
||||
while IFS="=" read -r k v
|
||||
do
|
||||
if [[ $k == url* ]]; then
|
||||
urls+=($(echo "$v" | xargs))
|
||||
fi
|
||||
done <<< $($GREP_CMD -v '^#.*' "$config")
|
||||
|
||||
|
||||
## Main
|
||||
IFS=$'\n'
|
||||
for file in $*; do
|
||||
for url in "${urls[@]}"; do
|
||||
if [ "$exists" = "y" ]; then
|
||||
if checkFile "$url" "$file"; then
|
||||
info "$url $file: true"
|
||||
else
|
||||
info "$url $file: false"
|
||||
fi
|
||||
else
|
||||
info "Uploading '$file' to '$url'"
|
||||
set +e
|
||||
upload "$file" "$url"
|
||||
set -e
|
||||
if [ "$delete" = "y" ] && [ $? -eq 0 ]; then
|
||||
info "Deleting file: $file"
|
||||
rm -f "$file"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done
|
@ -1,256 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Simple script for downloading all your files. It goes through all
|
||||
# items visible to the logged in user and downloads the attachments
|
||||
# (the original files).
|
||||
#
|
||||
# The item's metadata are stored next to the files to provide more
|
||||
# information about the item: tags, dates, custom fields etc. This
|
||||
# contains most of your user supplied data.
|
||||
#
|
||||
# This script is intended for having your data outside and independent
|
||||
# of docspell. Another good idea for a backup strategy is to take
|
||||
# database dumps *and* storing the releases of docspell next to this
|
||||
# dump.
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# export-files.sh <docspell-base-url> <target-directory>
|
||||
#
|
||||
# The docspell base url is required as well as a directory to store
|
||||
# all the files into.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# export-files.sh http://localhost:7880 /tmp/ds-download
|
||||
#
|
||||
# The script then asks for username and password and starts
|
||||
# downloading. Files are downloaded into the following structure
|
||||
# (below the given target directory):
|
||||
#
|
||||
# - yyyy-mm (item date)
|
||||
# - A3…XY (item id)
|
||||
# - somefile.pdf (attachments with name)
|
||||
# - metadata.json (json file with items metadata)
|
||||
#
|
||||
# By default, files are not overwritten, it stops if existing files
|
||||
# are encountered. Configuration can be specified using environment
|
||||
# variables:
|
||||
#
|
||||
# - OVERWRITE_FILE= if `y` then overwriting existing files is ok.
|
||||
# - SKIP_FILE= if `y` then existing files are skipped (supersedes
|
||||
# OVERWRITE_FILE).
|
||||
# - DROP_ITEM= if `y` the item folder is removed before attempting to
|
||||
# download it. If this is set to `y` then the above options don't
|
||||
# make sense, since they operate on the files inside the item folder
|
||||
#
|
||||
# Docspell sends with each file its sha256 checksum via the ETag
|
||||
# header. This is used to do a integrity check after downloading.
|
||||
|
||||
CURL_CMD="curl"
|
||||
JQ_CMD="jq"
|
||||
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "The base-url to docspell is required."
|
||||
exit 1
|
||||
else
|
||||
BASE_URL="$1"
|
||||
shift
|
||||
fi
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "A directory is required to store the files into."
|
||||
exit 1
|
||||
else
|
||||
TARGET="$1"
|
||||
shift
|
||||
fi
|
||||
|
||||
set -o errexit -o pipefail -o noclobber -o nounset
|
||||
|
||||
LOGIN_URL="$BASE_URL/api/v1/open/auth/login"
|
||||
SEARCH_URL="$BASE_URL/api/v1/sec/item/search"
|
||||
INSIGHT_URL="$BASE_URL/api/v1/sec/collective/insights"
|
||||
DETAIL_URL="$BASE_URL/api/v1/sec/item"
|
||||
ATTACH_URL="$BASE_URL/api/v1/sec/attachment"
|
||||
|
||||
OVERWRITE_FILE=${OVERWRITE_FILE:-n}
|
||||
DROP_ITEM=${DROP_ITEM:-n}
|
||||
|
||||
errout() {
|
||||
>&2 echo "$@"
|
||||
}
|
||||
|
||||
trap "{ rm -f ${TMPDIR-:/tmp}/ds-export.*; }" EXIT
|
||||
|
||||
mcurl() {
|
||||
tmpfile1=$(mktemp -t "ds-export.XXXXX")
|
||||
tmpfile2=$(mktemp -t "ds-export.XXXXX")
|
||||
set +e
|
||||
"$CURL_CMD" -# --fail --stderr "$tmpfile1" -o "$tmpfile2" -H "X-Docspell-Auth: $auth_token" "$@"
|
||||
status=$?
|
||||
set -e
|
||||
if [ $status -ne 0 ]; then
|
||||
errout "$CURL_CMD -H 'X-Docspell-Auth: …' $@"
|
||||
errout "curl command failed (rc=$status)! Output is below."
|
||||
cat "$tmpfile1" >&2
|
||||
cat "$tmpfile2" >&2
|
||||
rm -f "$tmpfile1" "$tmpfile2"
|
||||
return 2
|
||||
else
|
||||
ret=$(cat "$tmpfile2")
|
||||
rm "$tmpfile2" "$tmpfile1"
|
||||
echo $ret
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
errout "Login to Docspell."
|
||||
errout "Using url: $BASE_URL"
|
||||
if [ -z "${DS_USER:-}" ]; then
|
||||
errout -n "Account: "
|
||||
read DS_USER
|
||||
fi
|
||||
if [ -z "${DS_PASS:-}" ]; then
|
||||
errout -n "Password: "
|
||||
read -s DS_PASS
|
||||
fi
|
||||
echo
|
||||
|
||||
declare auth
|
||||
declare auth_token
|
||||
declare auth_time
|
||||
|
||||
|
||||
login() {
|
||||
auth=$("$CURL_CMD" -s --fail -XPOST \
|
||||
--data-binary "{\"account\":\"$DS_USER\", \"password\":\"$DS_PASS\"}" "$LOGIN_URL")
|
||||
|
||||
if [ "$(echo $auth | "$JQ_CMD" .success)" == "true" ]; then
|
||||
errout "Login successful"
|
||||
auth_token=$(echo $auth | "$JQ_CMD" -r .token)
|
||||
auth_time=$(date +%s)
|
||||
else
|
||||
errout "Login failed."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
checkLogin() {
|
||||
elapsed=$((1000 * ($(date +%s) - $auth_time)))
|
||||
maxtime=$(echo $auth | "$JQ_CMD" .validMs)
|
||||
|
||||
elapsed=$(($elapsed + 1000))
|
||||
if [ $elapsed -gt $maxtime ]; then
|
||||
errout "Need to re-login $elapsed > $maxtime"
|
||||
login
|
||||
fi
|
||||
}
|
||||
|
||||
listItems() {
|
||||
OFFSET="${1:-0}"
|
||||
LIMIT="${2:-50}"
|
||||
errout "Get next items with offset=$OFFSET, limit=$LIMIT"
|
||||
REQ="{\"offset\":$OFFSET, \"limit\":$LIMIT, \"withDetails\":true, \"query\":\"\"}"
|
||||
|
||||
mcurl -XPOST -H 'ContentType: application/json' -d "$REQ" "$SEARCH_URL" | "$JQ_CMD" -r '.groups[].items[]|.id'
|
||||
}
|
||||
|
||||
fetchItemCount() {
|
||||
mcurl -XGET "$INSIGHT_URL" | "$JQ_CMD" '[.incomingCount, .outgoingCount] | add'
|
||||
}
|
||||
|
||||
fetchItem() {
|
||||
mcurl -XGET "$DETAIL_URL/$1"
|
||||
}
|
||||
|
||||
downloadAttachment() {
|
||||
attachId="$1"
|
||||
errout " - Download '$attachName' ($attachId)"
|
||||
|
||||
if [ -f "$attachOut" ] && [ "$SKIP_FILE" == "y" ]; then
|
||||
errout " - Skipping file '$attachOut' since it already exists"
|
||||
else
|
||||
if [ -f "$attachOut" ] && [ "$OVERWRITE_FILE" == "y" ]; then
|
||||
errout " - Removing attachment file as requested: $attachOut"
|
||||
rm -f "$attachOut"
|
||||
fi
|
||||
|
||||
checksum1=$("$CURL_CMD" -s -I -H "X-Docspell-Auth: $auth_token" "$ATTACH_URL/$attachId/original" | \
|
||||
grep -i 'etag' | cut -d':' -f2 | xargs | tr -d '\r')
|
||||
"$CURL_CMD" -s -o "$attachOut" -H "X-Docspell-Auth: $auth_token" "$ATTACH_URL/$attachId/original"
|
||||
checksum2=$(sha256sum "$attachOut" | cut -d' ' -f1 | xargs)
|
||||
if [ "$checksum1" == "$checksum2" ]; then
|
||||
errout " - Checksum ok."
|
||||
else
|
||||
errout " - WARNING: Checksum mismatch! Server: $checksum1 Downloaded: $checksum2"
|
||||
return 3
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
downloadItem() {
|
||||
checkLogin
|
||||
itemData=$(fetchItem "$1")
|
||||
errout "Get item $(echo $itemData | "$JQ_CMD" -r .id)"
|
||||
created=$(echo $itemData|"$JQ_CMD" '.created')
|
||||
created=$((($(echo $itemData|"$JQ_CMD" '.created') + 500) / 1000))
|
||||
itemId=$(echo $itemData | "$JQ_CMD" -r '.id')
|
||||
out="$TARGET/$(date -d @$created +%Y-%m)/$itemId"
|
||||
|
||||
if [ -d "$out" ] && [ "$DROP_ITEM" == "y" ]; then
|
||||
errout "Removing item folder as requested: $out"
|
||||
rm -rf "$out"
|
||||
fi
|
||||
|
||||
mkdir -p "$out"
|
||||
if [ -f "$out/metadata.json" ] && [ "$SKIP_FILE" == "y" ]; then
|
||||
errout " - Skipping file 'metadata.json' since it already exists"
|
||||
else
|
||||
if [ -f "$out/metadata.json" ] && [ "$OVERWRITE_FILE" == "y" ]; then
|
||||
errout " - Removing metadata.json as requested"
|
||||
rm -f "$out/metadata.json"
|
||||
fi
|
||||
echo $itemData > "$out/metadata.json"
|
||||
fi
|
||||
while read attachId attachName; do
|
||||
attachOut="$out/$attachName"
|
||||
checkLogin
|
||||
downloadAttachment "$attachId"
|
||||
done < <(echo $itemData | "$JQ_CMD" -r '.sources[] | [.id,.name] | join(" ")')
|
||||
}
|
||||
|
||||
login
|
||||
|
||||
allCount=$(fetchItemCount)
|
||||
errout "Downloading $allCount items…"
|
||||
|
||||
allCounter=0 innerCounter=0 limit=100 offset=0 done=n
|
||||
|
||||
while [ "$done" = "n" ]; do
|
||||
checkLogin
|
||||
|
||||
innerCounter=0
|
||||
while read id; do
|
||||
downloadItem "$id"
|
||||
innerCounter=$(($innerCounter + 1))
|
||||
done < <(listItems $offset $limit)
|
||||
|
||||
allCounter=$(($allCounter + $innerCounter))
|
||||
offset=$(($offset + $limit))
|
||||
|
||||
|
||||
if [ $innerCounter -lt $limit ]; then
|
||||
done=y
|
||||
fi
|
||||
|
||||
done
|
||||
errout "Downloaded $allCounter/$allCount items"
|
||||
if [[ $allCounter < $allCount ]]; then
|
||||
errout
|
||||
errout " Downloaded less items than were reported as available. This"
|
||||
errout " may be due to items in folders that you cannot see. Or it"
|
||||
errout " may be a bug."
|
||||
errout
|
||||
fi
|
@ -1,22 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# This script submits a job to regenerate all preview images. This may
|
||||
# be necessary if you change the dpi setting that affects the size of
|
||||
# the preview.
|
||||
|
||||
set -e
|
||||
|
||||
CURL_CMD="curl"
|
||||
JQ_CMD="jq"
|
||||
|
||||
|
||||
BASE_URL="${1:-http://localhost:7880}"
|
||||
TRIGGER_URL="$BASE_URL/api/v1/admin/attachments/generatePreviews"
|
||||
|
||||
echo "Login to trigger regenerating preview images."
|
||||
echo "Using url: $BASE_URL"
|
||||
echo -n "Admin Secret: "
|
||||
read -s ADMIN_SECRET
|
||||
echo
|
||||
|
||||
curl --fail -XPOST -H "Docspell-Admin-Secret: $ADMIN_SECRET" "$TRIGGER_URL"
|
@ -1,49 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# A script to reset a password.
|
||||
#
|
||||
# Usage:
|
||||
# ./reset-password.sh <baseurl> <admin-secret> <account>
|
||||
#
|
||||
# Example:
|
||||
# ./reset-password.sh http://localhost:7880 test123 your/account
|
||||
#
|
||||
|
||||
CURL_CMD="curl"
|
||||
JQ_CMD="jq"
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "The docspell base-url is required as first argument."
|
||||
exit 1
|
||||
else
|
||||
BASE_URL="$1"
|
||||
fi
|
||||
|
||||
if [ -z "$2" ]; then
|
||||
echo "The admin secret is required as second argument."
|
||||
exit 1
|
||||
else
|
||||
SECRET="$2"
|
||||
fi
|
||||
|
||||
if [ -z "$3" ]; then
|
||||
echo "The user account is required as third argument."
|
||||
exit 1
|
||||
else
|
||||
USER="$3"
|
||||
fi
|
||||
|
||||
RESET_URL="${BASE_URL}/api/v1/admin/user/resetPassword"
|
||||
|
||||
OUT=$("$CURL_CMD" -s -XPOST \
|
||||
-H "Docspell-Admin-Secret: $SECRET" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"account\": \"$USER\"}" \
|
||||
"$RESET_URL")
|
||||
|
||||
|
||||
if command -v "$JQ_CMD" > /dev/null; then
|
||||
echo $OUT | "$JQ_CMD"
|
||||
else
|
||||
echo $OUT
|
||||
fi
|
@ -4,10 +4,10 @@ base_url = "https://docspell.org"
|
||||
# Whether to automatically compile all Sass files in the sass directory
|
||||
compile_sass = true
|
||||
|
||||
[markdown]
|
||||
# Whether to do syntax highlighting
|
||||
# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
|
||||
highlight_code = true
|
||||
|
||||
highlight_theme = "gruvbox-dark"
|
||||
|
||||
# Whether to build a search index to be used later on by a JavaScript library
|
||||
|
@ -82,9 +82,9 @@ documentation, too.
|
||||
|
||||
In order to move to a different tool, it is necessary to get the data
|
||||
out of Docspell in a machine readable/automatic way. Currently, there
|
||||
is a [export-files.sh](@/docs/tools/export-files.md) script provided
|
||||
(in the `tools/` folder) that can be used to download all your files
|
||||
and item metadata.
|
||||
is a [export command](@/docs/tools/cli.md#export-data) in the command
|
||||
line client that can be used to download all your files and item
|
||||
metadata.
|
||||
|
||||
My recommendation is to run periodic database backups and also store
|
||||
the binaries/docker images. This lets you re-create the current state
|
||||
|
@ -7,8 +7,9 @@ To get started, here are some quick links:
|
||||
|
||||
- Using [docker and docker-compose](@/docs/install/docker.md). This
|
||||
sets up everything: all prerequisites, both docspell components and
|
||||
a container running the [consumedir.sh](@/docs/tools/consumedir.md)
|
||||
script to import files that are dropped in a folder.
|
||||
a container running the [dsc
|
||||
watch](@/docs/tools/cli.md#watch-a-directory) script to import files
|
||||
that are dropped in a folder.
|
||||
- [Download, Unpack and Run](@/docs/install/download_run.md). This
|
||||
option is also very quick, but you need to check the
|
||||
[prerequisites](@/docs/install/prereq.md) yourself. Database is
|
||||
@ -27,9 +28,9 @@ To get started, here are some quick links:
|
||||
thread](https://forums.unraid.net/topic/103425-docspell-hilfe/) in
|
||||
the German Unraid forum. Thanks for providing these!
|
||||
|
||||
Every [component](@/docs/intro/_index.md#components) (restserver, joex,
|
||||
consumedir) can run on different machines and multiple times. Most of
|
||||
the time running all on one machine is sufficient and also for
|
||||
Every [component](@/docs/intro/_index.md#components) (restserver,
|
||||
joex, dsc watch) can run on different machines and multiple times.
|
||||
Most of the time running all on one machine is sufficient and also for
|
||||
simplicity, the docker-compose setup reflects this variant.
|
||||
|
||||
While there are many different ways to run docspell, at some point all
|
||||
|
@ -167,7 +167,9 @@ directories.
|
||||
|
||||
The `watch` subcommand can be used to watch one or more directories
|
||||
and upload files when they arrive. It uses the `upload` command under
|
||||
the hood and therefore most options are also available here.
|
||||
the hood and therefore most options are also available here. You can
|
||||
upload via a source url, the integration endpoint or a valid session
|
||||
(requires to login).
|
||||
|
||||
It detects file creations and skips a rename within a watched folder.
|
||||
The flag `-r` or `--recursive` is required to recursively watch a
|
||||
@ -191,6 +193,10 @@ If watching a directory is not possible due to system constraints
|
||||
use the `upload` subcommand with `--poll` option which periodically
|
||||
traverses a directory.
|
||||
|
||||
When using the integration endpoint, it requires to specify `-i` and
|
||||
potentially a secret if the endpoint is protected with a secret.
|
||||
|
||||
|
||||
## Download files
|
||||
|
||||
The `download` command allows to download files that match a given
|
||||
@ -303,7 +309,8 @@ commands require the [admin
|
||||
secret](@/docs/configure/_index.md#admin-endpoint) either in the
|
||||
config file or as an argument.
|
||||
|
||||
Reset user password:
|
||||
### Reset user password
|
||||
|
||||
``` shell
|
||||
❯ dsc admin reset-password --account demo
|
||||
┌─────────┬──────────────┬──────────────────┐
|
||||
@ -313,7 +320,8 @@ Reset user password:
|
||||
└─────────┴──────────────┴──────────────────┘
|
||||
```
|
||||
|
||||
Recreate fulltext index:
|
||||
### Recreate fulltext index
|
||||
|
||||
``` shell
|
||||
❯ dsc admin --admin-secret admin123 recreate-index
|
||||
┌─────────┬─────────────────────────────────────┐
|
||||
@ -323,6 +331,34 @@ Recreate fulltext index:
|
||||
└─────────┴─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Convert all files to PDF
|
||||
``` shell
|
||||
❯ dsc admin --admin-secret admin123 convert-all-pdf
|
||||
┌─────────┬─────────────────────────────────┐
|
||||
│ success │ message │
|
||||
├─────────┼─────────────────────────────────┤
|
||||
│ true │ Convert all PDFs task submitted │
|
||||
└─────────┴─────────────────────────────────┘
|
||||
```
|
||||
|
||||
This may be necessary if you disabled pdf conversion before and are
|
||||
enabling it now.
|
||||
|
||||
### Regenerate preview images
|
||||
|
||||
``` shell
|
||||
❯ dsc admin --admin-secret admin123 convert-all-pdf
|
||||
┌─────────┬───────────────────────────────────────┐
|
||||
│ success │ message │
|
||||
├─────────┼───────────────────────────────────────┤
|
||||
│ true │ Generate all previews task submitted. │
|
||||
└─────────┴───────────────────────────────────────┘
|
||||
```
|
||||
|
||||
This submits tasks to (re)generate preview images of all files. This
|
||||
is necessary if you changed the `preview.dpi` setting in joex'
|
||||
config.
|
||||
|
||||
## Search for items
|
||||
|
||||
The `search` command takes a [query](@/docs/query/_index.md) and
|
||||
|
@ -1,58 +0,0 @@
|
||||
+++
|
||||
title = "Directory Cleaner (⊗)"
|
||||
description = "Clean directories from files in docspell"
|
||||
weight = 150
|
||||
+++
|
||||
|
||||
{% infobubble(mode="info", title="⚠ Please note") %}
|
||||
This script is now obsolete, you can use the [**CLI tool**](../cli/) instead.
|
||||
|
||||
Use the `cleanup` or the `upload` command.
|
||||
{% end %}
|
||||
|
||||
# Introduction
|
||||
|
||||
This script is made for cleaning up the consumption directory used for
|
||||
the consumedir service (as it is provided as docker container)which
|
||||
are copied or moved there.
|
||||
|
||||
<https://github.com/eikek/docspell/tree/master/tools/consumedir-cleaner>
|
||||
|
||||
## How it works
|
||||
|
||||
- Checks for every file (in the collective's folder of the given user
|
||||
name) if it already exists in the collective (using Docspell's API).
|
||||
- If so, by default those files are moved to an archive folder just
|
||||
besides the collective's consumption folders named _archive. The
|
||||
archive's files are organized into monthly subfolders by the date
|
||||
they've been added to Docspell
|
||||
- If set, those files can also be deleted instead of being moved to
|
||||
the archive. There is no undo function provided for this, so be
|
||||
careful.
|
||||
- If a file is found which does not exist in the collective, by
|
||||
default nothing happens, so that file would be found in every run
|
||||
and just ignored
|
||||
- If set, those files can also be uploaded to Docspell. Depending on
|
||||
the setting for files already existing these files would either be
|
||||
deleted or moved to the archive in the next run.
|
||||
|
||||
## Usage (parameters / settings)
|
||||
|
||||
Copy the script to your machine and run it with the following
|
||||
parameters:
|
||||
|
||||
1. URL of Docspell, including http(s)
|
||||
2. Username for Docspell, possibly including Collective (if other name
|
||||
as user)
|
||||
3. Password for Docspell
|
||||
4. Path to the directory which files shall be checked against
|
||||
existence in Docspell
|
||||
|
||||
Additionally, environment variables can be used to alter the behavior:
|
||||
|
||||
- `DS_CC_REMOVE`
|
||||
- `true` – delete files which already exist in the collective
|
||||
- `false` (default) - move them to the archive (see above)
|
||||
- `DS_CC_UPLOAD_MISSING`
|
||||
- `true` - uploads files which do not exist in the collective
|
||||
- `false` (default) - ignore them and do nothing
|
@ -1,191 +0,0 @@
|
||||
+++
|
||||
title = "Consume Directory (⊗)"
|
||||
description = "A script to watch a directory for new files and upload them to docspell."
|
||||
weight = 110
|
||||
+++
|
||||
|
||||
|
||||
{% infobubble(mode="info", title="⚠ Please note") %}
|
||||
This script is now obsolete, you can use the [**CLI tool**](../cli/) instead.
|
||||
|
||||
You can use the `watch` command, or the `upload` command with `--poll`.
|
||||
{% end %}
|
||||
|
||||
# Introduction
|
||||
|
||||
The `consumerdir.sh` is a bash script that works in two modes:
|
||||
|
||||
- Go through all files in given directories (recursively, if `-r` is
|
||||
specified) and sent each to docspell.
|
||||
- Watch one or more directories for new files and upload them to
|
||||
docspell.
|
||||
|
||||
It can watch or go through one or more directories. Files can be
|
||||
uploaded to multiple urls.
|
||||
|
||||
Run the script with the `-h` or `--help` option, to see a short help
|
||||
text. The help text will also show the values for any given option.
|
||||
|
||||
The script requires `curl` for uploading. It requires the
|
||||
`inotifywait` command if directories should be watched for new
|
||||
files.
|
||||
|
||||
Example for watching two directories:
|
||||
|
||||
``` bash
|
||||
./tools/consumedir.sh --path ~/Downloads --path ~/pdfs -m -dv \
|
||||
http://localhost:7880/api/v1/open/upload/item/5DxhjkvWf9S-CkWqF3Kr892-WgoCspFWDo7-XBykwCyAUxQ
|
||||
```
|
||||
|
||||
The script by default watches the given directories. If the `-o` or
|
||||
`--once` option is used, it will instead go through these directories
|
||||
and upload all files in there. For directory watching the
|
||||
`inotifywait` command is used and must be present. Another way is to
|
||||
use the `--poll` option. It expects the number of seconds to wait
|
||||
between running itself with `--once`.
|
||||
|
||||
Example using active polling (at 5 minutes interval):
|
||||
``` bash
|
||||
./tools/consumedir.sh --poll 300 --path ~/Downloads --path ~/pdfs -m -dv \
|
||||
http://localhost:7880/api/v1/open/upload/item/5DxhjkvWf9S-CkWqF3Kr892-WgoCspFWDo7-XBykwCyAUxQ
|
||||
```
|
||||
|
||||
Example for uploading all immediatly (the same as above only with `-o`
|
||||
added):
|
||||
|
||||
``` bash
|
||||
$ ./tools/consumedir.sh --once --path ~/Downloads --path ~/pdfs/ -m -dv \
|
||||
http://localhost:7880/api/v1/open/upload/item/5DxhjkvWf9S-CkWqF3Kr892-WgoCspFWDo7-XBykwCyAUxQ
|
||||
```
|
||||
|
||||
|
||||
The URL can be any docspell url that accepts uploads without
|
||||
authentication. This is usually a [source
|
||||
url](@/docs/webapp/uploading.md#anonymous-upload). It is also possible
|
||||
to use the script with the [integration
|
||||
endpoint](@/docs/api/upload.md#integration-endpoint).
|
||||
|
||||
The script can be run multiple times and on on multiple machines, the
|
||||
files are transferred via HTTP to the docspell server. For example, it
|
||||
is convenient to set it up on your workstation, so that you can drop
|
||||
files into some local folder to be immediatly transferred to docspell
|
||||
(e.g. when downloading something from the browser).
|
||||
|
||||
## Integration Endpoint
|
||||
|
||||
When given the `-i` or `--integration` option, the script changes its
|
||||
behaviour slightly to work with the [integration
|
||||
endpoint](@/docs/api/upload.md#integration-endpoint).
|
||||
|
||||
First, if `-i` is given, it implies `-r` – so the directories are
|
||||
watched or traversed recursively. The script then assumes that there
|
||||
is a subfolder with the collective name. Files must not be placed
|
||||
directly into a folder given by `-p`, but below a sub-directory that
|
||||
matches a collective name. In order to know for which collective the
|
||||
file is, the script uses the first subfolder.
|
||||
|
||||
If the endpoint is protected, the credentials can be specified as
|
||||
arguments `--iuser` and `--iheader`, respectively. The format is for
|
||||
both `<name>:<value>`, so the username cannot contain a colon
|
||||
character (but the password can).
|
||||
|
||||
Example:
|
||||
``` bash
|
||||
$ consumedir.sh -i -iheader 'Docspell-Integration:test123' -m -p ~/Downloads/ http://localhost:7880/api/v1/open/integration/item
|
||||
```
|
||||
|
||||
The url is the integration endpoint url without the collective, as
|
||||
this is amended by the script.
|
||||
|
||||
This watches the folder `~/Downloads`. If a file is placed in this
|
||||
folder directly, say `~/Downloads/test.pdf` the upload will fail,
|
||||
because the collective cannot be determined. Create a subfolder below
|
||||
`~/Downloads` with the name of a collective, for example
|
||||
`~/Downloads/family` and place files somewhere below this `family`
|
||||
subfolder, like `~/Downloads/family/test.pdf`.
|
||||
|
||||
|
||||
## Duplicates
|
||||
|
||||
With the `-m` option, the script will not upload files that already
|
||||
exist at docspell. For this the `sha256sum` command is required.
|
||||
|
||||
So you can move and rename files in those folders without worring
|
||||
about duplicates. This allows to keep your files organized using the
|
||||
file-system and have them mirrored into docspell as well.
|
||||
|
||||
|
||||
## Network Filesystems (samba cifs, nfs)
|
||||
|
||||
Watching a directory for changes relies on `inotify` subsystem on
|
||||
linux. This doesn't work on network filesystems like nfs or cifs. Here
|
||||
are some ideas to get around this limitation:
|
||||
|
||||
1. The `consumedir.sh` is just a shell script and doesn't need to run
|
||||
on the same machine as docspell. (Note that the default docker
|
||||
setup is mainly for demoing and quickstart, it's not required to
|
||||
run all of them on one machine). So the best option is to put the
|
||||
consumedir on the machine that contains the local filesystem. All
|
||||
files are send via HTTP to the docspell server anyways, so there is
|
||||
no need to first transfer them via a network filesystem or rsync.
|
||||
2. If option 1 is not possible for some reason, and you need to check
|
||||
a network filesystem, the only option left (that I know) is to
|
||||
periodically poll this directory. This is also possible with
|
||||
consumedir, using the `--poll` option (see above). You can also
|
||||
setup a systemd timer to periodically run this script with the
|
||||
`--once` option.
|
||||
3. Copy the files to the machine that runs consumedir, via rsync for
|
||||
example. Note that this has no advantage over otpion 1, as you now
|
||||
need to setup rsync on the other machine to run either periodically
|
||||
or when some file arrives. Then you can as well run the consumedir
|
||||
script. But it might be more convenient, if rsync is already
|
||||
running.
|
||||
|
||||
# Systemd
|
||||
|
||||
The script can be used with systemd to run as a service. This is an
|
||||
example unit file:
|
||||
|
||||
``` systemd
|
||||
[Unit]
|
||||
After=networking.target
|
||||
Description=Docspell Consumedir
|
||||
|
||||
[Service]
|
||||
Environment="PATH=/set/a/path"
|
||||
|
||||
ExecStart=/bin/su -s /bin/bash someuser -c "consumedir.sh --path '/a/path/' -m 'http://localhost:7880/api/v1/open/upload/item/5DxhjkvWf9S-CkWqF3Kr892-WgoCspFWDo7-XBykwCyAUxQ'"
|
||||
```
|
||||
|
||||
This unit file is just an example, it needs some fiddling. It assumes
|
||||
an existing user `someuser` that is used to run this service. The url
|
||||
`http://localhost:7880/api/v1/open/upload/...` is an anonymous upload
|
||||
url as described [here](@/docs/webapp/uploading.md#anonymous-upload).
|
||||
|
||||
|
||||
# Docker
|
||||
|
||||
The provided docker-compose setup runs this script to watch a single
|
||||
directory, `./docs` in current directory, for new files. If a new file
|
||||
is detected, it is pushed to docspell.
|
||||
|
||||
This utilizes the [integration
|
||||
endpoint](@/docs/api/upload.md#integration-endpoint), which is
|
||||
enabled in the config file, to allow uploading documents for all
|
||||
collectives. A subfolder must be created for each registered
|
||||
collective. The docker containers are configured to use http-header
|
||||
protection for the integration endpoint. This requires you to provide
|
||||
a secret, that is shared between the rest-server and the
|
||||
`consumedir.sh` script. This can be done by defining an environment
|
||||
variable which gets picked up by the containers defined in
|
||||
`docker-compose.yml`:
|
||||
|
||||
``` bash
|
||||
export DOCSPELL_HEADER_VALUE="my-secret"
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
|
||||
Now you can create a folder `./docs/<collective-name>` and place all
|
||||
files in there that you want to import. Once dropped in this folder
|
||||
the `consumedir` container will push it to docspell.
|
@ -1,59 +0,0 @@
|
||||
+++
|
||||
title = "Convert All PDFs (⊗)"
|
||||
description = "Convert all PDF files using OcrMyPdf."
|
||||
weight = 160
|
||||
+++
|
||||
|
||||
{% infobubble(mode="info", title="⚠ Please note") %}
|
||||
This script is now obsolete, you can use the [**CLI tool**](../cli/) instead.
|
||||
|
||||
Use the `convert-all-pdfs` admin command, e.g. `dsc admin
|
||||
convert-all-pdfs`.
|
||||
{% end %}
|
||||
|
||||
|
||||
# convert-all-pdf.sh
|
||||
|
||||
With version 0.9.0 there was support added for another external tool,
|
||||
[OCRMyPdf](https://github.com/jbarlow83/OCRmyPDF), that can convert
|
||||
PDF files such that they contain the OCR-ed text layer. This tool is
|
||||
optional and can be disabled.
|
||||
|
||||
In order to convert all previously processed files with this tool,
|
||||
there is an
|
||||
[endpoint](/openapi/docspell-openapi.html#api-Item-secItemConvertallpdfsPost)
|
||||
that submits a task to convert all PDF files not already converted for
|
||||
your collective.
|
||||
|
||||
There is no UI part to trigger this route, so you need to use curl or
|
||||
the script `convert-all-pdfs.sh` in the `tools/` directory.
|
||||
|
||||
# Requirements
|
||||
|
||||
It is a bash script that additionally needs
|
||||
[curl](https://curl.haxx.se/) and
|
||||
[jq](https://stedolan.github.io/jq/).
|
||||
|
||||
# Usage
|
||||
|
||||
```
|
||||
./convert-all-pdfs.sh [docspell-base-url]
|
||||
```
|
||||
|
||||
For example, if docspell is at `http://localhost:7880`:
|
||||
|
||||
```
|
||||
./convert-all-pdfs.sh http://localhost:7880
|
||||
```
|
||||
|
||||
The script asks for your account name and password. It then logs in
|
||||
and triggers the said endpoint. After this you should see a few tasks
|
||||
running.
|
||||
|
||||
There will be one task per file to convert. All these tasks are
|
||||
submitted with a low priority. So files uploaded through the webapp or
|
||||
a [source](@/docs/webapp/uploading.md#anonymous-upload) with a high
|
||||
priority, will be preferred as [configured in the job
|
||||
executor](@/docs/joex/intro.md#scheduler-config). This is to not
|
||||
disturb normal processing when many conversion tasks are being
|
||||
executed.
|
@ -1,54 +0,0 @@
|
||||
+++
|
||||
title = "Upload CLI (⊗)"
|
||||
description = "A script to quickly upload files from the command line."
|
||||
weight = 100
|
||||
+++
|
||||
|
||||
|
||||
{% infobubble(mode="info", title="⚠ Please note") %}
|
||||
This script is now obsolete, you can use the [**CLI tool**](../cli/) instead.
|
||||
|
||||
Use the `upload` command (or the `up` alias), like `dsc up *.pdf`.
|
||||
{% end %}
|
||||
|
||||
# Introduction
|
||||
|
||||
The `tools/ds.sh` is a bash script to quickly upload files from the
|
||||
command line. It reads a configuration file containing the URLs to
|
||||
upload to. Then each file given to the script will be uploaded to al
|
||||
URLs in the config.
|
||||
|
||||
The config file is expected in
|
||||
`$XDG_CONFIG_HOME/docspell/ds.conf`. `$XDG_CONFIG_HOME` defaults to
|
||||
`~/.config`.
|
||||
|
||||
The config file contains lines with key-value pairs, separated by a
|
||||
`=` sign. Lines starting with `#` are ignored. Example:
|
||||
|
||||
```
|
||||
# Config file
|
||||
url.1 = http://localhost:7880/api/v1/open/upload/item/5DxhjkvWf9S-CkWqF3Kr892-WgoCspFWDo7-XBykwCyAUxQ
|
||||
url.2 = http://localhost:7880/api/v1/open/upload/item/6DxhjkvWf9S-CkWqF3Kr892-WgoCspFWDo7-XBykwCyAUxQ
|
||||
```
|
||||
|
||||
The key must start with `url`. The urls should be [anonymous upload
|
||||
urls](@/docs/webapp/uploading.md#anonymous-upload).
|
||||
|
||||
|
||||
# Usage
|
||||
|
||||
- The `-c` option allows to specifiy a different config file.
|
||||
- The `-h` option shows a help overview.
|
||||
- The `-d` option deletes files after upload was successful
|
||||
- The `-e` option can be used to check for file existence in docspell.
|
||||
Instead of uploading, the script only checks whether the file is in
|
||||
docspell or not.
|
||||
|
||||
The script takes a list of files as arguments.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
``` bash
|
||||
./ds.sh ~/Downloads/*.pdf
|
||||
```
|
@ -1,215 +0,0 @@
|
||||
+++
|
||||
title = "Export Files (⊗)"
|
||||
description = "Downloads all files from docspell."
|
||||
weight = 165
|
||||
+++
|
||||
|
||||
{% infobubble(mode="info", title="⚠ Please note") %}
|
||||
This script is now obsolete, you can use the [**CLI tool**](../cli/) instead.
|
||||
|
||||
Use the `export` command, e.g. `dsc export --all --target .`.
|
||||
{% end %}
|
||||
|
||||
|
||||
# export-files.sh
|
||||
|
||||
This script can be used to download all files from docspell that have
|
||||
been uploaded before and the item metadata.
|
||||
|
||||
It downloads the original files, those that have been uploaded and not
|
||||
the converted pdf files.
|
||||
|
||||
The item's metadata are stored next to the files to provide more
|
||||
information about the item: corresponent, tags, dates, custom fields
|
||||
etc. This contains most of your user supplied data.
|
||||
|
||||
This script is intended for having your data outside and independent
|
||||
of docspell. Another good idea for a backup strategy is to take
|
||||
database dumps *and* storing the releases of docspell next to this
|
||||
dump.
|
||||
|
||||
Files are stored into the following folder structure (below the given
|
||||
target directory):
|
||||
|
||||
```
|
||||
- yyyy-mm (item date)
|
||||
- A3…XY (item id)
|
||||
- somefile.pdf (attachments with name)
|
||||
- metadata.json (json file with items metadata)
|
||||
```
|
||||
|
||||
By default, files are not overwritten, it stops if existing files are
|
||||
encountered. This and some other things can be changed using
|
||||
environment variables:
|
||||
|
||||
- `DS_USER` the account name for login, it is asked if not available
|
||||
- `DS_PASS` the password for login, it is asked if not available
|
||||
- `OVERWRITE_FILE=` if `y` then overwriting existing files is ok.
|
||||
Default is `n`.
|
||||
- `SKIP_FILE=` if `y` then existing files are skipped (supersedes
|
||||
`OVERWRITE_FILE`). Default is `n`.
|
||||
- `DROP_ITEM=` if `y` the item folder is removed before attempting to
|
||||
download it. If this is set to `y` then the above options don't make
|
||||
sense, since they operate on the files inside the item folder.
|
||||
Default is `n`.
|
||||
|
||||
Docspell sends the sha256 hash with each file via the ETag header.
|
||||
This is used to do a integrity check after downloading.
|
||||
|
||||
|
||||
# Requirements
|
||||
|
||||
It is a bash script that additionally needs
|
||||
[curl](https://curl.haxx.se/) and [jq](https://stedolan.github.io/jq/)
|
||||
to be available.
|
||||
|
||||
# Usage
|
||||
|
||||
```
|
||||
./export-files.sh <docspell-base-url> <target-directory>
|
||||
```
|
||||
|
||||
For example, if docspell is at `http://localhost:7880`:
|
||||
|
||||
```
|
||||
./export-files.sh http://localhost:7880 /tmp/ds-downloads
|
||||
```
|
||||
|
||||
The script asks for your account name and password. It then logs in
|
||||
and goes through all items downloading the metadata as json and the
|
||||
attachments.
|
||||
|
||||
|
||||
# Example Run
|
||||
|
||||
``` bash
|
||||
fish> env SKIP_FILE=y DS_USER=demo DS_PASS=test ./export-files.sh http://localhost:7880 /tmp/download
|
||||
Login to Docspell.
|
||||
Using url: http://localhost:7880
|
||||
|
||||
Login successful
|
||||
Downloading 73 items…
|
||||
Get next items with offset=0, limit=100
|
||||
Get item 57Znskthf3g-X7RP1fxzE2U-dwr4vM6Yjnn-b7s1PoCznhz
|
||||
- Download 'something.txt' (8HbeFornAUN-kBCyc8bHSVr-bnLBYDzgRQ7-peMZzyTzM2X)
|
||||
- Checksum ok.
|
||||
Get item 94u5Pt39q6N-7vKu3LugoRj-zohGS4ie4jb-68bW5gXU6Jd
|
||||
- Download 'letter-en.pdf' (6KNNmoyqpew-RAkdwEmQgBT-QDqdY97whZA-4k2rmbssdfQ)
|
||||
- Checksum ok.
|
||||
Get item 7L9Fh53RVG4-vGSt2G2YUcY-cvpBKRXQgBn-omYpg6xQXyD
|
||||
- Download 'mail.html' (A6yTYKrDc7y-xU3whmLB1kB-TGhEAVb12mo-RUw5u9PsYMo)
|
||||
- Checksum ok.
|
||||
Get item DCn9UtWUtvF-2qjxB5PXGEG-vqRUUU7JUJH-zBBrmSeGYPe
|
||||
- Download 'Invoice_7340224.pdf' (6FWdjxJh7yB-CCjY39p6uH9-uVLbmGfm25r-cw6RksrSx4n)
|
||||
- Checksum ok.
|
||||
…
|
||||
```
|
||||
|
||||
The resulting directory looks then like this:
|
||||
|
||||
``` bash
|
||||
…
|
||||
├── 2020-08
|
||||
│ ├── 6t27gQQ4TfW-H4uAmkYyiSe-rBnerFE2v5F-9BdqbGEhMcv
|
||||
│ │ ├── 52241.pdf
|
||||
│ │ └── metadata.json
|
||||
│ └── 9qwT2GuwEvV-s9UuBQ4w7o9-uE8AdMc7PwL-GFDd62gduAm
|
||||
│ ├── DOC-20191223-155707.jpg
|
||||
│ └── metadata.json
|
||||
├── 2020-09
|
||||
│ ├── 2CM8C9VaVAT-sVJiKyUPCvR-Muqr2Cqvi6v-GXhRtg6eomA
|
||||
│ │ ├── letter with spaces.pdf
|
||||
│ │ └── metadata.json
|
||||
│ ├── 4sXpX2Sc9Ex-QX1M6GtjiXp-DApuDDzGQXR-7pg1QPW9pbs
|
||||
│ │ ├── analyse.org
|
||||
│ │ ├── 201703.docx
|
||||
│ │ ├── 11812_120719.pdf
|
||||
│ │ ├── letter-de.pdf
|
||||
│ │ ├── letter-en.pdf
|
||||
│ │ └── metadata.json
|
||||
│ ├── 5VhP5Torsy1-15pwJBeRjPi-es8BGnxhWn7-3pBQTJv3zPb
|
||||
│ │ └── metadata.json
|
||||
│ ├── 7ePWmK4xCNk-gmvnTDdFwG8-JcN5MDSUNPL-NTZZrho2Jc6
|
||||
│ │ ├── metadata.json
|
||||
│ │ └── Rechnung.pdf
|
||||
…
|
||||
```
|
||||
|
||||
The `metadata.json` file contains all the item metadata. This may be
|
||||
useful when importing into other tools.
|
||||
|
||||
``` json
|
||||
{
|
||||
"id": "AWCNx7tJgUw-SdrNtRouNJB-FGs6Y2VP5bV-218sFN8mjjk",
|
||||
"direction": "incoming",
|
||||
"name": "Ruecksendung.pdf",
|
||||
"source": "integration",
|
||||
"state": "confirmed",
|
||||
"created": 1606171810005,
|
||||
"updated": 1606422917826,
|
||||
"itemDate": null,
|
||||
"corrOrg": null,
|
||||
"corrPerson": null,
|
||||
"concPerson": null,
|
||||
"concEquipment": null,
|
||||
"inReplyTo": null,
|
||||
"folder": null,
|
||||
"dueDate": null,
|
||||
"notes": null,
|
||||
"attachments": [
|
||||
{
|
||||
"id": "4aPmhrjfR9Z-AgknoW6yVoE-YkffioD2KXV-E6Vm6snH17Q",
|
||||
"name": "Ruecksendung.converted.pdf",
|
||||
"size": 57777,
|
||||
"contentType": "application/pdf",
|
||||
"converted": true
|
||||
}
|
||||
],
|
||||
"sources": [
|
||||
{
|
||||
"id": "4aPmhrjfR9Z-AgknoW6yVoE-YkffioD2KXV-E6Vm6snH17Q",
|
||||
"name": "Ruecksendung.pdf",
|
||||
"size": 65715,
|
||||
"contentType": "application/pdf"
|
||||
}
|
||||
],
|
||||
"archives": [],
|
||||
"tags": [
|
||||
{
|
||||
"id": "EQvJ6AHw19Y-Cdg3gF78zZk-BY2zFtNTwes-J95jpXpzhfw",
|
||||
"name": "Hupe",
|
||||
"category": "state",
|
||||
"created": 1606427083171
|
||||
},
|
||||
{
|
||||
"id": "4xyZoeeELdJ-tJ91GiRLinJ-7bdauy3U1jR-Bzr4VS96bGS",
|
||||
"name": "Invoice",
|
||||
"category": "doctype",
|
||||
"created": 1594249709473
|
||||
}
|
||||
],
|
||||
"customfields": [
|
||||
{
|
||||
"id": "5tYmDHin3Kx-HomKkeEVtJN-v99oKxQ8ot6-yFVrEmMayoo",
|
||||
"name": "amount",
|
||||
"label": "EUR",
|
||||
"ftype": "money",
|
||||
"value": "151.55"
|
||||
},
|
||||
{
|
||||
"id": "3jbwbep8rDs-hNJ9ePRE7gv-21nYMbUj3eb-mKRWAr4xSS2",
|
||||
"name": "invoice-number",
|
||||
"label": "Invoice-Nr",
|
||||
"ftype": "text",
|
||||
"value": "I454602"
|
||||
},
|
||||
{
|
||||
"id": "AH4p4NUCa9Y-EUkH66wLzxE-Rf2wJPxTAYd-DeGDm4AT4Yg",
|
||||
"name": "number",
|
||||
"label": "Number",
|
||||
"ftype": "numeric",
|
||||
"value": "0.10"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
@ -1,48 +0,0 @@
|
||||
+++
|
||||
title = "Regenerate Preview Images (⊗)"
|
||||
description = "Re-generates all preview images."
|
||||
weight = 130
|
||||
+++
|
||||
|
||||
{% infobubble(mode="info", title="⚠ Please note") %}
|
||||
This script is now obsolete, you can use the [**CLI tool**](../cli/) instead.
|
||||
|
||||
Use the `generate-previews` admin command, e.g. `dsc admin generate-previews`.
|
||||
{% end %}
|
||||
|
||||
# regenerate-previews.sh
|
||||
|
||||
This is a simple bash script to trigger the endpoint that submits task
|
||||
for generating preview images of your files. This is usually not
|
||||
needed, but should you change the `preview.dpi` setting in joex'
|
||||
config file, you need to regenerate the images to have any effect.
|
||||
|
||||
# Requirements
|
||||
|
||||
It is a bash script that additionally needs
|
||||
[curl](https://curl.haxx.se/) and
|
||||
[jq](https://stedolan.github.io/jq/).
|
||||
|
||||
# Usage
|
||||
|
||||
```
|
||||
./regenerate-previews.sh [docspell-base-url] [admin-secret]
|
||||
```
|
||||
|
||||
For example, if docspell is at `http://localhost:7880`:
|
||||
|
||||
```
|
||||
./convert-all-pdfs.sh http://localhost:7880 test123
|
||||
```
|
||||
|
||||
The script asks for the admin secret if not given to the command. It
|
||||
then logs in and triggers the said endpoint. After this you should see
|
||||
a few tasks running.
|
||||
|
||||
There will be one task per file to convert. All these tasks are
|
||||
submitted with a low priority. So files uploaded through the webapp or
|
||||
a [source](@/docs/webapp/uploading.md#anonymous-upload) with a high
|
||||
priority, will be preferred as [configured in the job
|
||||
executor](@/docs/joex/intro.md#scheduler-config). This is to not
|
||||
disturb normal processing when many conversion tasks are being
|
||||
executed.
|
@ -1,47 +0,0 @@
|
||||
+++
|
||||
title = "Reset Password (⊗)"
|
||||
description = "Resets a user password."
|
||||
weight = 120
|
||||
+++
|
||||
|
||||
{% infobubble(mode="info", title="⚠ Please note") %}
|
||||
This script is now obsolete, you can use the [**CLI tool**](../cli/) instead.
|
||||
|
||||
Use the `reset-password` admin command, e.g. `dsc admin reset-password
|
||||
--account "smith/john"`, where `smith` is the collective id and `john`
|
||||
the username.
|
||||
{% end %}
|
||||
|
||||
|
||||
This script can be used to reset a user password. This can be done by
|
||||
admins, who know the `admin-endpoint.secret` value in the
|
||||
[configuration](@/docs/configure/_index.md#admin-endpoint) file.
|
||||
|
||||
The script is in `/tools/reset-password/reset-password.sh` and it is
|
||||
only a wrapper around the admin endpoint `/admin/user/resetPassword`.
|
||||
|
||||
## Usage
|
||||
|
||||
It's very simple:
|
||||
|
||||
``` bash
|
||||
reset-password.sh <base-url> <admin-secret> <account>
|
||||
```
|
||||
|
||||
Three arguments are required to specify the docspell base url, the
|
||||
admin secret and the account you want to reset the password.
|
||||
|
||||
After the password has been reset, the user can login using it and
|
||||
change it again in the webapp.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
``` json
|
||||
❯ ./tools/reset-password/reset-password.sh http://localhost:7880 123 eike
|
||||
{
|
||||
"success": true,
|
||||
"newPassword": "HjtpG9BFo9y",
|
||||
"message": "Password updated"
|
||||
}
|
||||
```
|
Loading…
x
Reference in New Issue
Block a user