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
22 changes: 19 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ Prefer the smallest coherent change that fixes demonstrated behavior. Avoid spec

## Repository map

- `backend/src/MathArchive.Domain`: document entity and domain enum.
- `backend/src/MathArchive.Application`: document use cases, DTOs, validation, and storage/repository abstractions.
- `backend/src/MathArchive.Domain`: document and analytics entities and enums.
- `backend/src/MathArchive.Application`: document, storage audit, and analytics use cases, DTOs, validation, and storage/repository abstractions.
- `backend/src/MathArchive.Infrastructure`: EF Core/PostgreSQL repository, migrations, local file storage, authentication implementations, and development seed data.
- `backend/src/MathArchive.Api`: ASP.NET Core controllers, DI composition, ProblemDetails handling, health checks, and runtime configuration.
- `backend/tests/MathArchive.Application.Tests`: xUnit unit, API, health, storage, and PostgreSQL integration tests.
Expand Down Expand Up @@ -101,12 +101,22 @@ When changing document behavior, verify both sides agree on:
- string `DocumentType` values;
- `DocumentDto` fields and date/number shapes;
- multipart create/update field names and optional replacement file;
- `search`, `grade`, `generalOnly`, `topic`, `documentType`, `page`, and `pageSize` parameters;
- `search`, `grade`, `generalOnly`, `topic`, `documentType`, `createdFrom`, `createdTo`, `sort`, `page`, and `pageSize` parameters;
- `PagedResult` fields and infinite-page progression;
- content types, filenames, preview/download semantics, and ProblemDetails status codes.

Avoid relying on a new undocumented assumption on only one side of the contract. Update focused backend and frontend tests together when the API changes.

## Analytics invariants

- Event names are `SiteVisit`, `DocumentPreview`, and `DocumentDownload`; reporting uses `summary.documentDownloads` and `documents[].downloadCount`.
- Track previews only after successful intentional PDF/image preview navigation. Track downloads from the MathArchive card download, details download, and details open-file actions, not effects, raw file endpoints, or browser PDF controls.
- Analytics action counts are separate from the document metadata `DownloadCount`. The open-file link uses `/preview` but records a `DocumentDownload` action.
- Dispatch must not block file access. The public tracking helper intentionally uses credential-free `fetch` with `keepalive: true`, bypassing Axios authentication interceptors. Do not add blind retries that can double-count actions.
- Reporting requires `AdminOnly`. Calendar dates use the browser timezone and become an inclusive UTC start and exclusive UTC end; material-list creation-date filters instead use inclusive UTC calendar dates.
- Event names are persisted as strings. Renaming them requires a data migration for existing rows. Historical events survive document deletion; do not introduce cascading deletion.
- See [analytics documentation](docs/analytics.md) for the exact API, privacy limitations, and verification commands.

## Deployment and operations

- Frontend: Vercel; `vercel.json` provides SPA rewrites.
Expand Down Expand Up @@ -148,6 +158,8 @@ dotnet test backend/MathArchive.sln --no-build
cd frontend/math-archive-web
npm ci
npm test
npm run test:seo-generator
$env:VITE_API_BASE_URL='http://localhost:5293'
npm run build
```

Expand All @@ -172,3 +184,7 @@ Always account for one admin and a small audience. Do not inflate theoretical co
6. Report files changed, behavior changed, validation results, environment blockers, and intentionally out-of-scope concerns.

Preserve unrelated user changes in a dirty worktree. Do not modify backend and frontend areas outside the requested scope merely because adjacent cleanup is possible.

## AI-assisted development

Use the reusable workflow, prompts, and checklists in [`docs/ai-workflow/README.md`](docs/ai-workflow/README.md) for AI-assisted tasks. AI output requires human review, especially for authorization, API contracts, migrations, file deletion, CI, merge, and deployment. Never provide secrets or personal data to AI, and never invent test, PR, CI, or deployment results.
56 changes: 46 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ A single administrator can manage the archive through a protected admin panel.
* Navigate by school grade and topic
* Preview supported files
* Download documents
* View download statistics
* Open the actual file in a new tab
* Use the application without registration

### Administration
Expand All @@ -28,6 +28,8 @@ A single administrator can manage the archive through a protected admin panel.
* Edit document metadata
* Replace uploaded files
* Delete documents
* Filter materials by class and upload date, with newest-first sorting and a filtered total
* View site openings, document previews, and file-open/download analytics for a selected period
* Audit consistency between database records and stored files
* Safely clean up unreferenced files after an explicit confirmation
* Manage the archive through a dedicated admin interface
Expand Down Expand Up @@ -103,7 +105,7 @@ The current implementation stores files locally:
storage/documents
```

This keeps storage concerns outside controllers and application use cases and allows the local implementation to be replaced later with S3-compatible or cloud storage.
This is the local development path. Production uses a Render Persistent Disk mounted at `/app/storage`, with `FileStorage__RootPath=/app/storage/documents`. The backend intentionally runs as a single instance. PostgreSQL stores metadata, not file contents; database and source-file backups are separate concerns.

## Security

Expand All @@ -118,7 +120,7 @@ This keeps storage concerns outside controllers and application use cases and al
## Prerequisites

* .NET 9 SDK
* Node.js and npm
* Node.js 22 and npm (matching CI)
* Docker Desktop or another Docker Compose runtime

## Local Development
Expand Down Expand Up @@ -180,7 +182,7 @@ dotnet run --project backend/src/MathArchive.Api
Swagger is available in development at:

```text
https://localhost:7000/swagger
http://localhost:5293/swagger
```

Development seed data is applied when the application starts with an empty database.
Expand All @@ -189,17 +191,21 @@ Development seed data is applied when the application starts with an empty datab

```powershell
cd frontend/math-archive-web
npm install
npm ci
npm run dev
```

Optional frontend environment variable:
Vite normally starts at `http://localhost:5173` and selects another port if occupied. Optional frontend environment variable (in `.env.local` or the shell):

```text
VITE_API_BASE_URL=http://localhost:5000
VITE_API_BASE_URL=http://localhost:5293
```

For a production build, `VITE_API_BASE_URL` must point to the public API. Google Search Console HTML-tag verification is optional and can be enabled in the Vercel project environment variables:
Development defaults to this API URL. Development CORS permits loopback origins on different ports; production uses configured allowed origins.

For a production build, `VITE_API_BASE_URL` must point to the public API. The SEO generator fetches public material metadata. After retries, network failures or HTTP 502/503/504 responses use the three stable SEO pages and base sitemap as a fallback; invalid configuration, malformed data, and other HTTP failures fail the build.

Google Search Console HTML-tag verification is optional and can be enabled in the Vercel project environment variables:

```text
VITE_GOOGLE_SITE_VERIFICATION=verification-token-from-google
Expand All @@ -209,6 +215,8 @@ Store only the token value, not the complete `<meta>` element. When configured,

## Database Migrations

The backend applies migrations on startup when `Database:ApplyMigrationsOnStartup` is enabled (the default). Analytics includes a data migration that renames persisted event values; see [analytics migration behavior](docs/analytics.md#migration-and-deletion-behavior).

Apply existing migrations:

```powershell
Expand All @@ -233,6 +241,7 @@ dotnet ef migrations add MigrationName `
/
/materials
/materials/:id
/about
```

### Administration
Expand All @@ -243,8 +252,11 @@ dotnet ef migrations add MigrationName `
/admin/documents/new
/admin/documents/:id/edit
/admin/storage
/admin/analytics
```

`/admin` redirects to `/admin/documents`. Admin pages other than login require authentication.

## API Endpoints

### Public
Expand All @@ -253,7 +265,9 @@ dotnet ef migrations add MigrationName `
GET /api/documents
GET /api/documents/topics
GET /api/documents/{id}
GET /api/documents/{id}/preview
GET /api/documents/{id}/download
POST /api/analytics/events
```

### Administration
Expand All @@ -262,16 +276,32 @@ GET /api/documents/{id}/download
POST /api/auth/login
POST /api/admin/documents
GET /api/admin/storage/audit
GET /api/admin/analytics?from=<timestamp>&to=<timestamp>
POST /api/admin/storage/cleanup-orphans
PUT /api/admin/documents/{id}
DELETE /api/admin/documents/{id}
```

Login is public; the remaining administrative endpoints require the `Admin` role. The health endpoint is `GET /health`.

### Material list contract

`GET /api/documents` accepts `search`, `grade`, `generalOnly`, `topic`, `documentType`, `createdFrom`, `createdTo`, `sort`, `page`, and `pageSize`. Creation-date filters use inclusive UTC calendar dates (`YYYY-MM-DD`). The response contains `items`, `page`, `pageSize`, `totalCount`, and `totalPages`; filtering and sorting occur before pagination, so `totalCount` reflects all matching materials.

The admin list reuses this endpoint with `sort=CreatedAtDescending` and class/date controls; topic and type controls are hidden there, but the API still supports them. The public default sort groups grades in ascending order, general materials last, then newest materials first within each group. General materials have `grade=null`; the public URL `class=general` maps to the API's `generalOnly=true`.

### Analytics and file counters

Analytics event types are `SiteVisit`, `DocumentPreview`, and `DocumentDownload`. The admin report returns `summary.documentDownloads` and per-document `downloadCount`, counting MathArchive file-open/download actions, not browser PDF-viewer activity. These period-filtered analytics counts are separate from the existing document metadata `downloadCount`, which increments only through the backend `/download` operation. Embedded previews use `/preview` and do not increment that counter.

See the [analytics guide](docs/analytics.md) for tracking, privacy, date boundaries, migrations, and tests, and the [storage audit guide](docs/course-project/README.md) for reconciliation and cleanup.

## Tests

Run backend tests:
Run backend tests from the repository root. Integration tests create temporary PostgreSQL databases using local port `5433`; `MATHARCHIVE_TEST_CONNECTION_STRING` overrides the connection (CI uses port `5432`). Never point tests at production.

```powershell
docker compose up -d postgres
dotnet test backend/MathArchive.sln
```

Expand All @@ -280,6 +310,8 @@ Run frontend tests and production build:
```powershell
cd frontend/math-archive-web
npm test
npm run test:seo-generator
$env:VITE_API_BASE_URL='http://localhost:5293'
npm run build
```

Expand All @@ -288,7 +320,7 @@ npm run build
* Only one administrator is supported.
* Files are stored locally instead of in cloud object storage.
* PDF and image preview is supported.
* Word and Excel documents are download-only.
* Word and Excel documents have no embedded preview; file-open/download controls remain available, with handling determined by the browser.
* There is no public registration or user profile system.
* There is no moderation or multi-user administration workflow.

Expand All @@ -297,3 +329,7 @@ npm run build
MathArchive is an actively developed pet project built around a real use case.

The current focus is keeping document publishing simple for the administrator while making materials easy to find and download for students and teachers.

## AI-assisted development

Reusable AI rules, prompts, checklists, and an evidence-based Storage Reconciliation example are documented in [`docs/ai-workflow/README.md`](docs/ai-workflow/README.md).
4 changes: 3 additions & 1 deletion design-qa.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Homepage design QA

This is a historical QA record of the homepage/public-page redesign, not a current release sign-off. Test counts, local URLs, and observed fixture issues describe that verification session. See the [README](README.md) for current setup and commands. Admin styling was subsequently updated separately.

## Sources and setup

- Reference: `C:\Users\pc\AppData\Local\Temp\codex-clipboard-e24a1450-170f-4fda-b0dc-c4e5be95373d.png`
Expand Down Expand Up @@ -40,7 +42,7 @@ Passed.

- Verified the shared academic palette on `/materials`, a material details route, and `/about` at the desktop browser viewport.
- Confirmed five seeded material cards load from the local API and public navigation remains functional.
- Confirmed mathematical illustrations move over time with independent 12–18 second animations; `prefers-reduced-motion` reduces them to a static state.
- Confirmed mathematical illustrations move over time; `prefers-reduced-motion` reduces them to a static state. The current CSS uses independent 8, 10, and 12 second animations after subsequent movement adjustments.
- Confirmed the updated pages produce no browser console warnings or errors during the visual checks.
- Local CORS preflight from `http://localhost:5174` returned `204`, echoed that origin, and allowed the established API methods.
- The details preview reported a missing seeded physical file; this is existing local fixture/storage state rather than a CORS or redesign failure.
80 changes: 80 additions & 0 deletions docs/ai-workflow/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# AI-assisted workflow for MathArchive

## Purpose and scope

This workflow helps developers use AI consistently for analysis, implementation, testing, review, and documentation in MathArchive. It is intended for new and current contributors and applies to features, fixes, refactoring, tests, documentation, and CI analysis.

AI is a developer tool: it can accelerate navigation, prepare a diff, or support review, but it does not replace human responsibility for correctness, security, data, merge decisions, or deployment.

## Overall process

```text
Task definition
→ repository analysis
→ planning
→ implementation
→ test generation or updates
→ local verification
→ AI code review
→ manual review
→ documentation
→ Pull Request
→ CI
→ merge
→ deployment verification
```

Steps after local verification are performed by a developer or under explicit human control. Never mark a PR, CI run, merge, or deployment as complete without factual confirmation.

## Roles

### AI

- reads the task, `AGENTS.md`, related code, tests, and documentation;
- finds analogous implementations and proposes a minimal plan;
- prepares changes within the agreed scope;
- proposes or updates behavior-focused tests;
- runs available checks and reports their exact results;
- reviews the diff and identifies residual risks.

### Developer

- confirms requirements, risky decisions, and destructive operations;
- reviews the diff, contracts, authorization, and data scenarios;
- evaluates recommendations against MathArchive's actual scale;
- remains responsible for commits, Pull Requests, CI, merge, and deployment verification.

## How to use the workflow

1. Record the task description, acceptance criteria, constraints, and explicit non-goals.
2. Ask AI to read the [rules](ai-rules.md), root [`AGENTS.md`](../../AGENTS.md), and related files.
3. Select the relevant [use case](use-cases.md) and prompt from [`prompts/`](prompts/).
4. Before editing, inspect `git status`, architecture, analogous code, API contracts, and tests.
5. Confirm the plan if it changes data, authorization, an API, the database schema, or deployment.
6. Run the relevant local checks after implementation.
7. Perform AI review with the [checklist](checklists/ai-code-review.md), followed by manual review.
8. Complete the [Definition of Done](checklists/definition-of-done.md) before opening a PR. Leave unverified items unchecked and explain why.

## Quick start for a new developer

1. Read the root [`README.md`](../../README.md) and [`AGENTS.md`](../../AGENTS.md).
2. Review the [AI rules](ai-rules.md) and [Definition of Done](checklists/definition-of-done.md).
3. For the first task, copy the [feature implementation prompt](prompts/implement-feature.md), fill in its fields, and add acceptance criteria.
4. Read the [Storage Reconciliation example](example-storage-reconciliation.md) and its source [course-project documentation](../course-project/README.md) to understand the full cycle.
5. Never provide AI with secrets or production data, and never ask it to invent verification results.

## Documentation structure

- [AI rules](ai-rules.md)
- [Use cases](use-cases.md)
- [Storage Reconciliation example](example-storage-reconciliation.md)
- Prompts:
- [Implement a feature](prompts/implement-feature.md)
- [Generate tests](prompts/generate-tests.md)
- [Refactor code safely](prompts/refactor-code.md)
- [Update documentation](prompts/update-documentation.md)
- [Review a Pull Request](prompts/review-pull-request.md)
- Checklists:
- [AI code review](checklists/ai-code-review.md)
- [Definition of Done](checklists/definition-of-done.md)

Loading
Loading