- Groups CRUD in sidebar (create, rename, change color, reorder, delete) - Collapsible group headers with color dots and server count - "Move to Group" context menu on servers - Group dropdown in ServerDialog (add/edit) - 17 i18n keys (EN/RU/ZH) - Search auto-expands groups - Cleaned up old release binaries Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
16 KiB
16 KiB
Server Groups — полный план UI/UX и реализации
Контекст
ServerManager хранит серверы в плоском списке. При 10+ серверах разных типов (SSH, SQL, Redis, VPN, API, тестовые) становится неудобно. Нужна организация по группам: "Production", "Testing", "VPN Servers", "Hosting" и т.д.
Исследование рынка
| Инструмент | Подход |
|---|---|
| MobaXterm, mRemoteNG, SecureCRT | Дерево папок, вложенность, drag-and-drop |
| Termius | Группы (вложенные) + теги + цвета — золотой стандарт |
| Royal TS | Папки + наследование credentials от папки |
| AWS/Azure/GCP | Resource Groups + теги (key:value) |
| Zabbix, Datadog | Host Groups + теги, сервер в нескольких группах |
Выбранный паттерн: Группы с цветами (как Termius) — один уровень групп, цветовая кодировка, сворачивание/разворачивание.
1. Модель данных
Группы хранятся в servers.json (тот же зашифрованный файл):
{
"servers": [...],
"ssh_key": {...},
"groups": [
{
"id": "a1b2c3d4",
"name": "Production",
"color": "#ef4444",
"collapsed": false,
"order": 0
},
{
"id": "e5f6g7h8",
"name": "Testing",
"color": "#22c55e",
"collapsed": false,
"order": 1
}
]
}
Сервер получает поле group:
{
"alias": "my-prod-server",
"ip": "1.2.3.4",
"type": "ssh",
"group": "a1b2c3d4",
...
}
groupссылается наgroups[].id- Без
groupили пустой = "Без группы" - При удалении группы серверы становятся ungrouped
Почему в servers.json, а не settings.json:
- Группы тесно связаны с серверами
- Одна атомарная операция save
ssh.pyпросто игнорирует ключgroups— обратная совместимость
2. API в ServerStore
# Группы CRUD
get_groups() -> list[dict] # Все группы, отсортированы по order
get_group(group_id) -> dict | None # Одна группа по ID
add_group(name, color) -> dict # Создать, вернуть dict
update_group(group_id, **kwargs) # Обновить name/color/collapsed/order
remove_group(group_id) # Удалить, серверы → ungrouped
reorder_groups(ordered_ids: list[str]) # Установить порядок
# Серверы в группах
set_server_group(alias, group_id|None) # Переместить сервер в группу
get_servers_in_group(group_id|None) # Серверы группы (None = ungrouped)
3. UI Sidebar — Сгруппированный вид
Было (плоский список):
+-------------------------------------+
| Servers |
+-------------------------------------+
| [ Search... ] |
+-------------------------------------+
| ● SSH investor 1.2.3.4 |
| ● SSH main-ovh 5.6.7.8 |
| ● SSH API TOR... 9.10.11.12 |
| ● RDP 116 Windows 13.14.15.16 |
| ● RDS Reddis main 5.6.7.8 |
| ● MDB Maria Db... 5.6.7.8 |
| ● SSH sensey24.ru 17.18.19.20 |
| |
| Active: 2 |
| [+ Add] [Edit] [Delete] |
+-------------------------------------+
Станет (группы):
+------------------------------------------+
| Servers |
+------------------------------------------+
| [ Search... ] [+ Grp] |
+------------------------------------------+
| |
| v ● Production (3) |
| +----------------------------------+ |
| | ○ SSH main-ovh 5.6.7.8 | |
| +----------------------------------+ |
| | ○ RDS Reddis main 5.6.7.8 | |
| +----------------------------------+ |
| | ○ MDB Maria Db 5.6.7.8 | |
| +----------------------------------+ |
| |
| > ● VPN/Proxy (2) |
| (свёрнуто — серверы скрыты) |
| |
| v ● Hosting (3) |
| +----------------------------------+ |
| | ○ SSH sensey24.ru 17.18.19.20 | |
| +----------------------------------+ |
| | ○ SSH git.sensey 17.18.19.20 | |
| +----------------------------------+ |
| | ○ SSH 1gb server 21.22.23.24 | |
| +----------------------------------+ |
| |
| v ● Testing (1) |
| +----------------------------------+ |
| | ○ SSH thehost 25.26.27.28 | |
| +----------------------------------+ |
| |
| Без группы (1) |
| +----------------------------------+ |
| | ○ RDP 116 Windows 13.14.15.16 | |
| +----------------------------------+ |
| |
| Active: 2 |
| [+ Add] [Edit] [Delete] |
+------------------------------------------+
Заголовок группы (детально):
+----------------------------------------------+
| v ● Production (3) |
| ^ ^ ^ ^ |
| | | | | |
| | цвет название кол-во |
| | группы группы серверов |
| стрелка |
| свернуть/развернуть |
+----------------------------------------------+
v/>— кликабельная стрелка toggle●— цветная точка (\u25cf) цветом группы- Название — жирный шрифт
(3)— серый, кол-во серверов- Вся строка кликабельна для toggle
- ПКМ — контекстное меню группы
Серверы внутри группы:
- Отступ
padx=(12, 2)для визуальной вложенности - Всё остальное как сейчас: StatusBadge, TypeBadge, Name, IP
Поведение:
- Нет групп → плоский список (как сейчас, полная обратная совместимость)
- Есть группы → автоматически сгруппированный вид
- Поиск → все группы разворачиваются, пустые группы скрываются
- "Без группы" → только если есть группы И есть серверы без группы
4. Создание группы
Кнопка [+ Grp] рядом с поиском:
| [ Search... ] [+ Grp] |
GroupDialog (новый файл gui/group_dialog.py):
+----------------------------------+
| New Group |
+----------------------------------+
| |
| Name: |
| [ Production ] |
| |
| Color: |
| (●)(●)(●)(●)(●)(●)(●)(●) |
| red org amb grn blu ind pur pnk |
| |
| [ Cancel ] [ Save ] |
+----------------------------------+
Палитра из 8 цветов:
GROUP_COLORS = [
"#ef4444", # красный
"#f97316", # оранжевый
"#f59e0b", # янтарный
"#22c55e", # зелёный
"#3b82f6", # синий
"#6366f1", # индиго
"#a855f7", # фиолетовый
"#ec4899", # розовый
]
Каждый цвет — кнопка-кружок. Выбранный — с рамкой.
5. Перемещение серверов между группами
Способ 1: ПКМ на сервере → "Переместить в группу"
+-----------------------------+
| Open Terminal |
| Browse Files |
| Install Key |
|-----------------------------|
| Move to Group > |
| +---------------------+ |
| | ● Production | |
| | ● VPN/Proxy | |
| | ● Hosting | |
| | ● Testing | |
| |---------------------| |
| | Без группы | |
| +---------------------+ |
|-----------------------------|
| Check Status |
| Copy Alias |
|-----------------------------|
| Edit |
| Delete |
+-----------------------------+
Способ 2: Поле "Группа" в ServerDialog (создание/редактирование)
+----------------------------------+
| Add Server |
+----------------------------------+
| Alias: [ ] |
| IP: [ ] |
| Type: [ SSH v ] |
| Port: [ 22 ] |
| Group: [ ● Production v ] |
| +-----------------------+|
| | No group ||
| | ● Production ||
| | ● VPN/Proxy ||
| | ● Hosting ||
| | ● Testing ||
| +-----------------------+|
| User: [ root ] |
| Pass: [ •••••••• [👁] ] |
| ... |
+----------------------------------+
- Dropdown появляется только когда есть группы
- По умолчанию "No group"
- При редактировании — предвыбрана текущая группа
Drag-and-drop:
НЕ реализуем. CustomTkinter не имеет нативного DnD для ScrollableFrame. Реализация через низкоуровневый tkinter DnD = хрупко и сложно. ПКМ "Move to Group" — надёжнее и понятнее.
6. Контекстное меню группы (ПКМ на заголовке)
+------------------------+
| Rename |
| Change Color > |
| +------------------+ |
| | ● Red | |
| | ● Orange | |
| | ● Amber | |
| | ● Green | |
| | ● Blue | |
| | ● Indigo | |
| | ● Purple | |
| | ● Pink | |
| +------------------+ |
|------------------------|
| Move Up |
| Move Down |
|------------------------|
| Delete Group |
+------------------------+
- Rename → открывает GroupDialog в режиме редактирования
- Change Color → подменю с 8 цветами
- Move Up/Down → меняет
orderместами с соседней группой - Delete Group → подтверждение, серверы → ungrouped
7. i18n ключи (~17 штук)
| Ключ | EN | RU | ZH |
|---|---|---|---|
| group | Group | Группа | 分组 |
| groups | Groups | Группы | 分组 |
| no_group | No group | Без группы | 无分组 |
| ungrouped | Ungrouped | Без группы | 未分组 |
| add_group | Add Group | Добавить группу | 添加分组 |
| edit_group | Edit Group | Редактировать группу | 编辑分组 |
| rename_group | Rename | Переименовать | 重命名 |
| delete_group | Delete Group | Удалить группу | 删除分组 |
| delete_group_confirm | Delete '{name}'? Servers become ungrouped. | Удалить '{name}'? Серверы станут без группы. | 删除'{name}'?服务器将变为未分组。 |
| group_name | Group Name | Название группы | 分组名称 |
| group_color | Color | Цвет | 颜色 |
| group_name_required | Group name is required | Название обязательно | 名称为必填项 |
| move_to_group | Move to Group | Переместить в группу | 移动到分组 |
| move_up | Move Up | Вверх | 上移 |
| move_down | Move Down | Вниз | 下移 |
| change_color | Change Color | Изменить цвет | 更改颜色 |
| new_group | New Group | Новая группа | 新建分组 |
8. Файлы и изменения
| Файл | Что меняется |
|---|---|
core/server_store.py |
+8 методов для Groups CRUD, get_servers_in_group(), set_server_group() |
gui/sidebar.py |
Рефакторинг _refresh_list() на grouped layout, заголовки групп, контекстные меню, collapse/expand, кнопка "+ Grp", "Move to Group" submenu |
gui/group_dialog.py |
НОВЫЙ — диалог создания/редактирования группы (имя + палитра цветов) |
gui/server_dialog.py |
+dropdown "Group" (виден только когда есть группы) |
core/i18n.py |
+17 ключей EN/RU/ZH |
gui/app.py |
Привязка sidebar.add_group_callback к открытию GroupDialog |
9. Порядок реализации
Фаза 1: Данные (без UI)
- Методы GroupsCRUD в
server_store.py - Проверить что
ssh.pyне ломается
Фаза 2: Sidebar — grouped rendering
- Рефакторинг
_refresh_list()на helper-методы _render_group_header()с collapse toggle- Отступ для серверов внутри групп
- Поиск с автораскрытием групп
Фаза 3: Управление группами
group_dialog.py- Кнопка "+ Grp" в sidebar
- Контекстное меню группы (rename, delete, reorder, color)
Фаза 4: Назначение серверов группам
- "Move to Group" в контекстном меню сервера
- Dropdown "Group" в ServerDialog
Фаза 5: i18n + polish
- Все переводы
- Edge cases: пустые группы, все ungrouped, одна группа
10. Edge Cases
- Нет групп → плоский список, полная обратная совместимость
- Удаление группы → серверы переходят в "Без группы"
- Группа ссылается на удалённый ID → сервер отображается как ungrouped
- Поиск → все группы разворачиваются, пустые скрываются
- Миграция → старый
servers.jsonбезgroups→get_groups()возвращает[] - ssh.py → игнорирует
groupsиgroupполя, нулевой impact
11. Верификация
python build.py— собрать exe- Без групп — плоский список как раньше
- Создать группу "Production" красным цветом
- Создать группу "Testing" зелёным
- Переместить серверы в группы через ПКМ
- Свернуть/развернуть группу
- Поиск — группы разворачиваются
- Добавить новый сервер — выбрать группу в диалоге
- Удалить группу — серверы стали ungrouped
- Переименовать группу, сменить цвет
- Move Up/Down — порядок меняется
- Переключить язык — все строки переведены