@fonderie/react-admin-screens 0.4.0 → 0.5.0
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.
- package/README.md +8 -1
- package/dist/index.cjs +141 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +75 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.js +141 -29
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Status: **experimental** (0.x).
|
|
|
14
14
|
| Money | Catalog · Subscriber | what am I selling (configured vs stored) · what is this subscriber on, their wallet, ledger, a manual grant (`billingClient`) |
|
|
15
15
|
| Settings | Config & secrets | the `@fonderie/config` admin screens, as a sub-page |
|
|
16
16
|
| Messaging | Templates | the `@fonderie/courier` admin screens, as a sub-page |
|
|
17
|
-
| Activity | Audit · Admin log · Access | what happened, every workspace (`auditClient`) · who did what through here · who can be here |
|
|
17
|
+
| Activity | Audit · Admin log · Access | what happened, every workspace (`auditClient`) · who did what through here · who can be here, and issue or revoke scoped tokens |
|
|
18
18
|
|
|
19
19
|
```ts
|
|
20
20
|
import { AdminClient, AuditAdminClient, AuthAdminClient, BillingAdminClient, ConfigAdminClient, CourierAdminClient } from '@fonderie/client';
|
|
@@ -38,6 +38,13 @@ Pass `page` and `onNavigate` to own the URL (one route per page); omit both
|
|
|
38
38
|
and the shell keeps the page itself. Settings and Messaging appear only when
|
|
39
39
|
their client is given.
|
|
40
40
|
|
|
41
|
+
**Give the shell a `read` token, not the root one.** With `@fonderie/admin`
|
|
42
|
+
≥ 0.6 the configured `adminToken` is the root — every scope, including the
|
|
43
|
+
plaintext secret reveal, and the only credential that can mint or revoke.
|
|
44
|
+
Issue a scoped one for the dashboard (`fonderie admin token issue dashboard
|
|
45
|
+
--scopes read`, or the Access page itself) and hand the shell that. The
|
|
46
|
+
Access page then shows issuing as unavailable, which is the point.
|
|
47
|
+
|
|
41
48
|
Nothing here stores the token: the app constructs the clients and decides
|
|
42
49
|
where the token lives. Every request through the shell lands in the admin
|
|
43
50
|
log, including the ones the token refuses.
|
package/dist/index.cjs
CHANGED
|
@@ -38,7 +38,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
38
38
|
// src/screens/AdminShell.tsx
|
|
39
39
|
var import_react_config_admin_screens = require("@fonderie/react-config-admin-screens");
|
|
40
40
|
var import_react_courier_admin_screens = require("@fonderie/react-courier-admin-screens");
|
|
41
|
-
var
|
|
41
|
+
var import_react5 = require("react");
|
|
42
42
|
|
|
43
43
|
// src/styles.ts
|
|
44
44
|
var styles = {
|
|
@@ -346,34 +346,146 @@ function RoutesScreen({ client }) {
|
|
|
346
346
|
|
|
347
347
|
// src/screens/TokensScreen.tsx
|
|
348
348
|
var import_react_admin7 = require("@fonderie/react-admin");
|
|
349
|
+
var import_react = require("react");
|
|
349
350
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
351
|
+
var ALL = ["read", "write", "secrets"];
|
|
350
352
|
function TokensScreen({ client }) {
|
|
351
|
-
const { report, isLoading, error } = (0, import_react_admin7.useAdminTokens)(client);
|
|
353
|
+
const { report, isLoading, error, issue, revoke } = (0, import_react_admin7.useAdminTokens)(client);
|
|
354
|
+
const [name, setName] = (0, import_react.useState)("");
|
|
355
|
+
const [scopes, setScopes] = (0, import_react.useState)(["read"]);
|
|
356
|
+
const [days, setDays] = (0, import_react.useState)("");
|
|
357
|
+
const [minted, setMinted] = (0, import_react.useState)(null);
|
|
358
|
+
const toggle = (s) => setScopes((prev) => prev.includes(s) ? prev.filter((x) => x !== s) : [...prev, s]);
|
|
352
359
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: styles.container, children: [
|
|
353
360
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h1", { style: styles.title, children: "Access" }),
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
361
|
+
error ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: styles.error, role: "alert", children: error.status === 401 ? "Issuing and revoking need the root token (the one in your deployment config)." : error.explanation }) : null,
|
|
362
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { style: styles.subtitle, children: "Root token" }),
|
|
363
|
+
report ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { children: [
|
|
364
|
+
report.admin.ok ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.ok, children: "strong" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.bad, children: "weak" }),
|
|
365
|
+
report.admin.problems.map((p) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: styles.bad, children: p.message }, p.message))
|
|
366
|
+
] }) : null,
|
|
367
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { style: styles.subtitle, children: "Issued tokens" }),
|
|
368
|
+
report?.issued === null ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: styles.muted, children: "Issuing is off \u2014 give AdminModule a store and run its migrations." }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
|
|
369
|
+
minted ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { style: styles.ok, children: [
|
|
370
|
+
"Copy this now \u2014 it is never shown again.",
|
|
363
371
|
" ",
|
|
364
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.
|
|
365
|
-
|
|
366
|
-
|
|
372
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.mono, children: minted.token }),
|
|
373
|
+
" (",
|
|
374
|
+
minted.name,
|
|
375
|
+
")"
|
|
376
|
+
] }) : null,
|
|
377
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
378
|
+
"form",
|
|
379
|
+
{
|
|
380
|
+
style: styles.toolbar,
|
|
381
|
+
onSubmit: (e) => {
|
|
382
|
+
e.preventDefault();
|
|
383
|
+
const d = Number(days);
|
|
384
|
+
void issue({
|
|
385
|
+
name: name.trim(),
|
|
386
|
+
scopes,
|
|
387
|
+
...days && Number.isInteger(d) && d > 0 ? { expiresInDays: d } : {}
|
|
388
|
+
}).then((t) => {
|
|
389
|
+
setMinted({ name: t.name, token: t.token });
|
|
390
|
+
setName("");
|
|
391
|
+
setDays("");
|
|
392
|
+
}).catch(() => {
|
|
393
|
+
});
|
|
394
|
+
},
|
|
395
|
+
children: [
|
|
396
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
397
|
+
"input",
|
|
398
|
+
{
|
|
399
|
+
value: name,
|
|
400
|
+
onChange: (e) => setName(e.target.value),
|
|
401
|
+
placeholder: "name",
|
|
402
|
+
style: { ...styles.button, cursor: "text" },
|
|
403
|
+
"aria-label": "Name"
|
|
404
|
+
}
|
|
405
|
+
),
|
|
406
|
+
ALL.map((s) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { style: { ...styles.badge, cursor: "pointer" }, children: [
|
|
407
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { type: "checkbox", checked: scopes.includes(s), onChange: () => toggle(s) }),
|
|
408
|
+
" ",
|
|
409
|
+
s
|
|
410
|
+
] }, s)),
|
|
411
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
412
|
+
"input",
|
|
413
|
+
{
|
|
414
|
+
value: days,
|
|
415
|
+
onChange: (e) => setDays(e.target.value),
|
|
416
|
+
placeholder: "days (optional)",
|
|
417
|
+
style: { ...styles.button, cursor: "text", width: 140 },
|
|
418
|
+
"aria-label": "Days"
|
|
419
|
+
}
|
|
420
|
+
),
|
|
421
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
422
|
+
"button",
|
|
423
|
+
{
|
|
424
|
+
type: "submit",
|
|
425
|
+
style: styles.button,
|
|
426
|
+
disabled: !name.trim() || scopes.length === 0 || isLoading,
|
|
427
|
+
children: "Issue"
|
|
428
|
+
}
|
|
429
|
+
)
|
|
430
|
+
]
|
|
431
|
+
}
|
|
432
|
+
),
|
|
433
|
+
report?.issued && report.issued.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("table", { style: styles.table, children: [
|
|
434
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("tr", { children: [
|
|
435
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: styles.th, children: "Name" }),
|
|
436
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: styles.th, children: "Scopes" }),
|
|
437
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: styles.th, children: "Created" }),
|
|
438
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: styles.th, children: "Expires" }),
|
|
439
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: styles.th, children: "Last used" }),
|
|
440
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("th", { style: styles.th })
|
|
441
|
+
] }) }),
|
|
442
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("tbody", { children: report.issued.map((t) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("tr", { children: [
|
|
443
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("td", { style: styles.td, children: [
|
|
444
|
+
t.name,
|
|
445
|
+
t.revokedAt ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.badge, children: " revoked" }) : null
|
|
446
|
+
] }),
|
|
447
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { style: { ...styles.td, ...styles.mono }, children: t.scopes.join(", ") }),
|
|
448
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("td", { style: { ...styles.td, ...styles.muted }, children: [
|
|
449
|
+
new Date(t.createdAt).toLocaleDateString(),
|
|
450
|
+
" \xB7 ",
|
|
451
|
+
t.createdBy
|
|
452
|
+
] }),
|
|
453
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: t.expiresAt ? new Date(t.expiresAt).toLocaleDateString() : "never" }),
|
|
454
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { style: { ...styles.td, ...styles.muted }, children: t.lastUsedAt ? new Date(t.lastUsedAt).toLocaleString() : "never" }),
|
|
455
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("td", { style: styles.td, children: t.revokedAt ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
456
|
+
"button",
|
|
457
|
+
{
|
|
458
|
+
type: "button",
|
|
459
|
+
style: styles.button,
|
|
460
|
+
onClick: () => {
|
|
461
|
+
if (window.confirm(
|
|
462
|
+
`Revoke "${t.name}"? Anything using it stops working immediately.`
|
|
463
|
+
))
|
|
464
|
+
void revoke(t.id).catch(() => {
|
|
465
|
+
});
|
|
466
|
+
},
|
|
467
|
+
children: "Revoke"
|
|
468
|
+
}
|
|
469
|
+
) })
|
|
470
|
+
] }, t.id)) })
|
|
471
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: styles.muted, children: "None issued." })
|
|
472
|
+
] }),
|
|
473
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { style: styles.subtitle, children: "Legacy per-brick tokens" }),
|
|
474
|
+
report && report.legacy.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: styles.muted, children: "None \u2014 every brick's admin surface goes through this token." }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { style: styles.list, children: report?.legacy.map((l) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("li", { style: styles.row, children: [
|
|
475
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.mono, children: l.module }),
|
|
476
|
+
" ",
|
|
477
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: styles.muted, children: "still registers its standalone routes with its own token (deprecated)" })
|
|
478
|
+
] }, l.module)) })
|
|
367
479
|
] });
|
|
368
480
|
}
|
|
369
481
|
|
|
370
482
|
// src/screens/UsersScreen.tsx
|
|
371
483
|
var import_react_admin8 = require("@fonderie/react-admin");
|
|
372
|
-
var
|
|
484
|
+
var import_react2 = require("react");
|
|
373
485
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
374
486
|
function UsersScreen({ client }) {
|
|
375
|
-
const [input, setInput] = (0,
|
|
376
|
-
const [email, setEmail] = (0,
|
|
487
|
+
const [input, setInput] = (0, import_react2.useState)("");
|
|
488
|
+
const [email, setEmail] = (0, import_react2.useState)(void 0);
|
|
377
489
|
const { user, isLoading, error, suspend, unsuspend, revokeSessions } = (0, import_react_admin8.useAdminUser)(
|
|
378
490
|
client,
|
|
379
491
|
email ? { email } : {}
|
|
@@ -583,16 +695,16 @@ function CatalogScreen({ client }) {
|
|
|
583
695
|
|
|
584
696
|
// src/screens/SubscriberScreen.tsx
|
|
585
697
|
var import_react_admin10 = require("@fonderie/react-admin");
|
|
586
|
-
var
|
|
698
|
+
var import_react3 = require("react");
|
|
587
699
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
588
700
|
function SubscriberScreen({ client }) {
|
|
589
|
-
const [type, setType] = (0,
|
|
590
|
-
const [idInput, setIdInput] = (0,
|
|
591
|
-
const [subscriber, setSubscriber] = (0,
|
|
701
|
+
const [type, setType] = (0, import_react3.useState)("user");
|
|
702
|
+
const [idInput, setIdInput] = (0, import_react3.useState)("");
|
|
703
|
+
const [subscriber, setSubscriber] = (0, import_react3.useState)(null);
|
|
592
704
|
const { subscription, wallet, ledger, hasMoreLedger, isLoading, error, loadMoreLedger, grant } = (0, import_react_admin10.useAdminSubscriber)(client, subscriber, { limit: 20 });
|
|
593
|
-
const [amount, setAmount] = (0,
|
|
594
|
-
const [note, setNote] = (0,
|
|
595
|
-
const [granted, setGranted] = (0,
|
|
705
|
+
const [amount, setAmount] = (0, import_react3.useState)("");
|
|
706
|
+
const [note, setNote] = (0, import_react3.useState)("");
|
|
707
|
+
const [granted, setGranted] = (0, import_react3.useState)(null);
|
|
596
708
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { style: styles.container, children: [
|
|
597
709
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h1", { style: styles.title, children: "Subscriber" }),
|
|
598
710
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
@@ -757,11 +869,11 @@ function SubscriberScreen({ client }) {
|
|
|
757
869
|
|
|
758
870
|
// src/screens/AuditScreen.tsx
|
|
759
871
|
var import_react_admin11 = require("@fonderie/react-admin");
|
|
760
|
-
var
|
|
872
|
+
var import_react4 = require("react");
|
|
761
873
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
762
874
|
function AuditScreen({ client, pageSize = 50 }) {
|
|
763
|
-
const [draft, setDraft] = (0,
|
|
764
|
-
const [filter, setFilter] = (0,
|
|
875
|
+
const [draft, setDraft] = (0, import_react4.useState)({ workspaceId: "", type: "", actorId: "" });
|
|
876
|
+
const [filter, setFilter] = (0, import_react4.useState)(
|
|
765
877
|
{}
|
|
766
878
|
);
|
|
767
879
|
const { events, hasMore, isLoading, error, refresh, loadMore } = (0, import_react_admin11.useAdminAudit)(client, {
|
|
@@ -883,13 +995,13 @@ function AdminShell({
|
|
|
883
995
|
page,
|
|
884
996
|
onNavigate
|
|
885
997
|
}) {
|
|
886
|
-
const [own, setOwn] = (0,
|
|
998
|
+
const [own, setOwn] = (0, import_react5.useState)("attention");
|
|
887
999
|
const current = page ?? own;
|
|
888
1000
|
const go = (p) => {
|
|
889
1001
|
if (onNavigate) onNavigate(p);
|
|
890
1002
|
if (page === void 0) setOwn(p);
|
|
891
1003
|
};
|
|
892
|
-
const [editing, setEditing] = (0,
|
|
1004
|
+
const [editing, setEditing] = (0, import_react5.useState)(null);
|
|
893
1005
|
const has = {
|
|
894
1006
|
config: Boolean(configClient),
|
|
895
1007
|
courier: Boolean(courierClient),
|