Files
famapp/scripts/generate-icons.mjs
T
ginnoir c73338e256 Code-side
src/lib/dev-login-config.ts — startup assertion: throws if NODE_ENV=production + ENABLE_DEV_LOGIN=true, scoped to runtime (skipped during next build).
Container

scripts/migrate.mjs — runs Drizzle migrations against DATABASE_URL.
deploy/docker-entrypoint.sh — runs migrations then exec node server.js. Skip with RUN_MIGRATIONS=false.
Dockerfile — copies drizzle/, scripts/migrate.mjs, entrypoint into runner stage; ENTRYPOINT now points at the script.
Compose

deploy/compose.yaml — famapp now image: ${FAMAPP_IMAGE:-ghcr.io/ginnoir/famapp:latest} (build still works locally as fallback). Authentik pinned via AUTHENTIK_IMAGE_TAG (default 2024.12.3). New RUN_MIGRATIONS env passed through.
.env.production.example — documents FAMAPP_IMAGE, AUTHENTIK_IMAGE_TAG, RUN_MIGRATIONS.
CI/CD

.github/workflows/ci.yml — push/PR: typecheck + lint + format:check + build.
.github/workflows/release.yml — v* tag: build + push ghcr.io/ginnoir/famapp:vX.Y.Z, :X.Y, :latest to GHCR.
Docs

deploy/README.md — full deploy/rollback/release runbook.
CHANGELOG.md — release log seeded with an Unreleased entry.
docs/tasks/09-pre-deploy-checklist.md — task 09 reframed from one-shot removal to a recurring pre-deploy checklist.
STATUS.md — updated.
Verified: pnpm typecheck, pnpm format, pnpm build, and docker compose config all clean.
2026-05-06 17:37:37 -05:00

101 lines
2.7 KiB
JavaScript
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.
/**
* Generates placeholder PNG icons for the famapp PWA.
*
* Usage: node scripts/generate-icons.mjs
*
* Produces:
* public/icon-192.png
* public/icon-384.png
* public/icon-512.png
* public/icon-512-maskable.png (same image; OS applies its own mask)
* public/icon-180.png (apple-touch-icon)
*
* All images are solid indigo (#4F46E5) squares — replace with a real
* branded export from icon.svg when assets are finalised.
*/
import { deflateSync } from "zlib";
import { writeFileSync } from "fs";
import { resolve, dirname } from "path";
import { fileURLToPath } from "url";
const __dir = dirname(fileURLToPath(import.meta.url));
const publicDir = resolve(__dir, "../public");
// Build CRC-32 lookup table once.
const CRC_TABLE = new Uint32Array(256);
for (let i = 0; i < 256; i++) {
let c = i;
for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
CRC_TABLE[i] = c;
}
function crc32(buf) {
let crc = 0xffffffff;
for (let i = 0; i < buf.length; i++) crc = CRC_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
return (crc ^ 0xffffffff) >>> 0;
}
function u32(n) {
const b = Buffer.alloc(4);
b.writeUInt32BE(n, 0);
return b;
}
function pngChunk(type, data) {
const typeBytes = Buffer.from(type, "ascii");
const crc = u32(crc32(Buffer.concat([typeBytes, data])));
return Buffer.concat([u32(data.length), typeBytes, data, crc]);
}
/**
* Returns a Buffer containing a valid PNG of size×size filled with (r, g, b).
*/
function solidPNG(size, r, g, b) {
const PNG_SIG = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
const ihdr = pngChunk(
"IHDR",
Buffer.concat([
u32(size),
u32(size),
Buffer.from([8, 2, 0, 0, 0]), // 8-bit RGB, no interlace
]),
);
// One filter byte (0 = None) followed by size×3 RGB bytes per row.
const rowLen = 1 + size * 3;
const raw = Buffer.alloc(size * rowLen);
for (let y = 0; y < size; y++) {
const base = y * rowLen;
raw[base] = 0;
for (let x = 0; x < size; x++) {
raw[base + 1 + x * 3] = r;
raw[base + 2 + x * 3] = g;
raw[base + 3 + x * 3] = b;
}
}
const idat = pngChunk("IDAT", deflateSync(raw));
const iend = pngChunk("IEND", Buffer.alloc(0));
return Buffer.concat([PNG_SIG, ihdr, idat, iend]);
}
// Indigo-600 (#4F46E5)
const [R, G, B] = [0x4f, 0x46, 0xe5];
const icons = [
{ name: "icon-192.png", size: 192 },
{ name: "icon-384.png", size: 384 },
{ name: "icon-512.png", size: 512 },
{ name: "icon-512-maskable.png", size: 512 },
{ name: "icon-180.png", size: 180 },
];
for (const { name, size } of icons) {
const out = resolve(publicDir, name);
writeFileSync(out, solidPNG(size, R, G, B));
console.log(` ✓ public/${name} (${size}×${size})`);
}