Public: anyone can read this; the crew can change it.
Looks of `/` at phone and desktop width, signed out, are on file from the preview's own looks #12241
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/11313-looks-of-at-phone-and-desktop-width-sign → main 3 commits fork 2 threads · 2 unresolved
4 files
changed +50
−6
src/looks/service.ts +4 −2
| @@ -524,8 +524,10 @@ export function startLooks(app: App, options: LooksOptions): LooksHandle { | ||
| 524 | 524 | const asNobody = (kind: LookKind, page: string): boolean => |
| 525 | 525 | !sealed && |
| 526 | 526 | !(kind !== "thumb" && ownerPages.has(page)) && |
| 527 | !ownerLooks.some((l) => l.page === page && l.kind === kind) && | |
| 528 | (everyone || (kind === "thumb" && record.access === "public")); | |
| 527 | !ownerLooks.some((l) => l.page === page && l.kind === kind && l.as !== "nobody") && | |
| 528 | (everyone || | |
| 529 | ownerLooks.some((l) => l.page === page && l.as === "nobody") || | |
| 530 | (kind === "thumb" && record.access === "public")); | |
| 529 | 531 | const owner = getUserById(app.db, repo.ownerId); |
| 530 | 532 | const asOwner: GrantWho | undefined = |
| 531 | 533 | owner === undefined |
src/spawn/preview-looks.ts +15 −3
| @@ -158,11 +158,18 @@ export const PREVIEW_NEWCOMER = "preview-newcomer"; | ||
| 158 | 158 | export const PREVIEW_NEWCOMER_ORG = "preview-newcomer-forge"; |
| 159 | 159 | export const PREVIEW_NEWCOMER_PAGE = "/?as=newcomer"; |
| 160 | 160 | |
| 161 | /** | |
| 162 | * `/` as a stranger sees it (#11313). The home is a signed-in page for the owner, so | |
| 163 | * `/` itself is taken as the owner; this page is the same route with a query it ignores, | |
| 164 | * taken with no session at all, so its phone and desktop pictures are the signed-out home. | |
| 165 | */ | |
| 166 | export const PREVIEW_SIGNED_OUT_PAGE = "/?as=signedout"; | |
| 167 | ||
| 161 | 168 | export interface OwnerLook { |
| 162 | 169 | readonly page: string; |
| 163 | 170 | readonly kind: LookKind; |
| 164 | /** Taken as the newcomer, not the preview admin. */ | |
| 165 | readonly as?: "newcomer"; | |
| 171 | /** Taken as the newcomer, not the preview admin, or as nobody: no session at all. */ | |
| 172 | readonly as?: "newcomer" | "nobody"; | |
| 166 | 173 | } |
| 167 | 174 | |
| 168 | 175 | /** The named list: each page at desk and phone, taken as the owner. */ |
| @@ -190,6 +197,7 @@ export const PREVIEW_OWNER_LOOKS: readonly OwnerLook[] = [ | ||
| 190 | 197 | PREVIEW_RESILIENCE_PAGE, |
| 191 | 198 | PREVIEW_SEARCH_PAGE, |
| 192 | 199 | PREVIEW_NEWCOMER_PAGE, |
| 200 | PREVIEW_SIGNED_OUT_PAGE, | |
| 193 | 201 | PREVIEW_MACHINES_PAGE, |
| 194 | 202 | PREVIEW_DISK_OPEN_LOOK, |
| 195 | 203 | ...PREVIEW_SETTINGS_PAGES, |
| @@ -197,7 +205,11 @@ export const PREVIEW_OWNER_LOOKS: readonly OwnerLook[] = [ | ||
| 197 | 205 | "/forges", |
| 198 | 206 | ].flatMap((page) => |
| 199 | 207 | (["desktop", "phone"] as const).map((kind): OwnerLook => |
| 200 | page === PREVIEW_NEWCOMER_PAGE ? { page, kind, as: "newcomer" } : { page, kind }, | |
| 208 | page === PREVIEW_NEWCOMER_PAGE | |
| 209 | ? { page, kind, as: "newcomer" } | |
| 210 | : page === PREVIEW_SIGNED_OUT_PAGE | |
| 211 | ? { page, kind, as: "nobody" } | |
| 212 | : { page, kind }, | |
| 201 | 213 | ), |
| 202 | 214 | ); |
| 203 | 215 |
test/forge-looks.test.ts +16
| @@ -14,6 +14,7 @@ import { createApp, type App } from "../src/app.ts"; | ||
| 14 | 14 | import { createToken } from "../src/core/tokens.ts"; |
| 15 | 15 | import { createUser } from "../src/core/users.ts"; |
| 16 | 16 | import { buildRouter, serve, type Servers } from "../src/http/server.ts"; |
| 17 | import { PREVIEW_OWNER_LOOKS, PREVIEW_SIGNED_OUT_PAGE } from "../src/spawn/preview-looks.ts"; | |
| 17 | 18 | import { quietLogs } from "./helpers/globals.ts"; |
| 18 | 19 | |
| 19 | 20 | quietLogs(); |
| @@ -150,6 +151,21 @@ test("app.looks stays within 24 paths, each once", () => { | ||
| 150 | 151 | expect(new Set(looks).size).toBe(looks.length); |
| 151 | 152 | }); |
| 152 | 153 | |
| 154 | test("`/` signed out is its own look at phone and desktop, taken with no session (#11313)", async () => { | |
| 155 | // `/` is in looksAsOwner, so the service takes it as the owner; the signed-out home | |
| 156 | // is a page of its own whose looks the service takes as nobody. | |
| 157 | const { looks, looksAsOwner } = manifest(); | |
| 158 | expect(looks).toContain("/"); | |
| 159 | expect(looksAsOwner).toContain("/"); | |
| 160 | for (const kind of ["phone", "desktop"] as const) { | |
| 161 | expect(PREVIEW_OWNER_LOOKS).toContainEqual({ page: PREVIEW_SIGNED_OUT_PAGE, kind, as: "nobody" }); | |
| 162 | expect(PREVIEW_OWNER_LOOKS).toContainEqual({ page: "/", kind }); | |
| 163 | } | |
| 164 | const res = await fetch(`${servers.url}${PREVIEW_SIGNED_OUT_PAGE}`, { redirect: "manual" }); | |
| 165 | expect(res.status).toBe(200); | |
| 166 | expect(await res.text()).toContain("Start my forge"); | |
| 167 | }); | |
| 168 | ||
| 153 | 169 | test("a look below the top level is the preview seed's, taken as the owner", () => { |
| 154 | 170 | const { looks, looksAsOwner } = manifest(); |
| 155 | 171 | for (const p of looks.filter((p) => !isTopLevel(p))) { |
test/looks-owner.test.ts +15 −1
| @@ -45,7 +45,7 @@ import { | ||
| 45 | 45 | seedPreview, |
| 46 | 46 | } from "../src/spawn/preview-seed.ts"; |
| 47 | 47 | import { PREVIEW_CONFIG_FILES } from "../src/spawn/preview-config.ts"; |
| 48 | import { ownerLooksFor, PREVIEW_FORKS, PREVIEW_HELD_THEMES, PREVIEW_ITERATIONS_PAGE } from "../src/spawn/preview-looks.ts"; | |
| 48 | import { ownerLooksFor, PREVIEW_FORKS, PREVIEW_HELD_THEMES, PREVIEW_ITERATIONS_PAGE, PREVIEW_SIGNED_OUT_PAGE } from "../src/spawn/preview-looks.ts"; | |
| 49 | 49 | import { iterationsOf } from "../src/core/iterations.ts"; |
| 50 | 50 | import { localSource } from "../src/view/local.ts"; |
| 51 | 51 | import { isFormConfig, parseConfig } from "../src/web/render/config-format.ts"; |
| @@ -516,6 +516,20 @@ describe("looks of a public app", () => { | ||
| 516 | 516 | // The thumb is what a stranger sees, still. |
| 517 | 517 | expect(rows.find((l) => l.page === page && l.kind === "thumb")?.takenAs).toBe("nobody"); |
| 518 | 518 | } |
| 519 | // `/` signed out (#11313): its own page, phone and desktop, with no session in the grant. | |
| 520 | for (const [kind, width] of [["desktop", 1280], ["phone", 390]] as const) { | |
| 521 | expect(rows.find((l) => l.page === PREVIEW_SIGNED_OUT_PAGE && l.kind === kind)?.takenAs).toBe("nobody"); | |
| 522 | const call = calls.find( | |
| 523 | (c) => | |
| 524 | c.url.includes(`forge--${opened.value.number}.`) && | |
| 525 | new URL(c.url).searchParams.get("to") === PREVIEW_SIGNED_OUT_PAGE && | |
| 526 | c.width === width && | |
| 527 | c.dark !== true, | |
| 528 | ); | |
| 529 | expect(call).toBeDefined(); | |
| 530 | const grant = readGrant(dataDir, new URL(call!.url).searchParams.get("t") ?? "", { app: record, env }); | |
| 531 | expect(grant?.who).toBeUndefined(); | |
| 532 | } | |
| 519 | 533 | }, 120_000); |
| 520 | 534 | |
| 521 | 535 | test("production of the forge, and a preview of any other repository, add none", () => { |
new above
offline
This restates the constant and would still pass if the looks service stopped taking / signed out. Assert the behaviour, not the literal.