diff --git a/AGENTS.md b/AGENTS.md index dcb89fb..7825bf9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,3 +14,12 @@ - `client.version` — версия клиентского UI. - `server.version` — версия серверной части. - Базовое правило инкремента: `+1` по последнему числовому сегменту (patch), если не оговорено иное. + +## Deploy +- Все документы и заметки по деплою хранить в папке `Deploy Server/`. +- Для сервера `VPS-05` (`45.136.124.227`) доступ выполнять через пользователя `player`. +- По возможности все справки, комментарии и примечания в конфигах/документах писать на русском языке. +- Деплой UI выполнять только в один целевой контур за запуск: либо `prod` (основной), либо один выбранный тестовый (`ui_1`, `ui_2`, `ui_3`, `ui_drygmira`, `ui_milana`, `ui_aidar`). +- Если из запроса неясно, куда деплоить, обязательно сначала спросить пользователя, какой именно целевой контур нужен. +- По умолчанию сначала деплой и проверка на тестовом контуре; на основной (`prod`) деплоить только после явного подтверждения пользователя, что версия проверена и готова. +- При уточняющем вопросе отдельно предупреждать: деплой на основной выполнять только если точно подтверждена корректная работа. diff --git a/Deploy Server/servers-inventory.md b/Deploy Server/servers-inventory.md new file mode 100644 index 0000000..a7fa20c --- /dev/null +++ b/Deploy Server/servers-inventory.md @@ -0,0 +1,53 @@ +# SHiNE Deployment Servers Inventory + +## Scope +This folder contains all deployment-related notes and server records for SHiNE. + +## Legacy Production Server +- Name: `VPS-02` (legacy) +- Access: `root@194.87.0.247` +- Current role: old production server +- Confirmed services: + - `coturn` is installed and active (`systemd: active/running`) + - `caddy` is installed (reported by project context; verify version on host if needed) +- TURN configuration observed on host: + - `listening-port=3478` + - `external-ip=194.87.0.247` + - `relay-ip=194.87.0.247` + - auth mode: `use-auth-secret` + `static-auth-secret` +- SHiNE deployment note: + - This host is used as current/legacy runtime for SHiNE. + - Gradle-based deployment is used in this project (see repository deploy tasks and scripts). + +## Target Production Server (Migration) +- Name: `VPS-05` (new) +- Access: `root@45.136.124.227` +- Planned role: new primary production server for gradual migration +- Baseline setup done: + - `ripgrep` installed + - user `player` created + - user `player` added to `sudo` group + - deployment directory created: `/home/player/SHiNE` +- Rule: + - All SHiNE-related runtime files and deployments on VPS-05 should be placed under `/home/player/SHiNE`. + +## Additional TURN Node +- Name: `promo-node-93` +- Access: `ubuntu@93.170.12.154` (and `player` user for SHiNE operations) +- Role: additional TURN node for SHiNE calls +- TURN setup: + - `coturn` installed and active + - `listening-port=3478` + - `tls-listening-port=5349` + - `use-auth-secret` + shared `static-auth-secret` + - relay UDP port range: `49152-50152` +- Runtime files: + - `/etc/turnserver.conf` + - `/home/player/SHiNE/coturn/turnserver.conf` +- Cleanup done: + - Disabled old reverse SSH tunnel (`reverse-ssh.service`) that exposed `0.0.0.0:1200 -> localhost:22` to `194.87.0.247`. + +## Next Migration Steps (recommended) +1. Install and configure runtime dependencies (JDK, Caddy, DB, TURN if required). +2. Mirror SHiNE deployment process from VPS-02 using existing Gradle deployment flow. +3. Move traffic gradually and validate logs/metrics before final cutover. diff --git a/TODO_Будущие_доработки.md b/TODO_Будущие_доработки.md index 4d1c541..df15b60 100644 --- a/TODO_Будущие_доработки.md +++ b/TODO_Будущие_доработки.md @@ -31,3 +31,11 @@ - количество успешных вставок пар, - доля доставок в WS/push, - количество ретраев межсерверной пересылки. + +## 6) Ограничение текущих звонков (важно) +- Сейчас звонки работают только в рамках одного сигнального сервера (или единого контура, где обе стороны уже подключены). +- Сценарий «пользователь A на своих серверах, пользователь B на других серверах» пока не поддержан. +- TODO на будущее: +- временная межсерверная авторизация/сессия для старта звонка, +- отправка сигнальных сообщений между разными серверами пользователей, +- аккуратное завершение временной сессии после установления/завершения звонка. diff --git a/VERSION.properties b/VERSION.properties index 4687b44..f514621 100644 --- a/VERSION.properties +++ b/VERSION.properties @@ -1,2 +1,2 @@ -client.version=1.2.36 -server.version=1.2.30 +client.version=1.2.37 +server.version=1.2.31 diff --git a/docs/Ответ_на_вопрос_о_блокчейне_каналах_и_расширяемости.md b/docs/Ответ_на_вопрос_о_блокчейне_каналах_и_расширяемости.md new file mode 100644 index 0000000..ccaf767 --- /dev/null +++ b/docs/Ответ_на_вопрос_о_блокчейне_каналах_и_расширяемости.md @@ -0,0 +1,105 @@ +Дата: 27.04.2026 + +# Ответ по блокчейну, форматам, каналам и расширяемости + +Короткий итог: текущую систему можно запускать и развивать дальше. Архитектура уже в целом готова к расширению версиями, но расширять нужно по строгим правилам совместимости. + +## 1) Что важно зафиксировать про эволюцию форматов + +Да, ваш подход верный: +- новые возможности добавляем через новые `type/subType/version` и/или новый `frameCode`; +- старые блоки не переписываем и не «переподписываем»; +- делаем конвертер/адаптер чтения: старые блоки читаются и приводятся к новой внутренней модели. + +Это и есть правильная схема «старое автоматически читается и представляется в новом формате». + +## 2) Есть ли в коде узкие места, где нельзя расширять + +Критичных тупиков не видно, но есть важные ограничения: +- Парсер сейчас строгий: неизвестные `type/subType/version` и неизвестный frame отклоняются. +- Значит новые форматы нужно явно добавлять в парсер и серверную валидацию. +- Старые блоки без нужных полей не проблема, если новый код умеет читать их как legacy-ветку. + +Вывод: расширение возможно, но только через явную поддержку версий, а не «само появится». + +## 3) Про канал `0` + +Зафиксировано правило: +- канал `0` оставляем как технический root; +- контент-посты туда пока не публикуем. + +Это теперь отмечено в коде комментариями и проверками: +- на клиенте (UI) пост в `lineCode=0` блокируется с понятной ошибкой; +- на сервере добавлена валидация, которая тоже отклоняет `TEXT_POST` в канал `0`. + +## 4) Формат аватара (`ava`) — обновлено + +Раньше: +- `AR:` + +Теперь поддерживается составной формат: +- `SHA256:,AR:` + +Что сделано: +- парсер и сборщик формата в UI обновлены; +- при загрузке нового аватара считается `SHA-256` оптимизированного файла и сохраняется вместе с `AR`; +- при выборе существующего `txId` сначала качается файл, считается `SHA-256`, и только потом пишется `ava`; +- серверный граф связей теперь умеет извлекать `AR` из составной строки (включая fallback для legacy/кривых значений). + +Это улучшает целостность: у вас есть не только адрес файла, но и контрольный хэш. + +## 5) Как устроены ответы в каналах и насколько это надёжно + +Ответы (`REPLY` / `EDIT_REPLY`) сделаны отдельно от линейных постов: +- `REPLY` хранит ссылку на цель (`toBlockchainName + blockNumber + hash32`) и текст; +- `EDIT_REPLY` хранит ссылку на оригинальный reply (`blockNumber + hash32`) и новый текст. + +Сильные стороны: +- ссылка идёт по номеру + хэшу (устойчиво к подмене цели); +- редактирование отделено от оригинала и может агрегироваться в чтении. + +Для будущего это расширяемо: +- можно добавить `TEXT_REPLY_V2` с новым payload, сохранив `V1` для старых клиентов. + +## 6) Вложенные файлы и «мини-разметка» внутри сообщения + +Идея рабочая, но лучше делать аккуратно. + +### Вариант A: «теги в тексте» (ваша идея) + +Плюсы: +- быстро внедрить; +- стандартно для пользователей (похоже на HTML/Markdown). + +Риски: +- экранирование `<` `>` и безопасность рендера; +- сложнее валидировать и безопасно отображать (XSS/инъекции). + +Если идти этим путём, лучше: +- разрешить только whitelist-теги (`img/file/h1/h2/h3/b/i` и т.д.); +- хранить метаданные файла явно: `type,size,sha256,ar`; +- рендерить через безопасный парсер, а не через «сырой HTML». + +### Вариант B: структурированный payload (рекомендуется как основной) + +Сделать `TEXT_POST_V2`: +- массив сегментов: `text`, `image`, `file`, `heading`, `style`; +- для файла хранить `kind`, `mime`, `size`, `sha256`, `storage`, `address`. + +Плюс: +- надёжная валидация и безопасный рендер. + +Оптимальная стратегия: +1. В UI можно дать «мини-разметку» для ввода. +2. На запись преобразовывать её в структурированный payload V2. +3. Для старых клиентов отдавать fallback plain text. + +## 7) Практический roadmap без остановки разработки + +1. Оставить текущий blockchain running. +2. Формально описать versioning policy (что считается breaking/non-breaking). +3. Зафиксировать channel `0` как технический root-only. +4. Использовать `ava = SHA256 + AR` как новый стандарт. +5. Запланировать `TEXT_*_V2` для вложений и форматирования. + +Итог: стартовать и развивать можно уже сейчас. Основа хорошая, если строго держать версионирование и адаптеры чтения для legacy-блоков. diff --git a/predeploy/servers.md b/predeploy/servers.md new file mode 100644 index 0000000..3e5bbeb --- /dev/null +++ b/predeploy/servers.md @@ -0,0 +1,24 @@ +# SHiNE Predeploy Servers + +## Access policy +- VPS-05 (`45.136.124.227`): use `player@45.136.124.227` for regular deployment operations. +- TURN shared secret (both TURN nodes): `def6d444734d380d2f67a9d345b1debf985eaba0973c343e392c060d97c30106` + +## Servers +1. `VPS-02` (legacy): `root@194.87.0.247` + - Legacy production host. + - caddy installed. + - coturn installed. +2. `VPS-05` (target): `root@45.136.124.227` + - New migration target. + - User `player` created, in `sudo`. + - SHiNE base dir: `/home/player/SHiNE`. + - TURN config path: `/etc/turnserver.conf` (coturn), work docs/scripts in `/home/player/SHiNE/coturn`. +3. `promo-node-93` (TURN node): `ubuntu@93.170.12.154` + - TURN installed: `coturn` active. + - TURN ports: `3478/tcp`, `3478/udp`, `5349/tcp`, `5349/udp`. + - TURN relay ports: `49152-50152/udp`. + - TURN config path: `/etc/turnserver.conf`. + - SHiNE dir for player: `/home/player/SHiNE/coturn`. + - Access user `player` created with sudo and SSH key copied from `ubuntu`. + - Legacy reverse SSH tunnel to `194.87.0.247:1200` was disabled (`reverse-ssh.service`). diff --git a/shine-TURN-server/README.md b/shine-TURN-server/README.md new file mode 100644 index 0000000..6580f10 --- /dev/null +++ b/shine-TURN-server/README.md @@ -0,0 +1,17 @@ +# SHiNE TURN Server + +This directory stores TURN setup scripts and operational instructions. + +## Purpose +- Install and configure coturn for SHiNE calls. +- Keep repeatable setup scripts for new TURN nodes. +- Keep TURN-related config templates. + +## Current production model +- Multiple TURN servers are supported by backend config section: + - `call.ice.turn.servers.1.*` + - `call.ice.turn.servers.2.*` + - ... +- Each server can use: + - REST auth (`sharedSecret`) for temporary credentials, or + - static `username`/`password` (fallback). diff --git a/shine-UI/js/components/avatar-image.js b/shine-UI/js/components/avatar-image.js index 229cdee..831ca1d 100644 --- a/shine-UI/js/components/avatar-image.js +++ b/shine-UI/js/components/avatar-image.js @@ -1,5 +1,5 @@ import { state } from '../state.js'; -import { buildArweaveDataUrl, validateArweaveTxId } from '../services/arweave-file-service.js'; +import { buildArweaveDataUrl, validateArweaveTxId, validateSha256Hex } from '../services/arweave-file-service.js'; import { getCachedAvatarObjectUrl } from '../services/arweave-avatar-cache-service.js'; function normalizeLogin(value) { @@ -52,6 +52,8 @@ export function renderUserAvatar({ if (!validateArweaveTxId(txId)) { return wrap; } + const sha256Hex = String(avatar?.sha256Hex || avatar?.sha256 || '').trim().toLowerCase(); + const expectedSha256Hex = validateSha256Hex(sha256Hex) ? sha256Hex : ''; const img = document.createElement('img'); img.alt = 'Аватар'; @@ -65,7 +67,7 @@ export function renderUserAvatar({ setLoadedState(false); const gateway = state?.entrySettings?.arweaveServer; - void getCachedAvatarObjectUrl({ gateway, txId }) + void getCachedAvatarObjectUrl({ gateway, txId, expectedSha256Hex }) .then((objectUrl) => { const directUrl = buildArweaveDataUrl({ gateway, txId }); let triedDirectUrl = false; @@ -83,6 +85,12 @@ export function renderUserAvatar({ }; img.onerror = () => { if (!triedDirectUrl) { + if (expectedSha256Hex) { + releaseObjectUrl(); + img.removeAttribute('src'); + setLoadedState(false); + return; + } triedDirectUrl = true; releaseObjectUrl(); img.src = directUrl; @@ -95,6 +103,11 @@ export function renderUserAvatar({ img.src = objectUrl; }) .catch(() => { + if (expectedSha256Hex) { + img.removeAttribute('src'); + setLoadedState(false); + return; + } let directUrl = ''; try { directUrl = buildArweaveDataUrl({ gateway, txId }); diff --git a/shine-UI/js/components/avatar-wizard.js b/shine-UI/js/components/avatar-wizard.js index b0854b5..783c523 100644 --- a/shine-UI/js/components/avatar-wizard.js +++ b/shine-UI/js/components/avatar-wizard.js @@ -3,8 +3,10 @@ import { buildArweaveDataUrl, getArweaveUploadPrice, prepareAvatarImageFile, + sha256HexFromArrayBuffer, uploadArweaveFile, validateArweaveTxId, + validateSha256Hex, validateAvatarSourceFile, } from '../services/arweave-file-service.js'; import { saveProfileAvatarArweave } from '../services/user-profile-params.js'; @@ -69,6 +71,7 @@ export function openAvatarWizard({ let optimized = null; let priceInfo = null; let uploadedTxId = ''; + let uploadedSha256Hex = ''; function revokePreviewUrl() { if (!lastPreviewUrl) return; @@ -158,6 +161,7 @@ export function openAvatarWizard({ const showStepExistingPreview = async (txId) => { if (closed) return; const previewUrl = buildArweaveDataUrl({ gateway: cleanGateway, txId }); + let existingSha256Hex = ''; root.innerHTML = `