Данный проект является экспериментом. Здесь предыстория и небольшой анализ ситуации. Девиз проекта:
Only an gormless fool will begin to place business logic in stored procedures
Репозиторий: GitHub
Работает это так:
- Запускается приложение Ormless на сервере. При запуске оно считывает все странички с JavaScript-скриптами и кладёт их в переменную.
- Пользователь открывает страничку. Веб-сервер получает соответствующую команду и отдаёт из переменной страничку.
- Страница запрашивает данные, для чего отправляет API-команду на сервер.
- Сервер получает API-команду, проверяет авторизацию. Если всё нормально, команда сравнивается с имеющимся на сервере описанием этой команды - проверяются типы параметров и их содержимое.
- Если всё нормально - команда преобразуется в SQL-команду запуска хранимки. Команда отрабатывает, результат заворачивается в JSON.
- JSON передаётся на браузер как ответ.
- Переходим к пункту 2. Запись данных по тому же сценарию.
Разработка ведётся в двух местах:
- Разработка интерфейса и прочего взаимодействия с пользователем (JavaScript).
- Разработка БД и реализация бизнес-логики (один из SQL).
- В папке с программой необходимо создать папку (например
pages
) с веб-страничками (*.htm
,*.css
,*.js
), картинками и другими медиафайлами. - Заполнить файл настроек веб-сервера, файл
config.yml
. - Заполнить файл-описание команд API
command_parameters.json
. Соответствующие хранимки должны быть в базе данных. - Запустить приложение.
Ниже всё расписано более подробно.
Рассмотрим содержимое файла настроек:
WebPublic:
DomainName: lvh.me
HttpPort: 80
HttpIp: localhost
Https:
Enabled: false
Port: 443
Ip: localhost
Provider: letsencrypt
SubscriberEmail: [email protected]
CertFolder: ssl
CertPemFileName: ssl/cert.pem
KeyPemFileName: ssl/key.pem
WebPagesFolder: pages
JsSettingsPath: /js/_settings.js
CommandPathPrefix: /cmd/
ParametersCountLimit: 30
CommandParametersFileName: command_parameters.json
OAuthVerificationCodePath: /cmd/oauth_verification_code
OAuthYandex:
ClientId: aaa
ClientSecret: bbb
OAuthGoogle:
ClientId: ccc
ClientSecret: ddd
OAuthGitHub:
ClientId: eee
ClientSecret: fff
DbConnectionString: sqlserver://user:password@localhost?database=test&connection+timeout=30
Подробнее о каждом параметре.
Адрес приложения снаружи или имя сайта. Нужен для генерации ссылок, создании сертификата SSL и проч. Если публичный (внешний) порт отличается от стандартного, указываем здесь. Например lvh.me:5000
. Протокол подставит программа. Если SSL используется - https://
, иначе http://
.
Указывает какой порт слушать. Из-за особенностей хостинга может не совпадать с публичным.
Указывает IP для прослушивания. Из-за хостинга может потребоваться указать что-то отличное от localhost
.
Веб-сервер поддерживает работу с SSL. Включается параметром Enabled
. Указываем также IP
и Port
для прослушивания. Есть два режима работы: letsencrypt
- если будем использовать автопродляемый бесплатный сертификат от Let's Encrypt и custom
- если сертификат покупной.
В первом случае указываем почту для обратной связи SubscriberEmail
и папку CertFolder
, в которую будут записаны сертификаты и закрытый ключ.
Во втором случае указываем пути файлов - сертификата CertPemFileName
и закрытого ключа KeyPemFileName
в формате PEM. Если у Вас другой формат - разделите их по файлам в текстовом редакторе.
Имя папки с файлами веб-страниц и ресурсами. Файлы из этой папки будут доступны снаружи по тем же адресам, что и внутри папки. Кроме файла index.html
. Например: файл, который лежит в папке по адресу \img\aaa.png
снаружи будет доступен по адресу https://domain.name/img/aaa.png
.
Это относительный от доменного имени путь (снаружи) до генерируемого во время запуска веб-сервера JS-файла с полезными константами для формирования веб-страниц на клиенте. Пример содеримого данного файла:
const THIS_APP_URL = "http://lvh.me:9000";
const OAUTH_YANDEX_URL = "https://oauth.yandex.ru/authorize?client_id=xxx&response_type=code&state=yandex%token";
const OAUTH_GOOGLE_URL = "https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&client_id=yyy.apps.googleusercontent.com&redirect_uri=...";
const OAUTH_GITHUB_URL = "https://github.com/login/oauth/authorize?client_id=zzz...";
THIS_APP_URL
- это собственный адрес.OAUTH_YANDEX_URL
,OAUTH_GOOGLE_URL
,OAUTH_GITHUB_URL
- это ссылки на страницы авторизации соответствующего сервиса авторизации.
Все адреса API-команд базы данных форматированы так:
Доменное имя сайта + префикс команды + имя команды + "?" + GET-параметры (если вызов GET-методом). Пример: http://lvh.me:9000/cmd/entity.category_list?p=11
Максимальное количество параметров в API-команде, для предварительной проверки.
Имя файла с описаниями API-команд. См. ниже.
URL команды верификации OAuth. Это адрес для Callback-вызовов серверов OAuth. В личных кабинетах следует указывать адрес сайта + это значение. Из примера выше: http://lvh.me/cmd/oauth_verification_code
.
Настройки OAuth для авторизации на соответствующем сервисе авторизации. Содержат по две настройки:
client_id
- идентификатор клиента, полученный при регистрации на соответстувющем сервисе.client_secret
- это пароль или секрет, также выданный при регистрации.
Строка подключения к базе данных.
Для валидации команды перед вызовом используется файл-описание. В базе данных должны быть реализованы хранимые процедуры в точном соответствии с файлом описания. Все поля обязательны, если не указано иное. Пример:
[
{
"cmd_name": "#USER_REGISTER#",
"db_proc_name": "Entity.UserRegister",
"call_method": "ORMLESS",
"description": "Запись пользователя, прошедшего регистрацию через сервис OAuth",
"parameters": [
{ "name" : "user_name", "type" : "string", "description": "Веб сервер передаёт здесь ФИО пользователя или ник" }
,{ "name" : "user_email", "type" : "string", "description": "Веб сервер передаёт здесь адрес электронной почты" }
,{ "name" : "ext_id", "type" : "string", "description": "Внешний идентификатор в сервисе авторизации OAuth" }
,{ "name" : "oauth_service_name", "type" : "string", "description": "Наименование сервиса авторизации" }
]
}
,{
"cmd_name": "entity.category_list",
"db_proc_name": "Entity.CategoryList",
"call_method": "GET",
"description": "Процедура возвращает список элементов дерева категорий",
"parameters": [
{ "name" : "p", "type" : "int", "default" : "0", "description": "Идентификатор родительского элемента. Если 0 - то нужно вернуть элементы верхнего уровня." }
]
}
]
Все команды имеют имя, как составная часть адреса команды. Есть также команды для внутреннего пользования, ведь веб-сервер Ormless тоже использует БД. Вот названия необходимых ему команд:
#USER_REGISTER#
- запись пользователя при успешной авторизации через сервис OAuth. Вызвать снаружи эту команду нельзя.
Название хранимки в базе данных.
Метод вызова данной команды:
GET
- вызов снаружи HTTP-методом GETPOST
- вызов снаружи HTTP-методом POSTORMLESS
- при внутреннем вызове веб-сервера
Текстовое описание процедуры, как элемент документации.
Массив параметров процедуры. Каждый элемент содержит:
name
- имя параметра хранимой процедуры.type
- тип значения параметра.default
- значение по умолчанию. Указывать необязательно. Если параметр не передан, то будет использоваться указанное значение. Если параметр не передан и это значение не указано - веб-сервер вернёт ошибку до вызова хранимой процедуры.description
- описание параметра, как элемент документации.
Параметры могут быть следующих типов:
bool
int
uint
float
string
Диапазоны значений всех числовых параметров соответствуют размеру переменной в 64 бит.
- В программе использован драйвер для БД Microsoft SQL Server. При использовании другой БД программу необходимо перекомпилиовать с соответствующим драйвером.
- Скомпилировать приложение с драйверами всех баз, или с одним из универсальных драйверов и вынести это в настройку.
- Возможно имеет смысл давать назначать для API-команд не только префикс, а URL целиком.
- Возможно нужно добавить тип переменных date.
- Добавить логирование по ошибкам, чтобы потом статистику в админку выводить. Может быть стоит хранить в БД это.