docs(technical.profile): stub Profile SPA module (frontend-only, deploy, default-landing)
Profile had no technical.* module. Documents: frontend-only SPA (hiveiq-profile-frontend :5184, no backend/DB — composes auth/mgmt/messaging), PROFILE_VERSION sha-pinning + single-instance recreate, the profile.bcos.cloud->www.bcos.cloud root-redirect red herring, and that Profile is the sole is_default module (browser landing page). Inbox unread badge cross-links [profile.inbox] rather than duplicating. Captured from the 2026-07-17 prod deploy.
This commit is contained in:
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
module: technical.profile
|
||||||
|
title: Profile — user account & settings SPA
|
||||||
|
tab: Gotchas
|
||||||
|
order: 40
|
||||||
|
audience: dev
|
||||||
|
---
|
||||||
|
|
||||||
|
# Profile gotchas & diagnostics
|
||||||
|
|
||||||
|
## `profile.bcos.cloud/` root returns 302 → `www.bcos.cloud` — this is normal
|
||||||
|
|
||||||
|
A bare/direct hit on the root **redirects to the marketing site** (`www.bcos.cloud`). It's
|
||||||
|
intentional nginx routing — `00-redirect.conf` bounces `report` / `profile` / `logs` roots to
|
||||||
|
marketing. **Do not read this 302 as an outage or a broken deploy.** The real access path is the
|
||||||
|
HiveIQ Browser loading the module URL *with auth context* post-login, not an anonymous root hit.
|
||||||
|
|
||||||
|
To verify the SPA is actually healthy, check the container, not the public root:
|
||||||
|
- `docker ps | grep profile-frontend` → healthy, on the expected `:<sha>`.
|
||||||
|
- `curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:5184/` → **200** (container serves fine).
|
||||||
|
- Confirm the deployed build by grepping the served bundle inside the container, e.g.
|
||||||
|
`docker exec hiveiq-profile-frontend grep -rl '<marker>' /app/dist` — a `200` alone proves nothing
|
||||||
|
(an SPA returns `200` + `index.html` for any path).
|
||||||
|
|
||||||
|
## It is frontend-only — the bug is almost always in a backend
|
||||||
|
|
||||||
|
Profile owns no data. Symptom-to-owner:
|
||||||
|
- Inbox empty / unread count wrong / messages missing → **hiveops-messaging** (8086). See [profile.inbox].
|
||||||
|
- Preferences not saving (theme, `inboxPollSeconds`, notifications) → **hiveops-auth** (prefs are auth-owned).
|
||||||
|
- Password/MFA/backup-code issues → **hiveops-auth**. Institution dropdown empty → **hiveops-mgmt**.
|
||||||
|
|
||||||
|
## Deploy is single-instance (no blue/green)
|
||||||
|
|
||||||
|
Recreating `hiveiq-profile-frontend` briefly drops `profile.bcos.cloud`. Pin
|
||||||
|
**`PROFILE_VERSION=<sha>`** in the services `.env` (immutable tag); unset = mutable `:latest`. Keep a
|
||||||
|
rollback reference before recreating — capture the current image ID
|
||||||
|
(`docker inspect hiveiq-profile-frontend --format '{{.Image}}'`), since the previous `:latest` tag is
|
||||||
|
mutable and may not cleanly re-address the old build.
|
||||||
|
|
||||||
|
## The Inbox unread badge lives in the SPA docs, not here
|
||||||
|
|
||||||
|
The sidebar unread-badge feature (nav badge on the Inbox item, `#ef4444`, `inboxPollSeconds` poll,
|
||||||
|
capped `99+`, silent-on-failure) is documented in **[profile.inbox]** (Claude tab: "Sidebar unread
|
||||||
|
badge", added 2026-07-16). The count comes from `GET /api/messaging/messages/unread/count`
|
||||||
|
(hiveops-messaging, cached per userId). Don't duplicate it here.
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
---
|
||||||
|
module: technical.profile
|
||||||
|
title: Profile — user account & settings SPA
|
||||||
|
tab: Overview
|
||||||
|
order: 10
|
||||||
|
audience: dev
|
||||||
|
---
|
||||||
|
|
||||||
|
# Profile (`hiveops-profile`)
|
||||||
|
|
||||||
|
> **DRAFT · internal.** Written 2026-07-17 from `hiveops-profile` source + the prod deploy on that
|
||||||
|
> date. Verify against code before relying on specifics.
|
||||||
|
|
||||||
|
A **frontend-only SPA** — the user's account/settings surface (`profile.bcos.cloud`). It has **no
|
||||||
|
dedicated backend and no database of its own**; it composes existing services. If data misbehaves,
|
||||||
|
the Profile app is almost never the cause — check the owning backend. See [technical.profile] Gotchas.
|
||||||
|
|
||||||
|
## Responsibility
|
||||||
|
|
||||||
|
Served by container **`hiveiq-profile-frontend`** on port **5184** (nginx serving `/app/dist`). Built
|
||||||
|
with Vite; `VITE_AUTH_URL` (`auth.bcos.cloud`) and `VITE_API_URL` (`api.bcos.cloud`) are baked at
|
||||||
|
build time. It aggregates:
|
||||||
|
|
||||||
|
| Surface | Owning service | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| Inbox / messages / unread count | **hiveops-messaging** (8086, `hiveiq_messaging`) | see [profile.inbox] |
|
||||||
|
| User preferences (incl. `inboxPollSeconds`, display, appearance, notifications) | **hiveops-auth** | prefs are auth-owned, not profile |
|
||||||
|
| Security (password, MFA/TOTP, backup codes) | **hiveops-auth** | see [profile.security] |
|
||||||
|
| Institution dropdown (admin compose) | **hiveops-mgmt** `GET /api/v1/msp/institutions` | MSP_ADMIN/BCOS_ADMIN only |
|
||||||
|
|
||||||
|
Feature docs live in the SPA modules: [profile.inbox] · [profile.notifications] · [profile.security]
|
||||||
|
· [profile.display] · [profile.appearance] · [profile.profile]. **Do not add message/pref tables or
|
||||||
|
endpoints to profile** — messaging and auth own that data. See [platform.service-ownership].
|
||||||
|
|
||||||
|
## It is the default landing page
|
||||||
|
|
||||||
|
Profile is the **sole module flagged `is_default`** in mgmt `app_modules` (every other module is
|
||||||
|
`false`). The HiveIQ Browser's `getHomeUrl()` (`getDefaultModule()` → the first `isDefault` module)
|
||||||
|
therefore resolves to Profile, so **logged-in users land on the Profile page after login** — which is
|
||||||
|
why the Inbox unread badge is the first thing a user can see. Exceptions: `analyticsOnly` whitelabel
|
||||||
|
tenants land on Analytics, and a user must have Profile in their effective module set (it is
|
||||||
|
`is_core`, so normally everyone does). See [technical.browser] and [technical.mgmt] (module
|
||||||
|
allocation, `AppModuleService`).
|
||||||
|
|
||||||
|
## Build & deploy
|
||||||
|
|
||||||
|
- **Image:** `registry.bcos.cloud/hiveiq-profile-frontend`. Compose service `hiveiq-profile-frontend`
|
||||||
|
in `instances/services/compose/frontends.yml` (merged into the services project via `include`).
|
||||||
|
- **Version pin:** `image: ${REGISTRY_URL}/hiveiq-profile-frontend:${PROFILE_VERSION:-latest}`. Set
|
||||||
|
**`PROFILE_VERSION=<sha>`** in the services `.env` — an immutable tag per the prod convention (the
|
||||||
|
git short-sha of the release-to-main commit, e.g. `63712fb`). Left unset it silently resolves to
|
||||||
|
mutable `:latest`; the prod deploy on 2026-07-17 pinned it off `:latest` for the first time.
|
||||||
|
- **Deploy:** on the services VM (`173.231.195.250`), `~/hiveiq/hiveops-openmetal/hiveops/instances/services`:
|
||||||
|
```bash
|
||||||
|
# set PROFILE_VERSION=<sha> in .env, then:
|
||||||
|
docker compose pull hiveiq-profile-frontend
|
||||||
|
docker compose up -d hiveiq-profile-frontend
|
||||||
|
```
|
||||||
|
- **Single instance — NOT blue/green.** A recreate is a few seconds of `profile.bcos.cloud`
|
||||||
|
unavailability. Serialize with any other operator touching the shared services `.env`/tree.
|
||||||
|
|
||||||
|
See [technical.profile] Gotchas for the `profile.bcos.cloud → www.bcos.cloud` root-redirect red herring
|
||||||
|
and the "it's frontend-only, check the backend" rule.
|
||||||
Reference in New Issue
Block a user