298 lines
10 KiB
YAML
298 lines
10 KiB
YAML
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: valkey
|
|
namespace: paperless-ngx
|
|
spec:
|
|
replicas: 1
|
|
strategy:
|
|
type: Recreate
|
|
selector:
|
|
matchLabels: {app: valkey}
|
|
template:
|
|
metadata:
|
|
labels: {app: valkey}
|
|
spec:
|
|
automountServiceAccountToken: false
|
|
containers:
|
|
- name: valkey
|
|
image: valkey/valkey:9.0.6
|
|
args: ["--save", "", "--appendonly", "no"]
|
|
ports: [{name: redis, containerPort: 6379}]
|
|
readinessProbe:
|
|
exec: {command: [valkey-cli, ping]}
|
|
periodSeconds: 5
|
|
livenessProbe:
|
|
exec: {command: [valkey-cli, ping]}
|
|
periodSeconds: 20
|
|
resources:
|
|
requests: {cpu: 25m, memory: 64Mi}
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: valkey
|
|
namespace: paperless-ngx
|
|
spec:
|
|
selector: {app: valkey}
|
|
ports: [{name: redis, port: 6379, targetPort: redis}]
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: paperless
|
|
namespace: paperless-ngx
|
|
spec:
|
|
replicas: 1
|
|
strategy:
|
|
type: Recreate
|
|
selector:
|
|
matchLabels: {app: paperless}
|
|
template:
|
|
metadata:
|
|
labels: {app: paperless}
|
|
spec:
|
|
automountServiceAccountToken: false
|
|
enableServiceLinks: false
|
|
tolerations:
|
|
- key: node-role.kubernetes.io/control-plane
|
|
operator: Exists
|
|
effect: NoSchedule
|
|
affinity:
|
|
nodeAffinity:
|
|
preferredDuringSchedulingIgnoredDuringExecution:
|
|
- weight: 100
|
|
preference:
|
|
matchExpressions:
|
|
- key: kubernetes.io/hostname
|
|
operator: In
|
|
values: [arschrock]
|
|
containers:
|
|
- name: paperless
|
|
image: ghcr.io/paperless-ngx/paperless-ngx:3.2.1
|
|
ports: [{name: http, containerPort: 8000}]
|
|
env:
|
|
- {name: PAPERLESS_URL, value: "https://paper.brunner.ninja"}
|
|
- {name: PAPERLESS_REDIS, value: "redis://valkey:6379"}
|
|
- {name: PAPERLESS_DBENGINE, value: postgresql}
|
|
- {name: PAPERLESS_DBHOST, value: postgresql}
|
|
- {name: PAPERLESS_DBNAME, value: paperless}
|
|
- {name: PAPERLESS_DBUSER, value: paperless}
|
|
- name: PAPERLESS_DBPASS
|
|
valueFrom:
|
|
secretKeyRef: {name: paperless-postgres, key: USERDB_PASSWORD}
|
|
- {name: PAPERLESS_TIME_ZONE, value: "Europe/Vienna"}
|
|
- {name: PAPERLESS_OCR_LANGUAGE, value: "deu+eng"}
|
|
- {name: PAPERLESS_TASK_WORKERS, value: "24"}
|
|
- {name: PAPERLESS_THREADS_PER_WORKER, value: "1"}
|
|
- {name: PAPERLESS_CONSUMER_POLLING_INTERVAL, value: "30"}
|
|
- {name: PAPERLESS_APPS, value: "allauth.socialaccount.providers.openid_connect"}
|
|
- {name: PAPERLESS_SOCIAL_AUTO_SIGNUP, value: "true"}
|
|
- {name: PAPERLESS_SOCIAL_ACCOUNT_SYNC_GROUPS, value: "true"}
|
|
- {name: PAPERLESS_SOCIAL_ACCOUNT_SYNC_SUPERUSER_GROUP, value: "Paperless Admins"}
|
|
- name: PAPERLESS_SOCIALACCOUNT_PROVIDERS
|
|
valueFrom:
|
|
secretKeyRef: {name: paperless-oidc, key: providers}
|
|
- name: PAPERLESS_SECRET_KEY
|
|
valueFrom:
|
|
secretKeyRef: {name: paperless-secret-key, key: secret-key}
|
|
volumeMounts:
|
|
- {name: runtime, mountPath: /usr/src/paperless/data}
|
|
- {name: data, mountPath: /usr/src/paperless/media, subPath: media}
|
|
- {name: data, mountPath: /usr/src/paperless/consume, subPath: consume}
|
|
- {name: data, mountPath: /usr/src/paperless/export, subPath: export}
|
|
startupProbe:
|
|
httpGet: {path: /, port: http}
|
|
periodSeconds: 10
|
|
failureThreshold: 60
|
|
readinessProbe:
|
|
httpGet: {path: /, port: http}
|
|
periodSeconds: 10
|
|
livenessProbe:
|
|
httpGet: {path: /, port: http}
|
|
periodSeconds: 30
|
|
resources:
|
|
requests: {cpu: 100m, memory: 1Gi}
|
|
volumes:
|
|
- name: runtime
|
|
persistentVolumeClaim: {claimName: paperless-runtime}
|
|
- name: data
|
|
persistentVolumeClaim: {claimName: paperless-data}
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: paperless
|
|
namespace: paperless-ngx
|
|
spec:
|
|
selector: {app: paperless}
|
|
ports: [{name: http, port: 8000, targetPort: http}]
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: Ingress
|
|
metadata:
|
|
name: paperless
|
|
namespace: paperless-ngx
|
|
annotations:
|
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
spec:
|
|
ingressClassName: traefik
|
|
rules:
|
|
- host: paper.brunner.ninja
|
|
http:
|
|
paths:
|
|
- path: /
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: paperless
|
|
port: {name: http}
|
|
---
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: scanner-config
|
|
namespace: paperless-ngx
|
|
data:
|
|
config.local.js: |
|
|
const fs = require('node:fs/promises');
|
|
const path = require('node:path');
|
|
const crypto = require('node:crypto');
|
|
|
|
const output = process.env.SCAN_OUTPUT_DIR || '/var/lib/scanservjs/output';
|
|
const consume = process.env.PAPERLESS_CONSUME_DIR || '/paperless-consume';
|
|
|
|
module.exports = {
|
|
actions: [{
|
|
name: 'Send to Paperless',
|
|
async execute(fileInfo) {
|
|
const source = await fs.realpath(fileInfo.fullname);
|
|
const images = new Set(['.jpg', '.jpeg', '.png', '.tif', '.tiff', '.gif', '.webp']);
|
|
const extension = path.extname(source).toLowerCase();
|
|
if (!source.startsWith(output + '/') || !(['.pdf', '.zip'].includes(extension) || images.has(extension))) {
|
|
throw new Error('Only staged PDFs, images, and image ZIPs can be sent');
|
|
}
|
|
let upload = source;
|
|
let temp;
|
|
let name = path.basename(source);
|
|
let pending;
|
|
try {
|
|
if (extension === '.zip') {
|
|
const AdmZip = require(require.resolve('adm-zip', {paths: ['/usr/lib/scanservjs']}));
|
|
const entries = new AdmZip(source).getEntries();
|
|
if (entries.length === 0 || entries.some(entry =>
|
|
entry.isDirectory || entry.entryName !== path.basename(entry.entryName) ||
|
|
!images.has(path.extname(entry.entryName).toLowerCase()))) {
|
|
throw new Error('ZIP must contain only scanned image pages');
|
|
}
|
|
temp = await fs.mkdtemp('/tmp/paperless-scan-');
|
|
const inputs = [];
|
|
for (const [index, entry] of entries.entries()) {
|
|
const input = path.join(temp, `${String(index).padStart(4, '0')}${path.extname(entry.entryName)}`);
|
|
await fs.writeFile(input, entry.getData());
|
|
inputs.push(input);
|
|
}
|
|
upload = path.join(temp, 'merged.pdf');
|
|
const {execFile} = require('node:child_process');
|
|
const {promisify} = require('node:util');
|
|
await promisify(execFile)('convert', [...inputs, upload]);
|
|
name = path.basename(source, path.extname(source)) + '.pdf';
|
|
}
|
|
const destination = path.join(consume, `${Date.now()}-${crypto.randomBytes(6).toString('hex')}-${name}`);
|
|
pending = destination + '.part';
|
|
await fs.copyFile(upload, pending);
|
|
await fs.rename(pending, destination);
|
|
await fs.unlink(source);
|
|
} catch (error) {
|
|
if (pending) await fs.rm(pending, {force: true});
|
|
throw error;
|
|
} finally {
|
|
if (temp) await fs.rm(temp, {recursive: true, force: true});
|
|
}
|
|
},
|
|
}],
|
|
};
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: scanner
|
|
namespace: paperless-ngx
|
|
spec:
|
|
replicas: 1
|
|
strategy:
|
|
type: Recreate
|
|
selector:
|
|
matchLabels: {app: scanner}
|
|
template:
|
|
metadata:
|
|
labels: {app: scanner}
|
|
spec:
|
|
automountServiceAccountToken: false
|
|
securityContext:
|
|
supplementalGroups: [1000]
|
|
initContainers:
|
|
- name: prepare-output
|
|
image: busybox:1.37.0
|
|
command: [sh, -c, 'mkdir -p /volume/scanner-staging && chown 102:100 /volume/scanner-staging && chmod 775 /volume/scanner-staging && chmod g+w /volume/consume']
|
|
volumeMounts:
|
|
- {name: data, mountPath: /volume}
|
|
containers:
|
|
- name: scanner
|
|
image: sbs20/scanservjs:3.3.0
|
|
command: [sh, -c, "sed -i '/^\\[options\\]/a discovery = disable' /etc/sane.d/airscan.conf && exec /entrypoint.sh"]
|
|
env:
|
|
- {name: AIRSCAN_DEVICES, value: '"HP OfficeJet Pro 8020" = http://192.168.4.189:8080/eSCL, eSCL'}
|
|
ports: [{name: http, containerPort: 8080}]
|
|
volumeMounts:
|
|
- {name: data, mountPath: /var/lib/scanservjs/output, subPath: scanner-staging}
|
|
- {name: data, mountPath: /paperless-consume, subPath: consume}
|
|
- {name: config, mountPath: /etc/scanservjs/config.local.js, subPath: config.local.js, readOnly: true}
|
|
startupProbe:
|
|
httpGet: {path: /, port: http}
|
|
periodSeconds: 5
|
|
failureThreshold: 60
|
|
readinessProbe:
|
|
httpGet: {path: /, port: http}
|
|
periodSeconds: 10
|
|
livenessProbe:
|
|
httpGet: {path: /, port: http}
|
|
periodSeconds: 30
|
|
resources:
|
|
requests: {cpu: 100m, memory: 256Mi}
|
|
volumes:
|
|
- name: data
|
|
persistentVolumeClaim: {claimName: paperless-data}
|
|
- name: config
|
|
configMap: {name: scanner-config}
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: scanner
|
|
namespace: paperless-ngx
|
|
spec:
|
|
selector: {app: scanner}
|
|
ports: [{name: http, port: 8080, targetPort: http}]
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: Ingress
|
|
metadata:
|
|
name: scanner
|
|
namespace: paperless-ngx
|
|
annotations:
|
|
traefik.ingress.kubernetes.io/router.entrypoints: websecure
|
|
traefik.ingress.kubernetes.io/router.middlewares: default-authentik@kubernetescrd
|
|
spec:
|
|
ingressClassName: traefik
|
|
rules:
|
|
- host: scanner.brunner.ninja
|
|
http:
|
|
paths:
|
|
- path: /
|
|
pathType: Prefix
|
|
backend:
|
|
service:
|
|
name: scanner
|
|
port: {name: http}
|