// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0 // Where the brand's logos are read from for a share image and an email: the app brand's from the // app's own public folder, an organization's through the same caller every page uses (so only a // member reads it). The branding module decides which one is drawn (`shareBrand`); this file only // fetches bytes, and the module's kit checks decide whether they are a logo. import { existsSync } from "node:fs" import { readFile } from "node:fs/promises" import { createRequire } from "node:module" import path from "node:path" import { type AppBrand, appBrand } from "@app/config" import { type AppIdentity, logoGet, type ShareReaders } from "@app/modules/branding" import type { LogoSource } from "@conusai-kit/brand/logo/check" import type { TransportCaller } from "@conusai-kit/core" const TYPES: Readonly> = { ".png": "image/png", ".webp": "image/webp", ".svg": "image/svg+xml" } const KIT_FAVICON = "/favicon.png" const KIT_FAVICON_FILE = "@conusai-kit/brand/logo/favicon.png" /** The app's public folder: `node server.mjs` and `vite dev` run in apps/web. */ export const publicDirectory = (): string => path.join(process.cwd(), "public") /** A file of the public folder as a logo source. The path is the app brand's (`defineBrand` keeps it on this origin); it is still held inside the folder. */ export async function appLogoSource(logoPath: string, directory: string = publicDirectory()): Promise { const contentType = TYPES[path.extname(logoPath).toLowerCase()] if (contentType === undefined) throw new Error("the app brand's logo is a PNG, WebP or SVG file") const root = path.resolve(/*turbopackIgnore: true*/ directory) const file = path.resolve(/*turbopackIgnore: true*/ root, `.${logoPath}`) if (!file.startsWith(`${root}${path.sep}`)) throw new Error("the app brand's logo is a file of the public folder") // The tab icon the build emits (vite.config.ts) is not in the public folder: it is the kit's own file. const theKits = logoPath === KIT_FAVICON && !existsSync(file) const source = theKits ? createRequire(import.meta.url).resolve(KIT_FAVICON_FILE) : file return { contentType, bytes: new Uint8Array(await readFile(/*turbopackIgnore: true*/ source)) } } export const appIdentity = (brand: AppBrand = appBrand): AppIdentity => ({ name: brand.name, logo: { light: brand.logo.light, dark: brand.logo.dark }, }) /** The readers for an organization's page: its own logos through the caller, the app's from the public folder. */ export function readersFor(caller: TransportCaller, org: string, directory?: string): ShareReaders { return { appLogo: (logoPath) => appLogoSource(logoPath, directory), async tenantLogo(variant) { const logo = await caller.call(logoGet, { org, variant }) return { contentType: logo.contentType, bytes: new Uint8Array(Buffer.from(logo.body, "base64")) } }, } } /** The readers for the app's own card: no organization, so an organization's logo is never asked for. */ export const appOnlyReaders = (directory?: string): ShareReaders => ({ appLogo: (logoPath) => appLogoSource(logoPath, directory), tenantLogo: () => Promise.reject(new Error("the app's own card has no organization")), })