From 9eb2a0675777807f093dca694b00be7a4dcde2c8 Mon Sep 17 00:00:00 2001 From: Dmitrii Krosh Date: Wed, 24 Jun 2026 03:05:26 +0300 Subject: [PATCH] add env example, fix bug with Callable object --- .env.example | 47 ++++++++++++++++++++++++++++++++ src/nexus_sync/client/runtime.py | 21 ++++++++++++-- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index e69de29..4b8fdf7 100644 --- a/.env.example +++ b/.env.example @@ -0,0 +1,47 @@ +# nexus-sync environment example +# Copy this file to .env and replace example secrets before running. + +# ----------------------------------------------------------------------------- +# Server configuration +# ----------------------------------------------------------------------------- + +# Comma-separated client_id:token pairs accepted by the server. +# Example with one client: +# NEXUS_SYNC_CLIENT_TOKENS="laptop:change-me-long-random-token" +# Example with multiple clients: +# NEXUS_SYNC_CLIENT_TOKENS="laptop:change-me-token-1,desktop:change-me-token-2" +# +# Current development default in code is dev-client:dev-token if unset, but it is +# better to set this explicitly. +NEXUS_SYNC_CLIENT_TOKENS="dev-client:dev-token" + +# Logging level for both server and client: DEBUG, INFO, WARNING, ERROR, CRITICAL. +NEXUS_SYNC_LOG_LEVEL="INFO" + +# ----------------------------------------------------------------------------- +# Client configuration +# ----------------------------------------------------------------------------- + +# Base URL of the nexus-sync server. +NEXUS_SYNC_SERVER_URL="http://127.0.0.1:8000" + +# Stable ID of this machine/client. Must match one client_id from +# NEXUS_SYNC_CLIENT_TOKENS on the server. +NEXUS_SYNC_CLIENT_ID="dev-client" + +# Bearer token for this client. Must match the token paired with +# NEXUS_SYNC_CLIENT_ID on the server. +NEXUS_SYNC_CLIENT_TOKEN="dev-token" + +# Commands this client is allowed to execute when the server requests them. +# Supported presets currently implemented: +# - hostname +# - network_interfaces +# +# Safer explicit allowlist: +NEXUS_SYNC_ALLOWED_COMMANDS="hostname,network_interfaces" + +# Alternative: allow every locally registered preset. This still does NOT allow +# arbitrary shell strings from the server. Uncomment instead of the line above if +# desired. +# NEXUS_SYNC_ALLOWED_COMMANDS="full_access" diff --git a/src/nexus_sync/client/runtime.py b/src/nexus_sync/client/runtime.py index f75da6c..b240e08 100644 --- a/src/nexus_sync/client/runtime.py +++ b/src/nexus_sync/client/runtime.py @@ -7,7 +7,8 @@ import urllib.error import urllib.request from dataclasses import dataclass from datetime import UTC, datetime -from typing import Callable, Mapping +from types import TracebackType +from typing import Callable, Mapping, Protocol, Self from pydantic import ValidationError @@ -38,6 +39,22 @@ class HeartbeatError(RuntimeError): pass +class HeartbeatHTTPResponse(Protocol): + def __enter__(self) -> Self: ... + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc: BaseException | None, + traceback: TracebackType | None, + ) -> None: ... + + def read(self) -> bytes: ... + + +HeartbeatOpener = Callable[[urllib.request.Request], HeartbeatHTTPResponse] + + @dataclass(frozen=True) class ClientConfig: server_url: str @@ -84,7 +101,7 @@ def send_heartbeat( config: ClientConfig, heartbeat: HeartbeatRequest, *, - opener: Callable[[urllib.request.Request], object] = urllib.request.urlopen, + opener: HeartbeatOpener = urllib.request.urlopen, ) -> HeartbeatResponse: payload = heartbeat.model_dump_json().encode() request = urllib.request.Request(