diff --git a/docker/dockerfiles/build.sh b/docker/dockerfiles/build.sh index 042edfbb..537cdf5e 100755 --- a/docker/dockerfiles/build.sh +++ b/docker/dockerfiles/build.sh @@ -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 \ diff --git a/docker/dockerfiles/tools.dockerfile b/docker/dockerfiles/tools.dockerfile deleted file mode 100644 index cb6b85ea..00000000 --- a/docker/dockerfiles/tools.dockerfile +++ /dev/null @@ -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 " - -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*")' diff --git a/tools/consumedir-cleaner/consumedir-cleaner.sh b/tools/consumedir-cleaner/consumedir-cleaner.sh deleted file mode 100755 index 5c7a57c0..00000000 --- a/tools/consumedir-cleaner/consumedir-cleaner.sh +++ /dev/null @@ -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 diff --git a/tools/consumedir/consumedir.sh b/tools/consumedir/consumedir.sh deleted file mode 100755 index 3ee9f916..00000000 --- a/tools/consumedir/consumedir.sh +++ /dev/null @@ -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 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 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/. (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 A shell glob pattern that is used to skip files that match (value: $exclude)." - echo " --include 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 diff --git a/tools/convert-pdf/convert-all-pdfs.sh b/tools/convert-pdf/convert-all-pdfs.sh deleted file mode 100755 index a3a8c6d4..00000000 --- a/tools/convert-pdf/convert-all-pdfs.sh +++ /dev/null @@ -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 diff --git a/tools/download-files/download-files.sh b/tools/download-files/download-files.sh deleted file mode 100755 index 10f25ef1..00000000 --- a/tools/download-files/download-files.sh +++ /dev/null @@ -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 -# -# 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" diff --git a/tools/ds.sh b/tools/ds.sh deleted file mode 100755 index 61a8ae8e..00000000 --- a/tools/ds.sh +++ /dev/null @@ -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/ -# 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 diff --git a/tools/export-files/export-files.sh b/tools/export-files/export-files.sh deleted file mode 100755 index 6ab36ebd..00000000 --- a/tools/export-files/export-files.sh +++ /dev/null @@ -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 -# -# 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 diff --git a/tools/preview/regenerate-previews.sh b/tools/preview/regenerate-previews.sh deleted file mode 100755 index fdba0c67..00000000 --- a/tools/preview/regenerate-previews.sh +++ /dev/null @@ -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" diff --git a/tools/reset-password/reset-password.sh b/tools/reset-password/reset-password.sh deleted file mode 100755 index 01d377be..00000000 --- a/tools/reset-password/reset-password.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env bash -# -# A script to reset a password. -# -# Usage: -# ./reset-password.sh -# -# 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 diff --git a/website/site/config.toml b/website/site/config.toml index dbb29d5b..6c4b2770 100644 --- a/website/site/config.toml +++ b/website/site/config.toml @@ -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 diff --git a/website/site/content/docs/faq/_index.md b/website/site/content/docs/faq/_index.md index 84ee520f..74a74915 100644 --- a/website/site/content/docs/faq/_index.md +++ b/website/site/content/docs/faq/_index.md @@ -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 diff --git a/website/site/content/docs/install/quickstart.md b/website/site/content/docs/install/quickstart.md index e5b8e9d6..faf7eda5 100644 --- a/website/site/content/docs/install/quickstart.md +++ b/website/site/content/docs/install/quickstart.md @@ -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 diff --git a/website/site/content/docs/tools/cli.md b/website/site/content/docs/tools/cli.md index 8352d45f..7d8f6a5e 100644 --- a/website/site/content/docs/tools/cli.md +++ b/website/site/content/docs/tools/cli.md @@ -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 diff --git a/website/site/content/docs/tools/consumedir-cleaner.md b/website/site/content/docs/tools/consumedir-cleaner.md deleted file mode 100644 index 2c2e3f26..00000000 --- a/website/site/content/docs/tools/consumedir-cleaner.md +++ /dev/null @@ -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. - - - -## 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 diff --git a/website/site/content/docs/tools/consumedir.md b/website/site/content/docs/tools/consumedir.md deleted file mode 100644 index db82833d..00000000 --- a/website/site/content/docs/tools/consumedir.md +++ /dev/null @@ -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 `:`, 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/` and place all -files in there that you want to import. Once dropped in this folder -the `consumedir` container will push it to docspell. diff --git a/website/site/content/docs/tools/convert-all-pdf.md b/website/site/content/docs/tools/convert-all-pdf.md deleted file mode 100644 index 9679b48f..00000000 --- a/website/site/content/docs/tools/convert-all-pdf.md +++ /dev/null @@ -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. diff --git a/website/site/content/docs/tools/ds.md b/website/site/content/docs/tools/ds.md deleted file mode 100644 index 4aa901cb..00000000 --- a/website/site/content/docs/tools/ds.md +++ /dev/null @@ -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 -``` diff --git a/website/site/content/docs/tools/export-files.md b/website/site/content/docs/tools/export-files.md deleted file mode 100644 index bc9c0286..00000000 --- a/website/site/content/docs/tools/export-files.md +++ /dev/null @@ -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 -``` - -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" - } - ] -} -``` diff --git a/website/site/content/docs/tools/regenerate-previews.md b/website/site/content/docs/tools/regenerate-previews.md deleted file mode 100644 index a0fb2e65..00000000 --- a/website/site/content/docs/tools/regenerate-previews.md +++ /dev/null @@ -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. diff --git a/website/site/content/docs/tools/reset-password.md b/website/site/content/docs/tools/reset-password.md deleted file mode 100644 index 84aa20ec..00000000 --- a/website/site/content/docs/tools/reset-password.md +++ /dev/null @@ -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 -``` - -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" -} -```