@@ -52,10 +52,54 @@ export function getRootPageHtml(): string {
5252 margin-right: 8px;
5353 display: inline-block;
5454 }
55+ .token-box {
56+ display: inline-flex;
57+ gap: 0.25rem;
58+ align-items: center;
59+ margin-left: 0.5rem;
60+ vertical-align: middle;
61+ }
62+ .token-input {
63+ width: 120px;
64+ padding: 0.25rem 0.5rem;
65+ font-family: monospace;
66+ font-size: 0.7rem;
67+ border: 1px solid var(--border-color, #ccc);
68+ border-radius: 4px;
69+ background: var(--bg-primary, white);
70+ color: var(--text-primary, #333);
71+ overflow: hidden;
72+ text-overflow: ellipsis;
73+ }
74+ .copy-btn {
75+ padding: 0.25rem 0.75rem;
76+ background: #0066cc;
77+ color: white;
78+ border: none;
79+ border-radius: 4px;
80+ cursor: pointer;
81+ font-size: 0.8rem;
82+ white-space: nowrap;
83+ transition: background 0.2s;
84+ }
85+ .copy-btn:hover {
86+ background: #0052a3;
87+ }
88+ .copy-btn:active {
89+ background: #003d7a;
90+ }
91+ .copy-btn.copied {
92+ background: #28a745;
93+ }
5594 @media (prefers-color-scheme: dark) {
5695 .architecture-diagram {
5796 background: var(--bg-secondary, #1a1a1a);
5897 }
98+ .token-input {
99+ background: var(--bg-primary, #2a2a2a);
100+ color: var(--text-primary, #e0e0e0);
101+ border-color: var(--border-color, #444);
102+ }
59103 }
60104 </style>
61105</head>
@@ -106,6 +150,25 @@ export function getRootPageHtml(): string {
106150 <a href="/items/docs" target="_blank">Open API / Swagger Docs</a>
107151 </p>
108152
153+ <p><strong>Kubernetes Dashboard:</strong></p>
154+ <p>
155+ <svg class="link-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
156+ <path d="M10.9 2.1l-7.2 4.2v8.4l7.2 4.2 7.2-4.2V6.3L10.9 2.1zm5.5 11.8l-5.5 3.2-5.5-3.2V7.5l5.5-3.2 5.5 3.2v6.4z" fill="#326CE5"/>
157+ <circle cx="10.9" cy="10.9" r="2.5" fill="#326CE5"/>
158+ </svg>
159+ <a href="https://kube.roussev.com" target="_blank">Headlamp</a> - k8s readonly UI (copy token to access)
160+ <span class="token-box">
161+ <input
162+ type="text"
163+ class="token-input"
164+ id="headlamp-token"
165+ value="eyJhbGciOiJSUzI1NiIsImtpZCI6IkYwNnVfdXZvVDdvbHhYZnVrTGhHLWg1ZEhZN1hoMHNCczEyWjBOUm5GU1EifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJoZWFkbGFtcC1yZWFkb25seSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJoZWFkbGFtcC1yZWFkb25seSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImE0ZDg0ZGU4LWRmNTctNDg2ZC1hZjJkLWYyZTlhYTkxMGFlNiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTpoZWFkbGFtcC1yZWFkb25seSJ9.Sr1CZT5gAXBtT6ERnbTEPsvuUxuhQ9HtqqykIHpnoWoSNSU363p4Hs4FJGQorEgen8ne0hH_cOOIhthy7djqFxN16ctHGEpzmGPmooOswK5fA0pSKBC6NWyfxiTjmvJVar6W7K7-unX7rIp6uPLI0ULZmPwqBx7ZgAbEbhBmNC0bxUFi2W7EqttRRhIeWcFIdfr9ww5M-1LJOLCb9Usnz6pPhW8QAIhZZ2looXEWT5zclRQc0JpkWpzWpoyG0pB2HRVMl-xlGytz1QMpD4kHuBC3gAcr5pZUco0DUIgpm8_7_yNuQ7mc9WWCJC8mEwaos1352a2cE_DwsOdhMdSIXQ"
166+ readonly
167+ />
168+ <button class="copy-btn" onclick="copyToken()">Copy</button>
169+ </span>
170+ </p>
171+
109172 <p><strong>API Endpoints:</strong></p>
110173 <p><a href="/items/v1/health" target="_blank">/v1/health</a> - Service and database health check</p>
111174 <p><a href="/items/v1/items" target="_blank">/v1/items</a> - List all items</p>
@@ -195,6 +258,33 @@ graph TB
195258 theme: window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'default'
196259 });
197260 </script>
261+
262+ <script>
263+ function copyToken() {
264+ const tokenInput = document.getElementById('headlamp-token');
265+ const copyBtn = event.target;
266+
267+ // Select and copy the token
268+ tokenInput.select();
269+ tokenInput.setSelectionRange(0, 99999); // For mobile devices
270+
271+ navigator.clipboard.writeText(tokenInput.value).then(() => {
272+ // Change button text and style
273+ const originalText = copyBtn.textContent;
274+ copyBtn.textContent = 'Copied!';
275+ copyBtn.classList.add('copied');
276+
277+ // Reset after 2 seconds
278+ setTimeout(() => {
279+ copyBtn.textContent = originalText;
280+ copyBtn.classList.remove('copied');
281+ }, 2000);
282+ }).catch(err => {
283+ console.error('Failed to copy:', err);
284+ alert('Failed to copy token. Please copy it manually.');
285+ });
286+ }
287+ </script>
198288</body>
199289</html>` ;
200290}
0 commit comments