la-rose/scripts/fetch-logo.ts
semianiakavy 55b4ebc9a1 feat: use the source wordmark as the store logo
Replace the text brand in the header and footer with the logo the source site
publishes for its footer (637x220 PNG, BRAND.logoSourceUrl):

- src/store/constants.ts: add BRAND (source URL, local path, dimensions).
- scripts/fetch-logo.ts: mirror it into public/images/brand/logo_footer.png
  (idempotent, --force to refresh); `pnpm fetch:logo` runs it standalone.
- scripts/dev-server.ts: ensure the logo at boot, non-fatal on failure.
- public/index.html: <img class="brand__logo"> in the header (the wordmark is
  dark on the light header) and <img class="footer__logo"> in the footer, on a
  white plate because the footer is dark.
- public/styles.css: logo sizing (30px / 40px from 768px) + the footer plate.
2026-10-05 17:39:30 +00:00

78 lines
3.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { mkdir, stat, writeFile } from 'node:fs/promises';
import { dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { BRAND } from '../src/store/constants';
/**
* Mirror the store wordmark into public/images/brand/logo_footer.png.
*
* Source: the logo the site publishes for its footer (BRAND.logoSourceUrl, 637×220 PNG).
* The storefront ships that file instead of hotlinking la-rose.by — public/index.html
* points at BRAND.logoLocalPath — and this script keeps the local copy on disk.
*
* Idempotent: an existing non-empty file is kept; pass `--force` to re-download it
* (the source is a static theme asset, so a plain re-run is a no-op).
*
* pnpm fetch:logo # download only when missing
* pnpm fetch:logo -- --force # refresh the local copy
*
* The dev server also calls `ensureBrandLogo()` once at boot (scripts/dev-server.ts),
* so a fresh checkout gets the logo without an extra manual step.
*/
const USER_AGENT =
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36 la-rose-seed/1.0';
const REQUEST_TIMEOUT_MS = 20_000;
const PUBLIC_DIR = fileURLToPath(new URL('../public', import.meta.url));
/** Absolute path of the local file served at BRAND.logoLocalPath. */
export const BRAND_LOGO_FILE = join(PUBLIC_DIR, ...BRAND.logoLocalPath.split('/').filter(Boolean));
export type BrandLogoStatus = 'kept' | 'downloaded';
/** Ensure the local logo copy is on disk; download it when missing (or when forced). */
export async function ensureBrandLogo(force = false): Promise<BrandLogoStatus> {
if (!force) {
const info = await stat(BRAND_LOGO_FILE).catch(() => null);
if (info && info.size > 0) return 'kept';
}
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
try {
const response = await fetch(BRAND.logoSourceUrl, {
headers: { 'user-agent': USER_AGENT, accept: 'image/png,image/*;q=0.9,*/*;q=0.5' },
redirect: 'follow',
signal: controller.signal,
});
if (!response.ok) throw new Error(`HTTP ${response.status} ${response.statusText}`);
const contentType = response.headers.get('content-type') ?? '';
if (!contentType.startsWith('image/')) {
throw new Error(`unexpected content-type "${contentType || 'none'}"`);
}
const bytes = Buffer.from(await response.arrayBuffer());
if (bytes.length === 0) throw new Error('empty response body');
await mkdir(dirname(BRAND_LOGO_FILE), { recursive: true });
await writeFile(BRAND_LOGO_FILE, bytes);
return 'downloaded';
} finally {
clearTimeout(timer);
}
}
const isDirectRun =
process.argv[1] !== undefined && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
if (isDirectRun) {
const status = await ensureBrandLogo(process.argv.includes('--force'));
const kb = ((await stat(BRAND_LOGO_FILE)).size / 1024).toFixed(1);
console.log(
status === 'kept'
? `fetch-logo: kept ${BRAND.logoLocalPath} (${kb} kB)`
: `fetch-logo: downloaded ${BRAND.logoLocalPath} (${kb} kB)`,
);
}