GitHub Action commited on
Commit
56f6797
·
1 Parent(s): 1341120

Sync from GitHub with Git LFS

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. agents/notebook/templates/messages.html +1 -1
  2. agents/notebook/views.py +11 -0
  3. agents/web_ui.py +0 -1
  4. hf_repo/agents/notebook/templates/messages.html +2 -2
  5. hf_repo/hf_repo/hf_repo/agents/init.py +1 -1
  6. hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html +26 -2
  7. hf_repo/hf_repo/hf_repo/agents/notebook/views.py +43 -9
  8. hf_repo/hf_repo/hf_repo/agents/tools/storage.py +43 -2
  9. hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/db_structure.sql +22 -10
  10. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/add_message.py +5 -3
  11. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py +1 -1
  12. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py +11 -3
  13. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/__init__.py +1 -1
  14. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html +2 -2
  15. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py +1 -1
  16. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py +11 -1
  17. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/requirements.txt +2 -1
  18. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html +2 -2
  19. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py +1 -1
  20. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html +1 -1
  21. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/db_structure.sql +4 -3
  22. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py +5 -1
  23. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py +12 -0
  24. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html +3 -3
  25. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html +4 -3
  26. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html +2 -2
  27. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html +2 -2
  28. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html +1 -1
  29. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py +0 -5
  30. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py +35 -26
  31. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py +15 -9
  32. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/web_ui.py +0 -2
  33. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/login.html +14 -6
  34. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html +2 -4
  35. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/register.html +15 -7
  36. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py +7 -7
  37. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/config.yml +65 -40
  38. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/login.html +1 -0
  39. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/register.html +1 -0
  40. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/config.yml +40 -65
  41. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/login.html +5 -14
  42. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html +9 -2
  43. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/register.html +6 -14
  44. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py +36 -30
  45. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py +26 -19
  46. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/login.html +14 -0
  47. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/register.html +14 -0
  48. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html +40 -8
  49. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/requirements.txt +2 -1
  50. hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/web_ui.py +3 -0
agents/notebook/templates/messages.html CHANGED
@@ -51,7 +51,7 @@
51
  <div>
52
  {% if msg.user_did == request.session['did'] %}<span class="from-self" title="это ваше сообщение"></span>{% endif %}
53
  {% if msg.hidden %}<span class="private" title="личное сообщение"></span>{% endif %}
54
- Источник: <i>{{ msg.source }}</i> — {{ msg.timestamp[:19].replace('T', ' ') }}
55
  </div>
56
  <div>
57
  {% if msg.badges %}{{ msg.badges }}{% endif %}Пользователь: {% if msg.username %}<b>{{ msg.username }}</b>{% endif %} {% if msg.user_did %}({{ msg.user_did }}){% endif %}
 
51
  <div>
52
  {% if msg.user_did == request.session['did'] %}<span class="from-self" title="это ваше сообщение"></span>{% endif %}
53
  {% if msg.hidden %}<span class="private" title="личное сообщение"></span>{% endif %}
54
+ Источник: <i>{{ msg.source }}</i> — {{ msg.timestamp | format_timestamp }}
55
  </div>
56
  <div>
57
  {% if msg.badges %}{{ msg.badges }}{% endif %}Пользователь: {% if msg.username %}<b>{{ msg.username }}</b>{% endif %} {% if msg.user_did %}({{ msg.user_did }}){% endif %}
agents/notebook/views.py CHANGED
@@ -7,6 +7,7 @@ import uuid
7
  from fastapi import APIRouter, Request, Form, UploadFile, File
8
  from fastapi.responses import RedirectResponse, HTMLResponse, StreamingResponse
9
  from fastapi.templating import Jinja2Templates
 
10
  from starlette.status import HTTP_303_SEE_OTHER
11
  from typing import List
12
  from tools.storage import Storage
@@ -20,6 +21,16 @@ allowed_attributes = {
20
  'a': ['href', 'title']
21
  }
22
 
 
 
 
 
 
 
 
 
 
 
23
  # Очистка сообщений
24
  def sanitize_html(text: str) -> str:
25
  # 1. Сначала очищаем HTML
 
7
  from fastapi import APIRouter, Request, Form, UploadFile, File
8
  from fastapi.responses import RedirectResponse, HTMLResponse, StreamingResponse
9
  from fastapi.templating import Jinja2Templates
10
+ from datetime import datetime
11
  from starlette.status import HTTP_303_SEE_OTHER
12
  from typing import List
13
  from tools.storage import Storage
 
21
  'a': ['href', 'title']
22
  }
23
 
24
+ # Обработка даты и времени
25
+ def format_timestamp(value):
26
+ try:
27
+ dt = datetime.fromtimestamp(float(value))
28
+ return dt.strftime("%Y-%m-%d %H:%M:%S")
29
+ except Exception:
30
+ return str(value)
31
+
32
+ templates.env.filters['format_timestamp'] = format_timestamp
33
+
34
  # Очистка сообщений
35
  def sanitize_html(text: str) -> str:
36
  # 1. Сначала очищаем HTML
agents/web_ui.py CHANGED
@@ -20,7 +20,6 @@ storage = Storage()
20
  app = FastAPI()
21
  app.add_middleware(SessionMiddleware, secret_key="очень_секретный_ключ")
22
 
23
-
24
  app.mount("/static", StaticFiles(directory=os.path.join(os.path.dirname(__file__), "notebook/static")), name="static")
25
  templates = Jinja2Templates(directory=os.path.join(os.path.dirname(__file__), "notebook/templates"))
26
 
 
20
  app = FastAPI()
21
  app.add_middleware(SessionMiddleware, secret_key="очень_секретный_ключ")
22
 
 
23
  app.mount("/static", StaticFiles(directory=os.path.join(os.path.dirname(__file__), "notebook/static")), name="static")
24
  templates = Jinja2Templates(directory=os.path.join(os.path.dirname(__file__), "notebook/templates"))
25
 
hf_repo/agents/notebook/templates/messages.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <title>Сообщения</title>
5
  <style>
6
- .message { padding: 8px; margin: 10px 0; border-radius: 8px; }
7
  .source-user { background-color: #e0f7fa; }
8
  .source-cli { background-color: #dcedc8; }
9
  .source-llm { background-color: #f3e5f5; }
@@ -51,7 +51,7 @@
51
  <div>
52
  {% if msg.user_did == request.session['did'] %}<span class="from-self" title="это ваше сообщение"></span>{% endif %}
53
  {% if msg.hidden %}<span class="private" title="личное сообщение"></span>{% endif %}
54
- Источник: <i>{{ msg.source }}</i> — {{ msg.timestamp[:19].replace('T', ' ') }}
55
  </div>
56
  <div>
57
  {% if msg.badges %}{{ msg.badges }}{% endif %}Пользователь: {% if msg.username %}<b>{{ msg.username }}</b>{% endif %} {% if msg.user_did %}({{ msg.user_did }}){% endif %}
 
3
  <head>
4
  <title>Сообщения</title>
5
  <style>
6
+ .message { padding: 16px; margin: 16px 0; border-radius: 8px; border: 5px solid rgba(0, 0, 0, 0.3); box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); }
7
  .source-user { background-color: #e0f7fa; }
8
  .source-cli { background-color: #dcedc8; }
9
  .source-llm { background-color: #f3e5f5; }
 
51
  <div>
52
  {% if msg.user_did == request.session['did'] %}<span class="from-self" title="это ваше сообщение"></span>{% endif %}
53
  {% if msg.hidden %}<span class="private" title="личное сообщение"></span>{% endif %}
54
+ Источник: <i>{{ msg.source }}</i> — {{ msg.timestamp | format_timestamp }}
55
  </div>
56
  <div>
57
  {% if msg.badges %}{{ msg.badges }}{% endif %}Пользователь: {% if msg.username %}<b>{{ msg.username }}</b>{% endif %} {% if msg.user_did %}({{ msg.user_did }}){% endif %}
hf_repo/hf_repo/hf_repo/agents/init.py CHANGED
@@ -101,7 +101,7 @@ def init_config_table(storage, config):
101
 
102
  def ensure_directories():
103
  for folder in ["logs", "scripts"]:
104
- full_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", folder))
105
  if not os.path.exists(full_path):
106
  os.makedirs(full_path)
107
  print(f"[+] Создан каталог: {full_path}")
 
101
 
102
  def ensure_directories():
103
  for folder in ["logs", "scripts"]:
104
+ full_path = os.path.abspath(os.path.join(os.path.dirname(__file__), folder))
105
  if not os.path.exists(full_path):
106
  os.makedirs(full_path)
107
  print(f"[+] Создан каталог: {full_path}")
hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html CHANGED
@@ -10,6 +10,12 @@
10
  .source-system { background-color: #fff3e0; }
11
  .private::after { content: " 🔒"; }
12
  .from-self::before { content: "🧍 "; }
 
 
 
 
 
 
13
  </style>
14
  </head>
15
  <body>
@@ -23,8 +29,10 @@
23
 
24
  <h1>Сообщения</h1>
25
 
26
- <form method="post">
27
- <textarea name="text" rows="3" cols="40" placeholder="Введите сообщение..."></textarea><br>
 
 
28
  <button name="hidden" value="false" type="submit">📢 Отправить</button>
29
  <button name="hidden" value="true" type="submit">🙋 Отправить приватно</button>
30
  </form>
@@ -50,6 +58,22 @@
50
  </div>
51
  <hr>
52
  <div>{{ msg.text|safe }}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  </div>
54
  {% endfor %}
55
  </body>
 
10
  .source-system { background-color: #fff3e0; }
11
  .private::after { content: " 🔒"; }
12
  .from-self::before { content: "🧍 "; }
13
+ pre.code-block {
14
+ background-color: #f0f0f0;
15
+ padding: 10px;
16
+ border-radius: 6px;
17
+ overflow-x: auto;
18
+ }
19
  </style>
20
  </head>
21
  <body>
 
29
 
30
  <h1>Сообщения</h1>
31
 
32
+ <form method="post" enctype="multipart/form-data">
33
+ <textarea name="text" rows="3" cols="60" placeholder="Введите сообщение..."></textarea><br>
34
+ <textarea name="code" rows="6" cols="60" placeholder="Прикрепите код (опционально)..."></textarea><br>
35
+ <label>Файлы: <input type="file" name="binary_files" multiple></label><br>
36
  <button name="hidden" value="false" type="submit">📢 Отправить</button>
37
  <button name="hidden" value="true" type="submit">🙋 Отправить приватно</button>
38
  </form>
 
58
  </div>
59
  <hr>
60
  <div>{{ msg.text|safe }}</div>
61
+
62
+ {% if msg.code %}
63
+ <hr>
64
+ <div><b>Код:</b></div>
65
+ <pre class="code-block">{{ msg.code }}</pre>
66
+ {% endif %}
67
+
68
+ {% if msg.attachments %}
69
+ <hr>
70
+ <div><b>Файлы:</b></div>
71
+ <ul>
72
+ {% for file in msg.attachments %}
73
+ <li><a href="/download/{{ file.id }}">{{ file.filename }}</a> ({{ file.size }} байт)</li>
74
+ {% endfor %}
75
+ </ul>
76
+ {% endif %}
77
  </div>
78
  {% endfor %}
79
  </body>
hf_repo/hf_repo/hf_repo/agents/notebook/views.py CHANGED
@@ -2,11 +2,13 @@
2
 
3
  import re
4
  import bleach
 
5
 
6
- from fastapi import APIRouter, Request, Form
7
- from fastapi.responses import RedirectResponse, HTMLResponse
8
  from fastapi.templating import Jinja2Templates
9
  from starlette.status import HTTP_303_SEE_OTHER
 
10
  from tools.storage import Storage
11
 
12
  router = APIRouter()
@@ -76,27 +78,59 @@ def show_messages(request: Request, only_personal: bool = False):
76
  })
77
 
78
  @router.post("/messages")
79
- def post_message(
80
  request: Request,
81
  text: str = Form(...),
82
- hidden: str = Form(default="false")
 
 
83
  ):
84
  did = request.session.get("did", "anon")
85
  is_hidden = 1 if hidden.lower() == "true" else 0
86
 
87
- # Проверка на бан
88
  if storage.is_banned(did):
89
  return HTMLResponse(content="Вы забанены и не можете отправлять сообщения.", status_code=403)
90
 
91
- if text.strip():
92
- storage.write_note(
93
- content=sanitize_html(text.strip()),
 
 
 
 
94
  user_did=did,
95
  source="user",
96
- hidden=is_hidden
 
97
  )
 
 
 
 
 
 
 
 
 
 
 
 
98
  return RedirectResponse(url="/messages", status_code=303)
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  @router.get("/login")
101
  def login_page(request: Request):
102
  return templates.TemplateResponse("login.html", {"request": request})
 
2
 
3
  import re
4
  import bleach
5
+ import uuid
6
 
7
+ from fastapi import APIRouter, Request, Form, UploadFile, File
8
+ from fastapi.responses import RedirectResponse, HTMLResponse, StreamingResponse
9
  from fastapi.templating import Jinja2Templates
10
  from starlette.status import HTTP_303_SEE_OTHER
11
+ from typing import List
12
  from tools.storage import Storage
13
 
14
  router = APIRouter()
 
78
  })
79
 
80
  @router.post("/messages")
81
+ async def post_message(
82
  request: Request,
83
  text: str = Form(...),
84
+ code: str = Form(None),
85
+ hidden: str = Form(default="false"),
86
+ binary_files: List[UploadFile] = File(default=[])
87
  ):
88
  did = request.session.get("did", "anon")
89
  is_hidden = 1 if hidden.lower() == "true" else 0
90
 
 
91
  if storage.is_banned(did):
92
  return HTMLResponse(content="Вы забанены и не можете отправлять сообщения.", status_code=403)
93
 
94
+ if text.strip() or code or binary_files:
95
+ # Очистка текста
96
+ safe_text = sanitize_html(text.strip()) if text else ""
97
+
98
+ # Сохраняем сообщение и получаем message_id
99
+ message_id = storage.write_note_returning_id(
100
+ content=safe_text,
101
  user_did=did,
102
  source="user",
103
+ hidden=is_hidden,
104
+ code=code.strip() if code else None
105
  )
106
+
107
+ # Сохраняем файлы
108
+ for upload in binary_files:
109
+ data = await upload.read()
110
+ if data:
111
+ storage.save_attachment(
112
+ message_id=message_id,
113
+ filename=upload.filename,
114
+ mime_type=upload.content_type,
115
+ content=data
116
+ )
117
+
118
  return RedirectResponse(url="/messages", status_code=303)
119
 
120
+ @router.get("/download/{file_id}")
121
+ def download_file(file_id: int):
122
+ file = storage.get_attachment_by_id(file_id)
123
+ if not file:
124
+ raise HTTPException(status_code=404, detail="Файл не найден")
125
+
126
+ return StreamingResponse(
127
+ iter([file["binary"]]),
128
+ media_type=file["mime_type"],
129
+ headers={
130
+ "Content-Disposition": f'attachment; filename="{file["filename"]}"'
131
+ }
132
+ )
133
+
134
  @router.get("/login")
135
  def login_page(request: Request):
136
  return templates.TemplateResponse("login.html", {"request": request})
hf_repo/hf_repo/hf_repo/agents/tools/storage.py CHANGED
@@ -5,6 +5,7 @@ import sqlite3
5
  import os
6
  import json
7
  import uuid
 
8
 
9
  from datetime import datetime, timedelta, UTC
10
  from werkzeug.security import generate_password_hash, check_password_hash
@@ -714,13 +715,40 @@ class Storage:
714
  """, (content, user_did, source, timestamp, hidden))
715
  self.conn.commit()
716
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
717
  def get_notes(self, limit=50, user_did="anon", only_personal=False):
718
  cursor = self.conn.cursor()
719
 
720
  if only_personal:
721
  # Только личные (скрытые) сообщения пользователя
722
  query = """
723
- SELECT n.id, n.text, n.source, n.user_did, u.username, n.timestamp, n.hidden
724
  FROM notes n
725
  LEFT JOIN users u ON n.user_did = u.did
726
  WHERE n.user_did = ? AND n.hidden = 1
@@ -731,7 +759,7 @@ class Storage:
731
  else:
732
  # Личные сообщения + публичные от user/llm, которые не скрыты
733
  query = """
734
- SELECT n.id, n.text, n.source, n.user_did, u.username, u.badges, n.timestamp, n.hidden
735
  FROM notes n
736
  LEFT JOIN users u ON n.user_did = u.did
737
  WHERE n.user_did = ?
@@ -741,6 +769,19 @@ class Storage:
741
  """
742
  cursor.execute(query, (user_did, limit))
743
 
 
 
 
 
 
 
 
 
 
 
 
 
 
744
  return [dict(row) for row in cursor.fetchall()]
745
 
746
  # Пользователи
 
5
  import os
6
  import json
7
  import uuid
8
+ import time
9
 
10
  from datetime import datetime, timedelta, UTC
11
  from werkzeug.security import generate_password_hash, check_password_hash
 
715
  """, (content, user_did, source, timestamp, hidden))
716
  self.conn.commit()
717
 
718
+ def write_note_returning_id(self, content, user_did, source="user", hidden=False, code=None):
719
+ cursor = self.conn.cursor()
720
+ cursor.execute("""
721
+ INSERT INTO notes (timestamp, text, user_did, source, hidden, code)
722
+ VALUES (?, ?, ?, ?, ?, ?)
723
+ """, (time.time(), content, user_did, source, int(hidden), code))
724
+ self.conn.commit()
725
+ return cursor.lastrowid
726
+
727
+ def save_attachment(self, message_id, filename, mime_type, content):
728
+ cursor = self.conn.cursor()
729
+ cursor.execute("""
730
+ INSERT INTO attachments (message_id, filename, mime_type, size, binary)
731
+ VALUES (?, ?, ?, ?, ?)
732
+ """, (message_id, filename, mime_type, len(content), content))
733
+ self.conn.commit()
734
+
735
+ def get_attachment_by_id(self, file_id):
736
+ cursor = self.conn.cursor()
737
+ cursor.execute("""
738
+ SELECT id, filename, mime_type, size, binary
739
+ FROM attachments
740
+ WHERE id = ?
741
+ """, (file_id,))
742
+ row = cursor.fetchone()
743
+ return dict(row) if row else None
744
+
745
  def get_notes(self, limit=50, user_did="anon", only_personal=False):
746
  cursor = self.conn.cursor()
747
 
748
  if only_personal:
749
  # Только личные (скрытые) сообщения пользователя
750
  query = """
751
+ SELECT n.id, n.text, n.code, n.source, n.user_did, u.username, n.timestamp, n.hidden
752
  FROM notes n
753
  LEFT JOIN users u ON n.user_did = u.did
754
  WHERE n.user_did = ? AND n.hidden = 1
 
759
  else:
760
  # Личные сообщения + публичные от user/llm, которые не скрыты
761
  query = """
762
+ SELECT n.id, n.text, n.code, n.source, n.user_did, u.username, u.badges, n.timestamp, n.hidden
763
  FROM notes n
764
  LEFT JOIN users u ON n.user_did = u.did
765
  WHERE n.user_did = ?
 
769
  """
770
  cursor.execute(query, (user_did, limit))
771
 
772
+ result = [dict(row) for row in cursor.fetchall()]
773
+
774
+ for note in result:
775
+ note["attachments"] = self.get_attachments_for_note(note["id"])
776
+
777
+ return result
778
+
779
+ def get_attachments_for_note(self, message_id):
780
+ cursor = self.conn.cursor()
781
+ cursor.execute("""
782
+ SELECT id, filename, mime_type, size FROM attachments
783
+ WHERE message_id = ?
784
+ """, (message_id,))
785
  return [dict(row) for row in cursor.fetchall()]
786
 
787
  # Пользователи
hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/db_structure.sql CHANGED
@@ -42,17 +42,29 @@ CREATE TABLE IF NOT EXISTS diary_graph_index (
42
 
43
  -- Заметки, подсказки, сообщения пользователя и LLM
44
  CREATE TABLE IF NOT EXISTS notes (
45
- id INTEGER PRIMARY KEY AUTOINCREMENT, -- Уникальный идентификатор заметки
46
- text TEXT NOT NULL, -- Текст заметки
47
- tags TEXT, -- Теги (например: "idea", "instruction")
48
- user_did TEXT DEFAULT 'ALL', -- DID пользователя (или 'ALL' для всех)
49
- source TEXT DEFAULT 'user', -- Источник заметки: user | cli | llm | system
50
- links TEXT DEFAULT '', -- Ссылки или связи с другими объектами
51
- read INTEGER DEFAULT 0, -- Статус прочтения LLM: 0 = нет, 1 = да
52
- hidden INTEGER DEFAULT 0, -- Скрыта ли от пользователя: 0 = нет, 1 = да
53
- priority INTEGER DEFAULT 0, -- Приоритет заметки
 
54
  timestamp TEXT DEFAULT CURRENT_TIMESTAMP, -- Время создания
55
- llm_id TEXT -- Идентификатор LLM
 
 
 
 
 
 
 
 
 
 
 
56
  );
57
 
58
  -- Лог процессов: задачи, ошибки, события
 
42
 
43
  -- Заметки, подсказки, сообщения пользователя и LLM
44
  CREATE TABLE IF NOT EXISTS notes (
45
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
46
+ text TEXT NOT NULL, -- Основной текст заметки/сообщения
47
+ code TEXT, -- Прикреплённый код (Python, JS и т.п.)
48
+ tags TEXT, -- Теги (устанавливаются агентом, например: "idea", "instruction")
49
+ user_did TEXT DEFAULT 'ALL', -- Идентификатор пользователя (или 'ALL')
50
+ source TEXT DEFAULT 'user', -- Источник: user | cli | llm | system
51
+ links TEXT DEFAULT '', -- Ссылки на другие объекты (например, JSON со связями)
52
+ read INTEGER DEFAULT 0, -- Агент прочитал: 0 = нет, 1 = да
53
+ hidden INTEGER DEFAULT 0, -- Скрыто от UI (например, технические записи)
54
+ priority INTEGER DEFAULT 0, -- Приоритет обработки (>0: срочность/важность, задается вручную или агентом)
55
  timestamp TEXT DEFAULT CURRENT_TIMESTAMP, -- Время создания
56
+ llm_id TEXT -- Идентификатор агента, добавившего сообщение
57
+ );
58
+
59
+ -- Вложения (может быть несколько к одной заметке)
60
+ CREATE TABLE IF NOT EXISTS attachments (
61
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
62
+ message_id INTEGER NOT NULL, -- Связь с notes.id
63
+ filename TEXT, -- Имя файла
64
+ mime_type TEXT, -- Тип (например, image/png, application/zip)
65
+ size INTEGER, -- Размер файла
66
+ binary BLOB NOT NULL, -- Сами данные
67
+ FOREIGN KEY (message_id) REFERENCES notes(id) ON DELETE CASCADE
68
  );
69
 
70
  -- Лог процессов: задачи, ошибки, события
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/add_message.py CHANGED
@@ -5,11 +5,12 @@ from tools.storage import Storage
5
 
6
  storage = Storage()
7
 
8
- def add_message(content, source="cli", user_did="anon"):
9
  storage.write_note(
10
  content,
11
  source=source,
12
- user_did=user_did
 
13
  )
14
  print(f"[+] Сообщение от {source} ({user_did}) добавлено: {content}")
15
 
@@ -19,6 +20,7 @@ if __name__ == "__main__":
19
  parser.add_argument("--content", required=True)
20
  parser.add_argument("--source", default="cli")
21
  parser.add_argument("--user_did", default="anon")
 
22
  args = parser.parse_args()
23
 
24
- add_message(args.content, args.source, args.user_did)
 
5
 
6
  storage = Storage()
7
 
8
+ def add_message(content, source="cli", user_did="anon", hidden=1):
9
  storage.write_note(
10
  content,
11
  source=source,
12
+ user_did=user_did,
13
+ hidden=hidden
14
  )
15
  print(f"[+] Сообщение от {source} ({user_did}) добавлено: {content}")
16
 
 
20
  parser.add_argument("--content", required=True)
21
  parser.add_argument("--source", default="cli")
22
  parser.add_argument("--user_did", default="anon")
23
+ parser.add_argument("--hidden", default=1)
24
  args = parser.parse_args()
25
 
26
+ add_message(args.content, args.source, args.user_did, args.hidden)
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py CHANGED
@@ -735,7 +735,7 @@ class Storage:
735
  FROM notes n
736
  LEFT JOIN users u ON n.user_did = u.did
737
  WHERE n.user_did = ?
738
- OR ((n.source = 'user' OR n.source = 'llm') AND n.hidden = 0)
739
  ORDER BY n.timestamp DESC
740
  LIMIT ?
741
  """
 
735
  FROM notes n
736
  LEFT JOIN users u ON n.user_did = u.did
737
  WHERE n.user_did = ?
738
+ OR ((n.source = 'user' OR n.source = 'llm' OR n.source = 'cli') AND n.hidden = 0)
739
  ORDER BY n.timestamp DESC
740
  LIMIT ?
741
  """
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py CHANGED
@@ -1,5 +1,6 @@
1
  # agents/notebook/views.py
2
 
 
3
  import bleach
4
 
5
  from fastapi import APIRouter, Request, Form
@@ -12,13 +13,20 @@ router = APIRouter()
12
  templates = Jinja2Templates(directory="notebook/templates")
13
  storage = Storage()
14
 
15
- allowed_tags = ['b', 'i', 's', 'u', 'a', 'ol', 'ul', 'li', 'dl', 'dt', 'dd', 'table', 'caption', 'tr', 'th', 'td']
16
  allowed_attributes = {
17
  'a': ['href', 'title']
18
  }
19
 
20
- def sanitize_html(text):
21
- return bleach.clean(text, tags=allowed_tags, attributes=allowed_attributes, strip=True)
 
 
 
 
 
 
 
22
 
23
  @router.get("/chat")
24
  def chat_page(request: Request):
 
1
  # agents/notebook/views.py
2
 
3
+ import re
4
  import bleach
5
 
6
  from fastapi import APIRouter, Request, Form
 
13
  templates = Jinja2Templates(directory="notebook/templates")
14
  storage = Storage()
15
 
16
+ allowed_tags = ['b', 'i', 's', 'u', 'a', 'ol', 'ul', 'li', 'dl', 'dt', 'dd', 'table', 'caption', 'tr', 'th', 'td', 'code', 'pre', 'blockquote', 'br', 'hr']
17
  allowed_attributes = {
18
  'a': ['href', 'title']
19
  }
20
 
21
+ # Очистка сообщений
22
+ def sanitize_html(text: str) -> str:
23
+ # 1. Сначала очищаем HTML
24
+ cleaned = bleach.clean(text, tags=allowed_tags, attributes=allowed_attributes, strip=True)
25
+
26
+ # 2. Заменяем 3 и более <br> подряд на ровно два
27
+ cleaned = re.sub(r'(<br\s*/?>\s*){3,}', '<br><br>', cleaned, flags=re.IGNORECASE)
28
+
29
+ return cleaned
30
 
31
  @router.get("/chat")
32
  def chat_page(request: Request):
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/__init__.py CHANGED
@@ -1 +1 @@
1
- from agents.tools.storage import Storage
 
1
+ from tools.storage import Storage
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html CHANGED
@@ -25,8 +25,8 @@
25
 
26
  <form method="post">
27
  <textarea name="text" rows="3" cols="40" placeholder="Введите сообщение..."></textarea><br>
28
- <button name="hidden" value="false" type="submit">Отправить</button>
29
- <button name="hidden" value="true" type="submit">Отправить приватно</button>
30
  </form>
31
 
32
  <hr>
 
25
 
26
  <form method="post">
27
  <textarea name="text" rows="3" cols="40" placeholder="Введите сообщение..."></textarea><br>
28
+ <button name="hidden" value="false" type="submit">📢 Отправить</button>
29
+ <button name="hidden" value="true" type="submit">🙋 Отправить приватно</button>
30
  </form>
31
 
32
  <hr>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py CHANGED
@@ -12,7 +12,7 @@ router = APIRouter()
12
  templates = Jinja2Templates(directory="notebook/templates")
13
  storage = Storage()
14
 
15
- allowed_tags = ['b', 'i', 'a', 'ol', 'ul', 'li', 'dl', 'dt', 'dd', 'table', 'caption', 'tr', 'th', 'td']
16
  allowed_attributes = {
17
  'a': ['href', 'title']
18
  }
 
12
  templates = Jinja2Templates(directory="notebook/templates")
13
  storage = Storage()
14
 
15
+ allowed_tags = ['b', 'i', 's', 'u', 'a', 'ol', 'ul', 'li', 'dl', 'dt', 'dd', 'table', 'caption', 'tr', 'th', 'td']
16
  allowed_attributes = {
17
  'a': ['href', 'title']
18
  }
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py CHANGED
@@ -1,5 +1,7 @@
1
  # agents/notebook/views.py
2
 
 
 
3
  from fastapi import APIRouter, Request, Form
4
  from fastapi.responses import RedirectResponse, HTMLResponse
5
  from fastapi.templating import Jinja2Templates
@@ -10,6 +12,14 @@ router = APIRouter()
10
  templates = Jinja2Templates(directory="notebook/templates")
11
  storage = Storage()
12
 
 
 
 
 
 
 
 
 
13
  @router.get("/chat")
14
  def chat_page(request: Request):
15
  did = request.session.get("did")
@@ -72,7 +82,7 @@ def post_message(
72
 
73
  if text.strip():
74
  storage.write_note(
75
- content=text.strip(),
76
  user_did=did,
77
  source="user",
78
  hidden=is_hidden
 
1
  # agents/notebook/views.py
2
 
3
+ import bleach
4
+
5
  from fastapi import APIRouter, Request, Form
6
  from fastapi.responses import RedirectResponse, HTMLResponse
7
  from fastapi.templating import Jinja2Templates
 
12
  templates = Jinja2Templates(directory="notebook/templates")
13
  storage = Storage()
14
 
15
+ allowed_tags = ['b', 'i', 'a', 'ol', 'ul', 'li', 'dl', 'dt', 'dd', 'table', 'caption', 'tr', 'th', 'td']
16
+ allowed_attributes = {
17
+ 'a': ['href', 'title']
18
+ }
19
+
20
+ def sanitize_html(text):
21
+ return bleach.clean(text, tags=allowed_tags, attributes=allowed_attributes, strip=True)
22
+
23
  @router.get("/chat")
24
  def chat_page(request: Request):
25
  did = request.session.get("did")
 
82
 
83
  if text.strip():
84
  storage.write_note(
85
+ content=sanitize_html(text.strip()),
86
  user_did=did,
87
  source="user",
88
  hidden=is_hidden
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/requirements.txt CHANGED
@@ -12,4 +12,5 @@ jinja2
12
  python-multipart
13
  passlib[bcrypt]
14
  werkzeug
15
- itsdangerous
 
 
12
  python-multipart
13
  passlib[bcrypt]
14
  werkzeug
15
+ itsdangerous
16
+ bleach
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html CHANGED
@@ -46,10 +46,10 @@
46
  Источник: <i>{{ msg.source }}</i> — {{ msg.timestamp[:19].replace('T', ' ') }}
47
  </div>
48
  <div>
49
- {{ msg.badges }}Пользователь: {% if msg.username %}<b>{{ msg.username }}</b>{% endif %} {% if msg.user_did !="" %}({{ msg.user_did }}){% endif %}
50
  </div>
51
  <hr>
52
- <div>{{ msg.text }}</div>
53
  </div>
54
  {% endfor %}
55
  </body>
 
46
  Источник: <i>{{ msg.source }}</i> — {{ msg.timestamp[:19].replace('T', ' ') }}
47
  </div>
48
  <div>
49
+ {% if msg.badges %}{{ msg.badges }}{% endif %}Пользователь: {% if msg.username %}<b>{{ msg.username }}</b>{% endif %} {% if msg.user_did %}({{ msg.user_did }}){% endif %}
50
  </div>
51
  <hr>
52
+ <div>{{ msg.text|safe }}</div>
53
  </div>
54
  {% endfor %}
55
  </body>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py CHANGED
@@ -731,7 +731,7 @@ class Storage:
731
  else:
732
  # Личные сообщения + публичные от user/llm, которые не скрыты
733
  query = """
734
- SELECT n.id, n.text, n.source, n.user_did, u.username, n.timestamp, n.hidden
735
  FROM notes n
736
  LEFT JOIN users u ON n.user_did = u.did
737
  WHERE n.user_did = ?
 
731
  else:
732
  # Личные сообщения + публичные от user/llm, которые не скрыты
733
  query = """
734
+ SELECT n.id, n.text, n.source, n.user_did, u.username, u.badges, n.timestamp, n.hidden
735
  FROM notes n
736
  LEFT JOIN users u ON n.user_did = u.did
737
  WHERE n.user_did = ?
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html CHANGED
@@ -46,7 +46,7 @@
46
  Источник: <i>{{ msg.source }}</i> — {{ msg.timestamp[:19].replace('T', ' ') }}
47
  </div>
48
  <div>
49
- Пользователь: {% if msg.username %}<b>{{ msg.username }}</b>{% endif %} {% if msg.user_did !="" %}({{ msg.user_did }}){% endif %}
50
  </div>
51
  <hr>
52
  <div>{{ msg.text }}</div>
 
46
  Источник: <i>{{ msg.source }}</i> — {{ msg.timestamp[:19].replace('T', ' ') }}
47
  </div>
48
  <div>
49
+ {{ msg.badges }}Пользователь: {% if msg.username %}<b>{{ msg.username }}</b>{% endif %} {% if msg.user_did !="" %}({{ msg.user_did }}){% endif %}
50
  </div>
51
  <hr>
52
  <div>{{ msg.text }}</div>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/db_structure.sql CHANGED
@@ -199,9 +199,10 @@ CREATE TABLE IF NOT EXISTS users (
199
  user_id INTEGER PRIMARY KEY AUTOINCREMENT,
200
  ban DATETIME DEFAULT NULL, -- если стоит дата/время, то пользователь забанен до этого момента
201
  username TEXT, -- имя пользователя (необязательно уникальное)
202
- did TEXT UNIQUE, -- децентрализованный идентификатор
203
- mail TEXT UNIQUE, -- электронная почта
204
- password_hash TEXT, -- хэш пароля
 
205
  info TEXT, -- произвольная информация, JSON
206
  profile TEXT, -- структурированая информация, JSON
207
  contacts TEXT, -- JSON-массив альтернативных контактов (matrix, telegram и т.д.)
 
199
  user_id INTEGER PRIMARY KEY AUTOINCREMENT,
200
  ban DATETIME DEFAULT NULL, -- если стоит дата/время, то пользователь забанен до этого момента
201
  username TEXT, -- имя пользователя (необязательно уникальное)
202
+ badges TEXT, -- значки, присвоенные агентом (например, "🎓💬")
203
+ did TEXT UNIQUE NOT NULL, -- децентрализованный идентификатор
204
+ mail TEXT UNIQUE NOT NULL, -- электронная почта
205
+ password_hash TEXT NOT NULL, -- хэш пароля
206
  info TEXT, -- произвольная информация, JSON
207
  profile TEXT, -- структурированая информация, JSON
208
  contacts TEXT, -- JSON-массив альтернативных контактов (matrix, telegram и т.д.)
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py CHANGED
@@ -1,7 +1,7 @@
1
  # agents/notebook/views.py
2
 
3
  from fastapi import APIRouter, Request, Form
4
- from fastapi.responses import RedirectResponse
5
  from fastapi.templating import Jinja2Templates
6
  from starlette.status import HTTP_303_SEE_OTHER
7
  from tools.storage import Storage
@@ -66,6 +66,10 @@ def post_message(
66
  did = request.session.get("did", "anon")
67
  is_hidden = 1 if hidden.lower() == "true" else 0
68
 
 
 
 
 
69
  if text.strip():
70
  storage.write_note(
71
  content=text.strip(),
 
1
  # agents/notebook/views.py
2
 
3
  from fastapi import APIRouter, Request, Form
4
+ from fastapi.responses import RedirectResponse, HTMLResponse
5
  from fastapi.templating import Jinja2Templates
6
  from starlette.status import HTTP_303_SEE_OTHER
7
  from tools.storage import Storage
 
66
  did = request.session.get("did", "anon")
67
  is_hidden = 1 if hidden.lower() == "true" else 0
68
 
69
+ # Проверка на бан
70
+ if storage.is_banned(did):
71
+ return HTMLResponse(content="Вы забанены и не можете отправлять сообщения.", status_code=403)
72
+
73
  if text.strip():
74
  storage.write_note(
75
  content=text.strip(),
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py CHANGED
@@ -769,6 +769,18 @@ class Storage:
769
  return check_password_hash(result["password_hash"], password)
770
  return False
771
 
 
 
 
 
 
 
 
 
 
 
 
 
772
  def get_user_info(self, mail: str) -> dict | None:
773
  mail = mail.lower()
774
  cursor = self.conn.cursor()
 
769
  return check_password_hash(result["password_hash"], password)
770
  return False
771
 
772
+ def is_banned(self, user_did):
773
+ result = self.conn.execute("""
774
+ SELECT ban
775
+ FROM users
776
+ WHERE did = ?
777
+ """, (user_did,)).fetchone()
778
+
779
+ if result and result["ban"]:
780
+ return datetime.fromisoformat(result["ban"]) > datetime.now(UTC)
781
+
782
+ return False
783
+
784
  def get_user_info(self, mail: str) -> dict | None:
785
  mail = mail.lower()
786
  cursor = self.conn.cursor()
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html CHANGED
@@ -33,7 +33,7 @@
33
 
34
  <div>
35
  <a href="/messages">📢 Все сообщения</a> |
36
- <a href="/messages?only_personal=true">🙋 Только мои</a>
37
  </div>
38
 
39
  <hr>
@@ -41,8 +41,8 @@
41
  {% for msg in messages %}
42
  <div class="message source-{{ msg.source }}">
43
  <div>
44
- {% if msg.user_did == request.session['did'] %}<span class="from-self"></span>{% endif %}
45
- {% if msg.hidden %}<span class="private"></span>{% endif %}
46
  Источник: <i>{{ msg.source }}</i> — {{ msg.timestamp[:19].replace('T', ' ') }}
47
  </div>
48
  <div>
 
33
 
34
  <div>
35
  <a href="/messages">📢 Все сообщения</a> |
36
+ <a href="/messages?only_personal=true">🙋 Личные</a>
37
  </div>
38
 
39
  <hr>
 
41
  {% for msg in messages %}
42
  <div class="message source-{{ msg.source }}">
43
  <div>
44
+ {% if msg.user_did == request.session['did'] %}<span class="from-self" title="это ваше сообщение"></span>{% endif %}
45
+ {% if msg.hidden %}<span class="private" title="личное сообщение"></span>{% endif %}
46
  Источник: <i>{{ msg.source }}</i> — {{ msg.timestamp[:19].replace('T', ' ') }}
47
  </div>
48
  <div>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html CHANGED
@@ -39,13 +39,14 @@
39
  <hr>
40
 
41
  {% for msg in messages %}
42
- <div class="message source-{{ msg.source }} {% if msg.user_did == request.session['did'] %}from-self{% endif %}">
43
  <div>
44
- Источник: <i>{{ msg.source }}</i>{{ msg.timestamp }}
45
  {% if msg.hidden %}<span class="private"></span>{% endif %}
 
46
  </div>
47
  <div>
48
- Пользователь: {% if msg.username %}<b>{{ msg.username }}</b>{% endif %} ({{ msg.user_did }})
49
  </div>
50
  <hr>
51
  <div>{{ msg.text }}</div>
 
39
  <hr>
40
 
41
  {% for msg in messages %}
42
+ <div class="message source-{{ msg.source }}">
43
  <div>
44
+ {% if msg.user_did == request.session['did'] %}<span class="from-self"></span>{% endif %}
45
  {% if msg.hidden %}<span class="private"></span>{% endif %}
46
+ Источник: <i>{{ msg.source }}</i> — {{ msg.timestamp[:19].replace('T', ' ') }}
47
  </div>
48
  <div>
49
+ Пользователь: {% if msg.username %}<b>{{ msg.username }}</b>{% endif %} {% if msg.user_did !="" %}({{ msg.user_did }}){% endif %}
50
  </div>
51
  <hr>
52
  <div>{{ msg.text }}</div>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html CHANGED
@@ -41,11 +41,11 @@
41
  {% for msg in messages %}
42
  <div class="message source-{{ msg.source }} {% if msg.user_did == request.session['did'] %}from-self{% endif %}">
43
  <div>
44
- <strong>{{ msg.source }}</strong> — {{ msg.timestamp }}
45
  {% if msg.hidden %}<span class="private"></span>{% endif %}
46
  </div>
47
  <div>
48
- участник: {% if msg.username %}<b>{{ msg.username }}</b>{% endif %} ({{ msg.user_did[:10] }})...
49
  </div>
50
  <hr>
51
  <div>{{ msg.text }}</div>
 
41
  {% for msg in messages %}
42
  <div class="message source-{{ msg.source }} {% if msg.user_did == request.session['did'] %}from-self{% endif %}">
43
  <div>
44
+ Источник: <i>{{ msg.source }}</i> — {{ msg.timestamp }}
45
  {% if msg.hidden %}<span class="private"></span>{% endif %}
46
  </div>
47
  <div>
48
+ Пользователь: {% if msg.username %}<b>{{ msg.username }}</b>{% endif %} ({{ msg.user_did }})
49
  </div>
50
  <hr>
51
  <div>{{ msg.text }}</div>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html CHANGED
@@ -45,9 +45,9 @@
45
  {% if msg.hidden %}<span class="private"></span>{% endif %}
46
  </div>
47
  <div>
48
- участник: {{ msg.user_did[:10] }}...
49
- {% if msg.username %}({{ msg.username }}){% endif %}
50
  </div>
 
51
  <div>{{ msg.text }}</div>
52
  </div>
53
  {% endfor %}
 
45
  {% if msg.hidden %}<span class="private"></span>{% endif %}
46
  </div>
47
  <div>
48
+ участник: {% if msg.username %}<b>{{ msg.username }}</b>{% endif %} ({{ msg.user_did[:10] }})...
 
49
  </div>
50
+ <hr>
51
  <div>{{ msg.text }}</div>
52
  </div>
53
  {% endfor %}
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html CHANGED
@@ -45,7 +45,7 @@
45
  {% if msg.hidden %}<span class="private"></span>{% endif %}
46
  </div>
47
  <div>
48
- от {{ msg.user_did[:10] }}...
49
  {% if msg.username %}({{ msg.username }}){% endif %}
50
  </div>
51
  <div>{{ msg.text }}</div>
 
45
  {% if msg.hidden %}<span class="private"></span>{% endif %}
46
  </div>
47
  <div>
48
+ участник: {{ msg.user_did[:10] }}...
49
  {% if msg.username %}({{ msg.username }}){% endif %}
50
  </div>
51
  <div>{{ msg.text }}</div>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py CHANGED
@@ -45,14 +45,9 @@ def show_messages(request: Request, only_personal: bool = False):
45
  if not did:
46
  return RedirectResponse("/login", status_code=303)
47
 
48
- # Получаем инфу о пользователе, чтобы понять, оператор ли он
49
- user_info = storage.get_user_info_by_did(did)
50
- is_operator = bool(user_info and user_info.get("operator"))
51
-
52
  messages = storage.get_notes(
53
  limit=50,
54
  user_did=did,
55
- is_operator=is_operator,
56
  only_personal=only_personal
57
  )
58
  return templates.TemplateResponse("messages.html", {
 
45
  if not did:
46
  return RedirectResponse("/login", status_code=303)
47
 
 
 
 
 
48
  messages = storage.get_notes(
49
  limit=50,
50
  user_did=did,
 
51
  only_personal=only_personal
52
  )
53
  return templates.TemplateResponse("messages.html", {
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py CHANGED
@@ -714,38 +714,32 @@ class Storage:
714
  """, (content, user_did, source, timestamp, hidden))
715
  self.conn.commit()
716
 
717
- def get_notes(self, limit=50, user_did="anon", is_operator=False, only_personal=False):
718
  cursor = self.conn.cursor()
719
 
720
- if is_operator:
 
721
  query = """
722
- SELECT id, text, source, user_did, timestamp, hidden
723
- FROM notes
724
- ORDER BY timestamp DESC
 
 
725
  LIMIT ?
726
  """
727
- cursor.execute(query, (limit,))
728
  else:
729
- if only_personal:
730
- # Только личные сообщения
731
- query = """
732
- SELECT id, text, source, user_did, timestamp, hidden
733
- FROM notes
734
- WHERE user_did = ? AND hidden = 0
735
- ORDER BY timestamp DESC
736
- LIMIT ?
737
- """
738
- cursor.execute(query, (user_did, limit))
739
- else:
740
- # Публичные и личные
741
- query = """
742
- SELECT id, text, source, user_did, timestamp, hidden
743
- FROM notes
744
- WHERE (user_did = ? OR user_did = 'ALL') AND hidden = 0
745
- ORDER BY timestamp DESC
746
- LIMIT ?
747
- """
748
- cursor.execute(query, (user_did, limit))
749
 
750
  return [dict(row) for row in cursor.fetchall()]
751
 
@@ -790,6 +784,21 @@ class Storage:
790
  }
791
  return None
792
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
793
  # Утилиты
794
 
795
  def close(self):
 
714
  """, (content, user_did, source, timestamp, hidden))
715
  self.conn.commit()
716
 
717
+ def get_notes(self, limit=50, user_did="anon", only_personal=False):
718
  cursor = self.conn.cursor()
719
 
720
+ if only_personal:
721
+ # Только личные (скрытые) сообщения пользователя
722
  query = """
723
+ SELECT n.id, n.text, n.source, n.user_did, u.username, n.timestamp, n.hidden
724
+ FROM notes n
725
+ LEFT JOIN users u ON n.user_did = u.did
726
+ WHERE n.user_did = ? AND n.hidden = 1
727
+ ORDER BY n.timestamp DESC
728
  LIMIT ?
729
  """
730
+ cursor.execute(query, (user_did, limit))
731
  else:
732
+ # Личные сообщения + публичные от user/llm, которые не скрыты
733
+ query = """
734
+ SELECT n.id, n.text, n.source, n.user_did, u.username, n.timestamp, n.hidden
735
+ FROM notes n
736
+ LEFT JOIN users u ON n.user_did = u.did
737
+ WHERE n.user_did = ?
738
+ OR ((n.source = 'user' OR n.source = 'llm') AND n.hidden = 0)
739
+ ORDER BY n.timestamp DESC
740
+ LIMIT ?
741
+ """
742
+ cursor.execute(query, (user_did, limit))
 
 
 
 
 
 
 
 
 
743
 
744
  return [dict(row) for row in cursor.fetchall()]
745
 
 
784
  }
785
  return None
786
 
787
+ def get_user_info_by_did(self, did: str) -> dict | None:
788
+ cursor = self.conn.cursor()
789
+ cursor.execute(
790
+ "SELECT username, mail, operator FROM users WHERE did = ?",
791
+ (did,)
792
+ )
793
+ result = cursor.fetchone()
794
+ if result:
795
+ return {
796
+ "username": result["username"],
797
+ "mail": result["mail"],
798
+ "operator": result["operator"]
799
+ }
800
+ return None
801
+
802
  # Утилиты
803
 
804
  def close(self):
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py CHANGED
@@ -1,3 +1,5 @@
 
 
1
  from fastapi import APIRouter, Request, Form
2
  from fastapi.responses import RedirectResponse
3
  from fastapi.templating import Jinja2Templates
@@ -43,7 +45,10 @@ def show_messages(request: Request, only_personal: bool = False):
43
  if not did:
44
  return RedirectResponse("/login", status_code=303)
45
 
46
- is_operator = False # Пока не реализовано
 
 
 
47
  messages = storage.get_notes(
48
  limit=50,
49
  user_did=did,
@@ -61,17 +66,18 @@ def show_messages(request: Request, only_personal: bool = False):
61
  def post_message(
62
  request: Request,
63
  text: str = Form(...),
64
- hidden: str = Form(default=None)
65
  ):
66
  did = request.session.get("did", "anon")
67
- is_hidden = 1 if hidden else 0
68
 
69
- storage.write_note(
70
- content=text,
71
- user_did=did,
72
- source="user",
73
- hidden=is_hidden
74
- )
 
75
  return RedirectResponse(url="/messages", status_code=303)
76
 
77
  @router.get("/login")
 
1
+ # agents/notebook/views.py
2
+
3
  from fastapi import APIRouter, Request, Form
4
  from fastapi.responses import RedirectResponse
5
  from fastapi.templating import Jinja2Templates
 
45
  if not did:
46
  return RedirectResponse("/login", status_code=303)
47
 
48
+ # Получаем инфу о пользователе, чтобы понять, оператор ли он
49
+ user_info = storage.get_user_info_by_did(did)
50
+ is_operator = bool(user_info and user_info.get("operator"))
51
+
52
  messages = storage.get_notes(
53
  limit=50,
54
  user_did=did,
 
66
  def post_message(
67
  request: Request,
68
  text: str = Form(...),
69
+ hidden: str = Form(default="false")
70
  ):
71
  did = request.session.get("did", "anon")
72
+ is_hidden = 1 if hidden.lower() == "true" else 0
73
 
74
+ if text.strip():
75
+ storage.write_note(
76
+ content=text.strip(),
77
+ user_did=did,
78
+ source="user",
79
+ hidden=is_hidden
80
+ )
81
  return RedirectResponse(url="/messages", status_code=303)
82
 
83
  @router.get("/login")
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/web_ui.py CHANGED
@@ -12,7 +12,6 @@ process_name = os.path.splitext(os.path.basename(__file__))[0]
12
  from fastapi import FastAPI
13
  from fastapi.staticfiles import StaticFiles
14
  from fastapi.templating import Jinja2Templates
15
- #from agents.notebook.auth import router as auth_router
16
  from starlette.middleware.sessions import SessionMiddleware
17
  from agents.notebook.views import router as notebook_router
18
  from tools.storage import Storage
@@ -25,7 +24,6 @@ app.add_middleware(SessionMiddleware, secret_key="очень_секретный_
25
  app.mount("/static", StaticFiles(directory=os.path.join(os.path.dirname(__file__), "notebook/static")), name="static")
26
  templates = Jinja2Templates(directory=os.path.join(os.path.dirname(__file__), "notebook/templates"))
27
 
28
- #app.include_router(auth_router)
29
  app.include_router(notebook_router)
30
 
31
  @app.on_event("startup")
 
12
  from fastapi import FastAPI
13
  from fastapi.staticfiles import StaticFiles
14
  from fastapi.templating import Jinja2Templates
 
15
  from starlette.middleware.sessions import SessionMiddleware
16
  from agents.notebook.views import router as notebook_router
17
  from tools.storage import Storage
 
24
  app.mount("/static", StaticFiles(directory=os.path.join(os.path.dirname(__file__), "notebook/static")), name="static")
25
  templates = Jinja2Templates(directory=os.path.join(os.path.dirname(__file__), "notebook/templates"))
26
 
 
27
  app.include_router(notebook_router)
28
 
29
  @app.on_event("startup")
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/login.html CHANGED
@@ -1,6 +1,14 @@
1
- <form method="post">
2
- <label>Email: <input type="email" name="mail" required></label><br>
3
- <label>Пароль: <input type="password" name="password" required></label><br>
4
- <button type="submit">Войти</button>
5
- </form>
6
- Нет учетки: <a href="/register">зарегистрироваться</a>
 
 
 
 
 
 
 
 
 
1
+ {% if error %}
2
+ <p style="color:red">{{ error }}</p>
3
+ {% endif %}
4
+
5
+ <table>
6
+ <caption>Вход</caption>
7
+ <form method="post">
8
+ <tr><th><label>Email:</th><td><input type="email" name="mail" required></label></td></tr>
9
+ <tr><th><label>Пароль:</th><td><input type="password" name="password" required></label></td></tr>
10
+ <tr><th colspan="2"><button type="submit">Войти</button></th></tr>
11
+ </form>
12
+ </table>
13
+
14
+ <hr>Нет учетки: <a href="/register">Зарегистрироваться</a>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html CHANGED
@@ -25,10 +25,8 @@
25
 
26
  <form method="post">
27
  <textarea name="text" rows="3" cols="40" placeholder="Введите сообщение..."></textarea><br>
28
- <label>
29
- <input type="checkbox" name="hidden"> Скрыто
30
- </label><br>
31
- <button type="submit">Отправить</button>
32
  </form>
33
 
34
  <hr>
 
25
 
26
  <form method="post">
27
  <textarea name="text" rows="3" cols="40" placeholder="Введите сообщение..."></textarea><br>
28
+ <button name="hidden" value="false" type="submit">Отправить</button>
29
+ <button name="hidden" value="true" type="submit">Отправить приватно</button>
 
 
30
  </form>
31
 
32
  <hr>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/register.html CHANGED
@@ -1,7 +1,15 @@
1
- <form method="post">
2
- <label>Имя пользователя: <input type="text" name="username" required></label><br>
3
- <label>Email: <input type="email" name="mail" required></label><br>
4
- <label>Пароль: <input type="password" name="password" required></label><br>
5
- <button type="submit">Зарегистрироваться</button>
6
- </form>
7
- Есть учетка: <a href="/login">войти</a>
 
 
 
 
 
 
 
 
 
1
+ {% if error %}
2
+ <p style="color:red">{{ error }}</p>
3
+ {% endif %}
4
+
5
+ <table>
6
+ <caption>Регистрация</caption>
7
+ <form method="post">
8
+ <tr><th><label>Имя:</th><td><input type="text" name="username" required></label></td></tr>
9
+ <tr><th><label>Email:</th><td><input type="email" name="mail" required></label></td></tr>
10
+ <tr><th><label>Пароль:</th><td><input type="password" name="password" required></label></td></tr>
11
+ <tr><th colspan="2"><button type="submit">Зарегистрироваться</button></th></tr>
12
+ </form>
13
+ </table>
14
+
15
+ <hr>Есть учетка: <a href="/login">Войти</a>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py CHANGED
@@ -7,6 +7,9 @@ import json
7
  import uuid
8
 
9
  from datetime import datetime, timedelta, UTC
 
 
 
10
 
11
  SCRIPTS_BASE_PATH = "scripts"
12
 
@@ -747,21 +750,18 @@ class Storage:
747
  return [dict(row) for row in cursor.fetchall()]
748
 
749
  # Пользователи
750
- def _hash_password(self, password: str) -> str:
751
- return hashlib.sha256(password.encode()).hexdigest()
752
-
753
  def register_user(self, username: str, mail: str, password: str) -> bool:
754
  mail = mail.lower()
755
- did = f"did:example:{uuid.uuid4()}"
756
  try:
757
  self.conn.execute(
758
  "INSERT INTO users (username, mail, password_hash, did) VALUES (?, ?, ?, ?)",
759
- (username, mail, self._hash_password(password), did)
760
  )
761
  self.conn.commit()
762
  return True
763
  except sqlite3.IntegrityError:
764
- return False # уже существует (уникальность mail или did)
765
 
766
  def authenticate_user(self, mail: str, password: str) -> bool:
767
  mail = mail.lower()
@@ -772,7 +772,7 @@ class Storage:
772
  )
773
  result = cursor.fetchone()
774
  if result:
775
- return result["password_hash"] == self._hash_password(password)
776
  return False
777
 
778
  def get_user_info(self, mail: str) -> dict | None:
 
7
  import uuid
8
 
9
  from datetime import datetime, timedelta, UTC
10
+ from werkzeug.security import generate_password_hash, check_password_hash
11
+ from tools.identity import generate_did
12
+ from tools.crypto import generate_keypair
13
 
14
  SCRIPTS_BASE_PATH = "scripts"
15
 
 
750
  return [dict(row) for row in cursor.fetchall()]
751
 
752
  # Пользователи
 
 
 
753
  def register_user(self, username: str, mail: str, password: str) -> bool:
754
  mail = mail.lower()
755
+ did = generate_did()
756
  try:
757
  self.conn.execute(
758
  "INSERT INTO users (username, mail, password_hash, did) VALUES (?, ?, ?, ?)",
759
+ (username, mail, generate_password_hash(password), did)
760
  )
761
  self.conn.commit()
762
  return True
763
  except sqlite3.IntegrityError:
764
+ return False
765
 
766
  def authenticate_user(self, mail: str, password: str) -> bool:
767
  mail = mail.lower()
 
772
  )
773
  result = cursor.fetchone()
774
  if result:
775
+ return check_password_hash(result["password_hash"], password)
776
  return False
777
 
778
  def get_user_info(self, mail: str) -> dict | None:
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/config.yml CHANGED
@@ -1,44 +1,69 @@
1
- agent_id: did:hmp:dac57687-1839-423d-8624-c38324de2635
2
- agent_name: CognitiveCore
3
- agent_role: core
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  api_port: 8080
5
- bootstrap_responder: true
6
- debug: true
7
- default_llm: local-model
8
- default_user:
9
10
- password: password
11
- username: user
12
- dht_enabled: true
13
  dht_port: 20784
14
- dht_update: true
15
- enable_llm: true
16
- identity_agent: dac57687-1839-423d-8624-c38324de2635
17
  llm_backends:
18
- - format: gguf
19
- name: local-model
20
- path: /models/gguf/mistral.gguf
21
- prompt_template: mistral
22
- type: local
23
- - api_key: lm-studio-any-key
24
- base_url: http://127.0.0.1:1234/v1
25
- model: mistral
26
- name: lmstudio-local
27
- provider: openai-compatible
28
- type: api
29
- - api_key: sk-...
30
- model: gpt-4o
31
- name: openai-gpt4o
32
- provider: openai
33
- type: api
34
- log_level: INFO
35
- notebook_ui: true
36
- proxy_address: 127.0.0.1:9050
37
- proxy_mode: false
38
- proxy_type: socks5
39
- serve_api: true
40
- ui_hosts:
41
- - 127.0.0.1
42
- - ::1
 
 
 
 
 
 
43
  ui_port: 8765
44
- update_interval: 60
 
 
 
 
 
 
 
 
 
 
1
+ # HMP Agent Configuration
2
+
3
+ # === Общие параметры ===
4
+ agent_id: "" # Оставьте пустым для генерации DiD автоматически
5
+ agent_name: "CognitiveCore" # Имя агента
6
+ agent_role: "core" # 'core' или 'shell'
7
+
8
+ # === Прокси ===
9
+ proxy_mode: false # false — прокси не используется, иначе true
10
+ proxy_type: "socks5" # 'http', 'socks4', 'socks5' и т.д. (если proxy_mode: true)
11
+ proxy_address: "127.0.0.1:9050"
12
+
13
+ # === Функции ядра ===
14
+ enable_llm: true # доступ к LLM
15
+
16
+ serve_api: true # REST API
17
  api_port: 8080
18
+
19
+ # === DHT-сеть и обмен знаниями ===
20
+ dht_enabled: true # Включение участия в DHT-сети (включено принудительно!)
21
+ dht_update: true # Регулярные обновления и публикация данных в DHT (включено принудительно!)
22
+ bootstrap_responder: true # Агент отвечает на bootstrap-запросы (включено принудительно!)
 
 
 
23
  dht_port: 20784
24
+ update_interval: 60 # секунд (для DHT-обновлений)
25
+
26
+ # === LLM-бэкенды ===
27
  llm_backends:
28
+ - name: "local-model"
29
+ type: "local"
30
+ path: "/models/gguf/mistral.gguf"
31
+ format: "gguf"
32
+ prompt_template: "mistral"
33
+
34
+ - name: "lmstudio-local"
35
+ type: "api"
36
+ provider: "openai-compatible"
37
+ model: "mistral" # или то, что LM Studio показывает как модель
38
+ api_key: "lm-studio-any-key" # может быть заглушкой
39
+ base_url: "http://127.0.0.1:1234/v1"
40
+
41
+ - name: "openai-gpt4o"
42
+ type: "api"
43
+ provider: "openai"
44
+ model: "gpt-4o"
45
+ api_key: "sk-..."
46
+
47
+ # Пользователь может добавить сколько угодно дополнительных локальных или сетевых LLM
48
+
49
+ default_llm: "local-model" # если модели нет в списке `llm_backends` используется первая в списке
50
+
51
+ # === Веб-интерфейс ===
52
+ notebook_ui: true # UI в виде блокнота
53
+ # ui_hosts:
54
+ # - "0.0.0.0" # (небезопасно) доступ с любых IPv4-адресов
55
+ # - "::" # (небезопасно) доступ с любых IPv6-адресов
56
+ ui_hosts: # Какие IP прослушиваются, ["0.0.0.0"; "::"] - доступен везде
57
+ - "127.0.0.1"
58
+ - "::1"
59
  ui_port: 8765
60
+
61
+ # === Данные пользователя ===
62
+ default_user:
63
+ username: "user"
64
+ email: "[email protected]"
65
+ password: "password" # пусто при инициализации, будет установлен при регистрации
66
+
67
+ # === Отладка и логгирование ===
68
+ debug: true
69
+ log_level: "INFO" # DEBUG, INFO, WARNING, ERROR
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/login.html CHANGED
@@ -3,3 +3,4 @@
3
  <label>Пароль: <input type="password" name="password" required></label><br>
4
  <button type="submit">Войти</button>
5
  </form>
 
 
3
  <label>Пароль: <input type="password" name="password" required></label><br>
4
  <button type="submit">Войти</button>
5
  </form>
6
+ Нет учетки: <a href="/register">зарегистрироваться</a>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/register.html CHANGED
@@ -4,3 +4,4 @@
4
  <label>Пароль: <input type="password" name="password" required></label><br>
5
  <button type="submit">Зарегистрироваться</button>
6
  </form>
 
 
4
  <label>Пароль: <input type="password" name="password" required></label><br>
5
  <button type="submit">Зарегистрироваться</button>
6
  </form>
7
+ Есть учетка: <a href="/login">войти</a>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/config.yml CHANGED
@@ -1,69 +1,44 @@
1
- # HMP Agent Configuration
2
-
3
- # === Общие параметры ===
4
- agent_id: "" # Оставьте пустым для генерации DiD автоматически
5
- agent_name: "CognitiveCore" # Имя агента
6
- agent_role: "core" # 'core' или 'shell'
7
-
8
- # === Прокси ===
9
- proxy_mode: false # false — прокси не используется, иначе true
10
- proxy_type: "socks5" # 'http', 'socks4', 'socks5' и т.д. (если proxy_mode: true)
11
- proxy_address: "127.0.0.1:9050"
12
-
13
- # === Функции ядра ===
14
- enable_llm: true # доступ к LLM
15
-
16
- serve_api: true # REST API
17
  api_port: 8080
18
-
19
- # === DHT-сеть и обмен знаниями ===
20
- dht_enabled: true # Включение участия в DHT-сети (включено принудительно!)
21
- dht_update: true # Регулярные обновления и публикация данных в DHT (включено принудительно!)
22
- bootstrap_responder: true # Агент отвечает на bootstrap-запросы (включено принудительно!)
 
 
 
23
  dht_port: 20784
24
- update_interval: 60 # секунд (для DHT-обновлений)
25
-
26
- # === LLM-бэкенды ===
27
  llm_backends:
28
- - name: "local-model"
29
- type: "local"
30
- path: "/models/gguf/mistral.gguf"
31
- format: "gguf"
32
- prompt_template: "mistral"
33
-
34
- - name: "lmstudio-local"
35
- type: "api"
36
- provider: "openai-compatible"
37
- model: "mistral" # или то, что LM Studio показывает как модель
38
- api_key: "lm-studio-any-key" # может быть заглушкой
39
- base_url: "http://127.0.0.1:1234/v1"
40
-
41
- - name: "openai-gpt4o"
42
- type: "api"
43
- provider: "openai"
44
- model: "gpt-4o"
45
- api_key: "sk-..."
46
-
47
- # Пользователь может добавить сколько угодно дополнительных локальных или сетевых LLM
48
-
49
- default_llm: "local-model" # если модели нет в списке `llm_backends` используется первая в списке
50
-
51
- # === Веб-интерфейс ===
52
- notebook_ui: true # UI в виде блокнота
53
- # ui_hosts:
54
- # - "0.0.0.0" # (небезопасно) доступ с любых IPv4-адресов
55
- # - "::" # (небезопасно) доступ с любых IPv6-адресов
56
- ui_hosts: # Какие IP прослушиваются, ["0.0.0.0"; "::"] - доступен везде
57
- - "127.0.0.1"
58
- - "::1"
59
  ui_port: 8765
60
-
61
- # === Данные пользователя ===
62
- default_user:
63
- username: "user"
64
- email: "[email protected]"
65
- password: "password" # пусто при инициализации, будет установлен при регистрации
66
-
67
- # === Отладка и логгирование ===
68
- debug: true
69
- log_level: "INFO" # DEBUG, INFO, WARNING, ERROR
 
1
+ agent_id: did:hmp:dac57687-1839-423d-8624-c38324de2635
2
+ agent_name: CognitiveCore
3
+ agent_role: core
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  api_port: 8080
5
+ bootstrap_responder: true
6
+ debug: true
7
+ default_llm: local-model
8
+ default_user:
9
10
+ password: password
11
+ username: user
12
+ dht_enabled: true
13
  dht_port: 20784
14
+ dht_update: true
15
+ enable_llm: true
16
+ identity_agent: dac57687-1839-423d-8624-c38324de2635
17
  llm_backends:
18
+ - format: gguf
19
+ name: local-model
20
+ path: /models/gguf/mistral.gguf
21
+ prompt_template: mistral
22
+ type: local
23
+ - api_key: lm-studio-any-key
24
+ base_url: http://127.0.0.1:1234/v1
25
+ model: mistral
26
+ name: lmstudio-local
27
+ provider: openai-compatible
28
+ type: api
29
+ - api_key: sk-...
30
+ model: gpt-4o
31
+ name: openai-gpt4o
32
+ provider: openai
33
+ type: api
34
+ log_level: INFO
35
+ notebook_ui: true
36
+ proxy_address: 127.0.0.1:9050
37
+ proxy_mode: false
38
+ proxy_type: socks5
39
+ serve_api: true
40
+ ui_hosts:
41
+ - 127.0.0.1
42
+ - ::1
 
 
 
 
 
 
43
  ui_port: 8765
44
+ update_interval: 60
 
 
 
 
 
 
 
 
 
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/login.html CHANGED
@@ -1,14 +1,5 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head><title>Вход</title></head>
4
- <body>
5
- <h2>Вход</h2>
6
- {% if error %}<p style="color: red">{{ error }}</p>{% endif %}
7
- <form method="post">
8
- <input name="username" placeholder="Имя пользователя">
9
- <input name="password" type="password" placeholder="Пароль">
10
- <button type="submit">Войти</button>
11
- </form>
12
- <p>Нет аккаунта? <a href="/register">Регистрация</a></p>
13
- </body>
14
- </html>
 
1
+ <form method="post">
2
+ <label>Email: <input type="email" name="mail" required></label><br>
3
+ <label>Пароль: <input type="password" name="password" required></label><br>
4
+ <button type="submit">Войти</button>
5
+ </form>
 
 
 
 
 
 
 
 
 
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html CHANGED
@@ -20,11 +20,14 @@
20
  <a href="/login">Войти</a> | <a href="/register">Регистрация</a>
21
  {% endif %}
22
  </div>
 
23
  <h1>Сообщения</h1>
24
 
25
  <form method="post">
26
  <textarea name="text" rows="3" cols="40" placeholder="Введите сообщение..."></textarea><br>
27
- <input type="text" name="user_did" placeholder="User DID" readonly value="{{ username }}">
 
 
28
  <button type="submit">Отправить</button>
29
  </form>
30
 
@@ -38,11 +41,15 @@
38
  <hr>
39
 
40
  {% for msg in messages %}
41
- <div class="message source-{{ msg.source }} {% if msg.user_did == 'did:example:local-user' %}from-self{% endif %}">
42
  <div>
43
  <strong>{{ msg.source }}</strong> — {{ msg.timestamp }}
44
  {% if msg.hidden %}<span class="private"></span>{% endif %}
45
  </div>
 
 
 
 
46
  <div>{{ msg.text }}</div>
47
  </div>
48
  {% endfor %}
 
20
  <a href="/login">Войти</a> | <a href="/register">Регистрация</a>
21
  {% endif %}
22
  </div>
23
+
24
  <h1>Сообщения</h1>
25
 
26
  <form method="post">
27
  <textarea name="text" rows="3" cols="40" placeholder="Введите сообщение..."></textarea><br>
28
+ <label>
29
+ <input type="checkbox" name="hidden"> Скрыто
30
+ </label><br>
31
  <button type="submit">Отправить</button>
32
  </form>
33
 
 
41
  <hr>
42
 
43
  {% for msg in messages %}
44
+ <div class="message source-{{ msg.source }} {% if msg.user_did == request.session['did'] %}from-self{% endif %}">
45
  <div>
46
  <strong>{{ msg.source }}</strong> — {{ msg.timestamp }}
47
  {% if msg.hidden %}<span class="private"></span>{% endif %}
48
  </div>
49
+ <div>
50
+ от {{ msg.user_did[:10] }}...
51
+ {% if msg.username %}({{ msg.username }}){% endif %}
52
+ </div>
53
  <div>{{ msg.text }}</div>
54
  </div>
55
  {% endfor %}
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/register.html CHANGED
@@ -1,14 +1,6 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head><title>Регистрация</title></head>
4
- <body>
5
- <h2>Регистрация</h2>
6
- {% if error %}<p style="color: red">{{ error }}</p>{% endif %}
7
- <form method="post">
8
- <input name="username" placeholder="Имя пользователя">
9
- <input name="password" type="password" placeholder="Пароль">
10
- <button type="submit">Зарегистрироваться</button>
11
- </form>
12
- <p>Уже есть аккаунт? <a href="/login">Войти</a></p>
13
- </body>
14
- </html>
 
1
+ <form method="post">
2
+ <label>Имя пользователя: <input type="text" name="username" required></label><br>
3
+ <label>Email: <input type="email" name="mail" required></label><br>
4
+ <label>Пароль: <input type="password" name="password" required></label><br>
5
+ <button type="submit">Зарегистрироваться</button>
6
+ </form>
 
 
 
 
 
 
 
 
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/views.py CHANGED
@@ -1,14 +1,8 @@
1
- # agents/notebook/views.py
2
-
3
- from fastapi import APIRouter, Request, Form, Depends
4
  from fastapi.responses import RedirectResponse
5
  from fastapi.templating import Jinja2Templates
6
- from fastapi.exceptions import HTTPException
7
- from starlette.middleware.sessions import SessionMiddleware
8
  from starlette.status import HTTP_303_SEE_OTHER
9
  from tools.storage import Storage
10
- from passlib.hash import bcrypt
11
- from fastapi import FastAPI
12
 
13
  router = APIRouter()
14
  templates = Jinja2Templates(directory="notebook/templates")
@@ -16,13 +10,14 @@ storage = Storage()
16
 
17
  @router.get("/chat")
18
  def chat_page(request: Request):
19
- username = request.session.get("user")
20
- if not username:
 
21
  return RedirectResponse("/login", status_code=303)
22
 
23
  notes = storage.fetchall(
24
  "SELECT text, timestamp, source FROM notes WHERE hidden=0 AND user_did=? ORDER BY timestamp DESC LIMIT 20",
25
- (username,)
26
  )
27
  return templates.TemplateResponse("chat.html", {
28
  "request": request,
@@ -32,24 +27,26 @@ def chat_page(request: Request):
32
 
33
  @router.post("/chat")
34
  def submit_note(request: Request, message: str = Form(...)):
35
- username = request.session.get("user", "anon") # Можно вернуть anon, если не залогинен
36
  if message.strip():
37
- storage.execute(
38
- "INSERT INTO notes (text, source, user_did) VALUES (?, ?, ?)",
39
- (message.strip(), "user", username)
 
40
  )
41
  return RedirectResponse(url="/chat", status_code=303)
42
 
43
  @router.get("/messages")
44
  def show_messages(request: Request, only_personal: bool = False):
45
- username = request.session.get("user")
46
- if not username:
 
47
  return RedirectResponse("/login", status_code=303)
48
 
49
  is_operator = False # Пока не реализовано
50
  messages = storage.get_notes(
51
  limit=50,
52
- user_did=username,
53
  is_operator=is_operator,
54
  only_personal=only_personal
55
  )
@@ -57,21 +54,21 @@ def show_messages(request: Request, only_personal: bool = False):
57
  "request": request,
58
  "messages": messages,
59
  "only_personal": only_personal,
60
- "username": username # 👈 вот это
61
  })
62
 
63
  @router.post("/messages")
64
  def post_message(
65
  request: Request,
66
  text: str = Form(...),
67
- user_did: str = Form(default="anon"),
68
  hidden: str = Form(default=None)
69
  ):
 
70
  is_hidden = 1 if hidden else 0
71
 
72
  storage.write_note(
73
  content=text,
74
- user_did=user_did,
75
  source="user",
76
  hidden=is_hidden
77
  )
@@ -82,13 +79,15 @@ def login_page(request: Request):
82
  return templates.TemplateResponse("login.html", {"request": request})
83
 
84
  @router.post("/login")
85
- def login_user(request: Request, username: str = Form(...), password: str = Form(...)):
86
- if storage.authenticate_user(username, password):
87
- request.session["user"] = username
88
- return RedirectResponse("/chat", status_code=HTTP_303_SEE_OTHER)
 
 
89
  return templates.TemplateResponse("login.html", {
90
  "request": request,
91
- "error": "Неверный логин или пароль"
92
  })
93
 
94
  @router.get("/register")
@@ -96,13 +95,20 @@ def register_page(request: Request):
96
  return templates.TemplateResponse("register.html", {"request": request})
97
 
98
  @router.post("/register")
99
- def register_user(request: Request, username: str = Form(...), password: str = Form(...)):
100
- if storage.register_user(username, password):
101
- request.session["user"] = username
102
- return RedirectResponse("/chat", status_code=HTTP_303_SEE_OTHER)
 
 
 
 
 
 
 
103
  return templates.TemplateResponse("register.html", {
104
  "request": request,
105
- "error": "Пользователь уже существует"
106
  })
107
 
108
  @router.get("/logout")
 
1
+ from fastapi import APIRouter, Request, Form
 
 
2
  from fastapi.responses import RedirectResponse
3
  from fastapi.templating import Jinja2Templates
 
 
4
  from starlette.status import HTTP_303_SEE_OTHER
5
  from tools.storage import Storage
 
 
6
 
7
  router = APIRouter()
8
  templates = Jinja2Templates(directory="notebook/templates")
 
10
 
11
  @router.get("/chat")
12
  def chat_page(request: Request):
13
+ did = request.session.get("did")
14
+ username = request.session.get("username")
15
+ if not did:
16
  return RedirectResponse("/login", status_code=303)
17
 
18
  notes = storage.fetchall(
19
  "SELECT text, timestamp, source FROM notes WHERE hidden=0 AND user_did=? ORDER BY timestamp DESC LIMIT 20",
20
+ (did,)
21
  )
22
  return templates.TemplateResponse("chat.html", {
23
  "request": request,
 
27
 
28
  @router.post("/chat")
29
  def submit_note(request: Request, message: str = Form(...)):
30
+ did = request.session.get("did", "anon")
31
  if message.strip():
32
+ storage.write_note(
33
+ content=message.strip(),
34
+ user_did=did,
35
+ source="user"
36
  )
37
  return RedirectResponse(url="/chat", status_code=303)
38
 
39
  @router.get("/messages")
40
  def show_messages(request: Request, only_personal: bool = False):
41
+ did = request.session.get("did")
42
+ username = request.session.get("username")
43
+ if not did:
44
  return RedirectResponse("/login", status_code=303)
45
 
46
  is_operator = False # Пока не реализовано
47
  messages = storage.get_notes(
48
  limit=50,
49
+ user_did=did,
50
  is_operator=is_operator,
51
  only_personal=only_personal
52
  )
 
54
  "request": request,
55
  "messages": messages,
56
  "only_personal": only_personal,
57
+ "username": username
58
  })
59
 
60
  @router.post("/messages")
61
  def post_message(
62
  request: Request,
63
  text: str = Form(...),
 
64
  hidden: str = Form(default=None)
65
  ):
66
+ did = request.session.get("did", "anon")
67
  is_hidden = 1 if hidden else 0
68
 
69
  storage.write_note(
70
  content=text,
71
+ user_did=did,
72
  source="user",
73
  hidden=is_hidden
74
  )
 
79
  return templates.TemplateResponse("login.html", {"request": request})
80
 
81
  @router.post("/login")
82
+ def login_user(request: Request, mail: str = Form(...), password: str = Form(...)):
83
+ if storage.authenticate_user(mail, password):
84
+ user_info = storage.get_user_info(mail)
85
+ request.session["username"] = user_info["username"]
86
+ request.session["did"] = user_info["did"]
87
+ return RedirectResponse("/messages", status_code=HTTP_303_SEE_OTHER)
88
  return templates.TemplateResponse("login.html", {
89
  "request": request,
90
+ "error": "Неверный email или пароль"
91
  })
92
 
93
  @router.get("/register")
 
95
  return templates.TemplateResponse("register.html", {"request": request})
96
 
97
  @router.post("/register")
98
+ def register_user(
99
+ request: Request,
100
+ username: str = Form(...),
101
+ mail: str = Form(...),
102
+ password: str = Form(...)
103
+ ):
104
+ if storage.register_user(username, mail, password):
105
+ user_info = storage.get_user_info(mail)
106
+ request.session["username"] = user_info["username"]
107
+ request.session["did"] = user_info["did"]
108
+ return RedirectResponse("/messages", status_code=HTTP_303_SEE_OTHER)
109
  return templates.TemplateResponse("register.html", {
110
  "request": request,
111
+ "error": "Пользователь с таким email уже существует"
112
  })
113
 
114
  @router.get("/logout")
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/tools/storage.py CHANGED
@@ -4,6 +4,7 @@ import hashlib
4
  import sqlite3
5
  import os
6
  import json
 
7
 
8
  from datetime import datetime, timedelta, UTC
9
 
@@ -17,7 +18,6 @@ class Storage:
17
  self.conn = sqlite3.connect(db_path, check_same_thread=False)
18
  self.conn.row_factory = sqlite3.Row
19
  self._init_db()
20
- self._init_user_table()
21
 
22
  def _init_db(self):
23
  # Загружаем и выполняем весь SQL из файла db_structure.sql
@@ -750,39 +750,46 @@ class Storage:
750
  def _hash_password(self, password: str) -> str:
751
  return hashlib.sha256(password.encode()).hexdigest()
752
 
753
- def _init_user_table(self):
754
- self.conn.execute('''
755
- CREATE TABLE IF NOT EXISTS users (
756
- id INTEGER PRIMARY KEY AUTOINCREMENT,
757
- username TEXT UNIQUE NOT NULL,
758
- password_hash TEXT NOT NULL
759
- )
760
- ''')
761
- self.conn.commit()
762
-
763
- def register_user(self, username: str, password: str) -> bool:
764
- self._init_user_table()
765
  try:
766
  self.conn.execute(
767
- "INSERT INTO users (username, password_hash) VALUES (?, ?)",
768
- (username, self._hash_password(password))
769
  )
770
  self.conn.commit()
771
  return True
772
  except sqlite3.IntegrityError:
773
- return False # пользователь уже существует
774
 
775
- def authenticate_user(self, username: str, password: str) -> bool:
 
776
  cursor = self.conn.cursor()
777
  cursor.execute(
778
- "SELECT password_hash FROM users WHERE username = ?",
779
- (username,)
780
  )
781
  result = cursor.fetchone()
782
  if result:
783
  return result["password_hash"] == self._hash_password(password)
784
  return False
785
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
786
  # Утилиты
787
 
788
  def close(self):
 
4
  import sqlite3
5
  import os
6
  import json
7
+ import uuid
8
 
9
  from datetime import datetime, timedelta, UTC
10
 
 
18
  self.conn = sqlite3.connect(db_path, check_same_thread=False)
19
  self.conn.row_factory = sqlite3.Row
20
  self._init_db()
 
21
 
22
  def _init_db(self):
23
  # Загружаем и выполняем весь SQL из файла db_structure.sql
 
750
  def _hash_password(self, password: str) -> str:
751
  return hashlib.sha256(password.encode()).hexdigest()
752
 
753
+ def register_user(self, username: str, mail: str, password: str) -> bool:
754
+ mail = mail.lower()
755
+ did = f"did:example:{uuid.uuid4()}"
 
 
 
 
 
 
 
 
 
756
  try:
757
  self.conn.execute(
758
+ "INSERT INTO users (username, mail, password_hash, did) VALUES (?, ?, ?, ?)",
759
+ (username, mail, self._hash_password(password), did)
760
  )
761
  self.conn.commit()
762
  return True
763
  except sqlite3.IntegrityError:
764
+ return False # уже существует (уникальность mail или did)
765
 
766
+ def authenticate_user(self, mail: str, password: str) -> bool:
767
+ mail = mail.lower()
768
  cursor = self.conn.cursor()
769
  cursor.execute(
770
+ "SELECT password_hash FROM users WHERE mail = ?",
771
+ (mail,)
772
  )
773
  result = cursor.fetchone()
774
  if result:
775
  return result["password_hash"] == self._hash_password(password)
776
  return False
777
 
778
+ def get_user_info(self, mail: str) -> dict | None:
779
+ mail = mail.lower()
780
+ cursor = self.conn.cursor()
781
+ cursor.execute(
782
+ "SELECT username, did FROM users WHERE mail = ?",
783
+ (mail,)
784
+ )
785
+ result = cursor.fetchone()
786
+ if result:
787
+ return {
788
+ "username": result["username"],
789
+ "did": result["did"]
790
+ }
791
+ return None
792
+
793
  # Утилиты
794
 
795
  def close(self):
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/login.html ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head><title>Вход</title></head>
4
+ <body>
5
+ <h2>Вход</h2>
6
+ {% if error %}<p style="color: red">{{ error }}</p>{% endif %}
7
+ <form method="post">
8
+ <input name="username" placeholder="Имя пользователя">
9
+ <input name="password" type="password" placeholder="Пароль">
10
+ <button type="submit">Войти</button>
11
+ </form>
12
+ <p>Нет аккаунта? <a href="/register">Регистрация</a></p>
13
+ </body>
14
+ </html>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/register.html ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head><title>Регистрация</title></head>
4
+ <body>
5
+ <h2>Регистрация</h2>
6
+ {% if error %}<p style="color: red">{{ error }}</p>{% endif %}
7
+ <form method="post">
8
+ <input name="username" placeholder="Имя пользователя">
9
+ <input name="password" type="password" placeholder="Пароль">
10
+ <button type="submit">Зарегистрироваться</button>
11
+ </form>
12
+ <p>Уже есть аккаунт? <a href="/login">Войти</a></p>
13
+ </body>
14
+ </html>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/notebook/templates/messages.html CHANGED
@@ -1,18 +1,50 @@
1
- <!-- templates/messages.html -->
2
  <!DOCTYPE html>
3
  <html>
4
  <head>
5
- <title>Отправка сообщения агенту</title>
 
 
 
 
 
 
 
 
 
6
  </head>
7
  <body>
8
- <h2>Новое сообщение</h2>
9
- <form method="post" action="/messages">
10
- <label for="text">Сообщение:</label><br>
11
- <textarea name="text" rows="4" cols="50" required></textarea><br><br>
12
-
13
- <label><input type="checkbox" name="is_private" value="true"> Приватное сообщение</label><br><br>
 
 
14
 
 
 
 
15
  <button type="submit">Отправить</button>
16
  </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  </body>
18
  </html>
 
 
1
  <!DOCTYPE html>
2
  <html>
3
  <head>
4
+ <title>Сообщения</title>
5
+ <style>
6
+ .message { padding: 8px; margin: 10px 0; border-radius: 8px; }
7
+ .source-user { background-color: #e0f7fa; }
8
+ .source-cli { background-color: #dcedc8; }
9
+ .source-llm { background-color: #f3e5f5; }
10
+ .source-system { background-color: #fff3e0; }
11
+ .private::after { content: " 🔒"; }
12
+ .from-self::before { content: "🧍 "; }
13
+ </style>
14
  </head>
15
  <body>
16
+ <div style="margin-bottom: 10px;">
17
+ {% if username %}
18
+ Привет, {{ username }} | <a href="/logout">Выход</a>
19
+ {% else %}
20
+ <a href="/login">Войти</a> | <a href="/register">Регистрация</a>
21
+ {% endif %}
22
+ </div>
23
+ <h1>Сообщения</h1>
24
 
25
+ <form method="post">
26
+ <textarea name="text" rows="3" cols="40" placeholder="Введите сообщение..."></textarea><br>
27
+ <input type="text" name="user_did" placeholder="User DID" readonly value="{{ username }}">
28
  <button type="submit">Отправить</button>
29
  </form>
30
+
31
+ <hr>
32
+
33
+ <div>
34
+ <a href="/messages">📢 Все сообщения</a> |
35
+ <a href="/messages?only_personal=true">🙋 Только мои</a>
36
+ </div>
37
+
38
+ <hr>
39
+
40
+ {% for msg in messages %}
41
+ <div class="message source-{{ msg.source }} {% if msg.user_did == 'did:example:local-user' %}from-self{% endif %}">
42
+ <div>
43
+ <strong>{{ msg.source }}</strong> — {{ msg.timestamp }}
44
+ {% if msg.hidden %}<span class="private"></span>{% endif %}
45
+ </div>
46
+ <div>{{ msg.text }}</div>
47
+ </div>
48
+ {% endfor %}
49
  </body>
50
  </html>
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/requirements.txt CHANGED
@@ -11,4 +11,5 @@ uvicorn
11
  jinja2
12
  python-multipart
13
  passlib[bcrypt]
14
- werkzeug
 
 
11
  jinja2
12
  python-multipart
13
  passlib[bcrypt]
14
+ werkzeug
15
+ itsdangerous
hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/hf_repo/agents/web_ui.py CHANGED
@@ -13,11 +13,14 @@ from fastapi import FastAPI
13
  from fastapi.staticfiles import StaticFiles
14
  from fastapi.templating import Jinja2Templates
15
  #from agents.notebook.auth import router as auth_router
 
16
  from agents.notebook.views import router as notebook_router
17
  from tools.storage import Storage
18
 
19
  storage = Storage()
20
  app = FastAPI()
 
 
21
 
22
  app.mount("/static", StaticFiles(directory=os.path.join(os.path.dirname(__file__), "notebook/static")), name="static")
23
  templates = Jinja2Templates(directory=os.path.join(os.path.dirname(__file__), "notebook/templates"))
 
13
  from fastapi.staticfiles import StaticFiles
14
  from fastapi.templating import Jinja2Templates
15
  #from agents.notebook.auth import router as auth_router
16
+ from starlette.middleware.sessions import SessionMiddleware
17
  from agents.notebook.views import router as notebook_router
18
  from tools.storage import Storage
19
 
20
  storage = Storage()
21
  app = FastAPI()
22
+ app.add_middleware(SessionMiddleware, secret_key="очень_секретный_ключ")
23
+
24
 
25
  app.mount("/static", StaticFiles(directory=os.path.join(os.path.dirname(__file__), "notebook/static")), name="static")
26
  templates = Jinja2Templates(directory=os.path.join(os.path.dirname(__file__), "notebook/templates"))