Схема на базата данни
Licensing Server използва PostgreSQL (Azure PostgreSQL в production, локален Docker container в development). Миграциите се управляват чрез golang-migrate и се прилагат автоматично при стартиране на сървъра.
ER диаграма
Таблици в детайли
users
Основната таблица с потребители. Поддържа както OAuth, така и email/password автентикация.
| Колона | Тип | Описание |
|---|---|---|
id | UUID PK | Уникален идентификатор, генериран чрез gen_random_uuid() |
email | VARCHAR(255) | Email адрес на потребителя |
name | VARCHAR(255) | Име (nullable, от OAuth provider или регистрация) |
avatar_url | VARCHAR(500) | URL към аватар (nullable, от OAuth provider) |
provider | VARCHAR(20) | Тип автентикация: google, azure, github, или email |
provider_id | VARCHAR(255) | Уникален ID от provider-а (за email users = email адреса) |
plan | VARCHAR(20) | Текущ план: free или pro |
stripe_customer_id | VARCHAR(255) | Stripe Customer ID (nullable, unique) |
stripe_subscription_id | VARCHAR(255) | Stripe Subscription ID (nullable, unique) |
created_at | TIMESTAMPTZ | Дата на създаване |
updated_at | TIMESTAMPTZ | Последна промяна |
trial_started_at | TIMESTAMPTZ | Начало на trial период (nullable) |
total_dictation_seconds | FLOAT | Кумулативно време на диктовка в секунди |
total_dictation_count | INTEGER | Общ брой диктовки |
password_hash | VARCHAR(255) | bcrypt хеш на паролата (NULL за OAuth users) |
email_verified | BOOLEAN | Дали email-ът е потвърден (OAuth users = TRUE автоматично) |
Constraints:
uq_provider_identity UNIQUE (provider, provider_id)-- предотвратява дублиране на потребител от същия provider
Indexes:
idx_users_stripe_customer ON users (stripe_customer_id)-- за бързо търсене при Stripe webhooks
license_keys
Лицензни ключове, които могат да се активират от потребители.
| Колона | Тип | Описание |
|---|---|---|
id | UUID PK | Уникален идентификатор |
key | VARCHAR(32) | Лицензен ключ (unique) |
user_id | UUID FK | Потребител, който е активирал ключа |
device_id | VARCHAR(64) | Устройство, на което е активиран |
activated_at | TIMESTAMPTZ | Дата на активиране |
is_active | BOOLEAN | Дали ключът е активен |
created_at | TIMESTAMPTZ | Дата на създаване |
usage_records
Записи за използване на диктовка. Поддържа както автентикирани, така и анонимни (само device_id) потребители.
| Колона | Тип | Описание |
|---|---|---|
id | UUID PK | Уникален идентификатор |
user_id | UUID | Потребител (NULL за анонимни записи) |
device_id | VARCHAR(128) | Идентификатор на устройството |
duration_seconds | FLOAT | Продължителност на диктовката в секунди |
recorded_at | TIMESTAMPTZ | Дата на записване |
Indexes:
idx_usage_user_id-- за заявки по потребителidx_usage_device_id-- за заявки по устройство (анонимни)idx_usage_recorded_at-- за сортиране по време
stats_aggregate
Singleton таблица (id=1) с агрегирана статистика. Обновява се при всеки usage record чрез INSERT ... ON CONFLICT DO UPDATE (upsert).
| Колона | Тип | Описание |
|---|---|---|
id | INTEGER PK | Винаги 1 (singleton) |
total_dictation_seconds | FLOAT | Общо време на диктовка за всички потребители |
total_dictation_count | INTEGER | Общ брой диктовки |
updated_at | TIMESTAMPTZ | Последна промяна |
device_accounts
Проследява кои потребителски акаунти са влизали от дадено устройство. Използва се за ограничаване на безплатни акаунти (anti-abuse).
| Колона | Тип | Описание |
|---|---|---|
id | BIGSERIAL PK | Auto increment ID |
device_id | TEXT | Идентификатор на устройството |
user_id | UUID FK | Потребител, който е влязъл |
created_at | TIMESTAMPTZ | Дата на първо влизане от това устройство |
Constraints:
uq_device_user UNIQUE (device_id, user_id)-- всяка комбинация се записва само веднъж
Indexes:
idx_device_accounts_device_id-- за бързо търсене по устройство
email_tokens
Токени за email verification и password reset. Само SHA-256 хешът на токена се съхранява в базата -- суровият токен се изпраща само в email-а.
| Колона | Тип | Описание |
|---|---|---|
id | UUID PK | Уникален идентификатор |
user_id | UUID FK | Потребител |
token_hash | VARCHAR(64) | hex(sha256(rawToken)) -- unique |
kind | VARCHAR(20) | Тип: verify или reset |
expires_at | TIMESTAMPTZ | Дата на изтичане |
used_at | TIMESTAMPTZ | Дата на използване (NULL до консумиране) |
created_at | TIMESTAMPTZ | Дата на създаване |
Indexes:
idx_email_tokens_user_kind ON (user_id, kind)-- за инвалидиране на стари токениidx_email_tokens_expires ON (expires_at)-- за евентуално почистване
Миграции
Миграциите се намират в licensing-go/internal/database/migrations/ и се прилагат автоматично при стартиране на сървъра чрез golang-migrate.
| Миграция | Описание |
|---|---|
000001_initial | Създава users, license_keys, usage_records, stats_aggregate |
000002_device_accounts | Добавя device_accounts таблица за anti-abuse |
000003_email_auth | Добавя password_hash, email_verified към users; създава email_tokens |
Бележки за Azure PostgreSQL
pgcryptoразширението не е достъпно в Azure PostgreSQL -- вместо него се използва вградената функцияgen_random_uuid()(PG 13+).- В production се изисква
sslmode=requireилиsslmode=verify-fullвDATABASE_URL.