# Project plan — ipw-backend

Evidence date: 2026-10-11.

Source repository: `git@bitbucket.org:overdrive/ipw-backend.git`.

Inspected revision: `main` at `f5ff052` (2025-06-03, Jason Wandrag, "Remove unused console log"). The scan-host path `~/projects/sonar_scan/repos/ipw-backend` was not on this machine. The tree was read from a clone of that revision. Secret values are omitted.

This plan describes what the repository contains. It does not decide what the product should become.

There is no prior Silicon Ark set for this repository. The source README is the only product document in git. `README.md` points at `NewKnowledgeBase.md`; that file is not in the tree.

## Product

**Documented** in `README.md` and `package.json`: this repository is the backend for "the IPW system". The npm package is `ipw-server` ("IPW server application"), author Craig van Tonder, `private`, licence `UNLICENSED`. The frontend is a separate system and is not in this repository. The README says the server expects that frontend to be running separately, and names a Nuxt client through `NUXT_API_LOCATION` and `NUXT_CLIENT_LOCATION`.

**Implemented.** A NestJS 10 application on Express. Global prefix `api`. MySQL through Prisma 5 (`prisma db pull`, not Prisma Migrate). Passport local login, JWT access and refresh cookies, double-submit CSRF, role checks against `rbac_*` tables, an email outbox, document upload, member-group administration, and a control-product report.

**Inferred from this repository.** The schema and one SQL case statement describe wine-industry evaluation: SAWIS numbers, farms, cellar type, herbicides, pesticides, fungicides, nematicides, and evaluation-form statuses such as Pass, Fail, and Probation. This repository never expands the letters "IPW".

**Documented in a sibling repository, not in this one.** Silicon Ark `ipw_new` records that repository's README calling the product a rewrite of the old IPW website for Integrated Production of Wine. `ipw-backend` was split on 2025-05-26 (`1aafc59`, "split code base sever to backend"), the same day as the inspected tip of `ipw_new`. Whether this tree is that server half is **Requires Confirmation**. Silicon Ark `ipw-frontend` is a Nuxt client that calls `{NUXT_API_LOCATION}/api` (template host port 9000, staging `https://staging.ipw.co.za`, production `https://ipw.co.za`). This backend does not name those hosts. Silicon Ark `ipw-php` documents a CodeIgniter 4 IPW application. Comments here mention a PHP or Joomla legacy. Whether that is `ipw-php` is **Unknown / Requires Confirmation**.

## Branch reality

| Line | Tip | Date | What it is |
|---|---|---|---|
| `main` (checked-out default) | `f5ff052` | 2025-06-03 | Only history inspected. 9 commits, 2025-05-26 through 2025-06-03 |

Authors on those commits: Jaco Brits / jacobrits1 (2025-05-26 and 2025-05-27) and Jason Wandrag (2025-06-02 and 2025-06-03).

`1aafc59` (2025-05-26) is "split code base sever to backend". Earlier history of the combined codebase is not in this repository.

Whether anyone still runs this process, and on which host, is **Unknown / Requires Confirmation**.

## Objectives visible in the tree

| Objective | Provenance |
|---|---|
| Serve the IPW frontend over HTTP | **Documented** in `README.md` |
| Authenticate users and issue access and refresh tokens | **Implemented** in `src/auth/` |
| Replace legacy MD5 passwords with argon2 on successful legacy login | **Implemented** in `AuthService.checkOldPasswordStorage`. Forcing a password change is commented as not yet used |
| Let IPW administrators manage staff, members, roles, and member groups | **Implemented** in `src/admin/` |
| Let a signed-in user see groups, request to join a group, and edit a user row | **Implemented** in `src/users/`. Several role annotations on these routes are commented out |
| Queue a registration email and send it over SMTP | **Implemented** in `UsersService.create` and `EmailService` |
| Report control products used on evaluation forms whose status is 50 or 55 | **Implemented** in `src/reports/` |
| Store uploaded documents on disk and in `document_upload` | **Implemented** in `src/upload/` |
| Deploy by watching a Supabase `code_deploy` row, backing up MySQL, and applying SQL files | **Documented** in `README.md` and **implemented** as `deploy/deploy.sh`, with placeholders still in the script |
| Scan `main` with SonarQube | **Implemented** in `bitbucket-pipelines.yml` |

No roadmap, milestone dates, or issue tracker export is in the repository.

## Scope

In this repository:

- HTTP API under `/api`.
- Prisma schema pulled from MySQL (`src/prisma/schema.prisma`).
- Email templates `registration` and `reminder` (only `registration` is queued by code).
- A deployment shell script and two Supabase table scripts.
- One example SQL file under `migration/`.
- Jest spec scaffolds and one e2e spec.

Out of this repository, from evidence in this tree:

- The Nuxt frontend (`NUXT_CLIENT_LOCATION`, and a development static path `client/.output/static`).
- The MySQL server. Connection string is `DATABASE_URL`. No host name is committed.
- SMTP credentials.
- The Supabase project that `deploy/deploy.sh` is written to call (`https://supabase.appmonitor.co.za/` is a placeholder assignment in the script).
- PM2 ecosystem files. `package.json` runs `production.config.js`, `staging.config.js`, and `development.config.js`. Those files are not in git.

## System shape

```text
Nuxt client (separate repository)
        |  cookies + Authorization bearer (header.payload) + CSRF
        v
NestJS /api  (this repository)
        |-- Prisma --> MySQL (legacy IPW tables + newer user_password, email, document_*)
        |-- nodemailer --> SMTP
        |-- node-schedule --> email outbox (table cronjob, id 1)
        |-- filesystem --> ipw_uploads/   (not UPLOAD_DIRECTORY)
        |
deploy/deploy.sh (separate loop)
        |-- Supabase REST code_deploy / migrations
        |-- git pull, mysqldump, mysql < migration/*.sql
        |-- npm run start:production (PM2 config not in git)
```

Bitbucket Pipelines on `main` runs a SonarQube scan only. It does not build, test, or deploy.

## Major components

| Component | Role | Evidence |
|---|---|---|
| `AppModule` | Loads `.env.${NODE_ENV}`, static files, and feature modules. Global `AccessTokenGuard` and `RolesGuard` | `src/app.module.ts` |
| Auth | Login, refresh, logout, empty confirm and reset handlers | `src/auth/` |
| Users | Profile, group join, member create, user update and delete | `src/users/` |
| Admin | Staff list, member list, role assignment, group membership, group delete | `src/admin/` |
| Roles | RBAC role names from `rbac_roles` | `src/roles/` |
| Reports | Assessment years, groups, control products, table and CSV | `src/reports/` |
| Upload | Categories, upload, list, download | `src/upload/` |
| Email | Outbox in `email`, SMTP send on a schedule | `src/email/` |
| CSRF | `GET /api/csrf` and `csrf-csrf` middleware | `src/csrf/` |
| Group service | Search and delete `user_group`. Controller class is empty | `src/group/` |
| Member, management, public, audit modules | Controllers and services with no live routes | matching directories under `src/` |
| Prisma schema | Legacy content, evaluation, RBAC, SAWIS, and newer auth/upload tables | `src/prisma/schema.prisma` |

## Milestones visible in git

| Date | Commit | What landed |
|---|---|---|
| 2025-05-26 | `9e12c3d` | Initial commit |
| 2025-05-26 | `79d4684`, `39f4ef2` | Bitbucket pipeline and `sonar-project.properties` |
| 2025-05-26 | `1aafc59` | Split described as moving the server into this backend repository |
| 2025-05-26 | `71576ec` | Linter, SonarQube, README |
| 2025-05-27 | `9f07dec` | Self-deploy script and migration files |
| 2025-06-02 | `3931ee7` | Password hashing flow adjusted so a password update can be used to sign in |
| 2025-06-03 | `f01f621` | File upload/download, login by username or email, last-logon update |
| 2025-06-03 | `f5ff052` | Remove an unused console log. Current tip |

No release tags were inspected beyond this commit list. Tag presence was not required to read `main`.

## Known dependencies

Runtime (`package.json`): NestJS 10.2, Prisma 5.4, Passport (local and JWT), argon2, md5, cookie-parser, csrf-csrf, nodemailer, ejs, node-schedule, sqids, uuid, fast-csv, luxon, express-session and express-mysql-session (session use is commented out in `src/main.ts`), `@casl/ability`, `@casl/prisma`, `@casl/vue`, and `nest-casl`. No `casl` symbol is referenced under `src/`.

Tooling: TypeScript 5.2, Jest 29, ESLint, Prettier, Nest CLI. Node 18+ and npm 9+ are **documented** as recommended. `@types/node` is 20.8.7.

External systems: MySQL, SMTP, a Nuxt origin list (`CLIENT_ORIGIN`), optional Supabase for deploy triggers, Bitbucket Pipelines, SonarQube (`SONAR_HOST_URL`, `SONAR_TOKEN`).

## Current status

**Implemented and wired:** authentication, CSRF, admin user and group management, self-service group join (pending row), registration email enqueue, control-product reports, document upload to `ipw_uploads`, SonarQube pipeline.

**Present as empty modules or commented routes:** member, management, public, and audit controllers; profile selection; username-exists; auth confirmation; password-reset body; express session; CASL.

**Schema without a matching API in this revision:** evaluation-form editing, content pages, SAWIS compliance writes, probation processing, audit-log writes (admin only reads the latest 50 `auditlog` rows). Group delete has a commented PHP-style audit call and does not write `auditlog`.

**Deployment:** script and README exist. `REPO_NAME` and `SUPABASE_KEY` in `deploy/deploy.sh` are still the placeholder strings. PM2 config files named by `package.json` are absent. Whether the script has ever been scheduled is **Unknown / Requires Confirmation**.

## Human confirmation

1. Confirm that "IPW" here is Integrated Production of Wine, as stated in the `ipw_new` README and not in this repository.
2. Confirm that `ipw-frontend` is the Nuxt client for this API, and which environment URLs are live. `ipw_new` still contains its own `server/` and `client/`.
3. Whether evaluation forms, content pages, and SAWIS compliance are still served by another application.
4. Whether `deploy/deploy.sh` is in use, and what `REPO_NAME` should be.
5. Whether `user_class_id` and the numeric role ids 10, 11, 14, and 16 still match production `rbac_roles`.
6. Whether a new user's random password is delivered anywhere. This API emails a confirmation link and does not complete `POST /api/auth/confirm/:type/:userHash/:token`.
