File size: 3,746 Bytes
042d8bf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# remote_executor.py (مُحدَّث: يدعم التشفير والتوقيع واختيار السيرفر ديناميكياً)
# ============================================================
# يرسل المهمّة إلى سيرفر RPC خارجي مع تشفير + توقيع،
# أو يعمل بوضع JSON صافٍ لو لم يكن SecurityManager مفعَّل.
# يستخدم قائمة الأقران المكتشفة لاختيار الـ endpoint بدل IP ثابت.
# ============================================================

import requests
import json
import os
from typing import Any

# قائمة الأقران (URLs) المستخرجة من peer_discovery
from peer_discovery import PEERS
from peer_discovery import PORT

# عنوان افتراضي احتياطي (يمكن تغييره بمتغير بيئي REMOTE_SERVER)
FALLBACK_SERVER = os.getenv(
    "REMOTE_SERVER",
    "http://89.111.171.92:PORT/run"
)

# محاولة استيراد SecurityManager (اختياري)
try:
    from security_layer import SecurityManager
    security = SecurityManager(os.getenv("SHARED_SECRET", "my_shared_secret_123"))
    SECURITY_ENABLED = True
except ImportError:
    security = None
    SECURITY_ENABLED = False


def _choose_remote_server() -> str:
    """
    يختار عنوان السيرفر الذي سترسل إليه المهمة:
    1) إذا عُيّن متغير بيئي REMOTE_SERVER، يُستخدم.
    2) وإلا إذا اكتشفنا أقران عبر LAN/Internet، نأخذ أولهم.
    3) وإلا نعود إلى FALLBACK_SERVER.
    """
    env_url = os.getenv("REMOTE_SERVER")
    if env_url:
        return env_url.rstrip('/') + '/run'
    # PEERS يحوي عناوين كاملة من نوع http://ip:port/run
    if PEERS:
        # نختار الحد الأدنى من التحميل (اختياري) أو أول عنصر
        # هنا ببساطة نأخذ أول URL
        return next(iter(PEERS))
    # استخدام الافتراضي
    return FALLBACK_SERVER.rstrip('/') + '/run'


def execute_remotely(
    func_name: str,
    args: list[Any] | None = None,
    kwargs: dict[str, Any] | None = None
) -> Any:
    """إرسال استدعاء دالة إلى الخادم البعيد وإرجاع النتيجة."""
    if args is None:
        args = []
    if kwargs is None:
        kwargs = {}

    task = {
        "func": func_name,
        "args": args,
        "kwargs": kwargs,
        "sender_id": "client_node"
    }

    # اختيار السيرفر الصحيح ديناميكياً
    target_url = _choose_remote_server()

    try:
        if SECURITY_ENABLED:
            # 1) وقّع المهمة ثم شفّرها
            signed_task = security.sign_task(task)
            encrypted   = security.encrypt_data(json.dumps(signed_task).encode())

            headers = {
                "X-Signature": security.signature_hex,
                "Content-Type": "application/octet-stream"
            }
            payload = encrypted  # خام ثنائي
            resp = requests.post(
                target_url,
                headers=headers,
                data=payload,
                timeout=15
            )
        else:
            # وضع التطوير: أرسل JSON صريح
            headers = {"Content-Type": "application/json"}
            resp = requests.post(
                target_url,
                headers=headers,
                json=task,
                timeout=15
            )

        resp.raise_for_status()
        data = resp.json()
        return data.get("result", "⚠️ لا يوجد نتيجة")

    except Exception as e:
        return f"❌ فشل التنفيذ البعيد على {target_url}: {e}"