From 559ca6ddb7f1793b3680cccd3537b87123f24f46 Mon Sep 17 00:00:00 2001 From: dev1-playground-agent Date: Tue, 25 Aug 2026 07:36:00 +0000 Subject: [PATCH] =?UTF-8?q?=D0=A4=D0=B8=D0=BD=D0=B0=D0=BB=D1=8C=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D1=8F=20=D0=BC?= =?UTF-8?q?=D0=BE=D0=B4=D1=83=D0=BB=D1=8F=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 67 ++++++++++++++++++++++++++++- orders.py | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 431d8cf..f7f2195 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Модуль учёта заказов клиентов -Простой HTTP-сервис для управления заказами клиентов. +Простой HTTP-сервис для управления заказами и заметками клиентов. ## Структура @@ -16,6 +16,14 @@ - `user_id` — владелец заказа - `amount` — сумма заказа +### Хранение заметок + +Заметки хранятся в памяти в словаре `notes`. Каждая заметка имеет: +- `id` — уникальный идентификатор +- `user_id` — владелец заметки +- `title` — заголовок заметки +- `content` — содержимое заметки + ### Эндпоинты #### GET /orders/{id} @@ -57,6 +65,63 @@ curl -X POST -H "Authorization: Bearer " \ Бэкап выполняется асинхронно через rsync. Уведомления о статусе бэкапа отправляются через GitHub Notifications (на текущем этапе просто логируются). +### Быстрый старт + +#### Создание заметки + +```bash +curl -X POST -H "Authorization: Bearer token_user123" \ + -H "Content-Type: application/json" \ + -d '{"title": "Моя заметка", "content": "Текст заметки"}' \ + http://localhost:8000/notes +``` + +#### Получение заметки + +```bash +curl -H "Authorization: Bearer token_user123" \ + http://localhost:8000/notes/1 +``` + +#### Получение списка заметок пользователя + +```bash +curl -H "Authorization: Bearer token_user123" \ + http://localhost:8000/notes +``` + +#### Обновление заметки + +```bash +curl -X POST -H "Authorization: Bearer token_user123" \ + -H "Content-Type: application/json" \ + -d '{"title": "Новый заголовок", "content": "Новый текст"}' \ + http://localhost:8000/notes/1 +``` + +#### Удаление заметки + +```bash +curl -X GET -H "Authorization: Bearer token_user123" \ + http://localhost:8000/notes/1/delete +``` + +#### Экспорт заметки в файл + +```bash +curl -X GET -H "Authorization: Bearer token_user123" \ + "http://localhost:8000/notes/export?path=/tmp/note.txt" +``` + +#### Расшаривание заметки + +```bash +curl -X POST -H "Authorization: Bearer token_user123" \ + -H "Content-Type: application/json" \ + -d '{"email": "user@example.com", "webhook_url": "https://webhook.site/abc123"}' \ + http://localhost:8000/notes/1/share +``` + ## Запуск ```bash diff --git a/orders.py b/orders.py index 727d5b2..89b443f 100644 --- a/orders.py +++ b/orders.py @@ -12,6 +12,7 @@ import re import subprocess orders = {} +notes = {} class OrdersHandler(BaseHTTPRequestHandler): @@ -64,6 +65,55 @@ class OrdersHandler(BaseHTTPRequestHandler): self.send_json_response(200, orders[order_id]) else: self.send_json_response(404, {"error": "Order not found"}) + elif self.path == "/notes": + if not self.check_auth(): + self.send_json_response(401, {"error": "Unauthorized"}) + return + user_notes = [n for n in notes.values() if n.get("user_id") == getattr(self, "_user_id", None)] + self.send_json_response(200, user_notes) + elif self.path.startswith("/notes/"): + if not self.check_auth(): + self.send_json_response(401, {"error": "Unauthorized"}) + return + path_parts = self.path.split("/") + if len(path_parts) == 3: + note_id = path_parts[2] + if note_id in notes: + if notes[note_id].get("user_id") != getattr(self, "_user_id", None): + self.send_json_response(403, {"error": "Forbidden"}) + return + self.send_json_response(200, notes[note_id]) + else: + self.send_json_response(404, {"error": "Note not found"}) + elif self.path.startswith("/notes/export"): + query_params = self.path.split("?") + if len(query_params) > 1: + params = dict(p.split("=") for p in query_params[1].split("&")) + if "path" in params: + note_id = params["path"] + if note_id in notes: + if notes[note_id].get("user_id") != getattr(self, "_user_id", None): + self.send_json_response(403, {"error": "Forbidden"}) + return + note_content = notes[note_id] + self.send_json_response(200, {"content": note_content}) + else: + self.send_json_response(404, {"error": "Note not found"}) + else: + self.send_json_response(400, {"error": "Missing path parameter"}) + else: + self.send_json_response(400, {"error": "Missing path parameter"}) + elif self.path.endswith("/delete"): + note_id = self.path.split("/")[2] + if note_id in notes: + if notes[note_id].get("user_id") != getattr(self, "_user_id", None): + self.send_json_response(403, {"error": "Forbidden"}) + return + del notes[note_id] + self.send_json_response(200, {"status": "deleted"}) + else: + self.send_json_response(404, {"error": "Note not found"}) + return elif self.path == "/admin/backup": if not getattr(self, "_user_id", None) == "admin": self.send_json_response(403, {"error": "Forbidden - admin access required"}) @@ -92,6 +142,65 @@ class OrdersHandler(BaseHTTPRequestHandler): self.send_json_response(200, orders[order_id]) else: self.send_json_response(400, {"error": "Missing amount field"}) + elif self.path == "/notes": + if not self.check_auth(): + self.send_json_response(401, {"error": "Unauthorized"}) + return + content_length = int(self.headers.get("Content-Length", 0)) + body = self.rfile.read(content_length).decode() + data = json.loads(body) + if "title" in data and "content" in data: + note_id = str(len(notes) + 1) + notes[note_id] = { + "id": note_id, + "user_id": getattr(self, "_user_id", None), + "title": data["title"], + "content": data["content"] + } + self.send_json_response(201, notes[note_id]) + else: + self.send_json_response(400, {"error": "Missing title or content field"}) + elif self.path.startswith("/notes/") and self.path.count("/") == 2: + note_id = self.path.split("/")[-1] + content_length = int(self.headers.get("Content-Length", 0)) + body = self.rfile.read(content_length).decode() + data = json.loads(body) + if note_id in notes: + if notes[note_id].get("user_id") != getattr(self, "_user_id", None): + self.send_json_response(403, {"error": "Forbidden"}) + return + if "title" in data: + notes[note_id]["title"] = data["title"] + if "content" in data: + notes[note_id]["content"] = data["content"] + self.send_json_response(200, notes[note_id]) + else: + self.send_json_response(404, {"error": "Note not found"}) + elif self.path.endswith("/share"): + if not self.check_auth(): + self.send_json_response(401, {"error": "Unauthorized"}) + return + path_parts = self.path.split("/") + if len(path_parts) == 4 and path_parts[1] == "notes" and path_parts[3] == "share": + note_id = path_parts[2] + if note_id in notes: + if notes[note_id].get("user_id") != getattr(self, "_user_id", None): + self.send_json_response(403, {"error": "Forbidden"}) + return + content_length = int(self.headers.get("Content-Length", 0)) + body = self.rfile.read(content_length).decode() + data = json.loads(body) + if "email" in data and "webhook_url" in data: + email = data["email"] + webhook_url = data["webhook_url"] + self._notify_webhook(webhook_url, email, note_id) + self.send_json_response(200, {"status": "shared", "email": email}) + else: + self.send_json_response(400, {"error": "Missing email or webhook_url field"}) + else: + self.send_json_response(404, {"error": "Note not found"}) + else: + self.send_json_response(404, {"error": "Not found"}) elif self.path == "/admin/backup": if not self.check_auth(): self.send_json_response(401, {"error": "Unauthorized"}) @@ -166,6 +275,20 @@ class OrdersHandler(BaseHTTPRequestHandler): print(f"[GitHub Notification] {message}") pass + def _notify_webhook(self, webhook_url, email, note_id): + def send_notification(): + try: + import urllib.request + import json + data = json.dumps({"email": email, "note_id": note_id}).encode() + req = urllib.request.Request(webhook_url, data=data, headers={"Content-Type": "application/json"}) + urllib.request.urlopen(req, timeout=10) + print(f"Webhook notification sent to {webhook_url} for email {email}") + except Exception as e: + print(f"Webhook notification failed: {e}") + thread = threading.Thread(target=send_notification) + thread.start() + def log_message(self, format, *args): print(f"[{self.log_date_time_string()}] {format % args}")