# SHiNE-promo-solana-devnet

Временное промо-приложение для тестеров Web3-социальной сети SHiNE / «Сияние» в Solana Devnet.

Основной сценарий:
- приложение SHiNE открывает страницу вида `/?wallet=SOLANA_PUBLIC_KEY`;
- пользователь вводит имя и промокод;
- backend проверяет промокод и отправляет **реальную** devnet-транзакцию на `0.1 SOL`;
- использованный промокод фиксируется в файле и больше не может быть применён.

## Стек

- Java 21
- Spring Boot
- Gradle
- Thymeleaf + HTML/CSS/JS (server-side UI)

## Локальный запуск

1. Скопируйте пример настроек:

```bash
cp config/application.example.properties src/main/resources/application.properties
```

2. Положите реальный keypair-файл Solana CLI формата в:

```text
config/devnet-wallet.json
```

3. Запустите:

```bash
./gradlew bootRun
```

Приложение будет доступно на `http://localhost:8021`.

## Как указать порт

По умолчанию используется:

```properties
server.port=8021
```

Изменить можно:
- в `src/main/resources/application.properties`;
- или через параметр запуска:

```bash
./gradlew bootRun --args='--server.port=8090'
```

## Настройка Solana RPC Devnet

Параметр:

```properties
solana.rpc.url=https://api.devnet.solana.com
```

Для другого RPC достаточно заменить URL в properties.

## Настройка devnet keypair

- Файл должен быть в формате Solana keypair JSON: массив из 64 чисел (`0..255`).
- Пример лежит в `config/devnet-wallet.example.json`.
- Рабочий файл: `config/devnet-wallet.json`.

Важно: настоящий keypair **нельзя коммитить в GitHub**.  
Он добавлен в `.gitignore`.

## Где лежат промокоды

Файл:

```text
data/promo-codes.txt
```

## Где лежит файл использованных промокодов

Файл:

```text
data/promo-used.txt
```

## Формат `promo-codes.txt`

- одна строка = один промокод;
- пустые строки игнорируются;
- строки с `#` игнорируются как комментарии;
- промокод должен соответствовать regex: `[a-z0-9]{8}`.

## Формат `promo-used.txt`

Каждая запись в формате:

```text
promoCode | wallet | name | yyyy.MM.dd HH:mm | signature
```

Пример:

```text
aidar2km | 8xF...abc | Иван Петров | 2026.04.27 18:45 | 5xTxSignature...
```

## Пример URL

```text
http://localhost:8021/?wallet=8zYQ...DevnetAddress
```

## Пример API-запроса

```bash
curl -X POST http://localhost:8021/api/promo/top-up \
  -H "Content-Type: application/json" \
  -d '{
    "wallet":"SOLANA_PUBLIC_KEY",
    "name":"Иван Петров",
    "promoCode":"aidar2km"
  }'
```

## Проверка транзакции в Solana Explorer Devnet

Explorer URL формируется по шаблону:

```text
https://explorer.solana.com/tx/{signature}?cluster=devnet
```

## Сборка jar через Gradle

```bash
./gradlew clean build
```

JAR:

```text
build/libs/SHiNE-promo-solana-devnet.jar
```

## Запуск jar

```bash
java -jar build/libs/SHiNE-promo-solana-devnet.jar
```

## Health endpoint

```text
GET /health
```

Ответ:

```json
{
  "status": "ok",
  "app": "SHiNE-promo-solana-devnet"
}
```

## Логи

- логируется старт приложения;
- логируются успешные пополнения;
- логируются ошибки транзакций;
- приватный ключ не логируется.

## Gradle-задачи для серверного деплоя

В `build.gradle` добавлены задачи:

- `buildServerBundle` — готовит bundle в `build/server-bundle`;
- `deployToServer` — копирует JAR и `application.properties` на сервер и перезапускает systemd-сервис.

Запуск:

```bash
./gradlew deployToServer
```

Переопределение хоста/пути:

```bash
./gradlew deployToServer \
  -PdeployHost=user@10.147.20.7 \
  -PdeployPath=/home/user/docker/SHiNE-promo-solana-devnet \
  -PdeployService=SHiNE-promo-solana-devnet
```
