Keep listed published rules public and guest-claim rules unlisted, with semantic document markup and share chrome that stays clickable.

Guest publishes remain share-by-link but stay out of the catalog and sitemap, and their pages send noindex until claimed. The published-rule view now uses heading and list markup matching Markdown export, and top banners sit below the header so Share is not covered.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
adilalloandCursor committed 2026-09-17 17:42:01 -06:00
1 parent 77b0ec8ad8
commit 1445cb7e1e
27 files changed
+524 -86

No files matched your search

+34 -1
View File
@@ -95,6 +95,8 @@ describe("GET /api/rules/[id]", () => {
document: { sections: [] },
createdAt: new Date("2026-01-01T00:00:00Z"),
updatedAt: new Date("2026-01-02T00:00:00Z"),
userId: "user-1",
claimTokenHash: null,
};
findUniqueMock.mockResolvedValueOnce(row);
const res = await GET(
@@ -103,16 +105,47 @@ describe("GET /api/rules/[id]", () => {
);
expect(res.status).toBe(200);
const body = (await res.json()) as {
rule: { id: string; title: string; summary: string | null };
rule: {
id: string;
title: string;
summary: string | null;
isListed: boolean;
};
viewerIsOwner: boolean;
};
expect(body.rule.id).toBe("rule-1");
expect(body.rule.title).toBe("Mutual Aid Mondays");
expect(body.rule.summary).toBe("A grassroots community in Denver.");
expect(body.rule.isListed).toBe(true);
expect(body.viewerIsOwner).toBe(false);
expect(findUniqueMock).toHaveBeenCalledTimes(1);
});
it("marks guest-claim rules as unlisted without exposing the claim token", async () => {
isDatabaseConfiguredMock.mockReturnValue(true);
findUniqueMock.mockResolvedValueOnce({
id: "guest-1",
title: "Unlisted",
summary: null,
document: {},
createdAt: new Date("2026-01-01T00:00:00Z"),
updatedAt: new Date("2026-01-02T00:00:00Z"),
userId: null,
claimTokenHash: "secret-hash",
});
const res = await GET(
new NextRequest("https://x.test/api/rules/guest-1"),
makeContext("guest-1"),
);
expect(res.status).toBe(200);
const body = (await res.json()) as {
rule: { isListed: boolean; claimTokenHash?: unknown; userId?: unknown };
};
expect(body.rule.isListed).toBe(false);
expect(body.rule).not.toHaveProperty("claimTokenHash");
expect(body.rule).not.toHaveProperty("userId");
});
it("returns viewerIsOwner true when the signed-in user owns the rule", async () => {
isDatabaseConfiguredMock.mockReturnValue(true);
getSessionUserMock.mockResolvedValue({ id: "user-1", email: "a@b.c" });