| 1 | /** |
| 2 | * Public-site account-entry aliases (#5767). |
| 3 | * |
| 4 | * codewhale.net is not the signed-in CWC app. `/signin`, `/signup`, and |
| 5 | * `/auth/callback` used to locale-prefix into `/en/...` and 404. This module |
| 6 | * is the one mapping: callback hops to the CWC app immediately (query |
| 7 | * preserved); sign-in / create-account are locale-aware public pages that |
| 8 | * send the person to app.codewhale.net without implying a local CLI account |
| 9 | * is required. |
| 10 | */ |
| 11 | import { APP_URL } from "./i18n/links"; |
| 12 | import { pathLocale } from "./i18n/path"; |
| 13 | |
| 14 | /** |
| 15 | * App-icon raster (1254×1254) used on public account-entry pages: the canonical |
| 16 | * mark in the current brand treatment — blue gradient glyph on a white rounded |
| 17 | * tile. The master vectors are the canonical family in |
| 18 | * `codewhale-apps/packages/brand/svg/` (byte copies in `web/public/brand/`) — |
| 19 | * never hand-edit this PNG, regenerate it from the family. Pinned by hash so a |
| 20 | * redrawn or stale substitute fails the test rather than shipping. |
| 21 | */ |
| 22 | export const CANONICAL_MARK_SRC = "/brand/codewhale-mark.png"; |
| 23 | export const CANONICAL_MARK_SHA256 = |
| 24 | "7c174d1125b279d6c8200c776a6ed7b89a21e18bbf1c6e7dccf486a7a6879d8f"; |
| 25 | |
| 26 | export const APP_AUTH_CALLBACK_URL = `${APP_URL}/auth/callback`; |
| 27 | |
| 28 | const SIGN_IN_SLUGS = new Set(["signin"]); |
| 29 | const SIGN_IN_ALIASES = new Set(["login"]); |
| 30 | const SIGN_UP_SLUGS = new Set(["signup"]); |
| 31 | const SIGN_UP_ALIASES = new Set(["register", "create-account"]); |
| 32 | |
| 33 | export type PublicAuthKind = "sign-in" | "sign-up" | "callback"; |
| 34 | |
| 35 | /** Path after a routed locale prefix, with no leading slash. */ |
| 36 | export function publicAuthRemainder(pathname: string): string { |
| 37 | const segments = pathname.split("/").filter(Boolean); |
| 38 | if (pathLocale(pathname)) segments.shift(); |
| 39 | return segments.join("/"); |
| 40 | } |
| 41 | |
| 42 | export function publicAuthKind(pathname: string): PublicAuthKind | null { |
| 43 | const remainder = publicAuthRemainder(pathname); |
| 44 | if (remainder === "auth/callback") return "callback"; |
| 45 | if (SIGN_IN_SLUGS.has(remainder) || SIGN_IN_ALIASES.has(remainder)) return "sign-in"; |
| 46 | if (SIGN_UP_SLUGS.has(remainder) || SIGN_UP_ALIASES.has(remainder)) return "sign-up"; |
| 47 | return null; |
| 48 | } |
| 49 | |
| 50 | /** |
| 51 | * Fold `/login` onto `/signin` and `/register` onto `/signup`, keeping the |
| 52 | * locale prefix when one is already present. Bare aliases still go through |
| 53 | * the locale-prefix branch after this rewrite so `/login` becomes `/en/signin` |
| 54 | * rather than a 404. |
| 55 | */ |
| 56 | export function canonicalPublicAuthPath(pathname: string): string | null { |
| 57 | const remainder = publicAuthRemainder(pathname); |
| 58 | const canonical = |
| 59 | SIGN_IN_ALIASES.has(remainder) ? "signin" |
| 60 | : SIGN_UP_ALIASES.has(remainder) ? "signup" |
| 61 | : null; |
| 62 | if (!canonical) return null; |
| 63 | const locale = pathLocale(pathname); |
| 64 | return locale ? `/${locale}/${canonical}` : `/${canonical}`; |
| 65 | } |
| 66 | |
| 67 | /** CWC app destination. Chinese public pages keep the `/zh/` app entry. */ |
| 68 | export function publicAuthAppDestination( |
| 69 | kind: Exclude<PublicAuthKind, "callback">, |
| 70 | locale: string | null, |
| 71 | ): string { |
| 72 | const localized = locale === "zh" ? "/zh" : ""; |
| 73 | const path = kind === "sign-in" ? "/login" : "/signup"; |
| 74 | return `${APP_URL}${localized}${path}`; |
| 75 | } |
| 76 | |
| 77 | /** OAuth/email callbacks belong on the CWC app, never a localized 404. */ |
| 78 | export function publicAuthCallbackDestination(url: URL): string | null { |
| 79 | if (publicAuthKind(url.pathname) !== "callback") return null; |
| 80 | const destination = new URL(APP_AUTH_CALLBACK_URL); |
| 81 | destination.search = url.search; |
| 82 | return destination.toString(); |
| 83 | } |
| 84 |