@@ -0,0 +1,57 @@
|
||||
// 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<Record<string, string>> = { ".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<LogoSource> {
|
||||
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")),
|
||||
})
|
||||
Reference in New Issue
Block a user