@fuzdev/fuz_app 0.30.0 → 0.32.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/dist/actions/CLAUDE.md +630 -0
- package/dist/actions/action_rpc.d.ts +29 -0
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +42 -6
- package/dist/actions/action_types.d.ts +2 -2
- package/dist/actions/cancel.d.ts +12 -13
- package/dist/actions/cancel.d.ts.map +1 -1
- package/dist/actions/cancel.js +10 -13
- package/dist/actions/heartbeat.d.ts +8 -13
- package/dist/actions/heartbeat.d.ts.map +1 -1
- package/dist/actions/heartbeat.js +5 -8
- package/dist/actions/register_action_ws.d.ts +3 -3
- package/dist/actions/register_action_ws.js +2 -2
- package/dist/actions/register_ws_endpoint.d.ts +4 -4
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +3 -3
- package/dist/actions/rpc_client.d.ts +29 -0
- package/dist/actions/rpc_client.d.ts.map +1 -1
- package/dist/actions/rpc_client.js +31 -0
- package/dist/actions/socket.svelte.d.ts +16 -16
- package/dist/actions/socket.svelte.d.ts.map +1 -1
- package/dist/actions/socket.svelte.js +15 -15
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/auth/CLAUDE.md +945 -0
- package/dist/auth/account_action_specs.d.ts +216 -0
- package/dist/auth/account_action_specs.d.ts.map +1 -0
- package/dist/auth/account_action_specs.js +159 -0
- package/dist/auth/account_actions.d.ts +51 -0
- package/dist/auth/account_actions.d.ts.map +1 -0
- package/dist/auth/account_actions.js +119 -0
- package/dist/auth/account_queries.d.ts +6 -2
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +40 -4
- package/dist/auth/account_routes.d.ts +94 -16
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +108 -180
- package/dist/auth/account_schema.d.ts +85 -30
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +40 -8
- package/dist/auth/admin_action_specs.d.ts +674 -0
- package/dist/auth/admin_action_specs.d.ts.map +1 -0
- package/dist/auth/admin_action_specs.js +287 -0
- package/dist/auth/admin_actions.d.ts +69 -0
- package/dist/auth/admin_actions.d.ts.map +1 -0
- package/dist/auth/admin_actions.js +256 -0
- package/dist/auth/admin_rpc_actions.d.ts +49 -0
- package/dist/auth/admin_rpc_actions.d.ts.map +1 -0
- package/dist/auth/admin_rpc_actions.js +32 -0
- package/dist/auth/api_token.d.ts +10 -0
- package/dist/auth/api_token.d.ts.map +1 -1
- package/dist/auth/api_token.js +9 -0
- package/dist/auth/api_token_queries.d.ts +3 -3
- package/dist/auth/api_token_queries.js +3 -3
- package/dist/auth/app_settings_schema.d.ts +4 -3
- package/dist/auth/app_settings_schema.d.ts.map +1 -1
- package/dist/auth/app_settings_schema.js +2 -1
- package/dist/auth/audit_log_routes.d.ts +14 -6
- package/dist/auth/audit_log_routes.d.ts.map +1 -1
- package/dist/auth/audit_log_routes.js +22 -79
- package/dist/auth/audit_log_schema.d.ts +100 -29
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +83 -11
- package/dist/auth/bootstrap_routes.d.ts +14 -0
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +10 -3
- package/dist/auth/cleanup.d.ts +63 -0
- package/dist/auth/cleanup.d.ts.map +1 -0
- package/dist/auth/cleanup.js +80 -0
- package/dist/auth/invite_schema.d.ts +11 -10
- package/dist/auth/invite_schema.d.ts.map +1 -1
- package/dist/auth/invite_schema.js +4 -3
- package/dist/auth/migrations.d.ts +6 -0
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +28 -0
- package/dist/auth/permit_offer_action_specs.d.ts +364 -0
- package/dist/auth/permit_offer_action_specs.d.ts.map +1 -0
- package/dist/auth/permit_offer_action_specs.js +216 -0
- package/dist/auth/permit_offer_actions.d.ts +96 -0
- package/dist/auth/permit_offer_actions.d.ts.map +1 -0
- package/dist/auth/permit_offer_actions.js +428 -0
- package/dist/auth/permit_offer_notifications.d.ts +361 -0
- package/dist/auth/permit_offer_notifications.d.ts.map +1 -0
- package/dist/auth/permit_offer_notifications.js +179 -0
- package/dist/auth/permit_offer_queries.d.ts +165 -0
- package/dist/auth/permit_offer_queries.d.ts.map +1 -0
- package/dist/auth/permit_offer_queries.js +390 -0
- package/dist/auth/permit_offer_schema.d.ts +103 -0
- package/dist/auth/permit_offer_schema.d.ts.map +1 -0
- package/dist/auth/permit_offer_schema.js +142 -0
- package/dist/auth/permit_queries.d.ts +77 -14
- package/dist/auth/permit_queries.d.ts.map +1 -1
- package/dist/auth/permit_queries.js +119 -24
- package/dist/auth/session_queries.d.ts +4 -2
- package/dist/auth/session_queries.d.ts.map +1 -1
- package/dist/auth/session_queries.js +4 -2
- package/dist/auth/signup_routes.d.ts +13 -0
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +14 -7
- package/dist/http/CLAUDE.md +584 -0
- package/dist/http/pending_effects.d.ts +29 -0
- package/dist/http/pending_effects.d.ts.map +1 -0
- package/dist/http/pending_effects.js +31 -0
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/route_spec.js +4 -3
- package/dist/rate_limiter.d.ts +30 -0
- package/dist/rate_limiter.d.ts.map +1 -1
- package/dist/rate_limiter.js +25 -2
- package/dist/realtime/sse_auth_guard.d.ts +2 -0
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +5 -3
- package/dist/server/app_server.d.ts +13 -2
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +12 -1
- package/dist/testing/CLAUDE.md +668 -1
- package/dist/testing/admin_integration.d.ts +10 -7
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +382 -482
- package/dist/testing/app_server.d.ts +7 -6
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/attack_surface.d.ts +9 -3
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +4 -4
- package/dist/testing/audit_completeness.d.ts +11 -0
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +169 -134
- package/dist/testing/auth_apps.d.ts.map +1 -1
- package/dist/testing/auth_apps.js +4 -33
- package/dist/testing/db.d.ts +1 -1
- package/dist/testing/db.d.ts.map +1 -1
- package/dist/testing/db.js +2 -0
- package/dist/testing/entities.d.ts +35 -13
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +17 -0
- package/dist/testing/integration.d.ts +10 -0
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +352 -340
- package/dist/testing/integration_helpers.d.ts +16 -5
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +24 -4
- package/dist/testing/rate_limiting.d.ts +7 -0
- package/dist/testing/rate_limiting.d.ts.map +1 -1
- package/dist/testing/rate_limiting.js +41 -10
- package/dist/testing/rpc_helpers.d.ts +153 -1
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_helpers.js +184 -8
- package/dist/testing/sse_round_trip.d.ts +8 -0
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +10 -3
- package/dist/testing/standard.d.ts +9 -1
- package/dist/testing/standard.d.ts.map +1 -1
- package/dist/testing/standard.js +6 -2
- package/dist/testing/stubs.d.ts +10 -2
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +17 -2
- package/dist/testing/surface_invariants.d.ts +7 -3
- package/dist/testing/surface_invariants.d.ts.map +1 -1
- package/dist/testing/surface_invariants.js +5 -4
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +9 -38
- package/dist/ui/AccountSessions.svelte +8 -4
- package/dist/ui/AccountSessions.svelte.d.ts.map +1 -1
- package/dist/ui/AdminAccounts.svelte +61 -33
- package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
- package/dist/ui/AdminAuditLog.svelte +3 -2
- package/dist/ui/AdminAuditLog.svelte.d.ts.map +1 -1
- package/dist/ui/AdminInvites.svelte +3 -2
- package/dist/ui/AdminInvites.svelte.d.ts.map +1 -1
- package/dist/ui/AdminOverview.svelte +14 -9
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
- package/dist/ui/AdminPermitHistory.svelte +3 -2
- package/dist/ui/AdminPermitHistory.svelte.d.ts.map +1 -1
- package/dist/ui/AdminSessions.svelte +29 -25
- package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
- package/dist/ui/CLAUDE.md +363 -0
- package/dist/ui/OpenSignupToggle.svelte +6 -3
- package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -1
- package/dist/ui/PermitOfferForm.svelte +141 -0
- package/dist/ui/PermitOfferForm.svelte.d.ts +14 -0
- package/dist/ui/PermitOfferForm.svelte.d.ts.map +1 -0
- package/dist/ui/PermitOfferHistory.svelte +109 -0
- package/dist/ui/PermitOfferHistory.svelte.d.ts +11 -0
- package/dist/ui/PermitOfferHistory.svelte.d.ts.map +1 -0
- package/dist/ui/PermitOfferInbox.svelte +121 -0
- package/dist/ui/PermitOfferInbox.svelte.d.ts +12 -0
- package/dist/ui/PermitOfferInbox.svelte.d.ts.map +1 -0
- package/dist/ui/account_sessions_state.svelte.d.ts +53 -3
- package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.js +39 -16
- package/dist/ui/admin_accounts_state.svelte.d.ts +118 -2
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +99 -23
- package/dist/ui/admin_invites_state.svelte.d.ts +47 -1
- package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_invites_state.svelte.js +38 -26
- package/dist/ui/admin_rpc_adapters.d.ts +94 -0
- package/dist/ui/admin_rpc_adapters.d.ts.map +1 -0
- package/dist/ui/admin_rpc_adapters.js +100 -0
- package/dist/ui/admin_sessions_state.svelte.d.ts +26 -0
- package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_sessions_state.svelte.js +35 -21
- package/dist/ui/app_settings_state.svelte.d.ts +39 -0
- package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
- package/dist/ui/app_settings_state.svelte.js +34 -18
- package/dist/ui/audit_log_state.svelte.d.ts +40 -3
- package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
- package/dist/ui/audit_log_state.svelte.js +36 -42
- package/dist/ui/auth_state.svelte.d.ts +4 -3
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +4 -1
- package/dist/ui/permit_offers_state.svelte.d.ts +125 -0
- package/dist/ui/permit_offers_state.svelte.d.ts.map +1 -0
- package/dist/ui/permit_offers_state.svelte.js +197 -0
- package/package.json +3 -3
- package/dist/auth/admin_routes.d.ts +0 -29
- package/dist/auth/admin_routes.d.ts.map +0 -1
- package/dist/auth/admin_routes.js +0 -226
- package/dist/auth/app_settings_routes.d.ts +0 -27
- package/dist/auth/app_settings_routes.d.ts.map +0 -1
- package/dist/auth/app_settings_routes.js +0 -66
- package/dist/auth/invite_routes.d.ts +0 -18
- package/dist/auth/invite_routes.d.ts.map +0 -1
- package/dist/auth/invite_routes.js +0 -129
|
@@ -4,55 +4,104 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import { SvelteSet } from 'svelte/reactivity';
|
|
7
|
+
import { create_context } from '@fuzdev/fuz_ui/context_helpers.js';
|
|
7
8
|
import { Loadable } from './loadable.svelte.js';
|
|
8
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Svelte context carrying the reactive `AdminAccountsRpc` accessor. The
|
|
11
|
+
* provisioner (typically the admin route shell) calls `set(() => rpc)`;
|
|
12
|
+
* consumers read with `const get_rpc = admin_accounts_rpc_context.get();`
|
|
13
|
+
* and either pass the accessor straight to `AdminAccountsState`/
|
|
14
|
+
* `AdminSessionsState` or wrap it with `const rpc = $derived(get_rpc());`
|
|
15
|
+
* for direct RPC calls. Unset context falls back to `() => null` so
|
|
16
|
+
* components mounted without a provisioner surface the usual "rpc adapter
|
|
17
|
+
* not wired" path.
|
|
18
|
+
*/
|
|
19
|
+
export const admin_accounts_rpc_context = create_context(() => () => null);
|
|
9
20
|
export class AdminAccountsState extends Loadable {
|
|
21
|
+
#get_rpc;
|
|
10
22
|
accounts = $state.raw([]);
|
|
11
23
|
grantable_roles = $state.raw([]);
|
|
12
24
|
granting_keys = new SvelteSet();
|
|
13
25
|
revoking_ids = new SvelteSet();
|
|
26
|
+
retracting_ids = new SvelteSet();
|
|
14
27
|
account_count = $derived(this.accounts.length);
|
|
28
|
+
constructor(options) {
|
|
29
|
+
super();
|
|
30
|
+
this.#get_rpc = options?.get_rpc ?? (() => null);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* True when an RPC adapter is wired. UI uses this to gate all controls
|
|
34
|
+
* — fetch, grant, revoke, retract all flow through the same adapter.
|
|
35
|
+
*/
|
|
36
|
+
get has_rpc() {
|
|
37
|
+
return this.#get_rpc() !== null;
|
|
38
|
+
}
|
|
15
39
|
async fetch() {
|
|
40
|
+
const rpc = this.#get_rpc();
|
|
41
|
+
if (!rpc) {
|
|
42
|
+
this.error = 'rpc adapter not wired';
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
16
45
|
await this.run(async () => {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
const data = await response.json();
|
|
22
|
-
this.accounts = data.accounts ?? [];
|
|
23
|
-
this.grantable_roles = data.grantable_roles ?? [];
|
|
46
|
+
const { accounts, grantable_roles } = await rpc.list_accounts();
|
|
47
|
+
this.accounts = accounts;
|
|
48
|
+
this.grantable_roles = grantable_roles;
|
|
24
49
|
});
|
|
25
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Offer the role to the recipient via the `permit_offer_create` RPC.
|
|
53
|
+
* Server returns the pending offer; the recipient must accept before
|
|
54
|
+
* the permit materializes. Returns the offer payload on success so
|
|
55
|
+
* callers can drive follow-up UX (e.g. seed `PermitOffersState.outgoing`).
|
|
56
|
+
*
|
|
57
|
+
* A re-offer from the same admin to the same `(account, role)`
|
|
58
|
+
* refreshes the existing pending row — the returned offer id is stable
|
|
59
|
+
* across those calls.
|
|
60
|
+
*
|
|
61
|
+
* No-op when the rpc adapter is absent; `error` is set to a descriptive
|
|
62
|
+
* message so the UI surfaces the misconfiguration.
|
|
63
|
+
*/
|
|
26
64
|
async grant_permit(account_id, role) {
|
|
65
|
+
const rpc = this.#get_rpc();
|
|
66
|
+
if (!rpc) {
|
|
67
|
+
this.error = 'rpc adapter not wired';
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
27
70
|
const key = `${account_id}:${role}`;
|
|
28
71
|
this.granting_keys.add(key);
|
|
29
72
|
try {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
headers: { 'Content-Type': 'application/json' },
|
|
33
|
-
body: JSON.stringify({ role }),
|
|
34
|
-
});
|
|
35
|
-
if (!response.ok) {
|
|
36
|
-
this.error = await parse_response_error(response);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
73
|
+
const { offer } = await rpc.grant_permit({ to_account_id: account_id, role });
|
|
74
|
+
this.error = null;
|
|
39
75
|
await this.fetch();
|
|
76
|
+
return offer;
|
|
40
77
|
}
|
|
41
78
|
catch (e) {
|
|
42
79
|
this.error = e instanceof Error ? e.message : 'Failed to grant permit';
|
|
80
|
+
return undefined;
|
|
43
81
|
}
|
|
44
82
|
finally {
|
|
45
83
|
this.granting_keys.delete(key);
|
|
46
84
|
}
|
|
47
85
|
}
|
|
48
|
-
|
|
86
|
+
/**
|
|
87
|
+
* Revoke an active permit via the `permit_revoke` RPC.
|
|
88
|
+
*
|
|
89
|
+
* `actor_id` is the natural key — permits are actor-scoped, and the
|
|
90
|
+
* admin UI reads `row.actor.id` straight from the listing, so the state
|
|
91
|
+
* class takes it directly rather than deriving it from `account_id`.
|
|
92
|
+
* The optional `reason` is stamped on `permit.revoked_reason` and
|
|
93
|
+
* surfaced on the revokee's WS notification.
|
|
94
|
+
*/
|
|
95
|
+
async revoke_permit(actor_id, permit_id, reason) {
|
|
96
|
+
const rpc = this.#get_rpc();
|
|
97
|
+
if (!rpc) {
|
|
98
|
+
this.error = 'rpc adapter not wired';
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
49
101
|
this.revoking_ids.add(permit_id);
|
|
50
102
|
try {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
this.error = await parse_response_error(response);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
103
|
+
await rpc.revoke_permit({ actor_id, permit_id, reason: reason ?? null });
|
|
104
|
+
this.error = null;
|
|
56
105
|
await this.fetch();
|
|
57
106
|
}
|
|
58
107
|
catch (e) {
|
|
@@ -62,4 +111,31 @@ export class AdminAccountsState extends Loadable {
|
|
|
62
111
|
this.revoking_ids.delete(permit_id);
|
|
63
112
|
}
|
|
64
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Retract a pending offer the admin issued via the `permit_offer_retract`
|
|
116
|
+
* RPC. The action handles auth, audit, and the
|
|
117
|
+
* `permit_offer_retracted` WS notification.
|
|
118
|
+
*
|
|
119
|
+
* After success, refetches the listing so `pending_offers` drops the
|
|
120
|
+
* row and the "+ {role}" button un-hides.
|
|
121
|
+
*/
|
|
122
|
+
async retract_offer(offer_id) {
|
|
123
|
+
const rpc = this.#get_rpc();
|
|
124
|
+
if (!rpc) {
|
|
125
|
+
this.error = 'rpc adapter not wired';
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
this.retracting_ids.add(offer_id);
|
|
129
|
+
try {
|
|
130
|
+
await rpc.retract_offer(offer_id);
|
|
131
|
+
this.error = null;
|
|
132
|
+
await this.fetch();
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
this.error = e instanceof Error ? e.message : 'Failed to retract offer';
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
this.retracting_ids.delete(offer_id);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
65
141
|
}
|
|
@@ -1,17 +1,63 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Reactive state for admin invite management.
|
|
3
3
|
*
|
|
4
|
+
* Flows every operation through an injected `AdminInvitesRpc` adapter — the
|
|
5
|
+
* class stays decoupled from the concrete RPC client so tests can inject
|
|
6
|
+
* plain-function stubs. Mirrors `AdminAccountsRpc` / `AuditLogRpc`.
|
|
7
|
+
*
|
|
4
8
|
* @module
|
|
5
9
|
*/
|
|
6
10
|
import { SvelteSet } from 'svelte/reactivity';
|
|
7
11
|
import { Loadable } from './loadable.svelte.js';
|
|
8
|
-
import type { InviteWithUsernamesJson } from '../auth/invite_schema.js';
|
|
12
|
+
import type { InviteJson, InviteWithUsernamesJson } from '../auth/invite_schema.js';
|
|
13
|
+
/**
|
|
14
|
+
* Narrow RPC surface consumed by `AdminInvitesState`. Consumers adapt their
|
|
15
|
+
* typed RPC client to this shape. `error.data.reason` on thrown errors
|
|
16
|
+
* carries the `ERROR_INVITE_*` constant — handled by the caller when
|
|
17
|
+
* user-friendly messages are needed.
|
|
18
|
+
*/
|
|
19
|
+
export interface AdminInvitesRpc {
|
|
20
|
+
list: () => Promise<{
|
|
21
|
+
invites: Array<InviteWithUsernamesJson>;
|
|
22
|
+
}>;
|
|
23
|
+
create: (params: {
|
|
24
|
+
email?: string | null;
|
|
25
|
+
username?: string | null;
|
|
26
|
+
}) => Promise<{
|
|
27
|
+
ok: true;
|
|
28
|
+
invite: InviteJson;
|
|
29
|
+
}>;
|
|
30
|
+
delete: (params: {
|
|
31
|
+
invite_id: string;
|
|
32
|
+
}) => Promise<{
|
|
33
|
+
ok: true;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Svelte context carrying the reactive `AdminInvitesRpc` accessor. Mirrors
|
|
38
|
+
* `admin_accounts_rpc_context`. Unset context falls back to `() => null`.
|
|
39
|
+
*/
|
|
40
|
+
export declare const admin_invites_rpc_context: {
|
|
41
|
+
get: () => () => AdminInvitesRpc | null;
|
|
42
|
+
set: (value?: (() => AdminInvitesRpc | null) | undefined) => () => AdminInvitesRpc | null;
|
|
43
|
+
};
|
|
44
|
+
export interface AdminInvitesStateOptions {
|
|
45
|
+
/**
|
|
46
|
+
* Reactive accessor for the RPC adapter. `null` disables all operations
|
|
47
|
+
* (the state reports a descriptive error when mutations/fetches fire).
|
|
48
|
+
*/
|
|
49
|
+
get_rpc?: () => AdminInvitesRpc | null;
|
|
50
|
+
}
|
|
9
51
|
export declare class AdminInvitesState extends Loadable {
|
|
52
|
+
#private;
|
|
10
53
|
invites: Array<InviteWithUsernamesJson>;
|
|
11
54
|
creating: boolean;
|
|
12
55
|
readonly deleting_ids: SvelteSet<string>;
|
|
13
56
|
readonly invite_count: number;
|
|
14
57
|
readonly unclaimed_count: number;
|
|
58
|
+
constructor(options?: AdminInvitesStateOptions);
|
|
59
|
+
/** True when an RPC adapter is wired. All ops require it. */
|
|
60
|
+
get has_rpc(): boolean;
|
|
15
61
|
fetch(): Promise<void>;
|
|
16
62
|
create_invite(email?: string, username?: string): Promise<boolean>;
|
|
17
63
|
delete_invite(id: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin_invites_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/admin_invites_state.svelte.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"admin_invites_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/admin_invites_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAG5C,OAAO,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAC;AAC9C,OAAO,KAAK,EAAC,UAAU,EAAE,uBAAuB,EAAC,MAAM,0BAA0B,CAAC;AAElF;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,OAAO,CAAC;QAAC,OAAO,EAAE,KAAK,CAAC,uBAAuB,CAAC,CAAA;KAAC,CAAC,CAAC;IAC/D,MAAM,EAAE,CAAC,MAAM,EAAE;QAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACzB,KAAK,OAAO,CAAC;QAAC,EAAE,EAAE,IAAI,CAAC;QAAC,MAAM,EAAE,UAAU,CAAA;KAAC,CAAC,CAAC;IAC9C,MAAM,EAAE,CAAC,MAAM,EAAE;QAAC,SAAS,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC;QAAC,EAAE,EAAE,IAAI,CAAA;KAAC,CAAC,CAAC;CAC7D;AAED;;;GAGG;AACH,eAAO,MAAM,yBAAyB;qBAAwB,eAAe,GAAG,IAAI;yBAAtB,eAAe,GAAG,IAAI,wBAAtB,eAAe,GAAG,IAAI;CAEnF,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACxC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,eAAe,GAAG,IAAI,CAAC;CACvC;AAED,qBAAa,iBAAkB,SAAQ,QAAQ;;IAG9C,OAAO,EAAE,KAAK,CAAC,uBAAuB,CAAC,CAAkB;IACzD,QAAQ,UAAqB;IAC7B,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC,MAAM,CAAC,CAAmB;IAE3D,QAAQ,CAAC,YAAY,SAAiC;IACtD,QAAQ,CAAC,eAAe,SAA8D;gBAE1E,OAAO,CAAC,EAAE,wBAAwB;IAK9C,6DAA6D;IAC7D,IAAI,OAAO,IAAI,OAAO,CAErB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAYtB,aAAa,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAoBlE,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAgB9C"}
|
|
@@ -1,45 +1,56 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Reactive state for admin invite management.
|
|
3
3
|
*
|
|
4
|
+
* Flows every operation through an injected `AdminInvitesRpc` adapter — the
|
|
5
|
+
* class stays decoupled from the concrete RPC client so tests can inject
|
|
6
|
+
* plain-function stubs. Mirrors `AdminAccountsRpc` / `AuditLogRpc`.
|
|
7
|
+
*
|
|
4
8
|
* @module
|
|
5
9
|
*/
|
|
6
10
|
import { SvelteSet } from 'svelte/reactivity';
|
|
11
|
+
import { create_context } from '@fuzdev/fuz_ui/context_helpers.js';
|
|
7
12
|
import { Loadable } from './loadable.svelte.js';
|
|
8
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Svelte context carrying the reactive `AdminInvitesRpc` accessor. Mirrors
|
|
15
|
+
* `admin_accounts_rpc_context`. Unset context falls back to `() => null`.
|
|
16
|
+
*/
|
|
17
|
+
export const admin_invites_rpc_context = create_context(() => () => null);
|
|
9
18
|
export class AdminInvitesState extends Loadable {
|
|
19
|
+
#get_rpc;
|
|
10
20
|
invites = $state.raw([]);
|
|
11
21
|
creating = $state.raw(false);
|
|
12
22
|
deleting_ids = new SvelteSet();
|
|
13
23
|
invite_count = $derived(this.invites.length);
|
|
14
24
|
unclaimed_count = $derived(this.invites.filter((i) => !i.claimed_at).length);
|
|
25
|
+
constructor(options) {
|
|
26
|
+
super();
|
|
27
|
+
this.#get_rpc = options?.get_rpc ?? (() => null);
|
|
28
|
+
}
|
|
29
|
+
/** True when an RPC adapter is wired. All ops require it. */
|
|
30
|
+
get has_rpc() {
|
|
31
|
+
return this.#get_rpc() !== null;
|
|
32
|
+
}
|
|
15
33
|
async fetch() {
|
|
34
|
+
const rpc = this.#get_rpc();
|
|
35
|
+
if (!rpc) {
|
|
36
|
+
this.error = 'rpc adapter not wired';
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
16
39
|
await this.run(async () => {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
throw new Error(await parse_response_error(response, 'Failed to fetch invites'));
|
|
20
|
-
}
|
|
21
|
-
const data = await response.json();
|
|
22
|
-
this.invites = data.invites ?? [];
|
|
40
|
+
const { invites } = await rpc.list();
|
|
41
|
+
this.invites = invites;
|
|
23
42
|
});
|
|
24
43
|
}
|
|
25
44
|
async create_invite(email, username) {
|
|
45
|
+
const rpc = this.#get_rpc();
|
|
46
|
+
if (!rpc) {
|
|
47
|
+
this.error = 'rpc adapter not wired';
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
26
50
|
this.creating = true;
|
|
27
51
|
this.error = null;
|
|
28
52
|
try {
|
|
29
|
-
|
|
30
|
-
if (email)
|
|
31
|
-
body.email = email;
|
|
32
|
-
if (username)
|
|
33
|
-
body.username = username;
|
|
34
|
-
const response = await ui_fetch('/api/admin/invites', {
|
|
35
|
-
method: 'POST',
|
|
36
|
-
headers: { 'Content-Type': 'application/json' },
|
|
37
|
-
body: JSON.stringify(body),
|
|
38
|
-
});
|
|
39
|
-
if (!response.ok) {
|
|
40
|
-
this.error = await parse_response_error(response);
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
53
|
+
await rpc.create({ email: email ?? null, username: username ?? null });
|
|
43
54
|
await this.fetch();
|
|
44
55
|
return true;
|
|
45
56
|
}
|
|
@@ -52,13 +63,14 @@ export class AdminInvitesState extends Loadable {
|
|
|
52
63
|
}
|
|
53
64
|
}
|
|
54
65
|
async delete_invite(id) {
|
|
66
|
+
const rpc = this.#get_rpc();
|
|
67
|
+
if (!rpc) {
|
|
68
|
+
this.error = 'rpc adapter not wired';
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
55
71
|
this.deleting_ids.add(id);
|
|
56
72
|
try {
|
|
57
|
-
|
|
58
|
-
if (!response.ok) {
|
|
59
|
-
this.error = await parse_response_error(response);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
73
|
+
await rpc.delete({ invite_id: id });
|
|
62
74
|
await this.fetch();
|
|
63
75
|
}
|
|
64
76
|
catch (e) {
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin RPC adapter helpers for consumer UIs.
|
|
3
|
+
*
|
|
4
|
+
* Bridges a typed `rpc_call`-shaped function to the four narrow admin RPC
|
|
5
|
+
* interfaces the state classes consume — `AdminAccountsRpc`,
|
|
6
|
+
* `AdminInvitesRpc`, `AuditLogRpc`, `AppSettingsRpc`. Two calls at the
|
|
7
|
+
* admin shell layout wire everything:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import {create_throwing_rpc_call} from '@fuzdev/fuz_app/actions/rpc_client.js';
|
|
11
|
+
* const rpc_call = create_throwing_rpc_call(api);
|
|
12
|
+
* provide_admin_rpc_contexts(create_admin_rpc_adapters(rpc_call));
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* `create_throwing_rpc_call` unwraps every `Result` to throw on error, spreading
|
|
16
|
+
* the JSON-RPC `{code, message, data?}` onto the thrown `Error` so form
|
|
17
|
+
* components (e.g. `PermitOfferForm.svelte`) can match on
|
|
18
|
+
* `error.data?.reason` via `ERROR_OFFER_*` constants — optional chaining is
|
|
19
|
+
* required because JSON-RPC `data` is spec-level optional. Consumers that
|
|
20
|
+
* need a custom unwrap strategy can supply any function matching
|
|
21
|
+
* `AdminRpcCall` directly instead.
|
|
22
|
+
*
|
|
23
|
+
* No `.svelte.ts` suffix — this module holds no reactive state, only
|
|
24
|
+
* method-name mappings.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import type { ThrowingRpcCall } from '../actions/rpc_client.js';
|
|
29
|
+
import { type AdminAccountsRpc } from './admin_accounts_state.svelte.js';
|
|
30
|
+
import { type AdminInvitesRpc } from './admin_invites_state.svelte.js';
|
|
31
|
+
import { type AuditLogRpc } from './audit_log_state.svelte.js';
|
|
32
|
+
import { type AppSettingsRpc } from './app_settings_state.svelte.js';
|
|
33
|
+
/**
|
|
34
|
+
* Function-shaped contract for dispatching an RPC call by method name.
|
|
35
|
+
*
|
|
36
|
+
* Alias of `ThrowingRpcCall` — kept as a domain-specific name so reads of
|
|
37
|
+
* the admin UI code stay self-contained. Receives the method string and
|
|
38
|
+
* input, returns a Promise of the output — or throws on error carrying the
|
|
39
|
+
* JSON-RPC `{code, message, data?}` shape.
|
|
40
|
+
*
|
|
41
|
+
* The generic is load-bearing: contextual typing lets the narrow
|
|
42
|
+
* `Admin*Rpc` return types flow into `TOutput` so adapter methods typecheck
|
|
43
|
+
* without explicit casts.
|
|
44
|
+
*/
|
|
45
|
+
export type AdminRpcCall = ThrowingRpcCall;
|
|
46
|
+
/** The four admin RPC adapters assembled from a shared `rpc_call`. */
|
|
47
|
+
export interface AdminRpcAdapters {
|
|
48
|
+
admin_accounts: AdminAccountsRpc;
|
|
49
|
+
admin_invites: AdminInvitesRpc;
|
|
50
|
+
audit_log: AuditLogRpc;
|
|
51
|
+
app_settings: AppSettingsRpc;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Build the four admin RPC adapters from a single typed `rpc_call`.
|
|
55
|
+
*
|
|
56
|
+
* Method-name mapping:
|
|
57
|
+
*
|
|
58
|
+
* | Narrow RPC method | Action spec method |
|
|
59
|
+
* | ----------------------------------- | ---------------------------- |
|
|
60
|
+
* | `admin_accounts.list_accounts` | `admin_account_list` |
|
|
61
|
+
* | `admin_accounts.list_sessions` | `admin_session_list` |
|
|
62
|
+
* | `admin_accounts.grant_permit` | `permit_offer_create` |
|
|
63
|
+
* | `admin_accounts.revoke_permit` | `permit_revoke` |
|
|
64
|
+
* | `admin_accounts.retract_offer` | `permit_offer_retract` |
|
|
65
|
+
* | `admin_accounts.session_revoke_all` | `admin_session_revoke_all` |
|
|
66
|
+
* | `admin_accounts.token_revoke_all` | `admin_token_revoke_all` |
|
|
67
|
+
* | `admin_invites.list` | `invite_list` |
|
|
68
|
+
* | `admin_invites.create` | `invite_create` |
|
|
69
|
+
* | `admin_invites.delete` | `invite_delete` |
|
|
70
|
+
* | `audit_log.list` | `audit_log_list` |
|
|
71
|
+
* | `audit_log.permit_history` | `audit_log_permit_history` |
|
|
72
|
+
* | `app_settings.get` | `app_settings_get` |
|
|
73
|
+
* | `app_settings.update` | `app_settings_update` |
|
|
74
|
+
*
|
|
75
|
+
* All four adapter factories call through the same `rpc_call` — consumers
|
|
76
|
+
* only construct one adapter closure (typically wrapping
|
|
77
|
+
* `create_rpc_client`'s Proxy + Result-unwrap) regardless of how many
|
|
78
|
+
* admin surfaces they mount.
|
|
79
|
+
*/
|
|
80
|
+
export declare const create_admin_rpc_adapters: (rpc_call: AdminRpcCall) => AdminRpcAdapters;
|
|
81
|
+
/**
|
|
82
|
+
* Wire all four admin RPC contexts in a single call.
|
|
83
|
+
*
|
|
84
|
+
* Call once at the admin shell layout (e.g. `src/routes/admin/+layout.svelte`)
|
|
85
|
+
* with adapters built from `create_admin_rpc_adapters`. Every `Admin*.svelte`
|
|
86
|
+
* component that reads a context below this point sees the adapters.
|
|
87
|
+
*
|
|
88
|
+
* Each context accessor reads `adapters.{domain}` on every invocation, so
|
|
89
|
+
* mutating an adapter field on the same object propagates. Replacing the
|
|
90
|
+
* whole adapter set requires calling `provide_admin_rpc_contexts` again
|
|
91
|
+
* during init — in practice this is one-shot at layout mount.
|
|
92
|
+
*/
|
|
93
|
+
export declare const provide_admin_rpc_contexts: (adapters: AdminRpcAdapters) => void;
|
|
94
|
+
//# sourceMappingURL=admin_rpc_adapters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin_rpc_adapters.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/admin_rpc_adapters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAA6B,KAAK,gBAAgB,EAAC,MAAM,kCAAkC,CAAC;AACnG,OAAO,EAA4B,KAAK,eAAe,EAAC,MAAM,iCAAiC,CAAC;AAChG,OAAO,EAAwB,KAAK,WAAW,EAAC,MAAM,6BAA6B,CAAC;AACpF,OAAO,EAA2B,KAAK,cAAc,EAAC,MAAM,gCAAgC,CAAC;AAE7F;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GAAG,eAAe,CAAC;AAE3C,sEAAsE;AACtE,MAAM,WAAW,gBAAgB;IAChC,cAAc,EAAE,gBAAgB,CAAC;IACjC,aAAa,EAAE,eAAe,CAAC;IAC/B,SAAS,EAAE,WAAW,CAAC;IACvB,YAAY,EAAE,cAAc,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,yBAAyB,GAAI,UAAU,YAAY,KAAG,gBAuBjE,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GAAI,UAAU,gBAAgB,KAAG,IAKvE,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admin RPC adapter helpers for consumer UIs.
|
|
3
|
+
*
|
|
4
|
+
* Bridges a typed `rpc_call`-shaped function to the four narrow admin RPC
|
|
5
|
+
* interfaces the state classes consume — `AdminAccountsRpc`,
|
|
6
|
+
* `AdminInvitesRpc`, `AuditLogRpc`, `AppSettingsRpc`. Two calls at the
|
|
7
|
+
* admin shell layout wire everything:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import {create_throwing_rpc_call} from '@fuzdev/fuz_app/actions/rpc_client.js';
|
|
11
|
+
* const rpc_call = create_throwing_rpc_call(api);
|
|
12
|
+
* provide_admin_rpc_contexts(create_admin_rpc_adapters(rpc_call));
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* `create_throwing_rpc_call` unwraps every `Result` to throw on error, spreading
|
|
16
|
+
* the JSON-RPC `{code, message, data?}` onto the thrown `Error` so form
|
|
17
|
+
* components (e.g. `PermitOfferForm.svelte`) can match on
|
|
18
|
+
* `error.data?.reason` via `ERROR_OFFER_*` constants — optional chaining is
|
|
19
|
+
* required because JSON-RPC `data` is spec-level optional. Consumers that
|
|
20
|
+
* need a custom unwrap strategy can supply any function matching
|
|
21
|
+
* `AdminRpcCall` directly instead.
|
|
22
|
+
*
|
|
23
|
+
* No `.svelte.ts` suffix — this module holds no reactive state, only
|
|
24
|
+
* method-name mappings.
|
|
25
|
+
*
|
|
26
|
+
* @module
|
|
27
|
+
*/
|
|
28
|
+
import { admin_accounts_rpc_context } from './admin_accounts_state.svelte.js';
|
|
29
|
+
import { admin_invites_rpc_context } from './admin_invites_state.svelte.js';
|
|
30
|
+
import { audit_log_rpc_context } from './audit_log_state.svelte.js';
|
|
31
|
+
import { app_settings_rpc_context } from './app_settings_state.svelte.js';
|
|
32
|
+
/**
|
|
33
|
+
* Build the four admin RPC adapters from a single typed `rpc_call`.
|
|
34
|
+
*
|
|
35
|
+
* Method-name mapping:
|
|
36
|
+
*
|
|
37
|
+
* | Narrow RPC method | Action spec method |
|
|
38
|
+
* | ----------------------------------- | ---------------------------- |
|
|
39
|
+
* | `admin_accounts.list_accounts` | `admin_account_list` |
|
|
40
|
+
* | `admin_accounts.list_sessions` | `admin_session_list` |
|
|
41
|
+
* | `admin_accounts.grant_permit` | `permit_offer_create` |
|
|
42
|
+
* | `admin_accounts.revoke_permit` | `permit_revoke` |
|
|
43
|
+
* | `admin_accounts.retract_offer` | `permit_offer_retract` |
|
|
44
|
+
* | `admin_accounts.session_revoke_all` | `admin_session_revoke_all` |
|
|
45
|
+
* | `admin_accounts.token_revoke_all` | `admin_token_revoke_all` |
|
|
46
|
+
* | `admin_invites.list` | `invite_list` |
|
|
47
|
+
* | `admin_invites.create` | `invite_create` |
|
|
48
|
+
* | `admin_invites.delete` | `invite_delete` |
|
|
49
|
+
* | `audit_log.list` | `audit_log_list` |
|
|
50
|
+
* | `audit_log.permit_history` | `audit_log_permit_history` |
|
|
51
|
+
* | `app_settings.get` | `app_settings_get` |
|
|
52
|
+
* | `app_settings.update` | `app_settings_update` |
|
|
53
|
+
*
|
|
54
|
+
* All four adapter factories call through the same `rpc_call` — consumers
|
|
55
|
+
* only construct one adapter closure (typically wrapping
|
|
56
|
+
* `create_rpc_client`'s Proxy + Result-unwrap) regardless of how many
|
|
57
|
+
* admin surfaces they mount.
|
|
58
|
+
*/
|
|
59
|
+
export const create_admin_rpc_adapters = (rpc_call) => ({
|
|
60
|
+
admin_accounts: {
|
|
61
|
+
list_accounts: () => rpc_call('admin_account_list', null),
|
|
62
|
+
list_sessions: () => rpc_call('admin_session_list', null),
|
|
63
|
+
grant_permit: (params) => rpc_call('permit_offer_create', params),
|
|
64
|
+
revoke_permit: (params) => rpc_call('permit_revoke', params),
|
|
65
|
+
retract_offer: (offer_id) => rpc_call('permit_offer_retract', { offer_id }),
|
|
66
|
+
session_revoke_all: (params) => rpc_call('admin_session_revoke_all', params),
|
|
67
|
+
token_revoke_all: (params) => rpc_call('admin_token_revoke_all', params),
|
|
68
|
+
},
|
|
69
|
+
admin_invites: {
|
|
70
|
+
list: () => rpc_call('invite_list', null),
|
|
71
|
+
create: (params) => rpc_call('invite_create', params),
|
|
72
|
+
delete: (params) => rpc_call('invite_delete', params),
|
|
73
|
+
},
|
|
74
|
+
audit_log: {
|
|
75
|
+
list: (options) => rpc_call('audit_log_list', options ?? {}),
|
|
76
|
+
permit_history: (params) => rpc_call('audit_log_permit_history', params ?? {}),
|
|
77
|
+
},
|
|
78
|
+
app_settings: {
|
|
79
|
+
get: () => rpc_call('app_settings_get', null),
|
|
80
|
+
update: (params) => rpc_call('app_settings_update', params),
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
/**
|
|
84
|
+
* Wire all four admin RPC contexts in a single call.
|
|
85
|
+
*
|
|
86
|
+
* Call once at the admin shell layout (e.g. `src/routes/admin/+layout.svelte`)
|
|
87
|
+
* with adapters built from `create_admin_rpc_adapters`. Every `Admin*.svelte`
|
|
88
|
+
* component that reads a context below this point sees the adapters.
|
|
89
|
+
*
|
|
90
|
+
* Each context accessor reads `adapters.{domain}` on every invocation, so
|
|
91
|
+
* mutating an adapter field on the same object propagates. Replacing the
|
|
92
|
+
* whole adapter set requires calling `provide_admin_rpc_contexts` again
|
|
93
|
+
* during init — in practice this is one-shot at layout mount.
|
|
94
|
+
*/
|
|
95
|
+
export const provide_admin_rpc_contexts = (adapters) => {
|
|
96
|
+
admin_accounts_rpc_context.set(() => adapters.admin_accounts);
|
|
97
|
+
admin_invites_rpc_context.set(() => adapters.admin_invites);
|
|
98
|
+
audit_log_rpc_context.set(() => adapters.audit_log);
|
|
99
|
+
app_settings_rpc_context.set(() => adapters.app_settings);
|
|
100
|
+
};
|
|
@@ -1,16 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Reactive state for admin session overview.
|
|
3
3
|
*
|
|
4
|
+
* Both the listing and the two revoke-all mutations flow through the shared
|
|
5
|
+
* `AdminAccountsRpc` adapter (`list_sessions`, `session_revoke_all`,
|
|
6
|
+
* `token_revoke_all`). The former REST `GET /api/admin/sessions` route moved
|
|
7
|
+
* to the `admin_session_list` RPC method in the 2026-04-23 migration.
|
|
8
|
+
*
|
|
4
9
|
* @module
|
|
5
10
|
*/
|
|
6
11
|
import { SvelteSet } from 'svelte/reactivity';
|
|
7
12
|
import { Loadable } from './loadable.svelte.js';
|
|
13
|
+
import type { AdminAccountsRpc } from './admin_accounts_state.svelte.js';
|
|
8
14
|
import type { AdminSessionJson } from '../auth/audit_log_schema.js';
|
|
15
|
+
/**
|
|
16
|
+
* Options for `AdminSessionsState`.
|
|
17
|
+
*
|
|
18
|
+
* The RPC adapter drives every operation (listing + the two revoke-all
|
|
19
|
+
* mutations). Without it, `fetch` and the revoke controls no-op with
|
|
20
|
+
* `'rpc adapter not wired'` on `error`.
|
|
21
|
+
*/
|
|
22
|
+
export interface AdminSessionsStateOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Reactive accessor for the RPC adapter; returns `null` when unwired.
|
|
25
|
+
* Mirrors `AdminAccountsStateOptions.get_rpc` so a single adapter
|
|
26
|
+
* instance backs both states without tripping Svelte's
|
|
27
|
+
* `state_referenced_locally` warning.
|
|
28
|
+
*/
|
|
29
|
+
get_rpc?: () => AdminAccountsRpc | null;
|
|
30
|
+
}
|
|
9
31
|
export declare class AdminSessionsState extends Loadable {
|
|
32
|
+
#private;
|
|
10
33
|
sessions: Array<AdminSessionJson>;
|
|
11
34
|
readonly revoking_account_ids: SvelteSet<string>;
|
|
12
35
|
readonly revoking_token_account_ids: SvelteSet<string>;
|
|
13
36
|
readonly active_count: number;
|
|
37
|
+
constructor(options?: AdminSessionsStateOptions);
|
|
38
|
+
/** True when an RPC adapter is wired. `fetch` and the revoke controls no-op without it. */
|
|
39
|
+
get has_rpc(): boolean;
|
|
14
40
|
fetch(): Promise<void>;
|
|
15
41
|
revoke_all_for_account(account_id: string): Promise<void>;
|
|
16
42
|
revoke_all_tokens_for_account(account_id: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin_sessions_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/admin_sessions_state.svelte.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"admin_sessions_state.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/admin_sessions_state.svelte.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAC,SAAS,EAAC,MAAM,mBAAmB,CAAC;AAE5C,OAAO,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAC;AAC9C,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,kCAAkC,CAAC;AACvE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,6BAA6B,CAAC;AAElE;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB;IACzC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,gBAAgB,GAAG,IAAI,CAAC;CACxC;AAED,qBAAa,kBAAmB,SAAQ,QAAQ;;IAG/C,QAAQ,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAkB;IACnD,QAAQ,CAAC,oBAAoB,EAAE,SAAS,CAAC,MAAM,CAAC,CAAmB;IACnE,QAAQ,CAAC,0BAA0B,EAAE,SAAS,CAAC,MAAM,CAAC,CAAmB;IAEzE,QAAQ,CAAC,YAAY,SAAkC;gBAE3C,OAAO,CAAC,EAAE,yBAAyB;IAK/C,2FAA2F;IAC3F,IAAI,OAAO,IAAI,OAAO,CAErB;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAYtB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBzD,6BAA6B,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAiBtE"}
|
|
@@ -1,36 +1,50 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Reactive state for admin session overview.
|
|
3
3
|
*
|
|
4
|
+
* Both the listing and the two revoke-all mutations flow through the shared
|
|
5
|
+
* `AdminAccountsRpc` adapter (`list_sessions`, `session_revoke_all`,
|
|
6
|
+
* `token_revoke_all`). The former REST `GET /api/admin/sessions` route moved
|
|
7
|
+
* to the `admin_session_list` RPC method in the 2026-04-23 migration.
|
|
8
|
+
*
|
|
4
9
|
* @module
|
|
5
10
|
*/
|
|
6
11
|
import { SvelteSet } from 'svelte/reactivity';
|
|
7
12
|
import { Loadable } from './loadable.svelte.js';
|
|
8
|
-
import { parse_response_error, ui_fetch } from './ui_fetch.js';
|
|
9
13
|
export class AdminSessionsState extends Loadable {
|
|
14
|
+
#get_rpc;
|
|
10
15
|
sessions = $state.raw([]);
|
|
11
16
|
revoking_account_ids = new SvelteSet();
|
|
12
17
|
revoking_token_account_ids = new SvelteSet();
|
|
13
18
|
active_count = $derived(this.sessions.length);
|
|
19
|
+
constructor(options) {
|
|
20
|
+
super();
|
|
21
|
+
this.#get_rpc = options?.get_rpc ?? (() => null);
|
|
22
|
+
}
|
|
23
|
+
/** True when an RPC adapter is wired. `fetch` and the revoke controls no-op without it. */
|
|
24
|
+
get has_rpc() {
|
|
25
|
+
return this.#get_rpc() !== null;
|
|
26
|
+
}
|
|
14
27
|
async fetch() {
|
|
28
|
+
const rpc = this.#get_rpc();
|
|
29
|
+
if (!rpc) {
|
|
30
|
+
this.error = 'rpc adapter not wired';
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
15
33
|
await this.run(async () => {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
throw new Error(await parse_response_error(response, 'Failed to fetch sessions'));
|
|
19
|
-
}
|
|
20
|
-
const data = await response.json();
|
|
21
|
-
this.sessions = data.sessions ?? [];
|
|
34
|
+
const { sessions } = await rpc.list_sessions();
|
|
35
|
+
this.sessions = sessions;
|
|
22
36
|
});
|
|
23
37
|
}
|
|
24
38
|
async revoke_all_for_account(account_id) {
|
|
39
|
+
const rpc = this.#get_rpc();
|
|
40
|
+
if (!rpc) {
|
|
41
|
+
this.error = 'rpc adapter not wired';
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
25
44
|
this.revoking_account_ids.add(account_id);
|
|
26
45
|
try {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
});
|
|
30
|
-
if (!response.ok) {
|
|
31
|
-
this.error = await parse_response_error(response);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
46
|
+
await rpc.session_revoke_all({ account_id });
|
|
47
|
+
this.error = null;
|
|
34
48
|
await this.fetch();
|
|
35
49
|
}
|
|
36
50
|
catch (e) {
|
|
@@ -41,15 +55,15 @@ export class AdminSessionsState extends Loadable {
|
|
|
41
55
|
}
|
|
42
56
|
}
|
|
43
57
|
async revoke_all_tokens_for_account(account_id) {
|
|
58
|
+
const rpc = this.#get_rpc();
|
|
59
|
+
if (!rpc) {
|
|
60
|
+
this.error = 'rpc adapter not wired';
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
44
63
|
this.revoking_token_account_ids.add(account_id);
|
|
45
64
|
try {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
});
|
|
49
|
-
if (!response.ok) {
|
|
50
|
-
this.error = await parse_response_error(response);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
65
|
+
await rpc.token_revoke_all({ account_id });
|
|
66
|
+
this.error = null;
|
|
53
67
|
await this.fetch();
|
|
54
68
|
}
|
|
55
69
|
catch (e) {
|