107 lines
3.7 KiB
JavaScript
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)
|
|
})
|