Вводные
- Торговая компания работающая в сфере B2B
- Нужно опубликовать каталог товаров для автоматической загрузки данных из него в базы клиентов
- Группы номенклатуры
- Номенклатура
- Цены
- Остатки
На мысль о данной реализации навела статья //infostart.ru/1c/articles/1217831/, но в ней больше описано про подход и реализацию со стороны 1С. В своей статье опишу настройку сервиса, формирование БД, настройку доступа и обращение по HTTP.
Вариант реализации
Установка сервиса PostgREST
Разворачивать сервис буду на VDS с Ubuntu 20.04, хостинг digitalocean.
Подключаюсь к серверу по SSH с помощью PuTTY (ссылка) используя SSL ключ (ссылка). Ключ можно указать в настройках подключения, так и загрузить в Pageant из группы программ PuTTY. Ключ в дальнейшем буду использовать для подключения к PostgreSQL через SSH туннель.
Добавляю разрешения в брандмауэр, включаю его
#Добавляю разрешения
sudo ufw allow ssh
sudo ufw allow http
sudo ufw allow https
#Включаю брандмауэр
sudo ufw enable
Более подробно по ссылке
Для работы потребуется PostgreSQL (PG), для Ubuntu 20.04, PostgREST поддерживает версию 9.4 и выше, в стандартных репозиториях ubuntu 12 версия. Инструкция по установке https://www.digitalocean.com/community/tutorials/how-to-install-postgresql-on-ubuntu-20-04-quickstart-ru
# Устанавливаем postgres
sudo apt install postgresql postgresql-contrib
Устанавливаю пароль пользователя, добавляю схему
# При установке будет создан пользователь postgres, он состыкован с одноименным пользователем в PostgreSQL
# Переходим в сеанс пользователя postgres
sudo -i -u postgres
#Запускаю psql
psql
# Устанавливаю пароль пользователя postgres, под ним буду подключаться к базе через клиента
ALTER USER postgres WITH PASSWORD 'NojmyXBOiOnoDacmuecd';
# Добавляю пользователя для подключения из сервиса
CREATE USER authenticator NOINHERIT LOGIN PASSWORD 'NojmyXBOiOnoDacmuecd';
#Создаю схему api
create schema api;
#Добавляю роль анонимного пользователя
create role web_anon nologin;
#Наследую права доступа web_anon для authenticator
grant web_anon to authenticator;
#Разрешаю использовать схему api
grant usage on schema api to web_anon;
#Выхожу из psql
\q
#Выхожу из пользователя postgres
exit
Для подключения к базе PG с рабочего места использую DBeaver https://dbeaver.io/, (ссылка на видео по функционалу). При подключении использую SSH туннель с SSL ключом.
Скриншоты настройки подключения
Сервис будет работать на PostgREST http://postgrest.org/en/v7.0.0/
Для работы сервиса PostgREST буду использовать отдельного пользователя с ограниченными правами (
ссылка на статью по теме).
# Добавляю пользователя ubuntu
adduser pgadmin
# указываю пароль и остальные параметры
Приложение PostgREST поставляется в виде бинарного файла и запускается с параметрами подключения, которые можно зафиксировать в конфигурационном файле, пример файла можно получить запустив ./postgres --help
Получаем ссылку на архив по ссылке https://github.com/PostgREST/postgrest/releases/latest (ссылка на документацию)
UPD: Если используете PG14, то нужная версия v9.0.0 и выше
Получение приложения, настройка
#переходим в папку пользователя pg_admin
cd /home/pgadmin/
#Скачиваем архив
wget https://github.com/PostgREST/postgrest/releases/download/v7.0.1/postgrest-v7.0.1-linux-x64-static.tar.xz
# Распаковываем архив
tar -xf postgrest-v7.0.1-linux-x64-static.tar.xz
# Добавляем файл
nano postgrest.conf
# Содержимое файла
db-uri = "postgres://authenticator:NojmyXBOiOnoDacmuecd@localhost:5432/postgres"
db-schema = "api"
# this schema gets added to the search_path of every request
db-anon-role = "web_anon"
db-pool = 10
db-pool-timeout = 10
server-host = "!4"
server-port = 3000
#меняем владельца файлов на pgadmin
#загружали файлы из под root, без этого будет ошибка доступа к файлам
chown -R pgadmin:pgadmin /home/pgadmin/
Создаем, запускаем службу (ссылка, раздел Daemonizing)
#создаю файл сервиса
nano /etc/systemd/system/postgrest.service
#Содержимое файла
[Unit]
Description=REST API for any Postgres database
After=postgresql.service
[Service]
Type=simple
User=pgadmin
ExecStart=/home/pgadmin/postgrest /home/pgadmin/postgrest.conf
ExecReload=/bin/kill -SIGUSR1 $MAINPID
[Install]
WantedBy=multi-user.target
#Обновляю список сервисов
systemctl daemon-reload
#Запускаю сервис
systemctl start postgrest
#Проверяю корректность запуска
systemctl status postgrest
#Устанавливаю атозапуск
systemctl enable postgrest
Служба работает на 3000 порту, опубликуем её на 80 используя nginx
Установка, настройка nginx
#Устанавливаю nginx
sudo apt install nginx -y
#создаю файл
sudo nano /etc/nginx/sites-available/postgrest
# Содержимое
server {
listen 80;
server_name postgrest.malikov.pro;
#reverce proxy
location / {
proxy_pass http://localhost:3000;
}
}
# После сохранения файла
# создаю ссылку для активации настроек
sudo ln -s /etc/nginx/sites-available/postgrest /etc/nginx/sites-enabled/
# проверяю корректность настроек
sudo nginx-t
# загружаю изменения настроек в сервис
sudo service nginx reload
Проверяем подключение.
Создание структуры базы данных
create table api.products (
id serial primary key,
name text NOT NULL,
unit_name text NOT NULL,
price numeric,
count numeric,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
completed_at TIMESTAMPTZ
);
При изменении строк фиксирую дату изменения, это реализовано через триггеры (документация) (ссылка на статью по теме)
Запросы создания функции и подключения к таблице через триггеры
Создание функции
CREATE OR REPLACE FUNCTION api.trigger_set_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Добавление триггера
CREATE TRIGGER set_timestamp
BEFORE UPDATE ON api.products
FOR EACH ROW
EXECUTE PROCEDURE api.trigger_set_timestamp();
Заполняю тестовыми данными, для этого использую https://www.mockaroo.com/
Скриншот настройки генерации
Полученный файл запускаю через DBeaver.
Настройка прав доступа
(Ссылка на урок)
Добавляю права доступа для web_anon на чтение таблицы api.products
grant select on api.products to web_anon;
Проверяю результат
Добавляю пользователя с правом записи
#Добавляю пользователя
create role admin_user nologin;
#Расширяю права пользователя authenticator
grant admin_user to authenticator;
#Разрешаю доступ к схеме
grant usage on schema api to admin_user;
#Разрешаю полный доступ к базе
grant all on api.products to admin_user;
#Разрешаю доступ к последовательности таблицы (используется для формирования идентификатора записи)
grant usage, select on sequence api.products_id_seq to admin_user;
Для аутентификации на уровне PostgREST используется JWT формата HS256 (статья по работе из 1С) , ключ фиксируется в конфигурационном файле.
Добавление настройки JWT ключа
# Открываем файл на редактирование
nano /home/pgadmin/postgrest.conf
# Добавляем строку настройки, значение указываем свое
# если используете спецсимволы, то нужно сохранять в Base64 и указывать соответствующий флаг
jwt-secret = "SWFCI2oJCo52f2vhEZ7RoJk5fLsH9qj5"
# Перезагружаем сервис
systemctl restart postgrest
Токен можно сгенерировать используя сервис https://jwt.io/, результат помещается в заголовок HTTP запроса
"Authorization: Bearer $JWT_TOKEN"
В теле запроса на получение токена указывается поле "role", для которой выдается токен. При таком подходе токен будет бессрочным, что затрудняет его отзыв. Для решения вопроса можно добавить поле "exp" указывающее на время истечения токена.
Скриншот с примером генерации ключа
Для большей гибкости можно использовать параметры из тела JWT токена, пример ограничения доступа по email
#Добавялем схему auth
create schema auth;
#Разрешаем использовать схему пользователям,
#брал из оф. примеров, по идее можно указать "to authenticator"
grant usage on schema auth to web_anon, admin_user;
#Добавляем функцию проверяющую email
# request.jwt.claim берется из текущих настроек запроса
create or replace function auth.check_token() returns void
language plpgsql
as $$
begin
if current_setting('request.jwt.claim.email', true) =
'disgruntled@mycompany.com' then
raise insufficient_privilege
using hint = 'Nope, we are on to you';
end if;
end
$$;
Добавляем использование функции в настройки сервиса
# Открываем файл на редактирование
nano /home/pgadmin/postgrest.conf
# Добавляем строку настройки
pre-request = "auth.check_token"
# Перезагружаем сервис
systemctl restart postgrest
При формировании токена в тело добавляем параметр email
{
"role": "todo_user",
"email": "disgruntled@mycompany.com"
}
Эти данные можно использовать в представлениях для отбора, например цены по доступным для пользователя с email.
Добавление, обновление данных
(Ссылка на документацию)
Для добавления данных используется POST запрос с JSON в теле запроса, поддерживается групповая загрузка, пример
[
{"name":"Chicken - Leg, Fresh","unit_name":"шт","price":57,"count":97},
{"name":"Chicken Giblets","unit_name":"шт","price":100,"count":25}
]
Для обновления данных используется PATCH, в параметрах запроса указывается идентификатор, например "?id=eq.1002" в теле обновляемые поля, например
{
"price":10020
}
Итог
С помощью PostgREST можно организовать сервис работы с данными использующий функционал Postgres с удобной оберткой в HTTP.
Благодарю за внимание.