diff --git a/server/bin/www.ts b/server/bin/www.ts index 1582748..9612a47 100755 --- a/server/bin/www.ts +++ b/server/bin/www.ts @@ -11,6 +11,7 @@ import { } from '../services/integrationRegistry/index.js'; import { logErrorJson, logJson } from '../utils/logging.js'; import { sleep } from '../utils/misc.js'; +import { printStartupBanner } from '../utils/startupBanner.js'; const { app, shutdown } = await getBottle().then(async (bottle) => makeServer(bottle.container), @@ -40,6 +41,7 @@ app.set('port', port); const server = http .createServer(app) .listen(port, () => { + printStartupBanner(); // eslint-disable-next-line no-restricted-syntax logJson(`Server is running at http://localhost:${port}`); }) diff --git a/server/utils/startupBanner.ts b/server/utils/startupBanner.ts new file mode 100644 index 0000000..121707e --- /dev/null +++ b/server/utils/startupBanner.ts @@ -0,0 +1,55 @@ +/* eslint-disable no-console */ + +// A small, one-time ASCII banner printed at server startup. It is intentionally +// only emitted to an interactive terminal (TTY) so it never clutters +// structured/JSON logs in production or CI environments. + +const LOGO = [ + ' +########++ ', + ' +#+ +# ', + ' +#+ +# ', + ' +#######+ +#+ +# ', + ' +#+ +#+ +#+ +# ', + ' +#+ +#+ +# +# ', + ' +#+ +#+#+ +# ', + ' +#####+ +#+ ', + ' +#+ +###++ ', + ' +#+ +#+ ', + ' +++++#+++++++#+ ', + ' +#+ +#+ +#+ ', + ' +#+ +#+ +#+ ', + ' ++#++#++#++ ', + ' +###+ ', +]; + +const LINKS = [ + ['ROOST', 'https://roost.tools'], + ['GitHub', 'https://github.com/roostorg/coop'], + ['Docs', 'https://roostorg.github.io/coop/latest'], + ['Discord', 'https://discord.gg/5Csqnw2FSQ'], +] as const; + +/** + * Prints a compact Coop banner with project links. No-op unless stdout is an + * interactive terminal, so it stays out of aggregated logs. + */ +export function printStartupBanner(): void { + if (!process.stdout.isTTY) { + return; + } + + const labelWidth = Math.max(...LINKS.map(([label]) => label.length)); + const linkLines = [ + 'ROOST · Open Source Trust & Safety', + 'Coop · Review and enforcement tool', + '', + ...LINKS.map(([label, url]) => ` ${label.padEnd(labelWidth)} ${url}`), + ]; + + const lines = LOGO.map((logoLine, i) => { + const text = linkLines[i] ?? ''; + return ` ${logoLine} ${text}`; + }); + + console.log(`\n${lines.join('\n')}\n`); +}