Public: anyone can read this; the crew can change it.

Revoke on an invite link at /admin/limits goes through the confirm page, which says what revoking does #7970

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/7962-revoke-on-an-invite-link-at-admin-limits → main 1 commit fork

  • Merged. scout/7962-revoke-on-an-invite-link-at-admin-limits was merged into main as 5093ea8.

Review

Sign in to review this change.
3 files changed +22 −6
src/web/pages/admin-limits.ts +2 −5
@@ -20,7 +20,7 @@ import { notFoundPage, settingsLayout } from "../chrome.ts";
2020 import { field, readForm } from "../form.ts";
2121 import { html, when } from "../html.ts";
2222 import { flashTo, page } from "../layout.ts";
23 import { btn, card, icon, row, time } from "../ui.ts";
23 import { btn, card, confirmAction, icon, row, time } from "../ui.ts";
2424 import { accountSections } from "./origins.ts";
2525
2626 // GET /admin/limits
@@ -165,9 +165,7 @@ export function limitsPage(ctx: Ctx): Response {
165165 meta: html`Made ${time(i.createdAt)} · expires ${time(i.expiresAt)}`,
166166 trail:
167167 i.state === "open"
168 ? html`<form class="row-fields" method="post" action="/admin/invites/${i.id}/revoke">
169 ${btn("Revoke", { size: "sm", label: `Revoke invite ${i.id}` })}
170 </form>`
168 ? confirmAction({ action: `/admin/invites/${i.id}/revoke`, verb: "Revoke", size: "sm" })
171169 : html``,
172170 }),
173171 );
@@ -175,7 +173,6 @@ export function limitsPage(ctx: Ctx): Response {
175173 html`<div class="card-body">
176174 <p class="muted small">
177175 A link is shown once, when it is made, and is good for one signup.
178 Nothing reads these yet.
179176 </p>
180177 <form class="row-fields" method="post" action="/admin/invites">
181178 <label class="small">expires in (days)
src/web/pages/search.ts +10
@@ -717,6 +717,16 @@ const DESTRUCTIVE: {
717717 };
718718 },
719719 },
720 {
721 pattern: /^\/admin\/invites\/([^/]+)\/revoke$/,
722 describe: (m) => ({
723 verb: "Revoke",
724 title: `Revoke invite ${decodeURIComponent(m[1] ?? "")}?`,
725 detail:
726 "Whoever holds this link can no longer sign up with it. A new link has to be minted to let them in.",
727 phrase: "",
728 }),
729 },
720730 ];
721731
722732 function describeTarget(to: string, ctx: Ctx): Destructive | undefined {
test/invites.test.ts +10 −1
@@ -142,7 +142,16 @@ describe("invites: the settings form", () => {
142142 expect(page).not.toContain("invite_");
143143 const [item] = listInvites(app.db);
144144 expect(item!.state).toBe("open");
145 expect(page).toContain(`/admin/invites/${item!.id}/revoke`);
145 const to = `/admin/invites/${item!.id}/revoke`;
146 expect(page).toContain(`href="/-/confirm?to=${encodeURIComponent(to)}"`);
147 expect(page).not.toContain(`action="${to}"`);
148 expect(page).not.toContain("Nothing reads these yet.");
149 const confirm = await fetch(`${servers.url}/-/confirm?to=${encodeURIComponent(to)}`, { headers: { cookie: alice } });
150 expect(confirm.status).toBe(200);
151 const words = await confirm.text();
152 expect(words).toContain(`Revoke invite ${item!.id}?`);
153 expect(words).toContain("Whoever holds this link can no longer sign up with it.");
154 expect(words).toContain(`action="${to}"`);
146155 expect((await post(alice, `/admin/invites/${item!.id}/revoke`, {})).status).toBe(303);
147156 expect(listInvites(app.db)[0]!.state).toBe("revoked");
148157 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs