commit 302b09a4f7bd210a0453e2c4f77c70fbde3cef79 Author: Kavalar Date: Tue May 26 20:16:08 2026 +0300 First diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b2717c --- /dev/null +++ b/.gitignore @@ -0,0 +1,80 @@ +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ +pythonenv* + +# Серверные файлы +data.json +data.default.json + +# Логи +*.log +*.pid + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ +.DS_Store + +# OS generated files +Thumbs.db +ehthumbs.db +Desktop.ini + +# Backup files +*.bak +*.backup +*.tmp + +# Package files +*.egg +*.egg-info/ +dist/ +build/ +*.whl + +# Jupyter Notebook +.ipynb_checkpoints/ + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# pytest +.pytest_cache/ + +# Coverage +.coverage +.coverage.* +htmlcov/ +.coveragerc + +# Virtual Environment +venv/ +env/ +ENV/ +.venv + +# Secrets +*.key +*.pem +.env +.secrets + +# Проект специфичные +data.default.json +data.json +*.db +*.sqlite \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..5844781 --- /dev/null +++ b/index.html @@ -0,0 +1,503 @@ + + + + + + Бизнес-объединения ДНР | Аналитический обзор + Админ-панель + + + + + +
+
+

Бизнес-объединения ДНР

+

Аналитический обзор + Админ-панель (данные хранятся в data.json)

+
+ +
+ + +
+ +
+
Загрузка...
+
+ +
+
+ + + +
НазваниеТипСтатусЧленствоПриоритетРесурс
Загрузка...
+
+
+ + +
+ + +
+
+
+

Редактор организаций

+ +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ + + + +
+
+
✅ Данные сохранены
+ + + + \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..5596b44 --- /dev/null +++ b/main.py @@ -0,0 +1,16 @@ +# This is a sample Python script. + +# Press Shift+F10 to execute it or replace it with your code. +# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings. + + +def print_hi(name): + # Use a breakpoint in the code line below to debug your script. + print(f'Hi, {name}') # Press Ctrl+F8 to toggle the breakpoint. + + +# Press the green button in the gutter to run the script. +if __name__ == '__main__': + print_hi('PyCharm') + +# See PyCharm help at https://www.jetbrains.com/help/pycharm/ diff --git a/server.py b/server.py new file mode 100644 index 0000000..173f2ec --- /dev/null +++ b/server.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Сервер для управления данными бизнес-объединений ДНР. +Поддерживает: +- GET /data.json - получение данных +- POST /save - сохранение данных в файл +- POST /reset - сброс к дефолтным данным (загружает из data.default.json) +""" + +import http.server +import socketserver +import json +import os +import shutil +from urllib.parse import urlparse + +PORT = 8092 +DATA_FILE = 'data.json' +DEFAULT_DATA_FILE = 'data.default.json' + + +# Дефолтные данные вынесены в отдельный файл data.default.json +# Если нужно создать файл с дефолтом - раскомментировать функцию ниже + +class CustomHTTPRequestHandler(http.server.SimpleHTTPRequestHandler): + + def end_headers(self): + self.send_header('Access-Control-Allow-Origin', '*') + self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS') + self.send_header('Access-Control-Allow-Headers', 'Content-Type') + super().end_headers() + + def do_OPTIONS(self): + self.send_response(200) + self.end_headers() + + def do_GET(self): + parsed_path = urlparse(self.path) + + # Отдаём data.json + if parsed_path.path == '/data.json': + self.send_response(200) + self.send_header('Content-Type', 'application/json; charset=utf-8') + self.end_headers() + try: + with open(DATA_FILE, 'r', encoding='utf-8') as f: + self.wfile.write(f.read().encode('utf-8')) + except FileNotFoundError: + self.send_error(404, 'data.json not found') + return + + # Отдаём index.html + if parsed_path.path == '/' or parsed_path.path == '/index.html': + self.path = '/index.html' + + # Статические файлы + if self.path.endswith('.html'): + self.send_response(200) + self.send_header('Content-Type', 'text/html; charset=utf-8') + self.end_headers() + try: + with open(self.path[1:], 'rb') as f: + self.wfile.write(f.read()) + except FileNotFoundError: + self.send_error(404, 'File not found') + return + + super().do_GET() + + def do_POST(self): + parsed_path = urlparse(self.path) + + # Сохранение данных + if parsed_path.path == '/save': + content_length = int(self.headers.get('Content-Length', 0)) + post_data = self.rfile.read(content_length) + + try: + data = json.loads(post_data.decode('utf-8')) + + with open(DATA_FILE, 'w', encoding='utf-8') as f: + json.dump(data, f, ensure_ascii=False, indent=2) + + self.send_response(200) + self.send_header('Content-Type', 'application/json') + self.end_headers() + response = json.dumps({'success': True, 'message': 'Данные сохранены'}) + self.wfile.write(response.encode('utf-8')) + + except json.JSONDecodeError as e: + self.send_response(400) + self.send_header('Content-Type', 'application/json') + self.end_headers() + response = json.dumps({'success': False, 'message': f'Ошибка JSON: {str(e)}'}) + self.wfile.write(response.encode('utf-8')) + except Exception as e: + self.send_response(500) + self.send_header('Content-Type', 'application/json') + self.end_headers() + response = json.dumps({'success': False, 'message': f'Ошибка сервера: {str(e)}'}) + self.wfile.write(response.encode('utf-8')) + return + + # Сброс к дефолту (если есть файл data.default.json) + if parsed_path.path == '/reset': + try: + if os.path.exists(DEFAULT_DATA_FILE): + shutil.copy(DEFAULT_DATA_FILE, DATA_FILE) + self.send_response(200) + self.send_header('Content-Type', 'application/json') + self.end_headers() + response = json.dumps({'success': True, 'message': 'Данные сброшены к исходным'}) + self.wfile.write(response.encode('utf-8')) + else: + self.send_response(404) + self.send_header('Content-Type', 'application/json') + self.end_headers() + response = json.dumps({'success': False, 'message': 'Файл с дефолтными данными не найден'}) + self.wfile.write(response.encode('utf-8')) + except Exception as e: + self.send_response(500) + self.send_header('Content-Type', 'application/json') + self.end_headers() + response = json.dumps({'success': False, 'message': str(e)}) + self.wfile.write(response.encode('utf-8')) + return + + self.send_response(404) + self.end_headers() + + def log_message(self, format, *args): + print(f"[{self.address_string()}] {format % args}") + + +def ensure_data_file(): + """Проверяет наличие data.json, если нет - создаёт из дефолта или пустой""" + if not os.path.exists(DATA_FILE): + if os.path.exists(DEFAULT_DATA_FILE): + shutil.copy(DEFAULT_DATA_FILE, DATA_FILE) + print(f"📄 Создан файл данных из {DEFAULT_DATA_FILE}") + else: + # Создаём минимальный пустой файл + empty_data = { + "organizations": [], + "additionalOrganizations": [], + "metadata": {"lastUpdated": "2026-01-15", "totalOrganizations": 0} + } + with open(DATA_FILE, 'w', encoding='utf-8') as f: + json.dump(empty_data, f, ensure_ascii=False, indent=2) + print(f"📄 Создан пустой файл данных: {DATA_FILE}") + print(f"⚠️ Для работы с данными создайте файл {DEFAULT_DATA_FILE} с дефолтными данными") + + +def run_server(): + os.chdir(os.path.dirname(os.path.abspath(__file__))) + ensure_data_file() + + with socketserver.TCPServer(("", PORT), CustomHTTPRequestHandler) as httpd: + print("=" * 60) + print(f"🚀 Сервер запущен!") + print(f"📁 Директория: {os.getcwd()}") + print(f"🌐 Локальный доступ: http://localhost:{PORT}") + print(f"📄 Файл данных: {DATA_FILE}") + print(f"📄 Дефолтный файл (если есть): {DEFAULT_DATA_FILE}") + print(f"⏹️ Для остановки сервера нажмите Ctrl+C") + print("=" * 60) + + try: + httpd.serve_forever() + except KeyboardInterrupt: + print("\n🛑 Сервер остановлен.") + httpd.shutdown() + + +if __name__ == "__main__": + run_server() \ No newline at end of file