# Technical documentation — bigpond_frontend

Evidence date: 2026-10-19.

Source: `bigpond_frontend` branch `master` at `c5ef2b11a28f048fc83486c4f3533e2c8a72f43d` (2025-11-26). Remote: `git@bitbucket.org:overdrive/bigpond_frontend.git`.

The local path `~/projects/sonar_scan/repos/bigpond_frontend` was not on the machine that wrote this file. Facts below come from a read-only clone.

## Application type

Single-page React application (Create React App, `react-scripts` 5.0.1) with an AWS Amplify backend in the same repository. `package.json` name `bigpond`, version `0.1.0`, `private: true`.

Entry: `src/index.js` renders `src/App.js`. `App` wraps Amplify `Authenticator.Provider`, `ThemeProvider`, `AuthProvider`, and `BrowserRouter`, then `src/router/index.jsx`.

There is no separate backend repository inside this tree. Lambdas live under `amplify/backend/function`.

## Repository layout

```text
amplify/                 Amplify backend (api, auth, function, storage)
public/
src/
  components/            shared UI (nav, tables, uploads, contact, login)
  context/authContext.jsx
  pages/                 route screens
  router/                authenticated routes and public routes
  util/                  auth, S3, signup, error log
bitbucket-pipelines.yml  SonarQube on master
package.json
README.md
CHANGELOG.md
NewKnowledgeBase.md
rules.md
sonar-project.properties
Untitled-1.json          scratch JSON. Not referenced by the app entry
```

Also present and not part of the runtime: `.cursor`, `.cursorrules`, `.obsidian`, `.vscode`.

Not present at this commit: `CONTRIBUTING.md`, `amplify/backend/hosting`, `src/pages/customFlowPage`, `src/pages/pocPage`, `sonarqube_scan.bat`.

## Frontend structure

| Path | Responsibility |
|---|---|
| `src/router/basic/index.jsx` | `/` landing, `/login`, any other anonymous path redirects to `/` |
| `src/router/index.jsx` | After auth, role 1 and role 3 route tables. Also `/callback` and `/error` while logged out |
| `src/context/authContext.jsx` | `checkUser`, clients, bank numbers. API name `apibigpond` |
| `src/components/navigation/NavAdmin.jsx` | Admin links |
| `src/components/navigation/NavUser.jsx` | User links |
| `src/pages/customMatchPage` | Workpapers. Parent `index.jsx`, `StepCard`, `ResultsTable`, `MatchTable`, services, hooks |
| `src/pages/workflowPage` | Xero Testing |
| `src/pages/logsPage` | Log export |
| `src/util/signUp.js` | Cognito `AdminCreateUser` and `AdminAddUserToGroup` |
| `src/util/s3Objects.js` | List, delete, signed URL against a hardcoded bucket |

UI libraries in `package.json`: React 18.2, react-router-dom 6.9, react-bootstrap 2.7, Bootstrap 5.2, MUI 6, Amplify UI React 4, DataTables.net, Font Awesome, styled-components, Emotion.

Document libraries: `xlsx`, `xlsx-js-style`, `jspdf`, `jspdf-autotable`, `pdf-lib`, `pdfjs-dist`, `pdfmake`, `jszip`.

AWS SDK v3 clients for Cognito, S3, Secrets Manager, and SSM are dependencies of the frontend package as well as of Lambdas. `mysql2` and `openai` are also in the frontend `package.json`. The browser path that was confirmed to call Cognito admin APIs is `src/util/signUp.js`.

## Routing

Anonymous (`route !== "authenticated"`):

| Path | Screen |
|---|---|
| `/` | Landing |
| `/login` | Authenticator |
| `/callback`, `/callback/:id` | Xero callback |
| `/error` | Error card |
| other | Redirect to `/` |

Role `1`:

| Path | Screen |
|---|---|
| `/companies` | Audit firms. Default |
| `/clients` | Clients |
| `/users` | Users |
| `/audit` | “Audit Page” heading |
| `/workflow` | Xero Testing |
| `/custommatch` | Workpapers |
| `/logs` | Logs |
| `/callback`, `/error` | Same as above |
| other | Redirect to `/companies` |

Role `3`: `/clients` (default), `/audit`, `/workflow`, `/custommatch`, `/callback`, `/error`. Other paths redirect to `/clients`. No `/companies`, `/users`, or `/logs`.

`active === 0` replaces the whole shell with the inactive page.

Nav mismatch: the Users link sets the active class when the path segment is `userslist`, but navigation goes to `/users`. The link never receives `active-link`. **Implemented.**

## Authentication

Imported Cognito user pool and identity pool (`amplify/backend/auth/bigpond10dd5625/parameters.json`):

- Service type: `imported`.
- Region: `ap-southeast-2`.
- Username and required attribute: email.
- Password: length 8, lower, upper, number, symbol.
- MFA: `OFF`.
- Auto-verified: email.

`amplify/team-provider-info.json` records user pool id `ap-southeast-2_tRyN44wjs` and an identity pool id. `signUp.js` hardcodes the same user pool id and region.

Session id used as a table suffix: `identityId.split(":")[1]` with `-` replaced by `_`.

Amplify REST paths in `apibigpond/cli-inputs.json` use `"setting": "protected"` and also list guest create, read, update, and delete. Lambdas do not inspect Cognito groups. **Requires Confirmation** how API Gateway treats the guest list at deploy time.

## API Gateway

From `amplify/backend/backend-config.json`:

| API | Functions attached |
|---|---|
| `apibigpond` | `bigpondexpress`, `XeroExpress`, `xerotoken`, `xerotenants`, `xerorefresh`, `updatetableinvoice`, `sendxeroemail`, `frontendLogs`, `generatefile`, `ZipObjects`, `deleteSessionUploads`, `openaistart`, `ZipWorkPapers`, `contactmail` |
| `apibigpondfiles` | `generatefile`, `ZipObjects`, `deleteSessionUploads` |
| `manageFileProcessTable` | `manageFileProcessTable` |
| `tempFileProceesTable` | `tempFileProceesTable` (spelling as in the repository) |

`tempFileProcessTable` (correct spelling) also exists as a function directory. It is not the name of the fourth API.

Override files under `amplify/backend/api/*/override.js` set API Gateway `MinimumCompressionSize` to `0` (gzip). Added as `.ts` in `a9bba99` and renamed to `.js` in `c5ef2b1`.

`apibigpond/override.js` still contains:

```js
export function override(resources: AmplifyApiRestOverrides)
```

The other three override files use `exports.override = (resources) => { ... }`. **Requires Confirmation** that Amplify can load the `apibigpond` file.

### bigpondexpress routes

Express app in `amplify/backend/function/bigpondexpress/src/app.js`. Middleware opens MySQL through `/opt/createDBConnection`. CORS allow-list: `https://www.auditwhizz.net`, `http://localhost:3000`. `x-powered-by` is disabled.

User: `/getusers`, `/getusersbycompany/:companyid`, `/getuserbyid/:userid`, `/getuserbyusername/:username`, `/initUser/:username`, `/getuserbyemail/:email`, `/adduser/:user`, `/bulkaddusers`, `/deleteuserbyid/:userid`, `/updateuser/:user`, `/updateusersession/:user`, `/updateuserselectedbank/:user`, `/updateuserworkflowtype/:user`, `/updateuserclient/:user`, `/updateuseruploadprocess/:userid`, `/getuseruploadprocess/:userid`.

Company: `/getcompany`, `/getactivecompanies`, `/getcompanybyid/:companyid`, `/addcompany/:company`, `/deletecompanybyid/:companyid`, `/updatecompany/:company`.

Client: `/getcompanyclients`, `/getcompanyclientsid/:clientid`, `/getclientsbycompanyid/:companyid`, `/addcompanyclient/:client`, `/deletecompanyclientbyid/:clientid`, `/updatecompanyclient/:client`, `/updatecompanyxero/:company`, `/setclientactivepending/:clientid`.

Roles: `/getuserrole`, `/getuserrolebyid/:roleid`, `/adduserrole/:role`, `/deleteuserrolebyid/:roleid`, `/updateuserrole/:role`.

Session: `/getsessionbanknumbers/:sessionid`, `/deletemastersessiontable/:sessionid`, `/clearmastersessiontable/:sessionid`.

Flows: `/addcustommatchflow/:flow`, `/getcustommatchflowbyid/:flowid`, `/getcustommatchflowsbycompanyid/:companyid`, `/updatecustommatchflow/:flow`, `/deletecustommatchflowbyid/:flowid`.

Many identifiers are JSON stuffed into the URL path (`JSON.parse` on the path parameter). Flow JSON body is `req.body.json_value`. Success shape is `{ success: "call succeed!", rows }`. Error shape is `{ error: "call error!", err }` from `res.json`, which defaults to HTTP 200.

`/initUser` builds `CREATE TABLE master_table_${s3_session}` by string interpolation. The session id is derived from the Cognito identity id, not from a free-text field, but it is not passed as a SQL parameter.

### manageFileProcessTable actions

POST body `action`:

| Action | Role |
|---|---|
| `createTable` | Ensure the per-user processing table |
| `addFile` | Store a file row |
| `getResults` | Read results |
| `deleteTable` | Drop the table |
| `deleteStepRecords` | Delete one step |
| `updateFileProcessing` | Update processing state |
| `setLargeFiles` | Mark large files |
| `deleteStepMatch` | Delete match rows for a step |
| `getLogs` | Query `workpaper_logs` |

GET requires `userId` and optionally `stepNumber`. Table name comes from `getTableName(userId)`.

### Other Lambdas

| Function | Role |
|---|---|
| `XeroExpress` | Xero URL and client helpers. Redirect `https://www.auditwhizz.net/callback` |
| `xerotoken`, `xerotenants`, `xerorefresh` | OAuth token, tenants, refresh |
| `sendxeroemail` | SES mail from `no-reply@auditwhizz.net` |
| `updatetableinvoice` | Invoice table update. CORS production host |
| `generatefile` | Excel workbook from the session table. Buckets `bigpond-uploads` and `big-pond-uploads-bucket123209-dev` |
| `ZipObjects` | Zip S3 objects in the dev bucket name |
| `ZipWorkPapers` | Zip workpaper outputs |
| `deleteSessionUploads` | Delete session objects in the dev bucket name |
| `openAIFileProcessing` | Extract and match. Model `gpt-4.1`. Env `REACT_APP_OPENAI_API_KEY`. CloudFormation schedule `rate(1 minute)` |
| `processUnprocessedFiles` | Schedule `rate(5 minutes)`, enabled. Model `gpt-4o`. Env `S3_BUCKET=bigpondpuloads`. Secrets `DatabaseCredentials`, `OpenAIKey` |
| `openaistart` | Start helper. CORS production host |
| `contactmail` | SES to `bhawin@auditwhizz.net` |
| `frontendLogs` | `console` log of the client error. Response text `Logs created` |
| `tempFileProcessTable`, `tempFileProceesTable` | Temporary file-processing tables. Both spellings exist |
| `bigpondCustomMessage` | Cognito custom message trigger directory. Message template was not reviewed line by line |
| `bigpondCreateConnection` | Shared layer. `opt/createDBConnection.js` |

## Data

No migration directory or schema dump is in the repository. Tables are known because SQL strings name them.

| Table | How it is known |
|---|---|
| `users` | Select and insert in `bigpondexpress`. Columns include `company_id`, `fullname`, `email`, `role_id`, `cognito_uuid`, `s3_session`, `active`, `workflow_type`, `current_client`, `selected_bank_number`, `xlsx_process` |
| `company` | Firm name and `active` |
| `company_client` | Client name, contact, `active`, `xero_token`, `xero_refresh_token`, `xero_tenant_id`, `connection_id` |
| `user_role` | `select *` API. Seed values not in git |
| `custom_match_flows` | `company_id`, `type`, `json_value`, `active` |
| `master_table_{session}` | Created at login. Bank, invoice, receipt, textract, and file-name columns. See `/initUser` |
| `workpaper_logs` | Queried by `getLogs`. Not created in the snippet that was read |
| Per-user file-process tables | Name from `getTableName(userId)` inside `manageFileProcessTable` |

MySQL connection (`createDBConnection.js`):

- Region `ap-southeast-2`.
- Password and username from a Secrets Manager secret id hardcoded in that file.
- Host, port, and database name from SSM parameter `rdsDetails`, value split on commas, then each piece split on `=`.

`processUnprocessedFiles` declares a different secret name, `DatabaseCredentials`. Whether both secrets point at the same database is **Requires Confirmation**.

The secret id and the AWS account id from `team-provider-info.json` are omitted here. They are in the source repository.

## Storage

| Name in source | Where |
|---|---|
| Amplify resource `bigpondpuloads` | `amplify/backend/storage/bigpondpuloads` |
| Configured bucket name `big-pond-uploads-bucket` | `cli-inputs.json` |
| Hardcoded `big-pond-uploads-bucket123209-dev` | `s3Objects.js`, `explorerButtons.jsx`, `generatefile`, `ZipObjects`, `deleteSessionUploads` |
| Hardcoded `bigpond-uploads` | `generatefile` (`getBucket`), `src/util/getTemplate.js` |
| Env `S3_BUCKET=bigpondpuloads` | `processUnprocessedFiles` function parameters. This is the Amplify resource name, not the bucket string above |

Uploads from the workflow screen use Amplify Storage `accessLevel: private`.

**Requires Confirmation.** Which of these names is the bucket the live dev stack created (`amplify-bigpond-dev-123209` is the stack name fragment in `team-provider-info.json`).

## External services

### OpenAI

`openAIFileProcessing/src/index.js` defaults the model to `gpt-4.1` for both document processing and matching. The key is `process.env.REACT_APP_OPENAI_API_KEY`. If it is missing, the function logs a warning.

`processUnprocessedFiles/src/index.js` sets `model: "gpt-4o"` and is allowed secret `OpenAIKey`.

`StepCard.jsx` sends `model: 'gpt-4.1'` in one call and falls back to `model || 'gpt-4o'` in another.

Prompt construction: `openAIFileProcessing/src/buildPrompt.js` (`buildDocumentPrompt`, `buildMatchingPrompt`, `buildLargeDocumentPrompt`).

### Xero

Host `https://api.xero.com`. Redirect `https://www.auditwhizz.net/callback`. Token columns on `company_client`. Disconnect on deactivate uses the stored access token as a Bearer token.

### Email

SES. Contact recipient `bhawin@auditwhizz.net`. From address `no-reply@auditwhizz.net` for contact and Xero mail.

## Schedules

| Function | Expression | Source |
|---|---|---|
| `processUnprocessedFiles` | `rate(5 minutes)`, `enabled: true` | `function-parameters.json` |
| `openAIFileProcessing` | `rate(1 minute)` | CloudFormation `ScheduleExpression` |

README says unprocessed PDF/Excel files are processed every 5 minutes. That sentence matches `processUnprocessedFiles`, not the 1-minute schedule.

## Infrastructure and environments

`amplify/team-provider-info.json` Region `ap-southeast-2`. Stack id fragment `amplify-bigpond-dev-123209`. No `amplify/backend/hosting` directory.

Production browser origin used in CORS and Xero is `https://www.auditwhizz.net`. Local origin on some Lambdas is `http://localhost:3000` (Create React App default).

How the React app is hosted (Amplify Hosting, S3, another server) is **Unknown / Requires Confirmation**. It is not described by a hosting resource in git.

`README.md` getting started: clone, `npm install`, configure Amplify, set environment variables, `npm start`. It names `REACT_APP_OPENAI_API_KEY`. No `.env.example` was in the top-level file list.

## Security notes (as implemented)

- MFA is off in the imported auth parameters.
- Sign-up is hidden. User creation uses Cognito admin APIs from the browser with the signed-in user's AWS credentials (`signUp.js`). Those credentials must be allowed to call `AdminCreateUser`. That IAM permission was not inspected beyond the client call.
- Express routes do not check `role_id`.
- Guest verbs are present on protected API path definitions.
- `setErrorRes` returns the error object to the client.
- `CREATE TABLE` interpolates the session id.
- Contact recipient and from-address are hardcoded.
- User pool id is hardcoded in `signUp.js`.
- ReDoS fixes in `customMatchPage/utils/formatters.js` are **Documented** in `CHANGELOG.md` (bounded quantifiers). Sonar project key is `overdrive_bigpond_frontend_AYqLOFK0uNwRc2P2S9QL`. CPD excludes `**/amplify/**/function/**`.

## Build, test, and pipeline

Scripts: `react-scripts start`, `build`, `test`, `eject`.

`src/App.test.js` renders `App` and expects text `/learn react/i`. The landing page does not render that text. **Implemented** test does not describe the product.

No Cypress, Playwright, or PHPUnit tree. No `CONTRIBUTING.md`.

Bitbucket Pipelines (`bitbucket-pipelines.yml`):

- Clone depth: full.
- Branch `master` only.
- Step “SonarQube analysis”, pipe `sonarsource/sonarqube-scan:3.0.2`.
- Docker memory 3072 MB.
- Variables `SONAR_HOST_URL`, `SONAR_TOKEN`.
- `EXTRA_ARGS`: `-Dsonar.scanner.forceEmbeddedJava=true`.

The pipeline does not run `npm test` or `npm run build`.

## Dependencies of note

Runtime: `aws-amplify` 5.2, `@aws-amplify/ui-react` 4.3, `aws-sdk` v2 and `@aws-sdk/*` v3, `axios`, `mysql2`, `openai` 4.72, `react` 18.2, `react-scripts` 5.0.1.

Dev: `copy-webpack-plugin` 12.

`amplify/backend/package.json` was touched by commit `a9bba99`. It is the Amplify backend package, not the CRA package.

## Operational discrepancies

| Item | Detail |
|---|---|
| Override language | `apibigpond/override.js` is TypeScript syntax. Sibling override files are CommonJS |
| Bucket names | Three strings. See Storage |
| OpenAI model | `gpt-4.1` and `gpt-4o` both used |
| OpenAI credential | Env var on one Lambda, Secrets Manager name on another |
| Database credential | Hardcoded secret id in the layer, different secret name on `processUnprocessedFiles` |
| Schedules | 1 minute and 5 minutes |
| Logs navigation | Route on, link commented out |
| `tempFileProceesTable` | API and function use a misspelling. A second function directory uses the corrected spelling |
| `rules.md` | Tells authors not to preview the site and points at pages that are not in the tree |
| Task-creation file list | Included `sonarqube_scan.bat`. Absent at `c5ef2b1` |

## Unknown / requires confirmation

- Live hosting URL and whether `https://www.auditwhizz.net` is still the deployed front end.
- Whether the imported Cognito pool still matches `parameters.json`.
- Whether guest API permissions are deployed.
- Whether `apibigpond/override.js` packages.
- Which S3 bucket name is live.
- Which OpenAI key and model production uses.
- Contents of `user_role` and whether role 2 is a real role.
- Whether hard deletes of firms and users are approved.
- Whether the Expense Document generate rule's else branch is intentional.
- Whether the changelog's confidence split (6 vs 8) should replace the constant 6.
- Whether Logs should be in the sidebar for admins, users, or neither.
- Canonical product name: Audit Whizz or BigPond.
