Skip to content

feat(integrations): add Paperless-ngx external attachment integration#1491

Closed
szaiser wants to merge 1 commit into
sysadminsmedia:mainfrom
szaiser:feat/paperless-ngx-integration
Closed

feat(integrations): add Paperless-ngx external attachment integration#1491
szaiser wants to merge 1 commit into
sysadminsmedia:mainfrom
szaiser:feat/paperless-ngx-integration

Conversation

@szaiser
Copy link
Copy Markdown
Contributor

@szaiser szaiser commented May 12, 2026

What type of PR is this?

  • feature

What this PR does / why we need it:

Extends the external attachment infrastructure from #1481 with a full Paperless-ngx integration. Users can link Paperless documents to Homebox inventory items — no file copy, just a lightweight external reference — and get rich metadata directly in the attachment list.

Changes by file

Backend

  • backend/app/api/handlers/v1/v1_ctrl_integration_proxy.go (new) — generic authenticated reverse-proxy GET /v1/integrations/{name}/proxy?path={relPath}. Validates the integration name (regex ^[a-z][a-z0-9_-]{0,31}$ — prevents traversal), validates the path starts with / and contains no ://, then proxies the request to the service's configured base URL with Authorization: Token {token}. One handler serves all current and future integrations.
  • backend/app/api/routes.go — wire the new proxy handler.
  • backend/internal/data/repo/repo_item_attachments.go — add MimeTypePaperlessDocument = "paperless/document" constant and register it in MimeTypeForSourceType() / externalLinkMimeTypes.
  • backend/internal/core/services/service_items_attachments_external_test.go — extend existing service tests to cover Paperless MIME type registration.
  • backend/internal/data/repo/repo_item_attachments_test.go — extend existing repo tests.

Frontend

  • frontend/lib/integration-adapters.ts (new)ServiceAdapter interface and SERVICE_ADAPTERS registry (name, MIME type, settings keys, ID extractor, title builder). Adding a future integration only requires a new entry here; all detection, classification and hydration are registry-driven.
  • frontend/lib/integration-adapters.test.ts (new) — 25 Vitest unit tests covering extractPaperlessDocId, detectServiceFromUrl, classifyDroppedUrl, getAdapter, getAdapterByMimeType, buildTitle and registry shape.
  • frontend/stores/integration-cache.ts (new) — Pinia store holding per-service URL state and enriched-data / fetch-state caches. Drives reactive card promotion/demotion when the user saves or clears a service URL in Profile settings while the attachment list is mounted.
  • frontend/components/Item/AttachmentsList.vue — Paperless document card with: thumbnail, title (from Paperless API), correspondent, document type, colour-coded tags, page count, created date, open-in-Paperless button, and an amber ⚠ unreachable/error badge with tooltip. link/url attachments whose URL matches the configured Paperless base URL are transparently promoted to the rich card at render time (and demoted back if the URL is cleared).
  • frontend/pages/item/[id]/index/edit.vue — drag-and-drop Paperless URL detection: detects a dropped Paperless document URL, extracts the document ID and calls POST /v1/entities/{id}/attachments/external.
  • frontend/pages/profile.vue — Paperless URL + API token fields in the Integrations settings section; saves to user settings and immediately pushes the new URL into the shared store.
  • frontend/composables/preferences-utils.ts (new) — shared preference helpers extracted from use-preferences.ts (no functional change, pure refactor to avoid circular imports).
  • frontend/composables/use-preferences.test.ts (new) — tests for the extracted composable.
  • frontend/locales/en.json — add Paperless i18n keys (paperless_settings, paperless_url, paperless_url_placeholder, paperless_token, paperless_token_placeholder, integrations_sub).

Special notes for your reviewer:

  • The generic proxy handler (v1_ctrl_integration_proxy.go) is intentionally Paperless-unaware. It reads {name}_url and {name}_token from user settings and forwards with Authorization: Token {token}. Adding a future integration (e.g. Docspell) requires only a new SERVICE_ADAPTERS entry on the frontend and one line in MimeTypeForSourceType() on the backend.
  • The adapter registry pattern (integration-adapters.ts) is the single source of truth: drop detection, URL classification and card hydration all iterate SERVICE_ADAPTERS — no service-specific if/else chains.
  • Credentials are never exposed to the browser: the proxy reads them server-side and forwards them; only the URL is visible to the frontend.
  • Object URLs for thumbnails are created and revoked in onBeforeUnmount to avoid memory leaks.

Testing

  • Unit tests: frontend/lib/integration-adapters.test.ts — 25 tests, all green (npx vitest run lib/integration-adapters.test.ts).
  • Manual: tested against a live Paperless-ngx v2.x instance — drag-and-drop, card rendering (thumbnail, tags, correspondent), error badge when token is wrong, graceful demotion when URL is cleared.

Link Paperless-ngx documents to Homebox inventory items via drag-and-drop
or manual URL entry. Documents are stored as lightweight external links
(no file copy) and enriched on-demand via the existing generic proxy.

## What changes

### Backend
- New handler: `GET /v1/integrations/{name}/proxy` — generic authenticated
  reverse-proxy that forwards requests to any configured integration service.
  Validates the integration name (regex), the path parameter (must start with
  `/`, no `://`), and forwards `Authorization: Token {token}`.
- `POST /v1/entities/{id}/attachments/external` handler now accepts
  `source_type=paperless` (or any future integration registered in the repo).
- `MimeTypePaperlessDocument = "paperless/document"` constant and
  `MimeTypeForSourceType()` helper added to the attachment repo.

### Frontend
- `frontend/lib/integration-adapters.ts` — registry of `ServiceAdapter`
  objects (name, MIME type, settings keys, ID extractor, title builder). Adding
  a new integration only requires a new entry here.
- `frontend/stores/integration-cache.ts` — Pinia store that holds per-service
  URL state and enriched-data/fetch-state caches; drives reactive card
  promotion/demotion when the user saves or clears a service URL.
- `frontend/components/Item/AttachmentsList.vue` — Paperless document card
  with thumbnail, title, correspondent, document type, colour-tagged tags, page
  count, created date, open-in-Paperless button and unreachable/error badge.
  Attachments stored as `link/url` are transparently promoted to the rich
  Paperless card at render time when the service URL is configured.
- Drag-and-drop Paperless URL detection in the item edit page.
- Profile settings: Paperless URL + API token fields with live store update.
- `frontend/composables/preferences-utils.ts` — extracted shared preference
  helpers (no functional change, pure refactor).
- 25 unit tests in `integration-adapters.test.ts` covering ID extraction,
  service detection, URL classification, adapter lookup and registry shape.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 12, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 84dd3d46-092c-4d70-af8c-962e75e6e398

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@tankerkiller125
Copy link
Copy Markdown
Contributor

Closing as #1492 seems to be the PR that actually matters.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants