Add credentials panel on view page; remove copy buttons from dashboard cards

This commit is contained in:
2026-04-28 13:47:46 +00:00
parent 4cc19b32d8
commit beb6828520
3 changed files with 64 additions and 39 deletions
+64 -1
View File
@@ -2511,6 +2511,42 @@ def session_view_page(session_id: str, request: Request, user: User = Depends(re
except Exception: except Exception:
iframe_src = None iframe_src = None
if iframe_src: if iframe_src:
creds_html = ""
if service.type != ServiceType.RDP and (service.svc_login or service.svc_password):
rows = ""
if service.svc_login:
login_esc = service.svc_login.replace('"', '&quot;').replace('<', '&lt;')
rows += f'''<div class="cr-row"><span class="cr-label">Логин</span><span class="cr-val">{login_esc}</span><button class="cr-copy" data-copy="{login_esc}" title="Копировать">⎘</button></div>'''
if service.svc_password:
pass_esc = service.svc_password.replace('"', '&quot;').replace('<', '&lt;')
rows += f'''<div class="cr-row"><span class="cr-label">Пароль</span><span class="cr-val cr-masked">{pass_esc}</span><button class="cr-copy" data-copy="{pass_esc}" title="Копировать">⎘</button></div>'''
if service.svc_cred_hint:
hint_esc = service.svc_cred_hint.replace('<', '&lt;')
rows += f'''<p class="cr-hint">{hint_esc}</p>'''
creds_html = f'''
<div class="creds-panel" id="creds-panel">
<button class="creds-close" id="creds-close" title="Закрыть">✕</button>
{rows}
</div>
<script>
document.getElementById("creds-close").onclick = function() {{
document.getElementById("creds-panel").style.display = "none";
}};
document.querySelectorAll(".cr-copy").forEach(function(btn) {{
btn.addEventListener("click", async function() {{
var text = btn.dataset.copy;
try {{ await navigator.clipboard.writeText(text); }} catch(e) {{
var ta = document.createElement("textarea");
ta.value = text; ta.style.position = "fixed"; ta.style.opacity = "0";
document.body.appendChild(ta); ta.select();
document.execCommand("copy"); document.body.removeChild(ta);
}}
btn.classList.add("copied");
setTimeout(function() {{ btn.classList.remove("copied"); }}, 1500);
}});
}});
</script>'''
return HTMLResponse( return HTMLResponse(
content=f""" content=f"""
<!doctype html> <!doctype html>
@@ -2520,10 +2556,37 @@ def session_view_page(session_id: str, request: Request, user: User = Depends(re
<title>{service.name}</title> <title>{service.name}</title>
<style> <style>
html,body,iframe {{ margin:0; width:100%; height:100%; border:0; background:#0f1720; }} html,body,iframe {{ margin:0; width:100%; height:100%; border:0; background:#0f1720; }}
.creds-panel{{
position:fixed;right:16px;top:16px;z-index:999;
background:linear-gradient(180deg,rgba(15,24,36,.88),rgba(9,14,22,.94));
border:1px solid rgba(255,255,255,.22);backdrop-filter:blur(6px);
box-shadow:0 10px 28px rgba(0,0,0,.4);padding:10px 12px 11px;border-radius:14px;
min-width:220px;max-width:320px;
}}
.creds-close{{
position:absolute;top:6px;right:8px;background:none;border:none;
color:rgba(255,255,255,.55);font-size:14px;cursor:pointer;line-height:1;padding:2px 4px;
}}
.creds-close:hover{{color:#fff}}
.cr-row{{display:flex;align-items:center;gap:6px;margin-bottom:5px;}}
.cr-label{{font:600 11px/1 sans-serif;text-transform:uppercase;letter-spacing:.04em;
color:rgba(180,210,240,.7);min-width:46px;flex-shrink:0;}}
.cr-val{{font:600 13px/1 monospace;color:#dce8f5;flex:1;overflow:hidden;
text-overflow:ellipsis;white-space:nowrap;}}
.cr-masked{{letter-spacing:.1em;font-size:14px;}}
.cr-copy{{
flex-shrink:0;width:26px;height:26px;border:1px solid rgba(255,255,255,.26);
border-radius:6px;background:linear-gradient(180deg,#2a8cd6,#1668a6);
color:#fff;cursor:pointer;font-size:13px;display:grid;place-items:center;padding:0;
box-shadow:inset 0 1px 0 rgba(255,255,255,.22);transition:filter .15s;
}}
.cr-copy:hover{{filter:brightness(1.15)}}
.cr-copy.copied{{background:linear-gradient(180deg,#2ab86a,#1a8a4a);}}
.cr-hint{{margin:4px 0 0;font:400 11px/1.35 sans-serif;color:rgba(180,210,240,.65);}}
</style> </style>
</head> </head>
<body> <body>
<iframe src="{iframe_src}" allow="clipboard-read; clipboard-write"></iframe> <iframe src="{iframe_src}" allow="clipboard-read; clipboard-write"></iframe>{creds_html}
</body> </body>
</html> </html>
""".strip() """.strip()
-20
View File
@@ -392,26 +392,6 @@ button {
letter-spacing: .12em; letter-spacing: .12em;
font-size: 0.9rem; font-size: 0.9rem;
} }
.svc-cred-copy {
flex-shrink: 0;
width: 26px;
height: 26px;
border: 1px solid #b0c8de;
border-radius: 6px;
background: #fff;
color: #3a6d96;
cursor: pointer;
display: grid;
place-items: center;
padding: 0;
font-size: 0.78rem;
transition: background .15s, color .15s, border-color .15s;
font-family: sans-serif;
}
.svc-cred-copy::before { content: "\2398"; font-size: 0.9rem; }
.svc-cred-copy.copied { background: #1a8a4a; border-color: #1a8a4a; color: #fff; }
.svc-cred-copy.copied::before { content: "\2713"; }
.svc-cred-copy:hover:not(.copied) { background: #e6f0f9; border-color: #7aabcf; }
.tile-link { .tile-link {
position: absolute; position: absolute;
-18
View File
@@ -91,14 +91,12 @@
<div class="svc-cred-row"> <div class="svc-cred-row">
<span class="svc-cred-label">Логин</span> <span class="svc-cred-label">Логин</span>
<span class="svc-cred-value">{{ service.svc_login }}</span> <span class="svc-cred-value">{{ service.svc_login }}</span>
<button class="svc-cred-copy" type="button" data-copy="{{ service.svc_login }}" title="Копировать логин"></button>
</div> </div>
{% endif %} {% endif %}
{% if service.svc_password %} {% if service.svc_password %}
<div class="svc-cred-row"> <div class="svc-cred-row">
<span class="svc-cred-label">Пароль</span> <span class="svc-cred-label">Пароль</span>
<span class="svc-cred-value svc-cred-masked">{{ service.svc_password }}</span> <span class="svc-cred-value svc-cred-masked">{{ service.svc_password }}</span>
<button class="svc-cred-copy" type="button" data-copy="{{ service.svc_password }}" title="Копировать пароль"></button>
</div> </div>
{% endif %} {% endif %}
{% if service.svc_cred_hint %} {% if service.svc_cred_hint %}
@@ -219,21 +217,5 @@
}); });
})(); })();
</script> </script>
<script>
document.querySelectorAll('.svc-cred-copy').forEach(btn => {
btn.addEventListener('click', async (e) => {
e.preventDefault(); e.stopPropagation();
const text = btn.dataset.copy;
try { await navigator.clipboard.writeText(text); } catch(_) {
const ta = document.createElement('textarea');
ta.value = text; ta.style.position='fixed'; ta.style.opacity='0';
document.body.appendChild(ta); ta.select();
document.execCommand('copy'); document.body.removeChild(ta);
}
btn.classList.add('copied');
setTimeout(() => btn.classList.remove('copied'), 1500);
});
});
</script>
</body> </body>
</html> </html>