feat(stack): add Paperless-ngx document management system

- Add paperless.yml stack (port 8091, documents.schweitz.net)
- Uses shared postgres (DB: paperless) and redis (DB 8)
- ClamAV installed on host for virus scanning (port 3310)
- Add Paperless integration to library-desk stack
- Update CONTAINERS.md with Paperless and ClamAV profiles
- Add Portainer and NPM API documentation to setup-new-host.md
- Update redis-shared.yml and postgres-shared.yml with Paperless refs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-25 00:36:15 +01:00
co-authored by Claude Opus 4.5
parent 7e06c915ad
commit 765818b4e9
7 changed files with 655 additions and 12 deletions
+196 -2
View File
@@ -259,5 +259,199 @@ If already logged into another `.schweitz.net` service:
---
**Last Updated**: 2025-12-13
**Based on**: library.schweitz.net setup session
## Appendix: Portainer API for Remote Stack Deployment
When you cannot access the Portainer web UI (e.g., outside home network), you can deploy stacks via the API.
### Authentication
```bash
# Get JWT token (valid for 8 hours)
http --ignore-stdin POST http://192.168.86.149:8001/api/auth \
username=admin password=<password>
# Response: {"jwt":"eyJ..."}
```
### List Stacks
```bash
http --ignore-stdin GET http://192.168.86.149:8001/api/stacks \
"Authorization:Bearer <token>"
```
### Read Stack File
```bash
http --ignore-stdin GET "http://192.168.86.149:8001/api/stacks/<stack_id>/file" \
"Authorization:Bearer <token>"
```
### Create Stack
**Endpoint**: `POST /api/stacks/create/standalone/string?endpointId=3`
**Payload format**:
```json
{
"name": "stack-name",
"stackFileContent": "version: '3.8'\nservices:\n ...",
"env": [
{"name": "VAR_NAME", "value": "var_value"},
{"name": "SECRET_KEY", "value": "secret_value"}
]
}
```
**Example deployment script**:
```bash
#!/bin/bash
# Get token
TOKEN=$(http --ignore-stdin POST http://192.168.86.149:8001/api/auth \
username=admin password=<password> | jq -r '.jwt')
# Read stack file and create JSON payload
STACK_CONTENT=$(cat /path/to/stack.yml)
jq -n \
--arg name "my-stack" \
--arg content "$STACK_CONTENT" \
'{
name: $name,
stackFileContent: $content,
env: [
{name: "DB_PASSWORD", value: "secret123"},
{name: "API_KEY", value: "key456"}
]
}' > /tmp/payload.json
# Deploy
curl -s -X POST "http://192.168.86.149:8001/api/stacks/create/standalone/string?endpointId=3" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d @/tmp/payload.json
```
### Update Stack
```bash
curl -s -X PUT "http://192.168.86.149:8001/api/stacks/<stack_id>?endpointId=3" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d @/tmp/payload.json
```
### Delete Stack
```bash
http --ignore-stdin DELETE "http://192.168.86.149:8001/api/stacks/<stack_id>?endpointId=3" \
"Authorization:Bearer <token>"
```
### Key Notes
- **Endpoint ID**: Use `3` for the local Docker environment (verify with `GET /api/endpoints`)
- **Stack Type**: Use `standalone/string` for Docker Compose stacks (not Swarm)
- **Environment Variables**: Passed as array of `{name, value}` objects, referenced in compose as `${VAR_NAME}`
- **Token Expiry**: JWT tokens expire after 8 hours; re-authenticate if needed
---
## Appendix: NPM API for Remote Proxy Configuration
When you cannot access the NPM web UI, you can configure proxy hosts via the API (port 81).
### Authentication
```bash
http --ignore-stdin POST http://192.168.86.149:81/api/tokens \
identity=<email> secret=<password>
# Response: {"token":"eyJ...", "expires":"..."}
```
### List Proxy Hosts
```bash
http --ignore-stdin GET http://192.168.86.149:81/api/nginx/proxy-hosts \
"Authorization:Bearer <token>"
```
### Create Proxy Host (without SSL)
```bash
curl -s -X POST http://192.168.86.149:81/api/nginx/proxy-hosts \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"domain_names": ["subdomain.schweitz.net"],
"forward_scheme": "http",
"forward_host": "192.168.86.149",
"forward_port": 8091,
"access_list_id": 0,
"certificate_id": 0,
"ssl_forced": false,
"caching_enabled": false,
"block_exploits": true,
"advanced_config": "",
"allow_websocket_upgrade": true,
"http2_support": false,
"hsts_enabled": false,
"hsts_subdomains": false,
"enabled": true,
"locations": []
}'
```
### Request Let's Encrypt Certificate
```bash
curl -s -X POST http://192.168.86.149:81/api/nginx/certificates \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"domain_names": ["subdomain.schweitz.net"],
"meta": {"dns_challenge": false},
"provider": "letsencrypt"
}'
# Response includes certificate ID
```
### Update Proxy Host with SSL
```bash
curl -s -X PUT http://192.168.86.149:81/api/nginx/proxy-hosts/<proxy_id> \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"domain_names": ["subdomain.schweitz.net"],
"forward_scheme": "http",
"forward_host": "192.168.86.149",
"forward_port": 8091,
"access_list_id": 0,
"certificate_id": <cert_id>,
"ssl_forced": true,
"caching_enabled": false,
"block_exploits": true,
"advanced_config": "",
"allow_websocket_upgrade": true,
"http2_support": true,
"hsts_enabled": true,
"hsts_subdomains": false,
"enabled": true,
"locations": []
}'
```
### Key Notes
- **API Port**: NPM API is on port 81, not 80/443
- **Token Expiry**: Tokens expire after 1 day
- **SSL Flow**: Create proxy host → Request certificate → Update proxy host with certificate_id
- **forward_host**: Use container name (if on same network) or host IP
- **Verify**: Check `meta.nginx_online: true` in response
---
**Last Updated**: 2025-12-24
**Based on**: library.schweitz.net setup session, Paperless-ngx API deployment