-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathreadme.md
192 lines (153 loc) · 13.7 KB
/
readme.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
Данный проект является экспериментом. Здесь [предыстория и небольшой анализ ситуации](https://1div0.ru/бизнес-логика-в-хранимках/ "Бизнес логика в хранимках — это хорошо или плохо?").
Девиз проекта:
> Only an **gormless** fool will begin to place business logic in stored procedures
Репозиторий: [GitHub](https://github.com/a1div0/ormless.git "GitHub - Ormless")
# Концепт
![Концепт Ormless](https://1div0.ru/wp-content/uploads/2020/03/Ormless-2.png "Концепт Ormless")
Работает это так:
1. Запускается приложение Ormless на сервере. При запуске оно считывает все странички с JavaScript-скриптами и кладёт их в переменную.
2. Пользователь открывает страничку. Веб-сервер получает соответствующую команду и отдаёт из переменной страничку.
3. Страница запрашивает данные, для чего отправляет API-команду на сервер.
4. Сервер получает API-команду, проверяет авторизацию. Если всё нормально, команда сравнивается с имеющимся на сервере описанием этой команды - проверяются типы параметров и их содержимое.
5. Если всё нормально - команда преобразуется в SQL-команду запуска хранимки. Команда отрабатывает, результат заворачивается в JSON.
6. JSON передаётся на браузер как ответ.
7. Переходим к пункту 2. Запись данных по тому же сценарию.
Разработка ведётся в двух местах:
1. Разработка интерфейса и прочего взаимодействия с пользователем (JavaScript).
2. Разработка БД и реализация бизнес-логики (один из SQL).
# Как пользоваться
1. В папке с программой необходимо создать папку (например `pages`) с веб-страничками (`*.htm`, `*.css`, `*.js`), картинками и другими медиафайлами.
2. Заполнить файл настроек веб-сервера, файл `config.yml`.
3. Заполнить файл-описание команд API `command_parameters.json`. Соответствующие хранимки должны быть в базе данных.
4. Запустить приложение.
Ниже всё расписано более подробно.
# Файл настроек
Рассмотрим содержимое файла настроек:
``` yml
WebPublic:
DomainName: lvh.me
HttpPort: 80
HttpIp: localhost
Https:
Enabled: false
Port: 443
Ip: localhost
Provider: letsencrypt
SubscriberEmail: info@lvh.me
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
```
Подробнее о каждом параметре.
### DomainName
Адрес приложения снаружи или имя сайта. Нужен для генерации ссылок, создании сертификата SSL и проч. Если публичный (внешний) порт отличается от стандартного, указываем здесь. Например `lvh.me:5000`. Протокол подставит программа. Если SSL используется - `https://`, иначе `http://`.
### HttpPort
Указывает какой порт слушать. Из-за особенностей хостинга может не совпадать с публичным.
### HttpIp
Указывает IP для прослушивания. Из-за хостинга может потребоваться указать что-то отличное от `localhost`.
### Https
Веб-сервер поддерживает работу с SSL. Включается параметром `Enabled`. Указываем также `IP` и `Port` для прослушивания. Есть два режима работы: `letsencrypt` - если будем использовать автопродляемый бесплатный сертификат от [Let's Encrypt](https://letsencrypt.org "Let's Encrypt") и `custom` - если сертификат покупной.
В первом случае указываем почту для обратной связи `SubscriberEmail` и папку `CertFolder`, в которую будут записаны сертификаты и закрытый ключ.
Во втором случае указываем пути файлов - сертификата `CertPemFileName` и закрытого ключа `KeyPemFileName` в формате PEM. Если у Вас другой формат - разделите их по файлам в текстовом редакторе.
### WebPagesFolder
Имя папки с файлами веб-страниц и ресурсами. Файлы из этой папки будут доступны снаружи по тем же адресам, что и внутри папки. Кроме файла `index.html`. Например: файл, который лежит в папке по адресу `\img\aaa.png` снаружи будет доступен по адресу `https://domain.name/img/aaa.png`.
### JsSettingsPath
Это относительный от доменного имени путь (снаружи) до генерируемого во время запуска веб-сервера JS-файла с полезными константами для формирования веб-страниц на клиенте. Пример содеримого данного файла:
``` 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` - это ссылки на страницы авторизации соответствующего сервиса авторизации.
### CommandPathPrefix
Все адреса API-команд базы данных форматированы так:
Доменное имя сайта + **префикс команды** + имя команды + "?" + GET-параметры (если вызов GET-методом). Пример: `http://lvh.me:9000/cmd/entity.category_list?p=11`
### ParametersCountLimit
Максимальное количество параметров в API-команде, для предварительной проверки.
### CommandParametersFileName
Имя файла с описаниями API-команд. См. ниже.
### OAuthVerificationCodePath
URL команды верификации OAuth. Это адрес для Callback-вызовов серверов OAuth. В личных кабинетах следует указывать адрес сайта + это значение. Из примера выше: `http://lvh.me/cmd/oauth_verification_code`.
### OAuthYandex, OAuthGoogle, OAuthGitHub
Настройки OAuth для авторизации на соответствующем сервисе авторизации. Содержат по две настройки:
* `client_id` - идентификатор клиента, полученный при регистрации на соответстувющем сервисе.
* `client_secret` - это пароль или секрет, также выданный при регистрации.
### DbConnectionString
Строка подключения к базе данных.
# Файл описания API-команд
Для валидации команды перед вызовом используется файл-описание. В базе данных должны быть реализованы хранимые процедуры в точном соответствии с файлом описания. Все поля обязательны, если не указано иное. Пример:
``` json
[
{
"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 - то нужно вернуть элементы верхнего уровня." }
]
}
]
```
### cmd_name
Все команды имеют имя, как составная часть адреса команды. Есть также команды для внутреннего пользования, ведь веб-сервер Ormless тоже использует БД. Вот названия необходимых ему команд:
* `#USER_REGISTER#` - запись пользователя при успешной авторизации через сервис OAuth. Вызвать снаружи эту команду нельзя.
### db_proc_name
Название хранимки в базе данных.
### call_method
Метод вызова данной команды:
* `GET` - вызов снаружи HTTP-методом GET
* `POST` - вызов снаружи HTTP-методом POST
* `ORMLESS` - при внутреннем вызове веб-сервера
### description
Текстовое описание процедуры, как элемент документации.
### parameters
Массив параметров процедуры. Каждый элемент содержит:
* `name` - имя параметра хранимой процедуры.
* `type` - тип значения параметра.
* `default` - значение по умолчанию. Указывать необязательно. Если параметр не передан, то будет использоваться указанное значение. Если параметр не передан и это значение не указано - веб-сервер вернёт ошибку до вызова хранимой процедуры.
* `description` - описание параметра, как элемент документации.
Параметры могут быть следующих типов:
* `bool`
* `int`
* `uint`
* `float`
* `string`
Диапазоны значений всех числовых параметров соответствуют размеру переменной в 64 бит.
# Ограничения
1. В программе использован драйвер для БД Microsoft SQL Server. При использовании другой БД программу необходимо перекомпилиовать с соответствующим драйвером.
# Возможные улучшения
1. Скомпилировать приложение с драйверами всех баз, или с одним из универсальных драйверов и вынести это в настройку.
2. Возможно имеет смысл давать назначать для API-команд не только префикс, а URL целиком.
3. Возможно нужно добавить тип переменных date.
4. Добавить логирование по ошибкам, чтобы потом статистику в админку выводить. Может быть стоит хранить в БД это.