# Arda — Admin React SPA

Arda is the single-page React app that staff use for everything: moderation, factory order management, inventory, shipping, channel / media curation, room admin, analytics, and more. It's backed by a tiny Node/Express proxy that authenticates each request and forwards it to `apps/admin_api` or `cloud/cloud_api`.

**Webapp root:** [webapps/arda/](../../webapps/arda).
**Proxy entry:** [webapps/arda/arda.js](../../webapps/arda/arda.js):19 — port `PORT` or 3010.
**SPA entry (webpack):** [webapps/arda/app/index.js](../../webapps/arda/app/index.js) → router at [webapps/arda/app/App.jsx](../../webapps/arda/app/App.jsx).
**Shared components:** [webapps/src/components/](../../webapps/src/components).
**Not a yarn workspace** (see [workspaces.md](../workspaces.md#non-workspace-packages)).

## Proxy Model

Arda's server isn't an "API" of its own — it's a narrow auth + proxy layer. The browser authenticates against Arda (session cookie), and every `/api/admin/*` or `/cloud/admin/*` request is forwarded to the right upstream with the user's credentials attached.

```mermaid
flowchart LR
    BROWSER["Browser<br/>React SPA"]
    ARDA["arda.js<br/>(Express, :3010)"]

    subgraph Upstream
        ADMIN["apps/admin_api<br/>(:3999)"]
        CAPI["cloud/cloud_api<br/>(:3002)"]
        API["apps/api<br/>(:3009)"]
    end

    BROWSER -->|GET /js/bundle.js| ARDA
    BROWSER -->|POST /api/auth/login| ARDA
    ARDA -->|forward| API

    BROWSER -->|/api/admin/*| ARDA
    ARDA -->|ensureAuthenticated<br/>+ ensureModerator| ARDA
    ARDA -->|proxy| ADMIN

    BROWSER -->|/cloud/admin/*| ARDA
    ARDA -->|ensureAuthenticated<br/>+ ensureModerator| ARDA
    ARDA -->|proxy| CAPI
```

The relevant proxy registrations live at [arda.js:69-79](../../webapps/arda/arda.js):

```js
app.get("/api/admin/*", API.ensureAuthenticated, API.ensureModerator, adminFunctions.adminApiServerGetRequest);
app.post("/api/admin/*", API.ensureAuthenticated, API.ensureModerator, adminFunctions.adminApiServerPostRequest);
// ...
app.get("/cloud/admin/*", API.ensureAuthenticated, API.ensureModerator, adminFunctions.cloudApiServerGetRequest);
```

A session cookie (`accessToken` / `refreshToken`) or a Bearer header provides the access token; the proxy re-attaches it to outgoing admin-API calls.

## Feature Areas

The SPA is routed by React Router. There are 86 routes in [App.jsx](../../webapps/arda/app/App.jsx). Components are grouped by domain under [webapps/src/components/](../../webapps/src/components):

| Folder | What it covers |
|--------|----------------|
| `Accounts/` | User search, editor, onboarding flow, reports (user reports, room reports) |
| `Analytics/` | Analytics dashboard (recharts + summary stats) |
| `Apps/` | RDC (Remote Desktop Connection) build management |
| `Auth/` | Login / logout for staff |
| `BigLogistics/` | Inventory management (IntakeReport, InventoryAdd, BigProductCustomsInfo), BigShipper (next order, batch queue, pickup, fulfilment) |
| `BigOrders/` | Shopify orders, BigOrder home, custom-order creation wizards, webhook debug |
| `CloudApi/` | Shared Cloud API client utilities and error handling for the SPA |
| `Developers/` | OAuth client admin UI — `DevHome`, `OAuthClientEditor`, `OAuthClientSecretModal`, `OAuthGrantsList`, `OAuthAuditLog`. Surfaces `/admin/oauth/clients/*` and related endpoints (see [services/oauth.md](../services/oauth.md)). |
| `Events/` | Topic management (pub/sub categories) |
| `Experimental/` | KB browser, DHL charges tools, factory QA, team globe, worker-status panel |
| `Fabricator/` | Order jobs, batches, scan-request home |
| `MoviesTV/` | Cinema categories, media items/products, in-app purchases, coupons, cinema sales, TV dashboard |
| `Network/` | Logs viewer, media-server list, Redis browser, Redis info |
| `Rooms/` | Create/edit room, browse environments |
| `Util/` | Formatters, shared table components |
| `Wrappers/` | `MenuPageWrapper` (page layout shell) |

## Build Setup

- **React 17** (the CLAUDE.md / repo-root README mention "React 0.16"; in practice the current Arda bundle uses React 17 — see [webapps/package.json](../../webapps/package.json)).
- **Webpack 4** with Babel preset-env + preset-react. Entry `app/index.js`, output `webapps/arda/public/js/bundle.js`.
- **Semantic UI React** for most UI controls.
- **Three.js + recharts** for 3D and chart widgets.

Dev and prod builds both live on the root `package.json` at the monorepo root:

- `yarn dev:arda-webpack` — `webpack --mode=development --watch`
- `yarn webpack:arda` — one-shot development build (useful before a commit)
- Production: run `webpack` inside `webapps/arda` (no `--mode`) and commit the bundle. Jenkins copies it to the webserver (see the "Building for production" section of the [repo root README](../../README.md)).

## Running Locally

```bash
# In the monorepo root
yarn dev        # starts everything (arda server + webpack)
# or
yarn dev:arda-server
yarn dev:arda-webpack   # in a separate tab
```

Arda needs a `.env` with the upstream URLs + bearer keys for `apps/api`, `apps/admin_api`, and `cloud/cloud_api`. See the "Running the webserver" section in the [repo root README](../../README.md#running-the-webserver) for the minimum env-file shape.

## OAuth Authorization-Server Role

Arda doubles as the consent UI for Bigscreen's OAuth 2.0 provider (plan 14). The SPA proxy serves two extra endpoints that the React app **does not** render — they're server-rendered Pug pages the browser lands on during a third-party OAuth redirect.

| Method | Path | What it does |
|--------|------|--------------|
| `GET` | `/oauth/authorize` | Validates client + scope + PKCE, 302s to `/login` if unauthenticated, otherwise renders `views/oauth_consent.pug`. Handler in [`webapps/src/server/oauth.js`](../../webapps/src/server/oauth.js). |
| `POST` | `/oauth/authorize/decision` | CSRF-checked allow/deny. On allow, proxies to `admin_api` (`/admin/oauth/my_grant`, `/admin/oauth/codes`) and 302s to the client's `redirect_uri` with the code. |

Gated on `OAUTH_AUTHORIZE_ENABLED=true` ([arda.js:92-101](../../webapps/arda/arda.js)). Arda does not talk to Redis directly — the code mint goes through `admin_api`, which writes to the shared `oauth:code:*` keys that `apps/api` reads during `/oauth/token`. See [services/oauth.md](../services/oauth.md) for the full flow.

## Other Artifacts in `webapps/`

- `webapps/src/` — shared source (components, API client, server helpers) imported by `arda`
- `webapps/src/server/api.js` — defines `ensureAuthenticated`, `ensureModerator`, and the various `adminApiServer*Request` proxy helpers
- `webapps/src/server/admin.js` — the actual proxy forwarders
- `webapps/src/server/oauth.js` — OAuth consent-UI handlers (authorize + decision)
- `webapps/src/server/oauthCodes.js` — arda-local authorization-code minter (stays in lockstep with `auth/OAuthCodes.ts`)
- `webapps/src/api/api.js` — client-side API wrapper used by React components

## Further reading

- The admin-API endpoints Arda ultimately calls → [services/admin-api.md](../services/admin-api.md)
- The cloud-admin endpoints Arda calls → [services/cloud-api.md](../services/cloud-api.md)
- Auth details (session cookie ↔ access token) → [libraries/auth.md](../libraries/auth.md)
- OAuth 2.0 provider surface (consent, client CRUD, token exchange) → [services/oauth.md](../services/oauth.md)
- A Python SDK that exercises the same admin-API for external scripts → [clients/python/admin-api.md](../../clients/python/admin-api.md)
