@adobedjangir/commerce-admin-management 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +637 -0
  2. package/actions/commerce-creds.js +262 -0
  3. package/actions/configurations/commerce/index.js +41 -0
  4. package/actions/configurations/commerce-connection-save/index.js +53 -0
  5. package/actions/configurations/commerce-connection-status/index.js +27 -0
  6. package/actions/configurations/commerce-connection-test/index.js +47 -0
  7. package/actions/configurations/export-config/index.js +256 -0
  8. package/actions/configurations/ext.config.yaml +192 -0
  9. package/actions/configurations/import-config/index.js +541 -0
  10. package/actions/configurations/registration/index.js +37 -0
  11. package/actions/configurations/sync-store-mappings-from-commerce/index.js +190 -0
  12. package/actions/configurations/system-config-list/index.js +127 -0
  13. package/actions/configurations/system-config-save/index.js +160 -0
  14. package/actions/configurations/system-config-schema/index.js +327 -0
  15. package/actions/utils.js +73 -0
  16. package/package.json +80 -0
  17. package/scripts/build-web.js +58 -0
  18. package/scripts/setup.js +445 -0
  19. package/src/abdb-config.js +8 -0
  20. package/src/abdb-helper.js +8 -0
  21. package/src/index.js +22 -0
  22. package/src/oauth1a.js +8 -0
  23. package/src/system-config-crypto.js +8 -0
  24. package/src/system-config-shared.js +8 -0
  25. package/web/dist/index.css +305 -0
  26. package/web/dist/index.js +2986 -0
  27. package/web/index.js +7 -0
  28. package/web/src/components/App.js +54 -0
  29. package/web/src/components/AppSectionNav.js +49 -0
  30. package/web/src/components/CommerceSetupWizard.js +160 -0
  31. package/web/src/components/ExtensionRegistration.js +33 -0
  32. package/web/src/components/MainPage.js +147 -0
  33. package/web/src/components/SystemConfig.js +1464 -0
  34. package/web/src/components/SystemConfigSchemaEditor.js +459 -0
  35. package/web/src/hooks/useConfirm.js +355 -0
  36. package/web/src/hooks/useSystemConfig.js +238 -0
  37. package/web/src/hooks/useSystemConfigSchema.js +102 -0
  38. package/web/src/index.js +46 -0
  39. package/web/src/nav-icons.js +30 -0
  40. package/web/src/nav.json +10 -0
  41. package/web/src/pages/index.js +17 -0
  42. package/web/src/schema/systemConfigSchema.js +82 -0
  43. package/web/src/settings.js +101 -0
  44. package/web/src/styles/index.css +337 -0
  45. package/web/src/theme.js +104 -0
  46. package/web/src/utils/storeMappingsFromCommerceRest.js +73 -0
  47. package/web/src/utils.js +52 -0
  48. package/web/styles.css +337 -0
package/README.md ADDED
@@ -0,0 +1,637 @@
1
+ # @adobedjangir/commerce-admin-management — Developer Guide
2
+
3
+ A turnkey Adobe App Builder package for **schema-driven system configuration**
4
+ in Adobe Commerce extensions. Provides encrypted scoped storage, a React-Spectrum
5
+ admin UI mounted inside the Commerce Admin, OAuth1a helpers, import/export,
6
+ and an extension point for host apps to add their own pages and actions.
7
+
8
+ ---
9
+
10
+ ## Table of contents
11
+
12
+ 1. [What you get](#what-you-get)
13
+ 2. [Quick start](#quick-start)
14
+ 3. [What `npm install` scaffolds for you](#what-npm-install-scaffolds-for-you)
15
+ 4. [Required `.env` values](#required-env-values)
16
+ 5. [Architecture](#architecture)
17
+ 6. [Built-in actions](#built-in-actions)
18
+ 7. [Storage model](#storage-model)
19
+ 8. [Encryption](#encryption)
20
+ 9. [Commerce connection flow](#commerce-connection-flow)
21
+ 10. [Adding a host page (UI tab)](#adding-a-host-page-ui-tab)
22
+ 11. [Adding a host action (backend)](#adding-a-host-action-backend)
23
+ 12. [Reusable helpers exported by the package](#reusable-helpers-exported-by-the-package)
24
+ 13. [Configuration override matrix](#configuration-override-matrix)
25
+ 14. [Common operations](#common-operations)
26
+ 15. [Troubleshooting](#troubleshooting)
27
+
28
+ ---
29
+
30
+ ## What you get
31
+
32
+ | Capability | Where it lives |
33
+ |---|---|
34
+ | Schema-driven config form (sections / groups / fields) | UI: `SystemConfig` page (built-in tab) |
35
+ | Per-scope values (`default → websites → stores`) with Magento-style inheritance | Action: `system-config-list` / `system-config-save` |
36
+ | AES-256-GCM encryption of sensitive fields at rest | `system-config-crypto` module |
37
+ | Adobe Commerce REST/OAuth1a client helper | `commerce-creds` module |
38
+ | First-run setup wizard for Commerce credentials (saved encrypted in ABDB) | UI: `CommerceSetupWizard` |
39
+ | Bulk JSON export/import with cross-environment website/store id remapping | Actions: `export-config`, `import-config` |
40
+ | App Builder Database (ABDB) auto-provisioning on deploy | `ext.config.yaml` |
41
+ | Host-extensible nav and pages | `configureWeb({ extraNav, extraPages })` |
42
+ | Auto-scaffolded host scaffold on `npm install` | `scripts/setup.js` (postinstall hook) |
43
+
44
+ ---
45
+
46
+ ## Quick start
47
+
48
+ ```bash
49
+ # 1. Create a new App Builder project (skip if you already have one)
50
+ aio app init my-commerce-admin
51
+ cd my-commerce-admin
52
+
53
+ # 2. Install the package — postinstall scaffolds everything
54
+ npm install @adobedjangir/commerce-admin-management
55
+
56
+ # 3. Fill in .env (see required values below)
57
+ cp env.dist .env
58
+ $EDITOR .env
59
+
60
+ # 4. Deploy. ABDB is auto-provisioned; 11 actions + web assets ship to CDN.
61
+ aio app deploy
62
+ ```
63
+
64
+ Open the URL the deploy prints. First run shows the Commerce setup wizard;
65
+ enter your store URL + OAuth1a creds, click **Test connection**, then
66
+ **Save & continue**. The rest of the UI is then unlocked.
67
+
68
+ For local dev:
69
+
70
+ ```bash
71
+ aio app run
72
+ # → https://localhost:9080
73
+ ```
74
+
75
+ ---
76
+
77
+ ## What `npm install` scaffolds for you
78
+
79
+ The package's `postinstall` runs `scripts/setup.js`, which idempotently
80
+ materializes a host scaffold:
81
+
82
+ | Path | First install | Subsequent installs |
83
+ |---|---|---|
84
+ | `app.config.yaml` | Adds `extensions: commerce/backend-ui/1: $include …` if absent | No-op |
85
+ | `web-src/src/index.js` | Writes the package-wired bootstrap | Re-syncs if the marker is present; left alone if the host imported `@adobedjangir/commerce-admin-management/web` and removed the marker |
86
+ | `web-src/src/nav.json` | Writes a starter entry → `#/welcome` | Never overwritten |
87
+ | `web-src/src/pages/index.js` | Writes a registry that imports `Welcome` | Never overwritten |
88
+ | `web-src/src/pages/Welcome.js` | Writes a React-Spectrum starter page | Never overwritten |
89
+
90
+ > The "never overwritten" rule means you can edit those files freely;
91
+ > upgrading the package will not lose your changes.
92
+
93
+ The package also runs `scripts/build-web.js`, producing
94
+ `node_modules/@adobedjangir/commerce-admin-management/web/dist/index.{js,css}` so the host
95
+ bundle has a ready-to-consume artifact.
96
+
97
+ ---
98
+
99
+ ## Required `.env` values
100
+
101
+ | Variable | Purpose |
102
+ |---|---|
103
+ | `OAUTH_CLIENT_ID` | Adobe IMS Server-to-Server credentials — for ABDB token |
104
+ | `OAUTH_CLIENT_SECRET` | same |
105
+ | `OAUTH_ORG_ID` | same |
106
+ | `OAUTH_SCOPES` | should include `AdobeID, openid, read_organizations, additional_info.projectedProductContext, additional_info.roles, adobeio_api, read_client_secret, manage_client_secrets, event_receiver_api, commerce.accs` |
107
+ | `AIO_DB_REGION` | One of `amer \| emea \| apac \| aus`. Must match the region where the App Builder Database service is entitled. |
108
+ | `SYSTEM_CONFIG_CRYPT_KEY` | At least 8 chars. **Generate once with `openssl rand -base64 32` and never rotate** — rotating breaks every encrypted value already in ABDB. |
109
+
110
+ Commerce REST creds (`COMMERCE_BASE_URL`, `COMMERCE_CONSUMER_KEY`, …) are
111
+ **no longer set via `.env`**. They are entered in the in-app wizard and
112
+ encrypted into ABDB under `default/_system/commerce/connection`.
113
+
114
+ ---
115
+
116
+ ## Architecture
117
+
118
+ ```
119
+ ┌─────────────────────────────────────────────────────────────┐
120
+ │ Adobe Commerce Admin ← iframe ← Experience Cloud Shell │
121
+ └─────────────────────────────────────────────────────────────┘
122
+
123
+
124
+ https://<workspace>.adobeio-static.net
125
+
126
+
127
+ React app (@adobedjangir/commerce-admin-management/web/dist/index.js)
128
+
129
+ ┌───────────────┼───────────────┐
130
+ ▼ ▼ ▼
131
+ HashRouter AppSectionNav MainPage (gated on connection status)
132
+
133
+
134
+ getNavItems() + getPageComponent(id)
135
+
136
+
137
+ <Page runtime ims> ─→ callAction(props, key, op, body)
138
+
139
+
140
+ POST /api/v1/web/<package>/<action>
141
+
142
+
143
+ OpenWhisk action handlers
144
+
145
+ ┌───────────────┼───────────────┐
146
+ ▼ ▼ ▼
147
+ ABDB Commerce REST/OAuth1a IMS
148
+ (system_config_data, system_config_schema, _system/*)
149
+ ```
150
+
151
+ ---
152
+
153
+ ## Built-in actions
154
+
155
+ All under runtime package **`CommerceAdminManagement`**.
156
+
157
+ | Action | Verb | Description |
158
+ |---|---|---|
159
+ | `commerce-rest-get` | POST | Proxy any Commerce REST GET via OAuth1a using stored creds |
160
+ | `commerce-connection-status` | POST | `{ configured: bool, creds: masked }` |
161
+ | `commerce-connection-test` | POST | Verifies form values OR saved creds against `store/storeConfigs` |
162
+ | `commerce-connection-save` | POST | Tests then encrypts + upserts creds in ABDB |
163
+ | `system-config-list` | POST | Reads paths with Magento-style scope inheritance, decrypts sensitive fields |
164
+ | `system-config-save` | POST | Upserts values, encrypts sensitive paths |
165
+ | `system-config-schema` | POST | CRUD on the schema document |
166
+ | `export-config` | POST | Dumps schema + values (sensitive decrypted) as portable JSON |
167
+ | `import-config` | POST | Re-imports a dump, re-encrypts with the target's key, remaps website/store ids via Commerce REST |
168
+ | `sync-store-mappings-from-commerce` | POST | Refreshes the `general/settings/store_mappings` blob from Commerce |
169
+
170
+ All actions are `require-adobe-auth: false` and `include-ims-credentials: true`,
171
+ so they receive the host's OAuth credentials via env-injection — they don't
172
+ require a user IMS token.
173
+
174
+ ---
175
+
176
+ ## Storage model
177
+
178
+ ABDB collection `system_config_data` — one document per (scope, scope_id, path):
179
+
180
+ ```json
181
+ {
182
+ "_id": "sysconfig__default__0__campaign_general__url__url",
183
+ "scope": "default",
184
+ "scope_id": "0",
185
+ "path": "campaign_general/url/url",
186
+ "value": "https://example.com",
187
+ "createdAt": "2026-01-04T...",
188
+ "updatedAt": "2026-06-19T..."
189
+ }
190
+ ```
191
+
192
+ - `_id` is `sysconfig__<scope>__<scopeId>__<path-with-/-as-__>`
193
+ - Inheritance chain: `stores:<storeId> → websites:<websiteId> → default:0`
194
+ - Sensitive `value`s carry the prefix `enc:v1:<salt>:<iv>:<tag>:<ct>` (all
195
+ base64url) — see [Encryption](#encryption).
196
+
197
+ Schema is stored separately in `system_config_schema` (single doc `_id: 'v1'`).
198
+
199
+ ---
200
+
201
+ ## Encryption
202
+
203
+ `SYSTEM_CONFIG_CRYPT_KEY` is the master secret for AES-256-GCM.
204
+
205
+ ```js
206
+ const { encrypt, decrypt, isEncrypted } = require('@adobedjangir/commerce-admin-management/crypto')
207
+
208
+ encrypt('hello', params) // → 'enc:v1:...:...:...:...'
209
+ decrypt('enc:v1:...', params) // → 'hello'
210
+ isEncrypted('enc:v1:...') // → true
211
+ ```
212
+
213
+ - Per-record `salt` ⇒ same plaintext under same key produces a different
214
+ ciphertext each save (no oracle attacks).
215
+ - Wire format is versioned (`v1`) so the algorithm can be rotated without
216
+ breaking existing values.
217
+ - A fallback to `OAUTH_CLIENT_SECRET` exists for first-touch convenience but
218
+ is **strongly discouraged in production** — your IMS client secret rotates;
219
+ your at-rest data must not.
220
+
221
+ ---
222
+
223
+ ## Commerce connection flow
224
+
225
+ ```
226
+ First load ──→ GET /commerce-connection-status {configured: false}
227
+
228
+
229
+ CommerceSetupWizard
230
+
231
+ User fills form ──→ POST /commerce-connection-test {ok: true|false}
232
+ User clicks Save ──→ POST /commerce-connection-save
233
+
234
+ ▼ encrypt(JSON.stringify(creds), SYSTEM_CONFIG_CRYPT_KEY)
235
+ │ upsert ABDB at default/_system/commerce/connection
236
+
237
+ App unlocks → System Config tab visible
238
+ ```
239
+
240
+ Any action that needs Commerce REST calls
241
+ `getCommerceCreds(params, logger)` or
242
+ `getStoredCommerceOauthClient(params, logger)`, which read+decrypt the blob.
243
+ A 5-minute in-process cache avoids repeating the lookup per request.
244
+
245
+ A **Reconfigure Commerce** button in the top-right of the nav lets operators
246
+ edit/replace the creds later.
247
+
248
+ ---
249
+
250
+ ## Adding a host page (UI tab)
251
+
252
+ Three edits, no rebuild of the package:
253
+
254
+ ### 1. Create the page
255
+
256
+ ```jsx
257
+ // web-src/src/pages/Orders.js
258
+ import React from 'react'
259
+ import { View, Heading } from '@adobe/react-spectrum'
260
+
261
+ export default function Orders ({ runtime, ims }) {
262
+ return (
263
+ <View padding="size-400">
264
+ <Heading level={2}>Orders</Heading>
265
+ </View>
266
+ )
267
+ }
268
+ ```
269
+
270
+ ### 2. Register it
271
+
272
+ ```js
273
+ // web-src/src/pages/index.js
274
+ import Welcome from './Welcome'
275
+ import Orders from './Orders'
276
+
277
+ const pages = {
278
+ welcome: Welcome,
279
+ orders: Orders
280
+ }
281
+
282
+ export default pages
283
+ ```
284
+
285
+ ### 3. Add a nav entry
286
+
287
+ ```json
288
+ // web-src/src/nav.json
289
+ {
290
+ "items": [
291
+ { "id": "welcome", "path": "/welcome", "label": "Welcome", "icon": "Folder" },
292
+ { "id": "orders", "path": "/orders", "label": "Orders", "icon": "ShoppingCart" }
293
+ ]
294
+ }
295
+ ```
296
+
297
+ `aio app run` → the tab appears. The page receives `{ runtime, ims }` as
298
+ props for use with `callAction`.
299
+
300
+ **Icon names** come from a Spectrum-icon registry in
301
+ [`web/src/nav-icons.js`](web/src/nav-icons.js). Built-in: `Settings`,
302
+ `Properties`, `Data`, `User`, `ShoppingCart`, `Box`, `Folder`. To add more,
303
+ extend that file and rebuild the package (`node scripts/build-web.js`).
304
+
305
+ ### Override a built-in page
306
+
307
+ If you want to replace the built-in **System Configurations** with your own
308
+ version, just put an entry with the same id in your host registry:
309
+
310
+ ```js
311
+ const pages = { 'system-config': MyOwnSystemConfig, ... }
312
+ ```
313
+
314
+ The host always wins on id collision.
315
+
316
+ ---
317
+
318
+ ## Adding a host action (backend)
319
+
320
+ Adobe App Builder merges `application.runtimeManifest` from `app.config.yaml`
321
+ with the extension's manifest, so host-defined actions live next to the
322
+ package's without any wiring step.
323
+
324
+ ### 1. Write the action
325
+
326
+ ```js
327
+ // actions/reports-list/index.js
328
+ const { Core } = require('@adobe/aio-sdk')
329
+ const { errorResponse } = require('@adobedjangir/commerce-admin-management/actions/utils')
330
+ const {
331
+ getStoredCommerceOauthClient
332
+ } = require('@adobedjangir/commerce-admin-management/actions/commerce-creds')
333
+
334
+ async function main (params) {
335
+ const logger = Core.Logger('reports-list', { level: params.LOG_LEVEL || 'info' })
336
+ try {
337
+ const oauth = await getStoredCommerceOauthClient(params, logger)
338
+ const orders = await oauth.get('orders?searchCriteria[pageSize]=10')
339
+ return { statusCode: 200, body: { ok: true, items: orders.items || [] } }
340
+ } catch (e) {
341
+ if (e.code === 'COMMERCE_NOT_CONFIGURED') {
342
+ return errorResponse(412, e.message, logger)
343
+ }
344
+ return errorResponse(500, e.message, logger)
345
+ }
346
+ }
347
+
348
+ exports.main = main
349
+ ```
350
+
351
+ ### 2. Declare it in `app.config.yaml`
352
+
353
+ ```yaml
354
+ application:
355
+ runtimeManifest:
356
+ packages:
357
+ HostActions:
358
+ license: Apache-2.0
359
+ actions:
360
+ reports-list:
361
+ function: actions/reports-list/index.js
362
+ web: 'yes'
363
+ runtime: 'nodejs:20'
364
+ inputs:
365
+ LOG_LEVEL: debug
366
+ SYSTEM_CONFIG_CRYPT_KEY: $SYSTEM_CONFIG_CRYPT_KEY
367
+ OAUTH_CLIENT_ID: $OAUTH_CLIENT_ID
368
+ OAUTH_CLIENT_SECRET: $OAUTH_CLIENT_SECRET
369
+ OAUTH_ORG_ID: $OAUTH_ORG_ID
370
+ OAUTH_SCOPES: $OAUTH_SCOPES
371
+ AIO_DB_REGION: $AIO_DB_REGION
372
+ annotations:
373
+ require-adobe-auth: false
374
+ include-ims-credentials: true
375
+ final: true
376
+ ```
377
+
378
+ `aio app deploy` ships 11 package actions **+** your `HostActions/reports-list`
379
+ together. The action URL appears in `web-src/src/config.json` automatically.
380
+
381
+ ### 3. Call it from a host page
382
+
383
+ ```jsx
384
+ import { callAction } from '@adobedjangir/commerce-admin-management/web'
385
+
386
+ // inside the component:
387
+ const r = await callAction({ runtime, ims }, 'HostActions/reports-list', '', { /* body */ })
388
+ console.log(r.body)
389
+ ```
390
+
391
+ `callAction(props, key, operation, body)` POSTs to the URL stored under
392
+ `key` in `web-src/src/config.json`, adds `Bearer` + `x-gw-ims-org-id`
393
+ headers, parses the JSON response, and surfaces non-2xx as thrown errors
394
+ with `err.status` and `err.response`.
395
+
396
+ ---
397
+
398
+ ## Reusable helpers exported by the package
399
+
400
+ ### Backend (Node)
401
+
402
+ ```js
403
+ // ABDB client + helpers
404
+ const {
405
+ getClient, // → { client, close } using IMS S2S creds
406
+ withDbClient, // run a function with an auto-closed client
407
+ ensureImportCollectionsExist
408
+ } = require('@adobedjangir/commerce-admin-management/abdb')
409
+
410
+ // Magento-style scope helpers
411
+ const {
412
+ toStateKey, // (scope, scopeId, path) → ABDB _id
413
+ isValidPath,
414
+ normalizeScope,
415
+ normalizeScopeId,
416
+ buildInheritanceChain,
417
+ SENSITIVE_PLACEHOLDER
418
+ } = require('@adobedjangir/commerce-admin-management/shared')
419
+
420
+ // Per-record AES-256-GCM
421
+ const {
422
+ encrypt,
423
+ decrypt,
424
+ isEncrypted
425
+ } = require('@adobedjangir/commerce-admin-management/crypto')
426
+
427
+ // Commerce REST/OAuth1a
428
+ const {
429
+ getOauthClient,
430
+ getCommerceOauthClient
431
+ } = require('@adobedjangir/commerce-admin-management/oauth1a')
432
+
433
+ // Commerce credential storage (stored encrypted in ABDB)
434
+ const {
435
+ readCommerceCreds,
436
+ writeCommerceCreds,
437
+ testCommerceConnection,
438
+ getCommerceCreds,
439
+ getStoredCommerceOauthClient
440
+ } = require('@adobedjangir/commerce-admin-management/actions/commerce-creds')
441
+
442
+ // Schema-driven config lookup at runtime (used by external sync actions)
443
+ const { getConfig } = require('@adobedjangir/commerce-admin-management/config')
444
+ const value = await getConfig('campaign_general/url/url', params, { scope: 'stores', scopeCode: 'en_ch' })
445
+
446
+ // Top-level barrel:
447
+ const { getConfig, getClient, encrypt } = require('@adobedjangir/commerce-admin-management')
448
+ ```
449
+
450
+ ### Front-end (ESM)
451
+
452
+ ```js
453
+ import {
454
+ // App shell
455
+ CommerceAdminManagementApp, // full router + Spectrum provider
456
+ configureWeb, // pass actionUrls + extraNav + extraPages
457
+
458
+ // Components
459
+ MainPage,
460
+ SystemConfig,
461
+ SystemConfigSchemaEditor,
462
+ AppSectionNav,
463
+
464
+ // Registries
465
+ getNavItems,
466
+ getPageComponent,
467
+ NAV_ICONS,
468
+ getNavIcon,
469
+ BUILT_IN_PAGES,
470
+
471
+ // Hooks
472
+ useSystemConfig,
473
+ useSystemConfigSchema,
474
+ useConfirm,
475
+
476
+ // Utilities
477
+ callAction,
478
+
479
+ // Theme tokens (for styling host pages consistently)
480
+ THEME, PALETTE, RADIUS, SHADOW, SPACE, FONT
481
+ } from '@adobedjangir/commerce-admin-management/web'
482
+ import '@adobedjangir/commerce-admin-management/web/styles.css'
483
+ ```
484
+
485
+ ---
486
+
487
+ ## Configuration override matrix
488
+
489
+ | Setting | Default lives in | Override location | Override mechanism |
490
+ |---|---|---|---|
491
+ | ABDB auto-provision | package `ext.config.yaml` (`runtimeManifest.database.auto-provision: true`) | host `app.config.yaml` | `application: runtimeManifest: database: auto-provision: false` |
492
+ | ABDB region | package `ext.config.yaml` (literal `emea`) | host `app.config.yaml` | `application: runtimeManifest: database: region: amer` (and update `AIO_DB_REGION` in `.env` to match) |
493
+ | Runtime IMS token | computed by `aio-lib-core-auth` | n/a | The action runtime injects via `include-ims-credentials: true` |
494
+ | Action keys (URL paths) | `web/src/settings.js` (`DEFAULT_ACTION_KEYS`) | host bootstrap | `configureWeb({ actionKeys: { ... } })` |
495
+ | Built-in nav entries | package `web/src/nav.json` | host `web-src/src/nav.json` | extras merge after built-ins; host wins on id collision |
496
+ | Built-in pages | package `web/src/pages/index.js` | host `web-src/src/pages/index.js` | extras merge; host wins on id collision |
497
+ | Bootstrap (`web-src/src/index.js`) | package generator | own the file | Either remove the marker comment **and** import from `@adobedjangir/commerce-admin-management/web`, **or** edit freely while keeping the marker (loses on next install) |
498
+
499
+ ---
500
+
501
+ ## Common operations
502
+
503
+ ### Rotate `SYSTEM_CONFIG_CRYPT_KEY`
504
+
505
+ **Do not** unless you've planned for re-encryption — every existing encrypted
506
+ value becomes undecryptable. If you must:
507
+
508
+ 1. Export with the old key: `POST /export-config { schemaOnly: false }` — the
509
+ dump contains plaintext values for sensitive fields (v2+ dump format).
510
+ 2. Update `SYSTEM_CONFIG_CRYPT_KEY` in `.env` and redeploy.
511
+ 3. Re-import the dump: `POST /import-config { dump, overwrite: true }` — the
512
+ importer re-encrypts sensitive paths with the new key.
513
+
514
+ For cross-environment migration (different key on each side), pass
515
+ `sourceCryptKey: '<old-key>'` to `import-config` so it can decrypt the
516
+ ciphertext with the old key before re-encrypting with the new one.
517
+
518
+ ### Change region
519
+
520
+ 1. Update `AIO_DB_REGION` in `.env`.
521
+ 2. Update `region:` literal under `runtimeManifest.database` in
522
+ `app.config.yaml` (host overrides the package default).
523
+ 3. Ensure the workspace has App Builder Database entitled in that region in
524
+ the Adobe Developer Console.
525
+ 4. `aio app deploy`.
526
+
527
+ ### Promote a deploy
528
+
529
+ ```bash
530
+ aio app deploy --workspace Production
531
+ ```
532
+
533
+ Targets the named workspace's database/region/region-bound creds.
534
+ `SYSTEM_CONFIG_CRYPT_KEY` should differ per environment; use
535
+ `import-config` with `sourceCryptKey` to migrate values.
536
+
537
+ ### Add an icon for use in `nav.json`
538
+
539
+ 1. Import the icon into `packages/commerce-admin-management/web/src/nav-icons.js`
540
+ 2. Add it to the `NAV_ICONS` map
541
+ 3. `node packages/commerce-admin-management/scripts/build-web.js`
542
+ 4. Reference it in `nav.json` as `"icon": "<NewIconName>"`
543
+
544
+ ### Re-link a local package during development
545
+
546
+ If you're hacking on the package and need the host to pick up changes:
547
+
548
+ ```bash
549
+ cd packages/commerce-admin-management
550
+ node scripts/build-web.js
551
+ cd ../..
552
+ npm install ./packages/commerce-admin-management # refreshes node_modules link
553
+ rm -rf .parcel-cache dist web-src/dist
554
+ aio app run
555
+ ```
556
+
557
+ ---
558
+
559
+ ## Troubleshooting
560
+
561
+ | Symptom | Likely cause | Fix |
562
+ |---|---|---|
563
+ | `Database not provisioned` | App Builder Database not entitled on the workspace, **or** never ran `aio app deploy` since adding the package | Add the service in the Developer Console (region must match `AIO_DB_REGION`), then `aio app deploy` |
564
+ | `Invalid region '$AIO_DB_REGION'` | Tried to use `$VAR` substitution in `runtimeManifest.database.region` | aio substitutes `$VAR` only inside action `inputs:`. Use a literal region in YAML |
565
+ | Blank page at `#/<anything>` | Route table doesn't include a catch-all | Already fixed — `App.js` uses `path="*"`. If you forked it, add `<Route path="*" element={…}>` |
566
+ | Stuck on "Checking Commerce connection…" forever | Running outside Experience Cloud iframe; `@adobe/uix-guest`'s `attach()` was blocking | Already fixed — MainPage's `attach()` now races a 2-second timeout |
567
+ | Reports tab blank but System Configurations works | `pages/index.js` used CommonJS `require('./X').default` interop | Use ES `import X from './X'` instead |
568
+ | Nav button overflows below tabs | CSS not picked up | Make sure the package's `dist/index.css` is loaded (the bootstrap imports it automatically) |
569
+ | Encryption errors after env change | `SYSTEM_CONFIG_CRYPT_KEY` was rotated and there are old `enc:v1:` values | Either restore the old key, or perform the export/import re-encryption flow above |
570
+ | `npm install` overwrote my bootstrap | Marker comment still present | Remove the marker comment line, **and** ensure your file imports from `@adobedjangir/commerce-admin-management/web` so setup recognizes host-managed mode |
571
+
572
+ ---
573
+
574
+ ## File reference
575
+
576
+ ```
577
+ packages/commerce-admin-management/
578
+ ├── DEVELOPER.md ← you are here
579
+ ├── README.md ← short overview
580
+ ├── package.json
581
+ ├── src/ ← thin re-exports from @adobedjangir/commerce-admin-get-config
582
+ │ ├── abdb-helper.js
583
+ │ ├── abdb-config.js
584
+ │ ├── system-config-crypto.js
585
+ │ ├── system-config-shared.js
586
+ │ └── oauth1a.js
587
+ ├── actions/
588
+ │ ├── utils.js
589
+ │ ├── commerce-creds.js ← read/write/test encrypted creds in ABDB
590
+ │ └── configurations/
591
+ │ ├── ext.config.yaml ← runtime manifest (database + 11 actions)
592
+ │ ├── index.html
593
+ │ ├── registration/index.js
594
+ │ ├── commerce/index.js
595
+ │ ├── commerce-connection-status/index.js
596
+ │ ├── commerce-connection-test/index.js
597
+ │ ├── commerce-connection-save/index.js
598
+ │ ├── system-config-list/index.js
599
+ │ ├── system-config-save/index.js
600
+ │ ├── system-config-schema/index.js
601
+ │ ├── export-config/index.js
602
+ │ ├── import-config/index.js
603
+ │ └── sync-store-mappings-from-commerce/index.js
604
+ ├── web/
605
+ │ ├── index.js ← re-exports dist/
606
+ │ ├── styles.css ← flat fallback
607
+ │ ├── dist/ ← built by scripts/build-web.js
608
+ │ └── src/
609
+ │ ├── index.js ← package entry
610
+ │ ├── nav.json ← built-in nav entries
611
+ │ ├── nav-icons.js ← string-name → Spectrum icon map
612
+ │ ├── settings.js ← actionKeys, extensionId, configureWeb, registries
613
+ │ ├── theme.js
614
+ │ ├── utils.js ← callAction
615
+ │ ├── styles/index.css
616
+ │ ├── pages/index.js ← built-in page registry
617
+ │ ├── components/
618
+ │ │ ├── App.js ← router + Provider + ErrorBoundary
619
+ │ │ ├── MainPage.js ← gates app on Commerce connection
620
+ │ │ ├── ExtensionRegistration.js ← UIX guest registration
621
+ │ │ ├── AppSectionNav.js ← top nav from registry
622
+ │ │ ├── CommerceSetupWizard.js ← first-run wizard
623
+ │ │ ├── SystemConfig.js ← main config form
624
+ │ │ └── SystemConfigSchemaEditor.js ← schema editor
625
+ │ ├── hooks/
626
+ │ ├── schema/systemConfigSchema.js
627
+ │ └── utils/storeMappingsFromCommerceRest.js
628
+ └── scripts/
629
+ ├── build-web.js ← esbuild bundle
630
+ └── setup.js ← postinstall scaffolding
631
+ ```
632
+
633
+ ---
634
+
635
+ ## License
636
+
637
+ Apache-2.0 © Adobe Inc.