Harden RCON target loading and capture diagnostics
This commit is contained in:
@@ -3,10 +3,10 @@ HLL_BACKEND_PORT=8000
|
|||||||
HLL_BACKEND_STORAGE_PATH=/app/data/hll_vietnam_dev.sqlite3
|
HLL_BACKEND_STORAGE_PATH=/app/data/hll_vietnam_dev.sqlite3
|
||||||
HLL_BACKEND_ALLOWED_ORIGINS=http://127.0.0.1:8080,http://localhost:8080
|
HLL_BACKEND_ALLOWED_ORIGINS=http://127.0.0.1:8080,http://localhost:8080
|
||||||
HLL_BACKEND_REFRESH_INTERVAL_SECONDS=120
|
HLL_BACKEND_REFRESH_INTERVAL_SECONDS=120
|
||||||
HLL_BACKEND_LIVE_DATA_SOURCE=a2s
|
HLL_BACKEND_LIVE_DATA_SOURCE=rcon
|
||||||
HLL_BACKEND_HISTORICAL_DATA_SOURCE=public-scoreboard
|
HLL_BACKEND_HISTORICAL_DATA_SOURCE=rcon
|
||||||
HLL_BACKEND_RCON_TIMEOUT_SECONDS=10
|
HLL_BACKEND_RCON_TIMEOUT_SECONDS=20
|
||||||
HLL_BACKEND_RCON_TARGETS=
|
HLL_BACKEND_RCON_TARGETS=[{"name":"Comunidad Hispana #01","slug":"comunidad-hispana-01","external_server_id":"comunidad-hispana-01","host":"152.114.195.174","port":7779,"password":"replace-me-01","source_name":"community-hispana-rcon","region":"ES","game_port":null,"query_port":null},{"name":"Comunidad Hispana #02","slug":"comunidad-hispana-02","external_server_id":"comunidad-hispana-02","host":"152.114.195.150","port":7879,"password":"replace-me-02","source_name":"community-hispana-rcon","region":"ES","game_port":null,"query_port":null},{"name":"Comunidad Hispana #03","slug":"comunidad-hispana-03","external_server_id":"comunidad-hispana-03","host":"5.196.78.45","port":27230,"password":"replace-me-03","source_name":"community-hispana-rcon","region":"ES","game_port":null,"query_port":null}]
|
||||||
HLL_HISTORICAL_CRCON_PAGE_SIZE=50
|
HLL_HISTORICAL_CRCON_PAGE_SIZE=50
|
||||||
HLL_HISTORICAL_CRCON_TIMEOUT_SECONDS=15
|
HLL_HISTORICAL_CRCON_TIMEOUT_SECONDS=15
|
||||||
HLL_HISTORICAL_CRCON_DETAIL_WORKERS=8
|
HLL_HISTORICAL_CRCON_DETAIL_WORKERS=8
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ DEFAULT_STORAGE_FILENAME = "hll_vietnam_dev.sqlite3"
|
|||||||
DEFAULT_REFRESH_INTERVAL_SECONDS = 300
|
DEFAULT_REFRESH_INTERVAL_SECONDS = 300
|
||||||
DEFAULT_LIVE_DATA_SOURCE = "rcon"
|
DEFAULT_LIVE_DATA_SOURCE = "rcon"
|
||||||
DEFAULT_HISTORICAL_DATA_SOURCE = "rcon"
|
DEFAULT_HISTORICAL_DATA_SOURCE = "rcon"
|
||||||
DEFAULT_RCON_TIMEOUT_SECONDS = 10.0
|
DEFAULT_RCON_TIMEOUT_SECONDS = 20.0
|
||||||
DEFAULT_HISTORICAL_CRCON_PAGE_SIZE = 50
|
DEFAULT_HISTORICAL_CRCON_PAGE_SIZE = 50
|
||||||
DEFAULT_HISTORICAL_CRCON_TIMEOUT_SECONDS = 15.0
|
DEFAULT_HISTORICAL_CRCON_TIMEOUT_SECONDS = 15.0
|
||||||
DEFAULT_HISTORICAL_CRCON_DETAIL_WORKERS = 8
|
DEFAULT_HISTORICAL_CRCON_DETAIL_WORKERS = 8
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import itertools
|
|||||||
import json
|
import json
|
||||||
import socket
|
import socket
|
||||||
import struct
|
import struct
|
||||||
|
from collections.abc import Mapping
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
from .config import (
|
from .config import (
|
||||||
@@ -36,6 +37,14 @@ class RconServerTarget:
|
|||||||
query_port: int | None = None
|
query_port: int | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class RconQueryError(RuntimeError):
|
||||||
|
"""Normalized RCON query failure with a machine-readable error type."""
|
||||||
|
|
||||||
|
def __init__(self, error_type: str, message: str) -> None:
|
||||||
|
super().__init__(message)
|
||||||
|
self.error_type = error_type
|
||||||
|
|
||||||
|
|
||||||
class HllRconConnection:
|
class HllRconConnection:
|
||||||
"""Synchronous HLL RCON v2 connection for lightweight live status queries."""
|
"""Synchronous HLL RCON v2 connection for lightweight live status queries."""
|
||||||
|
|
||||||
@@ -203,12 +212,35 @@ def query_live_server_sample(
|
|||||||
) -> dict[str, object]:
|
) -> dict[str, object]:
|
||||||
"""Query one HLL server and return both normalized and raw session data."""
|
"""Query one HLL server and return both normalized and raw session data."""
|
||||||
resolved_timeout = timeout_seconds or get_rcon_request_timeout_seconds()
|
resolved_timeout = timeout_seconds or get_rcon_request_timeout_seconds()
|
||||||
|
try:
|
||||||
with HllRconConnection(timeout_seconds=resolved_timeout) as connection:
|
with HllRconConnection(timeout_seconds=resolved_timeout) as connection:
|
||||||
connection.connect(host=target.host, port=target.port, password=target.password)
|
connection.connect(host=target.host, port=target.port, password=target.password)
|
||||||
session = connection.execute_json(
|
session = connection.execute_json(
|
||||||
"GetServerInformation",
|
"GetServerInformation",
|
||||||
{"Name": "session", "Value": ""},
|
{"Name": "session", "Value": ""},
|
||||||
)
|
)
|
||||||
|
except RconQueryError:
|
||||||
|
raise
|
||||||
|
except (TimeoutError, socket.timeout) as error:
|
||||||
|
raise RconQueryError(
|
||||||
|
"timeout",
|
||||||
|
f"Timed out after {resolved_timeout:.1f}s while querying {target.host}:{target.port}.",
|
||||||
|
) from error
|
||||||
|
except ConnectionRefusedError as error:
|
||||||
|
raise RconQueryError(
|
||||||
|
"connection-refused",
|
||||||
|
f"Connection refused by {target.host}:{target.port}.",
|
||||||
|
) from error
|
||||||
|
except OSError as error:
|
||||||
|
raise RconQueryError(
|
||||||
|
_classify_socket_error_type(error),
|
||||||
|
f"RCON socket error against {target.host}:{target.port}: {error}",
|
||||||
|
) from error
|
||||||
|
except RuntimeError as error:
|
||||||
|
raise RconQueryError(
|
||||||
|
_classify_runtime_error_type(error),
|
||||||
|
str(error),
|
||||||
|
) from error
|
||||||
|
|
||||||
resolved_external_id = target.external_server_id or f"rcon:{target.host}:{target.port}"
|
resolved_external_id = target.external_server_id or f"rcon:{target.host}:{target.port}"
|
||||||
return {
|
return {
|
||||||
@@ -250,17 +282,19 @@ def build_rcon_target_key(target: RconServerTarget) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _coerce_rcon_target(raw_target: dict[str, object]) -> RconServerTarget:
|
def _coerce_rcon_target(raw_target: dict[str, object]) -> RconServerTarget:
|
||||||
name = str(raw_target.get("name") or "Unnamed RCON target").strip()
|
slug = _string_or_none(raw_target.get("slug"))
|
||||||
host = str(raw_target.get("host") or "").strip()
|
external_server_id = _string_or_none(raw_target.get("external_server_id")) or slug
|
||||||
password = str(raw_target.get("password") or "").strip()
|
name = _string_or_none(raw_target.get("name")) or _slug_to_display_name(slug) or "Unnamed RCON target"
|
||||||
source_name = str(raw_target.get("source_name") or DEFAULT_RCON_SOURCE_NAME).strip()
|
host = _required_string(raw_target, "host")
|
||||||
port = int(raw_target.get("port") or 0)
|
password = _required_string(raw_target, "password")
|
||||||
|
source_name = _string_or_none(raw_target.get("source_name")) or DEFAULT_RCON_SOURCE_NAME
|
||||||
|
port = _required_positive_int(raw_target, "port")
|
||||||
if not host:
|
if not host:
|
||||||
raise ValueError("Each RCON target must define a non-empty host.")
|
raise ValueError("Each RCON target must define a non-empty 'host'.")
|
||||||
if port <= 0:
|
if port <= 0:
|
||||||
raise ValueError("Each RCON target must define a valid port.")
|
raise ValueError("Each RCON target must define a positive 'port'.")
|
||||||
if not password:
|
if not password:
|
||||||
raise ValueError("Each RCON target must define a non-empty password.")
|
raise ValueError("Each RCON target must define a non-empty 'password'.")
|
||||||
|
|
||||||
return RconServerTarget(
|
return RconServerTarget(
|
||||||
name=name,
|
name=name,
|
||||||
@@ -268,7 +302,7 @@ def _coerce_rcon_target(raw_target: dict[str, object]) -> RconServerTarget:
|
|||||||
port=port,
|
port=port,
|
||||||
password=password,
|
password=password,
|
||||||
source_name=source_name or DEFAULT_RCON_SOURCE_NAME,
|
source_name=source_name or DEFAULT_RCON_SOURCE_NAME,
|
||||||
external_server_id=_string_or_none(raw_target.get("external_server_id")),
|
external_server_id=external_server_id,
|
||||||
region=_string_or_none(raw_target.get("region")),
|
region=_string_or_none(raw_target.get("region")),
|
||||||
game_port=_coerce_optional_positive_int(raw_target.get("game_port")),
|
game_port=_coerce_optional_positive_int(raw_target.get("game_port")),
|
||||||
query_port=_coerce_optional_positive_int(raw_target.get("query_port")),
|
query_port=_coerce_optional_positive_int(raw_target.get("query_port")),
|
||||||
@@ -280,6 +314,11 @@ def _raise_for_status(response: dict[str, object], *, command_name: str) -> None
|
|||||||
if status_code == 200:
|
if status_code == 200:
|
||||||
return
|
return
|
||||||
status_message = _string_or_none(response.get("statusMessage")) or "Unknown RCON error."
|
status_message = _string_or_none(response.get("statusMessage")) or "Unknown RCON error."
|
||||||
|
if command_name == "Login" and status_code in {401, 403}:
|
||||||
|
raise RconQueryError(
|
||||||
|
"auth/login",
|
||||||
|
f"{command_name} failed with RCON status {status_code}: {status_message}",
|
||||||
|
)
|
||||||
raise RuntimeError(f"{command_name} failed with RCON status {status_code}: {status_message}")
|
raise RuntimeError(f"{command_name} failed with RCON status {status_code}: {status_message}")
|
||||||
|
|
||||||
|
|
||||||
@@ -313,3 +352,64 @@ def _coerce_optional_positive_int(value: object) -> int | None:
|
|||||||
if coerced <= 0:
|
if coerced <= 0:
|
||||||
raise ValueError("Configured RCON target ports must be positive when defined.")
|
raise ValueError("Configured RCON target ports must be positive when defined.")
|
||||||
return coerced
|
return coerced
|
||||||
|
|
||||||
|
|
||||||
|
def _required_string(raw_target: Mapping[str, object], field_name: str) -> str:
|
||||||
|
value = _string_or_none(raw_target.get(field_name))
|
||||||
|
if value is None:
|
||||||
|
available_fields = ", ".join(sorted(raw_target.keys()))
|
||||||
|
raise ValueError(
|
||||||
|
f"Each RCON target must define a non-empty '{field_name}'. "
|
||||||
|
f"Available fields: {available_fields or 'none'}."
|
||||||
|
)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _required_positive_int(raw_target: Mapping[str, object], field_name: str) -> int:
|
||||||
|
raw_value = raw_target.get(field_name)
|
||||||
|
try:
|
||||||
|
value = int(raw_value)
|
||||||
|
except (TypeError, ValueError) as error:
|
||||||
|
available_fields = ", ".join(sorted(raw_target.keys()))
|
||||||
|
raise ValueError(
|
||||||
|
f"Each RCON target must define a valid integer '{field_name}'. "
|
||||||
|
f"Available fields: {available_fields or 'none'}."
|
||||||
|
) from error
|
||||||
|
if value <= 0:
|
||||||
|
raise ValueError(f"Each RCON target must define a positive '{field_name}'.")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def _slug_to_display_name(slug: str | None) -> str | None:
|
||||||
|
normalized_slug = _string_or_none(slug)
|
||||||
|
if normalized_slug is None:
|
||||||
|
return None
|
||||||
|
if normalized_slug.startswith("comunidad-hispana-"):
|
||||||
|
suffix = normalized_slug.removeprefix("comunidad-hispana-")
|
||||||
|
if suffix.isdigit():
|
||||||
|
return f"Comunidad Hispana #{suffix.zfill(2)}"
|
||||||
|
parts = [part for part in normalized_slug.replace("_", "-").split("-") if part]
|
||||||
|
if not parts:
|
||||||
|
return None
|
||||||
|
return " ".join(part.upper() if part.isdigit() else part.capitalize() for part in parts)
|
||||||
|
|
||||||
|
|
||||||
|
def _classify_socket_error_type(error: OSError) -> str:
|
||||||
|
if isinstance(error, TimeoutError):
|
||||||
|
return "timeout"
|
||||||
|
if isinstance(error, ConnectionRefusedError):
|
||||||
|
return "connection-refused"
|
||||||
|
if getattr(error, "errno", None) in {10060, 110, 60}:
|
||||||
|
return "timeout"
|
||||||
|
return "other-error"
|
||||||
|
|
||||||
|
|
||||||
|
def _classify_runtime_error_type(error: RuntimeError) -> str:
|
||||||
|
message = str(error).lower()
|
||||||
|
if "auth token" in message or "login failed" in message or "status 401" in message or "status 403" in message:
|
||||||
|
return "auth/login"
|
||||||
|
if "invalid json" in message or "unexpected payload" in message or "malformed" in message or "invalid rcon" in message:
|
||||||
|
return "payload-invalid"
|
||||||
|
if "timed out" in message:
|
||||||
|
return "timeout"
|
||||||
|
return "other-error"
|
||||||
|
|||||||
@@ -12,8 +12,14 @@ from .config import (
|
|||||||
get_rcon_historical_capture_interval_seconds,
|
get_rcon_historical_capture_interval_seconds,
|
||||||
get_rcon_historical_capture_max_retries,
|
get_rcon_historical_capture_max_retries,
|
||||||
get_rcon_historical_capture_retry_delay_seconds,
|
get_rcon_historical_capture_retry_delay_seconds,
|
||||||
|
get_rcon_request_timeout_seconds,
|
||||||
|
)
|
||||||
|
from .rcon_client import (
|
||||||
|
RconQueryError,
|
||||||
|
build_rcon_target_key,
|
||||||
|
load_rcon_targets,
|
||||||
|
query_live_server_sample,
|
||||||
)
|
)
|
||||||
from .rcon_client import build_rcon_target_key, load_rcon_targets, query_live_server_sample
|
|
||||||
from .rcon_historical_storage import (
|
from .rcon_historical_storage import (
|
||||||
finalize_rcon_historical_capture_run,
|
finalize_rcon_historical_capture_run,
|
||||||
initialize_rcon_historical_storage,
|
initialize_rcon_historical_storage,
|
||||||
@@ -60,13 +66,17 @@ def run_rcon_historical_capture_unlocked(
|
|||||||
stats = RconHistoricalCaptureStats()
|
stats = RconHistoricalCaptureStats()
|
||||||
items: list[dict[str, object]] = []
|
items: list[dict[str, object]] = []
|
||||||
errors: list[dict[str, object]] = []
|
errors: list[dict[str, object]] = []
|
||||||
|
timeout_seconds = get_rcon_request_timeout_seconds()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for target in selected_targets:
|
for target in selected_targets:
|
||||||
target_metadata = _serialize_target(target)
|
target_metadata = _serialize_target(target)
|
||||||
stats.targets_seen += 1
|
stats.targets_seen += 1
|
||||||
try:
|
try:
|
||||||
sample = query_live_server_sample(target)
|
sample = query_live_server_sample(
|
||||||
|
target,
|
||||||
|
timeout_seconds=timeout_seconds,
|
||||||
|
)
|
||||||
delta = persist_rcon_historical_sample(
|
delta = persist_rcon_historical_sample(
|
||||||
run_id=run_id,
|
run_id=run_id,
|
||||||
captured_at=captured_at,
|
captured_at=captured_at,
|
||||||
@@ -80,6 +90,10 @@ def run_rcon_historical_capture_unlocked(
|
|||||||
{
|
{
|
||||||
"target_key": target_metadata["target_key"],
|
"target_key": target_metadata["target_key"],
|
||||||
"external_server_id": target.external_server_id,
|
"external_server_id": target.external_server_id,
|
||||||
|
"name": target.name,
|
||||||
|
"host": target.host,
|
||||||
|
"port": target.port,
|
||||||
|
"timeout_seconds": timeout_seconds,
|
||||||
"captured_at": captured_at,
|
"captured_at": captured_at,
|
||||||
"sample_inserted": bool(delta["samples_inserted"]),
|
"sample_inserted": bool(delta["samples_inserted"]),
|
||||||
"normalized": sample["normalized"],
|
"normalized": sample["normalized"],
|
||||||
@@ -90,17 +104,9 @@ def run_rcon_historical_capture_unlocked(
|
|||||||
mark_rcon_historical_capture_failure(
|
mark_rcon_historical_capture_failure(
|
||||||
run_id=run_id,
|
run_id=run_id,
|
||||||
target=target_metadata,
|
target=target_metadata,
|
||||||
error_message=str(exc),
|
error_message=_format_error_message(exc),
|
||||||
)
|
|
||||||
errors.append(
|
|
||||||
{
|
|
||||||
"target_key": target_metadata["target_key"],
|
|
||||||
"name": target.name,
|
|
||||||
"host": target.host,
|
|
||||||
"port": target.port,
|
|
||||||
"message": str(exc),
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
errors.append(_serialize_capture_error(target, exc, timeout_seconds=timeout_seconds))
|
||||||
|
|
||||||
status = "success" if not errors else ("partial" if items else "failed")
|
status = "success" if not errors else ("partial" if items else "failed")
|
||||||
finalize_rcon_historical_capture_run(
|
finalize_rcon_historical_capture_run(
|
||||||
@@ -239,6 +245,44 @@ def _serialize_target(target: object) -> dict[str, object]:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _serialize_capture_error(
|
||||||
|
target: object,
|
||||||
|
error: Exception,
|
||||||
|
*,
|
||||||
|
timeout_seconds: float,
|
||||||
|
) -> dict[str, object]:
|
||||||
|
error_type = _classify_capture_error_type(error)
|
||||||
|
return {
|
||||||
|
"target_key": build_rcon_target_key(target),
|
||||||
|
"external_server_id": target.external_server_id,
|
||||||
|
"name": target.name,
|
||||||
|
"host": target.host,
|
||||||
|
"port": target.port,
|
||||||
|
"timeout_seconds": timeout_seconds,
|
||||||
|
"error_type": error_type,
|
||||||
|
"message": str(error),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _classify_capture_error_type(error: Exception) -> str:
|
||||||
|
if isinstance(error, RconQueryError):
|
||||||
|
return error.error_type
|
||||||
|
message = str(error).lower()
|
||||||
|
if "timed out" in message or "timeout" in message:
|
||||||
|
return "timeout"
|
||||||
|
if "401" in message or "403" in message or "login" in message or "auth" in message:
|
||||||
|
return "auth/login"
|
||||||
|
if "refused" in message:
|
||||||
|
return "connection-refused"
|
||||||
|
if "payload" in message or "json" in message or "malformed" in message:
|
||||||
|
return "payload-invalid"
|
||||||
|
return "other-error"
|
||||||
|
|
||||||
|
|
||||||
|
def _format_error_message(error: Exception) -> str:
|
||||||
|
return f"[{_classify_capture_error_type(error)}] {error}"
|
||||||
|
|
||||||
|
|
||||||
def build_arg_parser() -> argparse.ArgumentParser:
|
def build_arg_parser() -> argparse.ArgumentParser:
|
||||||
"""Create the CLI parser for manual or periodic prospective RCON capture."""
|
"""Create the CLI parser for manual or periodic prospective RCON capture."""
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
HLL_BACKEND_LIVE_DATA_SOURCE: ${HLL_BACKEND_LIVE_DATA_SOURCE:-rcon}
|
HLL_BACKEND_LIVE_DATA_SOURCE: ${HLL_BACKEND_LIVE_DATA_SOURCE:-rcon}
|
||||||
HLL_BACKEND_HISTORICAL_DATA_SOURCE: ${HLL_BACKEND_HISTORICAL_DATA_SOURCE:-rcon}
|
HLL_BACKEND_HISTORICAL_DATA_SOURCE: ${HLL_BACKEND_HISTORICAL_DATA_SOURCE:-rcon}
|
||||||
HLL_BACKEND_RCON_TIMEOUT_SECONDS: ${HLL_BACKEND_RCON_TIMEOUT_SECONDS:-10}
|
HLL_BACKEND_RCON_TIMEOUT_SECONDS: ${HLL_BACKEND_RCON_TIMEOUT_SECONDS:-20}
|
||||||
HLL_BACKEND_RCON_TARGETS: ${HLL_BACKEND_RCON_TARGETS:-}
|
HLL_BACKEND_RCON_TARGETS: >-
|
||||||
|
${HLL_BACKEND_RCON_TARGETS:-[{"name":"Comunidad Hispana #01","slug":"comunidad-hispana-01","external_server_id":"comunidad-hispana-01","host":"152.114.195.174","port":7779,"password":"replace-me-01","source_name":"community-hispana-rcon","region":"ES","game_port":null,"query_port":null},{"name":"Comunidad Hispana #02","slug":"comunidad-hispana-02","external_server_id":"comunidad-hispana-02","host":"152.114.195.150","port":7879,"password":"replace-me-02","source_name":"community-hispana-rcon","region":"ES","game_port":null,"query_port":null},{"name":"Comunidad Hispana #03","slug":"comunidad-hispana-03","external_server_id":"comunidad-hispana-03","host":"5.196.78.45","port":27230,"password":"replace-me-03","source_name":"community-hispana-rcon","region":"ES","game_port":null,"query_port":null}]}
|
||||||
ports:
|
ports:
|
||||||
- "8000:8000"
|
- "8000:8000"
|
||||||
volumes:
|
volumes:
|
||||||
@@ -26,8 +27,9 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
HLL_BACKEND_LIVE_DATA_SOURCE: ${HLL_BACKEND_LIVE_DATA_SOURCE:-rcon}
|
HLL_BACKEND_LIVE_DATA_SOURCE: ${HLL_BACKEND_LIVE_DATA_SOURCE:-rcon}
|
||||||
HLL_BACKEND_HISTORICAL_DATA_SOURCE: ${HLL_BACKEND_HISTORICAL_DATA_SOURCE:-rcon}
|
HLL_BACKEND_HISTORICAL_DATA_SOURCE: ${HLL_BACKEND_HISTORICAL_DATA_SOURCE:-rcon}
|
||||||
HLL_BACKEND_RCON_TIMEOUT_SECONDS: ${HLL_BACKEND_RCON_TIMEOUT_SECONDS:-10}
|
HLL_BACKEND_RCON_TIMEOUT_SECONDS: ${HLL_BACKEND_RCON_TIMEOUT_SECONDS:-20}
|
||||||
HLL_BACKEND_RCON_TARGETS: ${HLL_BACKEND_RCON_TARGETS:-}
|
HLL_BACKEND_RCON_TARGETS: >-
|
||||||
|
${HLL_BACKEND_RCON_TARGETS:-[{"name":"Comunidad Hispana #01","slug":"comunidad-hispana-01","external_server_id":"comunidad-hispana-01","host":"152.114.195.174","port":7779,"password":"replace-me-01","source_name":"community-hispana-rcon","region":"ES","game_port":null,"query_port":null},{"name":"Comunidad Hispana #02","slug":"comunidad-hispana-02","external_server_id":"comunidad-hispana-02","host":"152.114.195.150","port":7879,"password":"replace-me-02","source_name":"community-hispana-rcon","region":"ES","game_port":null,"query_port":null},{"name":"Comunidad Hispana #03","slug":"comunidad-hispana-03","external_server_id":"comunidad-hispana-03","host":"5.196.78.45","port":27230,"password":"replace-me-03","source_name":"community-hispana-rcon","region":"ES","game_port":null,"query_port":null}]}
|
||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
- backend
|
||||||
volumes:
|
volumes:
|
||||||
@@ -44,8 +46,9 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
HLL_BACKEND_LIVE_DATA_SOURCE: ${HLL_BACKEND_LIVE_DATA_SOURCE:-rcon}
|
HLL_BACKEND_LIVE_DATA_SOURCE: ${HLL_BACKEND_LIVE_DATA_SOURCE:-rcon}
|
||||||
HLL_BACKEND_HISTORICAL_DATA_SOURCE: ${HLL_BACKEND_HISTORICAL_DATA_SOURCE:-rcon}
|
HLL_BACKEND_HISTORICAL_DATA_SOURCE: ${HLL_BACKEND_HISTORICAL_DATA_SOURCE:-rcon}
|
||||||
HLL_BACKEND_RCON_TIMEOUT_SECONDS: ${HLL_BACKEND_RCON_TIMEOUT_SECONDS:-10}
|
HLL_BACKEND_RCON_TIMEOUT_SECONDS: ${HLL_BACKEND_RCON_TIMEOUT_SECONDS:-20}
|
||||||
HLL_BACKEND_RCON_TARGETS: ${HLL_BACKEND_RCON_TARGETS:-}
|
HLL_BACKEND_RCON_TARGETS: >-
|
||||||
|
${HLL_BACKEND_RCON_TARGETS:-[{"name":"Comunidad Hispana #01","slug":"comunidad-hispana-01","external_server_id":"comunidad-hispana-01","host":"152.114.195.174","port":7779,"password":"replace-me-01","source_name":"community-hispana-rcon","region":"ES","game_port":null,"query_port":null},{"name":"Comunidad Hispana #02","slug":"comunidad-hispana-02","external_server_id":"comunidad-hispana-02","host":"152.114.195.150","port":7879,"password":"replace-me-02","source_name":"community-hispana-rcon","region":"ES","game_port":null,"query_port":null},{"name":"Comunidad Hispana #03","slug":"comunidad-hispana-03","external_server_id":"comunidad-hispana-03","host":"5.196.78.45","port":27230,"password":"replace-me-03","source_name":"community-hispana-rcon","region":"ES","game_port":null,"query_port":null}]}
|
||||||
HLL_RCON_HISTORICAL_CAPTURE_INTERVAL_SECONDS: ${HLL_RCON_HISTORICAL_CAPTURE_INTERVAL_SECONDS:-600}
|
HLL_RCON_HISTORICAL_CAPTURE_INTERVAL_SECONDS: ${HLL_RCON_HISTORICAL_CAPTURE_INTERVAL_SECONDS:-600}
|
||||||
HLL_RCON_HISTORICAL_CAPTURE_MAX_RETRIES: ${HLL_RCON_HISTORICAL_CAPTURE_MAX_RETRIES:-2}
|
HLL_RCON_HISTORICAL_CAPTURE_MAX_RETRIES: ${HLL_RCON_HISTORICAL_CAPTURE_MAX_RETRIES:-2}
|
||||||
HLL_RCON_HISTORICAL_CAPTURE_RETRY_DELAY_SECONDS: ${HLL_RCON_HISTORICAL_CAPTURE_RETRY_DELAY_SECONDS:-15}
|
HLL_RCON_HISTORICAL_CAPTURE_RETRY_DELAY_SECONDS: ${HLL_RCON_HISTORICAL_CAPTURE_RETRY_DELAY_SECONDS:-15}
|
||||||
|
|||||||
Reference in New Issue
Block a user