DEVICES_PANEL.md 31 KB

Legacy Admin Panel - Анализ ключевых фич

Расположение

/home/user/work/luckfox/mybeacon-legacy/www/my-wifi/panel/

Структура legacy админки

3 основные страницы:

  1. Devices (index.php) - управление устройствами (радарами)
  2. Clients (clients.php) - управление клиентами (организациями)
  3. Add Device (adddev.php) - добавление клиента с устройствами

КЛЮЧЕВЫЕ ФИЧИ (без технической реализации)

1. Devices Panel - Управление устройствами

A. Универсальный поиск ⭐ КЛЮЧЕВАЯ ФИЧА

Что делает:

  • Пользователь вводит любой текст в одно поле
  • Система ищет по всем колонкам таблицы одновременно
  • Фильтрация в реальном времени (при вводе)

Примеры поиска:

  • ac:84 → найдет все MAC-адреса содержащие "ac:84"
  • 192.168 → найдет все устройства с IP в этой подсети
  • Office_WiFi → найдет все устройства подключенные к этому SSID
  • beacon.ru → найдет все устройства с этим адресом отправки данных

Почему важно:

  • Админ не знает точный MAC → вводит часть → быстро находит
  • Админ помнит что "где-то было 192.168..." → вводит → находит
  • Не нужно помнить в какой колонке искать
  • Один поисковый запрос вместо 15 фильтров

Решение для нового проекта: ✅ ТАЩИМ - универсальное поисковое поле с поиском по всем полям


B. Компактная таблица с максимумом информации

Что делает:

  • Вся важная информация видна сразу в таблице
  • Не нужно кликать на строку чтобы увидеть детали
  • Все 15 полей конфига видны в одной строке

Колонки:

#  MAC         time      wf_ssid  wf_psk  ovpn_flag  ovpn_addr  wf_flag  wf_addr  bt_flag  bt_addr  fw_flag  fw_addr  reboot_flag  ip
1  ac:84:...   10:25     Office   pass123      1      https://      1     https://    1     https://    0       -           0      192.168.5.244

Почему важно:

  • Быстрый просмотр состояния всех устройств
  • Сравнение конфигов разных устройств
  • Видно кто онлайн (last seen), кто офлайн

Решение для нового проекта: ⚠️ ЧАСТИЧНО - компактная таблица с основным, детали в модальном окне


C. Быстрое редактирование прямо в таблице (inline editing)

Что делает:

  • Кликнул на ячейку → она превратилась в input
  • Изменил значение → нажал "Ok" → сохранено
  • Не нужно открывать формы, модальные окна
  • Изменение 1 поля = 2 клика (клик на ячейку + клик Ok)

Use case:

  • Админ видит что у device #5 неправильный wf_ssid
  • Кликает на ячейку
  • Меняет "Office" на "Office_Guest"
  • Клик Ok → сохранено
  • Время: 5 секунд

Почему опасно:

  • Случайный клик → случайное изменение
  • Нет подтверждения
  • Нет валидации
  • Нет истории кто и что изменил

Решение для нового проекта: ❌ НЕ ТАЩИМ - слишком опасно, заменяем модальными окнами с подтверждением


D. Сортировка по любой колонке

Что делает:

  • Клик на заголовок колонки → сортировка по возрастанию
  • Еще клик → сортировка по убыванию
  • Работает для всех колонок (кроме действий)

Use cases:

  • Отсортировать по time → увидеть кто давно не подключался
  • Отсортировать по MAC → найти все устройства одного производителя
  • Отсортировать по ip → сгруппировать по подсетям

Решение для нового проекта: ✅ ТАЩИМ - сортировка обязательно


2. Clients Panel - Управление клиентами (организациями)

A. Список клиентов с их устройствами

Что показывает:

#  Логин                  Пароль     Устройства              Оплачено до  Информация               Оплата
1  shop@example.com       12345      ac:84:c6:42:17:90      2025-05-01   ИП Иванов, +79001234567  Оплачено до мая
                                     cc:2d:e0:ca:9f:7e
                                     d8:0d:17:5e:07:94

2  mall@example.com       54321      b8:27:eb:c1:46:0e      2025-03-15   ТЦ "Мега", Петров        Лимит 1000

Ключевая фича:

  • Сразу видно сколько устройств у клиента
  • MAC-адреса отображаются вертикально (каждый с новой строки)
  • Информация о клиенте и оплате в одной строке

Решение для нового проекта: ✅ ТАЩИМ - список организаций с количеством устройств и статусом


B. Редактирование даты оплаты

Что делает:

  • В колонке "Оплачено до" дата является ссылкой
  • Клик → переход на tilledit.php?id=123
  • Отдельная страница с календарем
  • Выбрать новую дату → сохранить

Решение для нового проекта: ✅ ТАЩИМ - но через модальное окно, не отдельную страницу


C. Хранение доп. информации о клиенте

Что хранится:

  • Юридическое лицо / ИП
  • Контактное лицо
  • Телефон
  • Email
  • Адрес
  • Комментарии

Формат: Текстовое поле (textarea), админ пишет произвольный текст

Решение для нового проекта: ✅ ТАЩИМ - но структурированно (отдельные поля, не текст)


3. Add Device Panel - Создание клиента с устройствами

A. Одновременное создание клиента и привязка устройств

Workflow:

1. Админ вводит:
   - Логин клиента (email)
   - Пароль
   - Список MAC-адресов устройств (textarea, через ;)
     Пример: d8:0d:17:5e:07:94;ac:84:c6:42:17:90;cc:2d:e0:ca:9f:7e;
   - Информация о клиенте (textarea)
   - Оплата (checkbox: оплачен / лимит 1000)

2. Система создает:
   - Запись в таблице `users` (клиент)
   - Записи в таблице `user_devices` (связь клиент-устройство)
   - Автоматически все MAC из списка привязываются к клиенту

Почему удобно:

  • Не нужно сначала создать клиента, потом добавлять устройства по одному
  • Массовое добавление устройств (скопировал список MAC → вставил → готово)

Решение для нового проекта: ✅ ТАЩИМ - форма создания организации с возможностью сразу добавить несколько устройств


B. Простой контроль оплаты/лимитов

Логика:

  • Checkbox "Оплачен"
  • Если НЕ оплачен → устанавливается лимит 1000 (детекций/событий)
  • Поле "Оплачено до" (дата)
  • Поле "Оплата" (текст статуса)

Use case:

  • Клиент на пробном периоде → checkbox снят → лимит 1000
  • Клиент оплатил → checkbox включен → безлимит
  • Админ видит когда заканчивается период → продлевает

Решение для нового проекта: ⚠️ ЧАСТИЧНО - заменяем на статусы организации: pending, active, suspended


ЧТО ТАЩИМ В НОВЫЙ ПРОЕКТ

Обязательные фичи (Must Have)

1. ✅ Универсальный поиск

Где:

  • Devices panel
  • Organizations panel
  • Users panel

Реализация:

Поисковое поле → backend ищет по полям:
- Devices: MAC, simple_id, organization.name, organization.owner_email, IP
- Organizations: name, contact_email, owner.email
- Users: email, full_name, organization.name

Фичи:

  • Debounce 300ms (не слать запрос на каждый символ)
  • Минимум 2 символа для поиска
  • Подсветка найденного (опционально)
  • Placeholder с примерами: "Поиск по MAC, организации, владельцу..."

2. ✅ Компактная таблица с сортировкой

Devices:

Simple ID  |  MAC Address   |  Organization  |  Status  |  Last Seen  |  Actions
   #1      | ac:84:c6:...  |  Shop LLC      | online   |  2 min ago  |  ✏️ Edit
   #2      | cc:2d:e0:...  |  Mall Inc      | offline  |  5 days ago |  ✏️ Edit

⚠️ ВАЖНО: Кнопка Delete НЕ в таблице!

  • В колонке Actions только кнопка "Edit"
  • Кнопка "Delete" спрятана ВНУТРИ модального окна редактирования
  • В секции "Danger Zone" внизу модального окна
  • Требует двойного подтверждения

Почему:

  • Случайный клик на Delete в таблице = катастрофа
  • Удаление должно быть обдуманным действием
  • Сначала Edit (открыть модалку) → увидеть детали → Danger Zone → Delete

Сортировка:

  • По Simple ID (default: newest first)
  • По Organization (alphabetically)
  • По Status (online → offline → error)
  • По Last Seen (newest → oldest)

Клик на заголовок → меняет сортировку Индикатор направления → стрелка ↑↓


3. ✅ Создание организации с устройствами

Форма Create Organization:

Шаг 1: Информация об организации

Name: ________________
Contact Email: ________
Owner Email: __________
WiFi Enabled: ☐
BLE Enabled: ☐

Шаг 2: Добавить устройства (опционально)

⊞ Add Device

MAC Address: __________________  [Remove]
MAC Address: __________________  [Remove]
MAC Address: __________________  [Remove]

[+ Add Another Device]

Или массовое добавление:

☐ Bulk import from list

Paste MAC addresses (one per line or separated by ; or ,):
┌─────────────────────────────────────┐
│ ac:84:c6:42:17:90                  │
│ cc:2d:e0:ca:9f:7e;d8:0d:17:5e:07:94│
│ b8:27:eb:c1:46:0e                  │
└─────────────────────────────────────┘

[Parse & Add]  →  "Found 4 MAC addresses"

Кнопки:

[Create Organization]  [Cancel]

После создания:

  • Organization создана
  • Devices добавлены и автоматически привязаны
  • Owner получает email с доступом

4. ✅ Статусы и индикаторы

Device status badges:

.status-online    → зеленый  (#10b981)
.status-offline   → серый    (#6b7280)
.status-error     → красный  (#ef4444)

Last seen human-readable:

2 minutes ago
5 hours ago
3 days ago
Never (для новых устройств)

Organization status:

pending  → желтый  (ожидает активации superadmin)
active   → зеленый (работает)
suspended → красный (заблокирована)

5. ✅ Модальные окна для редактирования

Вместо inline editing:

Edit Device Modal:

┌─────────────────────────────────────┐
│ Edit Device #5                      │
│                                     │
│ MAC Address: ac:84:c6:42:17:90      │
│ (read-only, не редактируется)       │
│                                     │
│ Organization: [Dropdown ▼]          │
│   → Shop LLC                        │
│   → Mall Inc                        │
│   → Not assigned                    │
│                                     │
│ Status: [Dropdown ▼]                │
│   → Online                          │
│   → Offline                         │
│   → Error                           │
│                                     │
│ Config (JSON):                      │
│ ┌─────────────────────────┐         │
│ │ {                       │         │
│ │   "wifi_enabled": true  │         │
│ │ }                       │         │
│ └─────────────────────────┘         │
│                                     │
│ [Save Changes]  [Cancel]            │
│                                     │
│ ───────────────────────────────────│
│ Danger Zone:                        │
│ [🗑 Delete Device]                  │
│ (требует подтверждения)             │
└─────────────────────────────────────┘

Преимущества:

  • Валидация перед сохранением
  • Подтверждение действия
  • Невозможно случайно изменить
  • Audit log: кто, когда, что изменил
  • Кнопка Delete спрятана в модальном окне (не в таблице!)

Опциональные фичи (Nice to Have)

6. ⚠️ Bulk actions (массовые действия)

Что:

  • Checkbox в каждой строке таблицы
  • Checkbox "Select All" в заголовке
  • Панель массовых действий появляется при выборе

Пример:

☑ Select All    (5 selected)

[Change Organization ▼]  [Delete Selected]  [Export CSV]

Use cases:

  • Выбрать 10 устройств → переназначить в другую организацию
  • Выбрать 5 офлайн устройств → удалить
  • Выбрать все → экспорт в Excel

7. ⚠️ Фильтры по статусу (chips)

Вместо dropdown:

Status:  [All]  [Online]  [Offline]  [Error]
         ─────   (123)     (45)       (2)

Клик на chip → фильтрует таблицу Счетчик → показывает количество устройств в каждом статусе


8. ⚠️ Экспорт в CSV/Excel

Кнопка: "Export to Excel"

Что экспортирует:

  • Текущий отфильтрованный список (с учетом поиска)
  • Все колонки таблицы
  • Формат: .xlsx (Excel) или .csv

Use case:

  • Админ хочет отчет по всем устройствам организации "Shop LLC"
  • Поиск "Shop LLC" → 10 devices найдено
  • Export → получает Excel с 10 строками

ЧТО НЕ ТАЩИМ

❌ 1. Inline editing

Почему НЕ тащим:

  • Опасно (случайные изменения)
  • Нет валидации
  • Нет подтверждения
  • Нет audit trail

Замена: Модальные окна


❌ 2. OpenVPN туннели

Почему НЕ тащим:

  • По ТЗ: "Сейчас у нас VPN не будет"
  • Убираем:
    • ovpn_flag
    • ovpn_addr
    • VPN страницу

❌ 3. Множественные флаги и URL в БД

Legacy имело:

wf_flag       tinyint(1)
wf_addr       varchar(60)  -- "https://beacon.e-bash.ru/wifi_receiver.php"
bt_flag       tinyint(1)
bt_addr       varchar(60)  -- "https://beacon.e-bash.ru/newfilebeacon.php"
fw_flag       tinyint(1)
fw_addr       varchar(60)  -- "https://beacon.e-bash.ru/fw_update.php?mac="

Проблемы:

  • Хардкод URL в БД
  • Для изменения URL нужно обновить все записи
  • Нет версионирования API

Новый подход:

  • Модульность на уровне Organization: wifi_enabled, ble_enabled
  • Единые API endpoints (не хардкод в БД):
    • /api/v1/device/config - получить конфиг
    • /api/v1/device/wifi-events - отправить WiFi данные
    • /api/v1/device/ble-events - отправить BLE данные
  • Device config как JSONB для гибкости

⚠️ 4. WiFi credentials - ИЗМЕНЕНИЯ

Legacy:

wf_client_ssid  varchar(60)   -- WiFi SSID
wf_client_psk   varchar(60)   -- WiFi пароль (plain text!)

Проблемы Legacy:

  • ❌ Пароли в открытом виде (plain text в БД)
  • ❌ Хранение в таблице devices (нужно обновлять каждое устройство)
  • ✅ НО: админ и клиент могли смотреть и менять пароли

Новый подход:

Хранение:

  • WiFi credentials на уровне Organization (не в каждом device)
  • Шифрование паролей в БД (AES-256 или аналог)
  • Возможность расшифровки для показа админу/клиенту

UI - Просмотр и редактирование:

В Organizations Panel (для superadmin/owner/admin):

┌────────────────────────────────────────┐
│ Organization: Shop LLC                 │
│                                        │
│ WiFi Settings:                         │
│                                        │
│ SSID: ___________________             │
│       Shop_Guest_WiFi                  │
│                                        │
│ Password: ●●●●●●●●●●  [👁 Show]       │
│           (click Show → myPassword123) │
│                                        │
│ [Save Changes]                         │
└────────────────────────────────────────┘

Кнопка "Show Password":

  • По умолчанию: ●●●●●●●●●● (скрыт)
  • Клик "Show" → показывает plain text: myPassword123
  • Клик "Hide" → снова скрывает

Права доступа:

  • Superadmin - может смотреть и менять любые credentials
  • Owner - может смотреть и менять credentials своей организации
  • Admin - может смотреть и менять credentials своей организации
  • Manager/Operator/Viewer - НЕ могут смотреть пароли (только SSID)

API Endpoints:

# GET /api/v1/client/organization/wifi-credentials
# Permissions: owner, admin
{
  "ssid": "Shop_Guest_WiFi",
  "password": "myPassword123"  # Расшифрованный пароль
}

# PATCH /api/v1/client/organization/wifi-credentials
# Permissions: owner, admin
{
  "ssid": "Shop_Guest_WiFi_New",
  "password": "newPassword456"
}

Backend шифрование:

from cryptography.fernet import Fernet

class Organization(Base):
    # ...
    wifi_ssid = Column(String(100))
    wifi_password_encrypted = Column(String(500))  # Зашифрованный пароль

    @property
    def wifi_password(self):
        """Расшифровать пароль для показа админу"""
        if not self.wifi_password_encrypted:
            return None
        return decrypt_password(self.wifi_password_encrypted)

    @wifi_password.setter
    def wifi_password(self, plain_password):
        """Зашифровать пароль перед сохранением"""
        self.wifi_password_encrypted = encrypt_password(plain_password)

Device получает через API:

  • Device запрашивает /api/v1/device/config?mac=...
  • Backend проверяет organization_id устройства
  • Возвращает расшифрованные WiFi credentials
  • Device подключается к WiFi

Преимущества:

  • ✅ Пароли зашифрованы в БД (безопасно)
  • ✅ Админ/клиент могут смотреть и менять (удобно)
  • ✅ Централизованное хранение на уровне Organization
  • ✅ При смене пароля обновляется одна запись (не все devices)
  • ✅ Device получает актуальные credentials через API

❌ 5. Текстовые связи через разделители

Legacy:

device  text NOT NULL  -- "b8:27:eb:c1:46:0e;cc:2d:e0:ca:9f:7e;d8:0d:17:5e:07:94;"

Проблемы:

  • Невозможно сделать JOIN
  • Невозможно сделать foreign key constraint
  • Невозможно индексировать
  • Парсинг строки каждый раз

Новый подход:

  • Нормализованная БД
  • Таблица devices с organization_id (foreign key)
  • Один device = одна строка

❌ 6. Пароли в открытом виде

Legacy:

password  text NOT NULL  -- "12345" (plain text)

Новый подход:

  • hashed_password varchar(255)
  • bcrypt с salt
  • Минимум 8 символов, complexity requirements

❌ 7. Отсутствие аутентификации в админке

Legacy:

  • Нет проверки кто заходит в админку
  • Любой кто знает URL может зайти

Новый подход:

  • JWT аутентификация
  • Role-based access control
  • Session management
  • Audit logging

ПРИОРИТЕТЫ РЕАЛИЗАЦИИ

✅ Уже есть (MVP)

  1. CRUD устройств с модальными окнами
  2. CRUD организаций
  3. CRUD пользователей
  4. JWT аутентификация
  5. Multi-tenant изоляция
  6. RBAC (6 ролей)
  7. Базовая таблица с сортировкой

🔨 TODO Шаг 1: Поиск (приоритет HIGH)

Backend:

@router.get("/devices")
async def get_devices(
    search: Optional[str] = None,
    organization_id: Optional[int] = None,
    status: Optional[str] = None,
    sort_by: str = "simple_id",
    sort_order: str = "desc",
    offset: int = 0,
    limit: int = 50
):
    query = select(Device)

    # Search filter
    if search:
        query = query.join(Organization).where(
            or_(
                Device.mac_address.ilike(f"%{search}%"),
                cast(Device.simple_id, String).ilike(f"%{search}%"),
                Organization.name.ilike(f"%{search}%"),
                Organization.contact_email.ilike(f"%{search}%")
            )
        )

    # Status filter
    if status:
        query = query.where(Device.status == status)

    # Sorting
    if sort_order == "asc":
        query = query.order_by(asc(getattr(Device, sort_by)))
    else:
        query = query.order_by(desc(getattr(Device, sort_by)))

    total = await db.scalar(select(func.count()).select_from(query.subquery()))
    devices = await db.scalars(query.offset(offset).limit(limit))

    return {"devices": devices.all(), "total": total}

Frontend:

<input
  v-model="searchQuery"
  @input="debouncedSearch"
  placeholder="Поиск по MAC, организации, email..."
  class="search-input"
/>

🔨 TODO Шаг 2: Bulk import устройств (приоритет MEDIUM)

Форма Create Organization:

Добавить секцию:

☐ Bulk add devices

Paste MAC addresses:
┌────────────────────────────┐
│ ac:84:c6:42:17:90         │
│ cc:2d:e0:ca:9f:7e         │
│ d8:0d:17:5e:07:94         │
└────────────────────────────┘

Separator: [; or , or newline ▼]

[Parse]  →  "✓ Found 3 valid MAC addresses"

[Create Organization with 3 devices]

Backend:

@router.post("/organizations")
async def create_organization(
    data: OrganizationCreateSchema,
    devices: Optional[List[str]] = None  # List of MAC addresses
):
    # Create organization
    org = Organization(**data.dict())
    db.add(org)
    await db.flush()

    # Create devices if provided
    if devices:
        for mac in devices:
            device = Device(
                mac_address=mac,
                organization_id=org.id,
                simple_id=await get_next_simple_id()
            )
            db.add(device)

    await db.commit()
    return org

🔨 TODO Шаг 3: Фильтры по статусу (приоритет LOW)

UI:

<div class="status-filters">
  <button
    v-for="status in ['all', 'online', 'offline', 'error']"
    :class="{ active: selectedStatus === status }"
    @click="selectedStatus = status"
  >
    {{ status }} ({{ counts[status] }})
  </button>
</div>

Backend:

@router.get("/devices/counts")
async def get_device_counts():
    counts = await db.execute(
        select(
            Device.status,
            func.count(Device.id).label('count')
        ).group_by(Device.status)
    )
    return {row.status: row.count for row in counts}

ИТОГО: Сравнение Legacy → New

Фича Legacy New Project Status
Универсальный поиск ✅ DataTables ✅ Backend SQL 🔨 TODO
Компактная таблица ✅ Все поля видны ✅ Основные поля ✅ Done
Сортировка ✅ По всем колонкам ✅ По ключевым ✅ Done
Inline editing ✅ jEditable ❌ Убрали -
Модальные окна ❌ Не было ✅ Для редактирования ✅ Done
Bulk add devices ✅ Textarea с ";" ✅ Парсинг списка 🔨 TODO
Multi-tenant ❌ Все в одной куче ✅ Изоляция ✅ Done
RBAC ❌ Нет ролей ✅ 6 ролей ✅ Done
Audit logs ❌ Нет ✅ Полное логирование ✅ Done
JWT auth ❌ Нет ✅ Access + refresh ✅ Done
Статусы ❌ Только last seen ✅ online/offline/error ✅ Done
Simple ID ❌ Только MAC #1, #2, #3 ✅ Done
OpenVPN ✅ Было ❌ Убрали -
WiFi credentials ✅ Plain text в devices ✅ Encrypted в org + UI 🔨 TODO
Пароли юзеров ❌ Plain text ✅ Bcrypt hash ✅ Done

ВЫВОДЫ

Что сохранили (essence, не реализацию):

  1. Универсальный поиск - самая полезная фича для быстрой работы
  2. Компактное отображение - вся информация на виду
  3. Быстрое создание - организация + устройства одним действием
  4. Сортировка - удобный просмотр больших списков

Что выбросили:

  1. Inline editing - опасно, заменили на модальные окна
  2. OpenVPN - не нужно по ТЗ
  3. Хардкод в БД - заменили на JSONB config + API endpoints
  4. Текстовые связи - заменили на нормализованную БД
  5. Plain text user passwords - заменили на bcrypt
  6. Отсутствие auth - добавили JWT + RBAC

Что улучшили:

  1. Безопасность: JWT, RBAC, bcrypt, SQL injection защита
  2. Multi-tenant: изоляция данных по организациям
  3. Audit trail: кто, когда, что сделал
  4. Модальные окна: валидация, подтверждение, UX
  5. Simple ID: удобные номера вместо MAC
  6. Статусы: online/offline/error с индикаторами
  7. i18n: RU/EN поддержка

Next steps:

  1. 🔨 Реализовать универсальный поиск (HIGH priority)
  2. 🔨 WiFi credentials UI с шифрованием (HIGH priority)
  3. 🔨 Bulk import устройств (MEDIUM priority)
  4. 🔨 Фильтры по статусу (LOW priority)
  5. 🔨 Export to CSV/Excel (LOW priority)