ultisuite-client/scripts/emit-authentik-brand.mjs
R3D347HR4Y 303b2b1074
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
wow
2026-06-11 01:22:40 +02:00

107 lines
3.7 KiB
JavaScript

/**
* Assets branding UltiSuite (SSO Authentik + favicons frontend).
*
* Sorties :
* - ulti-backend/deploy/authentik/branding/ultisuite-logo-{light,dark}.png
* - ulti-backend/deploy/authentik/branding/ultisuite-favicon{,-light,-dark}.png
* - public/ultisuite-32.png, public/ultisuite-180.png (favicon / apple-touch suite)
*
* pnpm run brand:authentik
*/
import { mkdirSync, readFileSync } from "node:fs"
import { dirname, join } from "node:path"
import { fileURLToPath } from "node:url"
import sharp from "sharp"
const __dirname = dirname(fileURLToPath(import.meta.url))
const root = join(__dirname, "..")
const markPath = join(root, "public/ultisuite-mark.svg")
const publicDir = join(root, "public")
const outDir = join(root, "../ulti-backend/deploy/authentik/branding")
/** Contenu interne du mark (sans la balise <svg>), pour l'inliner dans le lockup. */
function markInnerSvg() {
const raw = readFileSync(markPath, "utf8")
const open = raw.indexOf(">", raw.indexOf("<svg"))
const close = raw.lastIndexOf("</svg>")
return raw.slice(open + 1, close)
}
/**
* Lockup horizontal : mark + « UltiSuite » (texte plat, deux tons).
* `ulti` / `suite` = couleurs du texte selon le thème.
*/
function lockupSvg({ ulti, suite }) {
const inner = markInnerSvg()
return `<svg xmlns="http://www.w3.org/2000/svg" width="560" height="140" viewBox="0 0 560 140">
<g transform="translate(10 10) scale(3)">${inner}</g>
<text x="156" y="96" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="78" font-weight="700" letter-spacing="-2" fill="${ulti}">Ulti<tspan fill="${suite}">Suite</tspan></text>
</svg>`
}
async function writeLockup(outPath, colors) {
const buf = Buffer.from(lockupSvg(colors))
await sharp(buf, { density: 144 })
.trim({ threshold: 1 })
.extend({ top: 8, bottom: 8, left: 8, right: 8, background: { r: 0, g: 0, b: 0, alpha: 0 } })
.png({ compressionLevel: 9 })
.toFile(outPath)
}
async function writeFavicon(outPath, { size = 32, bg } = {}) {
const iconSize = Math.round(size * 0.84)
const iconBuf = await sharp(markPath)
.resize(iconSize, iconSize, {
fit: "contain",
background: { r: 0, g: 0, b: 0, alpha: 0 },
})
.png()
.toBuffer()
const background =
bg === "light"
? { r: 255, g: 255, b: 255, alpha: 1 }
: bg === "dark"
? { r: 24, g: 24, b: 27, alpha: 1 }
: { r: 0, g: 0, b: 0, alpha: 0 }
const offset = Math.round((size - iconSize) / 2)
await sharp({
create: { width: size, height: size, channels: 4, background },
})
.composite([{ input: iconBuf, left: offset, top: offset }])
.png({ compressionLevel: 9 })
.toFile(outPath)
}
async function main() {
mkdirSync(outDir, { recursive: true })
// Authentik (SSO de toute la suite)
await writeLockup(join(outDir, "ultisuite-logo-light.png"), {
ulti: "#202124",
suite: "#4285F4",
})
await writeLockup(join(outDir, "ultisuite-logo-dark.png"), {
ulti: "#e8eaed",
suite: "#8ab4f8",
})
await writeFavicon(join(outDir, "ultisuite-favicon.png"))
await writeFavicon(join(outDir, "ultisuite-favicon-light.png"), { bg: "light" })
await writeFavicon(join(outDir, "ultisuite-favicon-dark.png"), { bg: "dark" })
// Frontend (favicon PNG fallback + apple-touch de la suite)
await writeFavicon(join(publicDir, "ultisuite-32.png"))
await writeFavicon(join(publicDir, "ultisuite-180.png"), { size: 180, bg: "light" })
console.log("Wrote Authentik branding to", outDir)
console.log(" ultisuite-logo-light.png / ultisuite-logo-dark.png")
console.log(" ultisuite-favicon.png (+ light/dark variants)")
console.log("Wrote public/ultisuite-32.png and public/ultisuite-180.png")
}
main().catch((e) => {
console.error(e)
process.exit(1)
})