Aman-Mittal opened a new issue, #436:
URL: https://github.com/apache/fineract-backoffice-ui/issues/436
## Problem
Downstream deployments want their own colours, logo, product name and menu,
and want to take
upstream releases as a version bump. Today every one of those changes means
editing a file
upstream also edits, so the deployment becomes a fork that conflicts on each
release.
The goal is not a better rebase — it is that the common case needs no
repository at all:
```dockerfile
FROM apache/fineract-backoffice-ui:1.0.0
COPY branding/ /usr/share/nginx/html/branding/
```
The design test for every customization point: **does upstream ever write
this file?** If yes, a
downstream editing it will conflict, however configurable it looks.
Much of the machinery is already here — a runtime `config.json` merged over
compiled defaults, a
complete design-token layer in `_common.scss` that Ionic reads through
`_ionic-theme.scss`, the
adapter boundary from ADR-0003, and a `NAV_OVERRIDES` token already in front
of the navigation
tree. The gap is that these stop one step short of the deployment.
## Defects found while scoping (all silent failures)
**A. `labelKey` is not a stable identity.** `NavOverrides.hidden` matches on
`labelKey`, but that
is a field upstream changes freely — the tree already mixes `nav.dashboard`,
`SIDEBAR.SEARCH`,
`LOAN_ACCOUNT_LOCK.TITLE` and the raw literal `'Campaigns'`. After a rename,
a deployment's
`hidden` entry stops matching and the menu they meant to suppress reappears
in production with no
error. Dividers are `{ labelKey: '', divider: true }`, so `"hidden": [""]`
removes every divider
at once.
*This one is time-sensitive.* The tree is at `1.0.0-rc.1` with nothing
released, so re-keying
`hidden` onto a stable `id` costs nothing today and becomes a breaking
change to a documented
config format the day 1.0.0 ships.
**B. The container entrypoint overwrites deployment config.**
`deploy/entrypoint.sh` writes
`config.json` whole. Its comment states "every key now has a default here…
no key can go missing",
but the heredoc emits five keys and omits `allowedApiOrigins`,
`institutionFeatures` and `nav`. A
deployment that bakes any of those into the image loses them at container
start —
`allowedApiOrigins` backs a security control, so this is more than cosmetic.
**C. The released image ships a federation host with no remote behind it.**
`deploy/Dockerfile`
runs `ng build` on the default project only and never builds or copies
`fineract-mfe`;
`src/main.ts` calls `initFederation` unconditionally with
`'./remoteEntry.json'`; and
`nginx.conf.template` ends in `try_files $uri $uri/ /index.html`, so that
path returns
`index.html` with a `200` rather than a 404. Federation therefore parses
HTML as JSON on every
page load. The `.catch(...).then(() => import('./bootstrap'))` chain absorbs
it so the app still
boots, but it puts a parse error in every production console and ships a
demo scaffold in a
release artifact. Separately, `isLocalDevServer` keys on `location.hostname
=== 'localhost'`, so
running the released container on localhost requests `http://localhost:4201`
— refused by
`connect-src 'self'`.
*(Traced through the build path by inspection; not yet reproduced against a
running image.)*
## Scope for 1.0
Filtered on three questions: does it add a dependency (no new npm package
**and** no reliance on a
service outside the container), does it widen the deployed CSP or reach an
external origin, and
does it freeze a contract we owe compatibility on. Everything below is
zero-dependency,
same-origin, and a no-op when the deployment supplies nothing.
- [ ] Stable `id` on every `NavItemConfig`; `hidden` re-keyed onto it;
`scripts/check-nav-ids.mjs`
enforcing presence, uniqueness and stability
- [ ] Layered config resolution with a deployment-owned
`branding/config.json` overlay
(404-tolerant, deep-merged) — fixes B by construction
- [ ] Federation remote map made conditional, so an image with no plugins
initialises empty and
silent — fixes C
- [ ] `BrandingService`: token allow-list, logo, app name, favicon, with a
WCAG 4.5:1 contrast
floor and fallback. Same-origin assets only, so `img-src 'self'` is
untouched
- [ ] Navigation composition — `hidden`, per-entry `overrides` (rename /
re-icon / reorder /
re-parent), and deployment-added `items`, all keyed on `id` and
passing the same permission
and feature gates as built-in entries
- [ ] External nav entries that open in a new tab. A top-level link
navigation is not a fetch,
script load or frame, so no CSP change is involved
- [ ] Deployment i18n overlay merged over the shipped catalogue, via the
`setTranslation(…, merge)`
already on the `I18N` adapter contract
- [ ] `/branding/` reserved and gitignored, with CI failing any PR that adds
a tracked file there
- [ ] Published JSON Schema so the overlay autocompletes and validates in an
editor
- [ ] Reference-downstream fixture in CI: a maximally customized overlay
loaded on every PR,
asserting every id still resolves and every token still applies
- [ ] Startup diagnostics for unknown ids and unrecognised tokens — a typo
must not present as a
missing feature
## Deferred past 1.0, with reasons
| Item | Why it waits |
|---|---|
| Published plugin host API | A host contract in 1.0.0 is a compatibility
obligation for the life of the major, and it has had no external consumer to
validate it. Wants its own artifact and cadence. |
| Federated remotes read from config | Inert without the API above — a
mounted remote could read no session, raise no dialog, share no HTTP chain.
Cross-origin also needs `script-src`. |
| Iframe nav entries | Needs `frame-src`. The new-tab form ships in 1.0 and
covers the common case. |
| External-origin logos | Needs `img-src`. Mounting under `/branding/`
covers it. |
| Branding fetched from a URL | Needs `connect-src` plus a running service.
When it lands it must stay optional and provider-agnostic — presentation config
must not require a backend. |
Notably, **everything deferred is developer-tier**. No deferred item is
something a non-developer
could have used, so the configure-only tier ships complete in 1.0.
## Note
`/admin/wc-cob-tools` is currently cross-listed under both Working Capital
and Admin. That looks
deliberate, but it means the same route has two nav entries and appears
twice in
`searchRoutes()` / `navDestinationsForPermissions()` results. Worth
confirming it is intended.
## Open questions
1. How wide is the published token allow-list? It is the only permanent
contract in the 1.0
scope. Suggest starting with the `:root` block in `_common.scss` and
nothing else.
2. Nav ids derived from route, or always explicit? Group headers have no
route, so a hybrid means
two rules. Suggest explicit, enforced by CI.
3. Fix C by making the remote conditional, or by dropping `fineract-mfe`
from the release
entirely? Either is defensible; shipping as-is is not.
4. Do overrides need an in-app editor at 1.0? A schema plus editor
autocomplete may be enough, and
an admin screen writing config back re-opens the backend-dependency
question.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]