From 147f1dec220fae700a37768ee773885b99e4830c Mon Sep 17 00:00:00 2001 From: "Andrew K. Choi" Date: Sun, 31 Aug 2025 15:01:12 +0900 Subject: [PATCH] NAS EMULATOR + WOL Proxy --- Dockerfile.nas | 7 ++++ Dockerfile.wol | 7 ++++ docker-compose.yml | 26 +++++++------ src/nas_emulator.py | 91 +++++++++++++++++++++++++++++++++++++++++++++ src/wol_proxy.py | 59 +++++++++++++++++++++++++++++ 5 files changed, 179 insertions(+), 11 deletions(-) create mode 100644 Dockerfile.nas create mode 100644 Dockerfile.wol create mode 100644 src/nas_emulator.py create mode 100644 src/wol_proxy.py diff --git a/Dockerfile.nas b/Dockerfile.nas new file mode 100644 index 0000000..0ceb24e --- /dev/null +++ b/Dockerfile.nas @@ -0,0 +1,7 @@ +# Dockerfile для эмулятора NAS +FROM python:3.11-slim +WORKDIR /app +COPY src/nas_emulator.py ./ +RUN pip install flask +EXPOSE 5050 +CMD ["python", "nas_emulator.py"] diff --git a/Dockerfile.wol b/Dockerfile.wol new file mode 100644 index 0000000..19122ea --- /dev/null +++ b/Dockerfile.wol @@ -0,0 +1,7 @@ +# Dockerfile для WOL-прокси +FROM python:3.11-slim +WORKDIR /app +COPY src/wol_proxy.py ./ +RUN pip install flask +EXPOSE 5000 +CMD ["python", "wol_proxy.py"] diff --git a/docker-compose.yml b/docker-compose.yml index c35284c..ceab1e8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: '3.8' - services: synology-bot: build: @@ -23,16 +21,22 @@ services: start_period: 20s networks: - bot-network - # Для ограничения ресурсов (раскомментируйте и настройте при необходимости): - # deploy: - # resources: - # limits: - # cpus: '0.50' - # memory: 512M - # reservations: - # cpus: '0.25' - # memory: 256M + wol-proxy: + build: + context: . + dockerfile: Dockerfile.wol + container_name: wol-proxy + restart: unless-stopped + network_mode: host + + nas-emulator: + build: + context: . + dockerfile: Dockerfile.nas + container_name: nas-emulator + restart: unless-stopped + network_mode: host networks: bot-network: driver: bridge diff --git a/src/nas_emulator.py b/src/nas_emulator.py new file mode 100644 index 0000000..73ce970 --- /dev/null +++ b/src/nas_emulator.py @@ -0,0 +1,91 @@ +from flask import Flask, request, jsonify + +app = Flask(__name__) + +# Заглушки для команд NAS +@app.route('/login', methods=['POST']) +def login(): + return jsonify({'status': 'success', 'message': 'login received'}) + +@app.route('/logout', methods=['POST']) +def logout(): + return jsonify({'status': 'success', 'message': 'logout received'}) + +@app.route('/wake_on_lan', methods=['POST']) +def wake_on_lan(): + return jsonify({'status': 'success', 'message': 'wake_on_lan received'}) + +@app.route('/power_on', methods=['POST']) +def power_on(): + return jsonify({'status': 'success', 'message': 'power_on received'}) + +@app.route('/power_off', methods=['POST']) +def power_off(): + return jsonify({'status': 'success', 'message': 'power_off received'}) + +@app.route('/reboot_system', methods=['POST']) +def reboot_system(): + return jsonify({'status': 'success', 'message': 'reboot_system received'}) + +@app.route('/get_system_status', methods=['GET']) +def get_system_status(): + return jsonify({'status': 'success', 'message': 'get_system_status received', 'data': {}}) + +@app.route('/get_shared_folders', methods=['GET']) +def get_shared_folders(): + return jsonify({'status': 'success', 'message': 'get_shared_folders received', 'folders': []}) + +@app.route('/get_system_load', methods=['GET']) +def get_system_load(): + return jsonify({'status': 'success', 'message': 'get_system_load received', 'load': {}}) + +@app.route('/get_storage_status', methods=['GET']) +def get_storage_status(): + return jsonify({'status': 'success', 'message': 'get_storage_status received', 'storage': {}}) + +@app.route('/get_security_status', methods=['GET']) +def get_security_status(): + return jsonify({'status': 'success', 'message': 'get_security_status received', 'security': {}}) + +@app.route('/get_processes', methods=['GET']) +def get_processes(): + return jsonify({'status': 'success', 'message': 'get_processes received', 'processes': []}) + +@app.route('/is_online', methods=['GET']) +def is_online(): + return jsonify({'status': 'success', 'message': 'is_online received', 'online': True}) + +@app.route('/wait_for_boot', methods=['POST']) +def wait_for_boot(): + return jsonify({'status': 'success', 'message': 'wait_for_boot received'}) + +@app.route('/file/list', methods=['GET']) +def list_files(): + return jsonify({'status': 'success', 'message': 'list_files received', 'files': []}) + +@app.route('/file/info', methods=['GET']) +def get_file_info(): + return jsonify({'status': 'success', 'message': 'get_file_info received', 'info': {}}) + +@app.route('/file/download', methods=['POST']) +def download_file(): + return jsonify({'status': 'success', 'message': 'download_file received'}) + +@app.route('/file/upload', methods=['POST']) +def upload_file(): + return jsonify({'status': 'success', 'message': 'upload_file received'}) + +@app.route('/file/delete', methods=['POST']) +def delete_file(): + return jsonify({'status': 'success', 'message': 'delete_file received'}) + +@app.route('/folder/create', methods=['POST']) +def create_folder(): + return jsonify({'status': 'success', 'message': 'create_folder received'}) + +@app.route('/file/rename', methods=['POST']) +def rename_file(): + return jsonify({'status': 'success', 'message': 'rename_file received'}) + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5050) \ No newline at end of file diff --git a/src/wol_proxy.py b/src/wol_proxy.py new file mode 100644 index 0000000..e037670 --- /dev/null +++ b/src/wol_proxy.py @@ -0,0 +1,59 @@ +from flask import Flask, request, jsonify +import socket +import struct +import os + +app = Flask(__name__) + +# Функция отправки WOL-пакета +def send_wol(mac_address): + # Проверка формата MAC + mac_address = mac_address.replace(':', '').replace('-', '') + if len(mac_address) != 12: + raise ValueError('Неверный формат MAC-адреса') + # Формирование magic packet + data = b'FF' * 6 + (mac_address * 16).encode() + send_data = b'' + for i in range(0, len(data), 2): + send_data += struct.pack('B', int(data[i:i+2], 16)) + # Отправка пакета + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + sock.sendto(send_data, ('', 9)) + sock.close() + +@app.route('/wol', methods=['POST']) +def wol(): + data = request.get_json() + mac = data.get('mac') + if not mac: + return jsonify({'error': 'MAC-адрес обязателен'}), 400 + try: + send_wol(mac) + return jsonify({'status': 'WOL sent', 'mac': mac}) + except Exception as e: + return jsonify({'error': str(e)}), 500 + +# Пример получения IP по MAC через парсинг DHCP lease-файла +@app.route('/ip', methods=['GET']) +def get_ip(): + mac = request.args.get('mac') + lease_file = '/var/lib/dhcp/dhcpd.leases' # путь к lease-файлу + if not mac: + return jsonify({'error': 'MAC-адрес обязателен'}), 400 + if not os.path.exists(lease_file): + return jsonify({'error': 'Lease-файл не найден'}), 500 + ip = None + with open(lease_file, 'r') as f: + for line in f: + if mac.lower() in line.lower(): + # Пример: lease 192.168.1.100 { ... hardware ethernet 00:11:22:33:44:55; ... } + if 'lease' in line: + ip = line.split()[1] + if ip: + return jsonify({'ip': ip, 'mac': mac}) + else: + return jsonify({'error': 'IP не найден для MAC'}), 404 + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000)