/
xine_igor
/
linux-app
Обзор
Документация
Войти
/
xine_igor
/
linux-app
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
app.py
451 строка
21 KB
xine_igor
update: app.py
02 июн 2026, 04:13
Верифицирован
02 июн 2026, 04:13
bfe5155
Код
Авторство
О чём код?
import json import random import sys import logging from http.server import BaseHTTPRequestHandler, HTTPServer from datetime import datetime # Настройка логирования для вывода в stdout (для интеграции с journalctl) logging.basicConfig( level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s', handlers=[logging.StreamHandler(sys.stdout)] ) # База данных в оперативной памяти сервера db = { "fast_food": [ {"id": 1, "name": "Вкусно и точка", "type": "Бургеры", "rating": 4.5}, {"id": 2, "name": "Додо Пицца", "type": "Пицца", "rating": 4.8} ], "users": [ {"id": 1, "username": "ivanov_99", "email": "ivanov@example.com"}, {"id": 2, "username": "petrova_m", "email": "petrova@example.com"} ] } # Списки для генерации случайных объектов RANDOM_RESTAURANT_NAMES = ["Бургер Кинг", "КФС", "Теремок", "Крошка Картошка", "Сабвей", "Папа Джонс", "Стардогс", "Вкусвилл Гоу"] RANDOM_RESTAURANT_TYPES = ["Бургеры", "Курица", "Блины", "Русская кухня", "Сэндвичи", "Пицца", "Азиатская кухня"] RANDOM_USERNAMES = ["alex_fast", "burger_lover", "dmitry_peters", "elena_k", "shaurma_fan", "mikhail_92", "olga_sweet", "sergey_cool"] RANDOM_DOMAINS = ["yandex.ru", "gmail.com", "mail.ru", "rambler.ru"] def get_health_status(): return { "status": "Healthy", "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "uptime_ok": True } class SimpleAPIHandler(BaseHTTPRequestHandler): # Переопределяем встроенный логгер, чтобы логи выводились единообразно def log_message(self, format, *args): pass def _set_headers(self, status_code=200, content_type="application/json"): self.send_response(status_code) self.send_header("Content-Type", content_type) self.end_headers() logging.info(f"ОТВЕТ: Код={status_code} | Метод={self.command} | Путь={self.path} | Клиент={self.client_address[0]}") def do_GET(self): logging.info(f"ПОЛУЧЕН ЗАПРОС: Метод=GET | Путь={self.path} | Клиент={self.client_address[0]}") # 1. Главная HTML-страница if self.path in ("/", "/index.html"): self._set_headers(200, "text/html; charset=utf-8") html_content = self.generate_html() self.wfile.write(html_content.encode("utf-8")) # 2. Эндпоинт Health Check elif self.path == "/api/health": self._set_headers(200) self.wfile.write(json.dumps(get_health_status(), ensure_ascii=False).encode("utf-8")) # 3. Получить все заведения elif self.path == "/api/fast-food": self._set_headers(200) self.wfile.write(json.dumps(db["fast_food"], ensure_ascii=False).encode("utf-8")) # 4. Получить всех пользователей elif self.path == "/api/users": self._set_headers(200) self.wfile.write(json.dumps(db["users"], ensure_ascii=False).encode("utf-8")) else: self._set_headers(404) self.wfile.write(json.dumps({"error": "Not Found"}).encode("utf-8")) def do_POST(self): logging.info(f"ПОЛУЧЕН ЗАПРОС: Метод=POST | Путь={self.path} | Клиент={self.client_address[0]}") # Автоматическая генерация заведения if self.path == "/api/fast-food/add": random_name = f"{random.choice(RANDOM_RESTAURANT_NAMES)} {random.choice(['Экспресс', 'Гранд', '24/7', 'Сити', ''])}".strip() new_restaurant = { "id": len(db["fast_food"]) + 1, "name": random_name, "type": random.choice(RANDOM_RESTAURANT_TYPES), "rating": round(random.uniform(3.0, 5.0), 1) } db["fast_food"].append(new_restaurant) logging.info(f"ГЕНЕРАЦИЯ: Создано случайное заведение '{random_name}'") self._set_headers(201) self.wfile.write(json.dumps(new_restaurant, ensure_ascii=False).encode("utf-8")) return # Автоматическая генерация пользователя elif self.path == "/api/users/add": random_user = { "id": len(db["users"]) + 1, "username": f"{random.choice(RANDOM_USERNAMES)}_{random.randint(10, 999)}", "email": f"user{random.randint(100, 999)}@{random.choice(RANDOM_DOMAINS)}" } db["users"].append(random_user) logging.info(f"ГЕНЕРАЦИЯ: Создан случайный пользователь '{random_user['username']}'") self._set_headers(201) self.wfile.write(json.dumps(random_user, ensure_ascii=False).encode("utf-8")) return # Чтение тела запроса для ручного создания 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')) except json.JSONDecodeError: self._set_headers(400) self.wfile.write(json.dumps({"error": "Invalid JSON"}).encode("utf-8")) return # Ручное создание заведения if self.path == "/api/fast-food": if "name" in data and "type" in data: try: rating = float(data.get("rating", 4.0)) except ValueError: rating = 4.0 new_restaurant = { "id": len(db["fast_food"]) + 1, "name": data["name"], "type": data["type"], "rating": rating } db["fast_food"].append(new_restaurant) logging.info(f"БАЗА ДАННЫХ: Добавлено заведение '{data['name']}'") self._set_headers(201) self.wfile.write(json.dumps(new_restaurant, ensure_ascii=False).encode("utf-8")) else: self._set_headers(400) self.wfile.write(json.dumps({"error": "Missing name or type"}).encode("utf-8")) # Ручное создание пользователя elif self.path == "/api/users": if "username" in data and "email" in data: new_user = { "id": len(db["users"]) + 1, "username": data["username"], "email": data["email"] } db["users"].append(new_user) logging.info(f"БАЗА ДАННЫХ: Добавлен пользователь '{data['username']}'") self._set_headers(201) self.wfile.write(json.dumps(new_user, ensure_ascii=False).encode("utf-8")) else: self._set_headers(400) self.wfile.write(json.dumps({"error": "Missing username or email"}).encode("utf-8")) else: self._set_headers(404) self.wfile.write(json.dumps({"error": "Endpoint not found"}).encode("utf-8")) def generate_html(self): health = get_health_status() ff_rows = "".join([ f"<tr><td>{item['id']}</td><td>{item['name']}</td><td>{item['type']}</td><td>★ {item['rating']}</td></tr>" for item in db["fast_food"] ]) users_rows = "".join([ f"<tr><td>{u['id']}</td><td>{u['username']}</td><td>{u['email']}</td></tr>" for u in db["users"] ]) html_template = """<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Управление сервисом быстрого питания</title> <style> body { font-family: Arial, sans-serif; margin: 30px; background-color: #f4f6f9; color: #333; } .container { max-width: 900px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } .health-card { background-color: #e2f0d9; border-left: 5px solid #70ad47; padding: 15px; margin-bottom: 20px; border-radius: 4px; } .health-title { font-weight: bold; margin-bottom: 5px; color: #385723; } table { width: 100%; border-collapse: collapse; margin-bottom: 20px; } th, td { border: 1px solid #ddd; padding: 10px; text-align: left; } th { background-color: #f2f2f2; } .form-section { background-color: #f9f9f9; padding: 15px; border: 1px solid #ddd; border-radius: 4px; margin-bottom: 30px; } .form-group { margin-bottom: 10px; } label { display: block; margin-bottom: 5px; font-weight: bold; } input[type="text"], input[type="email"] { width: 100%; padding: 8px; box-sizing: border-box; } button { padding: 10px 15px; background-color: #0078d4; color: white; border: none; border-radius: 4px; cursor: pointer; margin-right: 10px; } button:hover { background-color: #005a9e; } .btn-random { background-color: #28a745; } .btn-random:hover { background-color: #218838; } .button-group { display: flex; gap: 10px; } </style> </head> <body> <div class="container"> <div class="health-card"> <div class="health-title">Мониторинг состояния (Health Check)</div> <div>Статус: <strong>{{HEALTH_STATUS}}</strong></div> <div>Время проверки при загрузке: <span>{{HEALTH_TIMESTAMP}}</span></div> </div> <h1>Панель управления заведениями</h1> <h2>Сущность 1: Быстрое питание (Fast Food)</h2> <table> <thead> <tr><th>ID</th><th>Название</th><th>Тип кухни</th><th>Рейтинг</th></tr> </thead> <tbody> {{FF_ROWS}} </tbody> </table> <div class="form-section"> <h3>Управление заведениями</h3> <div class="form-group"> <label>Название:</label> <input type="text" id="ffName" placeholder="например, Крошка Картошка"> </div> <div class="form-group"> <label>Тип кухни:</label> <input type="text" id="ffType" placeholder="например, Русская кухня"> </div> <div class="form-group"> <label>Рейтинг (0-5):</label> <input type="text" id="ffRating" placeholder="4.5"> </div> <div class="button-group"> <button onclick="addFastFood()">Создать вручную</button> <button class="btn-random" onclick="addRandomFastFood()">Сгенерировать случайно</button> </div> </div> <h2>Сущность 2: Пользователи (Users)</h2> <table> <thead> <tr><th>ID</th><th>Имя пользователя</th><th>Email</th></tr> </thead> <tbody> {{USERS_ROWS}} </tbody> </table> <div class="form-section"> <h3>Управление пользователями</h3> <div class="form-group"> <label>Имя пользователя:</label> <input type="text" id="userName" placeholder="ivan_fast"> </div> <div class="form-group"> <label>Email:</label> <input type="email" id="userEmail" placeholder="user@example.com"> </div> <div class="button-group"> <button onclick="addUser()">Создать вручную</button> <button class="btn-random" onclick="addRandomUser()">Сгенерировать случайно</button> </div> </div> </div> <script> async function addFastFood() { const name = document.getElementById('ffName').value; const type = document.getElementById('ffType').value; const rating = document.getElementById('ffRating').value || "4.0"; if (!name || !type) return alert('Заполните название и тип кухни'); const response = await fetch('/api/fast-food', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, type, rating }) }); if (response.ok) location.reload(); } async function addRandomFastFood() { const response = await fetch('/api/fast-food/add', { method: 'POST' }); if (response.ok) location.reload(); } async function addUser() { const username = document.getElementById('userName').value; const email = document.getElementById('userEmail').value; if (!username || !email) return alert('Заполните все поля'); const response = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, email }) }); if (response.ok) location.reload(); } async function addRandomUser() { const response = await fetch('/api/users/add', { method: 'POST' }); if (response.ok) location.reload(); } </script> </body> </html> """ html = html_template.replace("{{HEALTH_STATUS}}", str(health['status'])) html = html.replace("{{HEALTH_TIMESTAMP}}", str(health['timestamp'])) html = html.replace("{{FF_ROWS}}", ff_rows) html = html.replace("{{USERS_ROWS}}", users_rows) return html def run(port=5000): server_address = ('', port) httpd = HTTPServer(server_address, SimpleAPIHandler) logging.info(f"СТАРТ: Сервер инициализирован на порту {port}") try: httpd.serve_forever() except KeyboardInterrupt: pass httpd.server_close() logging.info("СТОП: Сервер остановлен") if __name__ == '__main__': run() # ============================================================================== # ТЕХНИЧЕСКАЯ ДОКУМЕНТАЦИЯ API СЕРВИСА # ============================================================================== """ ## Общее описание Данный веб-сервис написан на чистом Python с использованием исключительно стандартной библиотеки. Порт приложения по умолчанию внутри Linux: 5000. При развертывании через проброс портов VirtualBox (NAT) порт по умолчанию: 8080. Все примеры ниже составлены для адреса: http://127.0.0.1:8080 (запросы со стороны Windows-хоста). --- ### 1. Главная страница (Интерфейс управления) Возвращает графическую HTML-панель управления со статусом работоспособности системы и таблицами данных. * **Метод:** `GET` * **Путь:** `/` или `/index.html` * **Пример curl (Linux Bash):** curl http://127.0.0.1:8080/ * **Пример curl (Windows PowerShell):** Invoke-RestMethod -Uri "http://127.0.0.1:8080/" -Method Get --- ### 2. Проверка работоспособности (Health Check) Возвращает текущий статус сервиса, время проверки на сервере и логическое подтверждение аптайма. * **Метод:** `GET` * **Путь:** `/api/health` * **Пример curl (Linux Bash):** curl http://127.0.0.1:8080/api/health * **Пример curl (Windows PowerShell):** Invoke-RestMethod -Uri "http://127.0.0.1:8080/api/health" --- ### 3. Получение списка всех заведений Возвращает массив JSON, содержащий все сохраненные объекты сущности «Быстрое питание». * **Метод:** `GET` * **Путь:** `/api/fast-food` * **Пример curl (Linux Bash):** curl http://127.0.0.1:8080/api/fast-food * **Пример curl (Windows PowerShell):** Invoke-RestMethod -Uri "http://127.0.0.1:8080/api/fast-food" --- ### 4. Ручное создание заведения Добавляет новое заведение быстрого питания на основе переданных JSON-данных. * **Метод:** `POST` * **Путь:** `/api/fast-food` * **JSON-структура тела:** { "name": "Название_Заведения", "type": "Тип_Кухни", "rating": 4.5 } * **Пример curl (Linux Bash):** curl -X POST -H "Content-Type: application/json" -d '{"name": "Теремок", "type": "Блины", "rating": 4.7}' http://127.0.0.1:8080/api/fast-food * **Пример curl (Windows PowerShell):** Invoke-RestMethod -Uri "http://127.0.0.1:8080/api/fast-food" -Method Post -ContentType "application/json" -Body '{"name": "Теремок", "type": "Блины", "rating": 4.7}' --- ### 5. Автоматическое создание заведения (Случайная генерация) Генерирует случайный объект заведения на основе внутренних списков и сохраняет его в БД. * **Метод:** `POST` * **Путь:** `/api/fast-food/add` * **Пример curl (Linux Bash):** curl -X POST http://127.0.0.1:8080/api/fast-food/add * **Пример curl (Windows PowerShell):** Invoke-RestMethod -Uri "http://127.0.0.1:8080/api/fast-food/add" -Method Post --- ### 6. Получение списка всех пользователей Возвращает массив JSON, содержащий все объекты сущности «Пользователи». * **Метод:** `GET` * **Путь:** `/api/users` * **Пример curl (Linux Bash):** curl http://127.0.0.1:8080/api/users * **Пример curl (Windows PowerShell):** Invoke-RestMethod -Uri "http://127.0.0.1:8080/api/users" --- ### 7. Ручное создание пользователя Добавляет нового пользователя на основе переданных JSON-данных. * **Метод:** `POST` * **Путь:** `/api/users` * **JSON-структура тела:** { "username": "имя_пользователя", "email": "почта@домен.com" } * **Пример curl (Linux Bash):** curl -X POST -H "Content-Type: application/json" -d '{"username": "mikhail", "email": "mikhail@gmail.com"}' http://127.0.0.1:8080/api/users * **Пример curl (Windows PowerShell):** Invoke-RestMethod -Uri "http://127.0.0.1:8080/api/users" -Method Post -ContentType "application/json" -Body '{"username": "mikhail", "email": "mikhail@gmail.com"}' --- ### 8. Автоматическое создание пользователя (Случайная генерация) Генерирует случайный объект пользователя на основе внутренних шаблонов имен и почтовых доменов. * **Метод:** `POST` * **Путь:** `/api/users/add` * **Пример curl (Linux Bash):** curl -X POST http://127.0.0.1:8080/api/users/add * **Пример curl (Windows PowerShell):** Invoke-RestMethod -Uri "http://127.0.0.1:8080/api/users/add" -Method Post """