SHA256
Compare commits
| Author | SHA256 | Date | |
|---|---|---|---|
|
|
cf33f9a3bd | ||
|
|
d2f65b169c | ||
|
|
fa1f7358b7 | ||
|
|
8208bb0b9d | ||
|
|
daa516babe | ||
|
|
6c2a9836eb | ||
|
|
bd3e6a56ca | ||
|
|
ea2ee1711c | ||
|
|
2890cdd457 | ||
|
|
e4dfb43b5e | ||
|
|
3926d561c0 |
+3
-2
@@ -13,6 +13,7 @@ build/
|
||||
.kotlin
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
@@ -102,8 +103,8 @@ ESP32/**/*.d
|
||||
ESP32/**/*.a
|
||||
|
||||
# Полные серверные бэкапы (тяжёлые архивы, не коммитим)
|
||||
server-backup/archive/**
|
||||
!server-backup/archive/.gitkeep
|
||||
deploy/backup/archive/**
|
||||
!deploy/backup/archive/.gitkeep
|
||||
|
||||
# Локальная дев-обвязка Claude (дев-сервер shine-UI, сессии, планы) — не коммитим
|
||||
.claude/
|
||||
|
||||
Generated
-8
@@ -1,8 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
Generated
-1
@@ -1 +0,0 @@
|
||||
shine-server-server
|
||||
Generated
-10
@@ -1,10 +0,0 @@
|
||||
<component name="ArtifactManager">
|
||||
<artifact type="jar" build-on-make="true" name="server:jar">
|
||||
<output-path>$PROJECT_DIR$/out/artifacts/server_jar</output-path>
|
||||
<root id="archive" name="server.jar">
|
||||
<element id="directory" name="META-INF">
|
||||
<element id="file-copy" path="$PROJECT_DIR$/META-INF/MANIFEST.MF" />
|
||||
</element>
|
||||
</root>
|
||||
</artifact>
|
||||
</component>
|
||||
Generated
-25
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleHome" value="" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/shine-server-blockchain" />
|
||||
<option value="$PROJECT_DIR$/shine-server-config" />
|
||||
<option value="$PROJECT_DIR$/shine-server-crypto" />
|
||||
<option value="$PROJECT_DIR$/shine-server-db" />
|
||||
<option value="$PROJECT_DIR$/shine-server-geo" />
|
||||
<option value="$PROJECT_DIR$/shine-server-log" />
|
||||
<option value="$PROJECT_DIR$/shine-server-net-protocol" />
|
||||
<option value="$PROJECT_DIR$/shine-server-net-server" />
|
||||
</set>
|
||||
</option>
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
Generated
-10
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="FrameworkDetectionExcludesConfiguration">
|
||||
<file type="web" url="file://$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17 (2)" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
-6
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -36,45 +36,45 @@
|
||||
- Модуль логически связан с SHiNE, но не должен автоматически подключаться к сборке или деплою основного сервера без отдельного решения.
|
||||
- В Solana-модуле действуют локальные инструкции `shine-solana/shine/AGENTS.md`; при изменениях внутри модуля сначала читать их.
|
||||
- В git добавлять исходники, lock-файлы, настройки проекта и документацию Solana-модуля, но не добавлять локальные ключи, `.git`, `.idea`, `.gradle`, `target`, `node_modules`, `test-ledger`, логи, временные run-отчёты и `.env`-конфиги.
|
||||
- Для Solana deploy/push использовать правила из локального `shine-solana/shine/AGENTS.md`; не смешивать deploy Solana-модуля с `deployServer`/`deployUI` основного проекта.
|
||||
- Для Solana deploy/push использовать правила из локального `shine-solana/shine/AGENTS.md`; не смешивать deploy Solana-модуля со скриптами основного server/UI deploy из `deploy/scripts/`.
|
||||
- Для регистрации пользователей в Solana (программа `shine_users`) единая актуальная инструкция по деплою/инициализации, адресам программ, и куда их прописывать в UI/сервере находится в:
|
||||
- `Dev_Docs/Инициализация_Solana_регистрации/README.md`
|
||||
- `docs/Инициализация_Solana_регистрации/README.md`
|
||||
- Этот файл считать основной справкой (single source of truth) по деплою и первичной инициализации Solana-регистрации в текущем проекте.
|
||||
- Актуальная архитектурная справка по устройству Solana-программ, PDA-счетам, ролям DAO и движению средств находится в:
|
||||
- `Dev_Docs/Solana_Architecture/README.md`
|
||||
- `docs/Solana_Architecture/README.md`
|
||||
- Документ формата пользовательской PDA-записи `shine_users` находится в:
|
||||
- `shine-solana/shine/doc/formats/shine-user-pda-format-v.1.0.md`
|
||||
|
||||
## Документация блокчейна
|
||||
- Актуальная документация по форматам блокчейна находится в `Dev_Docs/Blockchain/README.md`.
|
||||
- Актуальная документация по форматам блокчейна находится в `docs/Blockchain/README.md`.
|
||||
- Это точка входа (оглавление), рядом расположены детальные файлы по форматам, типам каналов и командным сообщениям.
|
||||
- При любом изменении кода, связанного с блокчейном (формат блока, типы каналов, правила чтения/записи, команды), обязательно обновлять соответствующие документы в `Dev_Docs/Blockchain/`.
|
||||
- Дополнительно обязательно вести `Dev_Docs/Blockchain/CHANGELOG.md`: дописывать изменения построчно с указанием даты/времени и хэша коммита, после которого внесено изменение.
|
||||
- При любом изменении кода, связанного с блокчейном (формат блока, типы каналов, правила чтения/записи, команды), обязательно обновлять соответствующие документы в `docs/Blockchain/`.
|
||||
- Дополнительно обязательно вести `docs/Blockchain/CHANGELOG.md`: дописывать изменения построчно с указанием даты/времени и хэша коммита, после которого внесено изменение.
|
||||
- Перед любым изменением формата блокчейна обязательно заранее предупреждать пользователя, что формат будет изменён.
|
||||
- Изменять формат блокчейна можно только после явного подтверждения пользователя (без подтверждения формат не менять).
|
||||
- Добавление любых данных в блокчейн выполнять только через операцию `AddBlock`.
|
||||
- Перед каждым `AddBlock` обязательно проверять/актуализировать текущее состояние вершины блокчейна (`last global number/hash`) и использовать его при формировании блока.
|
||||
|
||||
## Документация личных сообщений (DM)
|
||||
- Актуальная документация по логике личных сообщений находится в `Dev_Docs/Personal_Messages/Протокол_DM_v1.md`.
|
||||
- Точный байтовый формат DM находится в `Dev_Docs/Personal_Messages/Формат_DM_v1.md`.
|
||||
- Актуальная документация по логике личных сообщений находится в `docs/Personal_Messages/Протокол_DM_v1.md`.
|
||||
- Точный байтовый формат DM находится в `docs/Personal_Messages/Формат_DM_v1.md`.
|
||||
- При любом изменении кода, связанного с личными сообщениями (формат подписанного DM-блока, типы DM-сообщений, правила доставки/ACK/read-receipt, роутинг по сессиям, UI-логика чатов), обязательно обновлять оба документа:
|
||||
- `Dev_Docs/Personal_Messages/Протокол_DM_v1.md`
|
||||
- `Dev_Docs/Personal_Messages/Формат_DM_v1.md`
|
||||
- `docs/Personal_Messages/Протокол_DM_v1.md`
|
||||
- `docs/Personal_Messages/Формат_DM_v1.md`
|
||||
- Логика личных сообщений в коде должна всегда соответствовать этим документам.
|
||||
- Документ по личным сообщениям обязан поддерживаться в актуальном состоянии.
|
||||
|
||||
## Документация API сервера
|
||||
- Актуальная документация по публичному JSON/WebSocket API сервера находится в `Dev_Docs/API/`.
|
||||
- При любом изменении серверного API/эндпоинтов/операций `op` обязательно обновлять соответствующие документы в `Dev_Docs/API/`.
|
||||
- Актуальная документация по публичному JSON/WebSocket API сервера находится в `docs/API/`.
|
||||
- При любом изменении серверного API/эндпоинтов/операций `op` обязательно обновлять соответствующие документы в `docs/API/`.
|
||||
- Перед изменением самого серверного API обязательно явно предупредить пользователя, какие операции, поля запросов/ответов или коды ошибок будут изменены, и запросить отдельное подтверждение.
|
||||
- Без явного подтверждения пользователя формат серверного API не менять; допускается только приведение документации в соответствие уже существующему коду.
|
||||
- Если добавляется новая операция `op`, нужно обновить общий список операций в `Dev_Docs/API/09_Operations_Index.md` или создать его, если файла ещё нет.
|
||||
- Если добавляется новая операция `op`, нужно обновить общий список операций в `docs/API/09_Operations_Index.md` или создать его, если файла ещё нет.
|
||||
|
||||
## Документация Figma
|
||||
- Актуальная документация по переносу экранов SHiNE в Figma и обратному переносу из Figma в код находится в `Dev_Docs/Figma/`.
|
||||
- Точка входа: `Dev_Docs/Figma/README.md`.
|
||||
- Подробный рабочий регламент: `Dev_Docs/Figma/TRANSFER_UI_SCREENS.md`.
|
||||
- Актуальная документация по переносу экранов SHiNE в Figma и обратному переносу из Figma в код находится в `docs/Figma/`.
|
||||
- Точка входа: `docs/Figma/README.md`.
|
||||
- Подробный рабочий регламент: `docs/Figma/TRANSFER_UI_SCREENS.md`.
|
||||
- Для экранов регистрации, входа и других чувствительных UI-flow по умолчанию переносить экраны в Figma по одному, а не пачкой, если пользователь отдельно не подтвердил иной способ.
|
||||
|
||||
## Версионирование
|
||||
@@ -86,18 +86,17 @@
|
||||
- Обычные коммиты делать стандартным `git commit`; переменная `$GITEA_TOKEN` для коммитов не нужна и не используется.
|
||||
|
||||
## Deploy
|
||||
- Все документы и заметки по деплою хранить в папке `Deploy/`.
|
||||
- Production-хост SHiNE: `player@shineup.me` (`178.208.64.62`).
|
||||
- Второй production-хост SHiNE: `player@193.8.215.70` (`server2.shineup.me`).
|
||||
- Базовый путь на сервере для SHiNE: `/home/player` (проекты SHiNE размещать в `/home/player/SHiNE/...`).
|
||||
- Все документы, инструкции, backup-правила и скрипты деплоя хранить в папке `deploy/`.
|
||||
- Подробные правила для агента по деплою находятся в `deploy/AGENTS.md`; перед любым deploy читать этот файл.
|
||||
- Production-серверы SHiNE: `shineup.me` и `server2.shineup.me`.
|
||||
- Тестовые/devnet серверы SHiNE: `t1.shineup.me`, `t2.shineup.me`, `t3.shineup.me`, `t4.shineup.me`.
|
||||
- В deploy-документах и скриптах использовать домены, а не IP.
|
||||
- По возможности все справки, комментарии и примечания в конфигах/документах писать на русском языке.
|
||||
- Для операций `git push` при необходимости использовать токен из переменной окружения `$GITEA_TOKEN`.
|
||||
- Любые изменения и любой деплой на production `shineup.me` выполнять только после отдельного явного подтверждения пользователя.
|
||||
- Если пользователь пишет просто `задеплой` без уточнения production/test, по умолчанию деплоить на `server2.shineup.me`.
|
||||
- Default server deploy: `./gradlew deployServer` или `./gradlew deployServerTest2`.
|
||||
- Default UI deploy: `./gradlew deployUI` или `./gradlew deployUITest2`.
|
||||
- Production server deploy: `./gradlew deployServerProduction`.
|
||||
- Production UI deploy: `./gradlew deployUIProduction`.
|
||||
- Любые изменения и любой деплой на production (`shineup.me` и `server2.shineup.me`) выполнять только после отдельного явного подтверждения пользователя.
|
||||
- Перед production deploy обязательно проверить/обновить бэкап в `deploy/backup/archive/`.
|
||||
- Если пользователь пишет просто `задеплой` без уточнения production/test, уточнить целевой контур; не выбирать production автоматически.
|
||||
- Deploy выполнять shell-скриптами из `deploy/scripts/`; Gradle deploy-задачи не использовать.
|
||||
- Для локального запуска использовать `./gradlew startLocal` (или `startLocalWithBuild`).
|
||||
- Сначала предлагать локальную проверку, а деплой на сервер выполнять по запросу пользователя.
|
||||
- Для временной бесплатной загрузки аватаров в Arweave секретный JWK нельзя хранить в git и нельзя прописывать в репозиторный `application.properties`.
|
||||
@@ -123,26 +122,13 @@
|
||||
- `unknown_error`
|
||||
- В этих записях искать поля `reason`, `failureStage`, `pcConnectionState`, `pcIceConnectionState`, `routeLabel`, `configuredTurnHosts*`, `reachableTurnHosts*`.
|
||||
|
||||
## Недопроверенные фичи (обязательно)
|
||||
- Папка для учёта недопроверенных фич: `Dev_Docs/Pending_Features/`.
|
||||
- По каждой новой доработке, которая требует ручной проверки, добавлять отдельный markdown-файл в `Dev_Docs/Pending_Features/`.
|
||||
- Рекомендуемый формат имени файла: `YYYY-MM-DD_HHMM_<short-feature-name>.md`.
|
||||
- Имена новых файлов и краткие описания фич по возможности писать на русском языке.
|
||||
- Внутри файла обязательно указывать:
|
||||
- краткое описание фичи;
|
||||
- что именно проверять;
|
||||
- ожидаемый результат;
|
||||
- статус (например: `pending`, `in_progress`, `done`).
|
||||
- После подтверждения, что фича проверена и работает корректно, соответствующий файл удалять.
|
||||
- В `Dev_Docs/Pending_Features/README.md` вести краткий регламент и поддерживать актуальность.
|
||||
|
||||
## Будущие фичи / TODO
|
||||
- Папка для задач, сознательно отложенных на будущее: `TODO/`.
|
||||
- Точка входа по планам: `TODO/README.md`.
|
||||
- Внутри планы разделены по горизонтам: `near/`, `medium/`, `far/` и тематическим подпапкам.
|
||||
- Если пользователь спрашивает, какие есть планы или что можно продолжить, сначала читать `TODO/README.md`, затем при необходимости конкретные файлы из подпапок.
|
||||
- Файлы из этой папки не считать активными задачами и не начинать реализацию без явной просьбы пользователя.
|
||||
- Старую папку `Dev_Docs/Future_Features/` считать выведенной из использования и больше не использовать для новых записей.
|
||||
- Старую папку `docs/Future_Features/` считать выведенной из использования и больше не использовать для новых записей.
|
||||
- Если часть кода временно отключена или закомментирована, либо удалена как временная заглушка, в TODO-файле подробно описывать:
|
||||
- какие файлы и участки отключены;
|
||||
- что осталось в коде как заготовка;
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
# Production-серверы SHiNE
|
||||
|
||||
## Короткий ответ
|
||||
|
||||
По текущим данным репозитория у SHiNE описаны **два production-контура**:
|
||||
|
||||
- `player@shineup.me`
|
||||
- домен `shineup.me`
|
||||
- IP `178.208.64.62`
|
||||
|
||||
и
|
||||
|
||||
- `player@193.8.215.70`
|
||||
- домен `server2.shineup.me`
|
||||
- IP `193.8.215.70`
|
||||
|
||||
## 1. Основной production-хост
|
||||
|
||||
- SSH: `player@shineup.me`
|
||||
- домен: `shineup.me`
|
||||
- IP: `178.208.64.62`
|
||||
- пользователь: `player`
|
||||
- базовый путь: `/home/player`
|
||||
|
||||
Основные каталоги:
|
||||
|
||||
- проект SHiNE: `/home/player/SHiNE`
|
||||
- серверный jar: `/home/player/SHiNE/shine-server/shine-server.jar`
|
||||
- UI: `/home/player/SHiNE/shine-ui`
|
||||
- данные: `/home/player/SHiNE/shine-server/data/`
|
||||
- логи: `/home/player/SHiNE/shine-server/logs/app.log`
|
||||
|
||||
Сервисы:
|
||||
|
||||
- `shine-server.service`
|
||||
- `caddy.service`
|
||||
|
||||
Caddy:
|
||||
|
||||
- активный конфиг: `/etc/caddy/Caddyfile`
|
||||
- UI root: `/home/player/SHiNE/shine-ui`
|
||||
- `/ws` проксируется на `127.0.0.1:7070`
|
||||
|
||||
Deploy:
|
||||
|
||||
- `./gradlew deployServerProduction`
|
||||
- `./gradlew deployUIProduction`
|
||||
|
||||
Правило:
|
||||
|
||||
- любые изменения на `shineup.me` делать только после отдельного подтверждения пользователя.
|
||||
|
||||
## 2. Второй production-сервер
|
||||
|
||||
- SSH: `player@193.8.215.70`
|
||||
- домен: `server2.shineup.me`
|
||||
- IP: `193.8.215.70`
|
||||
- пользователь: `player`
|
||||
- базовый путь: `/home/player`
|
||||
|
||||
Роль:
|
||||
|
||||
- второй production-контур SHiNE;
|
||||
- использовать как production-сервер, несмотря на исторические имена deploy-задач
|
||||
`deployServerTest2` / `deployUITest2`.
|
||||
|
||||
Основные каталоги:
|
||||
|
||||
- проект SHiNE: `/home/player/SHiNE`
|
||||
- серверный jar: `/home/player/SHiNE/shine-server/shine-server.jar`
|
||||
- UI: `/home/player/SHiNE/shine-ui`
|
||||
- данные: `/home/player/SHiNE/shine-server/data/`
|
||||
- логи: `/home/player/SHiNE/shine-server/logs/app.log`
|
||||
|
||||
## 3. Связанные публичные production-публикации на том же хосте
|
||||
|
||||
На этом же production-хосте есть отдельная публикация для `shine_payments`:
|
||||
|
||||
- каталог: `/home/player/sites/test-solana-tickets.shineup.me`
|
||||
- домены:
|
||||
- `https://test-solana-tickets.shineup.me`
|
||||
- `https://test-solana-tickets.shiningpeople.ru`
|
||||
|
||||
Это не второй production-хост SHiNE, а отдельный сайт на том же сервере.
|
||||
|
||||
## 4. Какие серверы не считать production
|
||||
|
||||
Не production:
|
||||
|
||||
- `t1.shineup.me`
|
||||
- `t2.shineup.me`
|
||||
- `t3.shineup.me`
|
||||
- `t4.shineup.me`
|
||||
@@ -1,35 +0,0 @@
|
||||
# Deploy
|
||||
|
||||
Подробности о том, где что задеплоено в SHiNE, нужно искать в папке `Deploy/`.
|
||||
|
||||
Эта папка служит краткой картой окружений:
|
||||
|
||||
- [TEST_SERVERS.md](/home/ai/work/SHiNE/SHiNE-server-sha256/Deploy/TEST_SERVERS.md) — тестовые стенды;
|
||||
- [PRODUCTION_SERVERS.md](/home/ai/work/SHiNE/SHiNE-server-sha256/Deploy/PRODUCTION_SERVERS.md) — production-контур и связанные публичные публикации.
|
||||
|
||||
Ниже краткая сводка.
|
||||
|
||||
## Основные публичные контуры
|
||||
|
||||
- Production SHiNE:
|
||||
- `player@shineup.me`
|
||||
- домен `shineup.me`
|
||||
- IP `178.208.64.62`
|
||||
- Второй production SHiNE:
|
||||
- `player@193.8.215.70`
|
||||
- домен `server2.shineup.me`
|
||||
- IP `193.8.215.70`
|
||||
## Отдельный quad-devnet стенд
|
||||
|
||||
На отдельном VPS `178.208.90.249` подняты 4 независимых test/devnet-инстанса:
|
||||
|
||||
- `t1.shineup.me`
|
||||
- `t2.shineup.me`
|
||||
- `t3.shineup.me`
|
||||
- `t4.shineup.me`
|
||||
|
||||
## Важно
|
||||
|
||||
- Production-контура SHiNE сейчас два: `shineup.me` и `server2.shineup.me`.
|
||||
- `t1..t4.shineup.me` — это отдельные тестовые/devnet-контуры, не production.
|
||||
- Любые изменения на `shineup.me` делать только после отдельного подтверждения пользователя.
|
||||
@@ -1,124 +0,0 @@
|
||||
# Тестовые серверы SHiNE
|
||||
|
||||
Этот файл описывает тестовые стенды, которые сейчас фигурируют в проекте.
|
||||
|
||||
## 1. Исторический `test2`, теперь второй production-сервер
|
||||
|
||||
- SSH: `player@193.8.215.70`
|
||||
- Домен: `server2.shineup.me`
|
||||
- IP: `193.8.215.70`
|
||||
- Назначение: второй production-контур SHiNE
|
||||
|
||||
Структура:
|
||||
|
||||
- каталог SHiNE: `/home/player/SHiNE`
|
||||
- сервер: `/home/player/SHiNE/shine-server/shine-server.jar`
|
||||
- UI: `/home/player/SHiNE/shine-ui`
|
||||
- данные: `/home/player/SHiNE/shine-server/data/`
|
||||
- логи: `/home/player/SHiNE/shine-server/logs/app.log`
|
||||
|
||||
Сервисы:
|
||||
|
||||
- `shine-server.service`
|
||||
- `caddy.service`
|
||||
|
||||
Deploy:
|
||||
|
||||
- `./gradlew deployServer`
|
||||
- `./gradlew deployServerTest2`
|
||||
- `./gradlew deployUI`
|
||||
- `./gradlew deployUITest2`
|
||||
|
||||
Примечания:
|
||||
|
||||
- этот хост больше не считать test-контуром;
|
||||
- исторические имена deploy-задач `deployServerTest2` / `deployUITest2` сохранены, но сам хост считать production;
|
||||
- задача `deployUITest2` по умолчанию выкладывает UI на `server2.shineup.me`, а не на `t2.shineup.me`;
|
||||
- при описании окружений перечислять его как второй production-сервер.
|
||||
|
||||
## 2. Отдельный quad-devnet стенд `t1..t4`
|
||||
|
||||
- VPS: `178.208.90.249`
|
||||
- пользователь: `player`
|
||||
- назначение: 4 независимых SHiNE-инстанса на Solana `devnet`
|
||||
|
||||
Домены и логины:
|
||||
|
||||
- `server_t1` -> `https://t1.shineup.me`
|
||||
- `server_t2` -> `https://t2.shineup.me`
|
||||
- `server_t3` -> `https://t3.shineup.me`
|
||||
- `server_t4` -> `https://t4.shineup.me`
|
||||
|
||||
Каталоги:
|
||||
|
||||
- `/home/player/t1/server`
|
||||
- `/home/player/t1/UI`
|
||||
- `/home/player/t2/server`
|
||||
- `/home/player/t2/UI`
|
||||
- `/home/player/t3/server`
|
||||
- `/home/player/t3/UI`
|
||||
- `/home/player/t4/server`
|
||||
- `/home/player/t4/UI`
|
||||
|
||||
Подробная памятка на самом VPS:
|
||||
|
||||
- `/home/player/Agents.md`
|
||||
|
||||
Порты и systemd:
|
||||
|
||||
- `t1` -> `7101` -> `shine-t1.service`
|
||||
- `t2` -> `7102` -> `shine-t2.service`
|
||||
- `t3` -> `7103` -> `shine-t3.service`
|
||||
- `t4` -> `7104` -> `shine-t4.service`
|
||||
|
||||
Что важно по конфигу каждого инстанса:
|
||||
|
||||
- отдельный `/home/player/tX/server/application.properties`
|
||||
- `server.port=710X`
|
||||
- `server.SHiNE.login=server_tX`
|
||||
- `db.path=data/shine.sqlite`
|
||||
- `solana.cluster=devnet`
|
||||
- `solana.rpcUrl=https://api.devnet.solana.com`
|
||||
- `server.ui.indexPath=/home/player/tX/UI/index.html`
|
||||
- `server.info.url=https://tX.shineup.me`
|
||||
|
||||
UI каждого инстанса:
|
||||
|
||||
- живёт в отдельной копии `shine-UI`;
|
||||
- использует свой `js/deploy-config.js`;
|
||||
- по умолчанию смотрит именно на свой `tX.shineup.me`.
|
||||
|
||||
Caddy на стенде:
|
||||
|
||||
- конфиг: `/etc/caddy/Caddyfile`
|
||||
- статика: `/home/player/tX/UI`
|
||||
- `/ws` проксируется на `127.0.0.1:710X`
|
||||
|
||||
Operational-нюанс:
|
||||
|
||||
- при одновременных рестартах возможны `HTTP 429` от `api.devnet.solana.com`;
|
||||
- поэтому сервисы `shine-t1..shine-t4` лучше перезапускать по одному, с паузой.
|
||||
|
||||
## 3. Что проверять первым делом
|
||||
|
||||
Для любого test-контура полезны такие быстрые проверки:
|
||||
|
||||
```bash
|
||||
curl -I https://server2.shineup.me
|
||||
curl -I https://t1.shineup.me
|
||||
curl -I https://t2.shineup.me
|
||||
curl -I https://t3.shineup.me
|
||||
curl -I https://t4.shineup.me
|
||||
```
|
||||
|
||||
Для quad-devnet VPS:
|
||||
|
||||
```bash
|
||||
sudo systemctl --no-pager --full status caddy shine-t1 shine-t2 shine-t3 shine-t4
|
||||
```
|
||||
|
||||
Для второго production-контура:
|
||||
|
||||
```bash
|
||||
sudo systemctl --no-pager --full status shine-server caddy
|
||||
```
|
||||
@@ -11,7 +11,7 @@
|
||||
- Solana/Anchor-модуль `shine-solana/shine/`;
|
||||
- Telegram-агент-кодер `SHiNE-agent-bot-coder/`;
|
||||
- TURN-сервер;
|
||||
- документация `Dev_Docs/`;
|
||||
- документация `docs/`;
|
||||
- отдельные рабочие папки игроков `Players/`.
|
||||
|
||||
Без явных инструкций агент может путать эти зоны: например, смешать деплой Solana с деплоем сервера, изменить код от имени игрока, не обновить документацию API/DM/блокчейна или неправильно трактовать файл инструкций.
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
- `far/` - дальнее будущее без понятного срока.
|
||||
- Если пользователь спрашивает, какие есть планы или что можно продолжить, нужно смотреть эти три папки и отвечать кратким списком по горизонтам.
|
||||
- Файлы из `TODO/` не начинать реализовывать без явной команды пользователя.
|
||||
- После реализации фичи, требующей ручной проверки, нужно добавить отдельный файл в `Dev_Docs/Pending_Features/`.
|
||||
- После реализации фичи, требующей ручной проверки, нужно добавить отдельный файл в `docs/Pending_Features/`.
|
||||
|
||||
## Центр задач и предложений
|
||||
- Сервис хранит простые задачи и предложения в `data/task_center/items.json`.
|
||||
|
||||
+7
-17
@@ -45,30 +45,20 @@ shine-UI/server-ui.html
|
||||
- `shine_users`: `SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6`
|
||||
- `shine_payments`: `SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v`
|
||||
|
||||
Подробнее: `Dev_Docs/Инициализация_Solana_регистрации/README.md`
|
||||
Подробнее: `docs/Инициализация_Solana_регистрации/README.md`
|
||||
|
||||
## Синхронизация с партнёрскими серверами
|
||||
|
||||
Сервер должен синхронизировать блоки блокчейна и DM с серверами-партнёрами из `sync_servers`.
|
||||
Детали: `Dev_Docs/Blockchain/sync-between-servers.md`
|
||||
Детали: `docs/Blockchain/sync-between-servers.md`
|
||||
|
||||
## Деплой
|
||||
|
||||
```
|
||||
./gradlew deployServer
|
||||
./gradlew deployUI
|
||||
```
|
||||
|
||||
Default deploy по умолчанию идёт на `server2.shineup.me` (`player@193.8.215.70`).
|
||||
|
||||
Production deploy:
|
||||
|
||||
```
|
||||
./gradlew deployServerProduction
|
||||
./gradlew deployUIProduction
|
||||
```
|
||||
|
||||
Любые изменения на `shineup.me` делать только после отдельного явного подтверждения пользователя.
|
||||
- Основные инструкции по деплою находятся в `../deploy/AGENTS.md`.
|
||||
- Deploy выполнять shell-скриптами из `../deploy/scripts/`.
|
||||
- Gradle deploy-задачи не использовать: Gradle остаётся для сборки и локального запуска.
|
||||
- Любые изменения на production (`shineup.me`, `server2.shineup.me`) делать только после отдельного явного подтверждения пользователя.
|
||||
- Перед production deploy обязательно обновить/проверить backup в `deploy/backup/archive/`.
|
||||
|
||||
Логи на проде:
|
||||
- `/home/player/SHiNE/shine-server/logs/app.log`
|
||||
|
||||
+1
-1
@@ -268,7 +268,7 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
}
|
||||
|
||||
// Репосты временно отключены до будущей реализации.
|
||||
// Точка возврата: Dev_Docs/Future_Features/2026-05-24_1140_репосты_в_каналах_и_тредах.md
|
||||
// Точка возврата: docs/Future_Features/2026-05-24_1140_репосты_в_каналах_и_тредах.md
|
||||
if ((block.type & 0xFFFF) == 1
|
||||
&& (block.subType & 0xFFFF) == (MsgSubType.TEXT_REPOST & 0xFFFF)) {
|
||||
log.warn("AddBlock: repost_disabled (login={}, blockchainName={}, blockNumber={})",
|
||||
|
||||
@@ -44,7 +44,7 @@ webpush.vapid.subject=mailto:admin@shine.local
|
||||
# Тогда сервер будет выдавать временный username/password (TTL).
|
||||
# ------------------------------------------------------------
|
||||
call.ice.stun.urls=stun:stun.l.google.com:19302
|
||||
call.ice.turn.urls=turn:185.229.109.118:3478?transport=udp,turn:185.229.109.118:3478?transport=tcp
|
||||
call.ice.turn.urls=turn:turn1.shineup.me:3478?transport=udp,turn:turn1.shineup.me:3478?transport=tcp
|
||||
call.ice.turn.ttlSec=600
|
||||
call.ice.turn.userPrefix=shine
|
||||
call.ice.turn.sharedSecret=
|
||||
@@ -58,12 +58,24 @@ call.ice.turn.password=
|
||||
# Каждый блок описывает один TURN-узел. Новые узлы добавляются по индексу.
|
||||
# Приоритет авторизации на узел: sharedSecret -> статические username/password.
|
||||
# ------------------------------------------------------------
|
||||
call.ice.turn.servers.1.id=shineup-main-185
|
||||
call.ice.turn.servers.1.urls=turn:185.229.109.118:3478?transport=udp,turn:185.229.109.118:3478?transport=tcp
|
||||
call.ice.turn.servers.1.sharedSecret=def6d444734d380d2f67a9d345b1debf985eaba0973c343e392c060d97c30106
|
||||
call.ice.turn.servers.1.id=turn1
|
||||
call.ice.turn.servers.1.urls=turn:turn1.shineup.me:3478?transport=udp,turn:turn1.shineup.me:3478?transport=tcp
|
||||
call.ice.turn.servers.1.sharedSecret=
|
||||
call.ice.turn.servers.1.username=
|
||||
call.ice.turn.servers.1.password=
|
||||
|
||||
call.ice.turn.servers.2.id=turn2
|
||||
call.ice.turn.servers.2.urls=turn:turn2.shineup.me:3478?transport=udp,turn:turn2.shineup.me:3478?transport=tcp
|
||||
call.ice.turn.servers.2.sharedSecret=
|
||||
call.ice.turn.servers.2.username=
|
||||
call.ice.turn.servers.2.password=
|
||||
|
||||
call.ice.turn.servers.3.id=turn3
|
||||
call.ice.turn.servers.3.urls=turn:turn3.shineup.me:3478?transport=udp,turn:turn3.shineup.me:3478?transport=tcp
|
||||
call.ice.turn.servers.3.sharedSecret=
|
||||
call.ice.turn.servers.3.username=
|
||||
call.ice.turn.servers.3.password=
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# Временные debug HTTP API для тестирования соединений
|
||||
# true - endpoint'ы /debug/ws/* включены (только при наличии .debug-token)
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
# Задача 01: Доработка вкладки «Каналы» (UI + API)
|
||||
|
||||
## Кратко и по делу
|
||||
Нужно довести вторую вкладку «Каналы» до полностью рабочего состояния на реальных данных сервера.
|
||||
|
||||
Что должно работать:
|
||||
- список каналов;
|
||||
- вход в канал и чтение сообщений;
|
||||
- вход в тред сообщения (история/ветка);
|
||||
- ответ на сообщение;
|
||||
- лайк/снятие лайка;
|
||||
- подписка на пользователя;
|
||||
- подписка на канал;
|
||||
- видимое имя канала в формате `имя_пользователя/имя_канала`.
|
||||
|
||||
Запись любых новых сущностей делается через `AddBlock` с подписью на клиенте.
|
||||
Чтение делается через 3 API:
|
||||
- `ListSubscriptionsFeed`
|
||||
- `GetChannelMessages`
|
||||
- `GetMessageThread`
|
||||
|
||||
Техническая особенность (оставляем как есть):
|
||||
- на экране каналов индикатор непрочитанного = общее число сообщений канала.
|
||||
|
||||
---
|
||||
|
||||
## Подробное ТЗ
|
||||
|
||||
### 1. Цель
|
||||
Сделать рабочий каналовый сценарий «от списка до треда», где чтение строится на RPC API, а запись действий пользователя — только через `AddBlock`.
|
||||
|
||||
### 2. Что уже есть в проекте
|
||||
|
||||
#### 2.1 UI (частично)
|
||||
- Есть страницы:
|
||||
- `channels-list`
|
||||
- `channel-view`
|
||||
- `add-channel-view`
|
||||
- Есть запросы чтения в клиенте:
|
||||
- `authService.listSubscriptionsFeed(...)`
|
||||
- `authService.getChannelMessages(...)`
|
||||
- `authService.getMessageThread(...)`
|
||||
- Есть fallback на mock-данные при ошибках сервера.
|
||||
|
||||
#### 2.2 API/сервер (уже реализованы)
|
||||
- `ListSubscriptionsFeed`
|
||||
- `GetChannelMessages`
|
||||
- `GetMessageThread`
|
||||
- `AddBlock`
|
||||
|
||||
#### 2.3 Тесты
|
||||
- Есть интеграционный тест API каналов: `IT_06_ChannelsApi`.
|
||||
- Есть тесты генерации блоков каналов/связей: `IT_03_AddBlock_NoAuth`.
|
||||
- Формат `AddBlock` и его сборка/подпись описаны в `AddBlockSender`.
|
||||
|
||||
### 3. Проблемы текущей реализации (что надо закрыть)
|
||||
- Кнопки «подписаться на человека/канал» в списке каналов сейчас UI-only (модалка без реальной записи через `AddBlock`).
|
||||
- `add-channel-view` пока не создает канал на сервере через `AddBlock` (`CreateChannelBody`), только делает `navigate`.
|
||||
- `channel-view` добавляет пост локально (в память), а не отправляет блок `TEXT_POST` через `AddBlock`.
|
||||
- Нет полноценного экрана треда сообщения с реальными `GetMessageThread` и действиями `ответить/лайк/убрать лайк` через блоки.
|
||||
- Нет гарантированного отображения канала в требуемом формате `ownerLogin/channelName`.
|
||||
|
||||
### 4. Функциональные требования
|
||||
|
||||
#### 4.1 Список каналов
|
||||
На вкладке «Каналы» отображать 3 группы:
|
||||
- Мои каналы
|
||||
- Каналы пользователей, на кого я подписан
|
||||
- Каналы, на которые я подписан
|
||||
|
||||
Источник данных: `ListSubscriptionsFeed`.
|
||||
|
||||
Каждый канал показывать в формате:
|
||||
- `ownerLogin/channelName`
|
||||
|
||||
#### 4.2 Открытие канала
|
||||
При входе в канал:
|
||||
- загрузить сообщения через `GetChannelMessages`;
|
||||
- показать список сообщений в хронологическом порядке (по текущему параметру `sort`);
|
||||
- оставить техническую особенность непрочитанных как есть.
|
||||
|
||||
#### 4.3 Открытие треда сообщения
|
||||
При клике на сообщение:
|
||||
- загрузить тред через `GetMessageThread`;
|
||||
- показать `ancestors`, `focus`, `descendants`;
|
||||
- из треда должны быть доступны действия:
|
||||
- «Ответить»
|
||||
- «Лайк»
|
||||
- «Убрать лайк»
|
||||
|
||||
Запись действий — только `AddBlock`.
|
||||
|
||||
#### 4.4 Создание канала
|
||||
В `add-channel-view` кнопка «Создать» должна:
|
||||
- отправлять `AddBlock` с телом `CreateChannelBody`;
|
||||
- после успеха возвращать к списку каналов и обновлять его.
|
||||
|
||||
#### 4.5 Подписки
|
||||
- Подписка на пользователя: `AddBlock` с `ConnectionBody` подтип `CONNECTION_FOLLOW`, target = HEADER пользователя.
|
||||
- Подписка на канал: `AddBlock` с `ConnectionBody` подтип `CONNECTION_FOLLOW`, target = root блока канала (`CreateChannelBody` или HEADER для канала `0`).
|
||||
|
||||
### 5. API (форматы)
|
||||
|
||||
## 5.1 ListSubscriptionsFeed (чтение)
|
||||
Request:
|
||||
```json
|
||||
{
|
||||
"op": "ListSubscriptionsFeed",
|
||||
"requestId": "...",
|
||||
"payload": {
|
||||
"login": "A1",
|
||||
"limit": 200
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response (смысловые поля):
|
||||
- `ownedChannels[]`
|
||||
- `followedUsersChannels[]`
|
||||
- `followedChannels[]`
|
||||
|
||||
---
|
||||
|
||||
## 5.2 GetChannelMessages (чтение)
|
||||
Request:
|
||||
```json
|
||||
{
|
||||
"op": "GetChannelMessages",
|
||||
"requestId": "...",
|
||||
"payload": {
|
||||
"channel": {
|
||||
"ownerBlockchainName": "A1-001",
|
||||
"channelRootBlockNumber": 0,
|
||||
"channelRootBlockHash": ""
|
||||
},
|
||||
"limit": 200,
|
||||
"sort": "asc"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response (смысловые поля):
|
||||
- `channel`
|
||||
- `messages[]`
|
||||
|
||||
---
|
||||
|
||||
## 5.3 GetMessageThread (чтение)
|
||||
Request:
|
||||
```json
|
||||
{
|
||||
"op": "GetMessageThread",
|
||||
"requestId": "...",
|
||||
"payload": {
|
||||
"message": {
|
||||
"blockchainName": "A1-001",
|
||||
"blockNumber": 15,
|
||||
"blockHash": "..."
|
||||
},
|
||||
"depthUp": 20,
|
||||
"depthDown": 2,
|
||||
"limitChildrenPerNode": 50
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Response (смысловые поля):
|
||||
- `ancestors[]`
|
||||
- `focus`
|
||||
- `descendants[]`
|
||||
|
||||
---
|
||||
|
||||
## 5.4 AddBlock (запись)
|
||||
Любое изменение (создать канал, пост, reply, реакция, подписка) записывается через:
|
||||
```json
|
||||
{
|
||||
"op": "AddBlock",
|
||||
"requestId": "...",
|
||||
"payload": {
|
||||
"blockchainName": "A1-001",
|
||||
"blockNumber": 6,
|
||||
"prevBlockHash": "<64-hex>",
|
||||
"blockBytesB64": "<base64 full block>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Важно:
|
||||
- `blockBytesB64` формируется на клиенте.
|
||||
- Подпись блока формируется на клиенте приватным blockchain key пользователя.
|
||||
- Перед добавлением блока клиент берет актуальный курсор цепочки с сервера.
|
||||
|
||||
### 6. Типы блоков для каналов и связей (через AddBlock)
|
||||
- Создание канала: `CreateChannelBody`
|
||||
- Пост/ответ: `TextBody` (`TEXT_POST`, `TEXT_REPLY`)
|
||||
- Реакции: `ReactionBody` (лайк/снятие лайка)
|
||||
- Подписки: `ConnectionBody` (`CONNECTION_FOLLOW`)
|
||||
|
||||
### 7. Критерии приемки
|
||||
- Список каналов отображается с реальными данными API.
|
||||
- Формат названия канала в UI: `ownerLogin/channelName`.
|
||||
- Создание канала реально пишет блок и канал появляется после обновления.
|
||||
- Отправка поста/ответа/реакций реально пишет блок и видна после перечитки API.
|
||||
- Подписка на пользователя/канал реально пишет блок и отражается в выдаче.
|
||||
- Переход в тред сообщения показывает реальные `ancestors/focus/descendants`.
|
||||
- Непрочитанные в списке каналов = общее число сообщений (временное правило).
|
||||
|
||||
### 8. Локальный запуск (уже сделано)
|
||||
Команда:
|
||||
```bash
|
||||
./gradlew startLocal
|
||||
```
|
||||
|
||||
Что делает:
|
||||
- чистит логи;
|
||||
- билдит сервер;
|
||||
- запускает локальный WS сервер;
|
||||
- запускает локальный HTTP сервер клиента;
|
||||
- открывает браузер по URL с параметром `localWsPort`.
|
||||
@@ -1,25 +0,0 @@
|
||||
# Краткое описание задачи
|
||||
|
||||
Нужно сделать полностью рабочую вкладку «Каналы» в SHiNE.
|
||||
|
||||
Пользователь должен:
|
||||
- видеть список каналов;
|
||||
- открывать канал и читать сообщения;
|
||||
- открывать тред сообщения;
|
||||
- отвечать, ставить и убирать лайк;
|
||||
- подписываться на пользователей и каналы.
|
||||
|
||||
Чтение данных идет через 3 API:
|
||||
- `ListSubscriptionsFeed`
|
||||
- `GetChannelMessages`
|
||||
- `GetMessageThread`
|
||||
|
||||
Все действия записи делаются только через `AddBlock` с подписью на клиенте.
|
||||
|
||||
Формат имени канала в интерфейсе:
|
||||
- `имя_пользователя/имя_канала`
|
||||
|
||||
Локальный запуск проекта:
|
||||
```bash
|
||||
./gradlew startLocal
|
||||
```
|
||||
@@ -1,153 +0,0 @@
|
||||
# Задача 02: Web Push + подписанный API отправки личных сообщений
|
||||
|
||||
## Контекст (по текущему состоянию проекта)
|
||||
- Уже есть JSON WebSocket API для личных сообщений: `SendDirectMessage`, `AckIncomingMessage`, `UpsertPushToken`.
|
||||
- Сейчас серверный fallback-пуш реализован через FCM (`FcmPushSender`) и ключ `fcm.server.key`.
|
||||
- Клиент уже регистрирует service worker и токен Firebase, затем аплоадит push token на сервер.
|
||||
|
||||
## Цель
|
||||
Добавить полностью рабочий сценарий доставки личных сообщений с приоритетом:
|
||||
1) онлайн-доставка в активную WebSocket-сессию;
|
||||
2) если не подтверждено — Web Push;
|
||||
3) поддержать отдельный API отправки без авторизации, где доступ проверяется цифровой подписью Ed25519 по `clientKey` отправителя.
|
||||
|
||||
---
|
||||
|
||||
## Предварительная спецификация подписанного пакета (v1)
|
||||
> ВАЖНО: финально фиксируется после уточнений по endian/кодировкам/лимитам.
|
||||
|
||||
Пакет (binary):
|
||||
1. `prefix` — ASCII-константа, например `SHINE_MESSAGE`.
|
||||
2. `toLoginLen` — 1 байт.
|
||||
3. `toLogin` — ASCII, длина = `toLoginLen`.
|
||||
4. `fromLoginLen` — 1 байт.
|
||||
5. `fromLogin` — ASCII, длина = `fromLoginLen`.
|
||||
6. `timeMs` — 8 байт (unix ms).
|
||||
7. `nonce32` — 4 байта случайное число.
|
||||
8. `messageType` — 4 байта.
|
||||
9. `targetMode` — 1 байт:
|
||||
- `0` = всем сессиям пользователя,
|
||||
- `1` = конкретной сессии.
|
||||
10. Если `targetMode=1`:
|
||||
- `sessionIdLen` — 1 байт,
|
||||
- `sessionId` — ASCII.
|
||||
11. `messageLen` — 2 байта.
|
||||
12. `messageBytes` — бинарные данные длиной `messageLen`.
|
||||
13. `signature64` — 64 байта, Ed25519 подпись всего блока **без** `signature64`.
|
||||
|
||||
Ограничения (первичный draft):
|
||||
- общий размер пакета ≤ 4000 байт;
|
||||
- логины/префикс/идентификатор сессии — ASCII;
|
||||
- повторы отсекаются по `(fromLogin, timeMs, nonce32)` в окне TTL.
|
||||
|
||||
---
|
||||
|
||||
## Сервер: что доработать
|
||||
|
||||
### 1) Новый endpoint без авторизации
|
||||
Операция (через WS JSON обертку) условно `SendSignedDirectMessage`:
|
||||
- принимает пакет (base64 binary blob);
|
||||
- парсит и валидирует формат;
|
||||
- достает `fromLogin`, поднимает `clientKey` пользователя;
|
||||
- проверяет подпись Ed25519;
|
||||
- проверяет анти-replay (time window + nonce);
|
||||
- отправляет сообщение по правилам маршрутизации;
|
||||
- пишет результат (messageId, каналы доставки, причины недоставки).
|
||||
|
||||
### 2) Маршрутизация доставки
|
||||
Для `targetMode=1`:
|
||||
- если целевая сессия онлайн и ACK пришел вовремя — успех;
|
||||
- иначе отправка в Web Push этой сессии (если есть subscription).
|
||||
|
||||
Для `targetMode=0`:
|
||||
- обход всех сессий пользователя;
|
||||
- сначала online delivery + ACK;
|
||||
- для непринятых/офлайн — Web Push по соответствующим subscription;
|
||||
- если subscription отсутствует — тихий skip.
|
||||
|
||||
### 3) Миграция от FCM к Web Push
|
||||
- добавить конфиг VAPID (`webpush.public.key`, `webpush.private.key`, `webpush.subject`);
|
||||
- хранить на сервере не только token, а web-push subscription (endpoint + keys);
|
||||
- сделать отправщик Web Push и заменить/расширить текущий `FcmPushSender`.
|
||||
|
||||
### 4) Безопасность
|
||||
- строгая ASCII-валидация логинов/sessionId;
|
||||
- лимиты длины всех полей;
|
||||
- rate limit на endpoint;
|
||||
- audit-лог неуспешных проверок подписи/формата;
|
||||
- защита от replay.
|
||||
|
||||
---
|
||||
|
||||
## Клиент (shine-UI): что доработать
|
||||
|
||||
1. Перейти на стандартный Web Push flow:
|
||||
- регистрация service worker;
|
||||
- `PushManager.subscribe(...)` с VAPID public key;
|
||||
- отправка subscription на сервер (`UpsertPushSubscription` или расширение `UpsertPushToken`).
|
||||
|
||||
2. Service worker:
|
||||
- `push` handler получает payload целиком;
|
||||
- показывает системное уведомление;
|
||||
- при клике открывает/фокусирует нужный чат.
|
||||
|
||||
3. Online-сообщения:
|
||||
- сохранить текущий event-канал `IncomingDirectMessage`;
|
||||
- обязателен ACK (`AckIncomingMessage` уже есть).
|
||||
|
||||
4. Keep-alive:
|
||||
- UI отправляет `Ping` раз в 60 секунд при активной сессии.
|
||||
|
||||
---
|
||||
|
||||
## Документация
|
||||
Сделать отдельный документ настройки Web Push:
|
||||
- как сгенерировать VAPID ключи;
|
||||
- какие параметры прописать на сервере и в UI;
|
||||
- как проверить локально e2e (онлайн + офлайн пуш);
|
||||
- ограничения payload и рекомендации по ретраям.
|
||||
|
||||
---
|
||||
|
||||
## Этапы реализации (предложение)
|
||||
1. Зафиксировать бинарный формат + валидации.
|
||||
2. Реализовать серверный parser/validator/signature verify/replay guard.
|
||||
3. Реализовать Web Push sender + storage subscription.
|
||||
4. Подключить новый endpoint и маршрутизацию доставки.
|
||||
5. Обновить UI (subscription + service worker + ping timer).
|
||||
6. Добавить интеграционные тесты (online ACK / offline push / bad signature / replay / oversize).
|
||||
7. Добавить документацию.
|
||||
|
||||
---
|
||||
|
||||
## Что нужно уточнить до разработки
|
||||
1. Endian для `timeMs/nonce/messageType/messageLen` (big-endian или little-endian).
|
||||
2. Что именно подписывается: строго весь префикс..messageBytes (без подписи) — подтвердить.
|
||||
3. Диапазон допустимых `messageType`.
|
||||
4. TTL окна для анти-replay (например 5 минут / 15 минут).
|
||||
5. Лимиты длин для login/session/message.
|
||||
6. Можно ли временно оставить FCM как fallback, пока не готов Web Push в проде.
|
||||
7. Формат сообщения в `messageBytes`: opaque bytes или UTF-8 строка.
|
||||
|
||||
|
||||
## Статус реализации (12.04.2026)
|
||||
|
||||
### Что уже внедрено в коде
|
||||
- `SendDirectMessage` переведён на signed-binary payload (`blobB64`) без обязательной авторизации WS-сессии.
|
||||
- Внедрён бинарный парсер пакета формата `SHiNE_msg + version(1) + ... + signature64`.
|
||||
- Проверка подписи Ed25519 делается по `clientKey` отправителя через `shine-server-crypto` (`Ed25519Util`).
|
||||
- Добавлен anti-replay guard `(from_login, time_ms, nonce)` с TTL 15 минут.
|
||||
- Добавлено историческое хранилище `signed_direct_messages_history` с сырым пакетом `raw_packet`.
|
||||
- Логика доставки: сначала WS+ACK, затем fallback на Web Push (по подписке конкретной session).
|
||||
- Поле типа сообщения переведено на `uint16`, пока поддерживается только `1`.
|
||||
- Для `targetMode=1` при несуществующей сессии возвращается `success` с `sessionNotFound=true` и `delivered=0`.
|
||||
- UI переведён с Firebase/FCM на браузерный `PushManager.subscribe` + Service Worker `push`.
|
||||
- Добавлен keep-alive ping из UI раз в 60 секунд при авторизованной сессии.
|
||||
|
||||
### Что настроить в окружении
|
||||
- В `application.properties` задать:
|
||||
- `webpush.vapid.public`
|
||||
- `webpush.vapid.private`
|
||||
- `webpush.vapid.subject`
|
||||
- В `shine-UI/index.html` задать публичный VAPID ключ в `window.__SHINE_WEBPUSH_VAPID_PUBLIC_KEY__`.
|
||||
|
||||
@@ -35,5 +35,5 @@
|
||||
|
||||
## Какие документы потом обновить
|
||||
|
||||
- `Deploy/`;
|
||||
- `Dev_Docs/Blockchain/sync-between-servers.md`, если изменится поведение остановки/восстановления.
|
||||
- `deploy/`;
|
||||
- `docs/Blockchain/sync-between-servers.md`, если изменится поведение остановки/восстановления.
|
||||
|
||||
@@ -24,6 +24,6 @@ QR-подключение других устройств сейчас есть
|
||||
|
||||
## Какие документы потом обновить
|
||||
|
||||
- `Dev_Docs/Solana_Architecture/README.md`;
|
||||
- `docs/Solana_Architecture/README.md`;
|
||||
- `TODO/medium/2026-06-03_подключение_других_устройств_через_qr.md`;
|
||||
- `TODO/medium/2026-06-02_сессионные_homeserver_в_pda.md`.
|
||||
|
||||
+17
-3
@@ -12,16 +12,30 @@
|
||||
- откуда продолжать;
|
||||
- какие документы потом надо обновить.
|
||||
- Это не активная разработка. Тут только план и контекст.
|
||||
- Старую папку `Dev_Docs/Future_Features/` считать архивной и больше не использовать как источник новых задач.
|
||||
- Старую папку `docs/Future_Features/` считать архивной и больше не использовать как источник новых задач.
|
||||
|
||||
## Текущие задачи
|
||||
|
||||
- `2026-06-26_1800_корректное_завершение_за_30с.md` - дать сервису до 30 секунд на корректное завершение опасных операций перед рестартом.
|
||||
- `2026-06-26_1805_межсерверный_ws_и_dm_sync.md` - постоянный server-to-server WebSocket, push новых блоков и DM, ACK и backfill.
|
||||
- `2026-06-26_1810_подключение_устройств_по_qr.md` - довести подключение других устройств по QR и перевести это в нормальные типизированные сессии.
|
||||
- `2026-06-26_1815_esp32_файловое_хранилище.md` - использовать ESP32 как личное файловое хранилище для переписок и вложений.
|
||||
|
||||
## Перенесённые планы из `Dev_Docs/Future_Features/`
|
||||
## Децентрализация
|
||||
|
||||
Текущий production-режим SHiNE считается односерверным. Задачи по нескольким серверам, Arweave и realtime PDA/Solana sync вынесены в `Децентрализация/` и не блокируют выкладку текущей версии на GitHub.
|
||||
|
||||
- `Децентрализация/односерверный_production_режим.md` - границы текущей production-версии с одним сервером.
|
||||
- `Децентрализация/запись_блокчейнов_в_arweave.md` - будущая запись/архивация блокчейнов в Arweave.
|
||||
- `Децентрализация/realtime_pda_solana_sync.md` - будущая онлайн-синхронизация PDA и Solana.
|
||||
- `Децентрализация/межсерверная_передача_сообщений.md` - будущая доставка сообщений между серверами.
|
||||
- `Децентрализация/межсерверные_звонки.md` - будущая маршрутизация звонков между серверами.
|
||||
- `Децентрализация/2026-06-26_1805_межсерверный_ws_и_dm_sync.md` - перенесённый старый план постоянного server-to-server WS и DM sync.
|
||||
|
||||
## Новые фишки которые надо доделать
|
||||
|
||||
- `Новые фишки которые надо доделать/Новая_контентная_модель_блокчейна/` - отложенная новая контентная модель блокчейна, не входящая в текущий односерверный production-релиз.
|
||||
|
||||
## Перенесённые планы из `docs/Future_Features/`
|
||||
|
||||
### near
|
||||
|
||||
|
||||
@@ -104,9 +104,9 @@
|
||||
|
||||
## Какие документы нужно будет обновить при реализации
|
||||
|
||||
- `Dev_Docs/Blockchain/README.md` и связанные файлы, если изменятся типы служебных сообщений или форматы блокчейн-команд.
|
||||
- `Dev_Docs/API/` если изменится публичный серверный API или появятся новые операции.
|
||||
- `Dev_Docs/Personal_Messages/Протокол_DM_v1.md` если часть маршрутизации или подтверждений будет встроена в существующую логику доставки/сессий.
|
||||
- `docs/Blockchain/README.md` и связанные файлы, если изменятся типы служебных сообщений или форматы блокчейн-команд.
|
||||
- `docs/API/` если изменится публичный серверный API или появятся новые операции.
|
||||
- `docs/Personal_Messages/Протокол_DM_v1.md` если часть маршрутизации или подтверждений будет встроена в существующую логику доставки/сессий.
|
||||
- Документацию по homeserver/ESP32, если появится пользовательская или сервисная файловая логика на устройстве.
|
||||
|
||||
## С какого места продолжать позже
|
||||
|
||||
@@ -56,11 +56,9 @@
|
||||
- Код формирования репоста в `auth-service.js` не удалён: его можно будет использовать как основу при возвращении к задаче.
|
||||
- Код отображения target-полей и перехода к оригиналу не удалён: он нужен для будущей проверки и возможной совместимости с уже созданными тестовыми блоками.
|
||||
|
||||
## Почему это не лежит в Pending_Features
|
||||
## Почему это лежит в TODO
|
||||
|
||||
`Dev_Docs/Pending_Features/` предназначена для фич, которые уже реализованы и ждут ручной проверки.
|
||||
|
||||
Репосты сейчас не подходят под этот статус: они не должны проверяться как готовая фича, потому что пользовательский сценарий временно закрыт, а серверная запись новых репостов заблокирована. Поэтому старый pending-файл удалён, а задача перенесена сюда как будущая.
|
||||
Репосты сейчас не должны проверяться как готовая фича, потому что пользовательский сценарий временно закрыт, а серверная запись новых репостов заблокирована. Поэтому задача остаётся в TODO как будущая.
|
||||
|
||||
## Что сделать при возврате к реализации
|
||||
|
||||
@@ -82,11 +80,11 @@
|
||||
- отображение `targetBlockchainName`, `targetBlockNumber`, `targetBlockHash`.
|
||||
7. Добавить или обновить тесты на успешный репост и отказ некорректных target-полей.
|
||||
8. Обновить документацию:
|
||||
- `Dev_Docs/Blockchain/11_TEXT_Blocks.md`;
|
||||
- `Dev_Docs/Blockchain/CHANGELOG.md`;
|
||||
- `Dev_Docs/API/04_Add_Block_to_Blockchain_API.md`;
|
||||
- `docs/Blockchain/11_TEXT_Blocks.md`;
|
||||
- `docs/Blockchain/CHANGELOG.md`;
|
||||
- `docs/API/04_Add_Block_to_Blockchain_API.md`;
|
||||
- документы API чтения каналов/тредов, если изменятся поля ответа.
|
||||
9. После реализации перенести задачу из `TODO/` в `Dev_Docs/Pending_Features/` как фичу, требующую ручной проверки.
|
||||
9. После реализации отдельно согласовать ручную проверку пользовательского сценария.
|
||||
|
||||
## Минимальный чек-лист ручной проверки в будущем
|
||||
|
||||
|
||||
@@ -47,10 +47,10 @@
|
||||
|
||||
## Документы, которые обновить при реализации
|
||||
|
||||
- `Dev_Docs/Blockchain/`, если появятся или изменятся блоки баланса.
|
||||
- `Dev_Docs/Blockchain/CHANGELOG.md`, если меняется блокчейн-формат.
|
||||
- `Dev_Docs/API/`, если меняется серверный API.
|
||||
- `Dev_Docs/Pending_Features/` - добавить файл ручной проверки после реализации.
|
||||
- `docs/Blockchain/`, если появятся или изменятся блоки баланса.
|
||||
- `docs/Blockchain/CHANGELOG.md`, если меняется блокчейн-формат.
|
||||
- `docs/API/`, если меняется серверный API.
|
||||
- после реализации отдельно согласовать ручную проверку.
|
||||
- Документацию Solana-регистрации, если баланс будет связан с Solana-модулем.
|
||||
|
||||
## Минимальная проверка в будущем
|
||||
|
||||
@@ -34,10 +34,10 @@
|
||||
|
||||
## Документы, которые нужно обновить при возврате
|
||||
|
||||
- `Dev_Docs/Keys/README.md`
|
||||
- `Dev_Docs/Personal_Messages/Протокол_DM_v1.md`
|
||||
- `Dev_Docs/API/`
|
||||
- `Dev_Docs/Blockchain/`, если появятся новые блоки или команды для файлов.
|
||||
- `docs/Keys/README.md`
|
||||
- `docs/Personal_Messages/Протокол_DM_v1.md`
|
||||
- `docs/API/`
|
||||
- `docs/Blockchain/`, если появятся новые блоки или команды для файлов.
|
||||
|
||||
## С какого места продолжать
|
||||
|
||||
|
||||
@@ -84,11 +84,11 @@
|
||||
## Что нужно обновить при реализации
|
||||
|
||||
- `shine-solana/shine/doc/formats/shine-user-pda-format-v.1.0.md`
|
||||
- `Dev_Docs/Solana_Architecture/README.md`
|
||||
- `Dev_Docs/Инициализация_Solana_регистрации/README.md`
|
||||
- `Dev_Docs/Keys/README.md`
|
||||
- `Dev_Docs/Personal_Messages/Протокол_DM_v1.md`, если изменится адресация DM по типам сессий
|
||||
- `Dev_Docs/API/`, если появятся новые серверные операции или изменятся ответы
|
||||
- `docs/Solana_Architecture/README.md`
|
||||
- `docs/Инициализация_Solana_регистрации/README.md`
|
||||
- `docs/Keys/README.md`
|
||||
- `docs/Personal_Messages/Протокол_DM_v1.md`, если изменится адресация DM по типам сессий
|
||||
- `docs/API/`, если появятся новые серверные операции или изменятся ответы
|
||||
|
||||
## Что пока не делать
|
||||
|
||||
|
||||
@@ -37,9 +37,8 @@
|
||||
|
||||
## Что обновить при возврате
|
||||
|
||||
- `Dev_Docs/Pending_Features/README.md`
|
||||
- после реализации отдельно согласовать ручную проверку
|
||||
- `shine-UI/js/pages/connect-device-view.js`
|
||||
- `shine-UI/js/pages/device-qr-view.js`
|
||||
- `shine-UI/js/services/qr-key-transfer-service.js`
|
||||
- документацию по ключам, если формат переноса меняется
|
||||
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
## Документы, которые обновить при реализации
|
||||
|
||||
- Документацию UI/кошельков, если такая есть.
|
||||
- `Dev_Docs/Pending_Features/` - добавить файл ручной проверки после реализации.
|
||||
- `Dev_Docs/API/`, только если появится новый серверный API или логирование.
|
||||
- после реализации отдельно согласовать ручную проверку.
|
||||
- `docs/API/`, только если появится новый серверный API или логирование.
|
||||
|
||||
## Минимальная проверка
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ git show 0240db5:shine-solana/shine/programs/shine_login_guard/src/lib.rs
|
||||
- `shine-solana/shine/doc/programs/shine_login_guard.md`
|
||||
|
||||
5. Архитектурная документация:
|
||||
- `Dev_Docs/Solana_Architecture/README.md`
|
||||
- `docs/Solana_Architecture/README.md`
|
||||
|
||||
6. UI-логика precheck:
|
||||
- `shine-UI/js/pages/register-view.js`
|
||||
@@ -108,7 +108,7 @@ git show 0240db5:shine-solana/shine/programs/shine_login_guard/src/lib.rs
|
||||
4. Проверить, что `build.rs` всё ещё генерирует `generated_dictionary.rs` в прежнем формате.
|
||||
5. Сверить актуальность словарей в `src/dictionaries`.
|
||||
6. Обновить `shine_login_guard.md` обратно под словарную логику.
|
||||
7. Обновить `Dev_Docs/Solana_Architecture/README.md`.
|
||||
7. Обновить `docs/Solana_Architecture/README.md`.
|
||||
|
||||
### Проверка после возврата
|
||||
|
||||
|
||||
@@ -28,6 +28,6 @@
|
||||
|
||||
## Какие документы потом обновить
|
||||
|
||||
- `Dev_Docs/Personal_Messages/Протокол_DM_v1.md`, если изменится стратегия миграции старой истории;
|
||||
- `Dev_Docs/Personal_Messages/Формат_DM_v1.md`, если появится отдельное правило совместимости/конвертации;
|
||||
- при необходимости `Dev_Docs/API/12_Direct_Messages_Push_Calls_API.md`, если затронется поведение backlog/доставки.
|
||||
- `docs/Personal_Messages/Протокол_DM_v1.md`, если изменится стратегия миграции старой истории;
|
||||
- `docs/Personal_Messages/Формат_DM_v1.md`, если появится отдельное правило совместимости/конвертации;
|
||||
- при необходимости `docs/API/12_Direct_Messages_Push_Calls_API.md`, если затронется поведение backlog/доставки.
|
||||
|
||||
+7
-5
@@ -2,7 +2,9 @@
|
||||
|
||||
## Зачем
|
||||
|
||||
Сейчас синхронизация между серверами работает в основном как periodic sync и one-shot push. Для нормальной репликации ещё нужен постоянный межсерверный канал:
|
||||
Текущий production-режим SHiNE рассчитан на один основной сервер. Межсерверная синхронизация относится к будущей децентрализации и не должна блокировать выкладку односерверной production-версии.
|
||||
|
||||
Сейчас синхронизация между серверами работает в основном как periodic sync и one-shot push. Для нормальной репликации в будущем ещё нужен постоянный межсерверный канал:
|
||||
|
||||
- живое подключение к партнёру;
|
||||
- push новых блоков;
|
||||
@@ -35,7 +37,7 @@
|
||||
|
||||
## Какие документы потом обновить
|
||||
|
||||
- `Dev_Docs/Blockchain/sync-between-servers.md`;
|
||||
- `Dev_Docs/Personal_Messages/Протокол_DM_v1.md`;
|
||||
- `Dev_Docs/Personal_Messages/Формат_DM_v1.md`;
|
||||
- `Dev_Docs/API/`.
|
||||
- `docs/Blockchain/sync-between-servers.md`;
|
||||
- `docs/Personal_Messages/Протокол_DM_v1.md`;
|
||||
- `docs/Personal_Messages/Формат_DM_v1.md`;
|
||||
- `docs/API/`.
|
||||
@@ -0,0 +1,16 @@
|
||||
# Децентрализация
|
||||
|
||||
Папка для задач, которые нужны для будущего режима с несколькими серверами, Solana/PDA-синхронизацией и внешним хранением данных.
|
||||
|
||||
## Текущий статус
|
||||
|
||||
Сейчас production-режим SHiNE считается односерверным: один сервер обслуживает пользователей, сообщения, звонки и запись данных. Задачи из этой папки не являются блокерами для выкладки текущего репозитория на GitHub и запуска одного production-сервера.
|
||||
|
||||
## Задачи
|
||||
|
||||
- `односерверный_production_режим.md` - зафиксировать границы текущей production-версии.
|
||||
- `запись_блокчейнов_в_arweave.md` - вынести долговременную запись блокчейнов в Arweave.
|
||||
- `realtime_pda_solana_sync.md` - сделать онлайн-синхронизацию PDA/Solana в реальном времени.
|
||||
- `межсерверная_передача_сообщений.md` - реализовать доставку сообщений между серверами.
|
||||
- `межсерверные_звонки.md` - реализовать маршрутизацию звонков между серверами.
|
||||
- `2026-06-26_1805_межсерверный_ws_и_dm_sync.md` - старый план постоянного server-to-server WS и DM sync, перенесённый в контекст децентрализации.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Realtime-синхронизация PDA и Solana
|
||||
|
||||
## Зачем
|
||||
|
||||
В будущем PDA-записи и Solana-состояние должны автоматически и быстро синхронизироваться с серверным состоянием, чтобы данные пользователей, homeserver-сессии и связанные записи не расходились.
|
||||
|
||||
## Что сделать
|
||||
|
||||
1. Определить, какие серверные события должны обновлять PDA.
|
||||
2. Добавить очередь/воркер для надёжной отправки изменений в Solana.
|
||||
3. Добавить периодическую сверку серверного состояния с PDA.
|
||||
4. Добавить обработку ошибок, повторов и конфликтов версий.
|
||||
5. Добавить мониторинг задержек и неуспешных Solana-транзакций.
|
||||
|
||||
## Что учесть
|
||||
|
||||
- Solana/Anchor-модуль находится в `shine-solana/shine/` и ведётся отдельно от основного server/UI deploy.
|
||||
- Перед изменениями внутри Solana-модуля нужно читать `shine-solana/shine/AGENTS.md`.
|
||||
- Основная инструкция по Solana-регистрации находится в `docs/Инициализация_Solana_регистрации/README.md`.
|
||||
- Формат пользовательской PDA-записи описан в `shine-solana/shine/doc/formats/shine-user-pda-format-v.1.0.md`.
|
||||
|
||||
## Документы, которые потом нужно обновить
|
||||
|
||||
- `docs/Инициализация_Solana_регистрации/README.md`;
|
||||
- `docs/Solana_Architecture/README.md`;
|
||||
- `shine-solana/shine/doc/formats/shine-user-pda-format-v.1.0.md`, если меняется формат PDA.
|
||||
|
||||
## Статус
|
||||
|
||||
Отложено до этапа децентрализации.
|
||||
@@ -0,0 +1,29 @@
|
||||
# Запись блокчейнов в Arweave
|
||||
|
||||
## Зачем
|
||||
|
||||
Для будущей децентрализации нужно долговременное внешнее хранение блокчейнов, чтобы данные не зависели только от одного серверного диска.
|
||||
|
||||
## Что сделать
|
||||
|
||||
1. Определить, какие блокчейны и какие диапазоны блоков записываются в Arweave.
|
||||
2. Зафиксировать формат пачки блоков, метаданных, ссылок и контрольных хэшей.
|
||||
3. Добавить безопасный механизм публикации без хранения приватного JWK в git.
|
||||
4. Добавить проверку уже загруженных диапазонов, чтобы не плодить дубли.
|
||||
5. Описать восстановление блокчейна из Arweave при потере локальных данных.
|
||||
|
||||
## Важные ограничения
|
||||
|
||||
- Любое изменение формата блокчейна требует отдельного предупреждения и явного подтверждения пользователя.
|
||||
- Добавление данных в блокчейн должно выполняться только через `AddBlock`.
|
||||
- Секреты Arweave нельзя хранить в репозитории.
|
||||
|
||||
## Документы, которые потом нужно обновить
|
||||
|
||||
- `docs/Blockchain/README.md`;
|
||||
- `docs/Blockchain/CHANGELOG.md`;
|
||||
- документы deploy/секретов в `deploy/`, если появятся новые параметры.
|
||||
|
||||
## Статус
|
||||
|
||||
Отложено до этапа децентрализации.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Межсерверная передача сообщений
|
||||
|
||||
## Зачем
|
||||
|
||||
Когда у SHiNE появится несколько серверов, пользователи на разных серверах должны получать личные сообщения без ручной синхронизации и без привязки к одному центральному узлу.
|
||||
|
||||
## Что сделать
|
||||
|
||||
1. Определить протокол server-to-server доставки DM.
|
||||
2. Добавить маршрутизацию получателя по серверу, user id, публичному ключу или PDA.
|
||||
3. Добавить ACK, повторы, дедупликацию и backfill пропущенных сообщений.
|
||||
4. Разделить realtime-доставку и восстановление истории.
|
||||
5. Описать поведение при недоступности удалённого сервера.
|
||||
|
||||
## Что учесть
|
||||
|
||||
- Логика DM должна соответствовать документам в `docs/Personal_Messages/`.
|
||||
- При изменении формата signed DM-блока или правил доставки нужно обновлять протокол и байтовый формат DM.
|
||||
- Если появятся новые server API/WebSocket операции, нужно обновить `docs/API/`.
|
||||
|
||||
## Документы, которые потом нужно обновить
|
||||
|
||||
- `docs/Personal_Messages/Протокол_DM_v1.md`;
|
||||
- `docs/Personal_Messages/Формат_DM_v1.md`;
|
||||
- `docs/API/`;
|
||||
- `docs/API/09_Operations_Index.md`, если добавляются новые `op`.
|
||||
|
||||
## Статус
|
||||
|
||||
Отложено до этапа децентрализации.
|
||||
@@ -0,0 +1,29 @@
|
||||
# Межсерверные звонки
|
||||
|
||||
## Зачем
|
||||
|
||||
В будущем пользователи на разных серверах должны иметь возможность устанавливать звонки так же, как пользователи одного сервера.
|
||||
|
||||
## Что сделать
|
||||
|
||||
1. Определить протокол межсерверной сигнализации звонков.
|
||||
2. Добавить маршрутизацию offer/answer/ICE-кандидатов между серверами.
|
||||
3. Добавить обработку статусов занятости, отказа, таймаута и ошибок маршрута.
|
||||
4. Добавить диагностику доставки сигналов между серверами.
|
||||
5. Проверить совместимость с текущими логами `CallDeliveryReport`.
|
||||
|
||||
## Что учесть
|
||||
|
||||
- Специальная диагностика установки звонков идёт через `CallDeliveryReport`.
|
||||
- На production важно сохранять поля `reason`, `failureStage`, `pcConnectionState`, `pcIceConnectionState`, `routeLabel`, `configuredTurnHosts*`, `reachableTurnHosts*`.
|
||||
- Межсерверные звонки не должны ломать текущий односерверный сценарий.
|
||||
|
||||
## Документы, которые потом нужно обновить
|
||||
|
||||
- `docs/API/`, если добавляются или меняются операции сигнализации;
|
||||
- документы по звонкам/диагностике, если они будут выделены отдельно;
|
||||
- deploy-документы, если появятся новые параметры TURN/server-to-server маршрутизации.
|
||||
|
||||
## Статус
|
||||
|
||||
Отложено до этапа децентрализации.
|
||||
@@ -0,0 +1,23 @@
|
||||
# Односерверный production-режим
|
||||
|
||||
## Зачем
|
||||
|
||||
Перед выкладкой репозитория на GitHub и запуском production нужно явно зафиксировать, что текущая стабильная версия работает как один основной сервер.
|
||||
|
||||
## Что считаем текущей нормой
|
||||
|
||||
- Один production-сервер обслуживает пользователей, сообщения, звонки и серверные данные.
|
||||
- Децентрализованные сценарии не считаются обязательными для первого production-релиза.
|
||||
- Межсерверная доставка сообщений, межсерверные звонки, realtime PDA/Solana sync и запись блокчейнов в Arweave вынесены в отдельные будущие задачи.
|
||||
- Код и документация текущего production не должны создавать ожидание, что несколько серверов уже работают как единая realtime-сеть.
|
||||
|
||||
## Что сделать перед возвратом к децентрализации
|
||||
|
||||
1. Проверить актуальные документы по API, blockchain, DM и deploy.
|
||||
2. Выделить минимальный протокол server-to-server взаимодействия.
|
||||
3. Решить, какие данные остаются локальными, какие реплицируются между серверами, а какие записываются во внешнее долговременное хранилище.
|
||||
4. После изменения API, blockchain-форматов или DM-протокола обновить соответствующие документы по правилам проекта.
|
||||
|
||||
## Статус
|
||||
|
||||
Отложено. Текущий production работает как один сервер.
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
# Новая логика контента в блокчейне SHiNE
|
||||
|
||||
## Зачем это нужно
|
||||
|
||||
Сейчас блокчейн SHiNE хорошо умеет хранить обычные сообщения, ответы, лайки и связи между людьми.
|
||||
|
||||
Новая модель добавляет поверх этого более понятный смысл контента:
|
||||
|
||||
- обычный текст;
|
||||
- упражнение;
|
||||
- услуга / процедура;
|
||||
- курс;
|
||||
- стартовая страница канала (`entrypoint`).
|
||||
|
||||
Это нужно для того, чтобы канал стал не просто лентой постов, а полноценным пространством знаний, практик, услуг и сообществ.
|
||||
|
||||
## Что меняется для людей
|
||||
|
||||
### 1. В канале появятся понятные виды материалов
|
||||
|
||||
Сообщение можно будет создать не только как обычный текст, но и как:
|
||||
|
||||
- упражнение;
|
||||
- услугу / процедуру;
|
||||
- курс;
|
||||
- стартовую страницу канала.
|
||||
|
||||
Смысл в том, что приложение и сервер будут понимать, что это за материал, а не просто показывать любой текст одинаково.
|
||||
|
||||
### 2. У канала будет стартовая страница
|
||||
|
||||
У канала появится отдельное стартовое сообщение `entrypoint`.
|
||||
|
||||
Это не курс и не оглавление, а именно главная точка входа в канал:
|
||||
|
||||
- короткое объяснение, о чём канал;
|
||||
- описание структуры;
|
||||
- ссылки на нужные материалы;
|
||||
- удобное начало для новых людей.
|
||||
|
||||
У канала в каждый момент времени будет только одна актуальная стартовая страница.
|
||||
Если её исправляют, то сохраняется история версий.
|
||||
Если её удаляют, для интерфейса считается, что стартовой страницы у канала сейчас нет.
|
||||
|
||||
### 3. Курс, упражнение и услуга / процедура будут отличаться по смыслу
|
||||
|
||||
Это важно для логики и статистики.
|
||||
|
||||
- `Упражнение` — то, что человек может делать много раз.
|
||||
- `Услуга / процедура` — то, что тоже можно проходить много раз, но обычно с участием другого человека.
|
||||
- `Курс` — то, что можно начать, закончить или бросить.
|
||||
|
||||
За счёт этого сервер сможет честно считать активность, а интерфейс сможет показывать человеку именно те действия, которые подходят к данному типу материала.
|
||||
|
||||
### 4. Появятся статусные действия
|
||||
|
||||
На контент можно будет не только ответить или поставить лайк, но и отметить свой путь:
|
||||
|
||||
- сделал один раз;
|
||||
- заинтересовался и рассматривает;
|
||||
- начал;
|
||||
- закончил / освоил / знаю;
|
||||
- бросил.
|
||||
|
||||
При этом:
|
||||
|
||||
- для упражнений и услуг / процедур будет отдельно считаться, сколько раз человек сделал / прошёл;
|
||||
- для упражнений и курсов будет храниться текущий статус.
|
||||
|
||||
Текущий статус определяется просто:
|
||||
|
||||
- последнее статусное действие и считается актуальным.
|
||||
|
||||
Например:
|
||||
|
||||
- если последнее действие “заинтересовался и рассматривает”, значит человек присматривается, но ещё не начал;
|
||||
- если последнее действие `started`, значит материал сейчас в процессе;
|
||||
- если последнее действие `abandoned`, значит человек бросил;
|
||||
- если последнее действие `completed`, значит для системы он завершил / освоил материал.
|
||||
|
||||
### 5. К действиям можно добавлять живой текст
|
||||
|
||||
Практически любое статусное действие можно будет сопровождать коротким комментарием.
|
||||
|
||||
Например:
|
||||
|
||||
- “Начал изучать, потому что давно хотел разобраться”;
|
||||
- “Бросил, пока нет времени”;
|
||||
- “Прошёл процедуру, стало заметно легче”.
|
||||
|
||||
Это важно, потому что сам блокчейн будет хранить не только формальный статус, но и живую человеческую причину или заметку.
|
||||
|
||||
### 6. Появится подтверждение статуса другими людьми
|
||||
|
||||
Отдельный человек сможет подтвердить чей-то статус.
|
||||
|
||||
Примеры:
|
||||
|
||||
- подтвердить, что человек действительно занимался;
|
||||
- подтвердить, что он реально прошёл услугу;
|
||||
- подтвердить, что он освоил материал.
|
||||
|
||||
Подтверждение — это не замена статуса, а отдельное мнение / свидетельство со стороны.
|
||||
|
||||
### 7. Появится отдельный тип «мнение»
|
||||
|
||||
На любое сообщение можно будет ответить не только обычным ответом, но и специальным типом ответа: `мнение`.
|
||||
|
||||
Это по сути тоже текстовый ответ, но с отдельным смыслом:
|
||||
|
||||
- это отзыв;
|
||||
- это оценка;
|
||||
- это мнение о материале;
|
||||
- это явная метка для будущего анализа нейронками.
|
||||
|
||||
То есть:
|
||||
|
||||
- обычный ответ нужен для разговора;
|
||||
- `мнение` нужно для отзыва, оценки и анализа реакции людей.
|
||||
|
||||
## Что остаётся как раньше
|
||||
|
||||
### Комментарии
|
||||
|
||||
Обычные ответы на сообщения остаются.
|
||||
То есть обсуждение материалов не ломается и не меняется концептуально.
|
||||
|
||||
### Лайки контента
|
||||
|
||||
Лайк на сообщение, курс, упражнение или услугу остаётся обычной реакцией на конкретный блок.
|
||||
|
||||
### Лайк пользователю
|
||||
|
||||
Лайк пользователю не будет считаться реакцией на сообщение.
|
||||
Он относится к графу связей между людьми.
|
||||
|
||||
Это удобно, потому что:
|
||||
|
||||
- лайк человека — это отношение к человеку;
|
||||
- лайк материала — это отношение к контенту.
|
||||
|
||||
## Сообщество вокруг канала
|
||||
|
||||
Канал сможет работать не только как лента, но и как сообщество.
|
||||
|
||||
Для этого появятся простые действия:
|
||||
|
||||
- заявка на вступление;
|
||||
- самостоятельный выход;
|
||||
- принятие;
|
||||
- исключение.
|
||||
|
||||
Сервер сможет понимать:
|
||||
|
||||
- кто только подал заявку;
|
||||
- кто уже принят;
|
||||
- кто вышел;
|
||||
- кто был исключён.
|
||||
|
||||
## Личный канал и лента достижений
|
||||
|
||||
У каждого человека по смыслу появляется два важных пространства:
|
||||
|
||||
- канал его обычных постов;
|
||||
- отдельная лента его тренировок и достижений.
|
||||
|
||||
В обычном канале человек сможет:
|
||||
|
||||
- писать посты;
|
||||
- делиться мыслями;
|
||||
- публиковать материалы;
|
||||
- обсуждать темы как раньше.
|
||||
|
||||
А в ленте достижений будут видны его реальные действия:
|
||||
|
||||
- какие упражнения он делал;
|
||||
- какие услуги / процедуры проходил;
|
||||
- какие курсы его заинтересовали;
|
||||
- какие курсы он начал;
|
||||
- какие курсы он закончил;
|
||||
- что он бросил.
|
||||
|
||||
То есть блокчейн SHiNE сможет хранить не только слова человека, но и его путь, активность и историю практики.
|
||||
|
||||
## Что смогут делать авторы контента
|
||||
|
||||
Создатели контента в своих каналах смогут публиковать не только обычные посты, но и:
|
||||
|
||||
- упражнения;
|
||||
- курсы;
|
||||
- стартовую страницу канала;
|
||||
- услуги / процедуры, которые они оказывают.
|
||||
|
||||
Это превращает канал в сочетание:
|
||||
|
||||
- блога;
|
||||
- базы знаний;
|
||||
- пространства обучения;
|
||||
- каталога услуг и практик.
|
||||
|
||||
## Что увидит человек в интерфейсе
|
||||
|
||||
На специальных сообщениях в UI можно будет показывать отдельные кнопки действий.
|
||||
|
||||
Например:
|
||||
|
||||
- `Выполнил упражнение`
|
||||
- `Прошёл процедуру`
|
||||
- `Заинтересовало`
|
||||
- `Начал курс`
|
||||
- `Закончил курс`
|
||||
|
||||
То есть материал можно будет не просто прочитать, а сразу отметить реальное действие.
|
||||
|
||||
Также при ответе на любое сообщение можно будет выбрать:
|
||||
|
||||
- обычный ответ;
|
||||
- `мнение / отзыв`.
|
||||
|
||||
## Как будет работать лента достижений
|
||||
|
||||
Если кто-то зайдёт в твою ленту достижений, он сможет:
|
||||
|
||||
- прочитать, что ты делал;
|
||||
- оставить мнение / отзыв;
|
||||
- подтвердить, что это действительно было.
|
||||
|
||||
Это даёт основу для мягкой “сертификации” внутри SHiNE.
|
||||
|
||||
Например:
|
||||
|
||||
- человек прошёл курс и получил подтверждения;
|
||||
- человек прошёл процедуру и получил отзыв;
|
||||
- человек регулярно делает упражнения, и это видно в его истории.
|
||||
|
||||
Так постепенно у пользователя появляется не только лента постов, но и лента достижений, подтверждений и репутации.
|
||||
|
||||
## Ссылки внутри SHiNE
|
||||
|
||||
Для переходов между материалами вводятся простые внутренние адреса:
|
||||
|
||||
- обычная ссылка: `SHiNE/alice-001/157`
|
||||
- особополная ссылка: `SHiNE/alice-001/157/ХЭШ`
|
||||
|
||||
Первая форма — основная и каноническая.
|
||||
Вторая нужна там, где хочется добавить ещё и точную проверку по хэшу.
|
||||
|
||||
## Что это даёт в итоге
|
||||
|
||||
После внедрения новая блокчейн-логика позволит:
|
||||
|
||||
- строить каналы как структурированные пространства, а не просто как поток постов;
|
||||
- выделять упражнения, услуги и курсы как отдельные сущности;
|
||||
- показывать стартовую страницу канала;
|
||||
- хранить путь человека по материалу;
|
||||
- хранить отдельную ленту его действий и достижений;
|
||||
- считать активность и статусы;
|
||||
- подтверждать результаты другими людьми;
|
||||
- развивать сообщество вокруг канала.
|
||||
|
||||
И самое важное: всё это можно добавить как расширение уже существующего блокчейна SHiNE, не разрушая старую модель сообщений.
|
||||
|
||||
## Отдельный вопрос для будущего
|
||||
|
||||
Отзывы о людях как о людях — полезная идея, но её стоит дополнительно обдумать.
|
||||
|
||||
Например, на вкладке связей в будущем можно:
|
||||
|
||||
- писать человеку отзыв;
|
||||
- смотреть все отзывы о человеке;
|
||||
- выводить сначала отзывы близких друзей, родственников, друзей и контактов, а уже потом остальные.
|
||||
|
||||
Но этот слой нужно делать осторожно, чтобы он не стал слишком жёстким или неприятным для людей.
|
||||
|
||||
Поэтому отзывы о людях как отдельная социальная механика требуют дополнительного обсуждения и проектирования.
|
||||
+670
@@ -0,0 +1,670 @@
|
||||
# ТЗ: новая контентная модель блокчейна SHiNE
|
||||
|
||||
## Статус документа
|
||||
|
||||
Этот документ описывает предлагаемые новые типы блоков и правила их обработки.
|
||||
|
||||
Цель:
|
||||
|
||||
- добавить новую семантику контента;
|
||||
- не ломать существующие блоки `type=0..4`;
|
||||
- внедрить всё как расширение блокчейна за счёт новых форматов.
|
||||
|
||||
Документ является проектным ТЗ на реализацию в сервере, БД, API чтения и UI.
|
||||
|
||||
## 1. Базовые принципы
|
||||
|
||||
### 1.1. Совместимость
|
||||
|
||||
Старые типы не меняются:
|
||||
|
||||
- `type=0` — TECH
|
||||
- `type=1` — TEXT
|
||||
- `type=2` — REACTION
|
||||
- `type=3` — CONNECTION
|
||||
- `type=4` — USER_PARAM
|
||||
|
||||
Новые сущности и действия добавляются только как новые `type` и новые `body`.
|
||||
|
||||
Это означает:
|
||||
|
||||
- старые блоки продолжают читаться как раньше;
|
||||
- старые `TEXT_POST`, `TEXT_REPLY`, `REACTION_LIKE` и остальные форматы не ломаются;
|
||||
- существующий блокчейн остаётся валидным;
|
||||
- новый функционал появляется только там, где клиент и сервер умеют его понимать.
|
||||
|
||||
### 1.2. Общая стратегия
|
||||
|
||||
Новая модель делится на четыре слоя:
|
||||
|
||||
1. контентные сущности;
|
||||
2. текстовые отзывы и мнения;
|
||||
3. статусные действия пользователей;
|
||||
4. community-события вокруг канала.
|
||||
|
||||
### 1.3. Редактирование и удаление
|
||||
|
||||
Для новых контентных сущностей сохраняется действующий принцип SHiNE:
|
||||
|
||||
- редактирование всегда ссылается на оригинальный блок;
|
||||
- тип сущности edit не меняет;
|
||||
- удаление выполняется через `edit` с пустым текстом;
|
||||
- отдельный `DELETE`-подтип не вводится.
|
||||
|
||||
Это правило особенно важно для:
|
||||
|
||||
- `plain_text`
|
||||
- `exercise`
|
||||
- `service`
|
||||
- `course`
|
||||
- `entrypoint`
|
||||
|
||||
В пользовательских текстах и UI желательно использовать русские названия:
|
||||
|
||||
- обычный текст;
|
||||
- упражнение;
|
||||
- услуга / процедура;
|
||||
- курс;
|
||||
- стартовое сообщение канала.
|
||||
|
||||
## 2. Канонические внутренние ссылки
|
||||
|
||||
В новой модели поддерживаются только две формы внутренней ссылки:
|
||||
|
||||
- каноническая: `SHiNE/<blockchainName>/<blockNumber>`
|
||||
- особополная: `SHiNE/<blockchainName>/<blockNumber>/<blockHash>`
|
||||
|
||||
Примеры:
|
||||
|
||||
- `SHiNE/alice-001/157`
|
||||
- `SHiNE/alice-001/157/abcd1234...`
|
||||
|
||||
Правила:
|
||||
|
||||
- канонической считается именно короткая форма без хэша;
|
||||
- форма с хэшем используется как усиленный вариант для точной проверки;
|
||||
- внутри UI и серверной логики ссылка должна приводиться как минимум к паре:
|
||||
- `blockchainName`
|
||||
- `blockNumber`
|
||||
- если хэш присутствует, он участвует в дополнительной валидации ссылки.
|
||||
|
||||
## 3. Новые контентные сущности
|
||||
|
||||
## 3.1. Новый `type=5` — `CONTENT`
|
||||
|
||||
Назначение:
|
||||
|
||||
- хранение новых смысловых материалов канала;
|
||||
- сохранение линии канала;
|
||||
- поддержка edit-версий и логического удаления.
|
||||
|
||||
### 3.1.1. Подтипы `CONTENT`
|
||||
|
||||
- `subType=10` — `CONTENT_PLAIN`
|
||||
- `subType=11` — `CONTENT_EDIT_PLAIN`
|
||||
- `subType=20` — `CONTENT_EXERCISE`
|
||||
- `subType=21` — `CONTENT_EDIT_EXERCISE`
|
||||
- `subType=30` — `CONTENT_SERVICE`
|
||||
- `subType=31` — `CONTENT_EDIT_SERVICE`
|
||||
- `subType=40` — `CONTENT_COURSE`
|
||||
- `subType=41` — `CONTENT_EDIT_COURSE`
|
||||
- `subType=50` — `CONTENT_ENTRYPOINT`
|
||||
- `subType=51` — `CONTENT_EDIT_ENTRYPOINT`
|
||||
|
||||
### 3.1.2. Семантика подтипов
|
||||
|
||||
- `CONTENT_PLAIN` — обычный текст нового поколения.
|
||||
- `CONTENT_EXERCISE` — упражнение, которое можно выполнять многократно.
|
||||
- `CONTENT_SERVICE` — услуга / процедура, которую можно проходить многократно.
|
||||
- `CONTENT_COURSE` — курс / оглавление.
|
||||
- `CONTENT_ENTRYPOINT` — стартовое сообщение канала.
|
||||
|
||||
### 3.1.3. Почему `entrypoint` отдельный тип
|
||||
|
||||
`entrypoint` не считается курсом.
|
||||
|
||||
Это отдельная сущность, потому что:
|
||||
|
||||
- она описывает вход в канал;
|
||||
- по ней нельзя делать `started / completed / abandoned`;
|
||||
- у канала в каждый момент времени должна быть только одна актуальная стартовая страница.
|
||||
|
||||
### 3.1.4. Ограничение на `entrypoint`
|
||||
|
||||
Для одного канала допускается только один исходный блок `CONTENT_ENTRYPOINT`.
|
||||
|
||||
Правила:
|
||||
|
||||
- если entrypoint уже существует, создать второй нельзя;
|
||||
- изменять можно только через `CONTENT_EDIT_ENTRYPOINT`;
|
||||
- если entrypoint логически удалён, UI должен считать, что стартовой страницы больше нет;
|
||||
- исторический блок при этом остаётся в цепочке.
|
||||
|
||||
### 3.1.5. Формат body для `CONTENT_*`
|
||||
|
||||
Для `version=1` рекомендуется использовать формат, максимально совместимый по логике с текущими `TEXT_POST` / `TEXT_EDIT_POST`.
|
||||
|
||||
#### Создающие блоки
|
||||
|
||||
Для:
|
||||
|
||||
- `CONTENT_PLAIN`
|
||||
- `CONTENT_EXERCISE`
|
||||
- `CONTENT_SERVICE`
|
||||
- `CONTENT_COURSE`
|
||||
- `CONTENT_ENTRYPOINT`
|
||||
|
||||
body:
|
||||
|
||||
```text
|
||||
ContentLineBody_v1
|
||||
- lineCode: int32
|
||||
- prevLineNumber: int32
|
||||
- prevLineHash32: [32]
|
||||
- thisLineNumber: int32
|
||||
- textLenBytes: uint16
|
||||
- text UTF-8
|
||||
```
|
||||
|
||||
#### Edit-блоки
|
||||
|
||||
Для:
|
||||
|
||||
- `CONTENT_EDIT_PLAIN`
|
||||
- `CONTENT_EDIT_EXERCISE`
|
||||
- `CONTENT_EDIT_SERVICE`
|
||||
- `CONTENT_EDIT_COURSE`
|
||||
- `CONTENT_EDIT_ENTRYPOINT`
|
||||
|
||||
body:
|
||||
|
||||
```text
|
||||
ContentEditBody_v1
|
||||
- lineCode: int32
|
||||
- prevLineNumber: int32
|
||||
- prevLineHash32: [32]
|
||||
- thisLineNumber: int32
|
||||
- toBlockGlobalNumber: int32
|
||||
- toBlockHash32: [32]
|
||||
- textLenBytes: uint16
|
||||
- text UTF-8
|
||||
```
|
||||
|
||||
Правила:
|
||||
|
||||
- edit всегда ссылается на оригинальный блок соответствующего типа;
|
||||
- `toBlockchainName` в edit не хранится;
|
||||
- `textLen=0` означает логическое удаление содержимого;
|
||||
- тип исходной сущности edit не меняет.
|
||||
|
||||
### 3.1.6. Что считается комментарием
|
||||
|
||||
Комментарии не требуют нового формата.
|
||||
|
||||
Для обсуждения новых контентных сущностей продолжают использоваться уже существующие:
|
||||
|
||||
- `TEXT_REPLY`
|
||||
- `TEXT_EDIT_REPLY`
|
||||
|
||||
Это позволяет не ломать старую reply-механику и reuse текущую модель тредов.
|
||||
|
||||
## 4. Текстовые отзывы
|
||||
|
||||
## 4.1. Новый `type=6` — `TEXT_RATING`
|
||||
|
||||
Назначение:
|
||||
|
||||
- текстовая оценка / отзыв на объект;
|
||||
- без числовой шкалы;
|
||||
- с возможностью редактирования и логического удаления.
|
||||
|
||||
Смысл `TEXT_RATING`:
|
||||
|
||||
- это текст;
|
||||
- это специальный отзыв / мнение / оценка;
|
||||
- это явный сигнал, что перед нами не просто комментарий, а осмысленный отзыв;
|
||||
- в будущем это поле можно отдельно анализировать нейронками.
|
||||
|
||||
### 4.1.1. Подтипы
|
||||
|
||||
- `subType=10` — `TEXT_RATING_POST`
|
||||
- `subType=11` — `TEXT_RATING_EDIT`
|
||||
|
||||
### 4.1.2. Где разрешён `TEXT_RATING_POST`
|
||||
|
||||
Разрешён на target:
|
||||
|
||||
- `HEADER` пользователя;
|
||||
- контентный блок `type=5`;
|
||||
- при необходимости в будущем — на другие target-блоки по отдельному решению.
|
||||
|
||||
Сейчас в данном ТЗ:
|
||||
|
||||
- отзыв / оценка на пользователя — да;
|
||||
- отзыв / оценка на контент — да;
|
||||
- отзыв / лайк на канал целиком — не вводится, только оставляется как будущая возможность.
|
||||
|
||||
### 4.1.3. Где и как используется `TEXT_RATING_POST`
|
||||
|
||||
`TEXT_RATING_POST` можно создавать:
|
||||
|
||||
- как отзыв на контентный блок;
|
||||
- как отзыв на пользователя через target на `HEADER`;
|
||||
- как специальный ответ вместо обычного комментария.
|
||||
|
||||
Практическое правило для UI:
|
||||
|
||||
- при ответе на любое сообщение пользователь может выбрать:
|
||||
- обычный ответ;
|
||||
- `мнение / отзыв`.
|
||||
|
||||
### 4.1.4. Формат body
|
||||
|
||||
#### Создание
|
||||
|
||||
```text
|
||||
TextRatingBody_v1
|
||||
- toBlockchainNameLen: uint8
|
||||
- toBlockchainName UTF-8
|
||||
- toBlockGlobalNumber: int32
|
||||
- toBlockHash32: [32]
|
||||
- textLenBytes: uint16
|
||||
- text UTF-8
|
||||
```
|
||||
|
||||
#### Редактирование
|
||||
|
||||
```text
|
||||
TextRatingEditBody_v1
|
||||
- toBlockGlobalNumber: int32
|
||||
- toBlockHash32: [32]
|
||||
- textLenBytes: uint16
|
||||
- text UTF-8
|
||||
```
|
||||
|
||||
Правила:
|
||||
|
||||
- edit ссылается на оригинальный `TEXT_RATING_POST`;
|
||||
- пустой текст в edit означает логическое удаление отзыва.
|
||||
|
||||
## 5. Статусные действия и накопительные события
|
||||
|
||||
## 5.1. Новый `type=7` — `STATUS_ACTION`
|
||||
|
||||
Назначение:
|
||||
|
||||
- хранение действий пользователя по отношению к контенту;
|
||||
- вычисление текущего статуса;
|
||||
- накопительный учёт повторных прохождений;
|
||||
- подтверждение статусов другими людьми.
|
||||
|
||||
### 5.1.1. Подтипы
|
||||
|
||||
- `subType=10` — `STATUS_DONE_ONCE`
|
||||
- `subType=20` — `STATUS_INTERESTED`
|
||||
- `subType=30` — `STATUS_STARTED`
|
||||
- `subType=40` — `STATUS_COMPLETED`
|
||||
- `subType=50` — `STATUS_ABANDONED`
|
||||
- `subType=60` — `STATUS_CONFIRMED`
|
||||
|
||||
### 5.1.2. Матрица допустимости по контенту
|
||||
|
||||
`STATUS_DONE_ONCE` разрешён только для:
|
||||
|
||||
- `CONTENT_EXERCISE`
|
||||
- `CONTENT_SERVICE`
|
||||
|
||||
`STATUS_INTERESTED`, `STATUS_STARTED`, `STATUS_COMPLETED`, `STATUS_ABANDONED` разрешены только для:
|
||||
|
||||
- `CONTENT_EXERCISE`
|
||||
- `CONTENT_COURSE`
|
||||
|
||||
`CONTENT_ENTRYPOINT` не поддерживает:
|
||||
|
||||
- `interested`
|
||||
- `started`
|
||||
- `completed`
|
||||
- `abandoned`
|
||||
|
||||
### 5.1.3. Как считать текущее состояние
|
||||
|
||||
Для пары:
|
||||
|
||||
- `actorLogin`
|
||||
- `targetBlock`
|
||||
|
||||
актуальным статусом считается последнее по времени статусное событие из набора:
|
||||
|
||||
- `STATUS_INTERESTED`
|
||||
- `STATUS_STARTED`
|
||||
- `STATUS_COMPLETED`
|
||||
- `STATUS_ABANDONED`
|
||||
|
||||
Следствия:
|
||||
|
||||
- у одного пользователя по одному объекту в каждый момент времени только один актуальный статус;
|
||||
- если последним пришёл `interested`, статус считается “заинтересовался / рассматривает, но ещё не начал”;
|
||||
- если последним пришёл `started`, статус считается “в процессе”;
|
||||
- если последним пришёл `completed`, статус считается “завершён / освоен / знаю”;
|
||||
- если последним пришёл `abandoned`, статус считается “брошен”.
|
||||
|
||||
### 5.1.4. Как считать количество прохождений
|
||||
|
||||
`STATUS_DONE_ONCE` не меняет текущий статус.
|
||||
|
||||
Он считается отдельно как накопительное событие.
|
||||
|
||||
Сервер должен уметь считать:
|
||||
|
||||
- сколько раз пользователь сделал упражнение;
|
||||
- сколько раз пользователь прошёл услугу / процедуру.
|
||||
|
||||
### 5.1.5. Дополнительный текст действия
|
||||
|
||||
Каждое действие `STATUS_*` может содержать дополнительный текст-комментарий.
|
||||
|
||||
Примеры:
|
||||
|
||||
- как именно делал упражнение;
|
||||
- чем заинтересовал курс;
|
||||
- с какими мыслями начал курс;
|
||||
- почему бросил;
|
||||
- что именно подтверждает подтверждающий человек.
|
||||
|
||||
### 5.1.6. Подтверждение статуса
|
||||
|
||||
`STATUS_CONFIRMED` разрешён только на target-статусы:
|
||||
|
||||
- `STATUS_DONE_ONCE`
|
||||
- `STATUS_INTERESTED`
|
||||
- `STATUS_STARTED`
|
||||
- `STATUS_COMPLETED`
|
||||
- `STATUS_ABANDONED`
|
||||
|
||||
Это значит:
|
||||
|
||||
- подтверждение не ставится прямо на курс или упражнение;
|
||||
- подтверждение ставится на конкретный статусный блок другого человека.
|
||||
|
||||
Подтверждение:
|
||||
|
||||
- не меняет основной статус автора;
|
||||
- не меняет счётчик `done_once`;
|
||||
- хранится как отдельное мнение / свидетельство.
|
||||
|
||||
### 5.1.7. Формат body
|
||||
|
||||
Для `STATUS_DONE_ONCE`, `STATUS_INTERESTED`, `STATUS_STARTED`, `STATUS_COMPLETED`, `STATUS_ABANDONED`:
|
||||
|
||||
```text
|
||||
StatusActionBody_v1
|
||||
- toBlockchainNameLen: uint8
|
||||
- toBlockchainName UTF-8
|
||||
- toBlockGlobalNumber: int32
|
||||
- toBlockHash32: [32]
|
||||
- noteLenBytes: uint16
|
||||
- note UTF-8
|
||||
```
|
||||
|
||||
Для `STATUS_CONFIRMED`:
|
||||
|
||||
```text
|
||||
StatusConfirmBody_v1
|
||||
- toBlockchainNameLen: uint8
|
||||
- toBlockchainName UTF-8
|
||||
- toBlockGlobalNumber: int32
|
||||
- toBlockHash32: [32]
|
||||
- noteLenBytes: uint16
|
||||
- note UTF-8
|
||||
```
|
||||
|
||||
На уровне бинарного формата тело можно оставить одинаковым.
|
||||
Различие задаётся `subType` и правилами валидации target.
|
||||
|
||||
## 6. Community-события
|
||||
|
||||
## 6.1. Новый `type=8` — `COMMUNITY_EVENT`
|
||||
|
||||
Назначение:
|
||||
|
||||
- заявки в сообщество;
|
||||
- выход из сообщества;
|
||||
- принятие;
|
||||
- исключение.
|
||||
|
||||
### 6.1.1. Подтипы
|
||||
|
||||
- `subType=10` — `COMMUNITY_JOIN_REQUEST`
|
||||
- `subType=20` — `COMMUNITY_LEAVE`
|
||||
- `subType=30` — `COMMUNITY_ACCEPT`
|
||||
- `subType=40` — `COMMUNITY_REMOVE`
|
||||
|
||||
### 6.1.2. Базовая логика
|
||||
|
||||
`COMMUNITY_JOIN_REQUEST`
|
||||
|
||||
- создаёт пользователь;
|
||||
- target — `CONTENT_ENTRYPOINT` канала;
|
||||
- может содержать текст заявки.
|
||||
|
||||
`COMMUNITY_LEAVE`
|
||||
|
||||
- создаёт сам участник;
|
||||
- target — `CONTENT_ENTRYPOINT` канала;
|
||||
- подтверждение не требуется;
|
||||
- может содержать текст.
|
||||
|
||||
`COMMUNITY_ACCEPT`
|
||||
|
||||
- создаёт владелец канала;
|
||||
- target — конкретный блок `COMMUNITY_JOIN_REQUEST`;
|
||||
- может содержать текст.
|
||||
|
||||
`COMMUNITY_REMOVE`
|
||||
|
||||
- создаёт владелец канала;
|
||||
- target — `CONTENT_ENTRYPOINT` канала;
|
||||
- body дополнительно хранит `subjectLogin`, кого исключили;
|
||||
- может содержать текст.
|
||||
|
||||
### 6.1.3. Текущее членство
|
||||
|
||||
Пользователь считается текущим участником сообщества, если:
|
||||
|
||||
- у него есть хотя бы одно принятие в это сообщество;
|
||||
- после этого принятия нет более позднего:
|
||||
- `COMMUNITY_LEAVE`
|
||||
- `COMMUNITY_REMOVE`
|
||||
|
||||
Заявка сама по себе членство не создаёт.
|
||||
|
||||
### 6.1.4. Формат body
|
||||
|
||||
Для `JOIN_REQUEST` и `LEAVE`:
|
||||
|
||||
```text
|
||||
CommunityActionBody_v1
|
||||
- toBlockchainNameLen: uint8
|
||||
- toBlockchainName UTF-8
|
||||
- toBlockGlobalNumber: int32
|
||||
- toBlockHash32: [32]
|
||||
- noteLenBytes: uint16
|
||||
- note UTF-8
|
||||
```
|
||||
|
||||
Для `ACCEPT`:
|
||||
|
||||
```text
|
||||
CommunityAcceptBody_v1
|
||||
- toBlockchainNameLen: uint8
|
||||
- toBlockchainName UTF-8
|
||||
- toBlockGlobalNumber: int32
|
||||
- toBlockHash32: [32]
|
||||
- noteLenBytes: uint16
|
||||
- note UTF-8
|
||||
```
|
||||
|
||||
Для `REMOVE`:
|
||||
|
||||
```text
|
||||
CommunityRemoveBody_v1
|
||||
- toBlockchainNameLen: uint8
|
||||
- toBlockchainName UTF-8
|
||||
- toBlockGlobalNumber: int32
|
||||
- toBlockHash32: [32]
|
||||
- subjectLoginLen: uint8
|
||||
- subjectLogin ASCII
|
||||
- noteLenBytes: uint16
|
||||
- note UTF-8
|
||||
```
|
||||
|
||||
## 7. Что остаётся на старых типах
|
||||
|
||||
### 7.0. Обычный канал и лента достижений
|
||||
|
||||
На уровне продукта рекомендуется различать:
|
||||
|
||||
- обычный канал постов пользователя;
|
||||
- отдельную ленту его действий и достижений.
|
||||
|
||||
В обычном канале пользователь:
|
||||
|
||||
- пишет посты;
|
||||
- публикует материалы;
|
||||
- общается и обсуждает.
|
||||
|
||||
В ленте достижений видны события:
|
||||
|
||||
- какие упражнения он делал;
|
||||
- какие услуги / процедуры проходил;
|
||||
- какие курсы его заинтересовали;
|
||||
- какие курсы он начал;
|
||||
- какие курсы он завершил;
|
||||
- что он бросил.
|
||||
|
||||
В данном ТЗ эта модель фиксируется как продуктовая логика.
|
||||
Конкретный способ хранения можно реализовать:
|
||||
|
||||
- либо отдельным специальным каналом;
|
||||
- либо отдельным режимом чтения по статусным блокам.
|
||||
|
||||
### 7.1. Лайк пользователю
|
||||
|
||||
Лайк пользователю не вводится как `REACTION`.
|
||||
|
||||
Он остаётся в слое социальных связей:
|
||||
|
||||
- через `CONNECTION`
|
||||
- как будущий отдельный подтип связи
|
||||
|
||||
В этом ТЗ сам новый подтип связи не описывается детально.
|
||||
Нужно только зафиксировать правило:
|
||||
|
||||
- лайк человека относится к графу связей, а не к реакции на блок.
|
||||
|
||||
### 7.2. Лайк контента
|
||||
|
||||
Лайк на:
|
||||
|
||||
- `CONTENT_PLAIN`
|
||||
- `CONTENT_EXERCISE`
|
||||
- `CONTENT_SERVICE`
|
||||
- `CONTENT_COURSE`
|
||||
- `CONTENT_ENTRYPOINT`
|
||||
|
||||
может использовать уже существующий:
|
||||
|
||||
- `REACTION_LIKE`
|
||||
- `REACTION_UNLIKE`
|
||||
|
||||
Отдельный новый формат для лайка контента не нужен.
|
||||
|
||||
### 7.3. Канал целиком
|
||||
|
||||
В текущем ТЗ не вводятся:
|
||||
|
||||
- отзыв на канал целиком;
|
||||
- лайк канала целиком.
|
||||
|
||||
Это оставляется как будущая возможность.
|
||||
|
||||
### 7.4. Отзывы о людях
|
||||
|
||||
Отзывы о человеке как о человеке в текущем ТЗ допустимы через `TEXT_RATING` на `HEADER`.
|
||||
|
||||
Но продуктовую модель их показа нужно отдельно продумать.
|
||||
|
||||
Направление для будущего:
|
||||
|
||||
- просмотр отзывов о человеке на вкладке связей;
|
||||
- приоритетный вывод отзывов от близких друзей, родственников, друзей и контактов;
|
||||
- затем вывод остальных отзывов.
|
||||
|
||||
Эта тема полезна, но требует дополнительной осторожной проработки с точки зрения UX и социальных рисков.
|
||||
|
||||
## 8. Требования к серверу
|
||||
|
||||
Сервер после внедрения должен уметь:
|
||||
|
||||
1. Валидировать новые `type=5..8`.
|
||||
2. Хранить новые блоки без ломки старого чтения.
|
||||
3. Определять текущий статус пользователя по объекту:
|
||||
- `interested`
|
||||
- `started`
|
||||
- `completed`
|
||||
- `abandoned`
|
||||
4. Считать накопительные события `done_once` для:
|
||||
- `exercise`
|
||||
- `service`
|
||||
5. Считать подтверждения статусов.
|
||||
6. Определять единственный актуальный `entrypoint` канала.
|
||||
7. Определять текущее членство в сообществе канала.
|
||||
8. Поддерживать внутренние ссылки вида:
|
||||
- `SHiNE/<blockchainName>/<blockNumber>`
|
||||
- `SHiNE/<blockchainName>/<blockNumber>/<blockHash>`
|
||||
|
||||
## 9. Требования к UI
|
||||
|
||||
UI после внедрения должен уметь:
|
||||
|
||||
1. Показывать разные карточки для:
|
||||
- текста
|
||||
- упражнения
|
||||
- услуги
|
||||
- курса
|
||||
- entrypoint
|
||||
2. Показывать стартовую страницу канала, если `entrypoint` существует.
|
||||
3. Не показывать entrypoint, если он логически удалён.
|
||||
4. Давать человеку только допустимые действия по типу материала.
|
||||
5. Показывать:
|
||||
- текущий статус;
|
||||
- количество `done_once`;
|
||||
- подтверждения статуса.
|
||||
6. Показывать отдельные действия-кнопки на специальных блоках, например:
|
||||
- `Выполнил упражнение`
|
||||
- `Прошёл процедуру`
|
||||
- `Заинтересовало`
|
||||
- `Начал курс`
|
||||
- `Закончил курс`
|
||||
7. При ответе на сообщение давать выбор:
|
||||
- обычный ответ;
|
||||
- `мнение / отзыв`.
|
||||
8. Открывать внутренние ссылки SHiNE.
|
||||
|
||||
## 10. Вывод по совместимости
|
||||
|
||||
Предлагаемая модель реализуема без слома старого блокчейна.
|
||||
|
||||
Причина:
|
||||
|
||||
- старые `type=0..4` не меняются;
|
||||
- новые сущности вводятся только как новые `type=5..8`;
|
||||
- существующие `reply`, `like`, `edit`, `HEADER`, `CREATE_CHANNEL` и `CONNECTION` продолжают работать как раньше;
|
||||
- старые клиенты смогут игнорировать новые типы как неизвестные;
|
||||
- новые клиенты смогут постепенно включать поддержку нового функционала.
|
||||
|
||||
Итог:
|
||||
|
||||
- это расширение формата блокчейна;
|
||||
- это не миграция со сломом старых блоков;
|
||||
- это можно внедрять поэтапно.
|
||||
+2
-2
@@ -1,2 +1,2 @@
|
||||
client.version=1.2.327
|
||||
server.version=1.2.298
|
||||
client.version=1.2.331
|
||||
server.version=1.2.303
|
||||
|
||||
@@ -185,60 +185,6 @@ tasks.named('build') {
|
||||
finalizedBy tasks.named('integrationTest')
|
||||
}
|
||||
|
||||
tasks.register('deployServerProduction', JavaExec) {
|
||||
group = "!!deployment"
|
||||
description = "Production deploy: build → upload to shineup.me → restart service (только после явного подтверждения)"
|
||||
|
||||
classpath = sourceSets.test.runtimeClasspath
|
||||
mainClass = "test.it.IT_DeployRestartNoCleanNoTestsMain"
|
||||
workingDir = file('SHiNE-server')
|
||||
|
||||
dependsOn shadowJar
|
||||
systemProperty "it.remoteHost", System.getProperty("it.remoteHost", "shineup.me")
|
||||
systemProperty "it.remoteUser", System.getProperty("it.remoteUser", "player")
|
||||
systemProperty "it.remoteDir", System.getProperty("it.remoteDir", "/home/player/SHiNE/shine-server")
|
||||
systemProperty "it.service", System.getProperty("it.service", "shine-server")
|
||||
systemProperty "it.localJar", System.getProperty("it.localJar", "build/libs/shine-server.jar")
|
||||
|
||||
dependsOn testClasses
|
||||
}
|
||||
|
||||
tasks.register('deployUIProduction', Exec) {
|
||||
group = "!!deployment"
|
||||
description = "Production UI deploy: shineup.me (только после явного подтверждения)"
|
||||
workingDir = rootDir
|
||||
commandLine 'bash', file('deploy_shine-ui_production_shineupme.sh').absolutePath
|
||||
}
|
||||
|
||||
tasks.register('deployServer', Exec) {
|
||||
group = "!!deployment"
|
||||
description = "Default deploy server: server2.shineup.me"
|
||||
dependsOn shadowJar
|
||||
workingDir = rootDir
|
||||
environment 'LOCAL_JAR', file('SHiNE-server/build/libs/shine-server.jar').absolutePath
|
||||
commandLine 'bash', file('deploy_shine-server_test2.sh').absolutePath
|
||||
}
|
||||
|
||||
tasks.register('deployUI', Exec) {
|
||||
group = "!!deployment"
|
||||
description = "Default deploy UI: server2.shineup.me"
|
||||
workingDir = rootDir
|
||||
commandLine 'bash', file('deploy_shine-ui_production_server2shineupme.sh').absolutePath
|
||||
}
|
||||
|
||||
tasks.register('deployServerTest2') {
|
||||
group = "!!deployment"
|
||||
description = "Явный алиас второго production deploy server: server2.shineup.me"
|
||||
dependsOn tasks.named('deployServer')
|
||||
}
|
||||
|
||||
tasks.register('deployUITest2') {
|
||||
group = "!!deployment"
|
||||
description = "Явный алиас второго production deploy UI: server2.shineup.me"
|
||||
dependsOn tasks.named('deployUI')
|
||||
}
|
||||
|
||||
|
||||
tasks.register('startLocal', Exec) {
|
||||
group = "!!run"
|
||||
description = "Builds server, starts local WS server and local HTTP UI for end-to-end local testing"
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# AGENTS для deploy
|
||||
|
||||
## Главное
|
||||
|
||||
- Все вопросы деплоя SHiNE решать через эту папку `deploy/`.
|
||||
- Скрипты лежат в `deploy/scripts/`.
|
||||
- Production-серверы: `shineup.me` и `server2.shineup.me`.
|
||||
- Test/devnet серверы: `t1.shineup.me`, `t2.shineup.me`, `t3.shineup.me`, `t4.shineup.me`.
|
||||
- TURN-серверы: `turn1.shineup.me`, `turn2.shineup.me`, `turn3.shineup.me`.
|
||||
- В deploy-документах и скриптах использовать домены, а не IP.
|
||||
|
||||
## Production safety
|
||||
|
||||
- Любой deploy на `shineup.me` или `server2.shineup.me` выполнять только после отдельного явного подтверждения пользователя.
|
||||
- Перед production deploy обязательно проверить, что свежий бэкап лежит в `deploy/backup/archive/`.
|
||||
- Production wrappers требуют наличие `deploy/backup/archive/<date>/MANIFEST.txt`.
|
||||
- Секреты, `.env`, JWK, TURN shared secrets и приватные ключи не коммитить.
|
||||
|
||||
## Как деплоить
|
||||
|
||||
Сервер:
|
||||
|
||||
```bash
|
||||
bash deploy/scripts/<target>_server.sh
|
||||
```
|
||||
|
||||
UI:
|
||||
|
||||
```bash
|
||||
bash deploy/scripts/<target>_ui.sh
|
||||
```
|
||||
|
||||
Для настоящего `t2.shineup.me` использовать:
|
||||
|
||||
```bash
|
||||
bash deploy/scripts/test_t2_server.sh
|
||||
bash deploy/scripts/test_t2_ui.sh
|
||||
```
|
||||
|
||||
Не путать `server2.shineup.me` и `t2.shineup.me`.
|
||||
|
||||
## Документы
|
||||
|
||||
- `README.md` — карта deploy-папки.
|
||||
- `PRODUCTION_SERVERS.md` — production.
|
||||
- `TEST_SERVERS.md` — test/devnet.
|
||||
- `TURN_SERVERS.md` — TURN.
|
||||
- `CONFIGURE_TURN_IN_SHINE.md` — подключение TURN к SHiNE backend.
|
||||
- `SETUP_SERVER_FROM_ZERO.md` — сервер + Caddy + UI с нуля.
|
||||
- `SETUP_TURN_SERVER.md` — TURN с нуля.
|
||||
- `backup/README.md` — backup.
|
||||
|
||||
## Gradle
|
||||
|
||||
- Gradle deploy-задачи удалены.
|
||||
- Для сборки jar общий server deploy script вызывает `./gradlew shadowJar`.
|
||||
- Для локального запуска можно использовать `./gradlew startLocal`.
|
||||
@@ -1,17 +1,20 @@
|
||||
# Локальный деплой SHiNE-agent-bot-coder (systemd, пользователь ai)
|
||||
|
||||
## Где находится сервис
|
||||
|
||||
- Папка сервиса: `SHiNE-agent-bot-coder/`
|
||||
- Systemd unit: `SHiNE-agent-bot-coder/scripts/systemd/shine-agent-bot-coder.service`
|
||||
- Скрипт установки: `SHiNE-agent-bot-coder/scripts/systemd/install-local-systemd.sh`
|
||||
|
||||
## Предусловия
|
||||
|
||||
1. Заполнен `.env` на основе `.env.example`.
|
||||
2. Доступен рабочий Codex CLI:
|
||||
- `/home/ai/.cache/JetBrains/IntelliJIdea2026.1/aia/codex/bin/codex-x86_64-unknown-linux-musl`
|
||||
3. На машине установлен `systemd --user`.
|
||||
|
||||
## Установка
|
||||
|
||||
Из корня репозитория:
|
||||
|
||||
```bash
|
||||
@@ -19,18 +22,21 @@ bash SHiNE-agent-bot-coder/scripts/systemd/install-local-systemd.sh
|
||||
```
|
||||
|
||||
Скрипт:
|
||||
|
||||
1. проверяет наличие `python3`;
|
||||
2. копирует unit в `~/.config/systemd/user/`;
|
||||
3. делает `systemctl --user daemon-reload`;
|
||||
4. включает автозапуск и стартует сервис.
|
||||
|
||||
## Проверка
|
||||
|
||||
```bash
|
||||
systemctl --user status shine-agent-bot-coder --no-pager
|
||||
journalctl --user -u shine-agent-bot-coder -f
|
||||
```
|
||||
|
||||
## Перезапуск после изменений
|
||||
|
||||
```bash
|
||||
systemctl --user restart shine-agent-bot-coder
|
||||
```
|
||||
@@ -0,0 +1,60 @@
|
||||
# Подключение TURN к SHiNE-серверу
|
||||
|
||||
Клиент звонков запрашивает ICE-конфиг у backend через WS-операцию `GetCallIceConfig` и использует её для `RTCPeerConnection`.
|
||||
|
||||
## Репозиторный конфиг
|
||||
|
||||
В `SHiNE-server/src/main/resources/application.properties` можно хранить только публичные домены и пустые placeholders.
|
||||
|
||||
Пример:
|
||||
|
||||
```properties
|
||||
call.ice.stun.urls=stun:stun.l.google.com:19302
|
||||
call.ice.turn.urls=turn:turn1.shineup.me:3478?transport=udp,turn:turn1.shineup.me:3478?transport=tcp
|
||||
call.ice.turn.ttlSec=600
|
||||
call.ice.turn.userPrefix=shine
|
||||
call.ice.turn.sharedSecret=
|
||||
|
||||
call.ice.turn.servers.1.id=turn1
|
||||
call.ice.turn.servers.1.urls=turn:turn1.shineup.me:3478?transport=udp,turn:turn1.shineup.me:3478?transport=tcp
|
||||
call.ice.turn.servers.1.sharedSecret=
|
||||
```
|
||||
|
||||
## Production override
|
||||
|
||||
Реальные `sharedSecret`, статические логины/пароли и другие секреты задавать только на сервере через внешний `application.properties` или override-конфиг. В git их не хранить.
|
||||
|
||||
Рекомендуемый режим:
|
||||
|
||||
- на coturn включить `use-auth-secret`;
|
||||
- в coturn задать `static-auth-secret=<secret-on-server-only>`;
|
||||
- в SHiNE-сервере задать такой же `call.ice.turn.sharedSecret` или `call.ice.turn.servers.N.sharedSecret`;
|
||||
- сервер будет выдавать короткоживущие `turnUsername` / `turnPassword` с TTL.
|
||||
|
||||
Fallback-режим:
|
||||
|
||||
```properties
|
||||
call.ice.turn.sharedSecret=
|
||||
call.ice.turn.username=turn_user
|
||||
call.ice.turn.password=turn_password
|
||||
```
|
||||
|
||||
Fallback тоже не должен хранить реальные credentials в git.
|
||||
|
||||
## Деплой после изменения TURN-настроек
|
||||
|
||||
Для production сначала обновить бэкап в `deploy/backup/archive/`, затем выполнить нужный server deploy script:
|
||||
|
||||
```bash
|
||||
bash deploy/scripts/production_shineupme_server.sh
|
||||
```
|
||||
|
||||
Если менялись только серверные TURN-настройки во внешнем override-конфиге, достаточно перезапустить соответствующий `shine-server.service`.
|
||||
|
||||
## Проверка звонка
|
||||
|
||||
1. Авторизоваться двумя клиентами.
|
||||
2. Запустить звонок.
|
||||
3. Проверить, что звонок устанавливается в сети, где прямой P2P затруднён.
|
||||
4. В диагностике звонков смотреть `CallDeliveryReport`, `pcIceConnectionState`, `routeLabel`, `configuredTurnHosts*`, `reachableTurnHosts*`.
|
||||
5. Если TURN недоступен, клиент должен откатиться к STUN-конфигу по умолчанию.
|
||||
@@ -0,0 +1,59 @@
|
||||
# Production-серверы SHiNE
|
||||
|
||||
Production-контуров два. В документах и скриптах использовать домены, а не IP: физический VPS можно заменить без изменения deploy-логики.
|
||||
|
||||
## Основной production
|
||||
|
||||
- Домен: `shineup.me`
|
||||
- SSH: `player@shineup.me`
|
||||
- Логин сервера SHiNE: `shineupme`
|
||||
- Базовый каталог: `/home/player/SHiNE`
|
||||
- Сервер: `/home/player/SHiNE/shine-server/shine-server.jar`
|
||||
- UI: `/home/player/SHiNE/shine-ui`
|
||||
- Данные: `/home/player/SHiNE/shine-server/data/`
|
||||
- Логи: `/home/player/SHiNE/shine-server/logs/app.log`
|
||||
- systemd service: `shine-server.service`
|
||||
- Caddy site: `shineup.me`
|
||||
- WebSocket: `/ws` -> `127.0.0.1:7070`
|
||||
- Solana cluster: `mainnet-beta`
|
||||
|
||||
Deploy:
|
||||
|
||||
```bash
|
||||
bash deploy/scripts/production_shineupme_server.sh
|
||||
bash deploy/scripts/production_shineupme_ui.sh
|
||||
```
|
||||
|
||||
## Второй production
|
||||
|
||||
- Домен: `server2.shineup.me`
|
||||
- SSH: `player@server2.shineup.me`
|
||||
- Логин сервера SHiNE: `server2shineupme`
|
||||
- Базовый каталог: `/home/player/SHiNE`
|
||||
- Сервер: `/home/player/SHiNE/shine-server/shine-server.jar`
|
||||
- UI: `/home/player/SHiNE/shine-ui`
|
||||
- Данные: `/home/player/SHiNE/shine-server/data/`
|
||||
- Логи: `/home/player/SHiNE/shine-server/logs/app.log`
|
||||
- systemd service: `shine-server.service`
|
||||
- Caddy site: `server2.shineup.me`
|
||||
- WebSocket: `/ws` -> `127.0.0.1:7070`
|
||||
- Solana cluster: `mainnet-beta`
|
||||
|
||||
Deploy:
|
||||
|
||||
```bash
|
||||
bash deploy/scripts/production_server2_server.sh
|
||||
bash deploy/scripts/production_server2_ui.sh
|
||||
```
|
||||
|
||||
## Обязательное правило backup
|
||||
|
||||
Перед любым production deploy нужно обновить локальный бэкап в `deploy/backup/archive/`.
|
||||
|
||||
Production wrappers проверяют наличие `MANIFEST.txt` в `deploy/backup/archive/<date>/`. Если свежий бэкап не скопирован, deploy должен быть остановлен.
|
||||
|
||||
## Запрещено
|
||||
|
||||
- Не использовать IP как основной target deploy.
|
||||
- Не возвращать старые deploy-алиасы с названием `test2`.
|
||||
- Не считать `t2.shineup.me` вторым production: это test/devnet.
|
||||
@@ -0,0 +1,78 @@
|
||||
# Deploy SHiNE
|
||||
|
||||
Эта папка — единая точка входа по деплою SHiNE.
|
||||
|
||||
## Правило
|
||||
|
||||
- Все deploy-документы, deploy-скрипты и backup-инструкции держать здесь.
|
||||
- Deploy-привязки задавать через домены, а не через IP.
|
||||
- Production-серверы менять только после отдельного явного подтверждения пользователя.
|
||||
- Production deploy выполнять только после обновления локального бэкапа в `deploy/backup/archive/`.
|
||||
|
||||
## Контуры
|
||||
|
||||
- Основной production: `shineup.me`.
|
||||
- Второй production: `server2.shineup.me`.
|
||||
- Test/devnet стенд: `t1.shineup.me`, `t2.shineup.me`, `t3.shineup.me`, `t4.shineup.me`.
|
||||
|
||||
## Основные документы
|
||||
|
||||
- `PRODUCTION_SERVERS.md` — production-контуры.
|
||||
- `TEST_SERVERS.md` — test/devnet-контуры.
|
||||
- `TURN_SERVERS.md` — TURN-серверы.
|
||||
- `CONFIGURE_TURN_IN_SHINE.md` — как подключить TURN к SHiNE backend.
|
||||
- `SETUP_SERVER_FROM_ZERO.md` — настройка SHiNE-сервера и UI с нуля.
|
||||
- `SETUP_TURN_SERVER.md` — настройка TURN через Caddy/DNS/TLS.
|
||||
- `AGENT_BOT_CODER_LOCAL_SYSTEMD.md` — локальный systemd-deploy Telegram-бота агента-кодера.
|
||||
- `AGENTS.md` — правила для агента при деплое.
|
||||
|
||||
## Скрипты
|
||||
|
||||
Общие скрипты:
|
||||
|
||||
- `scripts/deploy_server.sh` — обновить существующий серверный jar и перезапустить systemd service.
|
||||
- `scripts/deploy_ui.sh` — обновить существующий UI, проверить Caddy root и подставить `deploy-config.js`.
|
||||
|
||||
Production wrappers:
|
||||
|
||||
- `scripts/production_shineupme_server.sh`
|
||||
- `scripts/production_shineupme_ui.sh`
|
||||
- `scripts/production_server2_server.sh`
|
||||
- `scripts/production_server2_ui.sh`
|
||||
|
||||
Test/devnet wrappers:
|
||||
|
||||
- `scripts/test_t1_server.sh`
|
||||
- `scripts/test_t1_ui.sh`
|
||||
- `scripts/test_t2_server.sh`
|
||||
- `scripts/test_t2_ui.sh`
|
||||
- `scripts/test_t3_server.sh`
|
||||
- `scripts/test_t3_ui.sh`
|
||||
- `scripts/test_t4_server.sh`
|
||||
- `scripts/test_t4_ui.sh`
|
||||
|
||||
## Примеры
|
||||
|
||||
UI на настоящий тестовый `t2.shineup.me`:
|
||||
|
||||
```bash
|
||||
bash deploy/scripts/test_t2_ui.sh
|
||||
```
|
||||
|
||||
Сервер на настоящий тестовый `t2.shineup.me`:
|
||||
|
||||
```bash
|
||||
bash deploy/scripts/test_t2_server.sh
|
||||
```
|
||||
|
||||
Production UI на `server2.shineup.me`:
|
||||
|
||||
```bash
|
||||
bash deploy/scripts/production_server2_ui.sh
|
||||
```
|
||||
|
||||
Production server на `shineup.me`:
|
||||
|
||||
```bash
|
||||
bash deploy/scripts/production_shineupme_server.sh
|
||||
```
|
||||
@@ -0,0 +1,113 @@
|
||||
# Настройка SHiNE-сервера с нуля
|
||||
|
||||
Инструкция описывает базовую подготовку существующего Linux/VPS-хоста под SHiNE server + UI. Привязка должна идти к домену, а не к IP.
|
||||
|
||||
## 1. DNS
|
||||
|
||||
Создать DNS-запись домена:
|
||||
|
||||
- production: `shineup.me` или `server2.shineup.me`;
|
||||
- test/devnet: `t1.shineup.me` ... `t4.shineup.me`.
|
||||
|
||||
После смены физического сервера достаточно обновить DNS.
|
||||
|
||||
## 2. Пользователь и пакеты
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install -y openjdk-17-jre-headless rsync caddy
|
||||
sudo useradd -m -s /bin/bash player || true
|
||||
```
|
||||
|
||||
## 3. Каталоги
|
||||
|
||||
Production:
|
||||
|
||||
```bash
|
||||
mkdir -p /home/player/SHiNE/shine-server/data
|
||||
mkdir -p /home/player/SHiNE/shine-server/logs
|
||||
mkdir -p /home/player/SHiNE/shine-ui
|
||||
```
|
||||
|
||||
Test/devnet:
|
||||
|
||||
```bash
|
||||
mkdir -p /home/player/tX/server/data
|
||||
mkdir -p /home/player/tX/server/logs
|
||||
mkdir -p /home/player/tX/UI
|
||||
```
|
||||
|
||||
## 4. application.properties
|
||||
|
||||
Каждый сервер должен иметь локальный внешний конфиг в рабочей директории сервиса.
|
||||
|
||||
Production пример:
|
||||
|
||||
```properties
|
||||
server.port=7070
|
||||
server.SHiNE.login=shineupme
|
||||
db.path=data/shine.sqlite
|
||||
server.ui.indexPath=/home/player/SHiNE/shine-ui/index.html
|
||||
server.info.url=https://shineup.me
|
||||
server.info.origin=production
|
||||
solana.cluster=mainnet-beta
|
||||
```
|
||||
|
||||
Test/devnet пример:
|
||||
|
||||
```properties
|
||||
server.port=7102
|
||||
server.SHiNE.login=server_t2
|
||||
db.path=data/shine.sqlite
|
||||
server.ui.indexPath=/home/player/t2/UI/index.html
|
||||
server.info.url=https://t2.shineup.me
|
||||
server.info.origin=devnet
|
||||
solana.cluster=devnet
|
||||
solana.rpcUrl=https://api.devnet.solana.com
|
||||
```
|
||||
|
||||
## 5. Caddy
|
||||
|
||||
Минимальный site block:
|
||||
|
||||
```caddyfile
|
||||
t2.shineup.me {
|
||||
encode zstd gzip
|
||||
|
||||
@ws path /ws /ws/*
|
||||
handle @ws {
|
||||
reverse_proxy 127.0.0.1:7102
|
||||
}
|
||||
|
||||
handle {
|
||||
root * /home/player/t2/UI
|
||||
try_files {path} /index.html
|
||||
file_server
|
||||
header -Etag
|
||||
header {
|
||||
Cache-Control "no-store, no-cache, must-revalidate, max-age=0"
|
||||
Pragma "no-cache"
|
||||
Expires "0"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
sudo caddy validate --config /etc/caddy/Caddyfile
|
||||
sudo systemctl restart caddy
|
||||
curl -I https://t2.shineup.me
|
||||
```
|
||||
|
||||
## 6. Первый deploy
|
||||
|
||||
Из репозитория:
|
||||
|
||||
```bash
|
||||
bash deploy/scripts/test_t2_server.sh
|
||||
bash deploy/scripts/test_t2_ui.sh
|
||||
```
|
||||
|
||||
Для production перед этими командами обязательно обновить `deploy/backup/archive/`.
|
||||
@@ -0,0 +1,85 @@
|
||||
# Настройка TURN-сервера
|
||||
|
||||
TURN-хосты SHiNE должны использовать домены:
|
||||
|
||||
- `turn1.shineup.me`
|
||||
- `turn2.shineup.me`
|
||||
- `turn3.shineup.me`
|
||||
|
||||
IP не фиксировать в документах и скриптах как основной идентификатор.
|
||||
|
||||
## 1. DNS
|
||||
|
||||
Создать или обновить DNS `A/AAAA` для нужного `turnX.shineup.me`.
|
||||
|
||||
## 2. Пакеты
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install -y coturn caddy
|
||||
```
|
||||
|
||||
Можно использовать вспомогательный скрипт на самом TURN-хосте:
|
||||
|
||||
```bash
|
||||
sudo bash deploy/scripts/setup_turn_coturn.sh --realm turn1.shineup.me --secret CHANGE_ME_LONG_RANDOM_SECRET
|
||||
```
|
||||
|
||||
## 3. coturn
|
||||
|
||||
Секреты не хранить в git. Настраивать на сервере через `/etc/turnserver.conf` или отдельный secret/override.
|
||||
|
||||
Минимальная схема:
|
||||
|
||||
```conf
|
||||
listening-port=3478
|
||||
tls-listening-port=5349
|
||||
fingerprint
|
||||
lt-cred-mech
|
||||
realm=turn1.shineup.me
|
||||
server-name=turn1.shineup.me
|
||||
use-auth-secret
|
||||
static-auth-secret=<secret-on-server-only>
|
||||
no-multicast-peers
|
||||
no-cli
|
||||
```
|
||||
|
||||
Включить сервис:
|
||||
|
||||
```bash
|
||||
sudo systemctl enable coturn
|
||||
sudo systemctl restart coturn
|
||||
```
|
||||
|
||||
## 4. Caddy
|
||||
|
||||
Caddy можно использовать для HTTPS health/check endpoint и автоматического TLS на домене.
|
||||
|
||||
Пример:
|
||||
|
||||
```caddyfile
|
||||
turn1.shineup.me {
|
||||
respond /health "ok" 200
|
||||
}
|
||||
```
|
||||
|
||||
Проверка:
|
||||
|
||||
```bash
|
||||
sudo caddy validate --config /etc/caddy/Caddyfile
|
||||
sudo systemctl restart caddy
|
||||
curl -I https://turn1.shineup.me/health
|
||||
```
|
||||
|
||||
## 5. Проверка портов
|
||||
|
||||
```bash
|
||||
sudo systemctl --no-pager --full status coturn caddy
|
||||
sudo ss -lntup | grep -E ':(3478|5349)'
|
||||
```
|
||||
|
||||
## 6. Важное
|
||||
|
||||
- TURN credentials и shared secret не коммитить.
|
||||
- Если TURN домен переносится на другой VPS, менять DNS, а не код приложения.
|
||||
- После изменения TURN-адресов обновить серверные/UI настройки, где они используются.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Test/devnet серверы SHiNE
|
||||
|
||||
Отдельный test/devnet стенд состоит из четырёх независимых SHiNE-инстансов:
|
||||
|
||||
- `t1.shineup.me`
|
||||
- `t2.shineup.me`
|
||||
- `t3.shineup.me`
|
||||
- `t4.shineup.me`
|
||||
|
||||
Это не production. Все deploy-цели задаются через домены.
|
||||
|
||||
## Общие параметры
|
||||
|
||||
- SSH: `player@tX.shineup.me`
|
||||
- Solana cluster: `devnet`
|
||||
- Solana RPC: `https://api.devnet.solana.com`
|
||||
- Caddy config: `/etc/caddy/Caddyfile`
|
||||
|
||||
## Инстансы
|
||||
|
||||
| Домен | Логин сервера | Сервер | UI | Порт | systemd |
|
||||
|---|---|---|---|---|---|
|
||||
| `t1.shineup.me` | `server_t1` | `/home/player/t1/server` | `/home/player/t1/UI` | `7101` | `shine-t1.service` |
|
||||
| `t2.shineup.me` | `server_t2` | `/home/player/t2/server` | `/home/player/t2/UI` | `7102` | `shine-t2.service` |
|
||||
| `t3.shineup.me` | `server_t3` | `/home/player/t3/server` | `/home/player/t3/UI` | `7103` | `shine-t3.service` |
|
||||
| `t4.shineup.me` | `server_t4` | `/home/player/t4/server` | `/home/player/t4/UI` | `7104` | `shine-t4.service` |
|
||||
|
||||
## Deploy
|
||||
|
||||
```bash
|
||||
bash deploy/scripts/test_t1_server.sh
|
||||
bash deploy/scripts/test_t1_ui.sh
|
||||
|
||||
bash deploy/scripts/test_t2_server.sh
|
||||
bash deploy/scripts/test_t2_ui.sh
|
||||
|
||||
bash deploy/scripts/test_t3_server.sh
|
||||
bash deploy/scripts/test_t3_ui.sh
|
||||
|
||||
bash deploy/scripts/test_t4_server.sh
|
||||
bash deploy/scripts/test_t4_ui.sh
|
||||
```
|
||||
|
||||
## Проверка
|
||||
|
||||
```bash
|
||||
curl -I https://t1.shineup.me
|
||||
curl -I https://t2.shineup.me
|
||||
curl -I https://t3.shineup.me
|
||||
curl -I https://t4.shineup.me
|
||||
```
|
||||
|
||||
На сервере:
|
||||
|
||||
```bash
|
||||
sudo systemctl --no-pager --full status caddy shine-t1 shine-t2 shine-t3 shine-t4
|
||||
```
|
||||
|
||||
## Operational-нюанс
|
||||
|
||||
Официальный `https://api.devnet.solana.com` может отдавать `HTTP 429`, если несколько инстансов одновременно делают bootstrap PDA/sync-серверов. Если после деплоя какой-то инстанс не подтянул `sync_servers`, рестартовать сервисы по одному с паузой.
|
||||
@@ -0,0 +1,40 @@
|
||||
# TURN-серверы SHiNE
|
||||
|
||||
TURN-серверы использовать и документировать через домены, а не через IP.
|
||||
|
||||
## Текущие домены
|
||||
|
||||
- `turn1.shineup.me`
|
||||
- `turn2.shineup.me`
|
||||
- `turn3.shineup.me`
|
||||
|
||||
## Назначение
|
||||
|
||||
TURN нужен для WebRTC-звонков, когда прямое peer-to-peer соединение невозможно из-за NAT/firewall.
|
||||
|
||||
## Базовые ожидания
|
||||
|
||||
- DNS каждого `turnX.shineup.me` указывает на актуальный физический сервер TURN.
|
||||
- TLS/HTTPS и вспомогательная маршрутизация обслуживаются через Caddy, если на сервере есть web endpoint для проверки.
|
||||
- `coturn` слушает TURN/STUN порты согласно локальному `turnserver.conf`.
|
||||
- Секреты TURN не хранить в git.
|
||||
|
||||
## Что проверять
|
||||
|
||||
```bash
|
||||
dig +short turn1.shineup.me
|
||||
dig +short turn2.shineup.me
|
||||
dig +short turn3.shineup.me
|
||||
```
|
||||
|
||||
На каждом TURN-хосте:
|
||||
|
||||
```bash
|
||||
sudo systemctl --no-pager --full status coturn caddy
|
||||
sudo ss -lntup | grep -E ':(3478|5349)'
|
||||
```
|
||||
|
||||
## Где настраивать
|
||||
|
||||
- TURN-хост с нуля: `SETUP_TURN_SERVER.md`.
|
||||
- Подключение TURN к SHiNE backend: `CONFIGURE_TURN_IN_SHINE.md`.
|
||||
@@ -1,7 +1,7 @@
|
||||
# AGENTS для server-backup
|
||||
# AGENTS для deploy/backup
|
||||
|
||||
## Назначение
|
||||
- Папка `server-backup/` хранит:
|
||||
- Папка `deploy/backup/` хранит:
|
||||
- тяжёлые локальные бэкапы сервера (НЕ в git);
|
||||
- лёгкую схему восстановления (в git), чтобы можно было поднять сервер даже без полного архива.
|
||||
|
||||
@@ -11,19 +11,20 @@
|
||||
- `backup-version.properties` — версия контура бэкапа.
|
||||
|
||||
## Правила
|
||||
- Полный бэкап складывать только в `server-backup/archive/`.
|
||||
- `server-backup/archive/**` не коммитить.
|
||||
- Полный бэкап складывать только в `deploy/backup/archive/`.
|
||||
- `deploy/backup/archive/**` не коммитить, кроме `.gitkeep`.
|
||||
- Перед production deploy проверить, что свежий бэкап скопирован в `deploy/backup/archive/<дата>/` и есть `MANIFEST.txt`.
|
||||
- Любое изменение схемы восстановления фиксировать в git.
|
||||
- После обновления схемы увеличивать `backup.schema.version`.
|
||||
- После нового полного бэкапа увеличивать `backup.full.version`.
|
||||
|
||||
## Как обновлять бэкап
|
||||
1. Обновить схему:
|
||||
- `bash server-backup/scheme/shineup.me/scripts/refresh_scheme.sh`
|
||||
- `bash deploy/backup/scheme/shineup.me/scripts/refresh_scheme.sh`
|
||||
2. Сделать новый полный бэкап:
|
||||
- `bash server-backup/scheme/shineup.me/scripts/backup_full.sh`
|
||||
3. Проверить `server-backup/archive/<дата>/MANIFEST.txt`.
|
||||
4. Поднять версии в `server-backup/backup-version.properties`.
|
||||
- `bash deploy/backup/scheme/shineup.me/scripts/backup_full.sh`
|
||||
3. Проверить `deploy/backup/archive/<дата>/MANIFEST.txt`.
|
||||
4. Поднять версии в `deploy/backup/backup-version.properties`.
|
||||
|
||||
## Как восстанавливать
|
||||
- Смотреть `server-backup/scheme/shineup.me/docs/RESTORE.md`.
|
||||
- Смотреть `deploy/backup/scheme/shineup.me/docs/RESTORE.md`.
|
||||
@@ -0,0 +1,9 @@
|
||||
# deploy/backup
|
||||
|
||||
- `archive/` — локальные полные бэкапы по датам (не коммитятся, кроме `.gitkeep`).
|
||||
- `scheme/` — лёгкая схема восстановления (коммитится).
|
||||
- `backup-version.properties` — версии схемы и полного бэкапа.
|
||||
|
||||
Production deploy требует, чтобы перед запуском свежий бэкап был скопирован в `deploy/backup/archive/<date>/` и содержал `MANIFEST.txt`.
|
||||
|
||||
Основной сервер-источник: `shineup.me`.
|
||||
+1
-1
@@ -706,4 +706,4 @@ syslog
|
||||
#no-tlsv1
|
||||
#no-tlsv1_1
|
||||
#no-tlsv1_2
|
||||
static-auth-secret=def6d444734d380d2f67a9d345b1debf985eaba0973c343e392c060d97c30106
|
||||
static-auth-secret=<secret-on-server-only>
|
||||
+9
-9
@@ -12,18 +12,18 @@ sudo apt install -y rsync caddy coturn docker.io
|
||||
```
|
||||
|
||||
## 3. Восстановление файлов из полного бэкапа
|
||||
Предполагается, что полный бэкап лежит локально в `server-backup/archive/YYYY-MM-DD/`.
|
||||
Предполагается, что полный бэкап лежит локально в `deploy/backup/archive/YYYY-MM-DD/`.
|
||||
|
||||
```bash
|
||||
rsync -a server-backup/archive/YYYY-MM-DD/home-player/SHiNE/ player@NEW_SERVER:/home/player/SHiNE/
|
||||
rsync -a server-backup/archive/YYYY-MM-DD/home-player/sites/ player@NEW_SERVER:/home/player/sites/
|
||||
rsync -a server-backup/archive/YYYY-MM-DD/home-player/gitea/ player@NEW_SERVER:/home/player/gitea/
|
||||
rsync -a server-backup/archive/YYYY-MM-DD/home-player/agent-memory/ player@NEW_SERVER:/home/player/agent-memory/
|
||||
rsync -a deploy/backup/archive/YYYY-MM-DD/home-player/SHiNE/ player@NEW_SERVER:/home/player/SHiNE/
|
||||
rsync -a deploy/backup/archive/YYYY-MM-DD/home-player/sites/ player@NEW_SERVER:/home/player/sites/
|
||||
rsync -a deploy/backup/archive/YYYY-MM-DD/home-player/gitea/ player@NEW_SERVER:/home/player/gitea/
|
||||
rsync -a deploy/backup/archive/YYYY-MM-DD/home-player/agent-memory/ player@NEW_SERVER:/home/player/agent-memory/
|
||||
|
||||
rsync -a server-backup/archive/YYYY-MM-DD/etc-system/caddy/ player@NEW_SERVER:/tmp/restore-caddy/
|
||||
rsync -a server-backup/archive/YYYY-MM-DD/var-lib/caddy/ player@NEW_SERVER:/tmp/restore-var-lib-caddy/
|
||||
rsync -a server-backup/archive/YYYY-MM-DD/etc-system/turnserver.conf player@NEW_SERVER:/tmp/turnserver.conf
|
||||
rsync -a server-backup/archive/YYYY-MM-DD/etc-system/*.service player@NEW_SERVER:/tmp/
|
||||
rsync -a deploy/backup/archive/YYYY-MM-DD/etc-system/caddy/ player@NEW_SERVER:/tmp/restore-caddy/
|
||||
rsync -a deploy/backup/archive/YYYY-MM-DD/var-lib/caddy/ player@NEW_SERVER:/tmp/restore-var-lib-caddy/
|
||||
rsync -a deploy/backup/archive/YYYY-MM-DD/etc-system/turnserver.conf player@NEW_SERVER:/tmp/turnserver.conf
|
||||
rsync -a deploy/backup/archive/YYYY-MM-DD/etc-system/*.service player@NEW_SERVER:/tmp/
|
||||
```
|
||||
|
||||
Далее на новом сервере:
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
REMOTE_HOST="${REMOTE_HOST:?REMOTE_HOST is required, example: player@shineup.me}"
|
||||
TARGET_DOMAIN="${TARGET_DOMAIN:?TARGET_DOMAIN is required, example: shineup.me}"
|
||||
REMOTE_SERVER_DIR="${REMOTE_SERVER_DIR:?REMOTE_SERVER_DIR is required}"
|
||||
REMOTE_LOGS_DIR="${REMOTE_LOGS_DIR:-$REMOTE_SERVER_DIR/logs}"
|
||||
REMOTE_DATA_DIR="${REMOTE_DATA_DIR:-$REMOTE_SERVER_DIR/data}"
|
||||
REMOTE_SERVICE_NAME="${REMOTE_SERVICE_NAME:?REMOTE_SERVICE_NAME is required}"
|
||||
SERVER_PORT="${SERVER_PORT:?SERVER_PORT is required}"
|
||||
LOCAL_JAR="${LOCAL_JAR:-$ROOT_DIR/SHiNE-server/build/libs/shine-server.jar}"
|
||||
BUILD_JAR="${BUILD_JAR:-1}"
|
||||
REQUIRE_BACKUP="${REQUIRE_BACKUP:-0}"
|
||||
BACKUP_DIR="${BACKUP_DIR:-$ROOT_DIR/deploy/backup/archive}"
|
||||
|
||||
if [[ "$REQUIRE_BACKUP" == "1" ]]; then
|
||||
if ! find "$BACKUP_DIR" -mindepth 2 -maxdepth 2 -name MANIFEST.txt -print -quit 2>/dev/null | grep -q .; then
|
||||
echo "ERROR: для production deploy сначала положите свежий бэкап в $BACKUP_DIR/<date>/MANIFEST.txt" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$BUILD_JAR" == "1" ]]; then
|
||||
echo "==> Building server jar"
|
||||
(cd "$ROOT_DIR" && ./gradlew shadowJar)
|
||||
fi
|
||||
|
||||
if [[ ! -f "$LOCAL_JAR" ]]; then
|
||||
echo "ERROR: локальный jar не найден: $LOCAL_JAR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "==> Deploy server to $TARGET_DOMAIN ($REMOTE_HOST)"
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=20 "$REMOTE_HOST" "echo SSH OK" >/dev/null
|
||||
ssh "$REMOTE_HOST" "sudo -n true"
|
||||
ssh "$REMOTE_HOST" "java -version >/dev/null 2>&1"
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
cleanup() {
|
||||
rm -rf "$TMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
cat >"$TMP_DIR/$REMOTE_SERVICE_NAME.service" <<EOF
|
||||
[Unit]
|
||||
Description=SHiNE Server ($TARGET_DOMAIN)
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=player
|
||||
Group=player
|
||||
WorkingDirectory=$REMOTE_SERVER_DIR
|
||||
ExecStart=/usr/bin/java -Dserver.port=$SERVER_PORT -jar $REMOTE_SERVER_DIR/shine-server.jar
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
StandardOutput=append:$REMOTE_LOGS_DIR/app.log
|
||||
StandardError=append:$REMOTE_LOGS_DIR/app.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
ssh "$REMOTE_HOST" "mkdir -p '$REMOTE_SERVER_DIR' '$REMOTE_DATA_DIR' '$REMOTE_LOGS_DIR'"
|
||||
rsync -az --timeout=120 "$LOCAL_JAR" "$REMOTE_HOST:$REMOTE_SERVER_DIR/shine-server.jar"
|
||||
rsync -az "$TMP_DIR/$REMOTE_SERVICE_NAME.service" "$REMOTE_HOST:/tmp/$REMOTE_SERVICE_NAME.service"
|
||||
|
||||
ssh "$REMOTE_HOST" "set -euo pipefail; \
|
||||
sudo mv -f '/tmp/$REMOTE_SERVICE_NAME.service' '/etc/systemd/system/$REMOTE_SERVICE_NAME.service'; \
|
||||
sudo chown root:root '/etc/systemd/system/$REMOTE_SERVICE_NAME.service'; \
|
||||
sudo chown -R player:player '$REMOTE_SERVER_DIR'; \
|
||||
touch '$REMOTE_LOGS_DIR/app.log'; \
|
||||
sudo systemctl daemon-reload; \
|
||||
sudo systemctl enable '$REMOTE_SERVICE_NAME'; \
|
||||
sudo systemctl restart '$REMOTE_SERVICE_NAME'; \
|
||||
sudo systemctl --no-pager --full status '$REMOTE_SERVICE_NAME' >/dev/null"
|
||||
|
||||
echo "Всё хорошо: сервер $TARGET_DOMAIN обновлён и сервис $REMOTE_SERVICE_NAME перезапущен"
|
||||
@@ -1,16 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SRC_DIR="shine-UI"
|
||||
REMOTE_HOST="${REMOTE_HOST:-player@shineup.me}"
|
||||
REMOTE_UI_DIR="${REMOTE_UI_DIR:-/home/player/SHiNE/shine-ui}"
|
||||
EXPECTED_CADDY_UI_ROOT="${EXPECTED_CADDY_UI_ROOT:-/home/player/SHiNE/shine-ui}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
SRC_DIR="$ROOT_DIR/shine-UI"
|
||||
REMOTE_HOST="${REMOTE_HOST:?REMOTE_HOST is required, example: player@shineup.me}"
|
||||
REMOTE_UI_DIR="${REMOTE_UI_DIR:?REMOTE_UI_DIR is required}"
|
||||
EXPECTED_CADDY_UI_ROOT="${EXPECTED_CADDY_UI_ROOT:-$REMOTE_UI_DIR}"
|
||||
ALLOW_CADDY_MISMATCH="${ALLOW_CADDY_MISMATCH:-0}"
|
||||
EXPECTED_CADDY_SITE="${EXPECTED_CADDY_SITE:-shineup.me}"
|
||||
EXPECTED_CADDY_SITE="${EXPECTED_CADDY_SITE:?EXPECTED_CADDY_SITE is required}"
|
||||
TARGET_URL="${TARGET_URL:-https://${EXPECTED_CADDY_SITE}}"
|
||||
DEPLOY_SERVER_LOGIN="${DEPLOY_SERVER_LOGIN:-}"
|
||||
DEPLOY_SERVER_ADDRESS="${DEPLOY_SERVER_ADDRESS:-}"
|
||||
DEPLOY_SOLANA_CLUSTER="${DEPLOY_SOLANA_CLUSTER:-}"
|
||||
DEPLOY_SOLANA_ENDPOINT="${DEPLOY_SOLANA_ENDPOINT:-}"
|
||||
REQUIRE_BACKUP="${REQUIRE_BACKUP:-0}"
|
||||
BACKUP_DIR="${BACKUP_DIR:-$ROOT_DIR/deploy/backup/archive}"
|
||||
VERSION_FILE="$ROOT_DIR/VERSION.properties"
|
||||
BUILD_VERSION="$(date -u +%Y%m%d%H%M%S)"
|
||||
VERSION_FILE="VERSION.properties"
|
||||
export BUILD_VERSION
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
|
||||
if [[ "$REQUIRE_BACKUP" == "1" ]]; then
|
||||
if ! find "$BACKUP_DIR" -mindepth 2 -maxdepth 2 -name MANIFEST.txt -print -quit 2>/dev/null | grep -q .; then
|
||||
echo "ERROR: для production deploy сначала положите свежий бэкап в $BACKUP_DIR/<date>/MANIFEST.txt" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -f "$VERSION_FILE" ]]; then
|
||||
echo "ERROR: version file not found: $VERSION_FILE" >&2
|
||||
@@ -24,18 +40,12 @@ if [[ -z "$CLIENT_VERSION" ]]; then
|
||||
fi
|
||||
export CLIENT_VERSION
|
||||
|
||||
TARGET_URL="${TARGET_URL:-https://${EXPECTED_CADDY_SITE}}"
|
||||
REMOTE_DIR="${REMOTE_UI_DIR}"
|
||||
DEPLOY_SERVER_LOGIN="${DEPLOY_SERVER_LOGIN:-}"
|
||||
DEPLOY_SERVER_ADDRESS="${DEPLOY_SERVER_ADDRESS:-}"
|
||||
DEPLOY_SOLANA_CLUSTER="${DEPLOY_SOLANA_CLUSTER:-}"
|
||||
DEPLOY_SOLANA_ENDPOINT="${DEPLOY_SOLANA_ENDPOINT:-}"
|
||||
DEPLOY_SOLANA_CLUSTER_NORMALIZED="$DEPLOY_SOLANA_CLUSTER"
|
||||
|
||||
if [[ "$DEPLOY_SOLANA_CLUSTER_NORMALIZED" == "mainnet" ]]; then
|
||||
DEPLOY_SOLANA_CLUSTER_NORMALIZED="mainnet-beta"
|
||||
fi
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
cleanup() {
|
||||
rm -rf "$TMP_DIR"
|
||||
}
|
||||
@@ -48,7 +58,7 @@ fi
|
||||
|
||||
echo "==> Preparing staged UI copy with build version: $BUILD_VERSION"
|
||||
echo "==> Client version from $VERSION_FILE: $CLIENT_VERSION"
|
||||
echo "==> Deploy target: $TARGET_URL ($REMOTE_DIR)"
|
||||
echo "==> Deploy target: $TARGET_URL ($REMOTE_UI_DIR)"
|
||||
rsync -a "$SRC_DIR"/ "$TMP_DIR"/
|
||||
|
||||
DEPLOY_CONFIG_FILE="$TMP_DIR/js/deploy-config.js"
|
||||
@@ -156,12 +166,14 @@ elif [[ "$ROOT_CHECK_OUTPUT" != *"$EXPECTED_CADDY_UI_ROOT"* ]]; then
|
||||
echo "WARN: proceeding due to ALLOW_CADDY_MISMATCH=1"
|
||||
fi
|
||||
|
||||
echo "==> Preparing remote directory: $REMOTE_DIR"
|
||||
ssh "$REMOTE_HOST" "sudo mkdir -p '$REMOTE_DIR'"
|
||||
echo "==> Preparing remote directory: $REMOTE_UI_DIR"
|
||||
ssh "$REMOTE_HOST" "sudo mkdir -p '$REMOTE_UI_DIR'"
|
||||
|
||||
echo "==> Syncing staged files to $REMOTE_DIR"
|
||||
echo "==> Syncing staged files to $REMOTE_UI_DIR"
|
||||
rsync -rlvz --delete --omit-dir-times --no-perms --no-owner --no-group \
|
||||
--rsync-path="sudo rsync" \
|
||||
"$TMP_DIR"/ "$REMOTE_HOST":"$REMOTE_DIR"/
|
||||
"$TMP_DIR"/ "$REMOTE_HOST":"$REMOTE_UI_DIR"/
|
||||
|
||||
ssh "$REMOTE_HOST" "sudo find '$REMOTE_UI_DIR' -type d -exec chmod o+rx {} +; sudo find '$REMOTE_UI_DIR' -type f -exec chmod o+r {} +"
|
||||
|
||||
echo "Всё хорошо: $TARGET_URL"
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REMOTE_HOST="player@server2.shineup.me" \
|
||||
TARGET_DOMAIN="server2.shineup.me" \
|
||||
REMOTE_SERVER_DIR="/home/player/SHiNE/shine-server" \
|
||||
REMOTE_SERVICE_NAME="shine-server" \
|
||||
SERVER_PORT="7070" \
|
||||
REQUIRE_BACKUP="1" \
|
||||
bash "$(dirname "$0")/deploy_server.sh"
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REMOTE_HOST="player@server2.shineup.me" \
|
||||
REMOTE_UI_DIR="/home/player/SHiNE/shine-ui" \
|
||||
EXPECTED_CADDY_UI_ROOT="/home/player/SHiNE/shine-ui" \
|
||||
EXPECTED_CADDY_SITE="server2.shineup.me" \
|
||||
DEPLOY_SERVER_LOGIN="server2shineupme" \
|
||||
DEPLOY_SERVER_ADDRESS="server2.shineup.me" \
|
||||
DEPLOY_SOLANA_CLUSTER="mainnet" \
|
||||
DEPLOY_SOLANA_ENDPOINT="https://solana-rpc.publicnode.com" \
|
||||
TARGET_URL="https://server2.shineup.me" \
|
||||
REQUIRE_BACKUP="1" \
|
||||
bash "$(dirname "$0")/deploy_ui.sh"
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REMOTE_HOST="player@shineup.me" \
|
||||
TARGET_DOMAIN="shineup.me" \
|
||||
REMOTE_SERVER_DIR="/home/player/SHiNE/shine-server" \
|
||||
REMOTE_SERVICE_NAME="shine-server" \
|
||||
SERVER_PORT="7070" \
|
||||
REQUIRE_BACKUP="1" \
|
||||
bash "$(dirname "$0")/deploy_server.sh"
|
||||
Regular → Executable
+2
-1
@@ -10,4 +10,5 @@ DEPLOY_SERVER_ADDRESS="shineup.me" \
|
||||
DEPLOY_SOLANA_CLUSTER="mainnet" \
|
||||
DEPLOY_SOLANA_ENDPOINT="https://solana-rpc.publicnode.com" \
|
||||
TARGET_URL="https://shineup.me" \
|
||||
bash "$(dirname "$0")/deploy_shine-PWA.sh"
|
||||
REQUIRE_BACKUP="1" \
|
||||
bash "$(dirname "$0")/deploy_ui.sh"
|
||||
@@ -5,10 +5,10 @@ set -euo pipefail
|
||||
# Запускать НА TURN-сервере под root.
|
||||
#
|
||||
# Пример:
|
||||
# sudo bash scripts/setup_turn_coturn.sh --secret "CHANGE_ME_LONG_SECRET" --realm "shineup.me"
|
||||
# sudo bash deploy/scripts/setup_turn_coturn.sh --secret "CHANGE_ME_LONG_SECRET" --realm "turn1.shineup.me"
|
||||
|
||||
SECRET=""
|
||||
REALM="shineup.me"
|
||||
REALM="turn1.shineup.me"
|
||||
MIN_PORT="49160"
|
||||
MAX_PORT="49200"
|
||||
|
||||
@@ -46,9 +46,12 @@ export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -y
|
||||
apt-get install -y coturn
|
||||
|
||||
PUBLIC_IP="$(hostname -I | awk '{print $1}')"
|
||||
PUBLIC_IP="$(getent ahostsv4 "$REALM" | awk '{print $1; exit}')"
|
||||
if [[ -z "${PUBLIC_IP}" ]]; then
|
||||
echo "Не удалось определить public ip автоматически, укажите вручную в /etc/turnserver.conf" >&2
|
||||
PUBLIC_IP="$(hostname -I | awk '{print $1}')"
|
||||
fi
|
||||
if [[ -z "${PUBLIC_IP}" ]]; then
|
||||
echo "Не удалось определить public ip автоматически по домену ${REALM}, укажите вручную в /etc/turnserver.conf" >&2
|
||||
PUBLIC_IP="0.0.0.0"
|
||||
fi
|
||||
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REMOTE_HOST="player@t1.shineup.me" \
|
||||
TARGET_DOMAIN="t1.shineup.me" \
|
||||
REMOTE_SERVER_DIR="/home/player/t1/server" \
|
||||
REMOTE_SERVICE_NAME="shine-t1" \
|
||||
SERVER_PORT="7101" \
|
||||
bash "$(dirname "$0")/deploy_server.sh"
|
||||
Regular → Executable
+2
-2
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REMOTE_HOST="player@178.208.90.249" \
|
||||
REMOTE_HOST="player@t1.shineup.me" \
|
||||
REMOTE_UI_DIR="/home/player/t1/UI" \
|
||||
EXPECTED_CADDY_UI_ROOT="/home/player/t1/UI" \
|
||||
EXPECTED_CADDY_SITE="t1.shineup.me" \
|
||||
@@ -10,4 +10,4 @@ DEPLOY_SERVER_ADDRESS="t1.shineup.me" \
|
||||
DEPLOY_SOLANA_CLUSTER="devnet" \
|
||||
DEPLOY_SOLANA_ENDPOINT="https://api.devnet.solana.com" \
|
||||
TARGET_URL="https://t1.shineup.me" \
|
||||
bash "$(dirname "$0")/deploy_shine-PWA.sh"
|
||||
bash "$(dirname "$0")/deploy_ui.sh"
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REMOTE_HOST="player@t2.shineup.me" \
|
||||
TARGET_DOMAIN="t2.shineup.me" \
|
||||
REMOTE_SERVER_DIR="/home/player/t2/server" \
|
||||
REMOTE_SERVICE_NAME="shine-t2" \
|
||||
SERVER_PORT="7102" \
|
||||
bash "$(dirname "$0")/deploy_server.sh"
|
||||
Regular → Executable
+2
-2
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REMOTE_HOST="player@178.208.90.249" \
|
||||
REMOTE_HOST="player@t2.shineup.me" \
|
||||
REMOTE_UI_DIR="/home/player/t2/UI" \
|
||||
EXPECTED_CADDY_UI_ROOT="/home/player/t2/UI" \
|
||||
EXPECTED_CADDY_SITE="t2.shineup.me" \
|
||||
@@ -10,4 +10,4 @@ DEPLOY_SERVER_ADDRESS="t2.shineup.me" \
|
||||
DEPLOY_SOLANA_CLUSTER="devnet" \
|
||||
DEPLOY_SOLANA_ENDPOINT="https://api.devnet.solana.com" \
|
||||
TARGET_URL="https://t2.shineup.me" \
|
||||
bash "$(dirname "$0")/deploy_shine-PWA.sh"
|
||||
bash "$(dirname "$0")/deploy_ui.sh"
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REMOTE_HOST="player@t3.shineup.me" \
|
||||
TARGET_DOMAIN="t3.shineup.me" \
|
||||
REMOTE_SERVER_DIR="/home/player/t3/server" \
|
||||
REMOTE_SERVICE_NAME="shine-t3" \
|
||||
SERVER_PORT="7103" \
|
||||
bash "$(dirname "$0")/deploy_server.sh"
|
||||
Regular → Executable
+2
-2
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REMOTE_HOST="player@178.208.90.249" \
|
||||
REMOTE_HOST="player@t3.shineup.me" \
|
||||
REMOTE_UI_DIR="/home/player/t3/UI" \
|
||||
EXPECTED_CADDY_UI_ROOT="/home/player/t3/UI" \
|
||||
EXPECTED_CADDY_SITE="t3.shineup.me" \
|
||||
@@ -10,4 +10,4 @@ DEPLOY_SERVER_ADDRESS="t3.shineup.me" \
|
||||
DEPLOY_SOLANA_CLUSTER="devnet" \
|
||||
DEPLOY_SOLANA_ENDPOINT="https://api.devnet.solana.com" \
|
||||
TARGET_URL="https://t3.shineup.me" \
|
||||
bash "$(dirname "$0")/deploy_shine-PWA.sh"
|
||||
bash "$(dirname "$0")/deploy_ui.sh"
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REMOTE_HOST="player@t4.shineup.me" \
|
||||
TARGET_DOMAIN="t4.shineup.me" \
|
||||
REMOTE_SERVER_DIR="/home/player/t4/server" \
|
||||
REMOTE_SERVICE_NAME="shine-t4" \
|
||||
SERVER_PORT="7104" \
|
||||
bash "$(dirname "$0")/deploy_server.sh"
|
||||
Regular → Executable
+2
-2
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REMOTE_HOST="player@178.208.90.249" \
|
||||
REMOTE_HOST="player@t4.shineup.me" \
|
||||
REMOTE_UI_DIR="/home/player/t4/UI" \
|
||||
EXPECTED_CADDY_UI_ROOT="/home/player/t4/UI" \
|
||||
EXPECTED_CADDY_SITE="t4.shineup.me" \
|
||||
@@ -10,4 +10,4 @@ DEPLOY_SERVER_ADDRESS="t4.shineup.me" \
|
||||
DEPLOY_SOLANA_CLUSTER="devnet" \
|
||||
DEPLOY_SOLANA_ENDPOINT="https://api.devnet.solana.com" \
|
||||
TARGET_URL="https://t4.shineup.me" \
|
||||
bash "$(dirname "$0")/deploy_shine-PWA.sh"
|
||||
bash "$(dirname "$0")/deploy_ui.sh"
|
||||
@@ -1,63 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
TARGET_HOST="${TARGET_HOST:-player@193.8.215.70}"
|
||||
TARGET_DOMAIN="${TARGET_DOMAIN:-server2.shineup.me}"
|
||||
REMOTE_BASE="${REMOTE_BASE:-/home/player/SHiNE}"
|
||||
REMOTE_SERVER_DIR="${REMOTE_SERVER_DIR:-$REMOTE_BASE/shine-server}"
|
||||
REMOTE_DATA_DIR="${REMOTE_DATA_DIR:-$REMOTE_SERVER_DIR/data}"
|
||||
REMOTE_LOGS_DIR="${REMOTE_LOGS_DIR:-$REMOTE_SERVER_DIR/logs}"
|
||||
REMOTE_UI_DIR="${REMOTE_UI_DIR:-$REMOTE_BASE/shine-ui}"
|
||||
REMOTE_SERVICE_NAME="${REMOTE_SERVICE_NAME:-shine-server}"
|
||||
LOCAL_JAR="${LOCAL_JAR:-SHiNE-server/build/libs/shine-server.jar}"
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
cleanup() {
|
||||
rm -rf "$TMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
if [[ ! -f "$LOCAL_JAR" ]]; then
|
||||
echo "ERROR: локальный jar не найден: $LOCAL_JAR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=20 "$TARGET_HOST" "echo SSH OK" >/dev/null
|
||||
ssh "$TARGET_HOST" "sudo -n true"
|
||||
ssh "$TARGET_HOST" "java -version >/dev/null 2>&1"
|
||||
|
||||
cat >"$TMP_DIR/shine-server.service" <<EOF
|
||||
[Unit]
|
||||
Description=SHiNE Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=player
|
||||
Group=player
|
||||
WorkingDirectory=$REMOTE_SERVER_DIR
|
||||
ExecStart=/usr/bin/java -Dserver.port=7070 -jar $REMOTE_SERVER_DIR/shine-server.jar
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
StandardOutput=append:$REMOTE_LOGS_DIR/app.log
|
||||
StandardError=append:$REMOTE_LOGS_DIR/app.log
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
TARGET_HOST="$TARGET_HOST" TARGET_DOMAIN="$TARGET_DOMAIN" REMOTE_UI_DIR="$REMOTE_UI_DIR" \
|
||||
bash "$(dirname "$0")/scripts/install_test2_caddyfile.sh"
|
||||
|
||||
ssh "$TARGET_HOST" "mkdir -p '$REMOTE_SERVER_DIR' '$REMOTE_DATA_DIR' '$REMOTE_LOGS_DIR' '$REMOTE_UI_DIR'"
|
||||
rsync -az --timeout=120 "$LOCAL_JAR" "$TARGET_HOST:$REMOTE_SERVER_DIR/shine-server.jar"
|
||||
rsync -az "$TMP_DIR/shine-server.service" "$TARGET_HOST:/tmp/shine-server.service"
|
||||
|
||||
ssh "$TARGET_HOST" "set -euo pipefail; \
|
||||
sudo mv -f /tmp/shine-server.service /etc/systemd/system/shine-server.service; \
|
||||
sudo chown root:root /etc/systemd/system/shine-server.service; \
|
||||
sudo chown -R player:player '$REMOTE_SERVER_DIR'; \
|
||||
touch '$REMOTE_LOGS_DIR/app.log'; \
|
||||
sudo systemctl daemon-reload; \
|
||||
sudo systemctl enable '$REMOTE_SERVICE_NAME'; \
|
||||
sudo systemctl restart '$REMOTE_SERVICE_NAME'"
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
TARGET_HOST="${TARGET_HOST:-player@193.8.215.70}"
|
||||
TARGET_DOMAIN="${TARGET_DOMAIN:-server2.shineup.me}"
|
||||
REMOTE_UI_DIR="${REMOTE_UI_DIR:-/home/player/SHiNE/shine-ui}"
|
||||
|
||||
TARGET_HOST="$TARGET_HOST" TARGET_DOMAIN="$TARGET_DOMAIN" REMOTE_UI_DIR="$REMOTE_UI_DIR" \
|
||||
bash "$(dirname "$0")/scripts/install_test2_caddyfile.sh"
|
||||
|
||||
REMOTE_HOST="$TARGET_HOST" \
|
||||
REMOTE_UI_DIR="$REMOTE_UI_DIR" \
|
||||
EXPECTED_CADDY_UI_ROOT="$REMOTE_UI_DIR" \
|
||||
EXPECTED_CADDY_SITE="$TARGET_DOMAIN" \
|
||||
DEPLOY_SERVER_LOGIN="server2shineupme" \
|
||||
DEPLOY_SERVER_ADDRESS="$TARGET_DOMAIN" \
|
||||
DEPLOY_SOLANA_CLUSTER="mainnet" \
|
||||
DEPLOY_SOLANA_ENDPOINT="https://solana-rpc.publicnode.com" \
|
||||
TARGET_URL="https://$TARGET_DOMAIN" \
|
||||
bash "$(dirname "$0")/deploy_shine-PWA.sh"
|
||||
|
||||
ssh "$TARGET_HOST" "sudo chmod o+x /home/player /home/player/SHiNE '$REMOTE_UI_DIR'; \
|
||||
sudo find '$REMOTE_UI_DIR' -type d -exec chmod o+rx {} +; \
|
||||
sudo find '$REMOTE_UI_DIR' -type f -exec chmod o+r {} +"
|
||||
@@ -129,7 +129,7 @@
|
||||
|
||||
- `shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/JsonHandlerRegistry.java`.
|
||||
|
||||
Если операция зарегистрирована в `HANDLERS` и `REQUEST_TYPES`, она считается доступной через JSON/WebSocket API. Общий актуальный индекс таких операций поддерживается в `Dev_Docs/API/09_Operations_Index.md`.
|
||||
Если операция зарегистрирована в `HANDLERS` и `REQUEST_TYPES`, она считается доступной через JSON/WebSocket API. Общий актуальный индекс таких операций поддерживается в `docs/API/09_Operations_Index.md`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
Отдельно появился новый серверный сценарий pairing через доверенный homeserver/ESP. Он не заменяет обычный вход и описан в:
|
||||
|
||||
- `Dev_Docs/Протоколы/ESP_Pairing_и_режимы_подключения.md`
|
||||
- `docs/Протоколы/ESP_Pairing_и_режимы_подключения.md`
|
||||
|
||||
Кратко:
|
||||
|
||||
@@ -321,7 +321,7 @@ SESSION_LOGIN:{sessionId}:{timeMs}:{nonce}
|
||||
|
||||
Точные форматы этих операций см. в `03_Session_Management_API.md` и в протокольном документе:
|
||||
|
||||
- `Dev_Docs/Протоколы/ESP_Pairing_и_режимы_подключения.md`
|
||||
- `docs/Протоколы/ESP_Pairing_и_режимы_подключения.md`
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
Подробная логика DM и бинарного формата:
|
||||
|
||||
- `Dev_Docs/Personal_Messages/Протокол_DM_v1.md`
|
||||
- `Dev_Docs/Personal_Messages/Формат_DM_v1.md`
|
||||
- `docs/Personal_Messages/Протокол_DM_v1.md`
|
||||
- `docs/Personal_Messages/Формат_DM_v1.md`
|
||||
|
||||
Важно:
|
||||
|
||||
|
||||
@@ -22,4 +22,4 @@
|
||||
|
||||
## Примечание
|
||||
|
||||
Если нужно добавить новый тип или подтип блока, сначала обновляйте профильный файл этого раздела, затем API-документацию в `Dev_Docs/API`.
|
||||
Если нужно добавить новый тип или подтип блока, сначала обновляйте профильный файл этого раздела, затем API-документацию в `docs/API`.
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
- после рестарта сервер добивает `BlockchainTmpRecovery` и `BlockchainResyncRecovery`;
|
||||
- `aidartest-001` успешно подтягивается с `shineup.me`;
|
||||
- итоговое локальное состояние по `aidartest-001` дошло до `last_block_number=13`.
|
||||
- В `Dev_Docs/Blockchain/sync-between-servers.md` добавлен практический результат ручной проверки на тестовом сервере.
|
||||
- В `docs/Blockchain/sync-between-servers.md` добавлен практический результат ручной проверки на тестовом сервере.
|
||||
|
||||
## 2026-06-26 17:03:22 +0400
|
||||
- Базовый коммит-ориентир: `71fdee0`.
|
||||
@@ -60,21 +60,21 @@
|
||||
- `BlockchainTmpRecoveryOnStartup` теперь разбирает marker-driven recovery для обычной записи блока:
|
||||
- если marker есть, recovery либо завершает swap tmp -> main, либо удаляет мусор;
|
||||
- если marker нет, временные артефакты считаются мусором и удаляются.
|
||||
- В `Dev_Docs/Blockchain/sync-between-servers.md` добавлено описание обычного `AddBlock` recovery и разделение между `write_pending` и `resync_pending`.
|
||||
- В `docs/Blockchain/sync-between-servers.md` добавлено описание обычного `AddBlock` recovery и разделение между `write_pending` и `resync_pending`.
|
||||
|
||||
## 2026-05-24 11:40:00 +0300
|
||||
- Базовый коммит-ориентир: `abdce05`.
|
||||
- `TEXT_REPOST (subType=30)` оставлен как зарезервированный формат, но новые блоки репоста временно отключены на уровне `AddBlock`.
|
||||
- В `11_TEXT_Blocks.md` зафиксировано, что запись `TEXT_REPOST` временно не используется до будущей реализации.
|
||||
- В `Dev_Docs/API/04_Add_Block_to_Blockchain_API.md` добавлен код отказа `repost_disabled`.
|
||||
- В `docs/API/04_Add_Block_to_Blockchain_API.md` добавлен код отказа `repost_disabled`.
|
||||
|
||||
## 2026-05-21 19:05:00 +0300
|
||||
- Базовый коммит-ориентир: `5344c42`.
|
||||
- Добавлен новый TEXT-подтип `TEXT_REPOST (subType=30)`:
|
||||
- обновлён перечень типов в `11_TEXT_Blocks.md`;
|
||||
- обновлена быстрая карта типов в `00_Blockchain_Formats_and_Block_Types.md`.
|
||||
- Уточнено API-описание поддержанных подтипов в `Dev_Docs/API/04_Add_Block_to_Blockchain_API.md`.
|
||||
- В документе `Dev_Docs/API/08_MCP_Чтение_и_дозапись_персонального_публичного_чата.md` зафиксировано, что чтение канала учитывает `TEXT_POST` и `TEXT_REPOST`.
|
||||
- Уточнено API-описание поддержанных подтипов в `docs/API/04_Add_Block_to_Blockchain_API.md`.
|
||||
- В документе `docs/API/08_MCP_Чтение_и_дозапись_персонального_публичного_чата.md` зафиксировано, что чтение канала учитывает `TEXT_POST` и `TEXT_REPOST`.
|
||||
|
||||
## 2026-05-20 11:34:17 +0300
|
||||
- Базовый коммит-ориентир: `a53444b`.
|
||||
@@ -82,7 +82,7 @@
|
||||
- `60/61` — `known_person / unknown_person` (знаю этого человека);
|
||||
- `70/71` — `shine_confirmed / shine_unconfirmed` (точно уверен, что сияющий);
|
||||
- `74/75` — `shine_seen / shine_unseen` (мало знаком, но видел сияющим).
|
||||
- Обновлён список CONNECTION-подтипов в `Dev_Docs/API/04_Add_Block_to_Blockchain_API.md`.
|
||||
- Обновлён список CONNECTION-подтипов в `docs/API/04_Add_Block_to_Blockchain_API.md`.
|
||||
|
||||
## 2026-05-19 20:30:21 +0300
|
||||
- Базовый коммит-ориентир: `7986184`.
|
||||
@@ -107,4 +107,4 @@
|
||||
- Для персонального канала (`type=100`) включена сборка парного потока при чтении (`A->B` + `B->A`, если существует).
|
||||
- Добавлена поддержка командного префикса `/.` и команды `/.desc` для актуализации описания канала при чтении.
|
||||
- Зафиксированы команды `/.add` и `/.remove` для каналов `type=200` (зарезервировано под расширение участниками).
|
||||
- В `AGENTS.md` добавлено обязательное правило актуализации документации в `Dev_Docs/Blockchain/`.
|
||||
- В `AGENTS.md` добавлено обязательное правило актуализации документации в `docs/Blockchain/`.
|
||||
|
||||
@@ -193,7 +193,7 @@ Full resync запускается только тогда, когда:
|
||||
### 5.4 Разрешение конфликтов
|
||||
|
||||
- Блоки пользовательского блокчейна: порядок определяется глобальным номером блока.
|
||||
Конфликтующие ветки (fork) разрешаются по правилам `AddBlock` (см. `Dev_Docs/Blockchain/README.md`).
|
||||
Конфликтующие ветки (fork) разрешаются по правилам `AddBlock` (см. `docs/Blockchain/README.md`).
|
||||
- DM: конфликтов нет, `message_key` уникален.
|
||||
|
||||
## 6. Маршрутизация DM между серверами
|
||||
|
||||
@@ -189,7 +189,7 @@
|
||||
3. Переносить эти изменения назад в код минимально необходимыми правками.
|
||||
4. Если из Figma следует уже не только визуальная, но и UX-логическая правка, отдельно проверить, что она согласована пользователем.
|
||||
|
||||
## Когда нужно добавить заметку в Pending_Features
|
||||
## Когда нужно отдельно согласовать ручную проверку
|
||||
|
||||
Если после изменения по Figma:
|
||||
- поменялась логика flow;
|
||||
@@ -197,7 +197,7 @@
|
||||
- нужен реальный прогон на test2;
|
||||
- затронута интеграция с Solana;
|
||||
|
||||
тогда нужно добавить файл в `Dev_Docs/Pending_Features/`.
|
||||
тогда нужно отдельно согласовать ручную проверку с пользователем.
|
||||
|
||||
## Что пока не оформлено для Miro
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
> Если в коде меняется деривация (формула секрета, параметры Argon2id, соль, формула
|
||||
> ключа, разделитель `|`, набор/имена суффиксов, формат homeserver-ключа, связь
|
||||
> dev-ключ ↔ Solana-адрес) — **в том же изменении обязательно править этот документ**.
|
||||
> Роли и назначение ключей описаны отдельно в `Dev_Docs/Keys/README.md` (архитектура).
|
||||
> Роли и назначение ключей описаны отдельно в `docs/Keys/README.md` (архитектура).
|
||||
> Здесь — только механика. Документ намеренно краткий.
|
||||
|
||||
---
|
||||
@@ -59,7 +59,7 @@ seed(32) = SHA-256(material)
|
||||
| device / **Solana** | `client.key` | Ключ устройства = Solana-ключ. Fee payer и подпись Solana-транзакций; адрес кошелька = `base58(clientPub)`. См. §3. |
|
||||
| homeserver | `homeserver.key:<имя>` | Ключ homeserver-устройства, по одному на каждый homeserver (различитель — имя). См. §4. |
|
||||
|
||||
Полные роли каждого ключа — в `Dev_Docs/Keys/README.md`.
|
||||
Полные роли каждого ключа — в `docs/Keys/README.md`.
|
||||
|
||||
---
|
||||
|
||||
@@ -77,7 +77,7 @@ seed(32) = SHA-256(material)
|
||||
|
||||
Кратко про роли на Solana: `root.key` — это **главный (master) ключ**: им управляют PDA-записью
|
||||
(`create/update`) и через это можно заменить все остальные ключи; `client.key` — это **пополняемый
|
||||
кошелёк и плательщик комиссий**. Полное описание ролей — `Dev_Docs/Keys/README.md`.
|
||||
кошелёк и плательщик комиссий**. Полное описание ролей — `docs/Keys/README.md`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+9
-9
@@ -30,7 +30,7 @@
|
||||
|
||||
`root key` — это **главный (master) ключ** в следующем смысле: зная `root key`, можно управлять пользовательской PDA-записью в Solana (`create_user_pda` / `update_user_pda`) и тем самым **заменить все остальные ключи** пользователя (device, blockchain, homeserver). Поэтому компрометация `root key` равносильна компрометации всей личности пользователя.
|
||||
|
||||
Важно не путать авторитет и кошелёк: `root key` — это авторитет над PDA-записью, а **SOL-комиссии за create/update платит `client key`** (он же fee payer и адрес для пополнения). Подробнее о том, какой ключ за что отвечает на Solana, — в `Dev_Docs/Keys/DERIVATION.md`, §3.
|
||||
Важно не путать авторитет и кошелёк: `root key` — это авторитет над PDA-записью, а **SOL-комиссии за create/update платит `client key`** (он же fee payer и адрес для пополнения). Подробнее о том, какой ключ за что отвечает на Solana, — в `docs/Keys/DERIVATION.md`, §3.
|
||||
|
||||
## `blockchain key`
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
|
||||
Arweave-кошелёк должен выводиться из `client key` по протоколу:
|
||||
|
||||
- `Dev_Docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md`
|
||||
- `docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md`
|
||||
|
||||
Если пользователь теряет только `client key`, в худшем случае ломается повседневная переписка и доступ конкретных устройств к ежедневным операциям. `root key` и `blockchain key` при правильной архитектуре остаются отдельно защищёнными.
|
||||
|
||||
@@ -158,13 +158,13 @@ Self-message - это сообщение пользователя самому
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- `Dev_Docs/Keys/DERIVATION.md` - **источник истины по конкретной деривации** секрета и ключей (формулы Argon2id, `base64|suffix→SHA-256→Ed25519`, суффиксы `root.key`/`bch.key`/`client.key`/`homeserver.key:<имя>`, Solana-ключ, ссылки на код).
|
||||
- `Dev_Docs/Personal_Messages/Протокол_DM_v1.md` - текущая логическая документация личных сообщений.
|
||||
- `Dev_Docs/Personal_Messages/Формат_DM_v1.md` - точный байтовый формат личных сообщений.
|
||||
- `Dev_Docs/Blockchain/README.md` - точка входа по форматам SHiNE-блокчейна.
|
||||
- `Dev_Docs/Solana_Architecture/README.md` - архитектура Solana-программ, PDA-счетов, DAO и движения средств.
|
||||
- `Dev_Docs/Инициализация_Solana_регистрации/README.md` - деплой и первичная инициализация Solana-регистрации.
|
||||
- `Dev_Docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md` - derivation Arweave-кошелька из `client key`.
|
||||
- `docs/Keys/DERIVATION.md` - **источник истины по конкретной деривации** секрета и ключей (формулы Argon2id, `base64|suffix→SHA-256→Ed25519`, суффиксы `root.key`/`bch.key`/`client.key`/`homeserver.key:<имя>`, Solana-ключ, ссылки на код).
|
||||
- `docs/Personal_Messages/Протокол_DM_v1.md` - текущая логическая документация личных сообщений.
|
||||
- `docs/Personal_Messages/Формат_DM_v1.md` - точный байтовый формат личных сообщений.
|
||||
- `docs/Blockchain/README.md` - точка входа по форматам SHiNE-блокчейна.
|
||||
- `docs/Solana_Architecture/README.md` - архитектура Solana-программ, PDA-счетов, DAO и движения средств.
|
||||
- `docs/Инициализация_Solana_регистрации/README.md` - деплой и первичная инициализация Solana-регистрации.
|
||||
- `docs/Протоколы/SHINE_ARWEAVE_DERIVATION_V1.md` - derivation Arweave-кошелька из `client key`.
|
||||
|
||||
## Что нужно уточнить перед реализацией
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# Поддержать проект Сияние
|
||||
|
||||
Статус: `pending`
|
||||
|
||||
## Кратко
|
||||
|
||||
В `shine-UI/js/pages/wallet-view.js` добавлен новый раздел `Поддержать проект Сияние` с тремя входами:
|
||||
|
||||
1. купить билет;
|
||||
2. посмотреть билет по номеру;
|
||||
3. сгенерировать новую пару ключей.
|
||||
|
||||
## Что проверить
|
||||
|
||||
1. Открыть `Кошелёк`.
|
||||
2. Перейти в `Поддержать проект Сияние`.
|
||||
3. Проверить экран покупки:
|
||||
- виден коэффициент;
|
||||
- виден остаток лимита очереди 1;
|
||||
- виден расчет в SOL;
|
||||
- кнопка `Справка` открывает отдельный экран;
|
||||
- покупка блокируется, если сумма больше остатка лимита.
|
||||
4. Проверить экран просмотра:
|
||||
- `12` ищется как билет очереди 1;
|
||||
- `2-5` и `3 8` ищутся как билеты очередей 2 и 3;
|
||||
- показываются статус, количество билетов до него и уже выплаченные значения.
|
||||
5. Проверить генератор ключей:
|
||||
- генерируется новая пара ключей;
|
||||
- публичный и секретный ключи показываются;
|
||||
- можно скопировать и скачать результат;
|
||||
- дополнительный текст в поле необязателен.
|
||||
|
||||
## Ожидаемый результат
|
||||
|
||||
- Экран раздела поддержки открывается из `wallet-view`.
|
||||
- Покупка билета выполняется по текущему курсу и с допуском 3%.
|
||||
- По номеру билета показывается понятная сводка по очереди.
|
||||
- Генерация ключей использует безопасный браузерный рандом и не требует сохранения секретного ключа.
|
||||
@@ -1,20 +0,0 @@
|
||||
# Promo-логины через продавцов в Solana
|
||||
|
||||
- краткое описание:
|
||||
- в `shine_users` добавлена поддержка PDA продавцов красивых логинов, promo-подписи `shine_promo_v1:<login>` и отдельная автономная страница `shine-UI/promo-code-generator.html` для генерации promo-кода через браузерный кошелёк или через ручной ввод Base58 seed 32 bytes;
|
||||
- что проверять:
|
||||
- admin-транзакцией создать или обновить `promo_seller_pda`;
|
||||
- сгенерировать promo-код на странице `promo-code-generator.html` в режиме wallet extension;
|
||||
- сгенерировать promo-код на той же странице в режиме ручного Base58 seed;
|
||||
- открыть HTML локально как отдельный файл без сервера и убедиться, что генерация в режиме Base58 seed продолжает работать;
|
||||
- зарегистрировать короткий/premium логин по promo-коду;
|
||||
- убедиться, что без promo-кода тот же логин не проходит `shine_login_guard`;
|
||||
- убедиться, что после успешной регистрации `remaining_sales` уменьшается на 1;
|
||||
- проверить отказ при исчерпанной квоте, неверной подписи и логине короче `min_login_length`;
|
||||
- ожидаемый результат:
|
||||
- promo-регистрация проходит только при валидной подписи продавца и соблюдении лимитов;
|
||||
- обычная регистрация без promo-кода продолжает работать как раньше;
|
||||
- страница генерации выдаёт строку формата `1seller-signatureBase58` в обоих режимах;
|
||||
- single-file HTML работает локально оффлайн минимум в режиме ручного Base58 seed;
|
||||
- статус:
|
||||
- `pending`
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user