scanservjs: Deploy scanserv-js
*scanserv-js* is a web-based front-end for SANE. It allows scanning documents from a browser. Using the `config.local.js` file, we implement the `afterScan` hook to automatically upload scanned files to *paperless-ngx* using its REST API.dch-webhooks-secrets
parent
8a966a7ffb
commit
1c31c01688
|
@ -0,0 +1 @@
|
||||||
|
paperless.token
|
|
@ -0,0 +1,36 @@
|
||||||
|
# scanserv-js
|
||||||
|
|
||||||
|
[scanservjs][0] is an open source, web-based front-end for the [SANE][1]
|
||||||
|
software suite. It provides a simple graphical user interface for scanning
|
||||||
|
photos and documents using any local or network-connected SANE-compatible
|
||||||
|
scanner.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```sh
|
||||||
|
kubectl apply -k scanservjs
|
||||||
|
```
|
||||||
|
|
||||||
|
Environment variables used by the [container image][2] can be set in the
|
||||||
|
`scanservjs.env` file.
|
||||||
|
|
||||||
|
|
||||||
|
## Integration with *paperless-ngx*
|
||||||
|
|
||||||
|
[paperless-ngx][3] is an open-source, web-based document management platform.
|
||||||
|
Integrating *scanservjs* with *paperless-ngx* allows scanned documents to be
|
||||||
|
fed automatically into the document management system for OCR, analysis, and
|
||||||
|
classification. This integration is performed using the `afterScan` hook in
|
||||||
|
the *scanservjs* `config.local.js` file and the *paperless-ngx* REST API to
|
||||||
|
upload documents scanned as PDF files.
|
||||||
|
|
||||||
|
To enable the integration, specify the URL to the *paperless-ngx* service in
|
||||||
|
the `PAPERLESS_URL` environment variable. Additionally, create an
|
||||||
|
[authentication token][4] for *paperless-ngx* user, store it in a file, and set
|
||||||
|
the `PAPERLESS_TOKEN_FILE` environment variable to the file path.
|
||||||
|
|
||||||
|
[0]: https://github.com/sbs20/scanservjs
|
||||||
|
[1]: http://sane-project.org/
|
||||||
|
[2]: https://github.com/sbs20/scanservjs/blob/master/docs/docker.md
|
||||||
|
[3]: https://docs.paperless-ngx.com/
|
||||||
|
[4]: https://docs.paperless-ngx.com/api/#authorization
|
|
@ -0,0 +1,77 @@
|
||||||
|
// vim: set sw=2 ts=2 sts=2 et :
|
||||||
|
const fs = require('node:fs/promises');
|
||||||
|
const http = require('node:http');
|
||||||
|
const https = require('node:https');
|
||||||
|
const path = require('node:path');
|
||||||
|
|
||||||
|
const HANDLERS = {
|
||||||
|
'http': http,
|
||||||
|
'https': https,
|
||||||
|
}
|
||||||
|
|
||||||
|
async function get_token() {
|
||||||
|
const buf = await fs.readFile(
|
||||||
|
process.env['PAPERLESS_TOKEN_FILE'],
|
||||||
|
'utf-8',
|
||||||
|
);
|
||||||
|
return buf.trimEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function paperless_send(filepath) {
|
||||||
|
if (!'PAPERLESS_URL' in process.env) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename = path.basename(filepath);
|
||||||
|
const boundary = `------------------------${Math.random() * 1e16}`;
|
||||||
|
const token = await get_token();
|
||||||
|
const options = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': `multipart/form-data; boundary=${boundary}`,
|
||||||
|
'Authorization': `Token ${token}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const url = new URL(
|
||||||
|
process.env['PAPERLESS_URL'] + '/api/documents/post_document/'
|
||||||
|
);
|
||||||
|
console.log('Uploading', filename, 'to', url.toString());
|
||||||
|
const handler = HANDLERS[url.protocol.slice(0, -1)];
|
||||||
|
const req = handler.request(url, options, (res) => {
|
||||||
|
console.log('Received HTTP status code', res.statusCode, 'from Paperless');
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', (e) => {
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
const header = `--${boundary}\r\n` +
|
||||||
|
`Content-Disposition: form-data; name="document"; filename="${filename}"\r\n` +
|
||||||
|
`\r\n`
|
||||||
|
const trailer = `\r\n--${boundary}--\r\n`
|
||||||
|
const f = await fs.open(filepath);
|
||||||
|
const st = await f.stat();
|
||||||
|
const length = header.length + st.size + trailer.length;
|
||||||
|
req.setHeader('Content-Length', length.toString());
|
||||||
|
req.write(header)
|
||||||
|
while (1) {
|
||||||
|
const d = await f.read();
|
||||||
|
if (!d.bytesRead) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
req.write(d.buffer.slice(0, d.bytesRead));
|
||||||
|
};
|
||||||
|
await f.close();
|
||||||
|
|
||||||
|
req.write(trailer);
|
||||||
|
req.end();
|
||||||
|
console.log('Upload complete');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
async afterScan(fileInfo) {
|
||||||
|
if (fileInfo.name.endsWith('.pdf')) {
|
||||||
|
await paperless_send(fileInfo.fullname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: scanservjs
|
||||||
|
app.kubernetes.io/component: scanservjs
|
||||||
|
app.kubernetes.io/instance: scanservjs
|
||||||
|
app.kubernetes.io/part-of: scanservjs
|
||||||
|
annotations:
|
||||||
|
nginx.ingress.kubernetes.io/proxy-body-size: '0'
|
||||||
|
nginx.ingress.kubernetes.io/auth-method: GET
|
||||||
|
nginx.ingress.kubernetes.io/auth-url: http://authelia.authelia.svc.cluster.local:9091/api/verify
|
||||||
|
nginx.ingress.kubernetes.io/auth-signin: https://auth.pyrocufflink.blue/?rm=$request_method
|
||||||
|
nginx.ingress.kubernetes.io/auth-response-headers: Remote-User,Remote-Name,Remote-Groups,Remote-Email
|
||||||
|
nginx.ingress.kubernetes.io/auth-snippet: |
|
||||||
|
proxy_set_header X-Forwarded-Method $request_method;
|
||||||
|
name: scanservjs
|
||||||
|
namespace: scanservjs
|
||||||
|
spec:
|
||||||
|
ingressClassName: nginx
|
||||||
|
tls:
|
||||||
|
- hosts:
|
||||||
|
- scan.pyrocufflink.blue
|
||||||
|
rules:
|
||||||
|
- host: scan.pyrocufflink.blue
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: scanservjs
|
||||||
|
port:
|
||||||
|
name: http
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
|
||||||
|
resources:
|
||||||
|
- scanservjs.yaml
|
||||||
|
- ingress.yaml
|
||||||
|
|
||||||
|
configMapGenerator:
|
||||||
|
- name: scanservjs
|
||||||
|
namespace: scanservjs
|
||||||
|
envs:
|
||||||
|
- scanservjs.env
|
||||||
|
options:
|
||||||
|
disableNameSuffixHash: true
|
||||||
|
- name: scanservjs-config
|
||||||
|
namespace: scanservjs
|
||||||
|
files:
|
||||||
|
- config.local.js
|
||||||
|
options:
|
||||||
|
disableNameSuffixHash: true
|
||||||
|
|
||||||
|
secretGenerator:
|
||||||
|
- name: paperless-token
|
||||||
|
namespace: scanservjs
|
||||||
|
files:
|
||||||
|
- paperless.token
|
||||||
|
options:
|
||||||
|
disableNameSuffixHash: true
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
SCANIMAGE_LIST_IGNORE=true
|
||||||
|
DEVICES=escl:https://172.30.0.234:443
|
||||||
|
PAPERLESS_URL=http://paperless-ngx.paperless-ngx.svc.cluster.local:8000
|
|
@ -0,0 +1,118 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: scanservjs
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: scanservjs
|
||||||
|
app.kubernetes.io/component: scanservjs
|
||||||
|
app.kubernetes.io/instance: scanservjs
|
||||||
|
app.kubernetes.io/part-of: scanservjs
|
||||||
|
name: scanservjs
|
||||||
|
namespace: scanservjs
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 8080
|
||||||
|
selector:
|
||||||
|
app.kubernetes.io/name: scanservjs
|
||||||
|
app.kubernetes.io/component: scanservjs
|
||||||
|
app.kubernetes.io/instance: scanservjs
|
||||||
|
type: ClusterIP
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: scanservjs
|
||||||
|
namespace: scanservjs
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: scanservjs
|
||||||
|
app.kubernetes.io/component: scanservjs
|
||||||
|
app.kubernetes.io/instance: scanservjs
|
||||||
|
app.kubernetes.io/part-of: scanservjs
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: scanservjs
|
||||||
|
app.kubernetes.io/component: scanservjs
|
||||||
|
app.kubernetes.io/instance: scanservjs
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: scanservjs
|
||||||
|
app.kubernetes.io/component: scanservjs
|
||||||
|
app.kubernetes.io/instance: scanservjs
|
||||||
|
spec:
|
||||||
|
initContainers:
|
||||||
|
- name: init-data
|
||||||
|
image: docker.io/sbs20/scanservjs:v2.26.1
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
command:
|
||||||
|
- cp
|
||||||
|
- -r
|
||||||
|
- /app/data/.
|
||||||
|
- /tmp/data
|
||||||
|
securityContext:
|
||||||
|
runAsNonRoot: true
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
runAsUser: 1000
|
||||||
|
runAsGroup: 1000
|
||||||
|
volumeMounts:
|
||||||
|
- name: scanservjs-datadir
|
||||||
|
mountPath: /tmp/data
|
||||||
|
containers:
|
||||||
|
- name: scanservjs
|
||||||
|
image: docker.io/sbs20/scanservjs:v2.26.1
|
||||||
|
imagePullPolicy: IfNotPresent
|
||||||
|
env:
|
||||||
|
- name: PAPERLESS_TOKEN_FILE
|
||||||
|
value: /run/secrets/paperless.token
|
||||||
|
envFrom:
|
||||||
|
- configMapRef:
|
||||||
|
name: scanservjs
|
||||||
|
optional: true
|
||||||
|
ports:
|
||||||
|
- name: scanservjs
|
||||||
|
containerPort: 8080
|
||||||
|
securityContext:
|
||||||
|
runAsNonRoot: true
|
||||||
|
readOnlyRootFilesystem: true
|
||||||
|
runAsUser: 1000
|
||||||
|
runAsGroup: 1000
|
||||||
|
volumeMounts:
|
||||||
|
- name: scanservjs-datadir
|
||||||
|
mountPath: /app/data
|
||||||
|
- name: scanservjs-configdir
|
||||||
|
mountPath: /app/config
|
||||||
|
- name: scanservjs-config
|
||||||
|
mountPath: /app/config/config.local.js
|
||||||
|
subPath: config.local.js
|
||||||
|
- name: scanservjs-tmp
|
||||||
|
mountPath: /tmp
|
||||||
|
- name: paperless-token
|
||||||
|
mountPath: /run/secrets/paperless.token
|
||||||
|
subPath: paperless.token
|
||||||
|
readOnly: true
|
||||||
|
securityContext:
|
||||||
|
fsGroup: 1000
|
||||||
|
volumes:
|
||||||
|
- name: scanservjs-datadir
|
||||||
|
emptyDir:
|
||||||
|
- name: scanservjs-configdir
|
||||||
|
emptyDir:
|
||||||
|
medium: Memory
|
||||||
|
- name: scanservjs-tmp
|
||||||
|
emptyDir:
|
||||||
|
medium: Memory
|
||||||
|
- name: scanservjs-config
|
||||||
|
configMap:
|
||||||
|
name: scanservjs-config
|
||||||
|
- name: paperless-token
|
||||||
|
secret:
|
||||||
|
secretName: paperless-token
|
||||||
|
optional: true
|
Loading…
Reference in New Issue