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