Активные сессии — строками: «Завершить» не вылезает за карточку; браузер и система одним словом

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
zuevav
2026-10-07 10:24:27 +03:00
parent 8f86f9d25a
commit ed598281e8
3 changed files with 54 additions and 4 deletions
+41
View File
@@ -486,6 +486,47 @@ type SessionView struct {
Created, Seen time.Time
}
// Device — браузер и система одним словом («Chrome · Windows») вместо строки User-Agent.
func (v SessionView) Device() string {
ua := v.UA
var b, o string
switch {
case strings.Contains(ua, "YaBrowser"):
b = "Яндекс Браузер"
case strings.Contains(ua, "Edg/"):
b = "Edge"
case strings.Contains(ua, "Firefox/"):
b = "Firefox"
case strings.Contains(ua, "Chrome/"):
b = "Chrome"
case strings.Contains(ua, "Safari/"):
b = "Safari"
case strings.Contains(ua, "curl/"):
b = "curl"
}
switch {
case strings.Contains(ua, "Windows"):
o = "Windows"
case strings.Contains(ua, "iPhone"), strings.Contains(ua, "iPad"):
o = "iOS"
case strings.Contains(ua, "Mac OS"):
o = "macOS"
case strings.Contains(ua, "Android"):
o = "Android"
case strings.Contains(ua, "Linux"):
o = "Linux"
}
switch {
case b != "" && o != "":
return b + " · " + o
case b != "" || o != "":
return b + o
case ua == "":
return "браузер неизвестен"
}
return "другой браузер"
}
// sessionID — короткий отпечаток токена: им список ссылается на сессию, сам токен в страницу не попадает.
func sessionID(token string) string {
sum := sha256.Sum256([]byte("bj-session-id:" + token))
@@ -145,10 +145,10 @@
<div class="card">
<h2>Активные сессии ({{len $U.Sessions}})</h2>
{{if $U.Sessions}}<table style="font-size:12.5px"><thead><tr><th>Пользователь</th><th>Вход</th><th>Активность</th><th>Адрес</th><th>Браузер</th><th></th></tr></thead><tbody>
{{range $U.Sessions}}<tr><td><b>{{.Login}}</b></td><td>{{msk .Created}}</td><td>{{msk .Seen}}</td><td><code>{{.IP}}</code></td><td class="muted" style="font-size:11px;max-width:320px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">{{.UA}}</td>
<td><form method="post" action="/admin/setup/users/session-drop"><input type="hidden" name="id" value="{{.ID}}"><button class="btn" type="submit">Завершить</button></form></td></tr>{{end}}
</tbody></table>{{else}}<p class="muted">Сессий нет.</p>{{end}}
{{if $U.Sessions}}<div class="ses-list">
{{range $U.Sessions}}<div class="ses"><span class="ses-main"><b>{{.Login}}</b><small>вход {{msk .Created}} · активность {{msk .Seen}} · <code>{{.IP}}</code> · <span title="{{.UA}}">{{.Device}}</span></small></span>
<form method="post" action="/admin/setup/users/session-drop" style="margin:0"><input type="hidden" name="id" value="{{.ID}}"><button class="btn btn-secondary btn-sm" type="submit">Завершить</button></form></div>{{end}}
</div>{{else}}<p class="muted">Сессий нет.</p>{{end}}
</div>
<div class="card">
@@ -365,6 +365,15 @@ label { font-size:13px; font-weight:500; }
.usr-del{background:none !important;border:0 !important;color:var(--err);font-size:13.5px;font-weight:500;padding:6px 2px;cursor:pointer}
.usr-del:hover{text-decoration:underline}
@media (max-width:600px){.usr-form{grid-template-columns:minmax(0,1fr)} .usr-main small{white-space:normal}}
/* активные сессии: строка — кто, когда, откуда; «Завершить» справа и не вылезает за карточку */
.ses-list{display:grid;gap:6px;margin-top:8px}
.ses{display:flex;align-items:center;gap:12px;padding:8px 12px;border:1px solid var(--border);border-radius:10px;background:var(--card)}
.ses-main{flex:1;min-width:0}
.ses-main b{display:block;font-size:14px}
.ses-main small{display:block;color:var(--muted);font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ses-main code{font-size:11.5px}
.ses .btn-sm{flex:none;padding:5px 12px;font-size:12.5px}
@media (max-width:600px){.ses-main small{white-space:normal}}
/* «Расширенные настройки» — под раскрытием, заголовком подблока */
.adv-box>summary{display:flex;flex-direction:column;gap:2px;cursor:pointer;list-style:none}
.adv-box>summary::-webkit-details-marker{display:none}