SHA256
Навести порядок в deploy и документации проекта
This commit is contained in:
+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,7 +36,7 @@
|
||||
- Модуль логически связан с 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/сервере находится в:
|
||||
- `docs/Инициализация_Solana_регистрации/README.md`
|
||||
- Этот файл считать основной справкой (single source of truth) по деплою и первичной инициализации Solana-регистрации в текущем проекте.
|
||||
@@ -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,19 +122,6 @@
|
||||
- `unknown_error`
|
||||
- В этих записях искать поля `reason`, `failureStage`, `pcConnectionState`, `pcIceConnectionState`, `routeLabel`, `configuredTurnHosts*`, `reachableTurnHosts*`.
|
||||
|
||||
## Недопроверенные фичи (обязательно)
|
||||
- Папка для учёта недопроверенных фич: `docs/Pending_Features/`.
|
||||
- По каждой новой доработке, которая требует ручной проверки, добавлять отдельный markdown-файл в `docs/Pending_Features/`.
|
||||
- Рекомендуемый формат имени файла: `YYYY-MM-DD_HHMM_<short-feature-name>.md`.
|
||||
- Имена новых файлов и краткие описания фич по возможности писать на русском языке.
|
||||
- Внутри файла обязательно указывать:
|
||||
- краткое описание фичи;
|
||||
- что именно проверять;
|
||||
- ожидаемый результат;
|
||||
- статус (например: `pending`, `in_progress`, `done`).
|
||||
- После подтверждения, что фича проверена и работает корректно, соответствующий файл удалять.
|
||||
- В `docs/Pending_Features/README.md` вести краткий регламент и поддерживать актуальность.
|
||||
|
||||
## Будущие фичи / TODO
|
||||
- Папка для задач, сознательно отложенных на будущее: `TODO/`.
|
||||
- Точка входа по планам: `TODO/README.md`.
|
||||
|
||||
@@ -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
|
||||
```
|
||||
+5
-15
@@ -54,21 +54,11 @@ shine-UI/server-ui.html
|
||||
|
||||
## Деплой
|
||||
|
||||
```
|
||||
./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`
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -35,5 +35,5 @@
|
||||
|
||||
## Какие документы потом обновить
|
||||
|
||||
- `Deploy/`;
|
||||
- `deploy/`;
|
||||
- `docs/Blockchain/sync-between-servers.md`, если изменится поведение остановки/восстановления.
|
||||
|
||||
@@ -56,11 +56,9 @@
|
||||
- Код формирования репоста в `auth-service.js` не удалён: его можно будет использовать как основу при возвращении к задаче.
|
||||
- Код отображения target-полей и перехода к оригиналу не удалён: он нужен для будущей проверки и возможной совместимости с уже созданными тестовыми блоками.
|
||||
|
||||
## Почему это не лежит в Pending_Features
|
||||
## Почему это лежит в TODO
|
||||
|
||||
`docs/Pending_Features/` предназначена для фич, которые уже реализованы и ждут ручной проверки.
|
||||
|
||||
Репосты сейчас не подходят под этот статус: они не должны проверяться как готовая фича, потому что пользовательский сценарий временно закрыт, а серверная запись новых репостов заблокирована. Поэтому старый pending-файл удалён, а задача перенесена сюда как будущая.
|
||||
Репосты сейчас не должны проверяться как готовая фича, потому что пользовательский сценарий временно закрыт, а серверная запись новых репостов заблокирована. Поэтому задача остаётся в TODO как будущая.
|
||||
|
||||
## Что сделать при возврате к реализации
|
||||
|
||||
@@ -86,7 +84,7 @@
|
||||
- `docs/Blockchain/CHANGELOG.md`;
|
||||
- `docs/API/04_Add_Block_to_Blockchain_API.md`;
|
||||
- документы API чтения каналов/тредов, если изменятся поля ответа.
|
||||
9. После реализации перенести задачу из `TODO/` в `docs/Pending_Features/` как фичу, требующую ручной проверки.
|
||||
9. После реализации отдельно согласовать ручную проверку пользовательского сценария.
|
||||
|
||||
## Минимальный чек-лист ручной проверки в будущем
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
- `docs/Blockchain/`, если появятся или изменятся блоки баланса.
|
||||
- `docs/Blockchain/CHANGELOG.md`, если меняется блокчейн-формат.
|
||||
- `docs/API/`, если меняется серверный API.
|
||||
- `docs/Pending_Features/` - добавить файл ручной проверки после реализации.
|
||||
- после реализации отдельно согласовать ручную проверку.
|
||||
- Документацию Solana-регистрации, если баланс будет связан с Solana-модулем.
|
||||
|
||||
## Минимальная проверка в будущем
|
||||
|
||||
@@ -37,9 +37,8 @@
|
||||
|
||||
## Что обновить при возврате
|
||||
|
||||
- `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,7 +58,7 @@
|
||||
## Документы, которые обновить при реализации
|
||||
|
||||
- Документацию UI/кошельков, если такая есть.
|
||||
- `docs/Pending_Features/` - добавить файл ручной проверки после реализации.
|
||||
- после реализации отдельно согласовать ручную проверку.
|
||||
- `docs/API/`, только если появится новый серверный API или логирование.
|
||||
|
||||
## Минимальная проверка
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
- `docs/Blockchain/README.md`;
|
||||
- `docs/Blockchain/CHANGELOG.md`;
|
||||
- документы deploy/секретов в `Deploy/`, если появятся новые параметры.
|
||||
- документы deploy/секретов в `deploy/`, если появятся новые параметры.
|
||||
|
||||
## Статус
|
||||
|
||||
|
||||
+2
-2
@@ -1,2 +1,2 @@
|
||||
client.version=1.2.330
|
||||
server.version=1.2.302
|
||||
client.version=1.2.331
|
||||
server.version=1.2.303
|
||||
|
||||
+1
-55
@@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
проверь ещё id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||
}
|
||||
|
||||
def appVersionProps = new Properties()
|
||||
@@ -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_server2shineupme.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 {} +"
|
||||
@@ -189,7 +189,7 @@
|
||||
3. Переносить эти изменения назад в код минимально необходимыми правками.
|
||||
4. Если из Figma следует уже не только визуальная, но и UX-логическая правка, отдельно проверить, что она согласована пользователем.
|
||||
|
||||
## Когда нужно добавить заметку в Pending_Features
|
||||
## Когда нужно отдельно согласовать ручную проверку
|
||||
|
||||
Если после изменения по Figma:
|
||||
- поменялась логика flow;
|
||||
@@ -197,7 +197,7 @@
|
||||
- нужен реальный прогон на test2;
|
||||
- затронута интеграция с Solana;
|
||||
|
||||
тогда нужно добавить файл в `docs/Pending_Features/`.
|
||||
тогда нужно отдельно согласовать ручную проверку с пользователем.
|
||||
|
||||
## Что пока не оформлено для Miro
|
||||
|
||||
|
||||
@@ -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`
|
||||
@@ -1,51 +0,0 @@
|
||||
# Синхронизация mainnet-адресов Solana по UI/серверу/ESP32
|
||||
|
||||
- статус: `pending`
|
||||
|
||||
## Кратко
|
||||
|
||||
Во все основные клиентские и серверные точки проекта протянуты актуальные mainnet-адреса программ:
|
||||
|
||||
- `shine_login_guard`: `SHiGxGsXGioQYCYhchQ5R7KWoxN5UjFAFsucPf6sfnh`
|
||||
- `shine_users`: `SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6`
|
||||
- `shine_payments`: `SHiPmXbM9Fs9khzRUW3TGKsS2W84aqaXTxs3ZkajW9v`
|
||||
|
||||
Также по умолчанию переключены основные RPC defaults на `https://api.mainnet-beta.solana.com` там, где это нужно для рабочего mainnet-сценария.
|
||||
|
||||
## Что проверять
|
||||
|
||||
1. Основной UI:
|
||||
- отображение и использование новых program id;
|
||||
- регистрация/чтение Solana PDA в mainnet;
|
||||
- экран кошелька и покупка лимита.
|
||||
|
||||
2. Server UI:
|
||||
- создание server PDA в mainnet;
|
||||
- чтение PDA;
|
||||
- обновление PDA;
|
||||
- корректная блокировка devnet-автопополнения при mainnet endpoint.
|
||||
|
||||
3. Browser plugin wallet:
|
||||
- резолв `user_pda` и `server PDA` через mainnet RPC.
|
||||
|
||||
4. Web UI `shine_payments`:
|
||||
- чтение очередей;
|
||||
- покупка билета;
|
||||
- admin/manager/dao tools открываются с новыми program id и mainnet RPC.
|
||||
|
||||
5. ESP32 homeserver UI:
|
||||
- отображаются новые program id;
|
||||
- дефолтный Solana RPC теперь mainnet.
|
||||
|
||||
6. Временный режим регистрации:
|
||||
- в основном UI логины длиной 5-7 символов без временного кода не проходят;
|
||||
- в основном UI логины длиной 5-7 символов с временным кодом проходят локальную проверку;
|
||||
- в основном UI любой другой временный код отклоняется сразу;
|
||||
- ESP32 и прямой on-chain вызов `shine_users` полагаются на текущий временный `shine_login_guard`, то есть валидные логины длиной от 5 символов допускаются без словарной классификации.
|
||||
|
||||
## Ожидаемый результат
|
||||
|
||||
- Во всех перечисленных поверхностях используются новые mainnet program id.
|
||||
- Запросы по умолчанию идут в mainnet, кроме явно devnet-утилит.
|
||||
- Нет обращений к старым devnet program id.
|
||||
- Временный UI-барьер для коротких логинов работает так, как задумано, и не ломает обычную регистрацию для логинов от 8 символов.
|
||||
@@ -1,42 +0,0 @@
|
||||
# Временный режим коротких логинов
|
||||
|
||||
- статус: `pending`
|
||||
|
||||
## Кратко
|
||||
|
||||
Временно упрощён `shine_login_guard`:
|
||||
|
||||
- on-chain допускаются любые валидные логины длиной от `5` символов;
|
||||
- словарная premium/trademark-классификация временно отключена.
|
||||
|
||||
Поверх этого основной UI вводит временное ограничение:
|
||||
|
||||
- логины длиной `8+` проходят обычную проверку;
|
||||
- логины длиной `5..7` допускаются только при вводе временного кода;
|
||||
- любой другой код отклоняется сразу.
|
||||
|
||||
ESP32 в этом временном режиме не ограничивается UI-кодом и использует только on-chain правила.
|
||||
|
||||
## Что проверять
|
||||
|
||||
1. Основной UI:
|
||||
- логин длиной `4` символа отклоняется;
|
||||
- логин длиной `5..7` без временного кода отклоняется;
|
||||
- логин длиной `5..7` с корректным временным кодом проходит локальную проверку;
|
||||
- логин длиной `5..7` с любым другим кодом отклоняется;
|
||||
- логин длиной `8+` проходит без временного кода.
|
||||
|
||||
2. Solana on-chain:
|
||||
- `shine_login_guard` возвращает `CLASS_FREE` для валидных логинов длиной от `5`;
|
||||
- `shine_login_guard` возвращает `CLASS_PREMIUM` для логинов короче `5` или с невалидными символами.
|
||||
|
||||
3. ESP32 / прямой вызов:
|
||||
- валидный логин длиной `5+` регистрируется без временного кода;
|
||||
- логин короче `5` не регистрируется.
|
||||
|
||||
## Ожидаемый результат
|
||||
|
||||
- Основной UI удерживает обычных пользователей от коротких логинов без временного кода.
|
||||
- Свои пользователи могут временно регистрировать короткие логины через UI-код.
|
||||
- ESP32 продолжает работать без отдельной промо-логики.
|
||||
- On-chain логика остаётся простой и компактной.
|
||||
@@ -1,39 +0,0 @@
|
||||
# 4 тестовых SHiNE-сервера на одном VPS (`178.208.90.249`)
|
||||
|
||||
- статус: `pending`
|
||||
|
||||
## Кратко
|
||||
|
||||
Поднят отдельный тестовый стенд:
|
||||
- `t1.shineup.me`
|
||||
- `t2.shineup.me`
|
||||
- `t3.shineup.me`
|
||||
- `t4.shineup.me`
|
||||
|
||||
Каждый домен ведёт на свой SHiNE-инстанс под пользователем `player`, все инстансы работают через Solana `devnet`.
|
||||
|
||||
## Что проверять
|
||||
|
||||
- UI каждого домена открывается без ошибок:
|
||||
- `https://t1.shineup.me`
|
||||
- `https://t2.shineup.me`
|
||||
- `https://t3.shineup.me`
|
||||
- `https://t4.shineup.me`
|
||||
- регистрация/логин клиента может работать через каждый из этих серверов
|
||||
- WebSocket-подключение реально идёт на свой инстанс, а не в соседний
|
||||
- чтение PDA и server-ui формы по умолчанию используют `devnet`
|
||||
- межсерверный sync после ручных тестов действительно использует `access_servers/sync_servers`
|
||||
- не происходит путаницы данных между `t1/t2/t3/t4`
|
||||
|
||||
## Ожидаемый результат
|
||||
|
||||
- каждый домен работает независимо
|
||||
- у каждого инстанса своя БД и свои локальные данные
|
||||
- `Caddy` корректно проксирует `/ws` на `7101..7104`
|
||||
- серверы не падают после старта
|
||||
- если devnet RPC временно даёт `429`, последовательный рестарт по одному инстансу позволяет подтянуть `sync_servers`
|
||||
|
||||
## Примечание
|
||||
|
||||
Подробная схема размещения описана в:
|
||||
- [docs/deploy/test-server/178.208.90.249_quad_devnet.md](/home/ai/work/SHiNE/SHiNE-server-sha256/docs/deploy/test-server/178.208.90.249_quad_devnet.md)
|
||||
@@ -1,17 +0,0 @@
|
||||
# Итог звонка как DM от инициатора
|
||||
|
||||
- краткое описание фичи:
|
||||
Вместо локальных `call-tech` записей итог звонка теперь отправляется как обычное DM-сообщение только от стороны, которая инициировала звонок.
|
||||
|
||||
- что именно проверять:
|
||||
1. Успешный звонок: после завершения инициатор отправляет в чат DM вида `Звонок: 37с` или `Звонок: 2м 14с`.
|
||||
2. Неуспешный звонок без ответа: инициатор отправляет `Звонил, но недозвонился: нет ответа`.
|
||||
3. Неуспешный звонок, когда у адресата нет доставленных сессий: инициатор отправляет `Звонил, но недозвонился: абонент не в сети`.
|
||||
4. Неуспешный звонок при проблеме соединения: инициатор отправляет `Звонил, но недозвонился: не удалось установить соединение`.
|
||||
5. Старые локальные `call-tech` bubble про итог звонка больше не появляются ни у инициатора, ни у принимающей стороны.
|
||||
|
||||
- ожидаемый результат:
|
||||
Итог звонка виден обеим сторонам как обычное DM-сообщение от инициатора, а локальные служебные записи про итог звонка больше не используются.
|
||||
|
||||
- статус:
|
||||
pending
|
||||
@@ -1,23 +0,0 @@
|
||||
## Краткое описание
|
||||
|
||||
Сервер перестал валидировать внутреннюю crypto-структуру `body` у контентных DM `type=1/2`.
|
||||
UI теперь не отбрасывает такие сообщения: если сообщение не удалось расшифровать, вместо текста показывается заглушка `Неудалось расшифровать сообщение`.
|
||||
|
||||
## Что проверять
|
||||
|
||||
- Отправка и приём обычных DM между штатными клиентами продолжают работать как раньше.
|
||||
- Сообщение с незнакомым форматом `body` у `type=1/2` принимается сервером и доходит до клиента.
|
||||
- В UI такое сообщение отображается в чате одной из ожидаемых заглушек:
|
||||
- `Формат сообщения не поддерживается`
|
||||
- `Не удалось расшифровать сообщение`
|
||||
- `Сообщение повреждено`
|
||||
- После выхода из аккаунта и повторного входа backlog с такими сообщениями тоже отображается с той же заглушкой.
|
||||
- ACK доставки по сессии для такого сообщения не зацикливается и сообщение не приходит бесконечно повторно.
|
||||
|
||||
## Ожидаемый результат
|
||||
|
||||
Сервер выступает транспортом для opaque DM-body, а клиент при неудачной расшифровке показывает безопасный fallback вместо полного пропуска сообщения с различением основных причин.
|
||||
|
||||
## Статус
|
||||
|
||||
pending
|
||||
@@ -1,28 +0,0 @@
|
||||
## Краткое описание
|
||||
|
||||
В DM добавлен клиентский формат технических вставок в начале plaintext:
|
||||
|
||||
- `<SHiNE:reply;v=1;id=...>`
|
||||
- `<SHiNE:call;v=1;status=...;...>`
|
||||
|
||||
UI скрывает такие вставки из текста сообщения, а call-вставки отображает специальным человекочитаемым видом.
|
||||
Также добавлена защита от пользовательского текста, начинающегося с `<SHiNE:`, и исправлен безопасный рендер превью последнего сообщения в списке личных диалогов.
|
||||
|
||||
## Что проверять
|
||||
|
||||
- Если пользователь отправляет обычный текст, начинающийся с `<SHiNE:`, он уходит как `< SHiNE:` и отображается как обычный текст.
|
||||
- DM с `<SHiNE:call...>` показывается в чате как специальное call-сообщение, а не как сырой техтекст.
|
||||
- При исходящем звонке короче `5` секунд call-summary не отправляется вообще.
|
||||
- В списке личных сообщений превью такого сообщения показывает человекочитаемый итог звонка.
|
||||
- DM с `<SHiNE:reply...>Текст ответа` скрывает техблок и показывает только `Текст ответа`.
|
||||
- В меню любого DM первым пунктом есть `Ответить`, и отправка из этого режима реально добавляет `<SHiNE:reply...>` в начало plaintext.
|
||||
- Если reply-цель отсутствует, сообщение всё равно показывается как обычный текст ответа.
|
||||
- Текст вида `<script>alert(1)</script>` или похожие HTML-теги не исполняются ни в чате, ни в списке личных сообщений, ни в списке каналов.
|
||||
|
||||
## Ожидаемый результат
|
||||
|
||||
Технические SHiNE-вставки работают только как управляющие метаданные UI, а отображаемый пользователю текст и превью остаются безопасными и не рендерят HTML.
|
||||
|
||||
## Статус
|
||||
|
||||
pending
|
||||
@@ -1,30 +0,0 @@
|
||||
# Исправление завершения звонка и таймаутов ACK
|
||||
|
||||
## Краткое описание
|
||||
|
||||
Исправлена клиентская логика звонков:
|
||||
- входящий экран теперь должен закрываться сразу при `remote_hangup`;
|
||||
- после нажатия `Ответить` добавлен таймаут ожидания стадии `connecting`, чтобы окно не висело бесконечно;
|
||||
- исходящий таймаут ожидания ACK больше не обрывает звонок слишком рано, если invite уже был доставлен.
|
||||
- в локальный app log клиента добавлен точный timeline по этапам звонка: invite, выбор remoteSessionId, accept/offer/answer, ICE, смены `RTCPeerConnection` и финализация.
|
||||
- добавлена явная диагностика `SESSION_NOT_FOUND` для звонковых сигналов: отдельный timeline-этап в клиенте и отдельный серверный warn-лог.
|
||||
|
||||
## Что проверять
|
||||
|
||||
- Если исходящая сторона завершила звонок, входящее окно у второй стороны закрывается сразу.
|
||||
- Если принять входящий звонок, а соединение/offer не приходит, экран не висит бесконечно и сам завершается по таймауту.
|
||||
- Если invite был доставлен, исходящий звонок не падает через 10 секунд раньше времени только из-за медленного ACK.
|
||||
- В app log по звонку видна последовательность этапов с локальными `+N ms`, по которой можно понять, где именно образуется задержка или гонка.
|
||||
- Если сигнал звонка не дошёл до целевой сессии, это явно видно и в клиенте, и в серверном логе.
|
||||
|
||||
## Ожидаемый результат
|
||||
|
||||
- Нет бесконечно висящего окна `Соединяем…` на принимающей стороне.
|
||||
- В логах вместо преждевременного `no_ack_10s` для уже доставленного invite появляется более поздний таймаут.
|
||||
- При `remote_hangup` звонок визуально завершается сразу.
|
||||
- По каждому проблемному звонку есть локальный timeline и тот же краткий timeline попадает в `CallDeliveryReport`.
|
||||
- В серверных логах есть строка `CALL_SIGNAL_SESSION_NOT_FOUND ...` с `callId`, `targetSessionId` и количеством активных сессий адресата.
|
||||
|
||||
## Статус
|
||||
|
||||
`pending`
|
||||
@@ -1,16 +0,0 @@
|
||||
# Адаптация верхней панели экрана «Связи»
|
||||
|
||||
## Краткое описание
|
||||
- Экран графа связей больше не расширяется за границы контейнера. Верхняя панель ограничена границами экрана, чтобы кнопки возврата, поиска и справки не обрезались на узких устройствах.
|
||||
|
||||
## Что проверить
|
||||
- Открыть экран «Связи» на ширине около 320, 390 и 794 px.
|
||||
- Убедиться, что кнопки возврата, «Найти» и «?» полностью видны и нажимаются.
|
||||
- Убедиться, что фильтры и граф связей остаются доступными.
|
||||
|
||||
## Ожидаемый результат
|
||||
- Верхние действия не выходят за левую или правую границу приложения.
|
||||
- Заголовок при нехватке места сокращается, а не сдвигает кнопки за край.
|
||||
|
||||
## Статус
|
||||
- `pending` — локальная проверка на ширине 320 и 794 px пройдена; нужна ручная проверка после публикации на тестовом или production-стенде.
|
||||
@@ -1,16 +0,0 @@
|
||||
# Кнопка поиска каналов
|
||||
|
||||
## Краткое описание
|
||||
- Emoji-иконка поиска в верхней панели «Каналов» заменена на контурную кнопку-лупу в стиле интерфейса.
|
||||
|
||||
## Что проверить
|
||||
- Открыть список каналов.
|
||||
- Убедиться, что кнопка поиска видна рядом с кнопкой создания канала и имеет подсказку «Найти канал».
|
||||
- Нажать кнопку и убедиться, что открывается прежнее окно поиска каналов.
|
||||
|
||||
## Ожидаемый результат
|
||||
- Кнопка выглядит как часть общей тёмной панели и не использует emoji.
|
||||
- Поиск каналов работает без изменений.
|
||||
|
||||
## Статус
|
||||
- `pending` — локальная визуальная проверка и открытие окна поиска пройдены; нужна ручная проверка после публикации на тестовом или production-стенде.
|
||||
@@ -1,20 +0,0 @@
|
||||
# Локальный тестовый вход
|
||||
|
||||
## Краткое описание
|
||||
- На `localhost`, `127.0.0.1` и `::1` доступна кнопка «Локальный тестовый вход».
|
||||
- Она создаёт только локальный демо-сеанс `local-tester`; пароль, серверная сессия и production не используются.
|
||||
- Экран личных сообщений и список каналов открывают автономные локальные состояния без запроса к серверу.
|
||||
- Профиль заполняется демонстрационными данными, чтобы интерфейс можно было проверить без серверного пользователя.
|
||||
|
||||
## Что проверить
|
||||
- Открыть локальный UI и нажать «Локальный тестовый вход».
|
||||
- Проверить переход к списку сообщений и доступ к экранам профиля, каналов, связей и настроек.
|
||||
- Обновить страницу и убедиться, что локальный сеанс сохраняется.
|
||||
- Выйти из сеанса и убедиться, что снова открывается экран старта.
|
||||
|
||||
## Ожидаемый результат
|
||||
- Локальный режим даёт возможность изучить интерфейс без реальной авторизации.
|
||||
- На `shineup.me` и тестовых доменах кнопка отсутствует.
|
||||
|
||||
## Статус
|
||||
- `pending` — локальный вход, переход к профилю и демонстрационные каналы проверены; нужна ручная проверка выхода из сеанса после публикации на тестовом или production-стенде.
|
||||
@@ -1,23 +0,0 @@
|
||||
# Экран проверки public Solana RPC в UI
|
||||
|
||||
- статус: `pending`
|
||||
|
||||
## Кратко
|
||||
|
||||
Добавлен отдельный экран разработчика для проверки публичных mainnet Solana RPC прямо из браузера.
|
||||
Экран отправляет реальный JSON-RPC `getVersion` запрос к списку public endpoint-ов и показывает,
|
||||
какие ноды реально подходят для browser use.
|
||||
|
||||
## Что проверять
|
||||
|
||||
- в `Настройки разработчика` появилась кнопка `Solana: проверить public RPC`;
|
||||
- экран открывается без ошибок;
|
||||
- по каждой mainnet ноде показывается итоговый статус;
|
||||
- для доступных нод видно HTTP-статус, время ответа и версию `solana-core`;
|
||||
- длинные URL и ошибки не распирают экран по ширине на телефоне.
|
||||
|
||||
## Ожидаемый результат
|
||||
|
||||
- можно визуально увидеть, какие public Solana RPC доступны именно из браузера;
|
||||
- проверка работает без промежуточного сервера;
|
||||
- экран остаётся mobile-first и не требует горизонтального скролла.
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
## Кратко
|
||||
- Переделан финальный flow регистрации: сначала экран ожидания подтверждения Solana с прогрессом и повторными проверками, потом отдельный экран успешной регистрации с кнопкой входа в стандартный экран сохранения ключей.
|
||||
|
||||
## Что проверять
|
||||
- После отправки регистрации открывается экран ожидания с текстом про подтверждение Solana и кликабельным `Tx ID`.
|
||||
- Прогресс-бар сначала идёт быстрее, потом заметно замедляется.
|
||||
- Первая проверка регистрации начинается примерно через 4 секунды, далее повторяется раз в 2 секунды.
|
||||
- Пока подтверждения нет, снизу показываются понятные статусы ожидания.
|
||||
- После подтверждения открывается экран «Поздравляем с регистрацией» с одной кнопкой `Войти в аккаунт`.
|
||||
- Кнопка `Войти в аккаунт` открывает штатный экран сохранения ключей с тремя галочками.
|
||||
- Стрелка назад в левом верхнем углу на обоих экранах возвращает в главное меню.
|
||||
- После успешного сохранения ключей регистрационный черновик и адрес кошелька очищаются.
|
||||
|
||||
## Ожидаемый результат
|
||||
- Пользователь не видит преждевременное поздравление до подтверждения регистрации в Solana.
|
||||
- Финальный успех показывается только после появления подтверждения регистрации.
|
||||
- Вход после регистрации идёт через тот же стандартный сценарий сохранения ключей, что и в обычном login-flow.
|
||||
|
||||
## Статус
|
||||
- pending
|
||||
@@ -1,20 +0,0 @@
|
||||
# Недопроверенные фичи
|
||||
|
||||
Эта папка хранит список доработок, которые уже реализованы, но ещё не подтверждены ручной проверкой.
|
||||
|
||||
## Как использовать
|
||||
|
||||
1. При каждом коммите с новыми пользовательскими фичами (если нужна ручная проверка) добавить новый файл:
|
||||
- формат: `YYYY-MM-DD_HHMM_<short-feature-name>.md`
|
||||
- название `<short-feature-name>` и текст файла по возможности писать на русском языке
|
||||
2. В файле указать:
|
||||
- что сделано;
|
||||
- как проверять;
|
||||
- ожидаемый результат;
|
||||
- текущий статус (`pending` / `in_progress` / `done`).
|
||||
3. После подтверждения работоспособности — удалить файл фичи из этой папки.
|
||||
|
||||
## Важно
|
||||
|
||||
- `README.md` не удаляется.
|
||||
- Количество недопроверенных фич = число файлов `*.md` в этой папке, кроме `README.md`.
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
# Регистрация: FAQ и режим пароля из 12 слов
|
||||
|
||||
- краткое описание:
|
||||
- на экране регистрации добавлен блок частых вопросов с переходом на отдельный экран справки;
|
||||
- добавлен альтернативный режим ввода пароля через 12 полей-слов в кошелёчном формате, которые склеиваются в одну строку без изменения API;
|
||||
- такой же режим добавлен и на экран входа по логину и паролю.
|
||||
|
||||
- что проверять:
|
||||
- на стартовом экране открыть `Зарегистрироваться`;
|
||||
- убедиться, что внизу экрана есть кнопки FAQ;
|
||||
- открыть несколько вопросов и проверить возврат обратно на регистрацию;
|
||||
- включить галочку `Представить пароль в виде 12 слов`;
|
||||
- убедиться, что появляется сетка с нумерованными полями в 3 колонки;
|
||||
- ввести часть слов, перейти дальше и проверить, что шаг подтверждения и генерация ключей работают;
|
||||
- выключить галочку и проверить, что пароль остаётся собранным в одном поле;
|
||||
- открыть экран входа по паролю и повторить те же проверки для режима `12 слов`;
|
||||
- пройти регистрацию до шага оплаты без ошибок интерфейса.
|
||||
|
||||
- ожидаемый результат:
|
||||
- FAQ открывается отдельным экраном и содержит понятные ответы;
|
||||
- режим `12 слов` не ломает регистрацию и вход и даёт тот же поток, что и обычный пароль;
|
||||
- пароль не отправляется в новом формате, а продолжает использоваться как одна строка.
|
||||
|
||||
- статус:
|
||||
- pending
|
||||
@@ -1,25 +0,0 @@
|
||||
# Временная бесплатная загрузка аватара в Arweave
|
||||
|
||||
- краткое описание фичи:
|
||||
Добавлены два временных `Test...` API для бесплатной загрузки маленьких аватаров в Arweave через серверный кошелёк с лимитом `3` загрузки на пользователя. В UI мастера смены аватара добавлен пункт `Залить аватар бесплатно`.
|
||||
|
||||
- что именно проверять:
|
||||
1. Пользователь с активной сессией открывает редактирование профиля.
|
||||
2. По нажатию на аватар открывается мастер `Сменить аватар`.
|
||||
3. В мастере есть пункт `Залить аватар бесплатно`.
|
||||
4. До первой загрузки UI показывает остаток `3 из 3`.
|
||||
5. Маленький JPEG/PNG/WebP после уменьшения до файла <= `128 KB` успешно уходит через `TestUploadFreeAvatar`.
|
||||
6. После загрузки приходит `txId`, и аватар сохраняется в профиль как `avatar.ar`.
|
||||
7. Остаток уменьшается: `2`, `1`, `0`.
|
||||
8. На четвёртой попытке сервер отвечает понятной ошибкой про исчерпанный бесплатный лимит.
|
||||
9. Если итоговый уменьшенный файл всё ещё > `128 KB`, UI не отправляет его и показывает понятную ошибку.
|
||||
10. Если серверный Arweave JWK/path не настроен, UI получает понятную ошибку временной функции.
|
||||
|
||||
- ожидаемый результат:
|
||||
- первые 3 маленькие аватарки загружаются через серверный Arweave-кошелёк;
|
||||
- после каждой успешной загрузки `ava` в профиле указывает на новый `txId`;
|
||||
- после исчерпания лимита дальнейшая бесплатная загрузка блокируется без записи в профиль;
|
||||
- обычная загрузка через свой Arweave-кошелёк продолжает работать отдельно.
|
||||
|
||||
- статус:
|
||||
pending
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
# Общий список каналов без stories
|
||||
|
||||
- Краткое описание:
|
||||
вкладка `Каналы` переведена на единый список без разделения на "мои" и "подписки".
|
||||
Название канала в списке теперь показывается как `login_владельца/название_канала`.
|
||||
Служебный канал `stories` скрыт из списка каналов, поиска, подписки и связанных UI-сценариев.
|
||||
|
||||
- Что проверять:
|
||||
1. Открыть вкладку `Каналы`.
|
||||
2. Убедиться, что сразу показывается один общий список.
|
||||
3. Проверить, что свои и чужие каналы отображаются вместе.
|
||||
4. Проверить формат названий: `ownerLogin/channelName`.
|
||||
5. Открыть свой канал и убедиться, что внутри сохраняется UI владельца.
|
||||
6. Открыть чужой канал и убедиться, что внутри сохраняется UI подписчика.
|
||||
7. Проверить, что `stories` не отображается:
|
||||
- в общем списке;
|
||||
- в поиске каналов;
|
||||
- в подписке на канал;
|
||||
- в списках выбора канала для репоста.
|
||||
|
||||
- Ожидаемый результат:
|
||||
- вкладка `Каналы` больше не делится на два режима;
|
||||
- все видимые каналы идут единым списком;
|
||||
- `stories` нигде не виден и не предлагается пользователю;
|
||||
- переход в канал сохраняет корректный UI в зависимости от владельца.
|
||||
|
||||
- Статус:
|
||||
`pending`
|
||||
@@ -1,47 +0,0 @@
|
||||
# Crash-safe запись обычного `AddBlock` через `tmp_bch`
|
||||
|
||||
## Кратко
|
||||
|
||||
Обычный `AddBlock` переведён на схему:
|
||||
|
||||
1. сборка `<blockchainName>.tmp_bch`;
|
||||
2. запись sidecar `<blockchainName>.write_check` с `blockNumber` и `blockHash`;
|
||||
3. создание пустого marker `<blockchainName>.write_pending`;
|
||||
4. SQL-транзакция;
|
||||
5. атомарная подмена `tmp -> main`;
|
||||
6. удаление временных файлов.
|
||||
|
||||
## Что проверить
|
||||
|
||||
1. Обычный `AddBlock` на свежей цепочке.
|
||||
2. Падение до SQL-commit:
|
||||
- должны остаться только временные файлы;
|
||||
- на старте они должны быть удалены.
|
||||
3. Падение после SQL-commit, но до `atomicReplaceBlockchainFile(...)`:
|
||||
- на старте recovery должен довести swap до конца.
|
||||
4. Падение после `atomicReplaceBlockchainFile(...)`, но до удаления marker/sidecar:
|
||||
- на старте recovery должен просто подчистить хвост.
|
||||
5. Сценарий без marker:
|
||||
- `tmp_bch` / `write_check` считаются мусором и удаляются.
|
||||
|
||||
## Ожидаемый результат
|
||||
|
||||
- БД и файловая версия цепочки остаются согласованными.
|
||||
- Повторный старт сервера не ломает chain и не требует ручной правки файлов.
|
||||
- `BlockchainTmpRecoveryOnStartup` корректно обрабатывает и живые остатки, и мусор.
|
||||
|
||||
## Статус
|
||||
|
||||
`pending`
|
||||
|
||||
## Что уже сделано
|
||||
|
||||
- В коде есть `tmp_bch`, `write_check` и `write_pending`.
|
||||
- `BlockchainWriter` пишет обычный `AddBlock` через временные артефакты.
|
||||
- `BlockchainTmpRecoveryOnStartup` умеет добивать или чистить незавершённую запись.
|
||||
|
||||
## Что ещё перепроверить
|
||||
|
||||
- ручной crash-test на тестовом сервере;
|
||||
- совместимость с уже существующими `resync_pending` marker-файлами;
|
||||
- отсутствие ложных срабатываний на старых временных файлах.
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
# Проверка аварийных остановок на разных этапах
|
||||
|
||||
## Кратко
|
||||
|
||||
Нужно отдельно проверить, как сервер восстанавливается после внезапной остановки:
|
||||
|
||||
1. во время обычного `AddBlock` / `tmp_bch`-pipeline;
|
||||
2. во время `full resync` цепочки;
|
||||
3. во время startup recovery, если остановка произошла на предыдущем запуске;
|
||||
4. при обычном апгрейде сервиса без явного crash-сценария.
|
||||
|
||||
## Что проверять
|
||||
|
||||
1. Остановка сервиса до `commit` БД.
|
||||
2. Остановка сервиса после `commit`, но до замены `main.bch`.
|
||||
3. Остановка сервиса во время `BlockchainResyncCleanupDAO`.
|
||||
4. Остановка сервиса во время повторной загрузки цепочки по `GetBlockchainBlock`.
|
||||
5. Поведение при обычном `systemctl restart`, когда сервер сам должен добить recovery.
|
||||
|
||||
## Ожидаемый результат
|
||||
|
||||
- после старта сервер либо дочищает временные артефакты, либо завершает незаконченный `resync`;
|
||||
- не остаётся битых `.tmp_bch`, `.write_check`, `.write_pending`, `.resync_pending`;
|
||||
- БД и файлы цепочки остаются согласованными;
|
||||
- обычная работа сервера не стартует поверх незавершённого recovery.
|
||||
|
||||
## Статус
|
||||
|
||||
`pending`
|
||||
@@ -105,7 +105,7 @@ DAO в текущем виде не является отдельной Anchor-
|
||||
|
||||
## Правило разделения с основным сервером
|
||||
|
||||
Solana-модуль лежит в основном репозитории как отдельная папка `shine-solana/shine/`, но не подключается автоматически к сборке или деплою основного сервера SHiNE. Команды `deployServer` и `deployUI` не должны деплоить Anchor-программы. Solana build/deploy выполняется отдельно из папки `shine-solana/shine/` по локальным правилам модуля.
|
||||
Solana-модуль лежит в основном репозитории как отдельная папка `shine-solana/shine/`, но не подключается автоматически к сборке или деплою основного сервера SHiNE. Скрипты основного server/UI deploy из `deploy/scripts/` не должны деплоить Anchor-программы. Solana build/deploy выполняется отдельно из папки `shine-solana/shine/` по локальным правилам модуля.
|
||||
|
||||
## Движение денег
|
||||
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
# Деплой SHiNE (шаблон)
|
||||
|
||||
Этот раздел хранит актуальные инструкции по деплою.
|
||||
|
||||
## Базовый сервер
|
||||
|
||||
- SSH: `player@shineup.me`
|
||||
- Домен: `shineup.me`
|
||||
- Базовый путь: `/home/player`
|
||||
|
||||
Для всех рабочих инструкций и скриптов использовать доменное имя `shineup.me`, а не фиксированный IP:
|
||||
|
||||
- актуальный IP должен браться через DNS-резолв на момент подключения;
|
||||
- ручное дублирование IP в документации и deploy-скриптах не поддерживать.
|
||||
|
||||
## Контуры деплоя
|
||||
|
||||
- Production:
|
||||
- SSH: `player@shineup.me`
|
||||
- Домен: `shineup.me`
|
||||
- IP: `178.208.64.62`
|
||||
- Second production:
|
||||
- SSH: `player@193.8.215.70`
|
||||
- Домен: `server2.shineup.me`
|
||||
- IP: `193.8.215.70`
|
||||
## Локальные команды
|
||||
|
||||
- Default server deploy: `./gradlew deployServer` или `./gradlew deployServerTest2`
|
||||
- Default UI deploy: `./gradlew deployUI` или `./gradlew deployUITest2`
|
||||
- Production server deploy: `./gradlew deployServerProduction`
|
||||
- Production UI deploy: `./gradlew deployUIProduction`
|
||||
- Локальный запуск: `./gradlew startLocal`
|
||||
|
||||
## Отдельные тестовые VPS
|
||||
|
||||
- Отдельный стенд с 4 devnet-серверами на одном VPS описан в:
|
||||
- `docs/deploy/test-server/README.md`
|
||||
|
||||
## Политика подтверждений
|
||||
|
||||
- `shineup.me` и `server2.shineup.me` считать production-контурами.
|
||||
- Любые изменения на production-контурах, включая deploy сервера, deploy UI, конфиги, перезапуски и миграции, делать только после отдельного явного подтверждения пользователя.
|
||||
## Second production deploy (`server2.shineup.me`)
|
||||
|
||||
- Это второй production-сервер SHiNE.
|
||||
- Исторические задачи `deployServer`, `deployUI`, `deployServerTest2`, `deployUITest2` по-прежнему могут указывать сюда.
|
||||
- Серверный deploy не запускает JUnit/IT-тесты на удалённом сервере.
|
||||
- `deployServer` / `deployServerTest2` делают:
|
||||
- сборку fat-jar локально;
|
||||
- синхронизацию `data/` и `shine.sqlite` с production `shineup.me`;
|
||||
- перенос `application.properties` с production с поправкой `server.ui.indexPath` на `/home/player/SHiNE/shine-ui/index.html`;
|
||||
- установку `systemd` unit на `193.8.215.70`;
|
||||
- перезапуск `shine-server.service`;
|
||||
- установку/проверку Caddy для `server2.shineup.me`.
|
||||
- `deployUI` / `deployUITest2` публикуют UI в `/home/player/SHiNE/shine-ui` на `193.8.215.70`.
|
||||
- Важно: историческое имя `deployUITest2` вводит в заблуждение. По умолчанию эта задача деплоит на `server2.shineup.me`, а не на `t2.shineup.me`.
|
||||
|
||||
## UI-деплой и Caddy (обязательно)
|
||||
|
||||
- Целевая директория UI-деплоя: `/home/player/SHiNE/shine-ui`.
|
||||
- `Caddyfile` на сервере должен смотреть в ту же директорию через `root * /home/player/SHiNE/shine-ui`.
|
||||
- В `deploy_shine-PWA.sh` добавлена проверка: скрипт ищет блок `shineup.me { ... }` (или значение `EXPECTED_CADDY_SITE`) и проверяет `root` внутри этого блока.
|
||||
- Если `root` внутри целевого блока не совпадает, деплой прерывается с ошибкой.
|
||||
- Для ручного обхода проверки (только осознанно): `ALLOW_CADDY_MISMATCH=1 ./gradlew deployUI`.
|
||||
- При необходимости можно явно переопределить путь деплоя:
|
||||
- `REMOTE_UI_DIR=/нужный/путь ./gradlew deployUI`
|
||||
- `EXPECTED_CADDY_UI_ROOT=/нужный/путь ./gradlew deployUI`
|
||||
- `EXPECTED_CADDY_SITE=example.com ./gradlew deployUI`
|
||||
|
||||
## Временные тестовые сайты Solana tickets
|
||||
|
||||
- Для HTML UI программы `shine_payments` используется отдельный временный тестовый сайт.
|
||||
- Основной каталог публикации:
|
||||
- `/home/player/sites/test-solana-tickets.shineup.me`
|
||||
- Рабочие домены:
|
||||
- `https://test-solana-tickets.shineup.me`
|
||||
- `https://test-solana-tickets.shiningpeople.ru`
|
||||
- Назначение:
|
||||
- ручная проверка сценариев покупки билетов;
|
||||
- проверка DAO-инструментов и лимитов менеджеров;
|
||||
- проверка ручного добавления билетов и `step_payout`.
|
||||
- Эти сайты не считать основным UI SHiNE; это отдельная тестовая публикация под Solana-часть.
|
||||
|
||||
### Важно для локального UI (history-router / Ctrl+F5)
|
||||
|
||||
- Локальный UI **обязательно** поднимать только через `./gradlew startLocal`.
|
||||
- Эта задача запускает `scripts/local_spa_server.py`, который делает SPA fallback: любой неизвестный путь (`/m/...`, `/channel/...`) возвращает `index.html`.
|
||||
- Это обязательно для корректной работы `Ctrl+F5` на внутренних роутов без `404`.
|
||||
- Рабочий URL выводится задачей в консоль в формате: `http://localhost:<WEB_PORT>/?localWsPort=<WS_PORT>`.
|
||||
|
||||
## Обязательные правила
|
||||
|
||||
1. Перед серверным деплоем проверить локально.
|
||||
2. При нестандартном деплое (другой хост, другая структура, ручные шаги) обязательно уточнить у пользователя, нужно ли обновить этот шаблон.
|
||||
3. Если деплой-процесс изменился, этот файл и файлы в `servers/` обновлять в том же коммите.
|
||||
@@ -1,43 +0,0 @@
|
||||
# Сервер `193.8.215.70` — второй production (`server2.shineup.me`)
|
||||
|
||||
- Пользователь: `player`
|
||||
- Домен: `server2.shineup.me`
|
||||
- Логин сервера: `server2shineupme`
|
||||
- Каталог SHiNE: `/home/player/SHiNE`
|
||||
- UI публикация для Caddy: `/home/player/SHiNE/shine-ui`
|
||||
- Сервер: `/home/player/SHiNE/shine-server/shine-server.jar`
|
||||
- Данные: `/home/player/SHiNE/shine-server/data/`
|
||||
- `shine.sqlite`
|
||||
- `*.bch`
|
||||
- Логи сервера: `/home/player/SHiNE/shine-server/logs/app.log`
|
||||
|
||||
## Сервисы
|
||||
|
||||
- `shine-server.service` (systemd)
|
||||
- `caddy.service` (systemd)
|
||||
|
||||
## Статус
|
||||
|
||||
- Это второй production-сервер SHiNE.
|
||||
- Исторические имена deploy-задач могут по-прежнему ссылаться на него как на `test2`.
|
||||
- В документации и в списке окружений считать его production-контуром.
|
||||
|
||||
## Caddy
|
||||
|
||||
- Конфиг: `/etc/caddy/Caddyfile`
|
||||
- Сайты:
|
||||
- `server2.shineup.me`
|
||||
- `agent.shiningpeople.ru`
|
||||
- Для `server2.shineup.me`:
|
||||
- `root * /home/player/SHiNE/shine-ui`
|
||||
- `try_files {path} /index.html`
|
||||
- `reverse_proxy /ws* -> 127.0.0.1:7070`
|
||||
|
||||
## Deploy
|
||||
|
||||
- Default server deploy:
|
||||
- `./gradlew deployServer`
|
||||
- `./gradlew deployServerTest2`
|
||||
- Default UI deploy:
|
||||
- `./gradlew deployUI`
|
||||
- `./gradlew deployUITest2`
|
||||
@@ -1,48 +0,0 @@
|
||||
# Сервер `shineup.me` — основной
|
||||
|
||||
- SSH: `player@shineup.me`
|
||||
- Определение IP: через DNS-резолв домена `shineup.me` на момент подключения
|
||||
- Текущий IP на момент последнего обновления документа: `178.208.64.62`
|
||||
- Пользователь: `player`
|
||||
- Базовый путь: `/home/player`
|
||||
- Каталог SHiNE: `/home/player/SHiNE`
|
||||
- UI публикация: `/home/player/SHiNE/shine-ui`
|
||||
- Сервер: `/home/player/SHiNE/shine-server/shine-server.jar`
|
||||
- Данные: `/home/player/SHiNE/shine-server/data/`
|
||||
- Логи сервера: `/home/player/SHiNE/shine-server/logs/app.log`
|
||||
|
||||
## Сервисы
|
||||
|
||||
- `shine-server.service` (systemd)
|
||||
- `caddy.service` (systemd)
|
||||
|
||||
## Caddy
|
||||
|
||||
- Активный конфиг (через systemd `ExecStart`): `/etc/caddy/Caddyfile`
|
||||
- Для UI:
|
||||
- `root * /home/player/SHiNE/shine-ui`
|
||||
- `try_files {path} /index.html` (SPA fallback)
|
||||
- no-cache заголовки
|
||||
- `reverse_proxy /ws* -> 127.0.0.1:7070`
|
||||
|
||||
## Дополнительно
|
||||
|
||||
- Для отдельной админки `shine_payments` используется каталог:
|
||||
- `/home/player/sites/test-solana-tickets.shineup.me`
|
||||
- Эта публикация используется как временный тестовый сайт для сценариев покупки билетов и выплат `shine_payments`.
|
||||
- Домены этой публикации:
|
||||
- `https://test-solana-tickets.shineup.me`
|
||||
- `https://test-solana-tickets.shiningpeople.ru`
|
||||
- Для всех deploy-скриптов и инструкций использовать именно `player@shineup.me`, без жёсткой фиксации IP.
|
||||
|
||||
## Правило изменений
|
||||
|
||||
- `shineup.me` — production.
|
||||
- Любые изменения на этом сервере делать только после отдельного явного подтверждения пользователя.
|
||||
|
||||
## Deploy
|
||||
|
||||
- Production deploy-задачи:
|
||||
- `./gradlew deployServerProduction`
|
||||
- `./gradlew deployUIProduction`
|
||||
- Default deploy-задачи `./gradlew deployServer` и `./gradlew deployUI` сюда больше не относятся.
|
||||
@@ -1,173 +0,0 @@
|
||||
# VPS `178.208.90.249` (`player`) — 4 тестовых SHiNE-сервера на devnet
|
||||
|
||||
## Назначение
|
||||
|
||||
Этот VPS используется как отдельный тестовый стенд для одновременного запуска 4 SHiNE-серверов:
|
||||
- `server_t1` → `https://t1.shineup.me`
|
||||
- `server_t2` → `https://t2.shineup.me`
|
||||
- `server_t3` → `https://t3.shineup.me`
|
||||
- `server_t4` → `https://t4.shineup.me`
|
||||
|
||||
Все 4 инстанса работают через Solana `devnet`.
|
||||
|
||||
## Каталоги
|
||||
|
||||
Структура в домашней папке пользователя `player`:
|
||||
- `/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`
|
||||
|
||||
Дополнительно:
|
||||
- подробная памятка на самом сервере: `/home/player/Agents.md`
|
||||
|
||||
## Порты и systemd
|
||||
|
||||
Соответствие инстансов:
|
||||
- `t1` → localhost `7101` → `shine-t1.service`
|
||||
- `t2` → localhost `7102` → `shine-t2.service`
|
||||
- `t3` → localhost `7103` → `shine-t3.service`
|
||||
- `t4` → localhost `7104` → `shine-t4.service`
|
||||
|
||||
Каждый unit запускает один и тот же jar:
|
||||
- `/home/player/tX/server/shine-server.jar`
|
||||
|
||||
Рабочая директория каждого unit:
|
||||
- `/home/player/tX/server`
|
||||
|
||||
Это важно, потому что сервер читает внешний `application.properties` именно из текущей рабочей директории.
|
||||
|
||||
## Конфиги сервера
|
||||
|
||||
У каждого инстанса свой файл:
|
||||
- `/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`
|
||||
- `server.info.origin=devnet`
|
||||
|
||||
Важно:
|
||||
- Program ID SHiNE одинаковые для mainnet и devnet.
|
||||
- Поэтому для перевода инстанса на devnet достаточно правильных `solana.cluster` и `solana.rpcUrl`.
|
||||
|
||||
## Конфиги UI
|
||||
|
||||
У каждого домена лежит отдельная копия `shine-UI`.
|
||||
|
||||
В каждой копии меняется `js/deploy-config.js`:
|
||||
- `defaultServerLogin=server_tX`
|
||||
- `defaultServerAddress=tX.shineup.me`
|
||||
- `defaultSolanaCluster=devnet`
|
||||
- `defaultSolanaEndpoint=https://api.devnet.solana.com`
|
||||
|
||||
За счёт этого:
|
||||
- UI по умолчанию открывает именно свой сервер
|
||||
- `server-ui` формы по умолчанию используют именно `devnet`
|
||||
|
||||
## Caddy
|
||||
|
||||
Конфиг:
|
||||
- `/etc/caddy/Caddyfile`
|
||||
|
||||
Логика по каждому сайту одинаковая:
|
||||
- статические файлы берутся из `/home/player/tX/UI`
|
||||
- путь `/ws` проксируется на `127.0.0.1:710X`
|
||||
|
||||
Права доступа:
|
||||
- каталог `/home/player` и `/home/player/tX` должен быть доступен на `+x` для чтения через Caddy
|
||||
- все каталоги и файлы в `/home/player/tX/UI` должны быть читаемы пользователем `caddy`
|
||||
|
||||
## Что уже проверено
|
||||
|
||||
После настройки было подтверждено:
|
||||
- `https://t1.shineup.me` отвечает `HTTP 200`
|
||||
- `https://t2.shineup.me` отвечает `HTTP 200`
|
||||
- `https://t3.shineup.me` отвечает `HTTP 200`
|
||||
- `https://t4.shineup.me` отвечает `HTTP 200`
|
||||
- `Caddy` успешно выпустил сертификаты Let's Encrypt на все 4 домена
|
||||
- все 4 процесса Java слушают свои порты `7101..7104`
|
||||
- в логах есть строка `WS сервер запущен на ws://localhost:710X/ws`
|
||||
|
||||
## Важный operational-нюанс
|
||||
|
||||
Официальный `https://api.devnet.solana.com` может отдавать `HTTP 429`, если несколько инстансов одновременно делают bootstrap PDA/sync-серверов на старте.
|
||||
|
||||
На практике это уже проявилось:
|
||||
- `t3` и `t4` подтянули `sync_servers` сразу
|
||||
- `t1` и `t2` на первом одновременном старте словили `429`
|
||||
- после последовательного рестарта `shine-t1` и `shine-t2` они тоже успешно сохранили `sync_servers`
|
||||
|
||||
Если после очередного деплоя какой-то инстанс не подтянул `sync_servers`, сначала делать не массовый рестарт, а по одному:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart shine-t1
|
||||
sleep 8
|
||||
sudo systemctl restart shine-t2
|
||||
sleep 8
|
||||
sudo systemctl restart shine-t3
|
||||
sleep 8
|
||||
sudo systemctl restart shine-t4
|
||||
```
|
||||
|
||||
## Как обновлять сервер
|
||||
|
||||
1. Локально собрать jar:
|
||||
|
||||
```bash
|
||||
./gradlew shadowJar
|
||||
```
|
||||
|
||||
2. Залить `SHiNE-server/build/libs/shine-server.jar` в каждый нужный каталог:
|
||||
- `/home/player/t1/server/shine-server.jar`
|
||||
- `/home/player/t2/server/shine-server.jar`
|
||||
- `/home/player/t3/server/shine-server.jar`
|
||||
- `/home/player/t4/server/shine-server.jar`
|
||||
|
||||
3. Если менялся runtime-конфиг, обновить соответствующий `application.properties`.
|
||||
|
||||
4. Перезапускать сервисы лучше по одному, с паузой, чтобы снизить шанс `429` от devnet RPC.
|
||||
|
||||
## Как обновлять UI
|
||||
|
||||
1. Взять свежую локальную папку `shine-UI/`.
|
||||
2. Для каждой копии подставить свой `deploy-config.js`.
|
||||
3. Скопировать файлы в `/home/player/tX/UI/`.
|
||||
4. Проверить права чтения для Caddy.
|
||||
|
||||
## Полезные команды на VPS
|
||||
|
||||
Проверка сервисов:
|
||||
|
||||
```bash
|
||||
sudo systemctl --no-pager --full status caddy shine-t1 shine-t2 shine-t3 shine-t4
|
||||
```
|
||||
|
||||
Перезапуск одного инстанса:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart shine-t1
|
||||
```
|
||||
|
||||
Логи одного инстанса:
|
||||
|
||||
```bash
|
||||
tail -n 200 /home/player/t1/server/logs/app.log
|
||||
sudo journalctl -u shine-t1 -n 200 --no-pager
|
||||
```
|
||||
|
||||
Проверка Caddy:
|
||||
|
||||
```bash
|
||||
sudo caddy validate --config /etc/caddy/Caddyfile
|
||||
curl -I https://t1.shineup.me
|
||||
```
|
||||
@@ -1,12 +0,0 @@
|
||||
# Тестовый VPS с 4 SHiNE-серверами
|
||||
|
||||
В этой папке описан отдельный тестовый VPS `178.208.90.249`, на котором подняты 4 независимых SHiNE-инстанса под доменами `t1.shineup.me` ... `t4.shineup.me`.
|
||||
|
||||
Текущая основная схема:
|
||||
- пользователь на VPS: `player`
|
||||
- все 4 инстанса работают через `devnet`
|
||||
- каждый инстанс имеет свой каталог `server`, свой каталог `UI`, свой `systemd` unit и свой localhost-порт
|
||||
- внешний HTTPS и маршрутизацию `/ws` обслуживает один общий `Caddy`
|
||||
|
||||
Детальное описание:
|
||||
- [178.208.90.249_quad_devnet.md](/home/ai/work/SHiNE/SHiNE-server-sha256/docs/deploy/test-server/178.208.90.249_quad_devnet.md)
|
||||
@@ -1,75 +0,0 @@
|
||||
# Установка TURN сервера (coturn)
|
||||
|
||||
## 1. Что нужно
|
||||
- Сервер с публичным IP (в примере: `37.214.58.208`).
|
||||
- Доступ `root` по SSH.
|
||||
- Открытые порты в firewall:
|
||||
- `3478/tcp`
|
||||
- `3478/udp`
|
||||
- диапазон relay-портов, например `49160-49200/udp`
|
||||
|
||||
## 2. Быстрая установка (рекомендуется)
|
||||
В проекте уже есть готовый скрипт:
|
||||
|
||||
```bash
|
||||
sudo bash scripts/setup_turn_coturn.sh \
|
||||
--secret "CHANGE_ME_LONG_RANDOM_SECRET" \
|
||||
--realm "shineup.me"
|
||||
```
|
||||
|
||||
Что делает скрипт:
|
||||
- ставит `coturn`;
|
||||
- включает режим `use-auth-secret` (временные логин/пароль);
|
||||
- пишет `/etc/turnserver.conf`;
|
||||
- включает и перезапускает сервис `coturn`.
|
||||
|
||||
## 3. Проверка
|
||||
Проверить статус:
|
||||
|
||||
```bash
|
||||
sudo systemctl status coturn --no-pager
|
||||
```
|
||||
|
||||
Проверить, что порт слушается:
|
||||
|
||||
```bash
|
||||
sudo ss -lntup | grep 3478
|
||||
```
|
||||
|
||||
## 4. Ручная установка (если без скрипта)
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y coturn
|
||||
```
|
||||
|
||||
Пример `/etc/turnserver.conf`:
|
||||
|
||||
```conf
|
||||
listening-port=3478
|
||||
fingerprint
|
||||
lt-cred-mech
|
||||
use-auth-secret
|
||||
static-auth-secret=CHANGE_ME_LONG_RANDOM_SECRET
|
||||
realm=shineup.me
|
||||
external-ip=37.214.58.208
|
||||
listening-ip=0.0.0.0
|
||||
relay-ip=37.214.58.208
|
||||
min-port=49160
|
||||
max-port=49200
|
||||
no-cli
|
||||
simple-log
|
||||
```
|
||||
|
||||
В `/etc/default/coturn`:
|
||||
|
||||
```conf
|
||||
TURNSERVER_ENABLED=1
|
||||
```
|
||||
|
||||
Запуск:
|
||||
|
||||
```bash
|
||||
sudo systemctl enable coturn
|
||||
sudo systemctl restart coturn
|
||||
```
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
# Подключение TURN к SHiNE-серверу
|
||||
|
||||
Начиная с текущей версии, клиент звонков запрашивает ICE-конфиг у backend через WS-операцию `GetCallIceConfig` и использует её для `RTCPeerConnection`.
|
||||
|
||||
## 1. Настройки backend
|
||||
Файл: `src/main/resources/application.properties`
|
||||
|
||||
Ключи:
|
||||
|
||||
```properties
|
||||
call.ice.stun.urls=stun:stun.l.google.com:19302
|
||||
call.ice.turn.urls=turn:37.214.58.208:3478?transport=udp,turn:37.214.58.208:3478?transport=tcp
|
||||
call.ice.turn.ttlSec=600
|
||||
call.ice.turn.userPrefix=shine
|
||||
call.ice.turn.sharedSecret=CHANGE_ME_LONG_RANDOM_SECRET
|
||||
|
||||
# fallback (если не используете shared-secret)
|
||||
call.ice.turn.username=
|
||||
call.ice.turn.password=
|
||||
```
|
||||
|
||||
## 2. Рекомендуемый режим (временные credentials)
|
||||
- На coturn и на SHiNE-сервере должен быть **одинаковый** secret:
|
||||
- coturn: `static-auth-secret=...`
|
||||
- SHiNE: `call.ice.turn.sharedSecret=...`
|
||||
- Тогда SHiNE выдаёт короткоживущие `turnUsername/turnPassword` (TTL).
|
||||
|
||||
## 3. Fallback режим (статический логин/пароль)
|
||||
Если временные credentials не используются:
|
||||
|
||||
```properties
|
||||
call.ice.turn.sharedSecret=
|
||||
call.ice.turn.username=turn_user
|
||||
call.ice.turn.password=turn_password
|
||||
```
|
||||
|
||||
## 4. Деплой после изменения
|
||||
```bash
|
||||
./gradlew deployServerNoCleanNoTests
|
||||
./gradlew deployWEB
|
||||
```
|
||||
|
||||
## 5. Проверка
|
||||
1. Авторизоваться двумя клиентами.
|
||||
2. Запустить звонок.
|
||||
3. Проверить, что звонок устанавливается даже в сети, где прямой P2P затруднён.
|
||||
4. Если TURN недоступен, клиент автоматически откатится к STUN-конфигу по умолчанию.
|
||||
|
||||
@@ -1,79 +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}"
|
||||
REMOTE_CADDYFILE="${REMOTE_CADDYFILE:-/etc/caddy/Caddyfile}"
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
cleanup() {
|
||||
rm -rf "$TMP_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
cat >"$TMP_DIR/Caddyfile" <<EOF
|
||||
{
|
||||
auto_https disable_redirects
|
||||
}
|
||||
|
||||
agent.shiningpeople.ru {
|
||||
redir / /agent/index.html 308
|
||||
redir /agent /agent/index.html 308
|
||||
|
||||
handle_path /agent/* {
|
||||
reverse_proxy 127.0.0.1:8765
|
||||
}
|
||||
}
|
||||
|
||||
$TARGET_DOMAIN {
|
||||
encode zstd gzip
|
||||
|
||||
@ws path /ws /ws/*
|
||||
handle @ws {
|
||||
reverse_proxy 127.0.0.1:7070
|
||||
}
|
||||
|
||||
handle {
|
||||
root * $REMOTE_UI_DIR
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:80 {
|
||||
encode zstd gzip
|
||||
|
||||
@ws path /ws /ws/*
|
||||
handle @ws {
|
||||
reverse_proxy 127.0.0.1:7070
|
||||
}
|
||||
|
||||
handle {
|
||||
root * $REMOTE_UI_DIR
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
ssh -o BatchMode=yes -o ConnectTimeout=20 "$TARGET_HOST" "echo SSH OK" >/dev/null
|
||||
ssh "$TARGET_HOST" "sudo -n true"
|
||||
rsync -az "$TMP_DIR/Caddyfile" "$TARGET_HOST:/tmp/caddy-test2.new"
|
||||
ssh "$TARGET_HOST" "set -euo pipefail; \
|
||||
sudo mv -f /tmp/caddy-test2.new '$REMOTE_CADDYFILE'; \
|
||||
sudo chown root:root '$REMOTE_CADDYFILE'; \
|
||||
sudo caddy validate --config '$REMOTE_CADDYFILE'; \
|
||||
sudo systemctl restart caddy"
|
||||
@@ -1,7 +0,0 @@
|
||||
# server-backup
|
||||
|
||||
- `archive/` — локальные полные бэкапы по датам (не коммитятся).
|
||||
- `scheme/` — лёгкая схема восстановления (коммитится).
|
||||
- `backup-version.properties` — версии схемы и полного бэкапа.
|
||||
|
||||
Текущий сервер-источник: `shineup.me`.
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from '../services/solana-wallet-service.js';
|
||||
import { loadSolanaWeb3 } from '../vendor/solana-web3-loader.js';
|
||||
import {
|
||||
checkLoginExistsOnSolana,
|
||||
formatSolanaErrorDetails,
|
||||
isUserAlreadyExistsSolanaError,
|
||||
registerUserOnSolana,
|
||||
@@ -24,8 +25,13 @@ import { defaultServerLogin } from '../deploy-config.js';
|
||||
|
||||
export const pageMeta = { id: 'registration-payment-view', title: 'Оплата регистрации', showAppChrome: false };
|
||||
const MIN_REQUIRED_SOL = 0.01;
|
||||
const AUTO_LOGIN_INITIAL_DELAY_MS = 10000;
|
||||
const AUTO_LOGIN_INITIAL_DELAY_MS = 1000;
|
||||
const AUTO_LOGIN_RETRY_MS = 2000;
|
||||
const REGISTRATION_PROGRESS_DURATION_MS = 12000;
|
||||
const REGISTRATION_POLL_START_DELAY_MS = 4000;
|
||||
const REGISTRATION_POLL_INTERVAL_MS = 2000;
|
||||
const REGISTRATION_CONFIRM_TIMEOUT_MS = 25000;
|
||||
const REGISTRATION_SUCCESS_SETTLE_DELAY_MS = 2000;
|
||||
const EMPTY_PASSWORD_WORDS = Array.from({ length: 12 }, () => '');
|
||||
|
||||
function getExplorerClusterName(endpoint) {
|
||||
@@ -60,6 +66,10 @@ function getCryptoRuntimeState() {
|
||||
return { hasCrypto, hasGetRandomValues, hasSubtle, secureContext };
|
||||
}
|
||||
|
||||
function clamp(value, min, max) {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
}
|
||||
|
||||
async function completeRegistrationLogin({ navigate, keyBundle }) {
|
||||
await authService.reconnect(state.entrySettings.shineServer);
|
||||
const result = await authService.createSessionForExistingUser(
|
||||
@@ -299,7 +309,7 @@ export function render({ navigate }) {
|
||||
}
|
||||
}
|
||||
|
||||
renderSolanaDoneStage({ navigate, status, keyBundle, registrationTxId });
|
||||
renderSolanaRegistrationStage({ navigate, status, keyBundle, registrationTxId });
|
||||
} catch (error) {
|
||||
const message = toUserMessage(error, 'Не удалось завершить регистрацию.');
|
||||
setAuthError(message);
|
||||
@@ -345,6 +355,192 @@ export function render({ navigate }) {
|
||||
return screen;
|
||||
}
|
||||
|
||||
function renderSolanaRegistrationStage({ navigate, status, keyBundle, registrationTxId = '' }) {
|
||||
const screen = document.querySelector('section.stack');
|
||||
if (!screen) return;
|
||||
const headerBackButton = screen.querySelector('.page-header .header-left .icon-btn');
|
||||
const card = screen.querySelector('.card.stack');
|
||||
if (!card) return;
|
||||
card.classList.add('registration-finish-card');
|
||||
|
||||
let progressTimerId = null;
|
||||
let pollStartTimerId = null;
|
||||
let pollTimerId = null;
|
||||
let successTimerId = null;
|
||||
let confirmationInFlight = false;
|
||||
let stageClosed = false;
|
||||
let successPending = false;
|
||||
let timeoutShown = false;
|
||||
const stageStartedAt = Date.now();
|
||||
const txExplorerUrl = makeSolanaExplorerTxUrl(registrationTxId, state.entrySettings.solanaServer);
|
||||
|
||||
const title = document.createElement('h2');
|
||||
title.className = 'registration-finish-title';
|
||||
title.textContent = 'Идёт регистрация в блокчейне...';
|
||||
|
||||
const hint = document.createElement('p');
|
||||
hint.className = 'auth-copy registration-finish-text';
|
||||
hint.textContent = 'Ждём, пока транзакция будет полностью одобрена сетью Solana.';
|
||||
|
||||
const txIdLine = document.createElement('p');
|
||||
txIdLine.className = 'meta-muted registration-finish-tx';
|
||||
if (registrationTxId && txExplorerUrl) {
|
||||
const txLabel = document.createElement('span');
|
||||
txLabel.textContent = 'Tx ID регистрации: ';
|
||||
const txLink = document.createElement('a');
|
||||
txLink.className = 'registration-finish-tx-link';
|
||||
txLink.href = txExplorerUrl;
|
||||
txLink.target = '_blank';
|
||||
txLink.rel = 'noopener noreferrer';
|
||||
txLink.textContent = registrationTxId;
|
||||
txIdLine.append(txLabel, txLink);
|
||||
} else {
|
||||
txIdLine.textContent = 'Tx ID регистрации: ожидаем подтверждённую подпись';
|
||||
}
|
||||
|
||||
const progressWrap = document.createElement('div');
|
||||
progressWrap.className = 'registration-progress';
|
||||
|
||||
const progressBar = document.createElement('div');
|
||||
progressBar.className = 'registration-progress-bar';
|
||||
progressWrap.append(progressBar);
|
||||
|
||||
const progress = document.createElement('p');
|
||||
progress.className = 'meta-muted registration-finish-progress';
|
||||
progress.textContent = 'Подготавливаем проверку подтверждения...';
|
||||
|
||||
const pollStatus = document.createElement('p');
|
||||
pollStatus.className = 'meta-muted registration-finish-progress';
|
||||
pollStatus.textContent = 'Через несколько секунд начнём проверять подтверждение регистрации.';
|
||||
|
||||
const stopTimers = () => {
|
||||
if (progressTimerId) {
|
||||
window.clearInterval(progressTimerId);
|
||||
progressTimerId = null;
|
||||
}
|
||||
if (pollStartTimerId) {
|
||||
window.clearTimeout(pollStartTimerId);
|
||||
pollStartTimerId = null;
|
||||
}
|
||||
if (pollTimerId) {
|
||||
window.clearInterval(pollTimerId);
|
||||
pollTimerId = null;
|
||||
}
|
||||
if (successTimerId) {
|
||||
window.clearTimeout(successTimerId);
|
||||
successTimerId = null;
|
||||
}
|
||||
};
|
||||
|
||||
const updateVisualProgress = () => {
|
||||
const elapsed = Date.now() - stageStartedAt;
|
||||
let widthPercent = 0;
|
||||
if (elapsed <= REGISTRATION_PROGRESS_DURATION_MS) {
|
||||
const phaseRatio = clamp(elapsed / REGISTRATION_PROGRESS_DURATION_MS, 0, 1);
|
||||
widthPercent = 100 * (1 - ((1 - phaseRatio) ** 1.85));
|
||||
} else {
|
||||
const extraRatio = 1 - Math.exp(-(elapsed - REGISTRATION_PROGRESS_DURATION_MS) / 9000);
|
||||
widthPercent = 88 + (10 * clamp(extraRatio, 0, 1));
|
||||
}
|
||||
progressBar.style.width = `${clamp(widthPercent, 0, 98)}%`;
|
||||
|
||||
if (elapsed < REGISTRATION_POLL_START_DELAY_MS) {
|
||||
progress.textContent = 'Отправили регистрацию в сеть. Даём транзакции несколько секунд на обработку.';
|
||||
return;
|
||||
}
|
||||
if (elapsed < REGISTRATION_CONFIRM_TIMEOUT_MS) {
|
||||
progress.textContent = 'Идёт ожидание подтверждения Solana. Индикатор может замедлиться, это нормально.';
|
||||
return;
|
||||
}
|
||||
progress.textContent = 'Подтверждение затянулось. Продолжаем автоматическую проверку.';
|
||||
};
|
||||
|
||||
const finalizeSuccess = () => {
|
||||
if (stageClosed || successPending) return;
|
||||
successPending = true;
|
||||
stopTimers();
|
||||
progress.textContent = 'Подтверждение получено. Завершаем регистрацию...';
|
||||
pollStatus.textContent = 'Запись уже найдена в Solana, готовим финальный экран.';
|
||||
status.style.display = 'none';
|
||||
progressBar.style.transition = `width ${REGISTRATION_SUCCESS_SETTLE_DELAY_MS}ms ease-out`;
|
||||
progressBar.style.width = '100%';
|
||||
successTimerId = window.setTimeout(() => {
|
||||
if (stageClosed) return;
|
||||
stageClosed = true;
|
||||
renderSolanaDoneStage({ navigate, status, keyBundle, registrationTxId });
|
||||
}, REGISTRATION_SUCCESS_SETTLE_DELAY_MS);
|
||||
};
|
||||
|
||||
const showTimeoutState = () => {
|
||||
if (timeoutShown || stageClosed) return;
|
||||
timeoutShown = true;
|
||||
status.className = 'status-line is-unavailable';
|
||||
status.textContent = 'Подтверждение не пришло вовремя. Возможно, регистрация уже прошла, а возможно ещё нет. Проверьте Tx ID ниже: мы продолжим автоматическую проверку.';
|
||||
status.style.display = '';
|
||||
pollStatus.textContent = 'Пока подтверждения нет. Подождите ещё немного, мы продолжаем проверять регистрацию.';
|
||||
};
|
||||
|
||||
const tryCheckRegistration = async () => {
|
||||
if (stageClosed || confirmationInFlight) return;
|
||||
confirmationInFlight = true;
|
||||
progress.textContent = 'Проверяем подтверждение регистрации в Solana...';
|
||||
status.style.display = timeoutShown ? '' : 'none';
|
||||
try {
|
||||
const result = await checkLoginExistsOnSolana({
|
||||
login: state.registrationDraft.login,
|
||||
solanaEndpoint: state.entrySettings.solanaServer,
|
||||
});
|
||||
if (result?.exists) {
|
||||
finalizeSuccess();
|
||||
return;
|
||||
}
|
||||
pollStatus.textContent = 'Пока ещё не прошла, подождите ещё немного...';
|
||||
if ((Date.now() - stageStartedAt) >= REGISTRATION_CONFIRM_TIMEOUT_MS) {
|
||||
showTimeoutState();
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Registration confirmation check failed', toUserMessage(error, 'registration-confirmation'));
|
||||
pollStatus.textContent = 'Проверяем регистрацию повторно...';
|
||||
if ((Date.now() - stageStartedAt) >= REGISTRATION_CONFIRM_TIMEOUT_MS) {
|
||||
showTimeoutState();
|
||||
}
|
||||
} finally {
|
||||
confirmationInFlight = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (headerBackButton) {
|
||||
const replacement = headerBackButton.cloneNode(true);
|
||||
replacement.addEventListener('click', () => {
|
||||
stageClosed = true;
|
||||
stopTimers();
|
||||
navigate('start-view');
|
||||
});
|
||||
headerBackButton.replaceWith(replacement);
|
||||
}
|
||||
|
||||
card.innerHTML = '';
|
||||
status.style.display = 'none';
|
||||
card.append(title, hint, txIdLine, progressWrap, progress, pollStatus, status);
|
||||
|
||||
updateVisualProgress();
|
||||
progressTimerId = window.setInterval(() => {
|
||||
if (stageClosed) return;
|
||||
updateVisualProgress();
|
||||
if ((Date.now() - stageStartedAt) >= REGISTRATION_CONFIRM_TIMEOUT_MS) {
|
||||
showTimeoutState();
|
||||
}
|
||||
}, 160);
|
||||
|
||||
pollStartTimerId = window.setTimeout(() => {
|
||||
if (stageClosed) return;
|
||||
void tryCheckRegistration();
|
||||
pollTimerId = window.setInterval(() => {
|
||||
void tryCheckRegistration();
|
||||
}, REGISTRATION_POLL_INTERVAL_MS);
|
||||
}, REGISTRATION_POLL_START_DELAY_MS);
|
||||
}
|
||||
|
||||
function renderSolanaDoneStage({ navigate, status, keyBundle, registrationTxId = '' }) {
|
||||
const screen = document.querySelector('section.stack');
|
||||
if (!screen) return;
|
||||
@@ -365,7 +561,7 @@ function renderSolanaDoneStage({ navigate, status, keyBundle, registrationTxId =
|
||||
|
||||
const hint = document.createElement('p');
|
||||
hint.className = 'auth-copy registration-finish-text';
|
||||
hint.textContent = 'Подождите 10 секунд, пока обновится транзакция вашей регистрации в блокчейне Solana. После этого вход в аккаунт произойдёт автоматически.';
|
||||
hint.textContent = 'Регистрация подтверждена в блокчейне Solana. Сейчас выполним автоматический вход в аккаунт.';
|
||||
|
||||
const txIdLine = document.createElement('p');
|
||||
txIdLine.className = 'meta-muted registration-finish-tx';
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
## Что не делать без отдельного решения
|
||||
|
||||
- Не включать `Understand Anything` в Gradle-сборку.
|
||||
- Не добавлять его в `deployServer` или `deployUI`.
|
||||
- Не добавлять его в основной server/UI deploy из `deploy/scripts/`.
|
||||
- Не переносить серверные модули в новую папку одновременно с этим экспериментом.
|
||||
- Не включать auto-update hook через `/understand --auto-update`, пока не понятно, нужен ли граф в каждом commit.
|
||||
|
||||
@@ -62,4 +62,3 @@
|
||||
```
|
||||
|
||||
До такого решения артефакты графа лучше считать локальными.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user