this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 116 lines 4.0 kB view raw
1/** 2 * Public JSON projection for registry read APIs (`/api/registry/profile/*`, 3 * `/api/registry/search`, `/api/registry/featured`). 4 * 5 * Strips AppView-only moderation and verification workflow fields 6 * (takedown columns, per-icon review, icon-access request metadata, etc.) 7 * so anonymous API consumers never see internal operational data. 8 * 9 * Includes a boolean `verified` when the project has the same public 10 * “verified” badge as Explore (`icon_access_status === 'granted'`), without 11 * exposing emails, timestamps, or admin DIDs. 12 */ 13import type { LinkEntry, ScreenshotEntry } from "./lexicons.ts"; 14import type { ProfileRow } from "./registry.ts"; 15import { bskyCdnAvatarUrl } from "./avatar.ts"; 16 17export interface PublicProfileJson { 18 did: string; 19 handle: string; 20 name: string; 21 description: string; 22 mainLink: string | null; 23 iosLink: string | null; 24 androidLink: string | null; 25 categories: string[]; 26 subcategories: string[]; 27 links: LinkEntry[]; 28 screenshots: ScreenshotEntry[]; 29 /** Fully-qualified URLs for lazily loaded detail-page screenshots. */ 30 screenshotUrls: string[]; 31 avatarCid: string | null; 32 avatarMime: string | null; 33 /** Fully-qualified URL for the profile avatar image, or null. */ 34 avatarUrl: string | null; 35 /** 36 * True when the project shows the public verified badge on Explore 37 * (admin-approved verification). No other verification metadata is exposed. 38 */ 39 verified: boolean; 40 /** 41 * Developer-facing SVG icon URL when the icon is approved and the project 42 * is verified; otherwise null. Raw `iconCid` / review state are not exposed. 43 */ 44 iconUrl: string | null; 45 /** 46 * Optional black-and-white companion to `iconUrl`. Same gating 47 * (verified project + approved variant); null when not present. 48 */ 49 iconBwUrl: string | null; 50 pdsUrl: string; 51 recordCid: string; 52 recordRev: string; 53 createdAt: number; 54 indexedAt: number; 55 /** Present when this profile appears in the featured join (search / featured lists). */ 56 featured?: ProfileRow["featured"]; 57} 58 59export function toPublicProfileJson( 60 profile: ProfileRow, 61 origin: string, 62): PublicProfileJson { 63 const avatarUrl = profile.avatarCid 64 ? bskyCdnAvatarUrl(profile.did, profile.avatarCid) 65 : null; 66 const verified = profile.iconAccessStatus === "granted"; 67 const iconUrl = profile.iconCid && 68 profile.iconStatus === "approved" && 69 profile.iconAccessStatus === "granted" 70 ? `${origin}/api/registry/icon/${encodeURIComponent(profile.did)}?v=${ 71 encodeURIComponent(profile.iconCid) 72 }` 73 : null; 74 const iconBwUrl = profile.iconBwCid && 75 profile.iconBwStatus === "approved" && 76 profile.iconAccessStatus === "granted" 77 ? `${origin}/api/registry/icon-bw/${encodeURIComponent(profile.did)}?v=${ 78 encodeURIComponent(profile.iconBwCid) 79 }` 80 : null; 81 const screenshotUrls = profile.screenshots.map((_, i) => 82 `${origin}/api/registry/screenshot/${encodeURIComponent(profile.did)}/${i}` 83 ); 84 85 const out: PublicProfileJson = { 86 did: profile.did, 87 handle: profile.handle, 88 name: profile.name, 89 description: profile.description, 90 mainLink: profile.mainLink, 91 iosLink: profile.iosLink, 92 androidLink: profile.androidLink, 93 categories: profile.categories, 94 subcategories: profile.subcategories, 95 // `website` was the former Landing Page button. The current public 96 // API exposes the primary web destination via `mainLink` instead. 97 links: profile.links.filter((entry) => entry.kind !== "website"), 98 screenshots: profile.screenshots, 99 screenshotUrls, 100 avatarCid: profile.avatarCid, 101 avatarMime: profile.avatarMime, 102 avatarUrl, 103 verified, 104 iconUrl, 105 iconBwUrl, 106 pdsUrl: profile.pdsUrl, 107 recordCid: profile.recordCid, 108 recordRev: profile.recordRev, 109 createdAt: profile.createdAt, 110 indexedAt: profile.indexedAt, 111 }; 112 if (profile.featured !== undefined) { 113 out.featured = profile.featured; 114 } 115 return out; 116}