feat(n8n): add school paperless intake pipeline

This commit is contained in:
Fizzlepoof
2026-05-13 20:53:00 +00:00
parent a04b2eecd7
commit 3269cc1fc5
11 changed files with 895 additions and 3 deletions

View File

@@ -0,0 +1,111 @@
# School Paperless Intake Workflow
Workflows:
- `n8n-workflows/18-school-paperless-intake.json`
- `n8n-workflows/19-paperless-school-metadata-enrichment.json`
This workflow is meant for a Telegram chat-driven upload pipeline that sends schoolwork into Paperless-NGX while recording intake metadata in shared Postgres.
## What it does
1. Accepts a multipart upload at `POST /webhook/school/intake/upload`
2. Requires form fields:
- `class_name`
- `assignment_name`
- `submission_kind`
3. Accepts optional fields:
- `semester`
- `course_code`
- `paper_kind`
- `notes`
- `source` (defaults to `telegram`)
- `telegram_chat_id`
- `telegram_message_id`
4. Generates a deterministic `intake_id` from date + class + assignment + file checksum
5. Stores/updates the intake row in Postgres first
6. Uploads the file to Paperless using the deterministic intake ID in the filename
7. Marks the row as `uploaded` after Paperless accepts the upload
## Shared Postgres expectations
- Use the shared Postgres network/credentials pattern already used by the automation stack.
- The workflow expects an n8n Postgres credential named `Shared Postgres`.
- Recommended target database: `school_intake`
- Tracked schema: `docs/reference/SCHOOL_INTAKE_POSTGRES_SCHEMA.sql`
## Required environment variables
Add to `automation/.env`:
```env
PAPERLESS_BASE_URL=https://paperless.paccoco.com
PAPERLESS_API_TOKEN=CHANGE_ME
LITELLM_API_KEY=CHANGE_ME
PAPERLESS_CORRESPONDENT_SCHOOL=
PAPERLESS_DOCUMENT_TYPE_SCHOOL=
PAPERLESS_TAG_SCHOOL=
PAPERLESS_TAG_ASSIGNMENTS=
PAPERLESS_TAG_TELEGRAM=
NODE_FUNCTION_ALLOW_BUILTIN=crypto
```
Add to the `n8n` service `environment:` block in `automation/docker-compose.yaml`:
```yaml
PAPERLESS_BASE_URL: ${PAPERLESS_BASE_URL}
PAPERLESS_API_TOKEN: ${PAPERLESS_API_TOKEN}
PAPERLESS_CORRESPONDENT_SCHOOL: ${PAPERLESS_CORRESPONDENT_SCHOOL}
PAPERLESS_DOCUMENT_TYPE_SCHOOL: ${PAPERLESS_DOCUMENT_TYPE_SCHOOL}
PAPERLESS_TAG_SCHOOL: ${PAPERLESS_TAG_SCHOOL}
PAPERLESS_TAG_ASSIGNMENTS: ${PAPERLESS_TAG_ASSIGNMENTS}
PAPERLESS_TAG_TELEGRAM: ${PAPERLESS_TAG_TELEGRAM}
NODE_FUNCTION_ALLOW_BUILTIN: crypto
```
Notes:
- `PAPERLESS_CORRESPONDENT_SCHOOL` and `PAPERLESS_DOCUMENT_TYPE_SCHOOL` should be numeric IDs if you want those fields auto-assigned.
- Leave optional Paperless IDs blank if you prefer Paperless rules to classify later.
- `NODE_FUNCTION_ALLOW_BUILTIN=crypto` is required because the Code node hashes the uploaded file.
- `N8N_BLOCK_ENV_ACCESS_IN_NODE` must remain `false`.
## Paperless metadata behavior
The upload uses:
- filename: `<intake_id>.<ext>`
- title: `<class_name> — <assignment_name> — <submission_kind>`
Workflow 18 deliberately keeps the Paperless upload minimal and safe:
- multipart `document`
- deterministic title
The deterministic filename/title preserve the `intake_id` for later correlation.
Workflow 19 handles the follow-up metadata pass after Paperless finishes processing:
- fetch Paperless document by webhook document id
- resolve `intake_id` from the stored filename
- look up the intake record in shared Postgres
- re-apply deterministic title
- merge generic school tags plus optional class/submission-kind mappings
- optionally set document type / correspondent from JSON env mappings
## Testing
Use the example curl request in:
- `n8n-workflows/fixtures/school-paperless-intake-webhook.curl.example.txt`
Minimum test checklist:
1. Import the workflow into n8n.
2. Attach the `Shared Postgres` credential.
3. Confirm the target DB/table exists.
4. Send the sample multipart request.
5. Verify a row appears in `school_paperless_intake` with `status=uploaded`.
6. Verify the uploaded Paperless document title and stored filename both include the deterministic intake ID.
7. Trigger workflow 19 with the Paperless document id and verify the row moves to `status=enriched`.
8. Re-submit the same file and metadata to confirm the same `intake_id` is reused and the Postgres row is upserted.
## Known assumptions / caveats
- The workflow is repo-only and was not live-tested against the running n8n/Paperless stack.
- The Postgres node uses SQL expressions inline rather than parameter binding because exported n8n node JSON can differ by version.
- If your n8n build blocks `require('crypto')`, add `NODE_FUNCTION_ALLOW_BUILTIN=crypto` to the container env.
- If Paperless needs tags as repeated `tags[]` fields instead of a comma-separated `tags` field, adjust the HTTP Request node after import.