Files
DndGamePlayerLicenseServer/README.md
T
Ivan Fontosh f19b50b2ad feat(activate): allow retireDeviceId to free a device slot on migration
Supports client migration from per-user UUID to machine fingerprint without consuming an extra maxDevices slot.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-23 10:39:46 +08:00

77 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DndGamePlayerLicenseServer
Сервис лицензирования для **DNDGamePlayer**: активация по продуктовому ключу, выдача подписанного токена Ed25519, отзыв по `sub` (клиент опрашивает `GET /v1/status`, если задан `DND_LICENSE_STATUS_URL` в Electron main).
Репозиторий клиента: отдельный проект `dnd_player` — публичный ключ вшит в `app/shared/license/bundledPublicKey.ts` и должен соответствовать `LICENSE_PRIVATE_KEY_PEM` здесь.
## Переменные окружения
| Переменная | Описание |
|------------|----------|
| `LICENSE_PRIVATE_KEY_PEM` | Приватный ключ Ed25519 в PEM (PKCS#8), **обязательно** |
| `LICENSE_ADMIN_TOKEN` | Bearer-токен для `/v1/admin/*` (по умолчанию `change-me-admin`) |
| `DND_LICENSE_DATA_PATH` | Путь к `data.json` (по умолчанию `./data.json` в корне репозитория) |
| `PORT` | Порт (по умолчанию `3847`) |
## Подготовка
1. Скопируйте `data.example.json``data.json`.
2. Сгенерируйте пару Ed25519; **публичный** ключ (SPKI DER base64) вставьте в клиент в `bundledPublicKey.ts`.
```bash
node -e "const c=require('crypto');const kp=c.generateKeyPairSync('ed25519');console.log(kp.privateKey.export({type:'pkcs8',format:'pem'}));"
```
### Демо-ключ (только для разработки)
Публичный ключ по умолчанию в клиенте соответствует этому приватному PEM:
```
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIDNxA9U1VSG9zoOvcJ5uB+JUe25UD5m9UwMi6slXzW44
-----END PRIVATE KEY-----
```
## Запуск
На Windows перед запуском задайте переменную окружения `LICENSE_PRIVATE_KEY_PEM` (многострочное значение в кавычках или через `.env` и загрузчик по желанию).
```bash
npm start
```
## Продакшен
Публичный endpoint: **`https://license.ttrpgplayer.ru/`** (nginx → `127.0.0.1:3847` на VPS).
Проверка: `curl https://license.ttrpgplayer.ru/health``{"ok":true}`.
Код на сервере: `/var/www/license_mailib_ru`, PM2-процесс `dnd-license`.
## Тесты
Из корня репозитория:
```bash
npm test
```
Запускает модульные тесты Node (`node --test`, см. `package.json``test`).
## API
- `POST /v1/activate``{ "productKey": "...", "deviceId": "...", "retireDeviceId?": "..." }``{ token, sub }`. `retireDeviceId` (опционально) удаляет старый слот устройства при миграции (не занимает лишний `maxDevices`).
- `GET /v1/status?sub=...``{ revoked: boolean }`.
- `POST /v1/track/download``{ "platform": "windows"|"macos"|"linux" }` → учёт скачиваний в `data.json``downloadStats.byMonth`.
- `POST /v1/admin/revoke``Authorization: Bearer <LICENSE_ADMIN_TOKEN>`, тело `{ "key": "TTRPG-..." }` или `{ "sub": "..." }`.
- `POST /v1/admin/product-keys/delete` — hard delete ключа `{ "key": "..." }`.
- `POST /v1/admin/issue` — админская выдача (`sub`, `pid`, `iat`, `exp`, `did`).
- `GET /v1/admin/licenses` — список ключей (новые сверху), поля `createdAtSec`, `expiryMode`, `validDays`, …
- `GET /v1/admin/stats/downloads` — обзор `{ months, currentMonth }`; `?month=YYYY-MM` — статистика за месяц.
- `POST /v1/admin/product-keys` — создание ключа (`pid`, `maxDevices`, `validDays` или `expiresAtSec`, опционально `count` 1100).
- `GET /health``{ ok: true }`.
## Клиент
`DND_LICENSE_STATUS_URL=http://localhost:3847/` (база URL для сборки пути `v1/status`).