Go to file
Andrey Kutin 7f114ed92b fix: keep HTTP response alive during async_write 2026-10-25 20:05:17 +03:00
deploy Add systemd unit, example config, and deb packaging 2026-07-03 09:44:39 +03:00
src fix: keep HTTP response alive during async_write 2026-10-25 20:05:17 +03:00
.gitignore Add gitignore 2026-07-13 16:42:25 +03:00
CMakeLists.txt fix: keep HTTP response alive during async_write 2026-10-25 20:05:17 +03:00
LICENSE Initial commit 2026-07-03 06:30:47 +00:00
README.md fix: handle null database descriptions 2026-10-25 12:54:51 +03:00
config.json Add project source files and build configuration 2026-07-03 09:40:24 +03:00
configure.sh fix directory before cpack 2026-08-05 10:56:00 +03:00

README.md

cards

Веб-сервер на C++20 с использованием Boost.Beast и PostgreSQL. Отдаёт случайные записи из таблицы random_data по HTTP API.

Требования

  • Компилятор с поддержкой C++20 (GCC 10+, Clang 10+)
  • CMake >= 3.14
  • PostgreSQL (runtime) + libpq + libpqxx >= 7.0
  • Boost (system)
  • nlohmann_json 3.x

Установка зависимостей (Debian/Ubuntu):

sudo apt-get install libboost-all-dev libpq-dev libpqxx-dev nlohmann-json3-dev cmake build-essential

Сборка

./configure.sh

Скрипт устанавливает зависимости для Debian/Ubuntu, собирает проект и создаёт deb-пакет.

Или вручную:

mkdir -p build && cd build
cmake ..
make -j"$(nproc)"
cd ..

Бинарник: build/bin/cards

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

По умолчанию читает config.json из рабочей директории. Можно указать путь к другому файлу:

./build/bin/cards /path/to/config.json

Для подробного вывода этапов запуска и итоговой конфигурации используйте флаг --debug. Он также включает диагностику работы с БД: создание схемы, запуск и остановку воркеров, запросы, выбранную стратегию выборки, результаты и переподключения после ошибок. Пароль базы данных в таком выводе не раскрывается.

./build/bin/cards --debug
./build/bin/cards --debug /path/to/config.json

Аргументы можно передавать в любом порядке. Программа принимает не более одного пути к файлу конфигурации.

Поля config.json

server

Поле Тип Обязательно
address string да
port int да
threads int да

database

Поле Тип Значение по умолчанию
host string "localhost"
port int 5432
dbname string пустая строка
user string пустая строка
password string (пусто)

Переменные окружения

Переопределяют соответствующие поля config.json:

  • DB_HOST
  • DB_PORT
  • DB_NAME
  • DB_USER
  • DB_PASSWORD

Пример:

DB_PASSWORD=secret ./build/bin/cards

API

Метод Путь Описание
GET / Случайная запись из таблицы random_data
GET /cards?count=N До N случайных записей из таблицы random_data
GET /health Проверка состояния сервера

Параметр count обязателен для /cards, должен быть целым числом и ограничивается диапазоном от 1 до 1000. Если записей в базе меньше запрошенного числа, сервер вернёт все доступные записи. Для выборок до 10 записей применяется обычная стратегия, для бо́льших — рекурсивный SQL-запрос.

Пример ответа GET /

{
    "id": 3,
    "name": "Gamma",
    "text": "Third test record",
    "description": "Third test record",
    "created_at": "2025-01-01 12:00:00",
    "timestamp": 1735732800000000000
}

Пример ответа GET /cards?count=2

{
    "data": [
        {
            "id": 3,
            "name": "Gamma",
            "text": "Third test record",
            "description": "Third test record",
            "created_at": "2025-01-01 12:00:00"
        },
        {
            "id": 5,
            "name": "Delta",
            "text": "Fourth test record",
            "description": "Fourth test record",
            "created_at": "2025-01-01 12:00:00"
        }
    ],
    "count": 2,
    "requested_count": 2,
    "total_available": 5,
    "strategy": "slow",
    "timestamp": 1735732800000000000
}

Пример ответа GET /health

{
    "status": "ok",
    "timestamp": 1735732800000000000
}

База данных

При первом запуске автоматически создаётся таблица random_data. Начальные данные сервер не добавляет: их необходимо загрузить самостоятельно.

Схема таблицы:

CREATE TABLE random_data (
    id          SERIAL PRIMARY KEY,
    name        VARCHAR(100) NOT NULL,
    text        TEXT NOT NULL,
    description TEXT,
    created_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

При обновлении существующей базы данных схему нужно мигрировать отдельно: CREATE TABLE IF NOT EXISTS не изменяет уже созданные таблицы. Например, для предыдущей схемы с полем value выполните ALTER TABLE random_data RENAME COLUMN value TO text;.

Поле description необязательное: если в БД оно имеет значение NULL, в ответе API оно будет передано как null.

systemd

Юнит-файл: deploy/systemd/cards.service

[Unit]
Description=Cards web server
After=network-online.target

[Service]
Type=simple
User=cards
Group=cards
ExecStart=/usr/bin/cards /etc/cards/config.json
Restart=on-failure
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=yes
ProtectSystem=full
ProtectHome=yes
PrivateTmp=yes

Установка вручную:

sudo cp deploy/systemd/cards.service /usr/lib/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now cards

Сборка deb-пакета

cd build
cmake ..
cpack

Результат: cards-1.0.1-Linux.deb

Содержимое deb-пакета

Путь Назначение
/usr/bin/cards Исполняемый файл
/usr/lib/systemd/system/cards.service systemd-юнит
/etc/cards/config.json.example Пример конфигурации
/usr/share/doc/cards/LICENSE Лицензия

Управляющие скрипты

При установке deb-пакета (postinst):

  • Создаётся пользователь и группа cards
  • Копируется /etc/cards/config.json.example → /etc/cards/config.json (если нет)
  • Сервис cards включается и запускается

При удалении (prerm):

  • Сервис останавливается и отключается

Завершение работы

  • Вручную: Ctrl+C или kill с сигналом SIGTERM
  • systemd: systemctl stop cards