# Features — ipw_new

Evidence date: 2026-10-11.

Source: `git@bitbucket.org:overdrive/ipw_new.git`, `main` at `111772e` (2025-05-26).

Status labels:

- **Current** — a route or page in this revision does the behaviour.
- **Partial** — the screen, schema, or service exists, and the path a user would take is incomplete.
- **Not implemented** — named in the UI, README, or schema, with no working handler in this revision.
- **Removed from the active path** — code is present only as comments.

There is no changelog and no feature flag file. "Recently changed" is the git history since the 2025-04-24 import.

## Current

### Public site

| Feature | Where | Notes |
|---|---|---|
| Home | `client/pages/index.vue` | No login middleware |
| Cellars | `client/pages/cellars.vue` | |
| About us | `client/pages/about-us.vue` | |
| Manuals | `client/pages/manuals.vue` | PDFs under `client/public/documents/manuals/` |
| Forms (static PDFs) | `client/pages/forms.vue` | Not the online evaluation forms |
| Literature and articles | `client/pages/literature-and-articles.vue` | |
| Audit policy pages | `client/pages/audit.vue` | Static. Not the audit module |
| Useful contacts | `client/pages/useful-contacts.vue` | |
| Courses | `client/pages/courses.vue` | |
| Climate change | `client/pages/climate-change.vue` | Client README still says `confronting-climate-change` |
| Greenhouse gas protocol | `client/pages/greenhouse-gas-protocol.vue` | |
| Environment and sustainability | `client/pages/environment-sustainability-initiatives.vue` | |
| Links | `client/pages/links.vue` | |
| Contact us | `client/pages/contact-us.vue` | No mail-send handler was found for this page |
| English and Afrikaans strings | `client/i18n.config.ts` and page-local messages | Locale code `afr` |
| Light and dark favicons | `client/nuxt.config.js` `app.head` | |
| Forbidden page | `client/pages/forbidden.vue` | |

### Authentication

| Feature | Where | Notes |
|---|---|---|
| Login | `POST /api/auth/login`, `client/pages/login.vue` | Argon2 then MD5. See business rules |
| Logout | `POST /api/auth/logout`, `Sidebar.vue` | Clears refresh hash and cookies |
| Refresh | `POST /api/auth/refresh`, `client/api/auth.js`, `useJwt.js` | |
| CSRF token | `GET /api/csrf` | Public |
| Split JWT cookies | `auth.service.ts` `setClientCookies` | Signature cookie httpOnly; payload cookies readable by the client |
| Login gate | `client/middleware/isLoggedIn.js`, `isNotLoggedIn.js` | Logged-in users hitting login go to `/member/guidelines` |

### Member (signed in)

| Feature | Where | Notes |
|---|---|---|
| Guidelines | `client/pages/member/guidelines.vue` | Login required. Static PDFs |
| Codings | `client/pages/member/codings.vue` | Login required. Static PDFs |
| Contact details | `GET` user contact-details, `member/contact-details.vue` | |
| Groups and roles | `member/groups-and-roles.vue` | Join, pending, cancel |
| Control-product report and CSV | `server/src/reports/`, `member/reports.vue` | Status filter 50 and 55 |
| Create member API | `PUT /api/user/member` | Not called by `registration.vue` |

### Staff administration

| Feature | Where | Notes |
|---|---|---|
| List, create, edit IPW users | `admin.controller.ts`, `client/pages/admin/ipw-users/` | API role: IPW Administrator |
| Find and edit member | `client/pages/admin/find-member/` | Calls admin API |
| Member registrations page | `client/pages/admin/member-registrations.vue` | Imports admin API |
| Member activity (last 50 audit rows) | `GET /api/admin/member-activity` | Reads `auditlog`. This app does not write it |
| Live users | `GET /api/admin/live-users` | Reads `ipw_sessions`. This app does not write it |
| Role assign and remove on group 1364 | `POST /api/admin/role`, `DELETE /api/admin/role` | |
| Group membership add and remove | admin controller | |
| List and delete groups | `GroupService`, admin controller | Delete counts evaluation-form rows first |
| Create member screen | `client/pages/admin/create-member.vue` | Uses `client/api/user.js` |

### Documents

| Feature | Where | Notes |
|---|---|---|
| Category list | `POST /api/upload/categories` | Farm and Cellar names are in the SQL notes |
| Upload, list, download | `upload.controller.ts`, `client/pages/upload/documents.vue` | |
| All profiles and all documents | `GET /api/upload/all-profiles`, `POST /api/upload/all-documents` | Staff auditor roles |
| Management list | `client/pages/management/list-members.vue` | Uses the upload API. Login required. No administrator middleware |

### Platform

| Feature | Where | Notes |
|---|---|---|
| Email queue and scheduler | `email.service.ts` | Registration template is the one the create-user path queues |
| Static client hosting from Nest | `app.module.ts` `ServeStaticModule` | |
| SonarQube on `main` | `bitbucket-pipelines.yml` | Scan only |
| Staging and production client build scripts | `client/package.json` | Set API location to the ipw.co.za hostnames |

## Partial

| Feature | What exists | What does not |
|---|---|---|
| Public registration | `registration.vue` form, validation, i18n | Submit calls `auth.login`, not create-user. Middleware `is-not-logged-in` is commented out |
| Email confirmation | Token table, link builder, EJS template, confirm page with a password form | `POST /api/auth/confirm/:type/:userHash/:token` returns without checking the token. The page does not call the API. One-day validity is template text only |
| Password reminder | `reminder.vue` calls `POST /api/auth/reset` and follows `redirect` to `/reset-instructions` | Reset does not load a user, store a token, or send `reminder.*.ejs` |
| Staff user-class check | `@UserClasses(EUserClass.IPWStaff)` on the admin controller | `users.guard.ts` is not registered in `app.module.ts` |
| Administrator UI versus API | Client middleware allows System Developer or IPW Administrator | Admin API allows only IPW Administrator |
| Password upgrade | MD5 success writes Argon2 | `forced_password_update` is unused. README asks to widen `users.password`; Prisma still has `Char(32)` |
| Evaluation-form screens | Many pages under `client/pages/admin/evaluation-forms/` and member-form `view-*.vue` files, plus Prisma models | No Nest controller. `setup.vue` renders a form and does not import `client/api` |
| Group administration screens | `add-group.vue`, `ipw-groups/edit-group/[id].vue` | API imports are commented out. List and delete exist on the admin API and are used from `ipw-groups/index.vue` |
| SAWIS | Pages `sawis-search.vue`, `sawis-compliance.vue`. Models `sawis_compliance`, `sawis_farm`, `user_group_sn` | API imports commented out. No SAWIS client in `server/src` |
| Probation | `probation/current.vue`, `probation/history.vue`. Model `group_probation` | API imports commented out |
| Certificates | `admin/certificates.vue` | API import commented out |
| Member edit | `client/pages/member/edit.vue` | Imports the report API, requires login. It is not a general profile editor wired to `PUT` user update in a way this review fully traced beyond the import |
| Compiled `dist/` | Present, and `build/build.sh` knows how to rebuild it | `dist/users/users.service.js` still uses `HOSTNAME` where the TypeScript uses `DOMAIN` |
| CASL | npm dependencies `@casl/ability`, `@casl/prisma`, `nest-casl` | No import under `server/src` |
| Pinia | `@pinia/nuxt` dependency | Module registration in `nuxt.config.js` is commented out |
| Express session | npm dependencies and a large commented block in `main.ts` | Not active. `main.ts` says to remove the packages |

## Not implemented in this revision

| Item | Evidence |
|---|---|
| Fastify server | Named only in the root README |
| Nest routes for `group`, `member`, `audit`, `management`, `public` | Controllers are empty classes |
| `GET /` "Hello World" | Expected by `server/test/app.e2e-spec.ts`. `AppController` has no handler |
| Writing `auditlog` | TODO in `group.service.ts` `remove` |
| Writing `ipw_sessions` | Read by live users only |
| File type and size limits on upload | `saveDocumentUpload` writes whatever Multer received |
| Captcha or second factor | Not present |
| Deploy pipeline | `bitbucket-pipelines.yml` is SonarQube only |
| PM2 process files | Scripts reference them. Files are not in `server/` |
| Full database dump | README names SharePoint `ipw_live.sql`. Not in git |
| `sonarqube_scan.bat` | Listed by the onboarding scan. Not in git |

Client README routes that have no page file:

- `member/profile`
- `member/logout`
- `home` (the home page is `index.vue`)
- `confronting-climate-change` (the page is `climate-change.vue`)

## Removed from the active path

These remain in the tree as comments or `*.old` files. They are not registered behaviour.

| Item | Where |
|---|---|
| Username-exists, select-profile, selected-profile | Commented handlers in `users.controller.ts` |
| Separate confirm routes for email and password | Commented in `auth.controller.ts` and `public.service.ts` |
| `POST /api/user` create | Commented in `users.controller.ts` |
| Session store (`express-mysql-session`) | Commented in `main.ts` |
| `validateLogin.js.old`, `useJwt.old.txt` | `client/middleware/`, `client/` |
| In-place Argon2 rewrite of `users.password` | Commented block in `auth.service.ts` `validateUser` |

## Planned

Nothing in git is labelled as a roadmap. The following are **inferred** intentions from TODOs and README notes, not approved plans:

| Note | Source | Provenance |
|---|---|---|
| Forbid `PUT /api/user/member` unless the caller is a member admin or IPW administrator | TODO in `users.controller.ts` | Inferred from a TODO |
| Force a password change after the MD5-to-Argon2 copy | Comment in `checkOldPasswordStorage` | Inferred. Flag exists, unused |
| Write an audit row when a group is deleted | TODO in `group.service.ts` | Inferred |
| Fix the blank client caused by Dart Sass | Root README "Known Issues", 2025-05-23 | Documented defect, not a scheduled fix |
| Prisma generator `output = "./generated"` | Root README | Documented. `schema.prisma` has no `output` field |
| Push the tree to GitHub `staging` and `production` remotes | Root README contributing section | Documented. Remotes are not in this clone |

## Recently changed

| Date | Change | Feature effect |
|---|---|---|
| 2025-04-24 | `c1257a5` init push | Brought in the client, server, SQL notes, and build scripts in one commit |
| 2025-04-24 | Sonar properties and pipeline | Analysis only |
| 2025-05-19 | Pipeline Docker memory 3072 MB | No product behaviour |
| 2025-05-23 | README setup and known issue | Documentation only. The "DB dump" commit did not add a dump |
| 2025-05-26 | `unhead`, Bootstrap and `vue-multiselect` dependency placement, newer `sass` | Client package only. `unhead` is not referenced from `client/nuxt.config.js` |

## Deprecated

No release note marks a feature deprecated. The legacy MD5 column is still an active login path. Treat it as **current compatibility behaviour**, not as a removed feature.
