homepage v0.6.0 [extra]


Блоки домашней страницы веб-панели (/api/homepage).

README

# homepage

Блоки домашней страницы веб-панели: что видит гость без входа.

Тип: Модуль. Категория: `extra`. Зависимостей нет.
## Описание
Публичная витрина панели. Текстовые блоки домашней страницы — объявления, ссылки, памятки — читаются без входа, а правятся прямо в кабинете.

У каждого блока и каждой ссылки — видимость (как панели мониторинга):
`public` (все, даже без входа) | `auth` (вошедшие в веб) |
`right:право[;право2]` (любое из прав). Ссылка без видимости наследует блок.
Фильтрация — на сервере: GET отдаёт только видимое запросившему.

На сохранение стоят лимиты: число блоков и ссылок ограничено, чтобы витрину нельзя было раздуть до бесконечности.
## Возможности
- `load` `{}` → `{blocks: [...]}` — конфиг блоков домашней страницы
- `save` `{data: {blocks: [...]}}` → сохранить блоки
- перенос ссылок между блоками и папками — в форме редактирования ссылки
  (селект «Блок / папка» показывает и папки)
## Папки ссылок
Папка — элемент блока `{type: "folder", title, visibility, links[]}`:
плитка 📁 со счётчиком ссылок, открывается модалкой по центру экрана
(как пикер иконок). Внутри — те же ссылки (правка, кнопки ↑/↓/✕, 🔄
обновление копий) + кнопка «+ Ссылка в папку». Кнопка «📁 Папка» в панели
блока создаёт папку, ✏️ на плитке — переименовывает и переносит в другой
блок (селект «Блок», только свои блоки; удаление — с
подтверждением и счётчиком ссылок). Видимость папки — как у ссылки
(пусто = наследует блок), ссылки внутри наследуют папку; фильтрация
на сервере, вложенные папки запрещены (POST — 400), лимит — 50 папок
на блок. Поиск 🔎 видит названия папок и ссылки внутри.
## Иконка с сайта (чекбокс 📥)
В формах ссылки/каталога под полем иконки — чекбокс «📥 Подтянуть иконку
с сайта»: сервер скачивает favicon страницы (`<link rel="icon">` или
`/favicon.ico`), проверяет сигнатуру картинки и подставляет `data:`-URL
в поле (дальше хранится как обычный текст иконки). Кеш на сервере
(`data/cache/homepage/icons`, TTL 7 суток) — повторная галочка по тому же
URL сеть не трогает. Роут `GET /api/homepage/icon?url=` — только с правом
`homepage_edit`; лимиты: страница ≤512 КБ, иконка ≤128 КБ, только http(s).
## Иконки ссылок
Что можно писать в поле «Иконка»:
- `fas fa-server` — Font Awesome solid (рекомендуется: работает всегда);
- `far fa-bell` — Font Awesome regular (покрывает лишь ~300 иконок; если
  глифа нет в regular — например `far fa-icons`, `far fa-house`, `far fa-server`
  — страница автоматически покажет solid-вариант той же иконки);
- `fab fa-telegram` — бренды (GitHub, Telegram, Docker и т.п.);
- Pro-стили (`fal`/`fad`/`fat`, light/duotone/thin) не вендорятся — тоже
  маппятся на solid автоматически;
- эмодзи/символ (показывается первая буква), URL картинки или `data:`.
В форме — живой предпросмотр, подсказки (datalist из ~140 ходовых иконок)
и кнопка 🎨 — пикер: сетка всех вендорных иконок (~2000 solid + ~580 брендов)
с живым поиском, клик подставляет в поле. Эмодзи/URL картинки — вручную.
Полный список иконок: https://fontawesome.com/search?m=free (галочка «Free» —
бесплатные; нужны solid/regular/brands — остальные стили недоступны).
## Внешние ссылки (контракт для других модулей)
Любой модуль может заявить в своём `ui_web.py` опциональный список:

```python
HOMEPAGE_LINKS = [
    {"block": "Доступ", "title": "SSH хост", "url": "https://ssh.local:2222/",
     "desc": "…", "icon": "…", "newTab": True,
     "visibility": "right:ssh_view"},
]
```

Поля: `title`+`url` обязательны; `block` — группа (по умолчанию имя модуля);
`visibility` — по умолчанию `auth` (чужие ссылки часто ведут во внутренние
сервисы). Homepage собирает их в read-only блоки после своих: в редакторе они
с бейджем «⚙ от модуля», сам модуль не правится и в файл не пишется.
Модуль править можно, не трогая: кнопка 👁 скрывает внешний блок целиком
или отдельные ссылки внутри, ✏️ даёт своё название. Состояние (`ext_state`)
хранится отдельно, уходит одним POST с Сохранить, при выключении модуля
молча переживает (ключи стабильны: модуль + контрактное название).
Скрытое редактору видно (бледным), остальным — нет вообще. Скрытие
с домашней каталог не чистит: скрытые ссылки остаются в 📚 с меткой
«👁 скрыт» — копии брать можно, сами копии (свои ссылки) правятся как обычно.
Deep-link: ссылки вида `/#<view>` доводит центральный hash-роутер ядра —
модулям свой hashchange запрещён (скрипт грузится до входа и ронял бы
глубокую ссылку). Анонима явный переход ведёт на вход, после логина —
до целевой вьюхи; повторный клик по той же ссылке доводится явно.
Пример: SSH-подключения remotedesk — когда тот заявит `HOMEPAGE_LINKS`,
ссылки появятся сами, трогать remotedesk не надо.
## Каталог ссылок (кнопка 📚)
Каталог — удобный забор контрактных ссылок к себе: кнопка
«📚 Из каталога (N)» рядом с «+ Добавить блок» и дропдаун «Из каталога»
в форме «+ Ссылка». Вставка — КОПИЯ (снимок полей, пометка `from: модуль /
блок`): дальше живёт сама, переживёт выключение модуля-источника.
Видимость копии — явная из оригинала (перенос в открытый блок доступ
не расширяет); тот же URL в том же блоке — предупреждение вместо дубля.
Не забудьте 💾 Сохранить после вставки.
## Обновление копий и импорт/экспорт
- У копии (`from: модуль / блок`) в режиме редактирования появляется кнопка
  🔄 — diff со свежим контрактом (было → стало) с галочками: применяется
  только выбранное. Источник пропал — честное «источник пропал из каталога».
- Кнопки ⬇ Экспорт / ⬆ Импорт — JSON своих блоков (бекап, перенос между
  ботами). Импорт валидирует как POST (лимиты, видимость, только http(s)/…),
  мусор отбрасывает, внешние блоки не трогает. После импорта — 💾 Сохранить.
- Строка 🔎 поиска по домашней — фильтр по
  заголовку/описанию/URL (видит названия папок и ссылки внутри).
  Работает и в режиме редактирования; скрывает отдельные плитки
  (а не только блоки целиком): совпавшая папка показывает все свои
  ссылки, несовпавшие плитки гаснут, в открытой папке строки тоже
  фильтруются. Enter в поле поиска применяет запрос явно.
## Безопасность по умолчанию
Новые блоки/ссылки создаются с видимостью `auth` (вошедшие в веб), а не
`public`: внутренняя ссылка не станет видна гостям одним неосторожным кликом.
`public` включается явно. URL — только `http(s)://` или путь `/…`
(`javascript:`/`data:` отклоняются в форме с подсказкой). Выход без сохранения
(кнопка ✕ Отмена) при несохранённых правках спрашивает подтверждение.
## Права
- `homepage_edit` — Редактирование блоков домашней (POST /api/homepage)
## Web
- `GET /api/homepage` — видимые запросившему блоки + `title` (публично)
- `POST /api/homepage` — сохранить (право `homepage_edit`)
- `GET /api/homepage/icon?url=` — favicon сайта как `data:`-URL
  (право `homepage_edit`, кеш 7 суток)
## Конфигурация (`config.schema.json`)
- обязательных нет; опциональные (дефолты):
  - `HOMEPAGE_TITLE` = `` (не задан) — заголовок домашней (шапка + `<title>`, по умолчанию «Домашний помощник»)
  - `HOMEPAGE_FILE` = `` (не задан) — путь файла блоков

Титул подставляется сервером в `index.html` при отдаче (мигания дефолта
нет): приоритет — `HOMEPAGE_TITLE` > `WEB_TITLE` (транспорт) > дефолт.
Меняется без правок кода: `data/env/homepage.env` → рестарт службы.

Манифест

{
  "name": "homepage",
  "version": "0.6.0",
  "category": "extra",
  "description": "Блоки домашней страницы веб-панели (/api/homepage).",
  "requires": [],
  "rights": [],
  "api_methods": [],
  "web_routes": [],
  "commit": "2e29b80f4592f81521104df461261a4954e7136a",
  "updated": "2026-10-09 11:35:43 +0300",
  "history": [
    {
      "version": "0.1.0",
      "date": "2026-09-08 09:46:42 +0300",
      "commit": "5c758d465ca4dd3e1541909f2b685dc5c5424246"
    },
    {
      "version": "0.2.0",
      "date": "2026-09-11 10:24:38 +0300",
      "commit": "1fa8180d936fbda6caa5efa0841f338fe2e569b1"
    },
    {
      "version": "0.3.0",
      "date": "2026-10-06 21:59:43 +0300",
      "commit": "a0317834eb7dc9b07a522101a02d88c2d221630b"
    },
    {
      "version": "0.4.0",
      "date": "2026-10-07 14:14:07 +0300",
      "commit": "209f7cedba2b8478455bc58efabbc2215e9067b4"
    },
    {
      "version": "0.4.1",
      "date": "2026-10-07 15:10:20 +0300",
      "commit": "9c9d7149e86e5ecebe11d97f8c5aa90c659fd9e6"
    },
    {
      "version": "0.5.0",
      "date": "2026-10-08 11:28:39 +0300",
      "commit": "40b35ddb4cb037b3409b990043ce129e64f44bed"
    },
    {
      "version": "0.5.1",
      "date": "2026-10-08 11:36:58 +0300",
      "commit": "22bae7f329ef8305f39a8ab3e0777f6a7bb870a4"
    },
    {
      "version": "0.6.0",
      "date": "2026-10-08 12:43:58 +0300",
      "commit": "8930762567a7584c72d7f50d1f3ac8918f1788cb"
    }
  ],
  "integrity": "sha256:9e9a6c6b4464b45ad06b78a82a317aad7b1a4b1f3d35df3682e85a1550d37db8",
  "files": "[9 файлов — см. вкладки ниже]",
  "note": "",
  "wdesc": "",
  "wbody": "",
  "_extra": {
    "api": "",
    "events_emitted": "[]",
    "events_subscribed": "[]",
    "shared_routes": "",
    "config_schema": "config.schema.json",
    "env_example": ".env.example"
  }
}

Файлы и исходники

Дерево файлов

  • · корень
    • 11.4 КБ
    • 0.1 КБ
    • 13.3 КБ
    • 10.0 КБ
    • 1.1 КБ
    • 1.5 КБ
    • 8.9 КБ
  • web/
    • 0.4 КБ
    • 136.0 КБ

Предпросмотр

Выберите файл в дереве выше — код откроется здесь.

История версий

versiondatecommit
0.6.02026-10-088930762567a7
0.5.12026-10-0822bae7f329ef
0.5.02026-10-0840b35ddb4cb0
0.4.12026-10-079c9d7149e86e
0.4.02026-10-07209f7cedba2b
0.3.02026-10-06a0317834eb7d
0.2.02026-09-111fa8180d936f
0.1.02026-09-085c758d465ca4