feat: billing — usage tracking per SQS operation in PostgreSQL

- New package app/billing: Init/RecordUsage/Close with auto-migrate
- Integration in actionHandler: record tenant_id, operation, msg_count, msg_bytes
- Helm chart: billing section in values.yaml, secret-billing.yaml, env vars in deployment
- Optional: billing disabled by default (BILLING_PG_HOST not set = no-op)
- Table: sqs_usage_records with index on (tenant_id, recorded_at)
This commit is contained in:
Naeel
2026-04-12 11:51:27 +03:00
parent d1af612b48
commit 1272388673
13 changed files with 448 additions and 4 deletions
+10 -2
View File
@@ -1,7 +1,7 @@
// app/admin/admin.go
// Admin API handlers for shared-sqs management
// Created: 2026-04-09
// Updated: 2026-04-12 09:28 MSK — demo UI token и изоляция UI API одним tenant-ом
// Updated: 2026-04-12 10:12 MSK — пометки о временном demo showcase режиме
package admin
import (
@@ -30,6 +30,9 @@ const (
uiTenantContextKey uiContextKey = "ui-tenant"
)
// ВАЖНО: значения ниже относятся только к временному demo/showcase режиму.
// Перед production rollout без публичного demo-режима этот блок должен быть удалён
// вместе с веткой authenticateUIDemoToken и связанными UI-подсказками.
const (
defaultUIDemoToken = "demo-ui-shared-sqs-ngcloud-2026"
defaultUIDemoTenantID = "t-demo-shared-sqs-ngcloud"
@@ -67,7 +70,8 @@ func NewHandler(store *tenant.TenantStore, adminToken string) *Handler {
return &Handler{store: store, adminToken: adminToken, nubesEndpoint: nubesEndpoint}
}
// uiDemoToken — возвращает публичный demo token для UI, если он не переопределён через env.
// uiDemoToken — возвращает публичный demo token для UI.
// Временный showcase-only путь: в production без demo user этот метод нужно удалить.
func (h *Handler) uiDemoToken() string {
if token := os.Getenv("SHARED_SQS_UI_DEMO_TOKEN"); token != "" {
return token
@@ -76,6 +80,7 @@ func (h *Handler) uiDemoToken() string {
}
// uiDemoTenantID — возвращает tenant ID, к которому привязан demo token.
// Существует только для showcase-режима, не для постоянной production auth-модели.
func (h *Handler) uiDemoTenantID() string {
if tenantID := os.Getenv("SHARED_SQS_UI_DEMO_TENANT_ID"); tenantID != "" {
return tenantID
@@ -84,6 +89,7 @@ func (h *Handler) uiDemoTenantID() string {
}
// uiDemoEmail — возвращает отображаемый email для demo UI session.
// Нужен только для публичного демо-логина и должен уйти вместе с demo path.
func (h *Handler) uiDemoEmail() string {
if email := os.Getenv("SHARED_SQS_UI_DEMO_EMAIL"); email != "" {
return email
@@ -92,6 +98,8 @@ func (h *Handler) uiDemoEmail() string {
}
// authenticateUIDemoToken — маппит публичный demo token на заранее сидированный demo tenant.
// Почему так: это быстрый showcase-вход для заказчика. Для production без demo user
// функция должна быть удалена, чтобы в коде не осталось публичного bypass-пути.
func (h *Handler) authenticateUIDemoToken(token string) (*tenant.Tenant, string, error) {
demoToken := h.uiDemoToken()
if demoToken == "" || subtle.ConstantTimeCompare([]byte(token), []byte(demoToken)) != 1 {