Архитектура системы

Материал из ISPWiki
Перейти к: навигация, поиск

Рекомендации

Небольшой набор правил для обеспечения целостности интерфейса как для конечных пользователей, так и для разработчиков.

Конфигурация

Не надо плодить лишних параметров в .conf файлах. Давайте, по возможности, избегать path для внутренних целей. Если кто-то захочет поменять расположение какого-либо из файлов панели, он всегда может заменить его символической ссылкой.

XML

Все функции панели работают с XML документом, который используется для формирования ответа пользователю (этот XML - единственный источник данных для формирования ответа). При работе с XML следует соблюдать следующие правила:

  • корневой элемент должен называться doc
  • при обработке XML избегайте использования XPath начинающихся с "//" - вы можете найти больше узлов, чем вам было нужно.

Общие рекомендации

  • Избегать ситуаций, ставящих пользователя в тупик; при возникновении ошибки он всегда должен точно знать, что делать, чтобы устранить ошибку. При возникновении ситуации, требующей дополнительных настроек, он всегда должен знать, где, как и какие настройки он должен произвести. В идеале, автоматом перенаправлять его на заполнение соответствующих настроек и потом автоматом возвращать обратно. При возникновении вопросов о поведении системы он должен быстро найти ответ в документации (по возможности в хинте)
  • По возможности пытаться решать проблемы автоматически, не напрягая при этом пользователя. Например, если обнаружено несоответствие каких-то данных, и его можно однозначно устранить (rotate уже существует), то устраняем. Если отсутствует нужная нам дира или файл и мы знаем с какими правами они должны быть и знаем каким должно быть дефолтное содержимое файла, то автоматом создаем их. И т.д.
  • В случае появления потенциально неправильных действий (warning) пишем сообщение не только в лог, но и отображаем их пользователю, посредством баннеров.
  • Всегда разрешать переход с вышестоящих уровней даже на отключенные учетные записи.
  • Все разделы в dashboard'е должны быть выстроены в ровные колонки и выглядеть органично.
  • Весь код должен быть многопоточным
    1. Временные данные запроса должны храниться в XML сессии
  • Изолировать функции панели
    1. Отдельный функционал должен быть оформлен в отдельный файл, а не размазан по всему коду
    2. Использовать неименованные namespace для сокрытия структуры данных модуля
  • Функции, используемые для проверки значения (check в метаданных), должны пропускать пустое значение параметра
    1. Пустое значение можно запретить, указав атрибут required
    2. Значение может оказаться пустым в результате внутренних преобразований. В этом случае оно должно быть признано верным
  • Все групповые операции (удаление, включение, выключение и т.д.) вызываются для каждого отдельного элемента, если операцию выполнить невозможно, обязательно генерируем эксепшен. На более высоком уровне он будет перехвачен, и продолжится выполнение операции для других элементов. В конечном итоге, в случае ошибок пользователь увидит список элементов, над которыми не удалось произвести операцию.
  • Игнорировать попытки удаления несуществующих объектов (не генерировать исключения)
  • Избегать дублирования кода
    1. Избегать случаев, когда несколько функций панели выполняют одни и те же действия. Такие действия следует объединять в отдельные функции и вызывать их посредством InternalCall, чтобы обеспечить возможность централизованно менять поведение. Есть возможность защитить такие функции от непосредственного вызова извне.
  • Все функции создающие какие либо сущности, должны возвращать их идентификатор (по которому можно обратится к нету) в теге elid. если создаваемых объектов несколько, возвращается несколько тегов elid

Рекомендации по работе с формами

Подробную информацию о формате xml документа описывающего форму вы можете найти в статье Описание форм

  • Функции редактирования/создания новых элементов, по возможности, должны возвращать новый идентификатор элемента в узле id и новое имя, если оно отличается от идентификатора, в узле name.
  • Параметр confirm (подтверждение пароля) в коде нигде не обрабатывается, следовательно, при вызовах по API его можно не указывать.
  • Все поля с валидаторами (специфическими требованиями к значениям) должны иметь placeholder
  • Если поле на форме подразумевает неограниченное значение, то оно должно иметь placeholder с сообщением "не ограничено" и обрабатывать пустое значение, выставляя большое значение самостоятельно.
  • Если проверка доступа к элементу реализована в Get, её можно не делать в Set (Get всегда вызывается перед обработкой Set)
  • Все проверки вводимых параметров должны быть объявлены в XML (@check и @checkargs). Значения полей с типами slider, checkbox и select проверяются автоматически, для чего во время set запросов выполняется соответствующий get запрос.
    • slider - целое число, лежащее в промежутке [min,max]. Если не указано - считается min.
    • checkbox - допустимые значения: on, off. Если не указано - считается off.
    • select - одно из значений в соответствующем slist. Если не указано - берется первое значение из списка slist после его сортировки
  • Значения, возвращаемые в get запросах, должны удовлетворять условиям, заданным в XML.
    • Если разработчик в get запросе не вернул значения для полей с типами slider, checkbox или select, значение будет подставлено автоматически (см. предыдущий пункт).
    • Значения параметров, переданные в get запрос, заменят собой значения одноименных полей формы. Таким образом можно показать пользователю форму с заранее заполнеными полями.
  • Значения для полей ввода в новых записях (запрос с пустым elid), заполняемые через setvalues, должны формироваться автоматически при Get запросе, если они небыли переданы в качестве параметров. Например, если вы заполняете некое поле Email после изменения поля Name, код может выглядеть следующим образом:
if (ses.Has("Name") && !ses.Has("Email"))
  ses.NewNode("Email", ses.Param("Name") + "@host.com");
  • Поля для ввода пароля при интеграции панели с внешними системами (другие панели: ip/dnsmanager, сервера баз данных и т.п.) должны иметь тип password. При этом, во время отображения формы (get запрос) должно отдаваться текущее значение запомненное в панели.

Рекомендации по оформлению списков

Подробную информацию о формате xml документа описывающего список вы можете найти в статье Описание списков

  • Заголовок списка должен совпадать с именем списка в главном меню (если не использовалось сокращение)
  • Использование подписи с prop'а только по согласованию с руководителем разработки.
  • В статистике все цифры должны подытоживаться. Все индикаторы должны отображать обе цифры аля "3/40", а не "4/". По колонке с prop'ами итог должен подводиться по тому, чего меньше (если речь идёт о нескольких возможных состояниях объекта, аля "вкл/выкл").
  • Не должно быть колонок, в которой нет ни одного значения.
  • Если есть prop'ы в списке, то комментарии, примечания и т.п. должны быть хинтами к соответствующему изображению. Если prop'ов нет, то в отдельной колонке в виде текста.
  • Если среди prop/xprop есть лампочка, она должна стоять первой
  • Не должно быть больше 8 групп кнопок у списка.
  • При описании колонки типа <col type="msg" ... /> содержащей состояние, обязательно указывается цвет текста.
    • red - ошибка
    • yellow - предупреждение
    • green - все хорошо

Рекомендации по расположению кнопок на toolbar

  • Кнопки изменения состояний (включить/выключить). На панели инструментов размещать только кнопки для изменения только одного (самого важного, наиболее изменяемого) состояния, все остальное решать галочками на форме редактирования объекта.

Кнопки везде располагаются в следующем порядке (разумеется, если у вас чего-то нет, пропускаем):

  • Создать
  • Изменить. При наличии этой кнопки default="yes" должен быть на ней. (Пользователи, которым такое поведение не удобно, могут изменить его в настройках таблицы)
  • Удалить
  • --разделитель--
  • Включить
  • Выключить
  • --разделитель--
  • любые другие кнопки не описанные ниже, по необходимости допускается ставить разделители между смысловыми группами кнопок
  • --разделитель--
  • кнопки отчетов
  • --разделитель--
  • кнопки фильтров
  • --разделитель--
  • кнопки переходов (на другой уровень, в другие панели и т.д.)

Рекомендации по работе с базами данных

  • Не использовать Зарезервированные_слова_СУБД в качестве имен таблиц и полей
  • Ключевые слова языка SQL должны быть в верхнем регистре (ну проще так запросы читать)
  • Разделение слов внутри имени символом '_'
  • Таблицы
    • Имена функций выводящих содержимое списков должны соответствовать именам таблиц (если это возможно)
    • Имена таблиц и полей маленькими буквами
    • Имена таблиц писать в единственном числе (Например: server, а не servers). Иначе внешние ссылки будут некрасиво выглядеть (см. выше)
    • Таблица со списком пользователей - users (user - зарезервированное слово в некоторых СУБД)
    • many2many Таблицы именуем через двойку в единственном числе слева и справа от двойки
  • Поля
    • Префиксы имен полей (обозначающие тип, размер и прочее) не использовать.
    • Тип boolean - StringField длины 3. Значения on/off (для совместимости с checkbox). Class_mgr_db::BoolField
    • Не использовать autoincrement поля (из-за проблем с репликацией данных MySQL). Используем Class_mgr_db::AutoIncrement
    • Поле, ссылающееся на другую таблицу, должно иметь такое же имя, как и таблица, на которую оно ссылается (если ссылок много, то имя таблицы следует использовать в качестве префикса)
    • Поле, содержащее уникальный идентификатор записи, должно иметь имя id Class_mgr_db::AutoIncrement
    • Поле, содержащее уникальное имя записи, должно иметь имя name
    • Поле, содержащее состояние объекта типа: включено/выключено, должно иметь имя active с возможными значениями on/off. Class_mgr_db::BoolField
    • Поле, содержащее уровень доступа, должно называться level быть целочисленным и принимать значения согласно Namespace_mgr_access
    • Поля, содержащие пароли, должны иметь тип Class_mgr_db::CryptedField. Это позволяет увеличить защиту данных (в частности от SQL инъекций)

SQLite

  • Базы храним в каталоге etc
  • Имя файла базы данных должно иметь расширение .db

Рекомендации по расположению разделов меню

Разделы меню во всех панелях располагаются в следующем порядке:

  • Разделы продукта
  • Состояние системы
  • Интеграция
  • Настройки
  • Справка. При этом сам модуль "Справка" должен занимать самую верхнюю позицию раздела "Справка"

Рекомендации по содержанию текстовых сообщений

Будет пополняться прецедентно.

  • Придерживаться единого стиля при написании сообщений
    1. Хинты писать с большой буквы и без точки в конце
    2. Для подписей к кнопкам и имен полей стараться использовать сообщения из секции common
  • Id - писать так.
  • Краткие подписи под кнопками. В идеале, подписи должны вмещаться в ширину кнопки. Максимум в 1.5 ширины кнопки. В особо трудных случаях допускается сокращать слова точкой. Развёрнутая информация о предназначении кнопки должна быть в подсказке(hint) к ней.
  • IP и другие адреса следует писать так: "IP-адрес" "MAC-адрес". Аббревиатура большими буквами через дефис.
  • Наименования модулей. Текст должен быть такой длины, чтобы вмещался в одну строчку в главном (боковом) меню. Максимальная длина названия также зависит от ширины тулбара, т.е. название и тулбар должны вмещаться на экране шириной 1280px
  • Хинт должен раскрывать суть, а не констатировать то, что и так указано в подписи поля. Например, если на форме есть чекбокс "Включить Spamassassin для домена" - то в хинте должно быть примерно следующее содержание "Разрешать ли фильтрацию спама с помощью Spamassassin для данного домена". Человек, которому понятие не знакомо, может сразу вникнуть о чём идёт речь. Если сообщение очевидно и пояснения не требуются, то нужно написать, где и для чего используется данное поле или перефразировать его название другими словами.
  • Если все что хочется написать в хинте не вмещается в 5 строк, то пишем техническую статью (даже если в ней всего будет пара абзацев) и добавляем на нее ссылку в "Полезные ссылки"

Рекомендации по обработке лимитов на различные ресурсы

  • В качестве безлимита использовать пустое значение. Ноль - это ноль (запрет на использование ресурса), а не анлим. Для того, чтобы клиенту было понятно, что пусто - это безлимит, используем placeholder. Пример использования:
 <field name="domainlimit">
     <input type="text" name="domainlimit" check="int" unlimit="" checkargs="0,"/>
 </field>
 ...
 <msg name="placeholder_domainlimit">не ограничено</msg>
  • Если цифра 0 в лимите бессмысленна, делайте проверку в валидатор, на минимальное значение 1, поможет пользователям избежать недопонимания.
  • Уменьшение лимитов. Если у пользователя использовано ресурсов больше, чем вы хотите задать (уменьшить) лимит. Значит отказываем в этой операции, генерим ошибку.
  • Оверселинг по умолчанию у нас разрешен. Т.е. реселлер может в сумме раздать своим пользователям больше лимитов, чем его собственный лимит. При этом при выделении какого либо ресурса пользователю нужно проверять оба лимита, лимит пользователя и лимит реселлера (сложив использование ресурса всеми его пользователями)
  • Если лимит не задан явно - доступ к ресурсу должен быть закрыт

Новые возможности COREmanager, которые нужно использовать в панелях

  • Notify bar (уведомления теперь отображается в панели оповещений в левом верхнем углу). Документация здесь.
  • Навигация из списков (nestedlist). Возможность связывания списков. Можно задать в какой список и с каким фильтром следует перейти при нажатии на отдельные значения таблицы. Документация здесь.
  • Перегрузка веб-интерфейса при изменениях в меню. Документация здесь, функция RefreshDesktop
  • Кнопки New в дашборде
  • Подсказки для кнопок при первой авторизации пользователя. Нужны для того, чтобы сориентировать пользователя как пользоваться панелью. Документация здесь

Загрузка системы

  • создание обработчиков базовых функций
    Создаются и регистрируются в системе: Базовые функции CORE, Обработчики входящих соединений, Функции для формирования выходного потока
  • загрузка дополнительной библиотеки <имя панели>.so / <имя панели>.dll
    Во время загрузки библиотека должна зарегистрировать в системе все необходимые ей компоненты. Кроме того, она может подгружать дополнительные библиотеки. Если загрузка какой-либо библиотеки завершается с ошибкой, все зарегистрированные ей компоненты автоматически удаляются из системы.

Обработка запросов

На каждом этапе обработки запроса вы можете добавлять собственные обработчики.

  1. получение запроса, чтение и разбор параметров запроса
    Обрабатываются данные GET и POST, формируются внутреннее представление данных для удобного и быстрого доступа
  2. аутентификация пользователя (попытка определить имя и уровень доступа пользователя отправившего запрос)
  3. поиск функции по имени (имя функции берется из параметра func, если параметр отсутствует, предполагается, что он равен desktop)
  4. авторизация пользователя
  5. создание Xml для сессии (lang, func, binary, host)
  6. вызов глобальных обработчиков событий (тех, что висят на действии "*") before="yes"
  7. вызов обработчиков событий для выбранной функции before="yes"
  8. вызов обработчика функции
  9. вызов обработчиков событий для выбранной функции after="yes"
  10. вызов глобальных обработчиков событий (тех, что висят на действии "*") after="yes"
  11. завершение транзакций (запись файлов, удаление временных данных, вызов commit для баз данных)
  12. формирование ответа в требуемом формате (формат задается через параметр запроса out)

Аутентификация пользователя

На этом этапе происходит попытка определить имя пользователя отправившего запрос и уровень его доступа. За аутентификацию пользователей, и их проверку отвечает базовая функция authenticate. Проверка пользователя необходима, чтобы определить существует ли такой пользователь в системе на данный момент.

  1. проверка COOKIE (<имя панели> + "5")
    COOKIE должен содержать следующие поля разделенные двоеточием: имя темы, аббревиатуру языка, код сессии.
    • загрузка сохраненных сессий из файла var/<имя панели>.ses (если он еще не был загружен)
      Этот файл записывается каждый раз, когда создается код сессии, на случай непредвиденного завершения работы панели.
    • проверка пользователя (вызов authenticate.<имя метода авторизации>)
  2. авторизация пользователя исходя из данных полученных от клиента (вызов authenticate)
  3. авторизация по IP
    В данном, случае имя пользователя нам уже известно (задано в конфиге), но все равно необходим вызов authenticate, чтобы определить дополнительные параметры.
  4. авторизация пользователей пришедших с доверенного источника соединений
    Все аналогично предыдущему пункту, только имя пользователя - это имя локального администратора.

Если ни одна из предпринятых попыток не завершилась успешно, сессии присваивается нулевой уровень доступа.