Skip to content
Projects

mishkatik/infra-billing has 90 GitHub stars and 10 forks. Star changes are not measured yet. Last checked 2026-11-12 18:07 UTC. Last push 2026-11-12.

How these counts are measured

Self-hosted панель учёта расходов на инфраструктуру: автосинк серверов, балансов и платежей через API хостеров. Мультивалютность, прогнозы, Telegram-уведомления.

Measured star growth

in 24h
Update delayed
in 7 days
Update delayed
in 15 days
Update delayed
in 1 month
Update delayed

Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.

README.md

Original

Infra Billing Panel

Персональная (single-user, self-hosted) панель учёта расходов на инфраструктуру: VPS, выделенные серверы, домены, CDN, объектные хранилища, лицензии и т.п. Панель сама ходит в API хостинг-провайдеров, тянет баланс аккаунта, список ресурсов, тарифы и даты списаний, складывает снапшоты и строит аналитику/прогнозы. Провайдеров без API владелец ведёт вручную.

Дашборд Infra Billing Panel

Возможности

  • Провайдеры с API: Timeweb Cloud, Hetzner Cloud, Hostkey (InvAPI), netcup, HostBill, ISPsystem BILLmanager, Selectel, 4VPS.SU, Netlen, Beget Cloud, Porkbun, Spaceship, Vultr, Linode, Aeza, VDSina, Cloudflare, StormWall, NetAngels, Yandex Cloud, Double Servers, OpenRouter, HostUp, HostVDS / CloudCore, DataPacket, Tube-Hosting, Gcore. Плюс Manual — провайдеры без API ведутся руками.
  • Автосинк (по расписанию + кнопкой): баланс + валюта аккаунта, список серверов/услуг, даты следующих списаний; история баланса по дням (снапшоты).
  • Импорт платежей там, где API отдаёт реестр: пополнения и списания (BILLmanager, Netlen, Vultr, Aeza, VDSina, Double Servers, HostVDS, Tube-Hosting, Gcore), пополнения и счета (Linode), оплаченные счета (HostBill, DataPacket), платежи (HostUp), потребление (Selectel, Yandex Cloud, OpenRouter), история биллинга (Cloudflare, best-effort). Ручные платежи — в журнале.
  • Аналитика: месячные/годовые расходы, разрезы по провайдеру / стране / типу / валюте, прогноз по будущим списаниям, ближайшие списания с подсветкой критичности.
  • Мультивалютность: суммы в своей валюте, конвертация к базовой; курсы ЦБ РФ или ручные.
  • Telegram-уведомления (только исходящие): низкий баланс, скорое списание, ошибка синка. Доставка напрямую или через SOCKS5-прокси, если Telegram с сервера недоступен.
  • Безопасность: аккаунт владельца создаётся при первом запуске; вход по паролю, passkey (WebAuthn) и/или Telegram (OpenID Connect) — методы переключаются в настройках; сессия — JWT в httpOnly-cookie; токены провайдеров шифруются AES-256-GCM. В списке и карточке провайдера секретов нет — только признак «задан»; показать их в открытом виде можно лишь по «глазику» в форме, из сессии владельца.
  • MCP-сервер на /api/mcp: любой MCP-клиент смотрит расходы, балансы и сервисы, а с токеном полного доступа — заводит сервисы и платежи и запускает синк.

RWP Shop

RWP Shop

RWP Shop — лучший многофункциональный бот для VPN-коммерции на Remnawave: личный кабинет клиента, платежи, подписки, поддержка, партнёрская программа и аналитика. Работает в браузере и как Mini App в Telegram. Есть интеграция с Infra Billing. Скидка 15% по промокоду MISH.

Стек

  • Backend: NestJS 11 (Node 22) · Prisma 7 · PostgreSQL 18 · zod (nestjs-zod) · axios · grammY
  • Frontend: Vite · React 19 · shadcn/ui · Tailwind CSS v4 · Golos Text · TanStack Query · axios
  • Монорепо: npm-workspaces — apps/backend, apps/frontend, packages/shared (общие zod-схемы)
  • Деплой: единый Docker-образ (бэкенд раздаёт API + собранный SPA) + отдельный Postgres

Установка (production)

Требования: Docker + Docker Compose plugin; домен и reverse-proxy с TLS (см. ниже — без HTTPS вход не работает, т.к. сессионная кука Secure). Образ берётся из GHCR (ghcr.io/mishkatik/infra-billing).

# 1. Каталог
mkdir -p /opt/infra-billing && cd /opt/infra-billing

# 2. Скачать prod-compose и пример конфига в .env
curl -fsSL -o docker-compose.yml https://raw.githubusercontent.com/mishkatik/infra-billing/main/docker-compose-prod.yml
curl -fsSL -o .env https://raw.githubusercontent.com/mishkatik/infra-billing/main/.env.example

# 3. Сгенерировать ключ шифрования (GNU sed; разделитель # — т.к. base64 содержит /)
sed -i "s#^ENCRYPTION_KEY=.*#ENCRYPTION_KEY=$(openssl rand -base64 32)#" .env

# 4. Пароль БД — один и тот же в POSTGRES_PASSWORD и в DATABASE_URL
pw=$(openssl rand -hex 24) && sed -i "s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$pw/" .env && sed -i "s|^\(DATABASE_URL=\"postgresql://infra:\)[^\@]*\(@.*\)|\1$pw\2|" .env

# 5. Запуск (миграции применяются на старте)
docker compose up -d && docker compose logs -f

При первом открытии панель покажет экран регистрации — создайте аккаунт владельца (логин + пароль; passkey можно добавить позже).

Панель поднимется на 127.0.0.1:8080. Дальше — reverse-proxy с TLS на ваш домен.

Reverse proxy + TLS (обязательно)

Контейнер слушает только 127.0.0.1:8080 — наружу не торчит. Поставьте перед ним reverse-proxy, который терминирует TLS. Без HTTPS логин не сработает (сессионная кука помечена Secure).

Пример Caddy (сам выпустит сертификат):

billing.example.com {
    reverse_proxy 127.0.0.1:8080
}

Caddy по умолчанию не ограничивает ожидание ответа бэкенда. В прокси, где такой лимит есть, поднимите его до ≥ 120s — ручной синк всех провайдеров может отвечать до ~2 минут (в nginx это proxy_read_timeout, по умолчанию 60s).

Обновление

Миграции базы применяются при старте нового образа и назад не откатываются, поэтому перед обновлением сделайте дамп и запомните текущую версию (она рядом с названием «Infra Billing» в боковом меню):

cd /opt/infra-billing && mkdir -p backups
docker compose exec -T infra-billing-db sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc' > backups/infra_billing-$(date +%Y%m%d-%H%M).dump

Токены провайдеров в дампе зашифрованы ключом ENCRYPTION_KEY из .env — без него их не восстановить, поэтому храните .env вместе с дампами.

Обновить и перезапустить:

cd /opt/infra-billing && docker compose pull && docker compose down && docker compose up -d && docker compose logs -f

Почистить неиспользуемые образы:

docker image prune

Откат на прошлую версию

Повторный docker compose pull не поможет: :latest уже указывает на новую версию, а прошлая может не запуститься на базе после новых миграций. Верните базу из дампа и закрепите прошлую версию образа:

cd /opt/infra-billing

# 1. Остановить панель и пересоздать базу из дампа (подставьте имя своего файла из backups/)
docker compose stop infra-billing
docker compose exec -T infra-billing-db sh -c 'dropdb -U "$POSTGRES_USER" "$POSTGRES_DB" && createdb -U "$POSTGRES_USER" "$POSTGRES_DB"'
docker compose exec -T infra-billing-db sh -c 'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --no-owner' < backups/infra_billing-ГГГГММДД-ЧЧММ.dump

# 2. Закрепить прошлую версию (например, 0.47.0) и поднять панель
sed -i 's#image: ghcr.io/mishkatik/infra-billing:.*#image: ghcr.io/mishkatik/infra-billing:0.47.0#' docker-compose.yml
docker compose up -d

Чтобы снова получать обновления, верните в docker-compose.yml тег :latest.

PostgreSQL 17 → 18 (с 0.44.0, не обязательно, но рекомендуется)

С 0.44.0 compose в репозитории использует postgres:18 и монтирует том в /var/lib/postgresql (раньше — /var/lib/postgresql/data). Переходить не обязательно: приложение работает и с PostgreSQL 17 — достаточно не трогать свой docker-compose.yml и обновлять только образ панели (docker compose pull). Но обновиться рекомендуется, чтобы не отставать от поддерживаемых версий и от compose-файла в репозитории. Файлы 17-й версии новый сервер сам не подхватит — данные надо обновить на месте, это делает образ pgautoupgrade:

# 1. Дамп на всякий случай
docker compose exec -T infra-billing-db sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc' > infra_billing-pg17.dump

# 2. Скачать новый compose (в нём уже postgres:18 и новый путь тома)
curl -fsSL -o docker-compose.yml https://raw.githubusercontent.com/mishkatik/infra-billing/main/docker-compose-prod.yml

# 3. Временно поставить у сервиса infra-billing-db образ pgautoupgrade и прогнать апгрейд
sed -i 's#image: postgres:18.*#image: pgautoupgrade/pgautoupgrade:18.6-trixie#' docker-compose.yml
docker compose down && docker compose up -d infra-billing-db && docker compose logs -f infra-billing-db
#    (ждать «database system is ready to accept connections» после «End of reindexing»)

# 4. Вернуть официальный образ и поднять всё
sed -i 's#image: pgautoupgrade/pgautoupgrade:.*#image: postgres:18.6#' docker-compose.yml
docker compose down && docker compose pull && docker compose up -d

Вход в систему

Аккаунт владельца создаётся при первом запуске прямо в панели — на экране регистрации (логин + пароль; там же можно сгенерировать стойкий пароль и сразу скопировать его).

Экран регистрации доступен без авторизации, пока админ не создан. Пройдите настройку сразу после деплоя и не открывайте панель в интернет до этого — иначе аккаунт может занять тот, кто откроет её первым.

Способы входа — пароль, passkey (WebAuthn: Touch ID / Windows Hello / аппаратный ключ) и Telegram (OpenID Connect). Это альтернативы, а не второй фактор — достаточно любого включённого. Управление — Настройки → Вход в систему: тумблеры «Пароль» / «Passkey» / «Telegram», добавление и удаление passkey-ключей, привязка Telegram. Хотя бы один рабочий способ всегда остаётся включённым (защита от блокировки).

Passkeys работают только в защищённом контексте (HTTPS или localhost). В настройках passkey задайте rpId (домен без https://) и Origin (полный адрес) — кнопка «Подставить текущий хост» заполнит их автоматически.

Вход через Telegram настраивается в BotFather: бот → Web Login → Switch to OpenID Connect Login. Там же берутся Client ID и Client Secret — это отдельное значение, не токен бота. В Allowed URLs внесите адрес панели и Redirect URL (<адрес панели>/api/auth/telegram/callback; на странице настроек его можно скопировать). Затем в панели укажите «Адрес панели» (кнопка «Текущий адрес»), Client ID и Client Secret, сохраните и нажмите «Привязать Telegram» — входить сможет только привязанный аккаунт. Смена Client ID, Secret или адреса сбрасывает привязку.

Восстановление доступа (забыли пароль)

Если вход по паролю выключен, а passkey или Telegram недоступны, — сначала верните вход по паролю. Passkey и привязка Telegram сохранятся:

docker compose exec infra-billing cli enable-password

Если и сам пароль утерян — сбросьте администратора. Это удалит логин, пароль, все passkey и привязку Telegram; панель снова покажет экран первичной настройки.

# Интерактивное меню:
docker compose exec -it infra-billing cli

# Либо сразу, без подтверждения:
docker compose exec infra-billing cli reset-admin --yes

Конфигурация (.env)

Переменная Назначение
PORT Порт бэкенда (он же отдаёт SPA), default 8080
POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB Параметры контейнера Postgres
POSTGRES_HOST_PORT Порт публикации Postgres на 127.0.0.1 (default 5432)
DATABASE_URL Строка подключения Prisma (хост = infra-billing-db в docker, 127.0.0.1 локально)
ENCRYPTION_KEY Обязательно. AES-256-GCM ключ для секретов в БД — токены провайдеров, секрет сессии и Client Secret входа через Telegram (32 байта base64). После смены ключа Client Secret нужно ввести заново
DOCS true — включить Swagger UI на /api/docs (публично при включении). Default false
COOKIE_SECURE Secure-флаг сессионной куки. Не задан — следует за NODE_ENV (в prod включён). false — только для http-деплоя за защищённым транспортом (Tailscale/NetBird); passkeys всё равно требуют https

API-доступ

Панель отдаёт тот же REST API, что использует фронтенд. Для программного доступа создайте токен: Настройки → API токены → Добавить (имя и доступ), затем копируйте токен. Запросы — с заголовком Authorization: Bearer <token>:

curl -H "Authorization: Bearer ib_…" https://infra-billing/api/providers

Каждый провайдер приходит с массивом accounts[] (у каждого аккаунта свои креды, баланс и синк) и суммами balances[] по валютам; аккаунт правится через /api/provider-accounts/{uuid}.

Доступ токена выбирается при создании:

  • только чтение (по умолчанию) — только GET-запросы, на остальное токену отвечают 403;
  • полный — всё, что есть в API: провайдеры, сервисы, платежи, синк, настройки, аналитика.

Ни один токен не видит секреты провайдеров и не управляет входом и другими токенами: это доступно только из сессии владельца. Токены, созданные до появления выбора, — с полным доступом.

MCP

Панель — MCP-сервер (Streamable HTTP) на https://<панель>/api/mcp, авторизация тем же токеном. В конфиге клиента, который поддерживает удалённые серверы с заголовками:

{
  "mcpServers": {
    "infra-billing": {
      "url": "https://infra-billing.example.com/api/mcp",
      "headers": { "Authorization": "Bearer ib_…" }
    }
  }
}

Сервер отдаёт три инструмента: list_operations (каталог доступных операций), describe_operation (параметры, тело и ответ одной операции в JSON Schema) и call_operation (вызов). Операции — обёртки над REST API, поэтому ограничения токена те же: токен «только чтение» видит и вызывает только чтение. Удалить провайдера или аккаунт, слить провайдеров и поменять настройки через MCP нельзя — это делается в панели. Каждый вызов пишется в лог ([MCP] <токен>: <операция> → <статус>).


Провайдеры — где взять доступ

Добавление: Провайдеры → Добавить, выбрать тип, ввести токен/креды (шифруются, в API не возвращаются). После сохранения провайдер сразу синкается.

  • Timeweb Cloud — ЛК → «API и Terraform» → создать токен.
  • Hetzner Cloud — Project → Security → API Tokens (read). Баланса/счетов/платежей в API нет — синкаем серверы, месячный cap тарифа, страну и синтетическую дату списания (1-е число следующего месяца) для upcoming-алертов.
  • Hostkey — InvAPI (invapi.hostkey.ru) → меню пользователя → API-ключи. Тянет credit-баланс (WHMCS), серверы с prebill_rate / периодом и синтетическую дату списания (1-е число следующего месяца). Реестр платежей в v1 не импортируется.
  • netcup — кнопка «Авторизоваться через netcup» в форме: войдите в SCP в браузере и подтвердите (OAuth2 device flow, токен подтянется сам). Баланса/цен/списаний в API нет — только серверы (страна определяется по дата-центру).
  • HostBill — base URL вашей инсталляции (напр. https://secure.veesp.com/api) + email + пароль.
  • ISP BILLmanager — base URL (https://.../billmgr) + логин + пароль (+ TOTP-секрет, если 2FA по OTP). Если хостер закрыл вход в API капчей (например, FirstVDS), панель сама переключается на stateless-авторизацию authinfo. Для неё добавьте IP сервера панели в список разрешённых — у FirstVDS это «Доступ к API» в настройках биллинга — иначе синк упадёт с ошибкой forbidden_auth_method. 2FA в этом режиме не работает (нет сессии для подтверждения кода) — отключите её у такого хостера.
  • Selectel — номер аккаунта + сервисный пользователь IAM (имя + пароль) с ролью «Наблюдатель» (reader): она даёт баланс, потребление и список бакетов (роли «Биллинг» для бакетов мало); опц. имя проекта Облачной платформы — для облачных серверов, роутеров и бакетов S3. Стоимость каждого — среднее потребление в сутки за последние 7 дней (у сервера — вместе с его дисками и публичными IP).
  • 4VPS.SU — API-ключ (ЛК → раздел API) + id панели (обычно 1).
  • Netlen — API-ключ (панель → раздел API). Важно: добавьте IP сервера в whitelist ключа, иначе запросы отклоняются (NO_IP_WHITELISTED). Баланс, серверы (цена в USD) и реестр транзакций (пополнения/списания) — всё из API.
  • Beget Cloud — логин аккаунта cp.beget.com + пароль (новый Cloud API, JWT). Опц. TOTP-секрет, если включена 2FA по приложению, и отдельный «API-пароль» из панели (Аккаунт → Безопасность → Beget API) — он включает синк баланса. Тянет VPS и облачные сервисы (БД/S3/CDN), цены в RUB; истории платежей и дат списаний в API нет.
  • Porkbun — API key + Secret API key (Account → API Access; ключ нужно включить на каждом домене). Регистратор доменов: тянет домены (type=domain, дата продления = дата истечения), баланс (USD) и цену продления по TLD. Истории платежей в API нет.
  • Spaceship — API key + secret (API manager). Регистратор доменов: тянет домены (type=domain, дата продления = дата истечения). Баланса, цен продления и истории платежей в API нет — цену домена можно проставить руками.
  • Vultr — API-ключ (Account → API). Если на ключе включён Access Control — добавьте IP сервера в whitelist, иначе запросы отклоняются (403). Тянет баланс (USD), серверы (цена из тарифа) и реестр billing-history (пополнения и списания); страна определяется по региону.
  • Linode (Akamai) — Personal Access Token (Account → API Tokens; доступ на чтение к Account и Linodes). Тянет серверы (цена из тарифа, с учётом региональных цен), баланс (USD) и историю платежей (пополнения + счета). Linode постоплатный: balance — это задолженность, поэтому храним чистую позицию (кредит — плюс, долг — минус). Страна определяется по региону.
  • Aeza — API-ключ (панель → API Keys). Тянет серверы (цена по сроку оплаты, страна по локации, дата продления), баланс (валюта аккаунта — RUB/USD/EUR) и историю транзакций (пополнения + списания). Суммы в API — в минорных единицах (÷100). Баланс предоплатный.
  • VDSina — постоянный API-токен из личного кабинета (список пользователей: https://cp.vdsina.ru/user/list). Тянет баланс основного счёта (RUB), серверы и операции по балансу (оплаченные пополнения + списания).
  • Cloudflare — API-токен (My Profile → API Tokens) с правами Registrar: Domains:Read и Billing:Read, плюс ID аккаунта (из URL дашборда или GET /accounts). Регистратор доменов: тянет домены (type=domain, годовой период, дата продления = дата истечения; цены в API нет → вводятся вручную) и историю биллинга (пополнения/списания, USD). Баланса в API нет (постоплата через Stripe); Billing API устарел, поэтому импорт платежей best-effort.
  • StormWall — API-ключ (личный кабинет → API-ключ). DDoS-защита/WAF: баланса, цен и платежей в API нет вообще (услуги вводятся с ценой вручную) — тянет только список услуг (/v3/services), для защищённых доменов — их реальное имя.
  • Yandex Cloud — авторизованный ключ (JSON) сервисного аккаунта целиком: в консоли создать сервисный аккаунт, выдать ему viewer на папку (инвентарь) и billing.accounts.viewer на биллинг-аккаунт (баланс и расходы), затем «Создать авторизованный ключ» — не статический ключ доступа. Папку и биллинг-аккаунт панель определяет сама и показывает бейджами прямо в форме. Тянет только Compute-инстансы (управляемые БД, бакеты, k8s в инвентарь не попадают), баланс биллинг-аккаунта и историю потребления по дням. Цен в Compute API нет — стоимость считается из отчёта потребления, а он отдаётся не чаще 1 запроса в минуту, так что на аккаунте с несколькими серверами цена и история могут подтянуться не с первого синка.
  • Double Servers — email + пароль от ЛК (doubleservers.com/login). Опц. TOTP-секрет, если включена 2FA по приложению. Тянет баланс (EUR), VPS (цена, дата продления expires_at) и историю: пополнения (/api/billing/history) + списания по серверам (/api/servers/{id}/history). Публичного API-токена нет — синк логинится в панель.
  • OpenRouter — Management API key (openrouter.ai → Settings → Management Keys). Обычный inference-ключ не подойдёт: синк проверяет его через /key и отклоняет с явной ошибкой. LLM-агрегатор, серверов у него нет — панель заводит по услуге (type=llm) на каждую модель, по которой был трафик. Цена услуги — фактический расход: за текущий месяц, а пока в нём трат нет — за всё окно /activity (последние ~30 суток UTC, посуточно). Оттуда же импортируются списания — по записи на «модель + день»; пополнений в API нет, поэтому в журнале только расход. Баланс — остаток купленных кредитов (/credits: пополнено − потрачено, USD). Названия моделей подставляются из каталога /models (тумблер «Имена из каталога» в форме; выключить — останутся слаги вида anthropic/claude-sonnet-5), вендор берётся из слага и показывается иконкой.
  • HostUp — API-ключ (cloud.hostup.se/api-management) со scope read:all или набором read:billing, read:services, read:vm, read:domains. Без read:services список VPS приходит пустым, без read:domains домены пропускаются (в логе — предупреждение). Тянет кредитный баланс аккаунта (SEK, или EUR, если аккаунт в евро), VPS (цена и цикл оплаты, дата списания — срок неоплаченного счёта, а если его нет — день после оплаченного периода paidThroughDate), cPanel-хостинг (type=other) и домены (дата списания — nextDueAt, срок счёта на продление). Полугодовой цикл приводится к месячной цене, 2–3 года — к годовой. Локации VPS в API нет — страну проставьте руками. Импортируются проведённые платежи живыми деньгами (карта, Swish, Bankgiro, SEPA…) как пополнения; оплата счёта из кредита и возвраты не импортируются. Pay-as-you-go Cloud VPS синкаются без цены.
  • HostVDS / CloudCore — логин и пароль OpenStack из панели (аккаунт → вкладка «API» → «OpenStack Dashboard», логин вида hostvds-…), не вход на сайт. Обе марки — одна панель; для CloudCore укажите base URL https://cloudcore.ru. Тянет серверы всех регионов: страна по региону, цена — месячная цена тарифа из публичного каталога плюс платный тариф сети, в валюте региона (EUR/USD, у CloudCore — RUB). Сверх пакета трафик не учитывается. Баланс и платежи — по желанию: email и пароль входа на сайт (+ TOTP-секрет, если включена 2FA); тогда тянутся баланс (USD), пополнения и списания — одно в день на сервер, с привязкой к услуге. Без них баланса нет (как у Hetzner).
  • DataPacket — API-ключ (клиентская панель → Security → Add API key; достаточно read-only). GraphQL API: тянет подписки — каждый сервер отдельной услугой (type=dedicated, цена — его позиция в подписке, страна по локации), а прочие регулярные позиции подписки (IP, трафик, DDoS, сборы) — одной услугой на подписку: в API у них нет id и привязки к серверу. Период и дата списания — из billingCycle подписки (месяц/квартал/год; дневной биллинг — постоплата без даты). Импортируются оплаченные счета как пополнения (сумма с НДС, дата оплаты); счета, оплаченные с баланса DataPacket или кредит-нотой, пропускаются. Баланса аккаунта в API нет.
  • Tube-Hosting — email + пароль от cp.tube-hosting.com (API-ключей нет — синк логинится в JSON API панели api.tube-hosting.com). Опц. TOTP-секрет, если на аккаунте подтверждена 2FA через приложение. Тянет баланс (EUR), группы услуг — по услуге на группу (сервер вместе с IPv4/трафик-бандлом; цена — сумма услуг группы, дата списания — оплаченный до runtime, период из runtimeInterval, 30 дней → месяц) и журнал: пополнения (PayPal, карта…) и списания за продления, привязанные к своей группе. Локации в API нет — страну проставьте руками.
  • Gcore — постоянный API-токен (портал → Profile → API tokens; заголовок APIKey). Деньги берутся из биллингового журнала аккаунта (/billing/client/v1; документированный /billing/org/v1 — только для реселлеров): баланс, оплаты картой (пополнения) и списания — по записи на позицию биллинга за день, окно 31 день. Услуги: с ролью токена Administrators — из Cloud cost report, по услуге на сервер вместе с подключёнными дисками и IP (страна по региону); с более узкой ролью (например, Users) Cloud недоступен — тогда по услуге на позицию биллинга («Instance … Frankfurt», «Block Storage …»). Цена — расход за последние 7 дней, приведённый к 30 дням. Оплата pay-as-you-go: дат списаний нет. Не-Cloud продукты (CDN и т.п.) — всегда по позициям биллинга.
  • Manual — без API, всё вводится руками.

Telegram-уведомления

  1. Создать бота у @BotFather, получить токен.
  2. Узнать свой chat id (например, через @userinfobot), боту нажать /start.
  3. В панели Настройки → Telegram — токен + chat id (опц. id топика), сохранить. Кнопка «Отправить примеры» пришлёт по образцу каждого типа уведомления. Изменения — без рестарта.
  4. SOCKS5-прокси (опционально) — если Telegram с сервера недоступен напрямую, в том же разделе укажите прокси: socks5h://user:pass@host:1080 (логин/пароль — только если прокси требует аутентификацию). Рекомендуемая схема — socks5h (DNS-имя резолвит сам прокси); со схемой socks5 имя резолвится локально и прокси получает голый IP — часть прокси такие CONNECT-запросы молча отбрасывает. Пустое поле — отправка напрямую. Таймаут запроса к Telegram — 10 секунд, ошибки доставки видны в логах контейнера.

Локальная разработка

Команды — через Task (brew install go-task), task без аргументов покажет список.

task install            # npm ci
task db-up              # поднять только Postgres (127.0.0.1:5432)

# .env для локального запуска вне docker — DATABASE_URL на 127.0.0.1:
#   DATABASE_URL="postgresql://infra:[email protected]:5432/infra_billing?schema=public"

task migrate            # prisma migrate dev
task dev                # backend :8080 + frontend :5173 (Vite проксирует /api)

Открыть http://localhost:5173. task migrate/task studio сами ходят в БД на 127.0.0.1 (см. LOCAL_DATABASE_URL в Taskfile.yml). Локальный билд образа: task docker-build + task docker-up (использует docker-compose.yml со сборкой из исходников).

Превью дашборда в шапке README обновляются так (нужны запущенные task dev и креды владельца):

# один раз, если нет системного Chrome: npx playwright install chromium
CAPTURE_USER=admin CAPTURE_PASSWORD='…' task docs-screenshot

Скрипт пишет docs/screenshot-{dark,light}.webp (Retina → рамка → WebP).

Explore related projects