@tokenoftrust/storefront-runner 2.0.55 → 2.0.57
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.
|
@@ -128,7 +128,56 @@
|
|
|
128
128
|
function showMsg(kind, text) {
|
|
129
129
|
const el = $("msg");
|
|
130
130
|
el.className = "msg " + kind;
|
|
131
|
-
el.
|
|
131
|
+
el.replaceChildren();
|
|
132
|
+
if (typeof text === "string") {
|
|
133
|
+
el.textContent = text;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (text && text.nodeType) el.appendChild(text);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function showProvisionPrompt(message) {
|
|
140
|
+
const wrap = document.createElement("div");
|
|
141
|
+
const line = document.createElement("p");
|
|
142
|
+
line.style.margin = "0 0 .6rem";
|
|
143
|
+
line.textContent = message || "This store's repo isn't provisioned yet.";
|
|
144
|
+
const btn = document.createElement("button");
|
|
145
|
+
btn.type = "button";
|
|
146
|
+
btn.id = "provisionBtn";
|
|
147
|
+
btn.className = "secondary";
|
|
148
|
+
btn.textContent = "Provision now?";
|
|
149
|
+
btn.title = "Create the store repo on this environment, then retry the invite";
|
|
150
|
+
btn.addEventListener("click", () => provisionStore(btn));
|
|
151
|
+
wrap.appendChild(line);
|
|
152
|
+
wrap.appendChild(btn);
|
|
153
|
+
showMsg("err", wrap);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function provisionStore(btn) {
|
|
157
|
+
if (btn) btn.disabled = true;
|
|
158
|
+
showMsg("", "Provisioning store repo…");
|
|
159
|
+
try {
|
|
160
|
+
const res = await fetch("/api/grants/provision-store", {
|
|
161
|
+
method: "POST",
|
|
162
|
+
headers: { "content-type": "application/json", accept: "application/json" },
|
|
163
|
+
body: JSON.stringify({ tenant, confirm: true }),
|
|
164
|
+
});
|
|
165
|
+
const body = await res.json().catch(() => ({}));
|
|
166
|
+
if (res.ok) {
|
|
167
|
+
const created = body.created ? "created" : "already present";
|
|
168
|
+
showMsg(
|
|
169
|
+
"ok",
|
|
170
|
+
"Store repo " + created + " for " + (body.tenant || tenant) +
|
|
171
|
+
". Click Send invite again.",
|
|
172
|
+
);
|
|
173
|
+
} else {
|
|
174
|
+
showMsg("err", body.error || ("Provision failed (HTTP " + res.status + ")."));
|
|
175
|
+
}
|
|
176
|
+
} catch (e) {
|
|
177
|
+
showMsg("err", "Provision failed: " + (e && e.message ? e.message : e));
|
|
178
|
+
} finally {
|
|
179
|
+
if (btn) btn.disabled = false;
|
|
180
|
+
}
|
|
132
181
|
}
|
|
133
182
|
|
|
134
183
|
async function invite() {
|
|
@@ -161,6 +210,11 @@
|
|
|
161
210
|
$("email").value = "";
|
|
162
211
|
if (noteEl) noteEl.value = "";
|
|
163
212
|
loadMembers();
|
|
213
|
+
} else if (
|
|
214
|
+
res.status === 409 &&
|
|
215
|
+
(body.code === "store-not-provisioned" || body.reason === "storeNotProvisioned")
|
|
216
|
+
) {
|
|
217
|
+
showProvisionPrompt(body.error);
|
|
164
218
|
} else {
|
|
165
219
|
showMsg("err", body.error || ("Invite failed (HTTP " + res.status + ")."));
|
|
166
220
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TENANT_CACHE projection for staff/operator store provision.
|
|
3
|
+
*
|
|
4
|
+
* Lives under src/config so pages/lib may call it without crossing the
|
|
5
|
+
* tenancy-resolver-fence (only config/middleware may import kvTenantResolver).
|
|
6
|
+
*/
|
|
7
|
+
import type { TenantConfig } from "@tot/public-runtime";
|
|
8
|
+
import { putTenant } from "@/config/kvTenantResolver";
|
|
9
|
+
|
|
10
|
+
export async function projectProvisionedTenant(
|
|
11
|
+
kv: KVNamespace,
|
|
12
|
+
config: TenantConfig,
|
|
13
|
+
repoMeta: { owner: string; repo: string; cloneUrl: string | null },
|
|
14
|
+
): Promise<void> {
|
|
15
|
+
await putTenant(kv, config);
|
|
16
|
+
await kv.put(
|
|
17
|
+
`repo:${config.tenant_id}`,
|
|
18
|
+
JSON.stringify({
|
|
19
|
+
owner: repoMeta.owner,
|
|
20
|
+
repo: repoMeta.repo,
|
|
21
|
+
cloneUrl: repoMeta.cloneUrl,
|
|
22
|
+
}),
|
|
23
|
+
);
|
|
24
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.57",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
|
|
6
6
|
"packageManager": "pnpm@11.9.0",
|
|
@@ -479,6 +479,14 @@ export const ACTION_CATALOG = {
|
|
|
479
479
|
argsAllow: ["resource", "capability"], renderedAllow: [],
|
|
480
480
|
desc: "A ToT-staff operator entered a vendor tenant (impersonation boundary).",
|
|
481
481
|
},
|
|
482
|
+
"dashboard.store_provision": {
|
|
483
|
+
// grants/provision-store.ts — "Provision now?" from the Team invite dead-end when
|
|
484
|
+
// the store repo is not yet provisioned. Same near-side admission as team-invite
|
|
485
|
+
// (membership → merchant/dev, staff selection → admin). Args are counts/ids only.
|
|
486
|
+
actors: ["admin", "merchant", "dev"], source: "ui", sinks: LIFECYCLE,
|
|
487
|
+
argsAllow: ["resource"], renderedAllow: ["created", "repo"],
|
|
488
|
+
desc: "A store's forge repo was provisioned from the Team console.",
|
|
489
|
+
},
|
|
482
490
|
"cache.purged": {
|
|
483
491
|
// cache-purge.ts — bumps a tenant's edge page-cache version (the ETL load step calls
|
|
484
492
|
// it per tenant post-migration). Bearer-secret service call (no session) → emitted
|