Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Чем меньше build-context, тем быстрее `docker build` и тем меньше
# финальный образ. Не пускаем внутрь сборки то, что не нужно runtime.

# Git
.git
.gitignore
.gitattributes
.github

# Тесты и инструменты разработки
tests
docs
deploy/grafana/*.json.bak
*.md
!README.md
LICENSE
Makefile
ROADMAP.md
CHANGELOG.md
CLAUDE.md
learnings.md

# Питоновские артефакты
__pycache__
*.pyc
*.pyo
*.pyd
.pytest_cache
.mypy_cache
.ruff_cache
*.egg-info
build
dist
htmlcov
.coverage

# Локальные runtime-данные
data
*.db
*.db-journal
*.db-wal
*.db-shm
*.log

# IDE / OS
.idea
.vscode
.DS_Store
Thumbs.db

# Compose / Docker meta — образ строится не из них
docker-compose.yml
.dockerignore
Dockerfile
.env
.env.*
62 changes: 62 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,68 @@

## [Unreleased]

## [0.7.0] — 2026-05-02

### Добавлено
- **Web-дашборд** (`webui.py`): stdlib `http.server`, без CDN и без
JS-фреймворков. CLI-флаг `--web-port PORT` (0 — выкл, default 0) и
`--web-host HOST` (default `127.0.0.1`). Эндпоинты:
- `GET /` — single-page HTML с inline-SVG sparkline хешрейта (последние
60 сэмплов), карточками шар/job/pool/uptime/sha-backend и SSE-логом
последних событий.
- `GET /api/stats` — JSON-снапшот (`Cache-Control: no-store`).
Контракт: `hashrate_ema/last/human`, `workers`, `pool_url`/`current_pool`,
`pool_difficulty`, `current_job_id`, `shares_total/accepted/rejected`,
`last_share_ts`, `uptime_s/human`, `sha_backend`, `started_at`, `now`.
- `GET /api/events` — Server-Sent Events: `share_found`,
`share_accepted`, `share_rejected`, `job`, `pool`. Keep-alive
каждые 15с. Чистая отписка при разрыве клиента.
- `GET /healthz` — то же тело, что у metrics-сервера (через тот же
`set_health_provider`).
- **`StatsProvider.subscribe(callback)`** — pub/sub шина для SSE.
`update_job` публикует `job`-event только при реальной смене job_id,
`record_share` — `share_found/accepted/rejected`, `update_pool` — `pool`.
Сломанный подписчик не валит publish: исключения ловятся и логируются.
- **`StatsProvider.set_sha_backend(name)`** — backend-имя видно в
`/api/stats` и в TUI рядом с хешрейтом.
- **Docker** (`Dockerfile`, `docker-compose.yml`, `.dockerignore`):
`python:3.11-slim`, healthcheck через stdlib `urllib` (без `curl`),
`ENTRYPOINT ["hope-hash"]`. Compose поднимает три сервиса
(`miner` + `prometheus` + `grafana`) с volume для SQLite и provisioning
Grafana. Env vars: `BTC_ADDRESS`, `WORKERS`, `HOPE_HASH_TELEGRAM_*`,
`GRAFANA_USER/PASSWORD`.
- **Provisioning Prometheus + Grafana** (`deploy/prometheus/prometheus.yml`,
`deploy/grafana/datasource.yml`, `deploy/grafana/dashboard.yml`):
Prometheus скрейпит `miner:8000/metrics` каждые 15с, Grafana
автоматически подхватывает `hope-hash.json` под папку «Hope-Hash».
- **Двуязычная документация** (`docs/`):
`getting-started.{en,ru}.md` (первый запуск, типичные проблемы),
`deploy.{en,ru}.md` (Docker compose, Telegram, healthcheck, reverse
proxy), `architecture.{en,ru}.md` (протокол, threading, hot path,
observers, BIP-ссылки).
- **README rewrite**: верх — английский, низ — русский, обе половины
с одинаковыми разделами (что / install / run / advanced flags / demo /
benchmark / architecture / realistic expectations / contributing).
Cross-link на `docs/architecture.{en,ru}.md`.
- **Тесты**: `test_webui.py` (17 тестов): publish/subscribe, render_html,
HTML/JSON-эндпоинты, SSE-стрим с реальным сокетом, `/healthz`-fallback,
идемпотентность start/stop. Итого **225 → 242** (+17).

### Изменено
- `StatsProvider.__init__` теперь принимает `sha_backend` (default
`"hashlib"`).
- `StatsProvider.update_job/record_share/update_pool` публикуют события
через `_publish` — без обратных совместимых ломок (старые потребители
просто не подписываются).
- `cli.main()` стартует `WebUIServer`, если `--web-port > 0`, и регистрирует
тот же health-provider, что и у `MetricsServer`.
- `__version__` → `0.7.0`.

### Документация
- `ROADMAP.md`: тикнуты web-морда (через stdlib `http.server`, не
FastAPI) и Docker-образ. Добавлен раздел «remaining» с явно
отложенными задачами (Stratum V2, Rust/PyO3, GPU, FastAPI).

## [0.6.0] — 2026-05-02

### Добавлено
Expand Down
48 changes: 48 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Hope-Hash — учебный solo BTC miner на чистом stdlib.
#
# Слим-Python база: ctypes-backend подхватывает libcrypto-3 из самого образа,
# поэтому никаких apt-get install для openssl-libs не нужно.
#
# Pip install -e . — это сам проект, не сторонняя зависимость (CLAUDE.md
# разрешает; pyproject.toml объявляет dependencies = []).

FROM python:3.11-slim

# Не записываем .pyc; разрешаем print/log без буферизации (важно для
# `docker logs -f`, иначе строки висят пока буфер не наполнится).
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1

WORKDIR /app

# Сначала только метаданные — слой с зависимостями кэшируется отдельно
# от исходников (хотя dependencies=[], pip всё равно строит wheel один раз).
COPY pyproject.toml README.md LICENSE ./
COPY src ./src

RUN pip install --upgrade pip \
&& pip install -e .

# Volume для SQLite-журнала шар. Default путь --db hope_hash.db ставим под
# /data, чтобы был один canonical монтируемый location.
VOLUME ["/data"]
WORKDIR /data

# Прокидываем порты: 8000 — общий для metrics+webui (compose их и пробрасывает),
# 9090 — если кто-то держит --metrics-port отдельно от --web-port.
EXPOSE 8000 9090

# Healthcheck без curl: stdlib urllib умеет всё необходимое и уже в образе.
# Проверяем /healthz на metrics-порту 8000 (compose именно туда мапит).
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD python -c "import urllib.request,sys; \
sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/healthz', timeout=4).status==200 else 1)" \
|| exit 1

ENTRYPOINT ["hope-hash"]

# Пустой default — пользователь обязательно передаёт BTC-адрес и флаги.
# Compose-файл подставляет их явно через `command:`.
CMD ["--help"]
Loading
Loading