# Technical documentation — unity-skeleton

Evidence date: 2026-09-30. Revision: `master` `7b94f92` (2020-10-29).

Identifier values in `src/config.js` and the key in `.env` are omitted on purpose. The file paths are the source of those values.

## What this repository is

A private Create React App (react-scripts 3.0.1) named `fuse-react-app` 3.2.0. It vendors the Fuse React admin template under `src/@fuse` and adds a thin Unity wrapper under `src/@unity`. Product screens live under `src/app/main`. There is one remote HTTP call. Domain modules talk to an in-browser axios mock.

Single branch `master`. 109 commits, 2020-05-26 through 2020-10-29. No Dockerfile, no `bitbucket-pipelines.yml`, no committed `sonar-project.properties`.

## Application structure

```text
public/                  index.html, manifest, favicon, template assets
src/index.js             Amplify.configure + ReactDOM.render
src/config.js            region, bucket, API host, Cognito ids, attachment constant
src/serviceWorker.js     CRA helper, not imported
src/@fuse                template components, layouts helpers, FuseUtils
src/@unity               UnityAuthorization, UnityLayout, UnityNavigation, UnityTheme
src/@lodash              lodash bundle used by the template
src/@fake-db             axios-mock-adapter routes, seeds, UnityLS localStorage helper
src/@history.js          custom history object passed to react-router Router
src/app/App.js           providers: fake-db, redux, persist, Auth, router, authorization, theme, layout
src/app/configs          routesConfig, navigationConfig, settingsConfig, themesConfig
src/app/auth             splash gate + authRoles
src/app/store            reducers, sagas, redux-persist, injectReducer
src/app/layouts          layout1 (default), layout2, layout3
src/app/main/pages       auth, profile, users, groups, privileges, themesettings, 404
src/app/main/modules     sales, ticketing, ExampleModule, module admin
src/styles               Tailwind source (tailwind.css is gitignored; built by yarn tailwind)
tailwind.js              Tailwind 1 config
purge-tailwindcss.js     production CSS purge
```

`jsconfig.json` sets `baseUrl` to `src`. Create React App treats that as the root for absolute imports, so `app/...`, `@fuse`, and `@unity` map onto `src/app`, `src/@fuse`, and `src/@unity`. The app was not ejected: there is no `config-overrides.js` and no `react-app-rewired` dependency. This document did not install dependencies or start the dev server.

## Frameworks and languages

| Piece | Version in package.json |
|---|---|
| React / React DOM | ^16.13.1 |
| react-scripts | 3.0.1 |
| react-router / react-router-dom / react-router-config | ^5.2.0 / ^5.2.0 / ^5.1.1 |
| Material UI core, icons, styles, lab, pickers | 4.11 / 4.9 / 4.10 / lab 4.0.0-alpha.56 / pickers 3.2.10 |
| Redux, react-redux, Redux Toolkit, redux-saga, redux-thunk, redux-persist, reselect | 4.0.4 / 7.1.0 / ^1.4.0 / ^1.1.3 / 2.3.0 / ^6.0.0 / ^4.0.0 |
| aws-amplify | ^3.0.21 |
| axios / axios-mock-adapter | 0.19.0 / 1.17.0 |
| Tailwind / PurgeCSS | 1.0.5 / 1.3.0 (dev) |
| formsy-react | 1.1.5 |
| jsonwebtoken, jwt-decode | 8.5.1 / 2.2.0 |
| xlsx | ^0.16.6 (sales Excel upload) |
| chart.js, react-chartjs-2, react-table, material-table | present; used by example widgets and grids |

JavaScript only. No TypeScript sources. `@babel/preset-env` is in `.babelrc` for the `build-docs` babel-node script.

## Runtime configuration

`src/config.js` (committed):

- `MAX_ATTACHMENT_SIZE`: 5000000. No reader in `src` outside this file.
- `s3.REGION`: `eu-west-1`. `s3.BUCKET`: `unity-app-files`.
- `apiGateway.REGION`: `eu-west-1`. `apiGateway.URL`: a prod API Gateway invoke URL (value omitted).
- `cognito.REGION`: `eu-west-1`. User pool id, app client id, and identity pool id are set (values omitted).

`src/index.js` passes those into `Amplify.configure` under `Auth`, `Storage`, and `API.endpoints[0].name = "unity"` with `mandatorySignIn: true`.

`.env` (committed) sets `REACT_APP_MAP_KEY`. `.gitignore` ignores `.env.local` and the usual CRA env variants, and does not ignore `.env`. The key value is omitted here. It is in git history. Whether it is still valid is **Unknown / Requires Confirmation**.

`App.js` contains a commented axios `baseURL` and the note "Set this from an env file when we need it". Domain calls use relative `/api/...`, which the mock adapter handles.

## Authentication

1. `Auth` component dispatches `INIT_AUTH_REQUEST` once.
2. `InitAuthSaga` registers an axios 401 interceptor, then reads `jwt_access_token`.
3. Valid `exp` → `Auth.currentUserInfo` → `API.get("unity", "/unity/uuid/" + username)` → `setUserData`.
4. Login repeats sign-in via `Auth.signIn`, writes the same localStorage key, then the same GET.
5. Role string written by both success paths that return a user object: `superuser`.
6. redux-persist blacklist includes `auth`, so the reducer resets on refresh and step 2 is what restores the person.
7. `user.actions.js` logout removes `jwt_access_token`. The file imports Amplify `Auth`.

Axios bearer header is set from that token. Amplify's own signing for `API.get` is separate from the axios header. The mock adapter sees axios; Amplify does not use the mock.

`LoginSaga.setSession` writes the access token to the console.

## State

Store: `@reduxjs/toolkit` `configureStore`, saga middleware, persist with `autoMergeLevel2` and `storage` (localStorage), key `root`.

Blacklist: `fuse`, `auth`, `SalesModule`.

`immutableCheck` ignores `fuse.settings` and `SalesModule.Clients`. `serializableCheck` ignores persist actions and `fuse.settings`.

`devTools` is on when `NODE_ENV !== "production"`.

Sagas spawned from `RootSaga.js`: auth, `GetAllUsers`, modules (includes sales, tickets, inject, add), groups, privileges.

`injectReducer` exists for code-split reducers (`withReducer.js`). The example module uses it.

## APIs

### Remote

| Call | Client | When |
|---|---|---|
| `GET /unity/uuid/{cognito username}` | `API.get("unity", ...)` | Login and session restore |

Response handling assumes a JSON array of objects with `strUser`, `strEmail`, and `strCognitoUuid`. Login reads `response.body` and parses it. Restore destructures `{body}` from the Amplify return value and parses that. If those two shapes differ, one path breaks. **Implemented** as two different access patterns. Which shape Amplify 3 actually returns was not executed for this document.

No other `API.get` / `API.post` / `Storage.` usage exists under `src`.

### Mock (axios)

Installed for the default axios instance (`src/@fake-db/mock.js`). `onAny().passThrough()` lets unmatched URLs hit the network.

| Path family | Handler | Effect |
|---|---|---|
| `/api/auth`, `/api/auth/access-token`, `/api/auth/register`, `/api/auth/user/update` | `api/Auth.js` | Mock JWT users in `authDB`. Cognito login does not call these. `UpdateUserData.js` posts the update route |
| `/api/getUsers` | auth/users mock | Admin user table |
| `/api/getGroups`, `/api/addGroup`, `/api/deleteGroup`, `/api/addPrivilegeToGroup` | `api/Groups.js` | `GroupDB` |
| `/api/getPrivileges`, `/api/addPrivilege`, `/api/deletePrivilege`, `/api/updateUserPrivileges` | `api/Privileges.js` | `PrivilegesDB` |
| `/api/getRoles` | no mock handler found | `InitRolesAndPrivileges.js` would post this path. `RootSaga.js` does not spawn that saga, and nothing under `src` dispatches `INIT_ROLES_AND_PRIVILEGES`. The file is unused |
| `/api/getAllowedModules`, `/api/addModule` | `api/Modules.js` | `moduleDB` plus a privilege row on add |
| `/api/getSalesClients`, `/api/addSalesClients`, `/api/updateSalesClients`, tracker, credit, sales type, AWS team | `api/SalesClients.js` | sales localStorage tables |
| `/api/getTickets`, `/api/addTicket`, `/api/updateTicket`, `/api/deleteTicket`, `/api/getSelectedTicket`, task and document routes | `api/Tickets.js` | `Tickets` table |

`UnityLS` is `JSON.parse` / `JSON.stringify` on `localStorage`.

The mock JWT secret in `api/Auth.js` is the placeholder `some-secret-code-goes-here`, expiry `"2 days"`. It signs tokens for the unused mock login.

## Routes

Registered in `src/app/configs/routesConfig.js` (hand-maintained; file comment says this must be automated):

| Path | Auth | Notes |
|---|---|---|
| `/login` | guest | shell chrome hidden |
| `/register`, `/registerTest` | guest | second path is `RegisterReWrite.js` |
| `/otp` | guest | |
| `/profile` | superuser, user | footer hidden |
| `/themesettings` | superuser, user | |
| `/users` | superuser, user | exact |
| `/modules/` | superuser, user | exact; nav link is `/modules` |
| `/groups` | superuser, user | not exact |
| `/privileges` | superuser, user | route-level `viewProfile` overwritten to null |
| `/sales`, `/sales/aws`, `/sales/client`, `/sales/credit`, `/sales/type`, `/sales/new` | superuser, user | footer hidden |
| `/ticketing`, `/ticketing/view` | superuser, user | footer hidden |
| `/ExampleModule` | superuser, user | |
| `/error` | superuser, user | |
| `/` | redirect to `/profile` | |
| fallback | redirect to `/error` | |

## Data model (browser)

There is no SQL schema. Tables are JSON arrays in `localStorage`, created from `src/@fake-db/seeds`.

Sales client shape: `src/app/main/modules/sales/Screens/NewClient/tabs/ClientManagement/NewUserSchema.js`.

Ticket create shape: Name, Description, Board, Status, Priority, Department, Client, AssignedTo.

User shape on the Cognito path: `{ uuid, role: "superuser", data: { displayName, photoURL, email, settings, shortcuts } }`. No `privileges` array.

User shape on the unused mock register path: also `password`, `group`, `privileges`, role `user`.

## Navigation injection

`UnityLayout` dispatches `INIT_MODULE_ACCESS` on mount. The saga posts the current uuid and role, then concatenates the mock module list onto navigation. Duplicate removal uses `indexOf` on object identity (`InjectModules.js`). Concatenated items are new references, so that filter does not collapse equal ids. **Inferred:** a second dispatch can append the same Sales / Ticketing / Example entries again. Not executed in a browser for this document.

## Build, environments, and scripts

| Script | What it runs |
|---|---|
| `start` | `tailwind` then `react-scripts start` |
| `build` | `tailwind`, `node ./purge-tailwindcss.js`, `cross-env GENERATE_SOURCEMAP=false react-scripts build` |
| `tailwind` | `tailwind build ./src/styles/tailwind.base.css -c ./tailwind.js -o ./src/styles/tailwind.css` |
| `test` | `react-scripts test --env=node` |
| `eject` | `react-scripts eject` |
| `build-docs` | deletes ts/tsx files under `src/app/main/documentation/material-ui-components` and runs babel-node on `build.js` there |
| `build-stats` / `bundle-report` | production stats + `webpack-bundle-analyzer` |

PurgeCSS scans `./src/**/*.js`, whitelists `pl-24`, `pl-40`, `pl-56`, `pl-72`, `pl-80`, and overwrites `src/styles/tailwind.css`.

`src/styles/tailwind.css` is gitignored. A fresh clone does not contain the generated sheet until `yarn start` or `yarn run build`.

Both `yarn.lock` and `package-lock.json` are committed. Scripts invoke `yarn`. `.yarnrc` is present at the repo root.

No environment split beyond CRA's `NODE_ENV` and the single `.env`. The API URL in `config.js` is a prod stage URL even for `yarn start`. **Implemented.**

## Testing

No test files. The `test` script is the CRA default. This onboarding did not run Yarn or the test runner.

## Security notes (observed, not a pentest)

- Cognito ids, the API host, and a maps key are in git.
- The access token is logged in `LoginSaga.setSession`.
- The mock JWT secret is a fixed placeholder. Mock login is unwired; the secret still ships.
- Domain authorization is client-side only. The mock API trusts the page that loaded the bundle.
- Register and login have no captcha and no client-side lockout.
- `jsonwebtoken` in the browser bundle signs the unused mock login. Cognito sign-in does not use it.

## Deployment and operations

**Unknown / Requires Confirmation.** Nothing in the repo names a host, a bucket for the CRA `build/` output, or a pipeline. `build/` is gitignored. The scan-host tree listing included `sonar-project.properties` and `sonarqube_scan.bat`; those paths are not in `7b94f92`.

## Related repository

`unity-soft-ui` is a different Bitbucket project (Soft UI / UnityLite, later commits in 2022). It also configures Amplify in `eu-west-1` and an API named `unity`, with a different S3 bucket name (`unity-lite-files` in that project's Silicon Ark docs). The two codebases are not one app. Whether they call the same API Gateway is **Unknown / Requires Confirmation**.
