diff --git a/.gitea/workflows/build-ext.yml b/.gitea/workflows/build-ext.yml new file mode 100644 index 0000000..06613ca --- /dev/null +++ b/.gitea/workflows/build-ext.yml @@ -0,0 +1,73 @@ +name: build-and-push-ext + +on: + push: + branches: + - dev4 + +jobs: + build_push: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Prepare SSH agent (optional) + shell: bash + run: | + set -euo pipefail + eval "$(ssh-agent -s)" + if [ -n "${{ secrets.SSH_PRIVATE_KEY || '' }}" ]; then + echo "${{ secrets.SSH_PRIVATE_KEY }}" | tr -d '\r' | ssh-add - >/dev/null 2>&1 || true + fi + mkdir -p ~/.ssh + ssh-keyscan git.4nkweb.com >> ~/.ssh/known_hosts 2>/dev/null || true + echo "SSH agent ready: $SSH_AUTH_SOCK" + # Rendre l'agent dispo aux steps suivants + echo "SSH_AUTH_SOCK=$SSH_AUTH_SOCK" >> "$GITHUB_ENV" + echo "SSH_AGENT_PID=$SSH_AGENT_PID" >> "$GITHUB_ENV" + + - name: Compute Docker tag from commit message or fallback + id: tag + shell: bash + run: | + set -euo pipefail + msg=$(git log -1 --pretty=%B) + if [[ "$msg" =~ ci:\ docker_tag=([a-zA-Z0-9._:-]+) ]]; then + tag="${BASH_REMATCH[1]}" + else + tag="dev-test" + fi + echo "TAG=$tag" | tee -a $GITHUB_OUTPUT + + - name: Docker login (git.4nkweb.com) + shell: bash + env: + REG_USER: ${{ secrets.USER }} + REG_TOKEN: ${{ secrets.TOKEN }} + run: | + set -euo pipefail + echo "$REG_TOKEN" | docker login git.4nkweb.com -u "$REG_USER" --password-stdin + + - name: Build image (target ext) + shell: bash + env: + DOCKER_BUILDKIT: "1" + run: | + set -euo pipefail + if [ -n "${SSH_AUTH_SOCK:-}" ]; then + docker build --ssh default \ + -t git.4nkweb.com/4nk/sdk_relay:${{ steps.tag.outputs.TAG }} \ + -f Dockerfile . + else + echo "SSH_AUTH_SOCK non défini: l'agent SSH n'est pas disponible. Assurez-vous de définir secrets.SSH_PRIVATE_KEY." + exit 1 + fi + + - name: Push image + shell: bash + run: | + set -euo pipefail + docker push git.4nkweb.com/4nk/sdk_relay:${{ steps.tag.outputs.TAG }} diff --git a/docs/ANALYSE.md b/docs/ANALYSE.md new file mode 100644 index 0000000..f2cd4ab --- /dev/null +++ b/docs/ANALYSE.md @@ -0,0 +1,37 @@ +## Analyse détaillée + +### Périmètre + +Service Rust `sdk_relay` interfaçant Bitcoin (RPC), Blindbit et WebSocket, avec configuration injectée. + +### Stack + +- **Langage**: Rust 2021 +- **Dépendances**: `tokio`, `tokio-tungstenite`, `zeromq`, `bitcoincore-rpc`, `serde[_json]`, `env_logger`, `futures-util`, `sdk_common` (git, branche `dev`, features `parallel`, `blindbit-backend`). + +### Build et image + +- Docker multi‑étapes: build dans `rust:latest` avec SSH pour deps privées, exécution `debian:bookworm-slim`. +- Binaire: `/usr/local/bin/sdk_relay`. +- Conf: build‑arg `CONF` écrit dans `/home/bitcoin/.conf`. +- Volumes: `/home/bitcoin/.4nk`, `/home/bitcoin/.bitcoin`. + +### Réseau et healthcheck + +- Ports: 8090, 8091 (exposés). Health: `GET /health` (via compose parent). + +### Logs + +- `RUST_LOG` géré par env; dans `lecoffre_node`, sortie tee vers `/home/bitcoin/.4nk/logs/sdk_relay.log`. + +### Risques et points d’attention + +- Dépendance `sdk_common` via git/branche `dev`: geler par tag/commit pour reproductibilité. +- Image d’exécution embarque `strace`; vérifier nécessité en prod. +- Permissions volume Windows: note de chown partiel dans compose parent. + +### Actions proposées + +- Pinner `sdk_common` sur un commit ou tag; documenter politique de mise à jour. +- Séparer images `-dev` et `-prod` si `strace` non requis. +- Documenter format du fichier de conf (`sdk_relay.conf`) et valeurs par défaut. diff --git a/src/main.rs b/src/main.rs index d7aae8a..033f14b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,6 +49,7 @@ use tokio::net::{TcpListener, TcpStream}; use tokio::sync::mpsc::{unbounded_channel, UnboundedSender}; use tokio_stream::wrappers::UnboundedReceiverStream; use tokio_tungstenite::tungstenite::Message; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; use anyhow::{Error, Result}; use zeromq::{Socket, SocketRecv}; @@ -412,6 +413,28 @@ async fn handle_zmq(zmq_url: String, blindbit_url: String) { } } +async fn handle_health_endpoint(mut stream: TcpStream) { + let response = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: 15\r\n\r\n{\"status\":\"ok\"}"; + let _ = stream.write_all(response.as_bytes()).await; + let _ = stream.shutdown().await; +} + +async fn start_health_server(port: u16) { + let listener = match TcpListener::bind(format!("0.0.0.0:{}", port)).await { + Ok(listener) => listener, + Err(e) => { + log::error!("Failed to bind health server on port {}: {}", port, e); + return; + } + }; + + log::info!("Health server listening on port {}", port); + + while let Ok((stream, _)) = listener.accept().await { + tokio::spawn(handle_health_endpoint(stream)); + } +} + #[tokio::main(flavor = "multi_thread")] async fn main() -> Result<()> { env_logger::init(); @@ -590,6 +613,9 @@ async fn main() -> Result<()> { tokio::spawn(MessageCache::clean_up()); + // Start health server on port 8091 + tokio::spawn(start_health_server(8091)); + // Let's spawn the handling of each connection in a separate task. while let Ok((stream, addr)) = listener.accept().await { tokio::spawn(handle_connection(stream, addr, our_sp_address));