feat: 유저별 로고·브랜드명 커스터마이징 — .smallclaw/users/<user>/logo.png, brand_name.txt

헤더/환영화면/채팅 아바타·라벨을 로그인 유저별로 스왑. 파일 없으면 기본 HomeClaw 유지.
애플·자스민 계정에 기본 로고(이모지 아이콘) 적용.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E5kyXurhNSboRcY3dAGceY
This commit is contained in:
kim
2026-09-11 16:13:38 +09:00
co-authored by Claude Sonnet 5
parent aaf8dbfb33
commit c22377778e
7 changed files with 54 additions and 9 deletions
+29
View File
@@ -2496,6 +2496,7 @@ app.get('/api/auth/status', (_req, res) => {
authenticated: !!session,
username: session?.username ?? null,
role: session?.role ?? null,
brandName: getUserBrandName(session?.username),
});
});
@@ -2836,6 +2837,34 @@ app.all('/api/localllm/:provider/{*subPath}', async (req: express.Request, res:
}
});
// Per-user brand name override, e.g. <configDir>/users/<username>/brand_name.txt
// containing "체리클로" — falls back to "HomeClaw" when absent.
function getUserBrandName(username: string | null | undefined): string | null {
if (!username) return null;
try {
const p = path.join(getConfig().getConfigDir(), 'users', username, 'brand_name.txt');
if (fs.existsSync(p)) {
const name = fs.readFileSync(p, 'utf8').trim();
if (name) return name.slice(0, 40);
}
} catch {}
return null;
}
// Per-user logo — falls back to the default HomeClaw logo when the user has
// no custom logo file. Drop a PNG at <configDir>/users/<username>/logo.png
// to override (e.g. an old family nickname logo).
app.get('/api/branding/logo', (req, res) => {
const username = (req as any).user?.username as string | undefined;
const defaultLogo = path.join(webUiPath, 'homeclaw_logo.png');
res.setHeader('Cache-Control', 'no-cache');
if (username) {
const custom = path.join(getConfig().getConfigDir(), 'users', username, 'logo.png');
if (fs.existsSync(custom)) return res.sendFile(custom);
}
res.sendFile(defaultLogo);
});
app.use(express.static(webUiPath, { setHeaders: (res) => { res.setHeader('Cache-Control', 'no-cache'); } }));
// Serve code directory files for HTML preview (window.open)