From 5bedcfe03f4aab98fb4c11b3769dadb8b360e7c1 Mon Sep 17 00:00:00 2001 From: sbs20 Date: Tue, 31 Oct 2023 16:00:32 +0000 Subject: [PATCH] Documentation overhaul General re-organisation and content update for v3. Also added github pages support. --- .gitignore | 6 + Dockerfile | 2 +- Gemfile | 7 + README.md | 73 +++- _config.yml | 37 ++ app-ui/src/components/About.vue | 7 +- app-ui/src/locales/en-US.json | 2 +- app-ui/src/locales/en.json | 2 +- assets/css/style.scss | 29 ++ docs/01-install.md | 47 +++ docs/02-docker.md | 435 +++++++++++++++++++++ docs/03-sane.md | 149 +++++++ docs/04-troubleshooting.md | 139 +++++++ docs/10-configuration.md | 138 +++++++ docs/11-integration.md | 33 ++ docs/{config.md => 12-recipes.md} | 232 +++++------ docs/{proxy.md => 13-proxy.md} | 0 docs/{development.md => 90-development.md} | 84 ++-- docs/91-localisation.md | 36 ++ docs/92-references.md | 46 +++ docs/93-qnap.md | 73 ++++ docs/docker.md | 345 ---------------- docs/install.md | 104 ----- docs/integration.md | 104 ----- docs/sane.md | 195 --------- docs/troubleshooting.md | 59 --- makedeb.sh | 6 +- 27 files changed, 1363 insertions(+), 1027 deletions(-) create mode 100644 Gemfile create mode 100644 _config.yml create mode 100644 assets/css/style.scss create mode 100644 docs/01-install.md create mode 100644 docs/02-docker.md create mode 100644 docs/03-sane.md create mode 100644 docs/04-troubleshooting.md create mode 100644 docs/10-configuration.md create mode 100644 docs/11-integration.md rename docs/{config.md => 12-recipes.md} (66%) rename docs/{proxy.md => 13-proxy.md} (100%) rename docs/{development.md => 90-development.md} (62%) create mode 100644 docs/91-localisation.md create mode 100644 docs/92-references.md create mode 100644 docs/93-qnap.md delete mode 100644 docs/docker.md delete mode 100644 docs/install.md delete mode 100644 docs/integration.md delete mode 100644 docs/sane.md delete mode 100644 docs/troubleshooting.md diff --git a/.gitignore b/.gitignore index ea3f1129..d918b119 100644 --- a/.gitignore +++ b/.gitignore @@ -63,3 +63,9 @@ app-server/data/preview/*.tif **/config.local.js var debian + +# Jekyll +.bundle +_site +.jekyll-metadata +Gemfile.lock diff --git a/Dockerfile b/Dockerfile index 7d69ef75..fc5e660b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -106,7 +106,7 @@ ARG UNAME=scanservjs # config files need write access). RUN groupadd -g $GID -o $UNAME \ && useradd -o -u $UID -g $GID -m -s /bin/bash $UNAME \ - && chown -R $UID:$GID /entrypoint.sh /srv/scanservjs /etc/sane.d/net.conf /etc/sane.d/airscan.conf + && chown -R $UID:$GID /entrypoint.sh /usr/share/scanservjs /etc/sane.d/net.conf /etc/sane.d/airscan.conf USER $UNAME # default build diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000..8a5789c7 --- /dev/null +++ b/Gemfile @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } + +gem "github-pages", group: :jekyll_plugins diff --git a/README.md b/README.md index 2dbda2b3..bec718fe 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,10 @@ Copyright 2016-2023 [Sam Strachan](https://github.com/sbs20) +## Current version + +* [v2.27.0](/sbs20/scanservjs/tree/v2.27.0) + ## What people are saying > I've decided to switch to using only this, I find using this in a browser is @@ -54,7 +58,7 @@ complicated installation. * Light and dark mode * Responsive design * Docker images for `amd64`, `arm64` and `armv7` -* Swagger API documentation +* OpenAPI documentation It supports any [SANE compatible devices](http://www.sane-project.org/sane-supported-devices.html). @@ -63,18 +67,50 @@ It supports any * SANE Scanner * Linux host (or VM with necessary pass-through e.g. USB) -* Software sane-utils, ImageMagick, Tesseract (optional) and nodejs +* Software sane-utils, ImageMagick, Tesseract and nodejs + +## Install + +* Debian: + ```sh + curl -s https://raw.githubusercontent.com/sbs20/scanservjs/master/bootstrap.sh | sudo bash -s -- -v latest + ``` +* Arch: + ```sh + yay -S scanservjs + ``` +* Docker: + ```sh + docker run -d \ + -p 8080:8080 \ + -v /var/run/dbus:/var/run/dbus \ + --restart unless-stopped \ + --name scanservjs-container \ + --privileged sbs20/scanservjs:latest + ``` ## Documentation -* [Manual installation](docs/install.md) -* [Docker installation](docs/docker.md) -* [Scanner and SANE setup](docs/sane.md) -* [Proxy setup](docs/proxy.md) -* [Troubleshooting](docs/troubleshooting.md) -* [Development notes](docs/development.md) -* [Configuration and device override](docs/config.md) -* [Integration](docs/integration.md) +### Installation and setup + +* [Standard install](docs/01-install.md) +* [Docker install](docs/02-docker.md) +* [SANE setup](docs/03-sane.md) +* [Troubleshooting](docs/04-troubleshooting.md) + +### Configuration + +* [Configuration](docs/10-configuration.md) +* [Integration](docs/11-integration.md) +* [Recipes](docs/12-recipes.md) +* [Using a proxy](docs/13-proxy.md) + +### Developing + +* [Development](docs/90-development.md) +* [Localisation](docs/91-localisation.md) +* [References](docs/92-references.md) +* [QNAP](docs/93-qnap.md) ## Running scanservjs @@ -91,9 +127,9 @@ Once the scanner is detected then you have a number of pages. This page gives access to the controls for your scanner. The app will generally find the settings available automatically, although some scanners mis-report their abilities. (If this is the case, then you can override what's detected, -see [Configuration and device override](docs/config.md) for more). If geometry -is available (selecting scan size and position) then you will have cropping -available to you. +see [Configuration and device override](docs/10-configuration.md) for more). If +geometry is available (selecting scan size and position) then you will have +cropping available to you. There is also the ability to perform batch scanning. If you have a document feeder, then just use the `Auto` option. If not then use `Manual` and the app @@ -118,6 +154,9 @@ Furthermore, users in real life will want to store their scans with their own names, directory structures and cloud services or NAS devices. The permutations and possibilities are endless and are beyond the scope of the app. +scanservjs can integrate files either through pipeline automation or file +actions. See [integration documentation](docs/11-integration.md) for more. + ### Settings The settings page allows you to change the appearance and locale / language. @@ -126,12 +165,12 @@ The settings page allows you to change the appearance and locale / language. Copyright information and system info. -## Swagger API documentation +## OpenAPI documentation -There is built in swagger documentation with an API explorer. Access it direct +There is built in OpenAPI documentation with an API explorer. Access it direct using `/api-docs` or navigate from the `About` page. -![swagger](https://github.com/sbs20/scanservjs/raw/master/docs/swagger.png) +![OpenAPI](https://github.com/sbs20/scanservjs/raw/master/docs/swagger.png) ## Why? @@ -152,4 +191,4 @@ and it's been a labour of love ever since. ## More about SANE - * http://www.sane-project.org/ + * diff --git a/_config.yml b/_config.yml new file mode 100644 index 00000000..5a03b5c8 --- /dev/null +++ b/_config.yml @@ -0,0 +1,37 @@ +domain: sbs20.github.io +url: https://sbs20.github.io +baseurl: /scanservjs +theme: jekyll-theme-minimal +exclude: [debian, var, node_modules, dist] +header_pages: + - about.md + - portfolio.md + +description: |- + SANE scanner nodejs web ui + +

Getting started

+ +

Installation and setup

+
    +
  1. Standard install
  2. +
  3. Docker install
  4. +
  5. SANE setup install
  6. +
  7. Troubleshooting
  8. +
+ +

Configuration

+
    +
  1. Configuration
  2. +
  3. Integration
  4. +
  5. Recipes
  6. +
  7. Using a proxy
  8. +
+ +

Developing

+
    +
  1. Development
  2. +
  3. Localisation
  4. +
  5. References
  6. +
  7. QNAP
  8. +
diff --git a/app-ui/src/components/About.vue b/app-ui/src/components/About.vue index af1d32c2..ed5acb59 100644 --- a/app-ui/src/components/About.vue +++ b/app-ui/src/components/About.vue @@ -6,6 +6,11 @@
{{ $t('about.main') }}
+ + +
{{ $t('about.issue') }} https://github.com/sbs20/scanservjs @@ -13,7 +18,7 @@
{{ $t('about.api') }} - Swagger /api-docs + /api-docs
{{ $t('about.system-info') }} diff --git a/app-ui/src/locales/en-US.json b/app-ui/src/locales/en-US.json index e2f88cd5..9b8c9096 100644 --- a/app-ui/src/locales/en-US.json +++ b/app-ui/src/locales/en-US.json @@ -14,7 +14,7 @@ "about": { "main": "scanservjs is a simple web-based UI for your scanner. It allows you to share one or more scanners (using SANE) on a network without the need for drivers or complicated installation. It can save to TIF, JPG, PNG, PDF and TXT (with Tesseract OCR) with varying compression settings, all of which can be configured. It supports multipage scanning and all SANE compatible devices.", "issue": "File an issue or view the source code:", - "api": "Swagger API documentation:", + "api": "OpenAPI documentation:", "system-info": "System information" }, diff --git a/app-ui/src/locales/en.json b/app-ui/src/locales/en.json index a7a7ad39..d5d3f462 100644 --- a/app-ui/src/locales/en.json +++ b/app-ui/src/locales/en.json @@ -15,7 +15,7 @@ "about": { "main": "scanservjs is a simple web-based UI for your scanner. It allows you to share one or more scanners (using SANE) on a network without the need for drivers or complicated installation. It can save to TIF, JPG, PNG, PDF and TXT (with Tesseract OCR) with varying compression settings, all of which can be configured. It supports multipage scanning and all SANE compatible devices.", "issue": "File an issue or view the source code:", - "api": "Swagger API documentation:", + "api": "OpenAPI documentation:", "system-info": "System information" }, diff --git a/assets/css/style.scss b/assets/css/style.scss new file mode 100644 index 00000000..fbe243a8 --- /dev/null +++ b/assets/css/style.scss @@ -0,0 +1,29 @@ +--- +--- + +@import "{{ site.theme }}"; + +a:hover, a:focus { + font-weight: unset; + background-color: lightblue; +} + +@media print, screen and (max-width: 960px) { + header { + padding-right: unset; + } +} + +@media print, screen and (min-width: 960px) { + .wrapper { + max-width: 1152px; + min-width: 860px; + width: unset; + } + + section { + float: none; + margin-left: 350px; + width: unset; + } +} diff --git a/docs/01-install.md b/docs/01-install.md new file mode 100644 index 00000000..ca05761d --- /dev/null +++ b/docs/01-install.md @@ -0,0 +1,47 @@ +# Installation + +## Debian + +If you're using a debian based distro then you can just use the installer +script: + +```sh +curl -s https://raw.githubusercontent.com/sbs20/scanservjs/master/bootstrap.sh | sudo bash -s -- -v latest +``` + +If you wish to install a specific version then you can do so: + +```sh +curl -s https://raw.githubusercontent.com/sbs20/scanservjs/master/bootstrap.sh | sudo bash -s -- -v v2.23.0 +``` + +Since v3.0.0, the oneline install is actually just downloading a `.deb` file. +You can do this yourself from the releases and then install it: +`sudo apt-get install ./scanservjs_3.0.0-1.deb` + +## Arch + +If you're using Arch, then [@dadosch](https://github.com/dadosch) created a +PKGBUILD script in [Arch's AUR](https://aur.archlinux.org/packages/scanservjs) +which allows Arch-distro-based users to quickly install and update scanservjs +with any AUR helper, for example: `yay -S scanservjs`. See +[package](https://aur.archlinux.org/packages/scanservjs/) for more. + +## Other distros + +If you're using another distro, then for the time being you either need to +manually run the steps in the install script or use docker. + +## Uninstall + +```sh +# Debian +sudo apt-get remove scanservjs + +# Arch +sudo yay -R scanservjs +``` + +Note: For Debian pre `v3.0.0` use the old installer with the `-u` switch: +`sudo /var/www/scanservjs/installer.sh -u`. If you have installed `v3.0.0` then +this *may* interfere. diff --git a/docs/02-docker.md b/docs/02-docker.md new file mode 100644 index 00000000..df90c368 --- /dev/null +++ b/docs/02-docker.md @@ -0,0 +1,435 @@ +# Running scanservjs under docker + +If you're already running Debian, Ubuntu or similar, and haven't used docker +before then it's probably easier just to install directly. For what it's worth, +that is my preferred installation method since Docker takes an already +complicated situation and makes it more complicated. + +## Getting started + +Get the image and run: + +```sh +# Get the latest +docker pull sbs20/scanservjs:latest + +# Remove old container +docker rm --force scanservjs-container 2> /dev/null + +# Run +docker run \ + --detach \ + --publish 8080:8080 \ + --volume /var/run/dbus:/var/run/dbus \ + --restart unless-stopped \ + --name scanservjs-container \ + --privileged \ + sbs20/scanservjs:latest +``` + +## General notes + +⚠ By default, configuration and scanned images are stored within the container +and will be lost if you recreate it. If you want to map your scanned images then +see mapping section below + +✅ The docker image supports arm as well as amd64. + +## Accessing hardware + +Docker is great for certain tasks. But it's less ideal for situations where the +container needs to access the host hardware. The simple solution is to run with +`--privileged` but that gives your container full root access to the host which +means you're putting a lot of trust in the container. It's better not to do +this, but it can be painful to avoid. The best solution in my view is to use [SANE over Network](./03-sane.md#defining-network-scanners). + +## Host attached scanner + +If your scanner is connected by USB to the host, and there are standard SANE +drivers, then you can map the device. The best way to do this is to map the +actual USB ports. + +In case you scanner is always plugged to your device: + +* Run `sudo sane-find-scanner -q` and you will get a result like + ``` + found USB scanner (vendor=0x04a9 [Canon], product=0x220d [CanoScan], chip=LM9832/3) at libusb:001:003 + ``` +* Or run `lsusb` which gives you + ``` + Bus 001 Device 003: ID 04a9:220d Canon, Inc. CanoScan N670U/N676U/LiDE 20 + ``` +* Both translate to `/dev/bus/usb/001/003`. +* The docker argument would be + `--device=/dev/bus/usb/001/003:/dev/bus/usb/001/003` +* You may also need to adjust permissions on the USB port of the host e.g. + ```sh + chmod a+rw dev/bus/usb/001/003 + ``` + See + [this](https://github.com/sbs20/scanservjs/issues/221#issuecomment-828757430) + helpful answer for more. + +## Ephemeral bus address + +In case your scanner is not always plugged in, the device path will change +every so often, and the previous solution will stop working. Also, some devices +will go to sleep after long idle times, effectively getting "unplugged" and +"plugged again" over and over. + +This can also happen if your container is running inside a VM resultig in an +[unstable device id](https://github.com/sbs20/scanservjs/issues/66). + +In this case, you may use `udev` so that it starts or re-configures your +container whenever your scanner is hot-plugged. This is suggested in +[the official Docker documentation](https://docs.docker.com/engine/reference/commandline/run/#device-cgroup-rule): + +* Run `lsusb` to retrieve your device "vendor ID" and "product ID". Reusing the + example above: + ``` + Bus 001 Device 003: ID 04a9:220d Canon, Inc. CanoScan N670U/N676U/LiDE 20 + ``` + means that the vendor ID is `04a9`, and the product ID is `220d`. +* Add a udev rule + + `/etc/udev/rules.d/50-add-scanner.rules` + ```ini + ACTION=="add", ATTR{idVendor}=="04a9", ATTR{idProduct}=="1774", RUN+="/etc/scan/bind-scanner-to-container.sh $name $major $minor $attr{idVendor} $attr{idProduct}" + ``` +* Make `udev` aware of this change: + ```sh + sudo udevadm control --reload-rules + ``` +* This will run the following script every time the scanner is plugged. + + `/etc/scan/bind-scanner-to-container.sh` + ```sh + #!/bin/bash + # This script must be executable by root + DEVICE_PATH="$1" + MAJOR_NUMBER="$2" + MINOR_NUMBER="$3" + + # USB identifiers of the device + VENDOR_ID="$4" + PRODUCT_ID="$5" + + CONTAINER_NAME="scan" + IMAGE_NAME="sbs20/scanservjs:release-v2.25.0" + + logger "Scanner ($VENDOR_ID:$PRODUCT_ID) is available at $DEVICE_PATH. Let's make it available to the scan server container" + + # Is the container running already? + container_id=$(docker ps -q -f name=$CONTAINER_NAME) + if [ -z "$container_id" ]; then + # Container was not running. We should start it, with the right device ID + device_nb=$(lsusb | grep "$VENDOR_ID:$PRODUCT_ID" | grep -o -E "Device [0-9]+" | grep -o -E "[0-9]+") + + if [ -z "$device_nb" ]; then + logger "Unable to find where this device is connected. Ignoring." + exit 1 + fi + + # Waiting for Docker to be available (if the scanner is plugged when the host boots, udev will trigger this script before Docker is even started) + attempts=0 + while true ; do + if [ "$(systemctl is-active docker)" == "active" ]; then + break + fi + sleep 10 + attempts=$(( attempts + 1 )) + if [ "$attempts" -gt 10 ]; then + logger "Docker is not running. Will not start scan server." + exit 1 + fi + done + + logger "Starting the scan server from $IMAGE_NAME, with device $device_nb ($VENDOR_ID:$PRODUCT_ID, major number is $MAJOR_NUMBER)..." + # --device adds the existing device to the container. + # --device-cgroup-rule makes it possible to add future hot-plugged devices + # see https://docs.docker.com/engine/reference/commandline/run/#device-cgroup-rule + docker run --detach \ + --rm \ + --publish 8080:8080 \ + --volume /var/run/dbus:/var/run/dbus \ + --volume /path/to/the/optional/scan/folder:/app/data/output \ + --volume /path/to/the/optional/custom/config:/app/config \ + --name "$CONTAINER_NAME" \ + --device=/dev/bus/usb/001/"$device_nb":/dev/bus/usb/001/"$device_nb" \ + --device-cgroup-rule="c $MAJOR_NUMBER:* rmw" \ + "$IMAGE_NAME" 2>&1 | logger + else + # Container is running. We just have to add the device there + logger "Adding the new scanner to the scan server container..." + docker exec "$CONTAINER_NAME" mknod "/dev/$DEVICE_PATH" c "$MAJOR_NUMBER" "$MINOR_NUMBER" 2>&1 | logger + fi + ``` +* If you prefer, you may tweak both files above e.g. to stop the container + when the scanner is disconnected, and re-start the container when the + device is re-connected. + +## Driverless over the network + +If your scanner is driverless over the network, then +[sane-airscan](https://github.com/alexpevzner/sane-airscan) should be able to +figure it out - but it uses Avahi / Zeroconf / Bonjour to discover devices on +the local network. You will want to share dbus to make it work +(`--volume /var/run/dbus:/var/run/dbus`). + +Note that driverless-mode scanning (using airscan over IPP-USB) often results in +problems. If anyone has ideas why (perhaps something additional needs sharing +from host to guest) then suggestions are welcome. +[The documentation](https://wiki.debian.org/SaneOverNetwork#Sharing_on_Debian_11_.28bullseye.29_and_later) +may be useful. + +## Proprietary drivers + +If you need **proprietary drivers** for your scanner then the best solution is +either to install the drivers on the host and share it over the network or to +create your own docker image based on the scanservjs one and add it in that way. + +Here is an example on how one particular Brother scanner model and its driver +can be installed in the Dockerfile. The driver (`brscan4-0.4.10-1.amd64.deb`) +needs to be placed next to the Dockerfile, then: + +```dockerfile +COPY brscan4-0.4.10-1.amd64.deb "$APP_DIR/brscan4-0.4.10-1.amd64.deb" +RUN apt install -yq "$APP_DIR/brscan4-0.4.10-1.amd64.deb" \ + && brsaneconfig4 -a name=ADS-2600W model=ADS-2600W nodename=10.0.100.30 +``` + +Note: The addition of more backends to the docker container is not planned +since it would mostly add cruft for most users who don't need it. + +## Using SANE over Network + +The best overall implementation with Docker if you can manage it is to +[share the host scanner over the network](./03-sane.md#configuring-the-server) +on the host (where the scanner is connected) and then set the `SANED_NET_HOSTS` +[environment variable](#environment-variables) on the docker container. + +[This](https://github.com/sbs20/scanservjs/issues/129#issuecomment-800226184) +user uses docker compose instead. See examples below. + +## Mapping volumes + +To access data from outside the docker container, there are two volumes you may +wish to map: + +* The scanned images: use + `--volume /local/path/scans:/usr/share/scanservjs/output` +* Configuration overrides: use `--volume /local/path/cfg:/etc/scanservjs` + +## User and group mapping + +When mapping volumes, special attention must be paid to users and file systems +permissions. + +The docker container runs as root by default. Changing the user's UID (e.g. by +using `--user 1000` for `docker run`) to access scans/configuration from outside +docker **is not advised since it will cause scans to fail.**. If running as a +different user is important to you then see the `scanservjs-user2001` target in +[../Dockerfile](../Dockerfile). + +Your alternatives are: + +1. changing the group of the container to a known group on the host e.g. + `--user 0:1000`. This will keep the user correct (`0`) but change the group + (`1000`). +2. building a docker image with a custom UID/GID pairing: clone this repository + and run + ```sh + docker build --build-arg UID=1234 --build-arg GID=5678 --tag scanservjs_custom . + ``` + (with UID and GID adjusted to your liking), then run the custom image (e.g. + `docker run scanservjs_custom`). +3. as a last resort, changing the host volume permissions e.g. + `chmod 777 local-volume` + +## Configuring SANE + +Sometimes you will need to configure SANE within the container. The best way to +achieve this is just volume mapping. + +Example to [configure `airscan.conf`](https://github.com/sbs20/scanservjs/issues/628): + +```sh +# Create your airscan config +cat > ./airscan.host.conf << EOF +[devices] +HP = http://192.168.1.150/eSCL, eSCL + +[options] +discovery = disable +EOF + +# Now map it +docker run -d \ + --publish 8080:8080 \ + --volume ./airscan.host.conf:/etc/sane.d/airscan.conf \ + --restart unless-stopped \ + --name scanservjs-container \ + --privileged sbs20/scanservjs:latest +``` + +Example to [configure `pixma.conf`](https://github.com/sbs20/scanservjs/issues/594): + +```sh +# Create your pixma config +cat > ./pixma.host.conf << EOF +bjnp://192.168.1.5 +EOF + +# Now map it +docker run -d \ + --publish 8080:8080 \ + --volume ./pixma.host.conf:/etc/sane.d/pixma.conf \ + --restart unless-stopped \ + --name scanservjs-container \ + --privileged sbs20/scanservjs:latest +``` + +## Environment variables + +There are some shortcuts available to volume mapping above by using environment variables: + +* `SANED_NET_HOSTS`: If you want to use a + [SaneOverNetwork](https://wiki.debian.org/SaneOverNetwork#Server_Configuration) + scanner then to perform the equivalent of adding hosts to + `/etc/sane.d/net.conf` specify a list of ip addresses separated by semicolons + in the `SANED_NET_HOSTS` environment variable. +* `AIRSCAN_DEVICES`: If you want to specifically add `sane-airscan` devices to + your `/etc/sane.d/airscan.conf` then use the `AIRSCAN_DEVICES` environment + variable (semicolon delimited). +* `PIXMA_HOSTS`: If you want to use a PIXMA scanner which uses the bjnp protocol + then to perform the equivalent of adding hosts to `/etc/sane.d/pixma.conf` + specify a list of ip addresses separated by semicolons in the `PIXMA_HOSTS` + environment variable. +* `DELIMITER`: if you need to include semi-colons (`;`) in your environment + variables, this allows you to choose an alternative delimiter. +* `DEVICES`: Force add devices use `DEVICES` (semicolon delimited) +* `SCANIMAGE_LIST_IGNORE`: To force ignore `scanimage -L` + +## Examples + +### Connect to the scanner over the network (recommended) +```sh +docker run --detach --publish 8080:8080 \ + --env SANED_NET_HOSTS="10.0.100.30" \ + --name scanservjs-container sbs20/scanservjs:latest +``` + +### Mapped USB device with mapped volumes + +```sh +docker run --detach --publish 8080:8080 \ + --volume $HOME/scan-data:/usr/share/scanservjs/output \ + --volume $HOME/scan-cfg:/etc/scanservjs \ + --device /dev/bus/usb/001/003:/dev/bus/usb/001/003 \ + --name scanservjs-container sbs20/scanservjs:latest +``` + +### Use airscan and a locally detected scanner + +This should support most use cases + +```sh +docker run --detach --publish 8080:8080 \ + --volume /var/run/dbus:/var/run/dbus \ + --name scanservjs-container sbs20/scanservjs:latest +``` + +### A bit of everything + +Add two net hosts to sane, use airscan to connect to two remote scanners, add +two pixma scanners using the bjnp protocol, don't use `scanimage -L`, force a +list of devices, override the OCR language and run in privileged mode + +```sh +docker run --detach --publish 8080:8080 \ + --env SANED_NET_HOSTS="10.0.100.30;10.0.100.31" \ + --env AIRSCAN_DEVICES='"Canon MFD" = "http://192.168.0.10/eSCL";"EPSON MFD" = "http://192.168.0.11/eSCL"' \ + --env PIXMA_HOSTS="10.0.100.32;10.0.100.33" \ + --env SCANIMAGE_LIST_IGNORE=true \ + --env DEVICES="net:10.0.100.30:plustek:libusb:001:003;net:10.0.100.31:plustek:libusb:001:003;airscan:e0:Canon TR8500 series;airscan:e1:EPSON Cool Series" \ + --env OCR_LANG="fra" \ + --volume /var/run/dbus:/var/run/dbus \ + --name scanservjs-container --privileged sbs20/scanservjs:latest +``` + +### Hosting it on a Synology NAS using Docker + +It can be convenient to host scanservjs on the same machine where you store your +scans — your NAS. Here's a possible approach for network scanning with a +Synology NAS: + +1. Install the + [Synology Docker package](https://www.synology.com/en-us/dsm/packages/Docker). +2. In DSM, create a service user "scanservjs" which will run the Docker + container. Make sure to give it write permission to the preferred target + location for scans. We'll use `/volume1/scans`. +3. SSH with an admin account onto the NAS and use `id` to determine the UID and + GID of the service user just created: + ```sh + admin@synology:~$ id scanservjs + uid=1034(scanservjs) gid=100(users) groups=100(users),65538(scanusers) + ``` + Keep the session open, we'll need it again in a moment. +4. On your workstation, download and extract + [the latest scanservjs release](https://github.com/sbs20/scanservjs/releases/latest). +5. In the repository root, create a text file named `docker-compose.yml` with + the following content: + ```yaml + version: "3" + services: + scanservjs: + build: + context: . + args: + # ----- enter UID and GID here ----- + UID: 1034 + GID: 100 + target: scanservjs-user2001 + container_name: scanservjs + environment: + # ----- specify network scanners here; see above for more possibilities ----- + - SANED_NET_HOSTS="10.0.100.30" + volumes: + # ---- enter your target location for scans before the ':' character ----- + - /volume1/scans:/app/data/output + - ./config:/app/config + ports: + - 8080:8080 + restart: unless-stopped + ``` +6. Copy the entire repository including `docker-compose.yml` onto your NAS (via + smb, sftp, ...). +7. In your SSH session from earlier, `cd` to the repository location and run + ```sh + sudo docker-compose up -d + ``` +8. After a medium-sized cup of tea, scanservjs should be available at + `http://:8080` +9. Bonus: Create a reverse proxy rule in the + [Application Portal](https://www.synology.com/en-global/knowledgebase/DSM/help/DSM/AdminCenter/application_appportalias) + so that scanservjs can be reached via `http://scan.synology.lan` (or + similar). Scanning can be slow, so set the proxy timeouts to 300 seconds or + more [to prevent timeout issues](troubleshooting.md). + +## Staging builds + +These may be less stable, but also have upcoming features. + +If you want to install the latest staging branch (this may contain newer code) + +```sh +docker pull sbs20/scanservjs:staging +docker rm --force scanservjs-container 2> /dev/null +docker run \ + --detach \ + --publish 8080:8080 \ + --volume /var/run/dbus:/var/run/dbus \ + --restart unless-stopped \ + --name scanservjs-container \ + --privileged sbs20/scanservjs:staging +``` diff --git a/docs/03-sane.md b/docs/03-sane.md new file mode 100644 index 00000000..7aac6201 --- /dev/null +++ b/docs/03-sane.md @@ -0,0 +1,149 @@ +# SANE, imagemagick, scanners + +## Gettings started + +There is a reasonable chance that your scanner will just work out of the box. +But it's far from certain. Given you're reading this and, at the very least, +thinking about installing something in Linux, there's a good chance you're half +expecting this. + +Old scanners tend to have built in support within SANE. Modern scanners tend to +be driverless and will have built in support with airscan. But there are +in-between scanners, often from HP or Brother, that require proprietary drivers. + +Sometimes the easiest way to find out what you need is just install everything +and then troubleshoot. + +## Defining network scanners + +If you have a non-network, wired device (e.g. USB) then, if you have the luxury, +SANE over network is a great option. It enables devices (e.g. my old Canon +CanoScan LiDE 20) to be available to SANE anywhere on the network. This is +especially useful to me since I host the scanner in one place and can install +`scanservjs` in many places across many operating systems and dockers for +testing. + +It effectively allows you to proxy a SANE backend (on the server). This is an +advantage because it means you only have to enable hardware access, with `udev` +and permissions in one place - it is especially helpful with docker, which is +not really meant to have host access. + +[SANE over Network](https://wiki.debian.org/SaneOverNetwork) + +``` +[Scanner] + <-(USB)-> + [Server Host] + <-(saned.socket)-> + [Client Host] + <-(SANE)-> + [scanimage] + <-(CLI)-> + [scanservjs] +``` + +### Configuring the server + +All you need is to define the subnets to which you want to grant access in +`saned.conf`. Of course "All" is doing a lot of work here and assumes you know +about subnets and CIDR notation. + +`/etc/sane.d/saned.conf` +```ini +# This is equivalent to 192.168.0.0/255.255.255.0 +192.168.0.0/24 + +# Equivalent to 10.0.0.0/255.255.0.0 +10.0.0.0/16 + +# Equivalent to 172.0.0.0/255.240.0.0 - useful for docker +172.16.0.0/12 +``` + +Make your changes to the file and restart the saned socket. + +```sh +## Local network +echo "192.168.0.0/24" >> /etc/sane.d/saned.conf +## Default docker network +echo "172.17.0.0/16" >> /etc/sane.d/saned.conf + +sudo systemctl enable saned.socket +sudo systemctl start saned.socket +``` + +### Configuring the client + +Add the server host (let's assume it is `192.168.0.10`) to the client. + +```sh +echo "192.168.0.10" >> /etc/sane.d/net.conf +``` + +Now if you run `scanimage -L` on the client you should see the scanner on the +host (prefixed with `net:192.168.0.10:`): + +``` +device `net:192.168.0.10:plustek:libusb:001:003' is a Canon CanoScan N670U/N676U/LiDE20 flatbed scanner +``` + +For more information on configuring the server and client see +[SANE over Network](https://wiki.debian.org/SaneOverNetwork#Server_Configuration). + +It's worth noting that network scanners do not always show up on the client. In +such cases you will need to get the device name from the host, prefix it with +`net:$ip:` and use the `-d` switch. From the scanimage manpage: + +From [`man scanimage`](https://linux.die.net/man/1/scanimage): + +> The -L or --list-devices option requests a (partial) list of devices that are +> available. The list is not complete since some devices may be available, but +> are not listed in any of the configuration files (which are typically stored +> in directory /etc/sane.d). This is particularly the case when accessing +> scanners through the network. If a device is not listed in a configuration +> file, the only way to access it is by its full device name. You may need to +> consult your system administrator to find out the names of such devices. + +## Scanners requiring additional drivers + +Some scanner models require additional drivers to function with SANE. For +example, Brother offers proprietary drivers for their USB and network scanners. +Please follow the manufacturer's instructions for setting up such scanners. +Once a scanner is listed in `scanimage -L`, it should be ready to use with +scanservjs. + +## SANE Airscan + +[sane-airscan](https://github.com/alexpevzner/sane-airscan) is useful for +supporting newer eSCL and WSD devices. Once installed you should just find that +it works with a simple `scanimage -L`. You can also specify a specific name for +the device in `/etc/sane.d/airscan.conf` + +```console +[devices] +"My scanner" = "http://10.0.111.4/eSCL" +``` + +Your URI will be different. You shouldn't need to do that though. + +Airscan relies on bonjour to make it work - which means broadcasting to the +local subnet to autodiscover the device. If you are running on a different +subnet, the autodiscovery won't work. You have two options: + +1. Use avahi reflector which "just" reflects broadcasts across the subnets + ```sh + apt-get install avahi-daemon -y sed -i "s/#enable-reflector=no/enable-reflector=yes/g" /etc/avahi/avahi-daemon.conf + systemctl restart avahi-daemon + ``` +2. If have servers galore then you can create a saned host in the same subnet as + the scanner which uses airscan to reach the scanner, but re-shares the + scanner as a SANED network scanner (see above) - we'll call this + `The bridge`. Then have your `scanimage` client reference `The bridge` as a + remote scanner host and add the IP address of `The bridge` to + `/etc/sane.d/net.conf` on the client (which will be the scanservjs server). + You may also need to add the full device name to the devices list in the + config e.g. + ```javascript + //config.devices.push('net:${bridge}:${device}); + config.devices.push('net:10.0.100.171:airscan:e0:Canon TR8500 series-5); + ``` diff --git a/docs/04-troubleshooting.md b/docs/04-troubleshooting.md new file mode 100644 index 00000000..2dba022f --- /dev/null +++ b/docs/04-troubleshooting.md @@ -0,0 +1,139 @@ +# Troubleshooting + +## Overview + +Scanservjs works by wrapping CLI calls to `scanimage` as the user `scanservjs` +which is a member of the `scanner` group. The chances are that if scanservjs +does not work, then neither does `scanimage`. + +`scanimage` can either connect to: + +* some hardware (e.g. USB or SCSI), in which case you may need to look at + permissions and `udev` rules or +* a network device, in which case you need a network route + +## Verify connectivity + +* Try running `scanimage -L`. You should see something like: + ```log + device `airscan:e0:Canon' is a eSCL Canon ip=10.0.109.3 + ``` +* If you don't see anything, then try as root `sudo scanimage -L` (for + diagnostic purposes) - this really should work. If it doesn't, then it's most + likely a SANE / driver related issue. +* If it works with `sudo` but not without, then this is just a permissions + issue. See below. +* If `scanimage` does not work, then try: `sane-find-scanner -q`; you should see + a result like: + ```log + found USB scanner (vendor=0x04a9 [Canon], product=0x220d [CanoScan]) at libusb:003:005 + ``` + + If nothing is working at this point, then this we're into the realms of + checking to see what version of SANE you're running and if your scanner is + supported. + +## Verify connectivity for scanservjs + +Try running as the application user: + +```sh +sudo su --shell /bin/bash - scanservjs --command 'scanimage -L' +``` + +If this does not work, see below. If it does then take it a step further and run +a scan: + +```sh +sudo su --shell /bin/bash - scanservjs --command 'scanimage --format tiff > test.tif' +``` + +which should output a tif file in the scanservjs home directory +(`/usr/share/scanservjs/`). If you can get this to work then scanservjs should +be working fine. + +## Permissions; `scanimage` works with `sudo` but not without + +Most likely you need a udev rule, but also verify group membership. + +* Add a udev rule for the scanner device. Use the vendorId:productId from + `lsusb` and add to `/etc/udev/rules.d/55-libsane.rules` as + + ```ini + ATTRS{idVendor}=="04a9", ATTRS{idProduct}=="220d", MODE="0666", GROUP="scanner", ENV{libsane_matched}="yes" + ``` + + Unplug / replug the scanner (don't skip this!). + +* Verify `scanservjs` is in the `scanner` group: `groups scanservjs`. + +## Getting logs + +Getting logs: use `journalctl`. See the journalctl manpage for details but +`sudo journalctl -e -u scanservjs` should be enough to get you started. + +## scanimage: sane_read: Invalid argument + +This is a problem with SANE rather than scanservjs. It usually signifies a +[problem with the driver](https://askubuntu.com/a/447283). Your best bet is +going back to first principles with SANE itself. Follow the steps +[here](./install.md#troubleshooting) + +## Cropping results in incorrect positioning + +Some scanners mis-report their size - don't know why, but they do. This means +that when the app attempts to crop things the maths is all wrong. The best way +around this is to override the reported scanner dimensions. See +[this recipe](./config.md#override-scanner-dimensions) for more. + +## JSON.parse error + +This happens when the browser received a string from the server which is not a +valid JSON string. Most likely you're running a proxy server (e.g. nginx) which +is timing out prior to the completion of the request. Scanservjs can sometimes +take a little while to fulfil its requests - usually because it's waiting for a +scanner, but sometimes because it's having to do a fair amount of image +processing and you're +[running on a low power CPU (e.g. RPi)](https://github.com/sbs20/scanservjs/issues/224). + +The solution is to increase the proxy timeout. For nginx that might look like: + +```conf +server{ + ... + proxy_read_timeout 300; + proxy_connect_timeout 300; + proxy_send_timeout 300; + ... +} +``` + +## Long scan timeout + +When scanning files with high resolution, e.g. 1200dpi it is very likely for the +request to timeout. This is because node HTTP times out after 2 minutes by +default. The solution is to increase the default timeout. That's possible by +setting `config.timeout = 600000;` (for 10 minutes for example). + +## Docker container loses scanner after device reboot + +As per +[issue #505](https://github.com/sbs20/scanservjs/issues/505#issuecomment-1364533826) +containers can lose their access to a device after a device reboot. + +This is more SANE and containers than this app. The user's solution was to add a +udev rule as below. You will need to substitute your own product and vendor +variables. + +`/etc/udev/rules.d/99-printer.rules` +```ini +SUBSYSTEMS=="usb",KERNELS=="1-1.1",DRIVERS=="usb",ATTRS{idProduct}=="0827", ATTRS{idVendor}=="04b8", ATTRS{serial}=="L53010612130846360",SYMLINK+="%s{manufacturer}_printer",TAG+="systemd",RUN+="/bin/bash -c '/usr/bin/systemctl restart container-scanservjs.service &'" +``` + +## Poor quality (weird or blocky) scans on Raspberry Pi + +USB-only scanners draw a lot of current relative to the Pi's available power. +This can manifest itself in unusual scans - technically valid images but with +odd colours and block transforms. Consider using a powered USB hub (e.g. for a +Canon LIDE 20). If you encounter similar then see +[here](https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=53832). diff --git a/docs/10-configuration.md b/docs/10-configuration.md new file mode 100644 index 00000000..01953ddf --- /dev/null +++ b/docs/10-configuration.md @@ -0,0 +1,138 @@ +# Configuration and integration + +Sometimes scanners don't return quite what you want them to. Likewise, perhaps +scanservjs doesn't provide the defaults you want. And maybe you want to do your +own thing after a scan. Furtunately it's possible to override most things you +might want to. + +There are various hooks where you can customise behaviour. + +TL;DR; copy `/etc/scanservjs/config.default.js` to +`/etc/scanservjs/config.local.js`, override the sections you want and then +restart the app `sudo systemctl restart scanservjs.service` + +If you are using docker, then you will want to map the configuration directory +e.g. `--volume /my/local/path:/etc/scanservjs`. + +## How it works + +scanservjs looks for a file called `/etc/scanservjs/config.local.js` and +attempts to call functions at different stages in the processing. + +Note that the config file only gets read at start-up - so if you make changes, +you will need to restart. + +See [example source](../app-server/config/config.default.js) for more options. + +## `afterConfig(config)` + +Whenever the config is read, the result is passed to this function before being +either used or sent down to the browser. This provides a reference to the config +where you can apply your own changes to global settings which include things +like: + + * Server port + * Log level + * Preview resolution + * Output filename + * Pipelines (output format) + +## `afterDevices(devices)` + +Whenever the devices are read, the result is passed to this function before +being used. You can alter the device definitions which are reported by SANE +which include scanner dimensions and geometry, modes, resolutions and sources. + +## `afterScan(fileInfo)` + +Whenever a scan completes, the resultant file is passed to this function. You +receive a reference to the file which has just been scanned; copy it somewhere, +call a script or write some code. + +## `actions` + +You can define custom actions which can be applied to files either in the UI or +applied at the end of a pipeline (just prior to `afterScan(fileInfo)`). An +action object must have a name and async execute method taking a `FileInfo`: + +```javascript +{ + name: 'Copy file to home directory', + async execute(fileInfo) { + await Process.spawn(`cp '${fileInfo.fullname}' ~/`); + } +} +``` + +## Example file + +```javascript +const Process = require('../server/classes/process'); + +module.exports = { + /** + * @param {Configuration} config + */ + afterConfig(config) { + // Set default preview resolution + config.previewResolution = 150; + + // Add a custom print pipeline + config.pipelines.push({ + extension: 'pdf', + description: 'Print PDF', + commands: [ + 'convert @- -quality 92 tmp-%04d.jpg && ls tmp-*.jpg', + 'convert @- scan-0000.pdf', + 'lp -d MY_PRINTER scan-0000.pdf', + 'ls scan-*.*' + ] + }); + }, + + /** + * @param {ScanDevice[]} devices + */ + afterDevices(devices) { + // Override the defaults for plustek scanners + devices + .filter(d => d.id.includes('plustek')) + .forEach(device => { + device.features['--mode'].default = 'Color'; + device.features['--resolution'].default = 150; + device.features['--resolution'].options = [75, 150, 300, 600]; + device.features['--brightness'].default = 0; + device.features['--contrast'].default = 5; + device.features['-x'].default = 215; + device.features['-y'].default = 297; + device.settings.batchMode.options = ['none']; + }); + } + + /** + * @param {FileInfo} fileInfo + * @returns {Promise.} + */ + async afterScan(fileInfo) { + // Copy the scan to my home directory + return await Process.spawn(`cp '${fileInfo.fullname}' ~/`); + } + + /** + * @type {Action[]} + */ + actions: [ + { + name: 'Echo', + /** + * @param {FileInfo} fileInfo + * @returns {Promise.} + */ + async execute(fileInfo) { + // Output the filepath (relative to the present working direectory) + return await Process.spawn(`echo '${fileInfo.fullname}'`); + } + } + ] +}; +``` diff --git a/docs/11-integration.md b/docs/11-integration.md new file mode 100644 index 00000000..097ce9a7 --- /dev/null +++ b/docs/11-integration.md @@ -0,0 +1,33 @@ +# Integration + +It's not uncommon to want to integrate scanservjs with other software - you may +wish to upload scans to Dropbox, paperless-ng or some other location. The +possibilities are endless but deep integration into the UI would add cruft for +the vast majority of users. + +Thankfully, the files just end up in a location on your filesystem so you are +free to integrate however you want. + +There are two recommended approaches: + +* Create a script or program which scans the output +directory for files and then does something with them. +* Create an [action](./10-configuration.md#actions) + +## paperless-ng + +[This discussion](https://github.com/sbs20/scanservjs/issues/351#issuecomment-913858423) +about paperless-ng resulted in +[scantopl](https://github.com/Celedhrim/scantopl) + +## Dropbox + +You could integrate with Dropbox using +[Dropbox-Uploader](https://github.com/andreafabrizi/Dropbox-Uploader) + +## insaned + +Use your scanner's hardware 'Scan' button to initiate a new scan via +`scanservjs`. Requires a scanner that exposes buttons as sensors and `curl`. + +* Repo: https://gitlab.com/xeijin-dev/insaned diff --git a/docs/config.md b/docs/12-recipes.md similarity index 66% rename from docs/config.md rename to docs/12-recipes.md index 8c04b978..779dfa24 100644 --- a/docs/config.md +++ b/docs/12-recipes.md @@ -1,135 +1,7 @@ -# Configuration and integration - -Sometimes scanners don't return quite what you want them to. Likewise, perhaps -scanservjs doesn't provide the defaults you want. And maybe you want to do your -own thing after a scan. Furtunately it's possible to override most things you -might want to. - -There are various hooks where you can customise behaviour: -* `afterConfig`: This provides a reference to the config where you can apply - your own changes to global settings which include things like: - * Server port - * Log level - * Preview resolution - * Output filename - * Pipelines (output format) -* `afterDevices`: You can alter the device definitions which are reported by - SANE which include scanner dimensions and geometry, modes, resolutions and - sources. -* `afterScan`: You receive a reference to the file which has just been scanned; - copy it somewhere, call a script or write some code. -* `actions`: You can define custom actions which can be applied to files either - in the UI or referenced in a pipeline. An action object must have a name and - async execute method taking a `FileInfo`: - ```javascript - { - name: 'Copy file to home directory', - async execute(fileInfo) { - await Process.spawn(`cp '${fileInfo.fullname}' ~/`); - } - } - ``` - -TL;DR; copy `./config/config.default.js` to `config/config.local.js`, override -the sections you want and then restart the app -`sudo systemctl restart scanservjs.service` - -If you are using docker, then you will want to map the configuration directory -e.g. `-v /my/local/path:/app/config`. - -## How it works - -scanservjs looks for a file called `config/config.local.js` and attempts to call -three functions at different stages in the processing: -* `afterConfig(config)`: whenever a config is read, the result is passed to this - function before being either used or sent down to the browser. -* `afterDevices(devices)`: whenever the devices are read, the result is passed - to this function before being used. -* `afterScan(fileInfo)`: whenever any scan completes, the resultant file is - passed to this function. -* `actions`: Either at the end of a specific pipeline or on user request. If it - runs at the end of the scan, then it's just prior to the `afterScan` event. -* See [example source](../packages/server/config/config.default.js) for more - options. -* Please note that the config file only gets read at start-up - so if you make - changes, you will need to restart. - -## Example file - -```javascript -const Process = require('../server/classes/process'); - -module.exports = { - /** - * @param {Configuration} config - */ - afterConfig(config) { - // Set default preview resolution - config.previewResolution = 150; - - // Add a custom print pipeline - config.pipelines.push({ - extension: 'pdf', - description: 'Print PDF', - commands: [ - 'convert @- -quality 92 tmp-%04d.jpg && ls tmp-*.jpg', - 'convert @- scan-0000.pdf', - 'lp -d MY_PRINTER scan-0000.pdf', - 'ls scan-*.*' - ] - }); - }, - /** - * @param {ScanDevice[]} devices - */ - afterDevices(devices) { - // Override the defaults for plustek scanners - devices - .filter(d => d.id.includes('plustek')) - .forEach(device => { - device.features['--mode'].default = 'Color'; - device.features['--resolution'].default = 150; - device.features['--resolution'].options = [75, 150, 300, 600]; - device.features['--brightness'].default = 0; - device.features['--contrast'].default = 5; - device.features['-x'].default = 215; - device.features['-y'].default = 297; - device.settings.batchMode.options = ['none']; - }); - } +# Recipes - /** - * @param {FileInfo} fileInfo - * @returns {Promise.} - */ - async afterScan(fileInfo) { - // Copy the scan to my home directory - return await Process.spawn(`cp '${fileInfo.fullname}' ~/`); - } - - /** - * @type {Action[]} - */ - actions: [ - { - name: 'Echo', - /** - * @param {FileInfo} fileInfo - * @returns {Promise.} - */ - async execute(fileInfo) { - // Output the filepath (relative to the present working direectory) - return await Process.spawn(`echo '${fileInfo.fullname}'`); - } - } - ] -}; -``` - -## Recipes - -### Override default width, height and resolution +## Override default width, height and resolution You have an old Canon flatbed but it returns daft default values for width and height. You also want to change the default resolution and limit the resolution @@ -152,7 +24,7 @@ options. } ``` -### Override scanner dimensions +## Override scanner dimensions Some scanners (I'm looking at you, Brother) report their dimensions [incorrectly](https://github.com/sbs20/scanservjs/issues/103). This throws off @@ -173,7 +45,7 @@ the cropping logic because scanservjs incorrectly trusts the SANE output. } ``` -### Friendly device name +## Friendly device name If you have many scanners available then you may wish to give devices friendly names as per [#212](https://github.com/sbs20/scanservjs/issues/212). @@ -193,7 +65,7 @@ can be anything you want it to be. You just need to override it. } ``` -### Insert your own pipelines +## Insert your own pipelines You may wish to add your own custom pipelines. Pipelines are arrays of shell commands which run after scans. @@ -245,7 +117,7 @@ your own pipelines at the top of the list. }, ``` -#### Pipeline using "ocrmypdf" +### Pipeline using `ocrmypdf` [ocrmypdf](https://github.com/jbarlow83/OCRmyPDF) is a tool which deskews crooked scans, automatically fixes incorrectly rotated pages and performs OCR with tesseract. It needs to be installed separately, see the @@ -267,7 +139,7 @@ Then, add the following pipeline: }); ``` -### Change the log level and default scan filename +## Change the log level and default scan filename ```javascript const dayjs = require('dayjs'); @@ -282,7 +154,7 @@ module.exports = { } ``` -### Change default output directory +## Change default output directory Exercise caution with this recipe - the app is designed not to allow unsafe paths by default. If you are happy to disable this check, then go ahead. @@ -296,7 +168,7 @@ module.exports = { } ``` -### Only show ISO paper sizes +## Only show ISO paper sizes You can use a filter to include only the paper sizes you want. To only show ISO sizes do something like the following. You can obviously extend or reverse the @@ -310,7 +182,7 @@ module.exports = { } ``` -### Handling `--page-width` and `--page-height` +## Handling `--page-width` and `--page-height` Some scanners need [additional arguments](https://github.com/sbs20/scanservjs/issues/401) for @@ -338,7 +210,7 @@ module.exports = { } ``` -### Override batchMode, filters and pipelines +## Override batchMode, filters and pipelines Devices also have their own batch modes, filters and pipelines. By default, each device inherits the settings in `config`. @@ -379,10 +251,10 @@ specific device or change the default. So just as with other device overrides: } ``` -### Add actions and call after a specific pipeline +## Add actions and call after a specific pipeline -Create a file action to do wwhatever you like - this might be useful for -integrating with paperless-ng. The example below defines a pipeline which +Create a file action to do whatever you like - this might be useful for +integrating with `paperless-ng`. The example below defines a pipeline which creates a PDF and then copies it to the home directory on completion. ```javascript @@ -420,7 +292,7 @@ module.exports = { }; ``` -### Add Basic Authentication +## Add Basic Authentication This is not meaningfully secure. There is no transport security by default, and the credentials are stored in plain text. But it may offer some peace of mind to @@ -439,3 +311,77 @@ module.exports = { } } ``` + +## Recipe for Scan2Cloud + +This recipe covers all major cloud providers such as Amazon, Dropbox, Google +(Drive/Photos), Microsoft (Azure Blob Storage, OneDrive), Nextcloud (via +WebDav), a network share of your choice (S/FTP) and many +[more](https://rclone.org/overview/) by using [Rclone](https://rclone.org/). + +1. Install [Rclone](https://rclone.org/) as described + [here](https://rclone.org/install/) +2. Configure your [Cloud Provider or Remote](https://rclone.org/overview/) + accordingly, for example [Nextcloud via Webdav](https://rclone.org/webdav/) + or [Google Drive](https://rclone.org/drive/) + +Now you have a choice. If you want the update to occur on the scan itself then +you need to integrate into the pipeline. Alternatively, sync the output +directory itself and use either inotify or cron. If you want to embed into the +pipeline then something like the following may help: + +```javascript + /** + * @param {FileInfo} fileInfo + * @returns {Promise.} + */ + async afterScan(fileInfo) { + // Copy the scan to my home directory + return await Process.spawn(`rclone copy '${fileInfo.fullname}' YOUR_PROVIDER:/path/to/folder`); + } +``` + +## Scan2Mail + +1. Setup and configure [msmtp](https://wiki.debian.org/msmtp) and msmtp-mta as + described + [here](https://decatec.de/linux/linux-einfach-e-mails-versenden-mit-msmtp/) +2. Install the MIME packer [mpack](https://linux.die.net/man/1/mpack) with + `sudo apt install mpack` to send the scanned files +3. Setup [OCRmyPDF](https://github.com/jbarlow83/OCRmyPDF) as described + [here](https://ocrmypdf.readthedocs.io/en/latest/installation.html) + +Now create the following pipeline in your `config/config.local.js` + +```javascript +const Process = require('../server/classes/process'); + +module.exports = { + afterConfig(config) { + config.pipelines.push({ + extension: 'pdf', + description: 'ocrmypdf', + get commands() { + return [ + 'convert @- -quality 92 tmp-%04d.jpg && ls tmp-*.jpg', + 'convert @- pdf:-', + `ocrmypdf -l ${config.ocrLanguage} --deskew --rotate-pages --force-ocr - "scan_0.pdf"`, + 'ls scan_*.*' + ]; + } + }); + }, + + /** + * @param {FileInfo} fileInfo + * @returns {Promise.} + */ + async afterScan(fileInfo) { + return await Process.spawn(`mpack -s "Document from Scanner@Office" "${fileInfo.fullname}" email@address.tld`); + } +}; +``` + +## Other recipes? + +If you have other recipes then please share them. diff --git a/docs/proxy.md b/docs/13-proxy.md similarity index 100% rename from docs/proxy.md rename to docs/13-proxy.md diff --git a/docs/development.md b/docs/90-development.md similarity index 62% rename from docs/development.md rename to docs/90-development.md index 8de80252..bc9a2993 100644 --- a/docs/development.md +++ b/docs/90-development.md @@ -1,12 +1,14 @@ # Development -## Install and setup +## Summary -```shell -# Setup any nodejs requirements -# https://github.com/nodesource/distributions#installation-instructions -# curl -fsSL https://deb.nodesource.com/setup_18.x | bash - +* Development requires at least Node 16 in order to support the UI build. +* All code in `app-server` must run under Node 10 +* Any commit must pass `npm run lint && npm run test && npm run build` + +## Getting started +```shell # Install dependencies sudo apt-get install curl nodejs npm imagemagick sane-utils tesseract-ocr @@ -23,26 +25,25 @@ cd scanservjs && npm install . npm run dev ``` -`npm run dev` will simultanesouly run the server (see package.json and -vite.config.js). +`npm run dev` will simultanesouly run the server (see [package.json](../package.json) and [vite.config.js](../app-ui/vite.config.js)). If you run into the following error, then you may need to increase your inotify limit: -``` +```log [nodemon] Internal watch failed: ENOSPC: System limit for number of file watchers reached, watch '/.../scanservjs/packages/server/src' ``` To incease it temporarily: -``` +```sh sudo sysctl fs.inotify.max_user_watches=131072 ``` To update it permanently will depend on your distribution - but this will work with Debian: -``` +```sh echo fs.inotify.max_user_watches=131072 | sudo tee -a /etc/sysctl.d/50-default.conf; sudo sysctl -p ``` @@ -50,24 +51,20 @@ echo fs.inotify.max_user_watches=131072 | sudo tee -a /etc/sysctl.d/50-default.c Before committing please verify and build -``` -npm run lint && npm run test && npm run build +```sh +npm run lint && npm run test && npm run build && ./makedeb.sh ``` ## Find missing translations -``` +```sh npm run util:missing-translations ``` -## References - -* [i18n](https://www.codeandweb.com/babeledit/tutorials/how-to-translate-your-vue-app-with-vue-i18n) - ## Docker Install docker -``` +```sh sudo apt install docker.io sudo systemctl unmask docker sudo systemctl start docker @@ -79,46 +76,16 @@ sudo chmod 666 /var/run/docker.sock Useful commands ```sh # Build -docker build -t scanservjs-image . +docker build --tag scanservjs-image . # Build the core image -docker build --target scanservjs-core -t scanservjs-image . +docker build --target scanservjs-core --tag scanservjs-image . # Remove any existing containers docker rm --force scanservjs-container 2> /dev/null # Different run options docker run -d -p 8080:8080 --name scanservjs-container --privileged scanservjs-image -docker run -d -p 8080:8080 -e SANED_NET_HOSTS="10.0.100.30" --name scanservjs-container --privileged scanservjs-image -docker run -d -p 8080:8080 -v /var/run/dbus:/var/run/dbus --name scanservjs-container --privileged scanservjs-image -docker run -d -p 8080:8080 -v $HOME/scan-data:/app/data/output --name scanservjs-container --privileged scanservjs-image - -# Copy image -docker save -o scanservjs-image.tar scanservjs-image -docker load -i scanservjs-image.tar - -# Shell inside image -docker run -it --entrypoint=/bin/bash scanservjs-image - -# Shell inside running container -docker exec -it scanservjs-container /bin/bash - -docker logs scanservjs-container - -# Start and stop -docker container rm scanservjs-container -docker container start scanservjs-container -docker container stop scanservjs-container -docker container restart scanservjs-container - -# Maintenance -docker ps -a -docker image prune -docker image rm -f $(docker image ls --filter dangling=true -q) -docker builder prune - -# Danger -docker image rm -f $(docker image ls -a -q) ``` ## Mount map configuration files @@ -137,7 +104,7 @@ docker run -d \ You may wish to attempt building with a different version of node. There are various ways to achieve this but Docker works well. -```Docker +```dockerfile # build.Dockerfile FROM node:18-bookworm-slim AS scanservjs-build ENV APP_DIR=/app @@ -171,3 +138,18 @@ id=$(docker create scanservjs-release-node18) docker cp $id:/app/debian ./ docker rm -v $id ``` + +## Pages + +```sh +sudo apt-get install -y ruby-full +gem install bundler --user-install + +# First time only +bundle config set --local path ~/.gem +bundle install + +bundle exec jekyll serve --incremental + +sudo touch /usr/bin/Gemfile +``` diff --git a/docs/91-localisation.md b/docs/91-localisation.md new file mode 100644 index 00000000..8a8a4749 --- /dev/null +++ b/docs/91-localisation.md @@ -0,0 +1,36 @@ +# Localisation + +The application uses `vue-i18n` for internationalisation (i18n, making an app +ready for localisation) and localisation (l10n, implementing specific locales). + +* Localisation files are stored in + [`/app-ui/src/locales/`](../app-ui/src/locales/) +* The list of locales is defined in + [`/app-ui/src/classes/constants.js`](../app-ui/src/classes/constants.js) + +## Adding a new locale + +* Copy an existing locale, name it according to the iso 639 and sub region; see + [w3c](https://www.w3.org/International/articles/language-tags/) +* Update the translations +* If possible, test it works (see [development](./90-development.md)) +* Raise a PR or attach it to a new issue or #154 +* Add the locale name to the fallback locale + [en](../app-ui/src/locales/en.json) under the "locales" key. +* If you can't translate a key, delete it - this will make it show up in + `missing translations` + +## Updating an existing locale + +Either an existing translation could be improved or is missing altogether. + +* Make the change and raise a PR +* Note that I am unable to adjudicate changes so please be thoughtful when + changing existing translations. And be collaborative - you are likely changing + something that someone else has put care into + +## Finding missing translations + +* Configure [development](./90-development.md) +* Run `npm run util:missing-translations` +* This will output JSON with all the missing keys diff --git a/docs/92-references.md b/docs/92-references.md new file mode 100644 index 00000000..1026a0f5 --- /dev/null +++ b/docs/92-references.md @@ -0,0 +1,46 @@ +# References + +* vue-i18n: + +## Creating a deb package + +* Primer: +* General examples: +* Also useful: +* Add user: +* File system hierarchy: +* The proper way: +* systemd: +* symlinks: + +## Docker for dummies + +```sh +# Copy image +docker save -o scanservjs-image.tar scanservjs-image +docker load -i scanservjs-image.tar + +# Shell inside image +docker run -it --entrypoint=/bin/bash scanservjs-image + +# Shell inside running container +docker exec -it scanservjs-container /bin/bash + +# Logs +docker logs scanservjs-container + +# Start and stop +docker container rm scanservjs-container +docker container start scanservjs-container +docker container stop scanservjs-container +docker container restart scanservjs-container + +# Maintenance +docker ps -a +docker image prune +docker image rm -f $(docker image ls --filter dangling=true -q) +docker builder prune + +# Danger +docker image rm -f $(docker image ls -a -q) +``` diff --git a/docs/93-qnap.md b/docs/93-qnap.md new file mode 100644 index 00000000..53b90e7a --- /dev/null +++ b/docs/93-qnap.md @@ -0,0 +1,73 @@ +# QNAP NAS + +This page is more or less abandoned. I no longer have any QNAP hardware and am +unable to test or experiment. + +It's likely that the application will work at a fundamental level, but will +require some effort to install the necessary packages. + +These instructions may serve as a useful starting point to get going. I will +gladly receive PRs which either update this document or assist with creating a +QNAP package. + +### install [Works on QTS 4.2.2] + + * [Install Entware](basics.md) + * SSH into your NAS - e.g. use PuTTY as admin + * Plug your scanner into a USB port + * Type `lsusb` to check the scanner is attached + * At the terminal type the following commands + * `opkg update` + * `opkg install sane-frontends imagemagick sudo` + * Confirm installation typing... + * `sane-find-scanner -q` + * `scanimage -L` + +### Get permissions working + +Pretend to be httpduser + +```sh +sudo -i -u httpdusr +``` + +If (when) that fails you need to edit sudoers and try again + +```sh +echo "admin ALL=(ALL) ALL" >> /opt/etc/sudoers +``` + +Once you're httpdusr then + +```sh +/opt/bin/scanimage -L +``` + +There are any number of problems you might face here. Your user probably won't +have access to "scanimage" or usb devices or the sane.d directory. And you +should probably do this with a group privilege. + +* [This thread](https://wiki.archlinux.org/index.php/SANE) and +* [that thread](https://bugs.launchpad.net/ubuntu/+source/sane-backends/+bug/270185/comments/3) + are really useful. The short version is to do this: + +```sh +addgroup scanner +usermod -G scanner httpdusr +chgrp scanner /dev/usb/* +chmod g+rw /dev/usb/* +chgrp scanner /opt/bin/scanimage +chmod 644 /opt/etc/sane.d/* +``` + +Find out the bus and device of your scanner using `lsusb` ... + +```log +Bus 003 Device 003: ID 04a9:220d Canon, Inc. CanoScan N670U/N676U/LiDE 20 +``` + +Then do this: chgrp scanner /proc/usb/{bus}/{dev} - so I did this: + +```sh +chgrp scanner /proc/usb/003/003 +``` diff --git a/docs/docker.md b/docs/docker.md deleted file mode 100644 index 27052101..00000000 --- a/docs/docker.md +++ /dev/null @@ -1,345 +0,0 @@ -# Running scanservjs under docker - -If you're already running Debian, Ubuntu or similar, and haven't used docker -before then it's probably easier just to install directly. For what it's worth, -that is my preferred installation method. - -## Quickstart - -Get the image - -```sh -docker pull sbs20/scanservjs:latest -``` - -Run it - -```sh -docker rm --force scanservjs-container 2> /dev/null -docker run -d \ - -p 8080:8080 \ - -v /var/run/dbus:/var/run/dbus \ - --restart unless-stopped \ - --name scanservjs-container \ - --privileged sbs20/scanservjs:latest -``` - -## General notes - -* ⚠ By default, configuration and scanned images are stored within the container - and will be lost if you recreate it. If you want to map your scanned images - then see mapping section below -* ✅ The docker image now supports arm as well as amd64. - -## Accessing hardware - -Docker is great for certain tasks. But it's less ideal for situations where the -container needs to access the host hardware. The "simple" solution is to run -with `--privileged` but that gives your container full root access to the host - -you're putting a lot of trust in the container. In short, best not. - -Depending on your setup you have a number of options: - -* If your scanner is **connected by USB to the host**, and there are standard - SANE drivers, then you can map the device. The best way to do this is to map - the actual USB ports. - * In case you scanner is always plugged to your device: - * Run `sudo sane-find-scanner -q` and you will get a result like - `found USB scanner (vendor=0x04a9 [Canon], product=0x220d [CanoScan], chip=LM9832/3) at libusb:001:003`. - * Or run `lsusb` which gives you - `Bus 001 Device 003: ID 04a9:220d Canon, Inc. CanoScan N670U/N676U/LiDE 20`. - * Both translate to `/dev/bus/usb/001/003`. - * The docker argument would be - `--device=/dev/bus/usb/001/003:/dev/bus/usb/001/003` - * You may also need to adjust permissions on the USB port of the host e.g. - `chmod a+rw dev/bus/usb/001/003` - see - [this](https://github.com/sbs20/scanservjs/issues/221#issuecomment-828757430) - helpful answer for more. - * In case your scanner is **not** always plugged, the device path will change - every so often, and the previous solution will not work. Also, some devices - will go to sleep after long idle times, effectively getting "unplugged" and - "plugged again" over and over.
In this case, you may use `udev` so that - it starts or re-configures your container whenever your scanner is - hot-plugged. This is suggested in - [the official Docker documentation](https://docs.docker.com/engine/reference/commandline/run/#device-cgroup-rule): - * Run `lsusb` to retrieve your device "vendor ID" and "product ID". In the - example above, - `Bus 001 Device 003: ID 04a9:220d Canon, Inc. CanoScan N670U/N676U/LiDE 20` - means that the vendor ID is `04a9`, and the product ID is `220d`. - * Add a udev rule - `/etc/udev/rules.d/50-add-scanner.rules` - ``` - ACTION=="add", ATTR{idVendor}=="04a9", ATTR{idProduct}=="1774", RUN+="/etc/scan/bind-scanner-to-container.sh $name $major $minor $attr{idVendor} $attr{idProduct}" - ``` - * Make `udev` aware of this change: `sudo udevadm control --reload-rules` - * This will run the following script every time the scanner is plugged. - `/etc/scan/bind-scanner-to-container.sh` - ```sh - #!/bin/bash - # This script must be executable by root - DEVICE_PATH="$1" - MAJOR_NUMBER="$2" - MINOR_NUMBER="$3" - - # USB identifiers of the device - VENDOR_ID="$4" - PRODUCT_ID="$5" - - CONTAINER_NAME="scan" - IMAGE_NAME="sbs20/scanservjs:release-v2.25.0" - - logger "Scanner ($VENDOR_ID:$PRODUCT_ID) is available at $DEVICE_PATH. Let's make it available to the scan server container" - - # Is the container running already? - container_id=$(docker ps -q -f name=$CONTAINER_NAME) - if [ -z "$container_id" ]; then - # Container was not running. We should start it, with the right device ID - device_nb=$(lsusb | grep "$VENDOR_ID:$PRODUCT_ID" | grep -o -E "Device [0-9]+" | grep -o -E "[0-9]+") - - if [ -z "$device_nb" ]; then - logger "Unable to find where this device is connected. Ignoring." - exit 1 - fi - - # Waiting for Docker to be available (if the scanner is plugged when the host boots, udev will trigger this script before Docker is even started) - attempts=0 - while true ; do - if [ "$(systemctl is-active docker)" == "active" ]; then - break - fi - sleep 10 - attempts=$(( attempts + 1 )) - if [ "$attempts" -gt 10 ]; then - logger "Docker is not running. Will not start scan server." - exit 1 - fi - done - - logger "Starting the scan server from $IMAGE_NAME, with device $device_nb ($VENDOR_ID:$PRODUCT_ID, major number is $MAJOR_NUMBER)..." - # --device adds the existing device to the container. - # --device-cgroup-rule makes it possible to add future hot-plugged devices - # see https://docs.docker.com/engine/reference/commandline/run/#device-cgroup-rule - docker run -d \ - --rm \ - -p 8080:8080 \ - -v /var/run/dbus:/var/run/dbus \ - -v /path/to/the/optional/scan/folder:/app/data/output \ - -v /path/to/the/optional/custom/config:/app/config \ - --name "$CONTAINER_NAME" \ - --device=/dev/bus/usb/001/"$device_nb":/dev/bus/usb/001/"$device_nb" \ - --device-cgroup-rule="c $MAJOR_NUMBER:* rmw" \ - "$IMAGE_NAME" 2>&1 | logger - else - # Container is running. We just have to add the device there - logger "Adding the new scanner to the scan server container..." - docker exec "$CONTAINER_NAME" mknod "/dev/$DEVICE_PATH" c "$MAJOR_NUMBER" "$MINOR_NUMBER" 2>&1 | logger - fi - ``` - * If you prefer, you may tweak both files above e.g. to stop the container - when the scanner is disconnected, and re-start the container when the - device is re-connected. - -* If your scanner is **driverless over the network**, then - [sane-airscan](https://github.com/alexpevzner/sane-airscan) should be able to - figure it out - but it uses Avahi / Zeroconf / Bonjour to discover devices on - the local network. You will want to share dbus to make it work - (`-v /var/run/dbus:/var/run/dbus`). - -* If your container is **running inside a VM** you may find that the USB device - id is [unstable](https://github.com/sbs20/scanservjs/issues/66) and changes - between boots. In these cases, you will probably find it easier to share the - scanner over the network on the host. - -* If you need **proprietary drivers** for your scanner then the best solution is - either to install the drivers on the host and share it over the network or to - create your own docker image based on the scanservjs one and add it in that - way. - - Here is an example on how one particular Brother scanner model and its driver - can be installed in the Dockerfile. The driver (`brscan4-0.4.10-1.amd64.deb`) - needs to be placed next to the Dockerfile, then: - ```dockerfile - COPY brscan4-0.4.10-1.amd64.deb "$APP_DIR/brscan4-0.4.10-1.amd64.deb" - RUN apt install -yq "$APP_DIR/brscan4-0.4.10-1.amd64.deb" \ - && brsaneconfig4 -a name=ADS-2600W model=ADS-2600W nodename=10.0.100.30 - ``` - Note: The addition of more backends to the docker container is not planned - since it would mostly add cruft for most users who don't need it. - -* Driverless-mode scanning (using airscan over IPP-USB) seems to result in - problems. If anyone has ideas why (perhaps something additional needs sharing - from host to guest) then suggestions are welcome. - -* The best fallback position for most cases is simply to - [share the host scanner over the network](https://github.com/sbs20/scanservjs/blob/master/docs/sane.md#configuring-the-server) - on the host (where the scanner is connected) and then set the - `SANED_NET_HOSTS` - [environment variable](https://github.com/sbs20/scanservjs/blob/master/docs/docker.md#environment-variables) - on the docker container. - [This](https://github.com/sbs20/scanservjs/issues/129#issuecomment-800226184) - user uses docker compose instead. See examples below. - -## Mapping volumes - -To access data from outside the docker container, there are two volumes you may -wish to map: - -* The scanned images: use `-v /local/path/scans:/app/data/output` -* Configuration overrides: use `-v /local/path/cfg:/app/config` - -### User and group mapping - -When mapping volumes, special attention must be paid to users and file systems -permissions. - -The docker container runs as root by default. Changing the user's UID (e.g. by -using `-u 1000` for `docker run`) to access scans/configuration from outside -docker **is not advised since it will cause scans to fail.**. If running as a -different user is important to you then see the `scanservjs-user2001` target in -[../Dockerfile](../Dockerfile). - -Your alternatives are: -1. changing the group of the container to a known group on the host e.g. - `-u 0:1000`. This will keep the user correct (`0`) but change the group - (`1000`). -2. building a docker image with a custom UID/GID pairing: clone this repository - and run - `docker build --build-arg UID=1234 --build-arg GID=5678 -t scanservjs_custom .` - (with UID and GID adjusted to your liking), then run the custom image (e.g. - `docker run scanservjs_custom`). -3. as a last resort, changing the host volume permissions e.g. - `chmod 777 local-volume` - -## Environment variables - -* `SANED_NET_HOSTS`: If you want to use a - [SaneOverNetwork](https://wiki.debian.org/SaneOverNetwork#Server_Configuration) - scanner then to perform the equivalent of adding hosts to - `/etc/sane.d/net.conf` specify a list of ip addresses separated by semicolons - in the `SANED_NET_HOSTS` environment variable. -* `AIRSCAN_DEVICES`: If you want to specifically add `sane-airscan` devices to - your `/etc/sane.d/airscan.conf` then use the `AIRSCAN_DEVICES` environment - variable (semicolon delimited). -* `PIXMA_HOSTS`: If you want to use a PIXMA scanner which uses the bjnp protocol then to perform the equivalent of adding hosts to `/etc/sane.d/pixma.conf` specify a list of ip addresses separated by semicolons in the `PIXMA_HOSTS` environment variable. -* `DELIMITER`: if you need to include semi-colons (`;`) in your environment - variables, this allows you to choose an alternative delimiter. -* `DEVICES`: Force add devices use `DEVICES` (semicolon delimited) -* `SCANIMAGE_LIST_IGNORE`: To force ignore `scanimage -L` - -## Examples - -### Connect to the scanner over the network (recommended) -```sh -docker run -d -p 8080:8080 \ - -e SANED_NET_HOSTS="10.0.100.30" \ - --name scanservjs-container sbs20/scanservjs:latest -``` - -### Mapped USB device with mapped volumes - -```sh -docker run -d -p 8080:8080 \ - -v $HOME/scan-data:/app/data/output \ - -v $HOME/scan-cfg:/app/config \ - --device /dev/bus/usb/001/003:/dev/bus/usb/001/003 \ - --name scanservjs-container sbs20/scanservjs:latest -``` - -### Use airscan and a locally detected scanner - -This should support most use cases - -```sh -docker run -d -p 8080:8080 \ - -v /var/run/dbus:/var/run/dbus \ - --name scanservjs-container sbs20/scanservjs:latest -``` - -### A bit of everything - -Add two net hosts to sane, use airscan to connect to two remote scanners, add two pixma scanners using the bjnp protocol, don't -use `scanimage -L`, force a list of devices, override the OCR language and run -in privileged mode - -```sh -docker run -d -p 8080:8080 \ - -e SANED_NET_HOSTS="10.0.100.30;10.0.100.31" \ - -e AIRSCAN_DEVICES='"Canon MFD" = "http://192.168.0.10/eSCL";"EPSON MFD" = "http://192.168.0.11/eSCL"' \ - -e PIXMA_HOSTS="10.0.100.32;10.0.100.33" \ - -e SCANIMAGE_LIST_IGNORE=true \ - -e DEVICES="net:10.0.100.30:plustek:libusb:001:003;net:10.0.100.31:plustek:libusb:001:003;airscan:e0:Canon TR8500 series;airscan:e1:EPSON Cool Series" \ - -e OCR_LANG="fra" \ - -v /var/run/dbus:/var/run/dbus \ - --name scanservjs-container --privileged sbs20/scanservjs:latest -``` - -### Hosting it on a Synology NAS using Docker - -It can be convenient to host scanservjs on the same machine where you store your -scans — your NAS. Here's a possible approach for network scanning with a -Synology NAS: - -1. Install the - [Synology Docker package](https://www.synology.com/en-us/dsm/packages/Docker). -2. In DSM, create a service user "scanservjs" which will run the Docker - container. Make sure to give it write permission to the preferred target - location for scans. We'll use `/volume1/scans`. -3. SSH with an admin account onto the NAS and use `id` to determine the UID and - GID of the service user just created: - ```sh - admin@synology:~$ id scanservjs - uid=1034(scanservjs) gid=100(users) groups=100(users),65538(scanusers) - ``` - Keep the session open, we'll need it again in a moment. -4. On your workstation, download and extract - [the latest scanservjs release](https://github.com/sbs20/scanservjs/releases/latest). -5. In the repository root, create a text file named `docker-compose.yml` with - the following content: - ```yaml - version: "3" - services: - scanservjs: - build: - context: . - args: - # ----- enter UID and GID here ----- - UID: 1034 - GID: 100 - target: scanservjs-user2001 - container_name: scanservjs - environment: - # ----- specify network scanners here; see above for more possibilities ----- - - SANED_NET_HOSTS="10.0.100.30" - volumes: - # ---- enter your target location for scans before the ':' character ----- - - /volume1/scans:/app/data/output - - ./config:/app/config - ports: - - 8080:8080 - restart: unless-stopped - ``` -6. Copy the entire repository including `docker-compose.yml` onto your NAS (via - smb, sftp, ...). -7. In your SSH session from earlier, `cd` to the repository location and run - ```sh - sudo docker-compose up -d - ``` -8. After a medium-sized cup of tea, scanservjs should be available at - `http://:8080` -9. Bonus: Create a reverse proxy rule in the - [Application Portal](https://www.synology.com/en-global/knowledgebase/DSM/help/DSM/AdminCenter/application_appportalias) - so that scanservjs can be reached via `http://scan.synology.lan` (or - similar). Scanning can be slow, so set the proxy timeouts to 300 seconds or - more [to prevent timeout issues](troubleshooting.md). - -## Staging builds - -These may be less stable, but also have upcoming features. - -If you want to install the latest staging branch (this may contain newer code) - -```sh -docker pull sbs20/scanservjs:staging -docker rm --force scanservjs-container 2> /dev/null -docker run -d -p 8080:8080 -v /var/run/dbus:/var/run/dbus --restart unless-stopped --name scanservjs-container --privileged sbs20/scanservjs:staging -``` diff --git a/docs/install.md b/docs/install.md deleted file mode 100644 index dd9fcb8c..00000000 --- a/docs/install.md +++ /dev/null @@ -1,104 +0,0 @@ -# Standard installation - -## One line install - -* If you don't already have your scanner working, then you must get - [SANE installed and working](./sane.md) and check permissions etc. Your - scanner can be attached to a different server / device if you're using saned. -* If you're using a debian based distro then you can just use the installer - script. But please note that this will install dependecies and needs to run as - root: - ```sh - curl -s https://raw.githubusercontent.com/sbs20/scanservjs/master/bootstrap.sh | sudo bash -s -- -v latest - ``` - - If you wish to install an old version then you can now do so: - ```sh - curl -s https://raw.githubusercontent.com/sbs20/scanservjs/master/bootstrap.sh | sudo bash -s -- -v v2.23.0 - ``` - -* If you're using Arch, then [@dadosch](https://github.com/dadosch) created a - PKGBUILD script in Arch's AUR which allows Arch-distro-based users to quickly - install and update scanservjs with any AUR helper, for example: - `yay -S scanservjs`. See - [package](https://aur.archlinux.org/packages/scanservjs/) for more. -* If you're using another distro, then for the time being you either need to - manually run the steps in the install script or use docker. - -## Manual download and install - -If you don't fancy running a script directly from `curl` then you can manually -download the package and then run the installer inside. - -``` -wget -O ~/scanservjs.tar.gz $(curl -s https://api.github.com/repos/sbs20/scanservjs/releases/latest | grep browser_download_url | cut -d '"' -f 4) -mkdir scanservjs -tar -xf scanservjs.tar.gz -C ./scanservjs/ -sudo ./scanservjs/installer.sh -i -rm scanservjs.tar.gz -rm -r scanservjs -``` - -## Uninstall - -To uninstall, just run `sudo /var/www/scanservjs/installer.sh -u`. - -* You need root privileges - hence `sudo` -* When you run with no arguments, it will just - [print the help](https://github.com/sbs20/scanservjs/blob/e4ce5f0de13a23c3050ddd7e58dedb790c9fa4d4/packages/server/installer.sh#L188) -* You can read the script to see what it does -* There are actually two uninstall options `-u` (safe and fine) and - `--force-uninstall`, which will also remove dependencies, which is a bit more - risky. Again, see the script source to decide for yourself. - -## Troubleshooting - -Scanservjs works by wrapping CLI calls to `scanimage` as the user `scanservjs` -which is a member of the `scanner` group. If connected by USB then we ultimately -need access to some hardware and that access may not be granted by default. To -debug where the problem is: - -* First, check that you've followed the instructions [here](./sane.md). -* Try running `sudo scanimage -L` (for diagnostic purposes) - this really should - work. If it doesn't, then it's most likely a SANE / driver related issue. -* Now try running as a normal user without sudo: `scanimage -L`. If you've - installed scanservjs then there should be a `scanservjs` user. Try the same - command as that user: `sudo su - scanservjs -c 'scanimage -L'`. If this - doesn't show your scanner then most likely you need a udev rule (see - [here](./sane.md)) to allow certain groups access to the hardware - but it's - also worth verifying that the `scanservjs` user is a member of the `scanner` - group (or the group specified in your udev rule): `groups scanservjs`. -* If everything so far has worked, then also try running a scan as the - scanservjs user with - `sudo su - scanservjs -c 'scanimage --format tiff > test.tif'` - this should - output a tif file in the scanservjs home directory (probably - `/home/scanservjs/`). If you can get this to work then scanservjs should be - working fine. -* Getting logs: use `journalctl`. See the journalctl manpage for details but - `sudo journalctl -e -u scanservjs` should be enough to get you started. - -## Old Debian - -For more on problems installing an up to date nodejs on Debian which includes -`npm`. See -[here](https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions) - -```console -curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash - -sudo apt install -y nodejs -sudo apt-get install npm sane-utils imagemagick -``` - -Debian calls the node binary "nodejs", which results in npm not being able to -find the command. Use one of the following to resolve the issue: -* `ln -s /usr/bin/nodejs /usr/bin/node` -* `sudo apt-get install nodejs-legacy` - -See -[run npm command gives error "/usr/bin/env: node: No such file or directory" #3911](https://github.com/nodejs/node-v0.x-archive/issues/3911#issuecomment-8956154) -for more details. - -## Arch - -If you're using Arch, you probably don't need help but this worked a few years -ago `sudo pacman -S nodejs npm sane-utils imagemagick curl` diff --git a/docs/integration.md b/docs/integration.md deleted file mode 100644 index 37b7aef4..00000000 --- a/docs/integration.md +++ /dev/null @@ -1,104 +0,0 @@ -# Integration - -It's not uncommon to want to integrate scanservjs with other software - you may -wish to upload scans to Dropbox, paperless-ng or some other location. The -possibilities are endless but deep integration into the UI would add cruft for -the vast majority of users. - -Thankfully, the files just end up in a location on your filesystem so you are -free to integrate however you want. - -The recommended way is to create a script or program which scans the output -directory for files and then does something with them. - -## paperless-ng - -[This discussion](https://github.com/sbs20/scanservjs/issues/351#issuecomment-913858423) -about paperless-ng resulted in -[scantopl](https://github.com/Celedhrim/scantopl) - -## Dropbox - -You could integrate with Dropbox using -[Dropbox-Uploader](https://github.com/andreafabrizi/Dropbox-Uploader) - -## insaned - -Use your scanner's hardware 'Scan' button to initiate a new scan via -`scanservjs`. Requires a scanner that exposes buttons as sensors and `curl`. - -* Repo: https://gitlab.com/xeijin-dev/insaned - -## Recipe for Scan2Cloud - -This recipe covers all major cloud providers such as Amazon, Dropbox, Google -(Drive/Photos), Microsoft (Azure Blob Storage, OneDrive), Nextcloud (via -WebDav), a network share of your choice (S/FTP) and many -[more](https://rclone.org/overview/) by using [Rclone](https://rclone.org/). - -1. Install [Rclone](https://rclone.org/) as described - [here](https://rclone.org/install/) -2. Configure your [Cloud Provider or Remote](https://rclone.org/overview/) - accordingly, for example [Nextcloud via Webdav](https://rclone.org/webdav/) - or [Google Drive](https://rclone.org/drive/) - -Now you have a choice. If you want the update to occur on the scan itself then -you need to integrate into the pipeline. Alternatively, sync the output -directory itself and use either inotify or cron. If you want to embed into the -pipeline then something like the following may help: - -```javascript - /** - * @param {FileInfo} fileInfo - * @returns {Promise.} - */ - async afterScan(fileInfo) { - // Copy the scan to my home directory - return await Process.spawn(`rclone copy '${fileInfo.fullname}' YOUR_PROVIDER:/path/to/folder`); - } -``` - -## Scan2Mail - -1. Setup and configure [msmtp](https://wiki.debian.org/msmtp) and msmtp-mta as - described - [here](https://decatec.de/linux/linux-einfach-e-mails-versenden-mit-msmtp/) -2. Install the MIME packer [mpack](https://linux.die.net/man/1/mpack) with - `sudo apt install mpack` to send the scanned files -3. Setup [OCRmyPDF](https://github.com/jbarlow83/OCRmyPDF) as described - [here](https://ocrmypdf.readthedocs.io/en/latest/installation.html) - -Now create the following pipeline in your `config/config.local.js` - -```javascript -const Process = require('../server/classes/process'); - -module.exports = { - afterConfig(config) { - config.pipelines.push({ - extension: 'pdf', - description: 'ocrmypdf', - get commands() { - return [ - 'convert @- -quality 92 tmp-%04d.jpg && ls tmp-*.jpg', - 'convert @- pdf:-', - `ocrmypdf -l ${config.ocrLanguage} --deskew --rotate-pages --force-ocr - "scan_0.pdf"`, - 'ls scan_*.*' - ]; - } - }); - }, - - /** - * @param {FileInfo} fileInfo - * @returns {Promise.} - */ - async afterScan(fileInfo) { - return await Process.spawn(`mpack -s "Document from Scanner@Office" "${fileInfo.fullname}" email@address.tld`); - } -}; -``` - -## Other recipes? - -If you have other recipes then please share them. diff --git a/docs/sane.md b/docs/sane.md deleted file mode 100644 index cd1d72aa..00000000 --- a/docs/sane.md +++ /dev/null @@ -1,195 +0,0 @@ -# SANE, imagemagick, scanners - -## Install - -Just use your package manager. - - * Debian / Ubuntu / Raspbian: `sudo apt install sane-utils imagemagick` - * Arch: `sudo pacman -S sane` - -## Validate SANE is working - -Either try: - -```sh -$ scanimage -L -device `net:localhost:plustek:libusb:001:004' is a Canon CanoScan N1240U/LiDE30 flatbed scanner -``` - -or - -```sh -$ sane-find-scanner -q -found USB scanner (vendor=0x04a9 [Canon], product=0x220d [CanoScan]) at libusb:003:005 -``` - -If you do not see the expected result then try each with `sudo` which really -should work; this is a diagnostic approach, not a solution. If it works then we -have established it's not a hardware / driver issue. - -There are a variety of approaches to fixing permissions according to what the -underlying problem is. In order of likelihood: - - * Add a udev rule for the scanner device. Use the vendorId:productId from - `lsusb` and add to `/etc/udev/rules.d/55-libsane.rules` as - `ATTRS{idVendor}=="04a9", ATTRS{idProduct}=="220d", MODE="0666", GROUP="scanner", ENV{libsane_matched}="yes"`. - Unplug / replug the scanner. - * Add current user to the `scanner` group - -## Defining network scanners - -### Configuring the server - -Assume the host (the device the scanner is connected to) has an IP of -`192.168.0.10` - -```sh -# Allow access from the following networks -## Local network -echo "192.168.0.0/24" >> /etc/sane.d/saned.conf -## Default docker network -echo "172.17.0.0/16" >> /etc/sane.d/saned.conf -sudo systemctl enable saned.socket -sudo systemctl start saned.socket -``` - -### Configuring the client - -Add the host (`192.168.0.10`) to the client. - -```sh -echo "192.168.0.10" >> /etc/sane.d/net.conf -``` - -Now if you run `scanimage -L` on the client you should see the scanner on the -host (prefixed with `net:192.168.0.10:`) - -For more information on configuring the server and client see -[SaneOverNetwork](https://wiki.debian.org/SaneOverNetwork#Server_Configuration). - -It's worth noting that network scanners do not always show up on the client. In -such cases you will need to get the device name from the host, prefix it with -`net:$ip:` and use the `-d` switch. From the scanimage manpage: - -> The -L or --list-devices option requests a (partial) list of devices that are -> available. The list is not complete since some devices may be available, but -> are not listed in any of the configuration files (which are typically stored -> in directory /etc/sane.d). This is particularly the case when accessing -> scanners through the network. If a device is not listed in a configuration -> file, the only way to access it is by its full device name. You may need to -> consult your system administrator to find out the names of such devices. - -## Scanners requiring additional drivers - -Some scanner models require additional drivers to function with SANE. For -example, Brother offers proprietary drivers for their USB and network scanners. -Please follow the manufacturer's instructions for setting up such scanners. -Once a scanner is listed in `scanimage -L`, it should be ready to use with -scanservjs. - -## SANE Airscan - -You may find [sane-airscan](https://github.com/alexpevzner/sane-airscan) useful -for supporting newer eSCL and WSD devices as the standard sane-escl package -doesn't seem to be widely available in most package managers yet. Once installed -you should just find that it works with a simple `scanimage -L`. You can also -specify a specific name for the device in `/etc/sane.d/airscan.conf` - -```console -[devices] -"My scanner" = "http://10.0.111.4/eSCL" -``` - -Your URI will be different. You shouldn't need to do that though. - -Airscan relies on bonjour to make it work - which means broadcasting to the -local subnet to autodiscover the device. If you are running on a different -subnet, the autodiscovery won't work. You have two options: - -1. Use avahi reflector which "just" reflects broadcasts across the subnets - ``` - apt-get install avahi-daemon -y sed -i "s/#enable-reflector=no/enable-reflector=yes/g" /etc/avahi/avahi-daemon.conf - systemctl restart avahi-daemon - ``` -2. If have servers galore then you can create a saned host in the same subnet as - the scanner which uses airscan to reach the scanner, but re-shares the - scanner as a SANED network scanner (see above) - we'll call this - `The bridge`. Then have your `scanimage` client reference `The bridge` as a - remote scanner host and add the IP address of `The bridge` to - `/etc/sane.d/net.conf` on the client (which will be the scanservjs server). - You may also need to add the full device name to the devices list in the - config e.g. - ```javascript - //config.devices.push('net:${bridge}:${device}); - config.devices.push('net:10.0.100.171:airscan:e0:Canon TR8500 series-5); - ``` - -## For QNAP NAS - -Please note - these instructions may be long out of date. If they are incorrect -then feel free to raise and issues or PR. - -### install [Works on QTS 4.2.2] - - * [Install Entware](basics.md) - * SSH into your NAS - e.g. use PuTTY as admin - * Plug your scanner into a USB port - * Type `lsusb` to check the scanner is attached - * At the terminal type the following commands - * `opkg update` - * `opkg install sane-frontends imagemagick sudo` - * Confirm installation typing... - * `sane-find-scanner -q` - * `scanimage -L` - -### Get permissions working - -Pretend to be httpduser -``` -sudo -i -u httpdusr -``` - -If (when) that fails you need to edit sudoers and try again - -``` -echo "admin ALL=(ALL) ALL" >> /opt/etc/sudoers -``` - -Once you're httpdusr then -``` -/opt/bin/scanimage -L -``` - -There are any number of problems you might face here. Your user probably won't -have access to "scanimage" or usb devices or the sane.d directory. And you -should probably do this with a group privilege. - -[This thread](https://wiki.archlinux.org/index.php/SANE) and -[that thread](https://bugs.launchpad.net/ubuntu/+source/sane-backends/+bug/270185/comments/3) -are really useful. The short version is to do this: - -``` -addgroup scanner -usermod -G scanner httpdusr -chgrp scanner /dev/usb/* -chmod g+rw /dev/usb/* -chgrp scanner /opt/bin/scanimage -chmod 644 /opt/etc/sane.d/* -``` - -Find out the bus and device of your scanner using `lsusb` ... -``` -Bus 003 Device 003: ID 04a9:220d Canon, Inc. CanoScan N670U/N676U/LiDE 20 -``` -Then do this: chgrp scanner /proc/usb/{bus}/{dev} - so I did this: -``` -chgrp scanner /proc/usb/003/003 -``` - -## Raspberry Pi - -USB-only scanners draw a lot of current relative to the Pi's available power. -This can manifest itself in unusual scans - technically valid images but with -odd colours and block transforms. Consider using a powered USB hub (e.g. for a -Canon LIDE 20). If you encounter similar then see -[here](https://www.raspberrypi.org/forums/viewtopic.php?f=28&t=53832). diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md deleted file mode 100644 index 25e23b96..00000000 --- a/docs/troubleshooting.md +++ /dev/null @@ -1,59 +0,0 @@ -# Troubleshooting - -## scanimage: sane_read: Invalid argument - -This is a problem with SANE rather than scanservjs. It usually signifies a -[problem with the driver](https://askubuntu.com/a/447283). Your best bet is -going back to first principles with SANE itself. Follow the steps -[here](./install.md#troubleshooting) - -## Cropping results in incorrect positioning - -Some scanners mis-report their size - don't know why, but they do. This means -that when the app attempts to crop things the maths is all wrong. The best way -around this is to override the reported scanner dimensions. See -[this recipe](./config.md#override-scanner-dimensions) for more. - -## JSON.parse error - -This happens when the browser received a string from the server which is not a -valid JSON string. Most likely you're running a proxy server (e.g. nginx) which -is timing out prior to the completion of the request. Scanservjs can sometimes -take a little while to fulfil its requests - usually because it's waiting for a -scanner, but sometimes because it's having to do a fair amount of image -processing and you're -[running on a low power CPU (e.g. RPi)](https://github.com/sbs20/scanservjs/issues/224). - -The solution is to increase the proxy timeout. For nginx that might look like: - -``` -server{ - ... - proxy_read_timeout 300; - proxy_connect_timeout 300; - proxy_send_timeout 300; - ... -} -``` - -## Long scan timeout - -When scanning files with high resolution, e.g. 1200dpi it is very likely for the -request to timeout. This is because node HTTP times out after 2 minutes by -default. The solution is to increase the default timeout. That's possible by -setting `config.timeout = 600000;` (for 10 minutes for example). - -## Docker container loses scanner after device reboot - -As per -[issue #505](https://github.com/sbs20/scanservjs/issues/505#issuecomment-1364533826) -containers can lose their access to a device after a device reboot. - -This is more SANE and containers than this app. The user's solution was to add a -udev rule as below. You will need to substitute your own product and vendor -variables. - -`/etc/udev/rules.d/99-printer.rules` -``` -SUBSYSTEMS=="usb",KERNELS=="1-1.1",DRIVERS=="usb",ATTRS{idProduct}=="0827", ATTRS{idVendor}=="04b8", ATTRS{serial}=="L53010612130846360",SYMLINK+="%s{manufacturer}_printer",TAG+="systemd",RUN+="/bin/bash -c '/usr/bin/systemctl restart container-scanservjs.service &'" -``` diff --git a/makedeb.sh b/makedeb.sh index c0d6e38b..7b3c6168 100755 --- a/makedeb.sh +++ b/makedeb.sh @@ -68,10 +68,6 @@ WorkingDirectory=$PATH_LIB WantedBy=multi-user.target EOF -# NPM is just recommended. The reason is that if it's required, then it fails in -# Debian Buster, where the only sensible way to install a modern nodejs is to -# use nodesource. Making it recommended but >= 7.5 keeps it off buster. - # DEBIAN/control cat > $DIR_DEBIAN/control << EOF Package: scanservjs @@ -90,7 +86,7 @@ Size: $(($(tar -Jcf - $BASE/* | wc -c) / 1024)) Homepage: https://github.com/sbs20/scanservjs EOF -# DEBIAN/preint +# DEBIAN/preinst cat > $DIR_DEBIAN/preinst << EOF #!/bin/sh if [ -d /var/www/scanservjs ] && grep -q '/var/www/scanservjs' /etc/systemd/system/scanservjs.service; then