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:
+196
-2
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user