Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ jobs:
path: TestResults/
if-no-files-found: warn

# PaperlessUI.Blazor is currently a scaffold-only project (no
# Paperless code). Once a real Paperless page is implemented,
# re-add it to Paperless.slnx and reintroduce the build step here.
# PaperlessUI.Blazor is in Paperless.slnx, so it's compiled as part
# of the NUKE build above — no separate step needed. Its Dockerfile +
# paperless-blazor compose service serve it behind nginx at /.

frontend-angular:
name: Build (PaperlessUI.Angular)
Expand Down
17 changes: 10 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Paperless — repo guide for Claude

Document management with OCR, AI summarization, and full-text search. .NET 10 backend. Frontend story: React is the canonical/priority implementation, Angular is a parallel implementation kept for comparison, Blazor is currently a WIP scaffold. The production demo UI today is the vanilla SPA at `PaperlessREST/wwwroot/` which nginx serves from `compose.yaml`.
Document management with OCR, AI summarization, and full-text search. .NET 10 backend. Frontend story: the production demo UI served by `compose.yaml`/nginx (at `/`) is **PaperlessUI.Blazor** — an Interactive-Server Blazor port of the original `PaperlessREST/wwwroot/` SPA, which it replaced at the nginx root (drag-drop upload, live OCR + AI-summary over SSE, SignalR circuit). React is the canonical/priority frontend implementation and Angular a parallel one kept for stack comparison; both consume the same `/api/*` backend and are built/validated in CI but are not the deployed compose UI. The `wwwroot/` SPA still ships (served by the REST app's `UseStaticFiles`) as the original Blazor ported from.

This file is the on-disk source of truth for working in this repo. Read it before touching anything.

Expand All @@ -13,20 +13,21 @@ This file is the on-disk source of truth for working in this repo. Read it befor
```
Paperless.slnx # modern slnx; flat — NUKE 10 doesn't traverse <Folder> wrappers
├── PaperlessREST/ # ASP.NET Core API (REST + SSE)
│ ├── wwwroot/ # Vanilla Bootstrap SPA — production demo UI mounted by nginx
│ ├── wwwroot/ # Original vanilla Bootstrap SPA (still served via UseStaticFiles); Blazor is the port that took over nginx /
│ └── sample-data/ # XML batch fixtures (input/archive/error), mounted by compose
├── PaperlessServices/ # BackgroundService worker (OCR + GenAI)
├── PaperlessREST.Tests/ # xUnit v3 + Testcontainers
├── PaperlessServices.Tests/ # xUnit v3 + Testcontainers
├── Paperless.TestSupport/ # Shared test lib — ContainerFixtureBase (template-method) + builders + TestPdf + AsyncCleanup
├── PaperlessUI.Blazor/ # Blazor Web App (Interactive Server) — production demo UI behind nginx /; in slnx, compiled by backend CI, has Dockerfile + paperless-blazor compose service
├── PaperlessUI.React/ # Vite + React 19 + TS (canonical frontend) — PaperlessUI.React.esproj
├── PaperlessUI.Angular/ # Angular 21 + pnpm (parallel implementation) — PaperlessUI.Angular.esproj
├── PaperlessUI.Blazor/ # Blazor Web App scaffold — WIP, NOT in Paperless.slnx, NOT built by CI
├── Pipeline/ # NUKE build (Build.csproj)
├── docker/, compose.yaml
└── docs/99_Reference/Rating-Matrix/ # course grading rubric (PDF + xlsx)
```

React + Angular share the backend so the same use-cases can be compared across stacks. Blazor is on-disk but not building until a Paperless page is implemented (add back to Paperless.slnx + ci.yml when it does).
React + Angular share the backend so the same use-cases can be compared across stacks. Blazor is the deployed demo UI (a vanilla port of the wwwroot SPA): it's in `Paperless.slnx`, compiled by the backend CI job, and its Dockerfile + `paperless-blazor` compose service sit behind nginx at `/`.

## Build & test

Expand All @@ -43,12 +44,12 @@ NUKE-based, single entry point. Targets compose via `Pipeline/Components/*.cs` (
The `ReportCoverage` gate is report-only (thresholds 0/0). CI publishes the markdown
summary and Codecov diff; regressions surface in PR review, not as a hard build fail.

UI projects build via their respective toolchains, never via NUKE:
The React/Angular UIs are `esproj` and build via their pnpm toolchains, never via NUKE. Blazor is a `.csproj` in the slnx, so `./build.sh Compile` builds it like any backend project:

```bash
cd PaperlessUI.React && pnpm install --frozen-lockfile && pnpm dev # canonical
cd PaperlessUI.Angular && pnpm install --frozen-lockfile && pnpm start # parallel impl
# Blazor scaffold: dotnet run --project PaperlessUI.Blazor (WIP, not in slnx)
dotnet run --project PaperlessUI.Blazor # deployed demo UI (also compiled by ./build.sh Compile)
```

## CI
Expand All @@ -61,6 +62,8 @@ cd PaperlessUI.Angular && pnpm install --frozen-lockfile && pnpm start # par
| `Build (PaperlessUI.Angular)` | non-blocking | `pnpm install --frozen-lockfile && pnpm run build` (ng's default config is production — do NOT pass `--configuration production` after `--`; pnpm 10 passes `--` literally to scripts) |
| `Build (PaperlessUI.React)` | non-blocking | `pnpm install --frozen-lockfile && pnpm run build` |

`PaperlessUI.Blazor` is a `.csproj` in the slnx, so the backend job compiles it as part of the NUKE build — there is no separate Blazor CI job; its Dockerfile + `paperless-blazor` compose service serve it behind nginx at `/`.

The workflow declares `concurrency:` (cancel-in-progress on PRs only) and least-privilege `permissions:` (read defaults; codecov/check annotations escalate as needed).

Coverage uploads to https://codecov.io/gh/ANcpLua/Paperless via tokenless OIDC. `codecov.yml` ignores host entry points, EF migrations, the pipeline, and test projects so the score reflects production surface only.
Expand Down Expand Up @@ -107,7 +110,7 @@ Anti-patterns that fail the self-check:
| Category | Where |
|---|---|
| Use Cases / REST API | `PaperlessREST/Features/DocumentManagement/Presentation/Endpoints/` |
| Web Frontend | React (`PaperlessUI.React/`, canonical) + Angular (`PaperlessUI.Angular/`, parallel impl) + the vanilla SPA at `PaperlessREST/wwwroot/` that nginx serves in production. Blazor (`PaperlessUI.Blazor/`) is a WIP scaffold, not currently built. |
| Web Frontend | **Blazor** (`PaperlessUI.Blazor/`) — the production demo UI nginx serves at `/` (Interactive-Server port of the wwwroot SPA). React (`PaperlessUI.React/`, canonical) + Angular (`PaperlessUI.Angular/`, parallel impl) share the backend for stack comparison. The original `PaperlessREST/wwwroot/` SPA still ships via `UseStaticFiles`. |
| Queues | `SWEN3.Paperless.RabbitMq` consumed by REST + Services |
| Logging | `Microsoft.Extensions.Logging`; `FakeLogger` in tests |
| Validation | Mapster + DataAnnotations + FluentValidation at the boundary |
Expand Down
27 changes: 14 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Paperless

**Document management with OCR, AI summarization, and full-text search.**
.NET 10 backend · React + Angular frontends (Blazor scaffold WIP) · NUKE + xUnit v3 + Testcontainers.
.NET 10 backend · Blazor demo UI (behind nginx) + React/Angular frontends · NUKE + xUnit v3 + Testcontainers.

<a href="https://github.com/ANcpLua/Paperless/actions/workflows/ci.yml">
<img src="https://github.com/ANcpLua/Paperless/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI">
Expand All @@ -24,20 +24,21 @@ Paperless.slnx # MSBuild slnx (modern format)
├── PaperlessServices/ # Background worker (OCR + GenAI)
├── PaperlessREST.Tests/ # xUnit v3 (unit + integration via Testcontainers)
├── PaperlessServices.Tests/ # xUnit v3 (unit + integration)
├── PaperlessREST/wwwroot/ # Vanilla Bootstrap SPA — production demo UI served by nginx
├── Paperless.TestSupport/ # Shared test lib (ContainerFixtureBase + builders + TestPdf + AsyncCleanup)
├── PaperlessREST/wwwroot/ # Original vanilla Bootstrap SPA (served via UseStaticFiles); Blazor took over nginx /
├── PaperlessUI.Blazor/ # Blazor Web App (Interactive Server) — production demo UI behind nginx /; in slnx, built by CI
│ └── PaperlessUI.Blazor.csproj
├── PaperlessUI.React/ # Frontend variant — Vite 8 + React 19 + TypeScript (canonical)
│ └── PaperlessUI.React.esproj
├── PaperlessUI.Angular/ # Frontend variant — Angular 21 + pnpm (parallel impl)
│ └── PaperlessUI.Angular.esproj
├── PaperlessUI.Blazor/ # Frontend variant — Blazor Web App scaffold (WIP, not in slnx, not built by CI)
│ └── PaperlessUI.Blazor.csproj
├── Pipeline/ # NUKE build (`./build.sh <Target>`)
├── docker/ # nginx config (single file: docker/nginx.conf)
├── PaperlessREST/sample-data/ # XML batch fixtures (input/archive/error), mounted by compose
└── compose.yaml # Local stack (postgres, minio, rabbitmq, elastic)
```

The production demo today is the vanilla SPA at `PaperlessREST/wwwroot/` (mounted by nginx in `compose.yaml`). React and Angular are parallel SDK-stack implementations consuming the same `/api/*` surface; Blazor is on-disk but not currently building.
The production demo UI is **PaperlessUI.Blazor** (Interactive Server), which nginx serves at `/` in `compose.yaml` — a vanilla-Blazor port of the original `PaperlessREST/wwwroot/` SPA that it replaced at the root. React and Angular are parallel SDK-stack implementations consuming the same `/api/*` surface (built/validated in CI, not deployed in compose). The `wwwroot/` SPA still ships via `UseStaticFiles`.

Contributor & agent conventions live in [`AGENTS.md`](AGENTS.md) (`CLAUDE.md` is a symlink to it).

Expand All @@ -63,8 +64,8 @@ cd PaperlessUI.React && pnpm install --frozen-lockfile && pnpm dev
# Angular — parallel implementation
cd PaperlessUI.Angular && pnpm install --frozen-lockfile && pnpm start

# Blazor scaffold (WIP, not currently in Paperless.slnx)
# dotnet run --project PaperlessUI.Blazor
# Blazor — deployed demo UI (in slnx; also compiled by ./build.sh Compile)
dotnet run --project PaperlessUI.Blazor
```

## Architecture
Expand All @@ -73,12 +74,12 @@ cd PaperlessUI.Angular && pnpm install --frozen-lockfile && pnpm start
%%{init: {'theme':'dark'}}%%
flowchart LR
subgraph Clients
wwwroot[PaperlessREST/wwwroot<br/>vanilla SPAproduction]
Blazor[PaperlessUI.Blazor<br/>demo UInginx /]
React[PaperlessUI.React<br/>canonical]
Angular[PaperlessUI.Angular<br/>parallel impl]
Blazor[PaperlessUI.Blazor<br/>WIP scaffold]
wwwroot[PaperlessREST/wwwroot<br/>original SPA — UseStaticFiles]
end
wwwroot & React & Angular & Blazor -->|HTTPS / SSE| REST[PaperlessREST<br/>ASP.NET Core]
Blazor & React & Angular & wwwroot -->|HTTPS / SSE| REST[PaperlessREST<br/>ASP.NET Core]
REST -->|EF Core| PG[(PostgreSQL)]
REST -->|S3| MIN[(MinIO)]
REST -->|HTTP| ES[(Elasticsearch)]
Expand All @@ -92,7 +93,7 @@ flowchart LR

## CI + Coverage

`Build & Test` (gate): backend unit + integration + coverage gate + Codecov upload.
`Build & Test` (gate): backend unit + integration + coverage gate + Codecov upload (also compiles `PaperlessUI.Blazor`, which is in the slnx).
Two non-gating jobs build the Angular and React apps via `pnpm`.

Coverage uploads to https://codecov.io/gh/ANcpLua/Paperless via tokenless OIDC.
Expand All @@ -105,7 +106,7 @@ The course rubric in [`docs/99_Reference/Rating-Matrix/`](docs/99_Reference/Rati
| Category | Where it lives |
|---|---|
| **Use Cases / REST API** | `PaperlessREST/Features/DocumentManagement/Presentation/Endpoints/DocumentEndpoints.cs` |
| **Web Frontend** | `PaperlessREST/wwwroot/` (vanilla SPA, production demo) + `PaperlessUI.React/` (canonical) + `PaperlessUI.Angular/` (parallel impl). `PaperlessUI.Blazor/` is a WIP scaffold, currently out of build. |
| **Web Frontend** | `PaperlessUI.Blazor/` (Interactive Server) — the production demo UI nginx serves at `/` + `PaperlessUI.React/` (canonical) + `PaperlessUI.Angular/` (parallel impl). The original `PaperlessREST/wwwroot/` SPA still ships via `UseStaticFiles`. |
| **Queues** | `SWEN3.Paperless.RabbitMq` package consumed by REST + Services |
| **Logging** | `Microsoft.Extensions.Logging` everywhere; `FakeLogger` in tests |
| **Validation** | Mapster + DataAnnotations + FluentValidation at the boundary |
Expand All @@ -125,7 +126,7 @@ The course rubric in [`docs/99_Reference/Rating-Matrix/`](docs/99_Reference/Rati

| Backend | Frontends | Infra |
|---|---|---|
| .NET 10, ASP.NET Core, EF Core 10.0.x, Mapster, ErrorOr, Hangfire 1.8.23, Polly | React 19.2 + Vite 8 + TypeScript 6 (canonical), Angular 21 + pnpm 10 (parallel), Blazor Web App scaffold (WIP) | PostgreSQL 17, RabbitMQ 4.3, MinIO (date-pinned), Elasticsearch 9.1, nginx |
| .NET 10, ASP.NET Core, EF Core 10.0.x, Mapster, ErrorOr, Hangfire 1.8.23, Polly | Blazor Web App / Interactive Server (deployed demo UI), React 19.2 + Vite 8 + TypeScript 6 (canonical), Angular 21 + pnpm 10 (parallel) | PostgreSQL 17, RabbitMQ 4.3, MinIO (date-pinned), Elasticsearch 9.1, nginx |
| xUnit v3.2.x, MTP v2, Testcontainers, AwesomeAssertions, Moq | – | OrbStack / Docker Compose |

(Exact pin values live in `Version.props` and `Directory.Packages.props` — the table is commentary.)
Loading
Loading