Compare commits
4
Commits
acba5dd5f1
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff7cbad050 | ||
|
|
3fe8a91622 | ||
|
|
dd67d2c2ec | ||
|
|
cc0b342208 |
No files matched your search
@@ -0,0 +1,3 @@
|
||||
public/
|
||||
resources/
|
||||
.hugo_build.lock
|
||||
@@ -37,6 +37,10 @@ Hugo static site with custom theme. Russian-language personal documentation/know
|
||||
- **creds**: `title`, `description`, `service` — body contains base64-encoded encrypted payload
|
||||
- **leadera / personal-site**: `title`, `tags`
|
||||
|
||||
## Workflow
|
||||
|
||||
- **Always run `hugo` after making any changes** (content, templates, config, CSS, JS) to verify the build succeeds without errors. This is mandatory before considering a task complete.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- Static assets must go in `static/` at project root, not `themes/a2v/static/` (the theme static dir is empty).
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
+++
|
||||
title = "База знаний"
|
||||
draft = false
|
||||
+++
|
||||
@@ -0,0 +1,4 @@
|
||||
+++
|
||||
title = "Семен"
|
||||
draft = false
|
||||
+++
|
||||
@@ -0,0 +1,7 @@
|
||||
+++
|
||||
title = "История изменений"
|
||||
date = 2026-04-26
|
||||
draft = false
|
||||
tags = []
|
||||
+++
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
+++
|
||||
title = "Дневник"
|
||||
draft = false
|
||||
+++
|
||||
@@ -0,0 +1,4 @@
|
||||
+++
|
||||
title = "База знаний"
|
||||
draft = false
|
||||
+++
|
||||
@@ -0,0 +1,4 @@
|
||||
+++
|
||||
title = "Яша"
|
||||
draft = false
|
||||
+++
|
||||
@@ -0,0 +1,4 @@
|
||||
+++
|
||||
title = "Креды"
|
||||
draft = false
|
||||
+++
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,4 @@
|
||||
+++
|
||||
title = "Дневник"
|
||||
draft = false
|
||||
+++
|
||||
@@ -0,0 +1,4 @@
|
||||
+++
|
||||
title = "Инструкции"
|
||||
draft = false
|
||||
+++
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,4 @@
|
||||
+++
|
||||
title = "База знаний"
|
||||
draft = false
|
||||
+++
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,4 @@
|
||||
+++
|
||||
title = "Планы"
|
||||
draft = false
|
||||
+++
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -6,11 +6,11 @@ relativeURLs = true
|
||||
canonifyURLs = false
|
||||
|
||||
[params]
|
||||
warmPrimary = "#c4623a"
|
||||
warmBg = "#f5f0e8"
|
||||
warmText = "#3d3530"
|
||||
warmAccent = "#d4a853"
|
||||
warmGray = "#6b6560"
|
||||
warmPrimary = "#1a1a1a"
|
||||
warmBg = "#ffffff"
|
||||
warmText = "#111111"
|
||||
warmAccent = "#444444"
|
||||
warmGray = "#888888"
|
||||
|
||||
[taxonomies]
|
||||
tag = "tags"
|
||||
@@ -19,33 +19,74 @@ canonifyURLs = false
|
||||
|
||||
[menu]
|
||||
[[menu.main]]
|
||||
name = "Дневник"
|
||||
url = "/diary/"
|
||||
name = "Яша"
|
||||
identifier = "yasha"
|
||||
weight = 1
|
||||
[[menu.main]]
|
||||
name = "Дневник"
|
||||
identifier = "yasha-diary"
|
||||
url = "/yasha/diary/"
|
||||
parent = "yasha"
|
||||
weight = 10
|
||||
[[menu.main]]
|
||||
name = "База знаний"
|
||||
url = "/kb/"
|
||||
weight = 2
|
||||
identifier = "yasha-kb"
|
||||
url = "/yasha/kb/"
|
||||
parent = "yasha"
|
||||
weight = 20
|
||||
[[menu.main]]
|
||||
name = "Планы"
|
||||
url = "/plans/"
|
||||
weight = 3
|
||||
[[menu.main]]
|
||||
name = "Leadera"
|
||||
url = "/leadera/"
|
||||
weight = 4
|
||||
identifier = "yasha-plans"
|
||||
url = "/yasha/plans/"
|
||||
parent = "yasha"
|
||||
weight = 30
|
||||
[[menu.main]]
|
||||
name = "Инструкции"
|
||||
url = "/guides/"
|
||||
weight = 5
|
||||
identifier = "yasha-guides"
|
||||
url = "/yasha/guides/"
|
||||
parent = "yasha"
|
||||
weight = 40
|
||||
[[menu.main]]
|
||||
name = "Креды"
|
||||
url = "/creds/"
|
||||
weight = 6
|
||||
identifier = "yasha-creds"
|
||||
url = "/yasha/creds/"
|
||||
parent = "yasha"
|
||||
weight = 50
|
||||
[[menu.main]]
|
||||
name = "Leadera"
|
||||
identifier = "yasha-leadera"
|
||||
url = "/yasha/leadera/"
|
||||
parent = "yasha"
|
||||
weight = 60
|
||||
[[menu.main]]
|
||||
name = "Личный сайт"
|
||||
url = "/personal-site/"
|
||||
weight = 7
|
||||
identifier = "yasha-personal-site"
|
||||
url = "/yasha/personal-site/"
|
||||
parent = "yasha"
|
||||
weight = 70
|
||||
|
||||
[[menu.main]]
|
||||
name = "Семен"
|
||||
identifier = "semen"
|
||||
weight = 2
|
||||
[[menu.main]]
|
||||
name = "История изменений"
|
||||
identifier = "semen-changelog"
|
||||
url = "/semen/changelog/"
|
||||
parent = "semen"
|
||||
weight = 10
|
||||
[[menu.main]]
|
||||
name = "База знаний"
|
||||
identifier = "semen-kb"
|
||||
url = "/semen/kb/"
|
||||
parent = "semen"
|
||||
weight = 20
|
||||
[[menu.main]]
|
||||
name = "Дневник"
|
||||
identifier = "semen-diary"
|
||||
url = "/semen/diary/"
|
||||
parent = "semen"
|
||||
weight = 30
|
||||
|
||||
[outputs]
|
||||
home = ["HTML", "JSON"]
|
||||
|
||||
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
[{"content":"Basic Authentication Username: agaltsovav Password: Magamet787! Конфигурация Nginx server { listen 8091; server_name docs.a2v.space; auth_basic \u0026#34;Restricted Access\u0026#34;; auth_basic_user_file /etc/nginx/.htpasswd; } Доступ URL: https://docs.a2v.space (через reverse proxy) Local: http://localhost:8091 (для разработки) Auth: всегда требуется Мастер-пароль для шифрования Важно: Пароль для расшифровки файлов в разделе creds/\nMaster Password: Popoloka16! Шифрование Алгоритм: AES-256-GCM Хранилище: client-side encryption Файлы шифруются при сохранении Расшифровываются при чтении ","section":"creds","tags":["docs","authentication","nginx"],"title":"docs.a2v.space Basic Auth","url":"/creds/docs-a2v-space-auth/"},{"content":"Docker Compose креды (~/Projects/eversolo-companion/docker-compose.yml) services: postgres: environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: password POSTGRES_DB: eversolo Локальные креды (.env файл) POSTGRES_USER=postgres POSTGRES_PASSWORD=password POSTGRES_DB=eversolo Хостинг Host: localhost Port: 5433 Database: eversolo Безопасность Используйте разные пароли для production Включите SSL для удаленного подключения Регулярно делайте бэкапы ","section":"creds","tags":["eversolo","companion","postgresql","docker"],"title":"Eversolo Companion PostgreSQL","url":"/creds/eversolo-companion-postgres/"},{"content":"\nNYVK4PEGwmOHmAnGk8IAUc+lyFJtnEAvX6ZddBro/ILRWHOXBGYywgglFvQZ18BLDLb5DuA8GPAIdB/LwTHRXYO0KsmtlZQVWGEeeXlXAoEXpa5theoOQhebkW6LmiKyNGAVW2z9rK3nz6NnztYdn49LvGMjrHuR4IwCLbl/pYa96CPNRnxGDFlduFi8lMSspM0rBFi/bZhNGhzy36ViCl+aKr5P9cle0ZvM8E8av/6c5JjgDt88QFI3VYdTaTCE0+uHsXlnJN7Qgg/KH7kD\n\n\n","section":"creds","tags":["postgresql","docker-compose","leadera","database"],"title":"Mac Studio — leadera-api БД (docker-compose)","url":"/creds/leadera-db-docker/"},{"content":"\np7Iw7Y3zbj1kLBcbOXGoO27oDwF85AhxBW7saT538hxi0ogJpyFeM49GDuLHzxuyknvdNC/rx3tb4AFnGkCeHSrrCmj+VK8Zxt7ujeujVJSRuOeeUlsqkHdlmaiXl2KQknrIsqVCi3Tnn0V5qcFxvo+1Ktc+surF6bBpWrTHzWV5mLtWVfD6C6kvvP/UFldFBD6v+FQs79yfoOe/j/bUrtpt6v5VndbbBmhUAQcy1BV2rpXxO373dTcLQ6coRWkt/RFo\n\n\n","section":"creds","tags":["postgresql","docker","leadera","database"],"title":"Mac Studio — leadera-api БД (локальная)","url":"/creds/leadera-db-local/"},{"content":"\nYEI16NAJ78d3lmSa/lca8Zxczt+u6GyMUYHduX47VoFQdj0Ixozd8R6yXoB7eM+AjhCtNGkOxGlOj2s73ABOBjLEIVIhD4Z+xTyzdtah4VpyLoltYOeNYsnbIIFARdF/FBYq5COXohhXs33K5ozznrtDd/awbKMjl/sbQqjXAZ+A3ZSob6/5uU+fFKetXLR6v4ZLAYQ57iiD21ImSLSggwv+rJgK5XyPDKEPD4N5nOsiDrLgoukVBMvgJ54PqfPp2Hni+2tdKOlMnOG2npTuu4SkyALcfFar\n\n\n","section":"creds","tags":["vpn","amnezia","infrastructure"],"title":"VPN VPS","url":"/creds/vpn-vps/"},{"content":"\nIpYXLMtgCFlGQ550DZCLjq37VsnNqL08Rn03iRS7kQ7ozBZNsiZySFzDAvYh4xIHytRDy0Sz0KGUXekr0S+cNiJsmtZ8kj0Pd0JhevhxTMZF2khFvs/LFWoTL5ZwAJ34y/qfLw2gaWX90VZDu0wO+KeAQeXhrWmol/2zUlfWX1AihyYqD2vRz+h2FcjD8ubYOXG35PyX7OlV1HPXgDv5X/bFNrxzPc867UOwB4aGxY9a\n\n\n","section":"creds","tags":["vps","reg.ru","infrastructure"],"title":"VPS reg.ru","url":"/creds/vps-reg-ru/"}]
|
||||
+83
-69
@@ -1,44 +1,53 @@
|
||||
:root {
|
||||
--warm-bg: #f5f0e8;
|
||||
--warm-primary: #c4623a;
|
||||
--warm-text: #3d3530;
|
||||
--warm-gray: #6b6560;
|
||||
--warm-accent: #d4a853;
|
||||
--warm-white: #fffdf9;
|
||||
--warm-bg: #ffffff;
|
||||
--warm-primary: #1a1a1a;
|
||||
--warm-text: #111111;
|
||||
--warm-gray: #888888;
|
||||
--warm-accent: #444444;
|
||||
--warm-white: #ffffff;
|
||||
--border-color: #d0d0d0;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--warm-bg);
|
||||
color: var(--warm-text);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
font-size: 1rem;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.site-header {
|
||||
background-color: var(--warm-primary);
|
||||
padding: 0.5rem 0;
|
||||
border-bottom: 3px solid var(--warm-primary);
|
||||
}
|
||||
|
||||
.site-header .navbar-brand {
|
||||
color: var(--warm-white);
|
||||
font-weight: 600;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
font-family: Georgia, "Times New Roman", Times, serif;
|
||||
}
|
||||
|
||||
.site-header .nav-link {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
padding: 0.5rem 1rem;
|
||||
transition: color 0.2s;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
|
||||
font-size: 0.875rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.site-header .nav-link:hover,
|
||||
.site-header .nav-link.active {
|
||||
color: var(--warm-white);
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
background-color: rgba(255, 255, 255, 0.12);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
@@ -52,6 +61,7 @@ body {
|
||||
.search-form input {
|
||||
background-color: rgba(255, 255, 255, 0.9);
|
||||
border: none;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
|
||||
}
|
||||
|
||||
.search-form input:focus {
|
||||
@@ -59,15 +69,14 @@ body {
|
||||
box-shadow: 0 0 0 2px var(--warm-accent);
|
||||
}
|
||||
|
||||
/* Search Results */
|
||||
.search-results {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
left: 0;
|
||||
background: var(--warm-white);
|
||||
border-top: 1px solid var(--warm-gray);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
border-top: 1px solid var(--border-color);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||||
z-index: 1000;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
@@ -87,7 +96,7 @@ body {
|
||||
|
||||
.search-results li {
|
||||
padding: 0.5rem 0;
|
||||
border-bottom: 1px solid var(--warm-bg);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.search-results li:last-child {
|
||||
@@ -101,52 +110,52 @@ body {
|
||||
|
||||
.search-results a:hover {
|
||||
color: var(--warm-primary);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
.sidebar {
|
||||
background-color: var(--warm-white);
|
||||
border-right: 1px solid #e0d8cc;
|
||||
border-right: 1px solid var(--border-color);
|
||||
min-height: calc(100vh - 60px);
|
||||
padding: 1.5rem 1rem;
|
||||
}
|
||||
|
||||
.sidebar-content h6 {
|
||||
color: var(--warm-primary);
|
||||
font-weight: 600;
|
||||
font-weight: 700;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
letter-spacing: 1px;
|
||||
border-bottom: 2px solid var(--warm-primary);
|
||||
padding-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.sidebar-content .nav-link {
|
||||
color: var(--warm-text);
|
||||
padding: 0.375rem 0.75rem;
|
||||
border-radius: 4px;
|
||||
border-radius: 2px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.sidebar-content .nav-link:hover {
|
||||
background-color: var(--warm-bg);
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* Main Content */
|
||||
.main-content {
|
||||
padding: 2rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.card {
|
||||
background-color: var(--warm-white);
|
||||
border: 1px solid #e0d8cc;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 2px;
|
||||
transition: box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
.card-title a {
|
||||
@@ -156,9 +165,9 @@ body {
|
||||
|
||||
.card-title a:hover {
|
||||
color: var(--warm-primary);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Plan Cards */
|
||||
.plan-card {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
@@ -172,20 +181,19 @@ body {
|
||||
}
|
||||
|
||||
.plan-card.in-progress {
|
||||
border-left: 4px solid #e67e22;
|
||||
border-left: 4px solid #c4623a;
|
||||
}
|
||||
|
||||
.plan-card.done {
|
||||
border-left: 4px solid #27ae60;
|
||||
border-left: 4px solid #2a2a2a;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.plan-card.cancelled {
|
||||
border-left: 4px solid #e74c3c;
|
||||
border-left: 4px solid #999;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* Diary Timeline */
|
||||
.diary-timeline {
|
||||
position: relative;
|
||||
padding-left: 2rem;
|
||||
@@ -205,7 +213,7 @@ body {
|
||||
position: relative;
|
||||
margin-bottom: 2rem;
|
||||
padding-bottom: 1.5rem;
|
||||
border-bottom: 1px solid #e0d8cc;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.diary-entry:last-child {
|
||||
@@ -217,11 +225,11 @@ body {
|
||||
position: absolute;
|
||||
left: -2rem;
|
||||
top: 0.5rem;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-color: var(--warm-primary);
|
||||
border-radius: 50%;
|
||||
transform: translateX(-5px);
|
||||
transform: translateX(-4px);
|
||||
}
|
||||
|
||||
.diary-entry .time {
|
||||
@@ -231,27 +239,26 @@ body {
|
||||
|
||||
.diary-date {
|
||||
color: var(--warm-primary);
|
||||
font-weight: 600;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Credentials */
|
||||
.creds-card {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.creds-card .encrypted-data {
|
||||
font-family: monospace;
|
||||
background: var(--warm-bg);
|
||||
background: #f5f5f5;
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
border-radius: 2px;
|
||||
word-break: break-all;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.creds-card .decrypted-fields {
|
||||
background: var(--warm-bg);
|
||||
background: #f5f5f5;
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.creds-card .field-row {
|
||||
@@ -270,11 +277,10 @@ body {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* Source path */
|
||||
.source-path {
|
||||
background-color: var(--warm-bg);
|
||||
border: 1px solid #e0d8cc;
|
||||
border-radius: 4px;
|
||||
background-color: #f5f5f5;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 2px;
|
||||
padding: 0.4rem 0.75rem;
|
||||
display: inline-block;
|
||||
}
|
||||
@@ -284,19 +290,19 @@ body {
|
||||
}
|
||||
.source-path .btn-outline-secondary {
|
||||
font-size: 0.75rem;
|
||||
border-color: #d0c8bc;
|
||||
border-color: var(--border-color);
|
||||
}
|
||||
.source-path .btn-outline-secondary:hover {
|
||||
background-color: var(--warm-primary);
|
||||
border-color: var(--warm-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Article styles */
|
||||
.article-content {
|
||||
background-color: var(--warm-white);
|
||||
padding: 2rem;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #e0d8cc;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.article-content h1,
|
||||
@@ -310,6 +316,12 @@ body {
|
||||
margin-top: 0;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 2px solid var(--warm-primary);
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
|
||||
.article-content h2 {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding-bottom: 0.3rem;
|
||||
}
|
||||
|
||||
.article-content table {
|
||||
@@ -327,21 +339,21 @@ body {
|
||||
.article-content th,
|
||||
.article-content td {
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #e0d8cc;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.article-content code {
|
||||
background-color: var(--warm-bg);
|
||||
background-color: #f0f0f0;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
border-radius: 2px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.article-content pre {
|
||||
background-color: var(--warm-text);
|
||||
color: var(--warm-bg);
|
||||
background-color: #1a1a1a;
|
||||
color: #e0e0e0;
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
border-radius: 2px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
@@ -351,14 +363,15 @@ body {
|
||||
}
|
||||
|
||||
.article-content a {
|
||||
color: var(--warm-primary);
|
||||
color: var(--warm-text);
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
.article-content a:hover {
|
||||
color: var(--warm-accent);
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -367,28 +380,30 @@ body {
|
||||
}
|
||||
|
||||
.tag {
|
||||
background-color: var(--warm-bg);
|
||||
color: var(--warm-gray);
|
||||
background-color: #f5f5f5;
|
||||
color: var(--warm-text);
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 20px;
|
||||
border-radius: 2px;
|
||||
font-size: 0.8rem;
|
||||
text-decoration: none;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.tag:hover {
|
||||
background-color: var(--warm-primary);
|
||||
color: white;
|
||||
border-color: var(--warm-primary);
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.site-footer {
|
||||
background-color: var(--warm-text);
|
||||
color: var(--warm-bg);
|
||||
background-color: var(--warm-primary);
|
||||
color: #ccc;
|
||||
padding: 1rem 0;
|
||||
margin-top: auto;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
@@ -416,10 +431,9 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
/* Context Quote */
|
||||
.context-quote {
|
||||
background-color: var(--warm-bg);
|
||||
border-left: 4px solid var(--warm-accent);
|
||||
background-color: #f5f5f5;
|
||||
border-left: 3px solid var(--warm-accent);
|
||||
padding: 1rem;
|
||||
margin-top: 1rem;
|
||||
font-style: italic;
|
||||
|
||||
@@ -1,317 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru-ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Diaries — docs.a2v.space</title>
|
||||
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../css/custom.css">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="../">docs.a2v.space</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMain" aria-controls="navbarMain" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../diary/">Дневник</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../kb/">База знаний</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<form class="d-flex search-form" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Поиск..." aria-label="Search" id="search-input">
|
||||
<button class="btn btn-outline-light" type="submit">Найти</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline-light d-md-none ms-2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebar" aria-expanded="false" aria-controls="sidebar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div id="search-results" class="search-results" style="display: none;">
|
||||
<div class="search-results-content">
|
||||
<button type="button" class="btn-close float-end" id="close-search"></button>
|
||||
<h5>Результаты поиска</h5>
|
||||
<ul id="results-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
<h6>Фильтры</h6>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item"><a class="nav-link" href="../diary/">Все записи</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6 class="mt-3">Теги</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/system-utils/">system-utils</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/opencode/">opencode</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/leadera/">leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/gantt/">gantt</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/canvas/">canvas</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/github/">github</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/roadmap/">roadmap</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/vikunja/">vikunja</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/tasktracker/">tasktracker</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/ollama/">ollama</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/qwen/">qwen</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/mtproxy/">mtproxy</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/infrastructure/">infrastructure</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/angular/">angular</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/go/">go</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/docker/">docker</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/eversolo/">eversolo</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/hugo/">hugo</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/docs/">docs</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/identity/">identity</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../tags/companion/">companion</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
|
||||
|
||||
<main class="col-md-9 col-lg-10 main-content">
|
||||
|
||||
<div class="article-content">
|
||||
<h1>Дневник</h1>
|
||||
|
||||
<div class="diary-timeline">
|
||||
|
||||
|
||||
<div class="diary-entry">
|
||||
<div class="diary-date">31.03.2026</div>
|
||||
<h3><a href="../diary/2026-03-31/">31 марта 2026</a></h3>
|
||||
|
||||
<div class="tags">
|
||||
|
||||
<a href="../tags/system-utils/" class="tag">system-utils</a>
|
||||
|
||||
<a href="../tags/opencode/" class="tag">opencode</a>
|
||||
|
||||
<a href="../tags/leadera/" class="tag">leadera</a>
|
||||
|
||||
<a href="../tags/gantt/" class="tag">gantt</a>
|
||||
|
||||
<a href="../tags/canvas/" class="tag">canvas</a>
|
||||
|
||||
<a href="../tags/github/" class="tag">github</a>
|
||||
|
||||
<a href="../tags/roadmap/" class="tag">roadmap</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="diary-entry">
|
||||
<div class="diary-date">30.03.2026</div>
|
||||
<h3><a href="../diary/2026-03-30/">30 марта 2026</a></h3>
|
||||
|
||||
<div class="tags">
|
||||
|
||||
<a href="../tags/vikunja/" class="tag">vikunja</a>
|
||||
|
||||
<a href="../tags/leadera/" class="tag">leadera</a>
|
||||
|
||||
<a href="../tags/tasktracker/" class="tag">tasktracker</a>
|
||||
|
||||
<a href="../tags/ollama/" class="tag">ollama</a>
|
||||
|
||||
<a href="../tags/qwen/" class="tag">qwen</a>
|
||||
|
||||
<a href="../tags/mtproxy/" class="tag">mtproxy</a>
|
||||
|
||||
<a href="../tags/infrastructure/" class="tag">infrastructure</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="diary-entry">
|
||||
<div class="diary-date">29.03.2026</div>
|
||||
<h3><a href="../diary/2026-03-29/">29 марта 2026</a></h3>
|
||||
|
||||
<div class="tags">
|
||||
|
||||
<a href="../tags/angular/" class="tag">angular</a>
|
||||
|
||||
<a href="../tags/go/" class="tag">go</a>
|
||||
|
||||
<a href="../tags/docker/" class="tag">docker</a>
|
||||
|
||||
<a href="../tags/eversolo/" class="tag">eversolo</a>
|
||||
|
||||
<a href="../tags/hugo/" class="tag">hugo</a>
|
||||
|
||||
<a href="../tags/docs/" class="tag">docs</a>
|
||||
|
||||
<a href="../tags/identity/" class="tag">identity</a>
|
||||
|
||||
<a href="../tags/infrastructure/" class="tag">infrastructure</a>
|
||||
|
||||
<a href="../tags/companion/" class="tag">companion</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p class="text-center mb-0">© 2026 docs.a2v.space</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/fuse.js@7.0.0/dist/fuse.min.js"></script>
|
||||
|
||||
|
||||
<script src="../js/search.js"></script>
|
||||
<script src="../js/creds.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
[{"content":"Timeline Утро — Обсуждали аналоги htop: порекомендовал btm, btop, glances. Создан конфиг ~/.config/bottom/bottom.toml День — Разбирались со скиллами OpenCode: как работают, автоматический подбор по description День — Созданы скиллы для Leadera проектов (Go: build-and-fix, restart-api; Angular: build-and-fix) День — Обсудили миграцию Ганта с DOM на Canvas 2D (Konva.js) — real-time коллаборация, экспорт PDF/PNG, 500+ задач Вечер — Установлен gh CLI, авторизован как agaltsovav Вечер — Созданы GitHub Projects: Leadera Roadmap (21 задача, P0–P3), agaltsovav.ru Roadmap (7 задач) Системные утилиты bottom (btm) — выбранный мониторинг, конфиг в ~/.config/bottom/bottom.toml Альтернативы: btop, glances OpenCode Skills Созданы скиллы:\napp-leadera-api/.opencode/skills/build-and-fix/ — сборка Go проекта + автофикс ошибок (до 10 итераций) app-leadera-api/.opencode/skills/restart-api/ — swagger генерация + pm2 restart app-leadera-fe/.opencode/skills/build-and-fix/ — сборка Angular + автофикс Gantt Canvas миграция Переход с DOM на Canvas 2D через Konva.js Цели: real-time коллаборация, экспорт PDF/PNG, 500+ задач Промт: GANTT_CANVAS_MIGRATION_PROMPT.md в app-leadera-fe GitHub CLI: gh авторизован (agaltsovav) Репо: app-leadera-fe, app-leadera-api, agaltsovav.ru Старые: leadera-fe/leadera-api (на VPS, leadera.ru) modece.ru — заброшен, планируется возвращение GitHub Projects — Roadmaps Leadera Roadmap (project #2) — 21 задача P0: JWT secret, CORS, bcrypt, ContextRole fallback, RegisterProtectedRoutes P1: Rate limiting, editor role check, MembersModal rewrite P2: HttpOnly cookies, CSRF, CSP, password policy, audit log, health-check, CI/CD, Canvas миграция, DnD P3: Code cleanup, тесты, email verification, поиск agaltsovav.ru Roadmap (project #1) — 7 задач Редизайн, мобильная версия, мультиязычность, блог, CI/CD, тёмная тема, форма обратной связи ","section":"diary","tags":["system-utils","opencode","leadera","gantt","canvas","github","roadmap"],"title":"31 марта 2026","url":"/diary/2026-03-31/"},{"content":"Timeline Утро — Установлен Vikunja (таск-трекер) на Mac Studio через Docker/Colima День — Создан проект «Leadera» в Vikunja (ID=2), добавлено 27 задач из аудита Leadera День — Начата реализация Leadera TaskTracker: миграции, модели, DTOs, repositories (бекенд), фронтенд (kanban, task-detail) Вечер — TaskTracker откачён по просьбе Антона — git checkout + git clean. Причина: Claude Code падал (SIGKILL), sub-agents не справлялись, Plane не запустился Вечер — Настроен MT Proxy на VPN VPS (185.234.64.52): порт 8443, секрет 9e40ae… Вечер — Qwen 3.5:9b подключён к OpenClaw через openclaw models set ollama/qwen3.5:9b, доступен для cron/spawn задач Вечер — Антон подключился к claw.a2v.space с мобильного, устройство одобрено Vikunja URL: http://192.168.0.37:8095 Проект Leadera: ID=2 API token: tk_635fb7ac… 27 задач из аудита, приоритеты P0–P3 Leadera TaskTracker ТЗ: leadera-tasktracker-spec.md Реализация через sub-agents (GLM-5.1) Откачено — проекты возвращены к предыдущему коммиту MT Proxy Сервер: 185.234.64.52, порт 8443 Docker контейнер: mtproxy Инфраструктура Qwen 3.5:9b доступен как дешёвая локальная модель для cron/spawn Мобильное подключение к claw.a2v.space подтверждено ","section":"diary","tags":["vikunja","leadera","tasktracker","ollama","qwen","mtproxy","infrastructure"],"title":"30 марта 2026","url":"/diary/2026-03-30/"},{"content":"Timeline 20:03 — Настроили Angular + Go проекты (leadera) через pm2, удалили proxy.conf, подняли PostgreSQL в Docker 20:31 — Изучили API Eversolo T8 — полный список REST-эндпоинтов для управления музыкой 20:49 — Обсудили важность записи контекста в файлы для сохранения памяти между сессиями 21:13 — Начали прLine truncated
|
||||
@@ -1 +0,0 @@
|
||||
[{"content":"Настройка SSH туннелей VPS reg.ru (95.163.232.9) # На Mac Studio: создать туннель для каждого сервиса ssh -N -R 8080:localhost:8080 root@95.163.232.9 # leadera-api ssh -N -R 8081:localhost:4096 root@95.163.232.9 # opencode ssh -N -R 8082:localhost:4200 root@95.163.232.9 # leadera-fe ssh -N -R 8083:localhost:8080 root@95.163.232.9 # docs-a2v Настройка launchd plist Файл: ~/Library/LaunchAgents/com.openclaw.sshtunnel.plist\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;!DOCTYPE plist PUBLIC \u0026#34;-//Apple//DTD PLIST 1.0//EN\u0026#34; \u0026#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd\u0026#34;\u0026gt; \u0026lt;plist version=\u0026#34;1.0\u0026#34;\u0026gt; \u0026lt;dict\u0026gt; \u0026lt;key\u0026gt;Label\u0026lt;/key\u0026gt; \u0026lt;string\u0026gt;com.openclaw.sshtunnel\u0026lt;/string\u0026gt; \u0026lt;key\u0026gt;ProgramArguments\u0026lt;/key\u0026gt; \u0026lt;array\u0026gt; \u0026lt;string\u0026gt;ssh\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;-N\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;-R\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;8080:localhost:8080\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;root@95.163.232.9\u0026lt;/string\u0026gt; \u0026lt;/array\u0026gt; \u0026lt;key\u0026gt;RunAtLoad\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;key\u0026gt;KeepAlive\u0026lt;/key\u0026gt; \u0026lt;true/\u0026gt; \u0026lt;/dict\u0026gt; \u0026lt;/plist\u0026gt; Автозапуск # Загрузить plist launchctl load ~/Library/LaunchAgents/com.openclaw.sshtunnel.plist # Проверить статус launchctl list | grep sshtunnel # Удалить (при необходимости) launchctl unload ~/Library/LaunchAgents/com.openclaw.sshtunnel.plist Доступ к сервисам Через reverse proxy на VPS:\ncode.a2v.space: порт 8080 → Mac Studio:4096 claw.a2v.space: порт 8081 → Mac Studio:8080 (leadera-api) sand.a2v.space: порт 8082 → Mac Studio:4200 (leadera-fe) docs.a2v.space: порт 8083 → Mac Studio:8080 (docs-a2v) SSL Все сервисы используют Let\u0026rsquo;s Encrypt сертификаты:\nПуть: /etc/letsencrypt/live/{claw,code,sand}.a2v.space/ Автопродление через certbot ","section":"guides","tags":["ssh","tunnel","vps","reverse-proxy"],"title":"SSH туннели для VPS reverse proxy","url":"/guides/ssh-tunnels-vps/"},{"content":"Общая схема Mac Studio (локальная сеть) → autossh (SSH-туннели) → VPS 95.163.232.9 (Debian) → nginx (HTTPS reverse proxy) → интернет Что нужно на VPS 1. Установленные пакеты apt update apt install nginx certbot python3-certbot-nginx 2. SSH-доступ Авторизация только по ключам Публичный ключ с Mac Studio добавлен в /root/.ssh/authorized_keys 3. nginx Для каждого домена создать server block:\nHTTP → редирект на HTTPS HTTPS с SSL-сертификатом Let\u0026rsquo;s Encrypt proxy_pass на localhost-порт туннеля Пример структуры:\n/etc/nginx/sites-available/ claw.a2v.space.conf code.a2v.space.conf sand.a2v.space.conf 4. SSL-сертификаты certbot certonly --nginx -d \u0026lt;domain\u0026gt; certbot renew --dry-run # проверить автообновление Что нужно на Mac Studio autossh через launchd Plist: ~/Library/LaunchAgents/com.openclaw.sshtunnel.plist\nОдин plist с несколькими -R флагами:\n\u0026lt;key\u0026gt;ProgramArguments\u0026lt;/key\u0026gt; \u0026lt;array\u0026gt; \u0026lt;string\u0026gt;/usr/local/bin/autossh\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;-M\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;0\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;-R\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;8080:localhost:18789\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;-R\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;8081:localhost:4096\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;-R\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;8082:localhost:4200\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;-R\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;8083:localhost:8080\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;root@95.163.232.9\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;-N\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;-i\u0026lt;/string\u0026gt; \u0026lt;string\u0026gt;/Users/\u0026lt;user\u0026gt;/.ssh/id_rsa\u0026lt;/string\u0026gt; \u0026lt;/array\u0026gt; Управление launchd launchctl load ~/Library/LaunchAgents/com.openclaw.sshtunnel.plist launchctl start com.openclaw.sshtunnel launchctl stop com.openclaw.sshtunnel Проверка # На VPS ss -tlnp | grep -E \u0026#39;808[0-3]\u0026#39; # Снаружи curl -I httpsLine truncated
|
||||
@@ -1,890 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru-ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Аудит Leadera — полный отчёт — docs.a2v.space</title>
|
||||
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../../css/custom.css">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="../../">docs.a2v.space</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMain" aria-controls="navbarMain" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<form class="d-flex search-form" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Поиск..." aria-label="Search" id="search-input">
|
||||
<button class="btn btn-outline-light" type="submit">Найти</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline-light d-md-none ms-2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebar" aria-expanded="false" aria-controls="sidebar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div id="search-results" class="search-results" style="display: none;">
|
||||
<div class="search-results-content">
|
||||
<button type="button" class="btn-close float-end" id="close-search"></button>
|
||||
<h5>Результаты поиска</h5>
|
||||
<ul id="results-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
<h6>Фильтры</h6>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Все инструкции</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6 class="mt-3">Теги</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/audit/">audit</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/leadera/">leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/security/">security</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/architecture/">architecture</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/websocket/">websocket</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/realtime/">realtime</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/gantt/">gantt</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/api/">api</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/frontend/">frontend</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/requirements/">requirements</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/angular/">angular</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/go/">go</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/ux/">ux</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/ssh/">ssh</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/tunnel/">tunnel</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/vps/">vps</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/reverse-proxy/">reverse-proxy</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/nginx/">nginx</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/devops/">devops</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/openclaw/">openclaw</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/opencode/">opencode</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/docker/">docker</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
|
||||
|
||||
<main class="col-md-9 col-lg-10 main-content">
|
||||
|
||||
<div class="article-content">
|
||||
<h1>Аудит Leadera — полный отчёт</h1>
|
||||
<div class="source-path mb-3">
|
||||
<small class="text-muted">
|
||||
📁 <code id="source-path" class="user-select-all">~/Projects/docs-a2v/content/guides/leadera-audit.md</code>
|
||||
<button class="btn btn-sm btn-outline-secondary ms-1 py-0 px-1" onclick="navigator.clipboard.writeText('~/Projects/docs-a2v/content/guides\/leadera-audit.md')" title="Копировать путь">
|
||||
📋
|
||||
</button>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="tags mb-3">
|
||||
|
||||
<a href="../../tags/audit/" class="tag">audit</a>
|
||||
|
||||
<a href="../../tags/leadera/" class="tag">leadera</a>
|
||||
|
||||
<a href="../../tags/security/" class="tag">security</a>
|
||||
|
||||
<a href="../../tags/architecture/" class="tag">architecture</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<h1 id="аудит-проекта-leadera">Аудит проекта Leadera</h1>
|
||||
<p><strong>Дата:</strong> 30.03.2026<br>
|
||||
<strong>Версия:</strong> pre-production, миграции 1–5</p>
|
||||
<hr>
|
||||
<h2 id="1-обзор-проекта">1. Обзор проекта</h2>
|
||||
<p>Leadera — SaaS-система управления IT-ландшафтом с диаграммами Ганта.</p>
|
||||
<h3 id="стек">Стек</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Компонент</th>
|
||||
<th>Технологии</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Backend</td>
|
||||
<td>Go 1.21+, Gin, PostgreSQL 16, pgx/v5 + sqlx, JWT (HMAC-SHA256)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Frontend</td>
|
||||
<td>Angular 21.1, Bootstrap 5, NgBootstrap, RxJS, Signals</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Инфраструктура</td>
|
||||
<td>Docker (Colima), pm2, nginx reverse proxy, VPS reg.ru</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="доменная-модель">Доменная модель</h3>
|
||||
<pre tabindex="0"><code>User ──┬── Space (через space_members с role)
|
||||
│
|
||||
└── GanttChart (принадлежит Space)
|
||||
├── GanttSection
|
||||
└── GanttTask
|
||||
</code></pre><p><strong>Роли:</strong> admin, editor, viewer — назначаются <strong>на уровне Space</strong> через таблицу <code>space_members</code>.</p>
|
||||
<h3 id="что-реализовано-endpoints">Что реализовано (endpoints)</h3>
|
||||
<p><strong>Auth (5):</strong> register, login, refresh, change-password, logout<br>
|
||||
<strong>Spaces (9):</strong> CRUD + members CRUD<br>
|
||||
<strong>Gantt Charts (9):</strong> CRUD + archive + members (добавлен, но удалён в миграции 5)<br>
|
||||
<strong>Gantt Sections (5):</strong> CRUD + reorder<br>
|
||||
<strong>Gantt Tasks (4):</strong> CRUD</p>
|
||||
<p><strong>Итого: ~32 endpoint</strong></p>
|
||||
<hr>
|
||||
<h2 id="2-архитектура-и-качество-кода">2. Архитектура и качество кода</h2>
|
||||
<h3 id="21-backend--структура">2.1. Backend — структура</h3>
|
||||
<pre tabindex="0"><code>cmd/server/main.go — точка входа
|
||||
internal/
|
||||
domain/ — модели (User, Space, GanttChart, etc.)
|
||||
dto/ — request/response DTO (auth, space, gantt)
|
||||
handlers/ — HTTP handlers (auth, space, gantt)
|
||||
middleware/ — auth, cors, space, gantt, role, logging, recovery
|
||||
repository/ — слой доступа к данным
|
||||
service/ — бизнес-логика
|
||||
config/ — конфигурация (.env)
|
||||
utils/ — jwt, password, response, validation
|
||||
migrations/ — SQL миграции (1-5)
|
||||
</code></pre><p><strong>Оценка:</strong> ✅ Чистая layered архитектура. Разделение ответственности чёткое.</p>
|
||||
<h3 id="22-frontend--структура">2.2. Frontend — структура</h3>
|
||||
<pre tabindex="0"><code>src/app/
|
||||
core/auth/ — auth service, interceptor
|
||||
core/models/ — app.model, space.model
|
||||
core/services/ — space.service, space-storage.service
|
||||
pages/auth/ — sign-in, sign-up, reset-pw, etc.
|
||||
pages/gantt/ — компоненты, модалки, сервисы, модели
|
||||
pages/dashboards/ — spaces list, dashboard
|
||||
layouts/ — navbar, topbar, main-layout
|
||||
</code></pre><p><strong>Оценка:</strong> ✅ Стандартная Angular-структура. Signals для state management.</p>
|
||||
<h3 id="23-проблемы-кода">2.3. Проблемы кода</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Проблема</th>
|
||||
<th>Критичность</th>
|
||||
<th>Описание</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>C1</td>
|
||||
<td><code>\r</code> в service-файлах</td>
|
||||
<td>Medium</td>
|
||||
<td>gantt_service.go, gantt_section_service.go содержат CRLF</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>C2</td>
|
||||
<td><code>main.go:238</code> — мёртвый код <code>getUserHandler</code></td>
|
||||
<td>Low</td>
|
||||
<td>Неиспользуемая функция</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>C3</td>
|
||||
<td>Swagger не сгенерирован</td>
|
||||
<td>Medium</td>
|
||||
<td>Аннотации есть, но <code>api/</code> директории нет</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>C4</td>
|
||||
<td><code>RegisterProtectedRoutes</code> не вызывается в main.go</td>
|
||||
<td><strong>Critical</strong></td>
|
||||
<td>GET/PUT/DELETE <code>/spaces/:id</code> не зарегистрированы</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<h2 id="3-права-доступа--главный-вопрос">3. Права доступа — главный вопрос</h2>
|
||||
<h3 id="31-текущая-реализация">3.1. Текущая реализация</h3>
|
||||
<p>Права назначаются <strong>исключительно через Space</strong>:</p>
|
||||
<ol>
|
||||
<li>Таблица <code>space_members</code> — связка user ↔ space с role (admin/editor/viewer)</li>
|
||||
<li><code>SpaceMiddleware.RequireSpace()</code> — проверяет членство и записывает <code>ContextSpaceRole</code> в context</li>
|
||||
<li><code>RoleMiddleware.RequireMinRole()</code> — проверяет иерархию ролей (admin > editor > viewer)</li>
|
||||
</ol>
|
||||
<p><strong>Диаграммы Ганта НЕ имеют собственной системы ролей.</strong> Таблица <code>gantt_chart_members</code> была создана (миграция 4) и тут же удалена (миграция 5). Все проверки доступа к диаграммам идут через space membership.</p>
|
||||
<h3 id="32-как-это-работает">3.2. Как это работает</h3>
|
||||
<pre tabindex="0"><code>Запрос → AuthMiddleware (JWT → userID)
|
||||
→ SpaceMiddleware.RequireSpace() (проверяет space_members → ContextSpaceRole)
|
||||
→ GanttMiddleware.RequireGanttChart() (проверяет chart.space_id == spaceID)
|
||||
→ RoleMiddleware.RequireMinRole("editor") (проверяет ContextSpaceRole)
|
||||
</code></pre><p><strong>Это правильно.</strong> Любой член Space с ролью editor+ может создавать/редактировать диаграммы. Viewers — только читать.</p>
|
||||
<h3 id="33--критическая-проблема-frontend-не-соответствует-backend">3.3. 🔴 Критическая проблема: Frontend не соответствует Backend</h3>
|
||||
<p><strong>Frontend <code>MembersModalComponent</code></strong> пытается управлять участниками <strong>диаграммы</strong>:</p>
|
||||
<ul>
|
||||
<li><code>ganttService.addMember(chartId, dto)</code> → <code>/api/v1/spaces/{spaceId}/gantt-charts/{chartId}/members</code></li>
|
||||
<li><code>ganttService.updateMemberRole(chartId, userId, dto)</code> → <code>.../members/{userId}</code></li>
|
||||
<li><code>ganttService.removeMember(chartId, userId)</code> → <code>.../members/{userId}</code></li>
|
||||
</ul>
|
||||
<p><strong>Backend <code>GanttHandler.RegisterRoutes()</code> НЕ регистрирует маршруты для members диаграмм.</strong> После удаления <code>gantt_chart_members</code> (миграция 5) эти endpoints не существуют.</p>
|
||||
<p><strong>Результат:</strong> модальное окно «Участники диаграммы» на фронтенде <strong>не работает</strong> — все вызовы вернут 404.</p>
|
||||
<h3 id="34--проблема-role-fallback">3.4. 🔴 Проблема: Role fallback</h3>
|
||||
<p>В <code>role_middleware.go:28-31</code>:</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">role</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">GetString</span>(<span style="color:#a6e22e">ContextSpaceRole</span>)
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">role</span> <span style="color:#f92672">==</span> <span style="color:#e6db74">""</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">role</span> = <span style="color:#a6e22e">c</span>.<span style="color:#a6e22e">GetString</span>(<span style="color:#a6e22e">ContextRole</span>) <span style="color:#75715e">// fallback на глобальную роль User</span>
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span></code></pre></div><p>Если <code>ContextSpaceRole</code> не установлен, используется <strong>глобальная роль пользователя</strong> (из JWT). Это значит:</p>
|
||||
<ul>
|
||||
<li>Пользователь с <code>role=admin</code> в таблице <code>users</code> получит admin-доступ к <strong>любому</strong> space, даже если он не член этого space</li>
|
||||
<li>Но <code>SpaceMiddleware</code> проверяет членство раньше, так что в цепочке <code>RequireSpace() → RequireMinRole()</code> это не эксплуатируется</li>
|
||||
<li>Однако если <code>RequireMinRole()</code> используется <strong>без</strong> <code>RequireSpace()</code> — это дыра</li>
|
||||
</ul>
|
||||
<p><strong>Рекомендация:</strong> Удалить fallback на <code>ContextRole</code>. Если <code>ContextSpaceRole</code> пуст — отказывать.</p>
|
||||
<h3 id="35-рекомендации-по-правам-доступа">3.5. Рекомендации по правам доступа</h3>
|
||||
<ol>
|
||||
<li><strong>Удалить модалку участников диаграммы</strong> на фронтенде — она мертва</li>
|
||||
<li><strong>Или</strong> добавить раздел «Участники Space» с возможностью управления ролями через <code>/spaces/:space_id/members</code> (эти endpoints работают)</li>
|
||||
<li>Удалить fallback на <code>ContextRole</code> в <code>role_middleware.go</code></li>
|
||||
<li>Добавить проверку: editor не может повысить кого-то до admin (сейчас может — <code>AddMember</code> разрешает editor+ назначать любую роль)</li>
|
||||
</ol>
|
||||
<hr>
|
||||
<h2 id="4-безопасность">4. Безопасность</h2>
|
||||
<h3 id="41-критические">4.1. Критические</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Проблема</th>
|
||||
<th>Файл</th>
|
||||
<th>Описание</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>S1</td>
|
||||
<td>JWT Secret = test</td>
|
||||
<td><code>.env</code></td>
|
||||
<td><code>JWT_SECRET=test_secret_key_change_in_production</code> — <strong>это прод-конфиг!</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>S2</td>
|
||||
<td>CORS: <code>*</code></td>
|
||||
<td><code>.env</code></td>
|
||||
<td><code>CORS_ALLOWED_ORIGINS=*</code> — любой домен может обращаться к API</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>S3</td>
|
||||
<td>Пароль БД в .env</td>
|
||||
<td><code>.env</code></td>
|
||||
<td><code>DB_PASSWORD=popoloka16</code> — creds в открытом виде</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>S4</td>
|
||||
<td>Refresh tokens: SHA256</td>
|
||||
<td><code>utils/password.go</code></td>
|
||||
<td>SHA256 вместо bcrypt для refresh token hash — быстро брутфорсится</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="42-высокие">4.2. Высокие</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Проблема</th>
|
||||
<th>Описание</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>S5</td>
|
||||
<td>Нет rate limiting</td>
|
||||
<td>Brute-force на <code>/auth/login</code> без ограничений</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>S6</td>
|
||||
<td>Нет CSRF protection</td>
|
||||
<td>API принимает POST/PUT/DELETE без CSRF-токена</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>S7</td>
|
||||
<td>Нет CSP заголовков</td>
|
||||
<td>X-Frame-Options, Content-Security-Policy не настроены</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>S8</td>
|
||||
<td>JWT в localStorage</td>
|
||||
<td>XSS может украсть access + refresh tokens</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="43-средние">4.3. Средние</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Проблема</th>
|
||||
<th>Описание</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>S9</td>
|
||||
<td>Нет password policy</td>
|
||||
<td>Минимальные требования к паролю не enforced</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>S10</td>
|
||||
<td>Нет audit log</td>
|
||||
<td>Действия с spaces/charts не логируются для аудита</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>S11</td>
|
||||
<td>Нет блокировки аккаунта</td>
|
||||
<td>Неограниченные попытки входа</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<h2 id="5-инфраструктура">5. Инфраструктура</h2>
|
||||
<h3 id="51-текущее-состояние">5.1. Текущее состояние</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Компонент</th>
|
||||
<th>Статус</th>
|
||||
<th>Примечание</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Go API (pm2: leadera-api)</td>
|
||||
<td>⚠️</td>
|
||||
<td>Порт 8080, хост 0.0.0.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Angular FE (pm2: leadera-fe)</td>
|
||||
<td>⚠️</td>
|
||||
<td>Порт 4200, хост 0.0.0.0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PostgreSQL (Docker)</td>
|
||||
<td>✅</td>
|
||||
<td>Порт 5432, контейнер app-leadera-db</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>VPS reverse proxy</td>
|
||||
<td>⚠️</td>
|
||||
<td>sand.a2v.space → 8082/8083, но 502 если сервисы не запущены</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>SSL</td>
|
||||
<td>✅</td>
|
||||
<td>Let’s Encrypt на VPS</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="52-проблемы">5.2. Проблемы</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Проблема</th>
|
||||
<th>Описание</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>I1</td>
|
||||
<td>Docker для БД, нативно для API</td>
|
||||
<td>Несогласованность — API и FE не в Docker</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>I2</td>
|
||||
<td>Нет health-check в pm2</td>
|
||||
<td>API может упасть без перезапуска</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>I3</td>
|
||||
<td>Нет CI/CD</td>
|
||||
<td>Ручной деплой через <code>make run</code> + pm2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>I4</td>
|
||||
<td>Нет nginx на Mac Studio</td>
|
||||
<td>FE и API торчат наружу напрямую</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<h2 id="6-функциональная-оценка">6. Функциональная оценка</h2>
|
||||
<h3 id="61-что-работает">6.1. Что работает</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Функция</th>
|
||||
<th>Backend</th>
|
||||
<th>Frontend</th>
|
||||
<th>Статус</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Регистрация/логин</td>
|
||||
<td>✅</td>
|
||||
<td>✅</td>
|
||||
<td>Работает</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>JWT auth + refresh</td>
|
||||
<td>✅</td>
|
||||
<td>✅</td>
|
||||
<td>Работает</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Spaces CRUD</td>
|
||||
<td>⚠️</td>
|
||||
<td>✅</td>
|
||||
<td><code>RegisterProtectedRoutes</code> не вызван (C4)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Space members</td>
|
||||
<td>✅</td>
|
||||
<td>✅</td>
|
||||
<td>Работает</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Gantt charts CRUD</td>
|
||||
<td>✅</td>
|
||||
<td>✅</td>
|
||||
<td>Работает</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Gantt sections</td>
|
||||
<td>✅</td>
|
||||
<td>✅</td>
|
||||
<td>Работает</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Gantt tasks</td>
|
||||
<td>✅</td>
|
||||
<td>⚠️</td>
|
||||
<td>Нет UI для задач внутри диаграммы</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Архивация диаграмм</td>
|
||||
<td>✅</td>
|
||||
<td>✅</td>
|
||||
<td>Работает</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Members диаграммы</td>
|
||||
<td>❌</td>
|
||||
<td>✅ (мёртвый UI)</td>
|
||||
<td>Backend endpoints не существуют</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="62-что-не-работает-или-отсутствует">6.2. Что НЕ работает или отсутствует</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Функция</th>
|
||||
<th>Статус</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Визуализация Ганта (таймлайн)</td>
|
||||
<td>❌ Нет компонента отрисовки</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Drag & Drop задач</td>
|
||||
<td>❌</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Real-time collaboration</td>
|
||||
<td>❌</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Уведомления</td>
|
||||
<td>❌</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Поиск</td>
|
||||
<td>❌</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Экспорт (PDF/Excel)</td>
|
||||
<td>❌</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Email verification</td>
|
||||
<td>❌</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Password reset</td>
|
||||
<td>Backend есть, UI есть — не проверено</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="63-оценка-готовности">6.3. Оценка готовности</h3>
|
||||
<p><strong>Система на стадии каркаса.</strong> Backend API функционален для CRUD операций, но:</p>
|
||||
<ul>
|
||||
<li>Визуализация Ганта — основная фича — не реализована (нет canvas/SVG рендеринга)</li>
|
||||
<li>Управление участниками только на уровне Space</li>
|
||||
<li>Нет CI/CD, тестов, мониторинга</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="7-приоритизированный-план-исправлений">7. Приоритизированный план исправлений</h2>
|
||||
<h3 id="неделя-1--критическое-безопасность">Неделя 1 — Критическое (безопасность)</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Приоритет</th>
|
||||
<th>Задача</th>
|
||||
<th>Трудоёмкость</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>P0</td>
|
||||
<td>Заменить JWT secret на криптостойкий</td>
|
||||
<td>1 час</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>P0</td>
|
||||
<td>Настроить CORS (только sand.a2v.space)</td>
|
||||
<td>1 час</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>P0</td>
|
||||
<td>bcrypt для refresh tokens вместо SHA256</td>
|
||||
<td>2 часа</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>P0</td>
|
||||
<td>Удалить fallback на ContextRole в role_middleware</td>
|
||||
<td>30 мин</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>P0</td>
|
||||
<td>Исправить <code>RegisterProtectedRoutes</code> — зарегистрировать маршруты</td>
|
||||
<td>1 час</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>P1</td>
|
||||
<td>Rate limiting на auth endpoints</td>
|
||||
<td>4 часа</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>P1</td>
|
||||
<td>Editor не может назначать admin роль</td>
|
||||
<td>2 часа</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="неделя-2--архитектура-прав">Неделя 2 — Архитектура прав</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Приоритет</th>
|
||||
<th>Задача</th>
|
||||
<th>Трудоёмкость</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>P1</td>
|
||||
<td>Удалить/переписать MembersModalComponent → Space members</td>
|
||||
<td>4 часа</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>P1</td>
|
||||
<td>Добавить страницу управления участниками Space</td>
|
||||
<td>8 часов</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>P2</td>
|
||||
<td>HttpOnly cookies для JWT (вместо localStorage)</td>
|
||||
<td>8 часов</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>P2</td>
|
||||
<td>CSRF protection</td>
|
||||
<td>4 часа</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 id="неделя-3--функциональность">Неделя 3 — Функциональность</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Приоритет</th>
|
||||
<th>Задача</th>
|
||||
<th>Трудоёмкость</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>P2</td>
|
||||
<td>Визуализация Ганта (canvas/d3)</td>
|
||||
<td>40+ часов</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>P2</td>
|
||||
<td>Drag & Drop для задач</td>
|
||||
<td>16 часов</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>P3</td>
|
||||
<td>Тесты (unit + integration)</td>
|
||||
<td>24 часа</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>P3</td>
|
||||
<td>CI/CD pipeline</td>
|
||||
<td>8 часов</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<h2 id="8-резюме">8. Резюме</h2>
|
||||
<h3 id="ключевые-выводы">Ключевые выводы</h3>
|
||||
<ol>
|
||||
<li><strong>Права доступа</strong> — архитектурно верно: роль назначается через Space, не через диаграмму. Но фронтенд ещё не обновлён после удаления <code>gantt_chart_members</code>.</li>
|
||||
<li><strong>Безопасность</strong> — критические уязвимости в конфигурации (JWT secret, CORS, bcrypt). Требуют немедленного исправления до любого публичного деплоя.</li>
|
||||
<li><strong>Функциональность</strong> — CRUD работает, но основная ценность (визуализация Ганта) не реализована.</li>
|
||||
<li><strong>Код</strong> — чистый, хорошая архитектура, но есть мёртвый код и незарегистрированные маршруты.</li>
|
||||
</ol>
|
||||
<h3 id="общая-оценка--pre-alpha">Общая оценка: 🔴 Pre-alpha</h3>
|
||||
<p>Проект готов к внутренней демонстрации CRUD, но не готов к продакшену. Первоочередное — безопасность и исправление мёртвых endpoints.</p>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p class="text-center mb-0">© 2026 docs.a2v.space</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/fuse.js@7.0.0/dist/fuse.min.js"></script>
|
||||
|
||||
|
||||
<script src="../../js/search.js"></script>
|
||||
<script src="../../js/creds.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,621 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru-ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Leadera — Фильтры и реорганизация списка диаграмм Ганта — docs.a2v.space</title>
|
||||
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../../css/custom.css">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="../../">docs.a2v.space</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMain" aria-controls="navbarMain" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<form class="d-flex search-form" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Поиск..." aria-label="Search" id="search-input">
|
||||
<button class="btn btn-outline-light" type="submit">Найти</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline-light d-md-none ms-2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebar" aria-expanded="false" aria-controls="sidebar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div id="search-results" class="search-results" style="display: none;">
|
||||
<div class="search-results-content">
|
||||
<button type="button" class="btn-close float-end" id="close-search"></button>
|
||||
<h5>Результаты поиска</h5>
|
||||
<ul id="results-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
<h6>Фильтры</h6>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Все инструкции</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6 class="mt-3">Теги</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/audit/">audit</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/leadera/">leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/security/">security</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/architecture/">architecture</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/websocket/">websocket</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/realtime/">realtime</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/gantt/">gantt</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/api/">api</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/frontend/">frontend</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/requirements/">requirements</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/angular/">angular</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/go/">go</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/ux/">ux</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/ssh/">ssh</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/tunnel/">tunnel</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/vps/">vps</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/reverse-proxy/">reverse-proxy</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/nginx/">nginx</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/devops/">devops</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/openclaw/">openclaw</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/opencode/">opencode</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/docker/">docker</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
|
||||
|
||||
<main class="col-md-9 col-lg-10 main-content">
|
||||
|
||||
<div class="article-content">
|
||||
<h1>Leadera — Фильтры и реорганизация списка диаграмм Ганта</h1>
|
||||
<div class="source-path mb-3">
|
||||
<small class="text-muted">
|
||||
📁 <code id="source-path" class="user-select-all">~/Projects/docs-a2v/content/guides/leadera-gantt-filters.md</code>
|
||||
<button class="btn btn-sm btn-outline-secondary ms-1 py-0 px-1" onclick="navigator.clipboard.writeText('~/Projects/docs-a2v/content/guides\/leadera-gantt-filters.md')" title="Копировать путь">
|
||||
📋
|
||||
</button>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="tags mb-3">
|
||||
|
||||
<a href="../../tags/leadera/" class="tag">leadera</a>
|
||||
|
||||
<a href="../../tags/gantt/" class="tag">gantt</a>
|
||||
|
||||
<a href="../../tags/api/" class="tag">api</a>
|
||||
|
||||
<a href="../../tags/frontend/" class="tag">frontend</a>
|
||||
|
||||
<a href="../../tags/requirements/" class="tag">requirements</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<h1 id="фильтры-и-реорганизация-списка-диаграмм-ганта">Фильтры и реорганизация списка диаграмм Ганта</h1>
|
||||
<p><strong>Дата:</strong> 07.04.2026
|
||||
<strong>Статус:</strong> Требования
|
||||
<strong>Проект:</strong> Leadera (dev — sand.a2v.space)</p>
|
||||
<hr>
|
||||
<h2 id="1-постановка-задачи">1. Постановка задачи</h2>
|
||||
<p>Список диаграмм Ганта должен показывать <strong>все доступные пользователю диаграммы</strong> из всех пространств (spaces), с возможностью фильтрации и поиска. Сейчас список привязан к одному пространству — нужно убрать эту зависимость и добавить удобный toolbar.</p>
|
||||
<hr>
|
||||
<h2 id="2-изменения-api">2. Изменения API</h2>
|
||||
<h3 id="21-новый-endpoint-списка-диаграмм">2.1. Новый endpoint списка диаграмм</h3>
|
||||
<p><strong>Было:</strong></p>
|
||||
<pre tabindex="0"><code>GET /api/v1/spaces/{spaceId}/gantt-charts?page=1&per_page=10
|
||||
</code></pre><p><strong>Стало:</strong></p>
|
||||
<pre tabindex="0"><code>GET /api/v1/gantt-charts?page=1&per_page=10
|
||||
</code></pre><p>Пользователь видит все диаграммы из <strong>всех пространств</strong>, где он является подтверждённым участником (статус <code>accepted</code>).</p>
|
||||
<h3 id="22-параметры-фильтрации-и-поиска">2.2. Параметры фильтрации и поиска</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Параметр</th>
|
||||
<th>Тип</th>
|
||||
<th>Описание</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>page</code></td>
|
||||
<td>int</td>
|
||||
<td>Номер страницы (default: 1)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>per_page</code></td>
|
||||
<td>int</td>
|
||||
<td>Записей на странице (default: 10)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>space_id</code></td>
|
||||
<td>UUID (optional)</td>
|
||||
<td>Фильтр по конкретному пространству</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>created_by</code></td>
|
||||
<td>UUID (optional)</td>
|
||||
<td>Фильтр по создателю диаграммы</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>search</code></td>
|
||||
<td>string (optional)</td>
|
||||
<td>Поиск по назвению и описанию (ILIKE, case-insensitive)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>sort</code></td>
|
||||
<td>string (optional)</td>
|
||||
<td>Сортировка: <code>created_at</code>, <code>updated_at</code>, <code>title</code> (default: <code>updated_at</code>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>order</code></td>
|
||||
<td>string (optional)</td>
|
||||
<td><code>asc</code> / <code>desc</code> (default: <code>desc</code>)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><strong>Примеры:</strong></p>
|
||||
<pre tabindex="0"><code>GET /api/v1/gantt-charts?search=миграция
|
||||
GET /api/v1/gantt-charts?space_id=0b334ded-...&created_by=a1b2c3d4-...
|
||||
GET /api/v1/gantt-charts?page=2&per_page=20&sort=title&order=asc
|
||||
</code></pre><h3 id="23-изменение-структуры-ответа">2.3. Изменение структуры ответа</h3>
|
||||
<p>Добавить поле <code>space</code> в каждую диаграмму:</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"data"</span>: [
|
||||
</span></span><span style="display:flex;"><span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"id"</span>: <span style="color:#e6db74">"uuid"</span>,
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"title"</span>: <span style="color:#e6db74">"Миграция backend"</span>,
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"description"</span>: <span style="color:#e6db74">"..."</span>,
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"created_by"</span>: <span style="color:#e6db74">"uuid"</span>,
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"created_by_name"</span>: <span style="color:#e6db74">"Антон Агальцов"</span>,
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"created_at"</span>: <span style="color:#e6db74">"2026-04-01T10:00:00Z"</span>,
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"updated_at"</span>: <span style="color:#e6db74">"2026-04-07T09:00:00Z"</span>,
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"space"</span>: {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"id"</span>: <span style="color:#e6db74">"uuid"</span>,
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"name"</span>: <span style="color:#e6db74">"IT Ландшафт"</span>,
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"color"</span>: <span style="color:#e6db74">"#c4623a"</span>
|
||||
</span></span><span style="display:flex;"><span> },
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"is_archived"</span>: <span style="color:#66d9ef">false</span>
|
||||
</span></span><span style="display:flex;"><span> }
|
||||
</span></span><span style="display:flex;"><span> ],
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"pagination"</span>: {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"page"</span>: <span style="color:#ae81ff">1</span>,
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"per_page"</span>: <span style="color:#ae81ff">10</span>,
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"total"</span>: <span style="color:#ae81ff">42</span>,
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"total_pages"</span>: <span style="color:#ae81ff">5</span>
|
||||
</span></span><span style="display:flex;"><span> }
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span></code></pre></div><h3 id="24-endpoint-для-списка-фильтров">2.4. Endpoint для списка фильтров</h3>
|
||||
<p>Вспомогательный endpoint для заполнения dropdown-фильтров:</p>
|
||||
<pre tabindex="0"><code>GET /api/v1/gantt-charts/filters
|
||||
</code></pre><p><strong>Ответ:</strong></p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"spaces"</span>: [
|
||||
</span></span><span style="display:flex;"><span> { <span style="color:#f92672">"id"</span>: <span style="color:#e6db74">"uuid"</span>, <span style="color:#f92672">"name"</span>: <span style="color:#e6db74">"IT Ландшафт"</span>, <span style="color:#f92672">"color"</span>: <span style="color:#e6db74">"#c4623a"</span> },
|
||||
</span></span><span style="display:flex;"><span> { <span style="color:#f92672">"id"</span>: <span style="color:#e6db74">"uuid"</span>, <span style="color:#f92672">"name"</span>: <span style="color:#e6db74">"DevOps"</span>, <span style="color:#f92672">"color"</span>: <span style="color:#e6db74">"#27ae60"</span> }
|
||||
</span></span><span style="display:flex;"><span> ],
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"creators"</span>: [
|
||||
</span></span><span style="display:flex;"><span> { <span style="color:#f92672">"id"</span>: <span style="color:#e6db74">"uuid"</span>, <span style="color:#f92672">"display_name"</span>: <span style="color:#e6db74">"Антон Агальцов"</span> },
|
||||
</span></span><span style="display:flex;"><span> { <span style="color:#f92672">"id"</span>: <span style="color:#e6db74">"uuid"</span>, <span style="color:#f92672">"display_name"</span>: <span style="color:#e6db74">"Иван Петров"</span> }
|
||||
</span></span><span style="display:flex;"><span> ]
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span></code></pre></div><p><strong>Логика <code>creators</code>:</strong> уникальные создатели всех диаграмм, доступных текущему пользователю. Получается через DISTINCT-запрос по <code>created_by</code> с JOIN на users.</p>
|
||||
<h3 id="25-backend--изменения-в-go">2.5. Backend — изменения в Go</h3>
|
||||
<p><strong>Файлы для изменения:</strong></p>
|
||||
<h4 id="internalhandlersgantt_handlergo"><code>internal/handlers/gantt_handler.go</code></h4>
|
||||
<ul>
|
||||
<li>Добавить новый handler <code>ListAllGanttCharts</code> — без привязки к space</li>
|
||||
<li>Зарегистрировать маршрут <code>GET /gantt-charts</code> (без <code>/spaces/:id/</code>)</li>
|
||||
<li>Парсить query-параметры: <code>space_id</code>, <code>created_by</code>, <code>search</code>, <code>sort</code>, <code>order</code></li>
|
||||
<li>Старый маршрут <code>GET /spaces/:id/gantt-charts</code> — оставить для обратной совместимости (или удалить, если не используется)</li>
|
||||
</ul>
|
||||
<h4 id="internalservicegantt_servicego"><code>internal/service/gantt_service.go</code></h4>
|
||||
<ul>
|
||||
<li>Новый метод <code>ListAll(ctx, userId, filters)</code> — запрашивает все spaces пользователя (через <code>space_member</code> с <code>status=accepted</code>), затем выбирает все gantt_charts из этих spaces с фильтрами</li>
|
||||
</ul>
|
||||
<h4 id="internalrepositorygantt_repositorygo"><code>internal/repository/gantt_repository.go</code></h4>
|
||||
<ul>
|
||||
<li>Новый метод с динамическим SQL:
|
||||
<ul>
|
||||
<li>Базовый JOIN: <code>gantt_charts</code> → <code>spaces</code> → <code>space_members</code></li>
|
||||
<li>WHERE: <code>space_members.user_id = $1 AND space_members.status = 'accepted'</code></li>
|
||||
<li>Опциональные фильтры: <code>AND space_id = $N</code>, <code>AND created_by = $N</code>, <code>AND (title ILIKE '%' || $N || '%' OR description ILIKE '%' || $N || '%')</code></li>
|
||||
<li>Возвращать поле <code>space</code> (id, name, color) через JOIN</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h4 id="internaldtogantt"><code>internal/dto/gantt/</code></h4>
|
||||
<ul>
|
||||
<li>Новые DTO: <code>GanttChartListFilters</code>, <code>GanttChartWithSpaceResponse</code>, <code>GanttFiltersResponse</code></li>
|
||||
</ul>
|
||||
<p><strong>SQL (пример):</strong></p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">SELECT</span> gc.<span style="color:#f92672">*</span>, s.id <span style="color:#66d9ef">as</span> space_id, s.name <span style="color:#66d9ef">as</span> space_name, s.color <span style="color:#66d9ef">as</span> space_color,
|
||||
</span></span><span style="display:flex;"><span> u.display_name <span style="color:#66d9ef">as</span> created_by_name
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">FROM</span> gantt_charts gc
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">JOIN</span> spaces s <span style="color:#66d9ef">ON</span> s.id <span style="color:#f92672">=</span> gc.space_id
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">JOIN</span> space_members sm <span style="color:#66d9ef">ON</span> sm.space_id <span style="color:#f92672">=</span> s.id
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">LEFT</span> <span style="color:#66d9ef">JOIN</span> users u <span style="color:#66d9ef">ON</span> u.id <span style="color:#f92672">=</span> gc.created_by
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">WHERE</span> sm.user_id <span style="color:#f92672">=</span> <span style="color:#960050;background-color:#1e0010">$</span><span style="color:#ae81ff">1</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">AND</span> sm.status <span style="color:#f92672">=</span> <span style="color:#e6db74">'accepted'</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">AND</span> (<span style="color:#960050;background-color:#1e0010">$</span><span style="color:#ae81ff">2</span>::uuid <span style="color:#66d9ef">IS</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">OR</span> gc.space_id <span style="color:#f92672">=</span> <span style="color:#960050;background-color:#1e0010">$</span><span style="color:#ae81ff">2</span>)
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">AND</span> (<span style="color:#960050;background-color:#1e0010">$</span><span style="color:#ae81ff">3</span>::uuid <span style="color:#66d9ef">IS</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">OR</span> gc.created_by <span style="color:#f92672">=</span> <span style="color:#960050;background-color:#1e0010">$</span><span style="color:#ae81ff">3</span>)
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">AND</span> (<span style="color:#960050;background-color:#1e0010">$</span><span style="color:#ae81ff">4</span>::text <span style="color:#66d9ef">IS</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">OR</span> gc.title <span style="color:#66d9ef">ILIKE</span> <span style="color:#e6db74">'%'</span> <span style="color:#f92672">||</span> <span style="color:#960050;background-color:#1e0010">$</span><span style="color:#ae81ff">4</span> <span style="color:#f92672">||</span> <span style="color:#e6db74">'%'</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">OR</span> gc.description <span style="color:#66d9ef">ILIKE</span> <span style="color:#e6db74">'%'</span> <span style="color:#f92672">||</span> <span style="color:#960050;background-color:#1e0010">$</span><span style="color:#ae81ff">4</span> <span style="color:#f92672">||</span> <span style="color:#e6db74">'%'</span>)
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">ORDER</span> <span style="color:#66d9ef">BY</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">CASE</span> <span style="color:#66d9ef">WHEN</span> <span style="color:#960050;background-color:#1e0010">$</span><span style="color:#ae81ff">5</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">'title'</span> <span style="color:#66d9ef">THEN</span> gc.title <span style="color:#66d9ef">END</span>,
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">CASE</span> <span style="color:#66d9ef">WHEN</span> <span style="color:#960050;background-color:#1e0010">$</span><span style="color:#ae81ff">5</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">'created_at'</span> <span style="color:#66d9ef">THEN</span> gc.created_at::text <span style="color:#66d9ef">END</span>,
|
||||
</span></span><span style="display:flex;"><span> gc.updated_at <span style="color:#66d9ef">DESC</span>
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">LIMIT</span> <span style="color:#960050;background-color:#1e0010">$</span><span style="color:#ae81ff">6</span> <span style="color:#66d9ef">OFFSET</span> <span style="color:#960050;background-color:#1e0010">$</span><span style="color:#ae81ff">7</span>;
|
||||
</span></span></code></pre></div><hr>
|
||||
<h2 id="3-изменения-frontend-angular">3. Изменения Frontend (Angular)</h2>
|
||||
<h3 id="31-toolbar-фильтров">3.1. Toolbar фильтров</h3>
|
||||
<p>Добавить toolbar над списком диаграмм:</p>
|
||||
<pre tabindex="0"><code>┌──────────────────────────────────────────────────────────────────┐
|
||||
│ [🔍 Поиск... ] [▼ Пространство] │
|
||||
│ [▼ Создатель] [▼ Сортировка] [Сбросить фильтры] │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
</code></pre><p><strong>Компоненты:</strong></p>
|
||||
<ul>
|
||||
<li><strong>Строка поиска</strong> — input с debounce (300ms), поиск по назвению и описанию</li>
|
||||
<li><strong>Dropdown «Пространство»</strong> — список из <code>/gantt-charts/filters</code>, с цветовой меткой</li>
|
||||
<li><strong>Dropdown «Создатель»</strong> — список уникальных создателей</li>
|
||||
<li><strong>Dropdown «Сортировка»</strong> — по дате обновления, дате создания, названию</li>
|
||||
<li><strong>Кнопка «Сбросить»</strong> — очищает все фильтры</li>
|
||||
</ul>
|
||||
<p><strong>Состояние фильтров в URL</strong> (query params):</p>
|
||||
<pre tabindex="0"><code>/gantt?search=миграция&space_id=xxx&sort=updated_at
|
||||
</code></pre><p>Это позволяет делиться ссылкой с уже настроенными фильтрами.</p>
|
||||
<h3 id="32-карточка-диаграммы--бейдж-пространства">3.2. Карточка диаграммы — бейдж пространства</h3>
|
||||
<p>На каждой карточке добавить бейдж с названием пространства:</p>
|
||||
<pre tabindex="0"><code>┌─────────────────────────────────────┐
|
||||
│ 🟠 IT Ландшафт │ ← бейдж с цветом пространства
|
||||
│ Миграция backend │
|
||||
│ Перевод PHP→Go, этап 2 │
|
||||
│ Создал: Антон А. · 07.04.2026 │
|
||||
└─────────────────────────────────────┘
|
||||
</code></pre><h3 id="33-форма-создания-диаграммы--выбор-пространства">3.3. Форма создания диаграммы — выбор пространства</h3>
|
||||
<p>В модальном окне создания:</p>
|
||||
<ul>
|
||||
<li>Dropdown со списком пространств, где пользователь имеет роль <strong>editor</strong> или выше</li>
|
||||
<li>Валидация: если пользователь не editor ни в одном пространстве — показать сообщение «Нет доступных пространств для создания»</li>
|
||||
</ul>
|
||||
<p><strong>Запрос доступных пространств:</strong> переиспользовать существующий <code>GET /api/v1/spaces</code> с фильтрацией на клиенте по <code>role ∈ [owner, admin, editor]</code>, либо добавить параметр <code>?min_role=editor</code> на backend.</p>
|
||||
<h3 id="34-изменение-routing">3.4. Изменение routing</h3>
|
||||
<p><strong>Было:</strong></p>
|
||||
<pre tabindex="0"><code>/gantt → список гантов текущего пространства
|
||||
</code></pre><p><strong>Стало:</strong></p>
|
||||
<pre tabindex="0"><code>/gantt → список всех доступных гантов (из всех пространств)
|
||||
</code></pre><p>Убрать зависимость от <code>SpaceStorageService.getSelectedSpaceId()</code> в списке гантов.</p>
|
||||
<h3 id="35-frontend--файлы-для-изменения">3.5. Frontend — файлы для изменения</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Файл</th>
|
||||
<th>Изменение</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>pages/gantt/services/gantt.service.ts</code></td>
|
||||
<td>Новый метод <code>listAll(filters)</code>, <code>getFilters()</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>pages/gantt/gantt.component.ts</code></td>
|
||||
<td>Подключить toolbar, парсинг URL-параметров</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>pages/gantt/gantt.component.html</code></td>
|
||||
<td>Добавить toolbar, бейджи на карточках</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>pages/gantt/components/gantt-card/</code></td>
|
||||
<td>Добавить бейдж пространства</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>pages/gantt/components/create-chart-modal/</code></td>
|
||||
<td>Dropdown выбора пространства</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>core/models/</code></td>
|
||||
<td>Обновить модели: добавить <code>space</code> в GanttChart</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<h2 id="4-обратная-совместимость">4. Обратная совместимость</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Endpoint</th>
|
||||
<th>Статус</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>GET /api/v1/spaces/{id}/gantt-charts</code></td>
|
||||
<td>Сохранить (для детального view внутри пространства)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>GET /api/v1/gantt-charts</code></td>
|
||||
<td><strong>Новый</strong> — глобальный список</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>GET /api/v1/gantt-charts/filters</code></td>
|
||||
<td><strong>Новый</strong> — данные для dropdown’ов</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>POST /api/v1/spaces/{id}/gantt-charts</code></td>
|
||||
<td>Сохранить (создание в контексте пространства)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Все CRUD для sections/tasks</td>
|
||||
<td>Без изменений</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<h2 id="5-критерии-приёмки">5. Критерии приёмки</h2>
|
||||
<ul>
|
||||
<li><input disabled="" type="checkbox"> Endpoint <code>GET /api/v1/gantt-charts</code> возвращает все доступные диаграммы</li>
|
||||
<li><input disabled="" type="checkbox"> Фильтр по <code>space_id</code> — показывает только диаграммы конкретного пространства</li>
|
||||
<li><input disabled="" type="checkbox"> Фильтр по <code>created_by</code> — показывает только диаграммы выбранного создателя</li>
|
||||
<li><input disabled="" type="checkbox"> Поиск <code>search</code> — ищет по title и description (case-insensitive)</li>
|
||||
<li><input disabled="" type="checkbox"> Dropdown фильтров заполняется реальными данными из <code>/filters</code></li>
|
||||
<li><input disabled="" type="checkbox"> Карточка диаграммы показывает бейдж пространства с цветом</li>
|
||||
<li><input disabled="" type="checkbox"> Форма создания предлагает только пространства с ролью ≥ editor</li>
|
||||
<li><input disabled="" type="checkbox"> Состояние фильтров сохраняется в URL (можно поделиться ссылкой)</li>
|
||||
<li><input disabled="" type="checkbox"> Старые endpoints (<code>/spaces/{id}/gantt-charts</code>) продолжают работать</li>
|
||||
<li><input disabled="" type="checkbox"> Swagger документация обновлена</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="6-порядок-реализации">6. Порядок реализации</h2>
|
||||
<ol>
|
||||
<li>Backend: DTO для новых запросов/ответов</li>
|
||||
<li>Backend: Repository — SQL-запрос с фильтрами + JOIN на spaces</li>
|
||||
<li>Backend: Service — <code>ListAll</code>, <code>GetFilters</code></li>
|
||||
<li>Backend: Handler — <code>ListAllGanttCharts</code>, <code>GetGanttFilters</code>, регистрация маршрутов</li>
|
||||
<li>Backend: Swagger — обновить документацию</li>
|
||||
<li>Frontend: Модели — обновить GanttChart, добавить Space в ответ</li>
|
||||
<li>Frontend: Service — <code>listAll()</code>, <code>getFilters()</code></li>
|
||||
<li>Frontend: Toolbar компонент — поиск + dropdown’ы</li>
|
||||
<li>Frontend: Карточка — бейдж пространства</li>
|
||||
<li>Frontend: Модальное окно создания — dropdown пространства</li>
|
||||
<li>Frontend: URL-параметры — синхронизация фильтров с router</li>
|
||||
<li>Тестирование</li>
|
||||
</ol>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p class="text-center mb-0">© 2026 docs.a2v.space</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/fuse.js@7.0.0/dist/fuse.min.js"></script>
|
||||
|
||||
|
||||
<script src="../../js/search.js"></script>
|
||||
<script src="../../js/creds.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,776 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru-ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Leadera: Вехи (Milestones) на диаграмме Ганта — docs.a2v.space</title>
|
||||
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../../css/custom.css">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="../../">docs.a2v.space</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMain" aria-controls="navbarMain" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<form class="d-flex search-form" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Поиск..." aria-label="Search" id="search-input">
|
||||
<button class="btn btn-outline-light" type="submit">Найти</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline-light d-md-none ms-2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebar" aria-expanded="false" aria-controls="sidebar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div id="search-results" class="search-results" style="display: none;">
|
||||
<div class="search-results-content">
|
||||
<button type="button" class="btn-close float-end" id="close-search"></button>
|
||||
<h5>Результаты поиска</h5>
|
||||
<ul id="results-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
<h6>Фильтры</h6>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Все инструкции</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6 class="mt-3">Теги</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/audit/">audit</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/leadera/">leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/security/">security</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/architecture/">architecture</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/websocket/">websocket</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/realtime/">realtime</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/gantt/">gantt</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/api/">api</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/frontend/">frontend</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/requirements/">requirements</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/angular/">angular</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/go/">go</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/ux/">ux</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/ssh/">ssh</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/tunnel/">tunnel</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/vps/">vps</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/reverse-proxy/">reverse-proxy</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/nginx/">nginx</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/devops/">devops</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/openclaw/">openclaw</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/opencode/">opencode</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/docker/">docker</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
|
||||
|
||||
<main class="col-md-9 col-lg-10 main-content">
|
||||
|
||||
<div class="article-content">
|
||||
<h1>Leadera: Вехи (Milestones) на диаграмме Ганта</h1>
|
||||
<div class="source-path mb-3">
|
||||
<small class="text-muted">
|
||||
📁 <code id="source-path" class="user-select-all">~/Projects/docs-a2v/content/guides/leadera-milestones.md</code>
|
||||
<button class="btn btn-sm btn-outline-secondary ms-1 py-0 px-1" onclick="navigator.clipboard.writeText('~/Projects/docs-a2v/content/guides\/leadera-milestones.md')" title="Копировать путь">
|
||||
📋
|
||||
</button>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="tags mb-3">
|
||||
|
||||
<a href="../../tags/leadera/" class="tag">leadera</a>
|
||||
|
||||
<a href="../../tags/gantt/" class="tag">gantt</a>
|
||||
|
||||
<a href="../../tags/angular/" class="tag">angular</a>
|
||||
|
||||
<a href="../../tags/go/" class="tag">go</a>
|
||||
|
||||
<a href="../../tags/api/" class="tag">api</a>
|
||||
|
||||
<a href="../../tags/ux/" class="tag">ux</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<h2 id="бизнес-требования">Бизнес-требования</h2>
|
||||
<h3 id="br-1-сущность-веха-milestone">BR-1: Сущность «Веха» (Milestone)</h3>
|
||||
<p>Веха — это маркер на таймлайне диаграммы Ганта, обозначающий важную дату в проекте. В отличие от задач, веха не имеет длительности и привязана к конкретной дате.</p>
|
||||
<p><strong>Поля вехи:</strong></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Поле</th>
|
||||
<th>Тип</th>
|
||||
<th>Описание</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>id</td>
|
||||
<td>UUID</td>
|
||||
<td>Идентификатор</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>gantt_chart_id</td>
|
||||
<td>UUID</td>
|
||||
<td>Принадлежность к диаграмме (не к секции!)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>title</td>
|
||||
<td>string</td>
|
||||
<td>Название вехи (обязательное, 1–255 символов)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>date</td>
|
||||
<td>date</td>
|
||||
<td>Дата вехи (без времени, обязательная)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>color</td>
|
||||
<td>string</td>
|
||||
<td>Цвет маркера (HEX, по умолчанию <code>#dc3545</code> — красный)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>created_by</td>
|
||||
<td>UUID</td>
|
||||
<td>Кто создал (не отображается в UI, хранится для аудита)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>updated_by</td>
|
||||
<td>UUID</td>
|
||||
<td>Кто обновил (не отображается в UI)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>created_at</td>
|
||||
<td>timestamp</td>
|
||||
<td>Дата создания</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>updated_at</td>
|
||||
<td>timestamp</td>
|
||||
<td>Дата обновления</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>deleted_at</td>
|
||||
<td>timestamp</td>
|
||||
<td>Soft delete</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><strong>Права доступа:</strong> Создание, редактирование и удаление вех доступно участникам с правами выше «только чтение» (аналогично задачам).</p>
|
||||
<p><strong>Особенности:</strong></p>
|
||||
<ul>
|
||||
<li>Вех может быть несколько в проекте</li>
|
||||
<li>Несколько вех могут иметь одну и ту же дату</li>
|
||||
<li>Вехи сортируются строго по дате (по возрастанию)</li>
|
||||
<li>Вехи не привязаны к секциям — они отображаются <strong>сквозь все секции</strong></li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h3 id="br-2-управление-вехами-модальное-окно">BR-2: Управление вехами (модальное окно)</h3>
|
||||
<p><strong>Вызов:</strong> Кнопка «Вехи» рядом с кнопкой «Создать секцию» на странице диаграммы.</p>
|
||||
<p><strong>Модальное окно содержит:</strong></p>
|
||||
<ul>
|
||||
<li>Заголовок: «Вехи проекта»</li>
|
||||
<li>Список всех вех текущей диаграммы (сортировка по дате по возрастанию)</li>
|
||||
<li>Каждая веха в списке показывает: цвет (индикатор), название, дату, кнопки «Редактировать» и «Удалить»</li>
|
||||
<li>Кнопка «Добавить веху»</li>
|
||||
</ul>
|
||||
<p><strong>Форма добавления/редактирования вехи:</strong></p>
|
||||
<ul>
|
||||
<li>Название — текстовый ввод (обязательное, 1–255 символов)</li>
|
||||
<li>Дата — datepicker (обязательное)</li>
|
||||
<li>Цвет — выбор цвета (по аналогии с палитрой цветов в задачах)</li>
|
||||
<li>По умолчанию цвет = красный (<code>#dc3545</code>)</li>
|
||||
</ul>
|
||||
<p><strong>Удаление:</strong> с подтверждением (confirm dialog).</p>
|
||||
<hr>
|
||||
<h3 id="br-3-отображение-вех-на-таймлайне">BR-3: Отображение вех на таймлайне</h3>
|
||||
<p><strong>Визуальное представление:</strong></p>
|
||||
<ul>
|
||||
<li><strong>Маркер-флажок</strong> (flag marker) на шкале таймлайна</li>
|
||||
<li>От маркера вниз идёт <strong>пунктирная вертикальная линия</strong> через все секции (на всю высоту диаграммы)</li>
|
||||
<li>Цвет линии и маркера = цвет вехи</li>
|
||||
</ul>
|
||||
<p><strong>Позиционирование:</strong></p>
|
||||
<ul>
|
||||
<li>Веха ставится ровно на свою дату на шкале времени</li>
|
||||
<li>Если дата вехи выходит за рамки текущего диапазона задач — таймлайн <strong>расширяется</strong> (аналогично задачам)</li>
|
||||
<li>При изменении масштаба (days/weeks/months/quarters) позиция вехи пересчитывается</li>
|
||||
</ul>
|
||||
<p><strong>Tooltip при наведении:</strong></p>
|
||||
<ul>
|
||||
<li>При наведении на маркер/линию — показывается tooltip с названием вехи</li>
|
||||
<li>Если на одну дату приходится несколько вех — в tooltip показываются <strong>все</strong> вехи этой даты с их названиями (каждая с индикацией своего цвета)</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="технические-требования">Технические требования</h2>
|
||||
<h3 id="база-данных">База данных</h3>
|
||||
<h4 id="новая-таблица-gantt_milestones">Новая таблица: gantt_milestones</h4>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">CREATE</span> <span style="color:#66d9ef">TABLE</span> gantt_milestones (
|
||||
</span></span><span style="display:flex;"><span> id UUID <span style="color:#66d9ef">PRIMARY</span> <span style="color:#66d9ef">KEY</span> <span style="color:#66d9ef">DEFAULT</span> gen_random_uuid(),
|
||||
</span></span><span style="display:flex;"><span> gantt_chart_id UUID <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">REFERENCES</span> gantt_charts(id) <span style="color:#66d9ef">ON</span> <span style="color:#66d9ef">DELETE</span> <span style="color:#66d9ef">CASCADE</span>,
|
||||
</span></span><span style="display:flex;"><span> title VARCHAR(<span style="color:#ae81ff">255</span>) <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span>,
|
||||
</span></span><span style="display:flex;"><span> date DATE <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span>,
|
||||
</span></span><span style="display:flex;"><span> color VARCHAR(<span style="color:#ae81ff">20</span>) <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">DEFAULT</span> <span style="color:#e6db74">'#dc3545'</span>,
|
||||
</span></span><span style="display:flex;"><span> created_by UUID <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">REFERENCES</span> users(id),
|
||||
</span></span><span style="display:flex;"><span> updated_by UUID <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">REFERENCES</span> users(id),
|
||||
</span></span><span style="display:flex;"><span> created_at <span style="color:#66d9ef">TIMESTAMP</span> <span style="color:#66d9ef">WITH</span> TIME <span style="color:#66d9ef">ZONE</span> <span style="color:#66d9ef">DEFAULT</span> NOW(),
|
||||
</span></span><span style="display:flex;"><span> updated_at <span style="color:#66d9ef">TIMESTAMP</span> <span style="color:#66d9ef">WITH</span> TIME <span style="color:#66d9ef">ZONE</span> <span style="color:#66d9ef">DEFAULT</span> NOW(),
|
||||
</span></span><span style="display:flex;"><span> deleted_at <span style="color:#66d9ef">TIMESTAMP</span> <span style="color:#66d9ef">WITH</span> TIME <span style="color:#66d9ef">ZONE</span>
|
||||
</span></span><span style="display:flex;"><span>);
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">CREATE</span> <span style="color:#66d9ef">INDEX</span> idx_gantt_milestones_chart <span style="color:#66d9ef">ON</span> gantt_milestones(gantt_chart_id);
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">CREATE</span> <span style="color:#66d9ef">INDEX</span> idx_gantt_milestones_date <span style="color:#66d9ef">ON</span> gantt_milestones(date);
|
||||
</span></span></code></pre></div><hr>
|
||||
<h3 id="backend-go-api">Backend (Go API)</h3>
|
||||
<h4 id="этап-1-миграция--домен">Этап 1: Миграция + Домен</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>Добавь в проект новую таблицу <code>gantt_milestones</code>.</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Создай миграцию в <code>cmd/migrate/</code>:</p>
|
||||
<ul>
|
||||
<li>Новая таблица <code>gantt_milestones</code> с полями: id (UUID PK), gantt_chart_id (UUID FK → gantt_charts, ON DELETE CASCADE), title (VARCHAR 255 NOT NULL), date (DATE NOT NULL), color (VARCHAR 20 NOT NULL DEFAULT ‘#dc3545’), created_by (UUID FK → users), updated_by (UUID FK → users), created_at, updated_at, deleted_at. Индексы по gantt_chart_id и date.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Создай доменную модель <code>GanttMilestone</code> в <code>internal/domain/gantt.go</code>:</p>
|
||||
</li>
|
||||
</ol>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">GanttMilestone</span> <span style="color:#66d9ef">struct</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">ID</span> <span style="color:#a6e22e">UUID</span> <span style="color:#e6db74">`json:"id"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">GanttChartID</span> <span style="color:#a6e22e">UUID</span> <span style="color:#e6db74">`json:"gantt_chart_id"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Title</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:"title"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Date</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span> <span style="color:#e6db74">`json:"date"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Color</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:"color"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">CreatedBy</span> <span style="color:#a6e22e">UUID</span> <span style="color:#e6db74">`json:"created_by"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">UpdatedBy</span> <span style="color:#a6e22e">UUID</span> <span style="color:#e6db74">`json:"updated_by"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">CreatedAt</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span> <span style="color:#e6db74">`json:"created_at"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">UpdatedAt</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span> <span style="color:#e6db74">`json:"updated_at"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">DeletedAt</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span> <span style="color:#e6db74">`json:"deleted_at,omitempty"`</span>
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span></code></pre></div></blockquote>
|
||||
<h4 id="этап-2-crud-для-ganttmilestones">Этап 2: CRUD для GanttMilestones</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>Реализуй полный CRUD для сущности <code>GanttMilestone</code> по аналогии с существующими handlers/services/repositories в проекте.</p>
|
||||
<p><strong>Роуты:</strong></p>
|
||||
<ul>
|
||||
<li><code>GET /api/gantt-charts/:chartId/milestones</code> — список вех диаграммы</li>
|
||||
<li><code>POST /api/gantt-charts/:chartId/milestones</code> — создать веху</li>
|
||||
<li><code>PUT /api/gantt-charts/:chartId/milestones/:milestoneId</code> — обновить веху</li>
|
||||
<li><code>DELETE /api/gantt-charts/:chartId/milestones/:milestoneId</code> — удалить веху (soft delete)</li>
|
||||
</ul>
|
||||
<p><strong>Repository:</strong> <code>internal/repository/gantt_milestone_repository.go</code></p>
|
||||
<ul>
|
||||
<li><code>GetByChartID(ctx, chartID) ([]domain.GanttMilestone, error)</code> — все неудалённые вехи диаграммы, отсортированные по date ASC</li>
|
||||
<li><code>GetByID(ctx, id) (*domain.GanttMilestone, error)</code></li>
|
||||
<li><code>Create(ctx, milestone) error</code></li>
|
||||
<li><code>Update(ctx, milestone) error</code></li>
|
||||
<li><code>Delete(ctx, id) error</code> — soft delete (set deleted_at)</li>
|
||||
<li>Все запросы — raw SQL через pgx, как в остальных репозиториях проекта</li>
|
||||
</ul>
|
||||
<p><strong>Service:</strong> <code>internal/service/gantt_milestone_service.go</code></p>
|
||||
<ul>
|
||||
<li>Валидация: title (1–255 символов), date (обязательное), color (валидный HEX, default ‘#dc3545’)</li>
|
||||
<li>Проверка прав доступа: пользователь должен иметь права выше «только чтение» в пространстве (space) диаграммы</li>
|
||||
<li>Проверка что chartId существует и не архивирован</li>
|
||||
</ul>
|
||||
<p><strong>Handler:</strong> <code>internal/handler/gantt_milestone_handler.go</code></p>
|
||||
<ul>
|
||||
<li>Swagger-аннотации для всех эндпоинтов</li>
|
||||
<li>Валидация входных данных</li>
|
||||
<li>Ошибки в формате проекта</li>
|
||||
</ul>
|
||||
<p><strong>DTO:</strong> <code>internal/dto/gantt/milestones_dto.go</code></p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">CreateGanttMilestoneRequest</span> <span style="color:#66d9ef">struct</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Title</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:"title" binding:"required,min=1,max=255"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Date</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:"date" binding:"required"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Color</span> <span style="color:#f92672">*</span><span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:"color" binding:"omitempty,max=20"`</span>
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">UpdateGanttMilestoneRequest</span> <span style="color:#66d9ef">struct</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Title</span> <span style="color:#f92672">*</span><span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:"title" binding:"omitempty,min=1,max=255"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Date</span> <span style="color:#f92672">*</span><span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:"date" binding:"omitempty"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Color</span> <span style="color:#f92672">*</span><span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:"color" binding:"omitempty,max=20"`</span>
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">GanttMilestoneResponse</span> <span style="color:#66d9ef">struct</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">ID</span> <span style="color:#a6e22e">domain</span>.<span style="color:#a6e22e">UUID</span> <span style="color:#e6db74">`json:"id"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">GanttChartID</span> <span style="color:#a6e22e">domain</span>.<span style="color:#a6e22e">UUID</span> <span style="color:#e6db74">`json:"gantt_chart_id"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Title</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:"title"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Date</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:"date"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Color</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:"color"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">CreatedByUser</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">CreatedByUpdatedUser</span> <span style="color:#e6db74">`json:"created_by_user"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">UpdatedByUser</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">CreatedByUpdatedUser</span> <span style="color:#e6db74">`json:"updated_by_user"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">CreatedAt</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span> <span style="color:#e6db74">`json:"created_at"`</span>
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">UpdatedAt</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Time</span> <span style="color:#e6db74">`json:"updated_at"`</span>
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">GanttMilestonesListResponse</span> <span style="color:#66d9ef">struct</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">Milestones</span> []<span style="color:#a6e22e">GanttMilestoneResponse</span> <span style="color:#e6db74">`json:"milestones"`</span>
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span></code></pre></div><p><strong>Регистрация роутов:</strong> Добавь группу milestone-роутов в роутер рядом с существующими роутами секций/задач. Проверка прав через существующий middleware.</p>
|
||||
</blockquote>
|
||||
<h4 id="этап-3-включение-вех-в-ответ-диаграммы">Этап 3: Включение вех в ответ диаграммы</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>При запросе данных диаграммы Ганта (полная загрузка) включай связанные вехи в ответ.</p>
|
||||
<ol>
|
||||
<li>Добавь эндпоинт или расширь существующий <code>GET /api/gantt-charts/:chartId</code> — включить поле <code>milestones</code> с массивом <code>GanttMilestoneResponse</code></li>
|
||||
<li>Вехи с <code>deleted_at IS NOT NULL</code> не возвращаются</li>
|
||||
<li>Вехи сортируются по <code>date</code> ASC</li>
|
||||
<li>Вехи загружаются вместе с данными диаграммы (tasks, sections) — один запрос при открытии Ганта</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<hr>
|
||||
<h3 id="frontend-angular">Frontend (Angular)</h3>
|
||||
<h4 id="этап-fe-1-модели--сервис">Этап FE-1: Модели + Сервис</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>Добавь в проект поддержку вех (milestones) на диаграмме Ганта.</p>
|
||||
<ol>
|
||||
<li>В <code>pages/gantt/models/</code> (или соответствующем файле моделей) добавь:</li>
|
||||
</ol>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">GanttMilestone</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">id</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">gantt_chart_id</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">title</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">date</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">color</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">created_by_user</span><span style="color:#f92672">?:</span> { <span style="color:#a6e22e">id</span>: <span style="color:#66d9ef">string</span>; <span style="color:#a6e22e">full_name</span>: <span style="color:#66d9ef">string</span> };
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">updated_by_user</span><span style="color:#f92672">?:</span> { <span style="color:#a6e22e">id</span>: <span style="color:#66d9ef">string</span>; <span style="color:#a6e22e">full_name</span>: <span style="color:#66d9ef">string</span> };
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">created_at</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">updated_at</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">GanttMilestoneCreateRequest</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">title</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">date</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">color?</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">GanttMilestoneUpdateRequest</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">title?</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">date?</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">color?</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span></code></pre></div><ol start="2">
|
||||
<li>
|
||||
<p>Добавь <code>milestones: GanttMilestone[]</code> в интерфейс/модель полной загрузки данных диаграммы</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Создай сервис <code>pages/gantt/services/gantt-milestone.service.ts</code>:</p>
|
||||
</li>
|
||||
</ol>
|
||||
<ul>
|
||||
<li>Inject HttpClient</li>
|
||||
<li>Методы:
|
||||
<ul>
|
||||
<li><code>getMilestones(chartId: string): Observable<GanttMilestone[]></code></li>
|
||||
<li><code>createMilestone(chartId: string, data: GanttMilestoneCreateRequest): Observable<GanttMilestone></code></li>
|
||||
<li><code>updateMilestone(chartId: string, milestoneId: string, data: GanttMilestoneUpdateRequest): Observable<GanttMilestone></code></li>
|
||||
<li><code>deleteMilestone(chartId: string, milestoneId: string): Observable<void></code></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Все методы возвращают Observable с типизированными ответами (по аналогии с GanttChartService)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<h4 id="этап-fe-2-модальное-окно-управления-вехами">Этап FE-2: Модальное окно управления вехами</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>Создай компонент модального окна для управления вехами диаграммы Ганта.</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Создай компонент <code>pages/gantt/components/milestones-modal/</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Кнопка вызова:</strong> Добавь кнопку «Вехи» рядом с кнопкой «Создать секцию» в toolbar диаграммы. По клику — открытие модального окна через ng-bootstrap NgbModal.</p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Модальное окно</strong> (ng-bootstrap modal):</p>
|
||||
<ul>
|
||||
<li>Заголовок: «Вехи проекта»</li>
|
||||
<li>Список вех (сортировка по date ASC):
|
||||
<ul>
|
||||
<li>Каждый элемент: цветной кружок (color), название, дата (форматированная), кнопки ✏️ (редактировать) и 🗑️ (удалить с confirm)</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Кнопка «Добавить веху» внизу списка</li>
|
||||
<li>Если вех нет — сообщение «Вех пока нет. Добавьте первую!»</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Форма добавления/редактирования</strong> (inline в модальном окне):</p>
|
||||
<ul>
|
||||
<li>Название — текстовый ввод (обязательное)</li>
|
||||
<li>Дата — ng-bootstrap datepicker (обязательное)</li>
|
||||
<li>Цвет — палитра цветов (использовать тот же компонент/подход, что и для задач)</li>
|
||||
<li>По умолчанию цвет = <code>#dc3545</code> (красный)</li>
|
||||
<li>Кнопки «Сохранить» и «Отмена»</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>При сохранении — вызов соответствующего метода сервиса (create/update)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>При удалении — confirm dialog → вызов deleteMilestone</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>После любого изменения — обновить список вех и обновить данные на канвасе (через сервис/GanttStore)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Инжектировать GanttMilestoneService и NgbModal</p>
|
||||
</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<h4 id="этап-fe-3-отображение-вех-на-таймлайне-konva-canvas">Этап FE-3: Отображение вех на таймлайне (Konva canvas)</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>Отобрази вехи (milestones) на canvas-диаграмме Ганта.</p>
|
||||
<p>Проект использует Konva.js для рендеринга.</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>При загрузке данных диаграммы — извлечь массив <code>milestones</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Для каждой вехи нарисовать на canvas:</p>
|
||||
<ul>
|
||||
<li><strong>Маркер-флажок</strong> (flag marker) на позиции даты вехи на шкале времени
|
||||
<ul>
|
||||
<li>Стиль: маленький треугольник или флаг на верхней границе таймлайна</li>
|
||||
<li>Цвет = milestone.color</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>Пунктирная вертикальная линия</strong> от маркера вниз через все секции (на всю высоту области секций)
|
||||
<ul>
|
||||
<li>Стиль: Konva.Line, dash: [5, 5], strokeWidth: 1.5, color = milestone.color, opacity: 0.7</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Позиционирование:</strong></p>
|
||||
<ul>
|
||||
<li>Рассчитать X-позицию по дате вехи с использованием текущей шкалы времени (как для задач)</li>
|
||||
<li>Если дата вехи выходит за текущий диапазон — расширить диапазон (влияет на общую ширину таймлайна)</li>
|
||||
<li>При изменении масштаба (days/weeks/months/quarters) — пересчитать позиции всех вех</li>
|
||||
<li>При скролле — вехи скроллятся вместе с задачами</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Tooltip при наведении:</strong></p>
|
||||
<ul>
|
||||
<li>При наведении на маркер или пунктирную линию — показать Konva.Tooltip или HTML tooltip:
|
||||
<ul>
|
||||
<li>Если веха одна на эту дату — показать название</li>
|
||||
<li>Если несколько вех на одну дату — показать список: цветной кружок + название для каждой</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Z-index:</strong></p>
|
||||
<ul>
|
||||
<li>Линии вех рисуются <strong>поверх</strong> фона секций, но <strong>под</strong> задачами</li>
|
||||
<li>Маркеры-флажки — поверх всего (самый высокий z-index)</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Реактивность:</strong></p>
|
||||
<ul>
|
||||
<li>При добавлении/удалении/редактировании вехи — перерисовать только вехи (без полного ре рендера задач)</li>
|
||||
<li>Подписаться на изменения массива milestones через сервис/store</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<hr>
|
||||
<h2 id="порядок-реализации">Порядок реализации</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Этап</th>
|
||||
<th>Сторона</th>
|
||||
<th>Зависимости</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>Миграция + Домен</td>
|
||||
<td>Backend</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>CRUD вех</td>
|
||||
<td>Backend</td>
|
||||
<td>Этап 1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>Включение вех в ответ диаграммы</td>
|
||||
<td>Backend</td>
|
||||
<td>Этап 2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td>Модели + Сервис</td>
|
||||
<td>Frontend</td>
|
||||
<td>— (параллельно с backend)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td>Модальное окно управления вехами</td>
|
||||
<td>Frontend</td>
|
||||
<td>Этапы 4 + backend 2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>6</td>
|
||||
<td>Отображение вех на таймлайне</td>
|
||||
<td>Frontend</td>
|
||||
<td>Этапы 4 + backend 3</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p class="text-center mb-0">© 2026 docs.a2v.space</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/fuse.js@7.0.0/dist/fuse.min.js"></script>
|
||||
|
||||
|
||||
<script src="../../js/search.js"></script>
|
||||
<script src="../../js/creds.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,839 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru-ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Leadera: Ограничения секций в диаграмме Ганта — docs.a2v.space</title>
|
||||
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../../css/custom.css">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="../../">docs.a2v.space</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMain" aria-controls="navbarMain" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<form class="d-flex search-form" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Поиск..." aria-label="Search" id="search-input">
|
||||
<button class="btn btn-outline-light" type="submit">Найти</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline-light d-md-none ms-2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebar" aria-expanded="false" aria-controls="sidebar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div id="search-results" class="search-results" style="display: none;">
|
||||
<div class="search-results-content">
|
||||
<button type="button" class="btn-close float-end" id="close-search"></button>
|
||||
<h5>Результаты поиска</h5>
|
||||
<ul id="results-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
<h6>Фильтры</h6>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Все инструкции</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6 class="mt-3">Теги</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/audit/">audit</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/leadera/">leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/security/">security</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/architecture/">architecture</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/websocket/">websocket</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/realtime/">realtime</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/gantt/">gantt</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/api/">api</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/frontend/">frontend</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/requirements/">requirements</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/angular/">angular</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/go/">go</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/ux/">ux</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/ssh/">ssh</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/tunnel/">tunnel</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/vps/">vps</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/reverse-proxy/">reverse-proxy</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/nginx/">nginx</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/devops/">devops</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/openclaw/">openclaw</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/opencode/">opencode</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../tags/docker/">docker</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
|
||||
|
||||
<main class="col-md-9 col-lg-10 main-content">
|
||||
|
||||
<div class="article-content">
|
||||
<h1>Leadera: Ограничения секций в диаграмме Ганта</h1>
|
||||
<div class="source-path mb-3">
|
||||
<small class="text-muted">
|
||||
📁 <code id="source-path" class="user-select-all">~/Projects/docs-a2v/content/guides/leadera-section-constraints.md</code>
|
||||
<button class="btn btn-sm btn-outline-secondary ms-1 py-0 px-1" onclick="navigator.clipboard.writeText('~/Projects/docs-a2v/content/guides\/leadera-section-constraints.md')" title="Копировать путь">
|
||||
📋
|
||||
</button>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="tags mb-3">
|
||||
|
||||
<a href="../../tags/leadera/" class="tag">leadera</a>
|
||||
|
||||
<a href="../../tags/gantt/" class="tag">gantt</a>
|
||||
|
||||
<a href="../../tags/angular/" class="tag">angular</a>
|
||||
|
||||
<a href="../../tags/go/" class="tag">go</a>
|
||||
|
||||
<a href="../../tags/api/" class="tag">api</a>
|
||||
|
||||
<a href="../../tags/ux/" class="tag">ux</a>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<h2 id="бизнес-требования">Бизнес-требования</h2>
|
||||
<h3 id="br-1-лимит-одновременных-задач-concurrency-limit">BR-1: Лимит одновременных задач (Concurrency Limit)</h3>
|
||||
<p>У каждой секции появляется параметр <strong>max_concurrent_tasks</strong> (целое число ≥ 0, по умолчанию 0).</p>
|
||||
<p><strong>Поведение:</strong></p>
|
||||
<ul>
|
||||
<li>Если <code>max_concurrent_tasks = 3</code> — в любой данный день в этой секции не может быть больше 3 активных задач одновременно</li>
|
||||
<li>Если <code>max_concurrent_tasks = 0</code> — ограничение отключено, лимита нет</li>
|
||||
<li>Пересечение считается по дате: задача занимает дни с <code>start_date</code> по <code>end_date</code> включительно. Две задачи «пересекаются», если у них есть хотя бы один общий день</li>
|
||||
<li>Ограничение работает <strong>только в рамках одной секции</strong>. Задачи из разных секций не влияют друг на друга</li>
|
||||
</ul>
|
||||
<p><strong>Срабатывание при:</strong></p>
|
||||
<ol>
|
||||
<li>Создании задачи — если добавление нарушает лимит → Alert</li>
|
||||
<li>Перетаскивании задачи (drag) — если новая позиция нарушает лимит → Alert</li>
|
||||
<li>Растягивании задачи (resize) — если новый размер нарушает лимит → Alert</li>
|
||||
<li>Редактировании задачи (форма) — если изменённые даты нарушают лимит → Alert</li>
|
||||
</ol>
|
||||
<p><strong>Важно:</strong> Действие <strong>НЕ отменяется</strong>. Задача создаётся/перемещается в любом случае, но пользователь видит предупреждение.</p>
|
||||
<p><strong>Текст Alert:</strong> «В секции “[название секции]” на [дата/диапазон] превышен лимит одновременных задач (максимум: N)»</p>
|
||||
<hr>
|
||||
<h3 id="br-2-блоки-секций-section-blocks">BR-2: Блоки секций (Section Blocks)</h3>
|
||||
<p>У каждой секции появляется набор <strong>блоков</strong> — особый вид записей, отображаемых на таймлайне как заштрихованные области.</p>
|
||||
<p><strong>Сущность GanttSectionBlock:</strong></p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Поле</th>
|
||||
<th>Тип</th>
|
||||
<th>Описание</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>id</td>
|
||||
<td>UUID</td>
|
||||
<td>Идентификатор</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>section_id</td>
|
||||
<td>UUID</td>
|
||||
<td>Принадлежность к секции</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>title</td>
|
||||
<td>string</td>
|
||||
<td>Название (например: «Отпуск», «Командировка»)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>start_date</td>
|
||||
<td>date</td>
|
||||
<td>Начало интервала (минимум 1 день)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>end_date</td>
|
||||
<td>date</td>
|
||||
<td>Конец интервала</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>block_concurrency</td>
|
||||
<td>boolean</td>
|
||||
<td>Запрещать ли пересечение обычных задач с данным блоком</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><strong>Валидация:</strong></p>
|
||||
<ul>
|
||||
<li><code>end_date</code> не может быть раньше <code>start_date</code></li>
|
||||
<li>Все поля обязательны</li>
|
||||
<li>Блок может быть в прошлом</li>
|
||||
</ul>
|
||||
<p><strong>Визуальное отображение на Ганте:</strong></p>
|
||||
<ul>
|
||||
<li>Заштрихованная область на <strong>всю высоту секции</strong> (включая все строки задач в секции)</li>
|
||||
<li>Название блока отображается <strong>поверх штриховки с подложкой</strong> (для читаемости)</li>
|
||||
</ul>
|
||||
<p><strong>Поведение block_concurrency:</strong></p>
|
||||
<ul>
|
||||
<li>Если <code>block_concurrency = true</code> — при пересечении задачи с данным блоком показывается Alert (аналогично BR-1), но задача <strong>всё равно создаётся/перемещается</strong></li>
|
||||
<li>Если <code>block_concurrency = false</code> — блок отображается визуально, но не влияет на задачи</li>
|
||||
</ul>
|
||||
<p><strong>Срабатывание при:</strong></p>
|
||||
<ol>
|
||||
<li>Создании задачи — пересекает блок с <code>block_concurrency = true</code> → Alert</li>
|
||||
<li>Перетаскивании задачи (drag) → Alert</li>
|
||||
<li>Растягивании задачи (resize) → Alert</li>
|
||||
<li>Редактировании задачи (форма) → Alert</li>
|
||||
</ol>
|
||||
<p><strong>Текст Alert:</strong> «В секции “[название секции]” задача пересекает блок “[название блока]” ([дата] — [дата])»</p>
|
||||
<hr>
|
||||
<h3 id="br-3-система-alert-ов">BR-3: Система Alert-ов</h3>
|
||||
<p>Все предупреждения (от BR-1 и BR-2) выводятся в <strong>единую область</strong> над диаграммой Ганта.</p>
|
||||
<p><strong>Поведение:</strong></p>
|
||||
<ul>
|
||||
<li>Alert-ы — Bootstrap Alert (warning), <strong>постоянные</strong></li>
|
||||
<li>Пользователь <strong>НЕ может</strong> закрыть Alert вручную</li>
|
||||
<li>Alert исчезает <strong>автоматически</strong>, когда текущая дата прошла дату конфликта (конфликт больше не актуален)</li>
|
||||
<li>Проверка конфликтов — <strong>только на фронтенде</strong> (на основе загруженных данных: задачи секции, блоки, max_concurrent_tasks)</li>
|
||||
<li>Если Alert-ов много — область <strong>сворачиваемая</strong> с счётчиком в бейдже (например: «⚠️ 3 предупреждения»)</li>
|
||||
<li>По умолчанию — развёрнуто</li>
|
||||
<li>Сворачивание/разворачивание — клик по заголовку области</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h3 id="br-4-настройки-секции">BR-4: Настройки секции</h3>
|
||||
<ul>
|
||||
<li>Настройки совмещены с <strong>существующим модальным окном редактирования секции</strong> (не отдельное окно)</li>
|
||||
</ul>
|
||||
<p><strong>Окно создания секции</strong> — добавляется:</p>
|
||||
<ul>
|
||||
<li>Поле «Макс. одновременных задач» (числовой ввод, 0 = без лимита, по умолчанию 0)</li>
|
||||
</ul>
|
||||
<p><strong>Окно редактирования секции</strong> — добавляется:</p>
|
||||
<ul>
|
||||
<li>Поле «Макс. одновременных задач» (числовой ввод, 0 = без лимита)</li>
|
||||
<li>Список блоков (таблица/список с CRUD: добавить, редактировать, удалить)
|
||||
<ul>
|
||||
<li><strong>Сортировка:</strong> по <code>start_date</code> по убыванию (будущее сверху)</li>
|
||||
<li>Каждый блок — форма: title, start_date, end_date, block_concurrency (чекбокс: «Запретить пересечение задач»)</li>
|
||||
<li>Валидация: end_date >= start_date, все поля заполнены</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h3 id="br-5-наследование">BR-5: Наследование</h3>
|
||||
<ul>
|
||||
<li>При появлении функционала копирования/дублирования диаграммы — настройки секций (лимит + блоки) наследуются</li>
|
||||
</ul>
|
||||
<hr>
|
||||
<h2 id="технические-требования">Технические требования</h2>
|
||||
<h3 id="база-данных">База данных</h3>
|
||||
<h4 id="новая-таблица-gantt_section_blocks">Новая таблица: gantt_section_blocks</h4>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">CREATE</span> <span style="color:#66d9ef">TABLE</span> gantt_section_blocks (
|
||||
</span></span><span style="display:flex;"><span> id UUID <span style="color:#66d9ef">PRIMARY</span> <span style="color:#66d9ef">KEY</span> <span style="color:#66d9ef">DEFAULT</span> gen_random_uuid(),
|
||||
</span></span><span style="display:flex;"><span> section_id UUID <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">REFERENCES</span> gantt_sections(id) <span style="color:#66d9ef">ON</span> <span style="color:#66d9ef">DELETE</span> <span style="color:#66d9ef">CASCADE</span>,
|
||||
</span></span><span style="display:flex;"><span> title VARCHAR(<span style="color:#ae81ff">255</span>) <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span>,
|
||||
</span></span><span style="display:flex;"><span> start_date DATE <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span>,
|
||||
</span></span><span style="display:flex;"><span> end_date DATE <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span>,
|
||||
</span></span><span style="display:flex;"><span> block_concurrency BOOLEAN <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">DEFAULT</span> <span style="color:#66d9ef">false</span>,
|
||||
</span></span><span style="display:flex;"><span> created_by UUID <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">REFERENCES</span> users(id),
|
||||
</span></span><span style="display:flex;"><span> updated_by UUID <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">REFERENCES</span> users(id),
|
||||
</span></span><span style="display:flex;"><span> created_at <span style="color:#66d9ef">TIMESTAMP</span> <span style="color:#66d9ef">WITH</span> TIME <span style="color:#66d9ef">ZONE</span> <span style="color:#66d9ef">DEFAULT</span> NOW(),
|
||||
</span></span><span style="display:flex;"><span> updated_at <span style="color:#66d9ef">TIMESTAMP</span> <span style="color:#66d9ef">WITH</span> TIME <span style="color:#66d9ef">ZONE</span> <span style="color:#66d9ef">DEFAULT</span> NOW(),
|
||||
</span></span><span style="display:flex;"><span> deleted_at <span style="color:#66d9ef">TIMESTAMP</span> <span style="color:#66d9ef">WITH</span> TIME <span style="color:#66d9ef">ZONE</span>,
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">CONSTRAINT</span> chk_block_dates <span style="color:#66d9ef">CHECK</span> (end_date <span style="color:#f92672">>=</span> start_date)
|
||||
</span></span><span style="display:flex;"><span>);
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">CREATE</span> <span style="color:#66d9ef">INDEX</span> idx_gantt_section_blocks_section <span style="color:#66d9ef">ON</span> gantt_section_blocks(section_id);
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">CREATE</span> <span style="color:#66d9ef">INDEX</span> idx_gantt_section_blocks_dates <span style="color:#66d9ef">ON</span> gantt_section_blocks(start_date, end_date);
|
||||
</span></span></code></pre></div><h4 id="изменение-таблицы-gantt_sections">Изменение таблицы: gantt_sections</h4>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sql" data-lang="sql"><span style="display:flex;"><span><span style="color:#66d9ef">ALTER</span> <span style="color:#66d9ef">TABLE</span> gantt_sections <span style="color:#66d9ef">ADD</span> <span style="color:#66d9ef">COLUMN</span> max_concurrent_tasks INTEGER <span style="color:#66d9ef">NOT</span> <span style="color:#66d9ef">NULL</span> <span style="color:#66d9ef">DEFAULT</span> <span style="color:#ae81ff">0</span>;
|
||||
</span></span></code></pre></div><hr>
|
||||
<h3 id="backend-go-api">Backend (Go API)</h3>
|
||||
<h4 id="этап-1-миграция--домен">Этап 1: Миграция + Домен</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>Добавь в проект новую таблицу <code>gantt_section_blocks</code> и колонку <code>max_concurrent_tasks</code> в <code>gantt_sections</code>.</p>
|
||||
<ol>
|
||||
<li>Создай миграцию в <code>cmd/migrate/</code>:
|
||||
<ul>
|
||||
<li>Новая таблица <code>gantt_section_blocks</code> с полями: id (UUID PK), section_id (UUID FK → gantt_sections, ON DELETE CASCADE), title (VARCHAR 255 NOT NULL), start_date (DATE NOT NULL), end_date (DATE NOT NULL), block_concurrency (BOOLEAN DEFAULT false), created_by (UUID FK → users), updated_by (UUID FK → users), created_at, updated_at, deleted_at. CHECK constraint: end_date >= start_date. Индексы по section_id и (start_date, end_date).</li>
|
||||
<li>ALTER TABLE gantt_sections ADD COLUMN max_concurrent_tasks INTEGER NOT NULL DEFAULT 0.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Создай доменную модель <code>GanttSectionBlock</code> в <code>internal/domain/gantt.go</code> со всеми полями таблицы.</li>
|
||||
<li>Добавь поле <code>MaxConcurrentTasks int</code> в структуру <code>GanttSection</code>.</li>
|
||||
<li>Обнови маппинг в relevant repository файлах, чтобы новое поле <code>max_concurrent_tasks</code> читалось/писалось.</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<h4 id="этап-2-crud-для-ganttsectionblocks">Этап 2: CRUD для GanttSectionBlocks</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>Реализуй полный CRUD для сущности <code>GanttSectionBlock</code> по аналогии с существующими handlers/services/repositories в проекте.</p>
|
||||
<p><strong>Роуты (внутри группы секции):</strong></p>
|
||||
<ul>
|
||||
<li><code>GET /api/gantt-charts/:chartId/sections/:sectionId/blocks</code> — список блоков секции</li>
|
||||
<li><code>POST /api/gantt-charts/:chartId/sections/:sectionId/blocks</code> — создать блок</li>
|
||||
<li><code>PUT /api/gantt-charts/:chartId/sections/:sectionId/blocks/:blockId</code> — обновить блок</li>
|
||||
<li><code>DELETE /api/gantt-charts/:chartId/sections/:sectionId/blocks/:blockId</code> — удалить блок (soft delete)</li>
|
||||
</ul>
|
||||
<p><strong>Repository:</strong> <code>internal/repository/gantt_section_block_repository.go</code></p>
|
||||
<ul>
|
||||
<li><code>GetBySectionID(ctx, sectionID) ([]domain.GanttSectionBlock, error)</code></li>
|
||||
<li><code>GetByID(ctx, id) (*domain.GanttSectionBlock, error)</code></li>
|
||||
<li><code>Create(ctx, block) error</code></li>
|
||||
<li><code>Update(ctx, block) error</code></li>
|
||||
<li><code>Delete(ctx, id) error</code> — soft delete (set deleted_at)</li>
|
||||
<li>Все запросы — raw SQL через pgx, как в остальных репозиториях проекта</li>
|
||||
</ul>
|
||||
<p><strong>Service:</strong> <code>internal/service/gantt_section_block_service.go</code></p>
|
||||
<ul>
|
||||
<li>Бизнес-логика: валидация dates (end >= start), проверка прав доступа к секции</li>
|
||||
<li>Inject repository</li>
|
||||
</ul>
|
||||
<p><strong>Handler:</strong> <code>internal/handler/gantt_section_block_handler.go</code></p>
|
||||
<ul>
|
||||
<li>Swagger-аннотации для всех эндпоинтов</li>
|
||||
<li>Валидация входных данных</li>
|
||||
<li>Ошибки в формате проекта (GanttErrorResponse)</li>
|
||||
</ul>
|
||||
<p><strong>Модели запросов:</strong></p>
|
||||
<ul>
|
||||
<li>Create: title (required), start_date (required), end_date (required), block_concurrency (bool, default false)</li>
|
||||
<li>Update: title, start_date, end_date, block_concurrency — все опциональны</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<h4 id="этап-3-обновление-настроек-секции">Этап 3: Обновление настроек секции</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>Обнови CRUD секций (gantt_sections) для поддержки нового поля <code>max_concurrent_tasks</code>.</p>
|
||||
<ol>
|
||||
<li>Добавь <code>max_concurrent_tasks</code> в <code>GanttSectionCreateRequest</code> (опциональное, int ≥ 0, default 0)</li>
|
||||
<li>Добавь <code>max_concurrent_tasks</code> в <code>GanttSectionUpdateRequest</code> (опциональное, int ≥ 0)</li>
|
||||
<li>Обнови SQL-запросы в repository: INSERT и UPDATE должны включать <code>max_concurrent_tasks</code></li>
|
||||
<li>При GET секции (или списке секций) — возвращать <code>max_concurrent_tasks</code> в ответе</li>
|
||||
<li>Валидация: значение должно быть ≥ 0</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<h4 id="этап-4-включение-блоков-в-ответ-секций">Этап 4: Включение блоков в ответ секций</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>При запросе списка секций или одной секции включай связанные блоки в ответ.</p>
|
||||
<ol>
|
||||
<li>Обнови <code>GET /api/gantt-charts/:chartId/sections/:sectionId</code> — добавить поле <code>blocks</code> с массивом GanttSectionBlock</li>
|
||||
<li>Обнови <code>GET /api/gantt-charts/:chartId/sections</code> — добавить поле <code>blocks</code> для каждой секции</li>
|
||||
<li>Блоки с <code>deleted_at IS NOT NULL</code> не возвращаются</li>
|
||||
<li>Блоки сортируются по start_date по убыванию (будущее сверху)</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<hr>
|
||||
<h3 id="frontend-angular">Frontend (Angular)</h3>
|
||||
<h4 id="этап-fe-1-модели--сервис">Этап FE-1: Модели + Сервис</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>Добавь в проект поддержку ограничений секций Ганта.</p>
|
||||
<ol>
|
||||
<li>В <code>pages/gantt/models/gantt-chart.model.ts</code> добавь:</li>
|
||||
</ol>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-typescript" data-lang="typescript"><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">GanttSectionBlock</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">id</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">section_id</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">title</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">start_date</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">end_date</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">block_concurrency</span>: <span style="color:#66d9ef">boolean</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">created_by_user</span>: <span style="color:#66d9ef">UserInfo</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">updated_by_user</span>: <span style="color:#66d9ef">UserInfo</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">created_at</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">updated_at</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">GanttSectionBlockCreateRequest</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">title</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">start_date</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">end_date</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">block_concurrency?</span>: <span style="color:#66d9ef">boolean</span>;
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">GanttSectionBlockUpdateRequest</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">title?</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">start_date?</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">end_date?</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">block_concurrency?</span>: <span style="color:#66d9ef">boolean</span>;
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">ConflictCheck</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">has_conflicts</span>: <span style="color:#66d9ef">boolean</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">conflicts</span>: <span style="color:#66d9ef">ConflictItem</span>[];
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span><span style="display:flex;"><span>
|
||||
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">interface</span> <span style="color:#a6e22e">ConflictItem</span> {
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">type</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'concurrency'</span> <span style="color:#f92672">|</span> <span style="color:#e6db74">'block'</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">section_id</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">section_title</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">block_title?</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">start_date?</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">end_date?</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">message</span>: <span style="color:#66d9ef">string</span>;
|
||||
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">dates?</span>: <span style="color:#66d9ef">string</span>[];
|
||||
</span></span><span style="display:flex;"><span>}
|
||||
</span></span></code></pre></div><ol start="2">
|
||||
<li>
|
||||
<p>Добавь <code>max_concurrent_tasks: number</code> в интерфейс <code>GanttSection</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Добавь <code>blocks: GanttSectionBlock[]</code> в интерфейс <code>GanttSection</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Добавь <code>max_concurrent_tasks?: number</code> в <code>GanttSectionCreateRequest</code> и <code>GanttSectionUpdateRequest</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Создай сервис <code>pages/gantt/services/gantt-section-block.service.ts</code>:</p>
|
||||
</li>
|
||||
</ol>
|
||||
<ul>
|
||||
<li>Inject HttpClient</li>
|
||||
<li>Методы: getBlocks(chartId, sectionId), createBlock(chartId, sectionId, data), updateBlock(chartId, sectionId, blockId, data), deleteBlock(chartId, sectionId, blockId)</li>
|
||||
<li>Все методы возвращают Observable с типизированными ответами (по аналогии с GanttChartService)</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<h4 id="этап-fe-2-интеграция-в-модальные-окна-секции">Этап FE-2: Интеграция в модальные окна секции</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>Интегрируй настройки ограничений в существующие модальные окна создания и редактирования секции Ганта.</p>
|
||||
<ol>
|
||||
<li>Найди существующие компоненты модальных окон секции (создание/редактирование).</li>
|
||||
</ol>
|
||||
<p><strong>Окно создания секции</strong> — добавить:</p>
|
||||
<ul>
|
||||
<li>Числовой ввод: «Максимум одновременных задач»</li>
|
||||
<li>Подсказка: «0 = без ограничений»</li>
|
||||
<li>Валидация: число ≥ 0</li>
|
||||
<li>Значение по умолчанию: 0</li>
|
||||
<li>Отправляется в <code>GanttSectionCreateRequest</code> как <code>max_concurrent_tasks</code></li>
|
||||
</ul>
|
||||
<p><strong>Окно редактирования секции</strong> — добавить:</p>
|
||||
<ul>
|
||||
<li>Числовой ввод: «Максимум одновременных задач» (аналогично созданию)</li>
|
||||
<li>Секция «Блоки (запретные интервалы)»:
|
||||
<ul>
|
||||
<li>Таблица/список существующих блоков с колонками: Название, Начало, Конец, Блокировка (чекбокс/иконка), Действия (редактировать/удалить)</li>
|
||||
<li><strong>Сортировка:</strong> по <code>start_date</code> по убыванию (будущее сверху)</li>
|
||||
<li>Кнопка «Добавить блок» — inline-форма:
|
||||
<ul>
|
||||
<li>title (текстовый ввод, обязательный)</li>
|
||||
<li>start_date (datepicker, обязательный)</li>
|
||||
<li>end_date (datepicker, обязательный)</li>
|
||||
<li>block_concurrency (чекбокс: «Запретить пересечение задач»)</li>
|
||||
<li>Валидация: end_date >= start_date, все поля заполнены</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Редактирование — inline или через ту же форму</li>
|
||||
<li>Удаление — с подтверждением</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<ol start="2">
|
||||
<li>При открытии окна редактирования — загрузить текущие настройки секции + блоки</li>
|
||||
<li>При сохранении — обновить max_concurrent_tasks и сохранить изменения блоков</li>
|
||||
<li>Инжектировать GanttChartService и GanttSectionBlockService</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<h4 id="этап-fe-3-визуализация-блоков-на-ганте">Этап FE-3: Визуализация блоков на Ганте</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>Отобрази блоки секций (GanttSectionBlock) на canvas-диаграмме Ганта.</p>
|
||||
<p>Проект использует Konva.js для рендеринга. Основной компонент: <code>gantt-chart-canvas</code>.</p>
|
||||
<ol>
|
||||
<li>При загрузке данных диаграммы — извлечь блоки из каждой секции</li>
|
||||
<li>В рендерере секций (canvas/timeline-renderer.ts или аналогичный) — для каждого блока:
|
||||
<ul>
|
||||
<li>Нарисовать заштрихованную область (diagonal hatch pattern) на <strong>всю высоту секции</strong> (все строки задач в этой секции)</li>
|
||||
<li>Область ограничена по горизонтали: от start_date до end_date (в пикселях по шкале времени)</li>
|
||||
<li>Цвет штриховки: полупрозрачный серый или configurable</li>
|
||||
<li>Поверх штриховки — текст с названием блока (block.title) на подложке (прямоугольник с фоном, чтобы текст читался)</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Блоки рендерятся <strong>до</strong> задач (z-index ниже), чтобы задачи были поверх</li>
|
||||
<li>Блоки с <code>block_concurrency = true</code> и <code>false</code> визуально не отличаются (штриховка одинаковая)</li>
|
||||
<li>При скролле/зуме — блоки пересчитываются как обычные элементы таймлайна</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<h4 id="этап-fe-4-система-alert-ов">Этап FE-4: Система Alert-ов</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>Создай компонент для отображения Alert-ов о конфликтах на диаграмме Ганта.</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Создай компонент <code>pages/gantt/components/gantt-conflict-alerts/</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Компонент размещается <strong>над</strong> canvas-диаграммой Ганта</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Входные данные: массив <code>ConflictItem[]</code> (из модели)</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Визуал:</p>
|
||||
<ul>
|
||||
<li>Bootstrap Alert (warning) для каждого конфликта</li>
|
||||
<li>Каждый Alert показывает message из ConflictItem</li>
|
||||
<li><strong>Без кнопки закрытия</strong> — Alert нельзя закрыть вручную</li>
|
||||
<li>Alert исчезает автоматически, когда текущая дата прошла дату конфликта</li>
|
||||
<li>Область <strong>сворачиваемая</strong>: заголовок «⚠️ Предупреждения (N)» — клик сворачивает/разворачивает список</li>
|
||||
<li>По умолчанию — развёрнуто</li>
|
||||
<li>При 0 конфликтов — область скрыта</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Состояние хранится в сервисе или в компоненте диаграммы:</p>
|
||||
<ul>
|
||||
<li>Добавление нового конфликта → area auto-expands</li>
|
||||
<li>Alert удаляется из массива <strong>автоматически</strong>, когда текущая дата прошла дату конфликта</li>
|
||||
<li>Проверка актуальности конфликтов — при каждой отрисовке / изменении данных</li>
|
||||
<li>Не использовать toast — Alert-ы постоянные</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<h4 id="этап-fe-5-проверка-конфликтов-при-операциях-с-задачами">Этап FE-5: Проверка конфликтов при операциях с задачами</h4>
|
||||
<p><strong>Промт для агента:</strong></p>
|
||||
<blockquote>
|
||||
<p>Интегрируй проверку конфликтов во все операции с задачами на диаграмме Ганта.</p>
|
||||
<p>Проект использует interaction-handler.ts для drag/resize и формы для создания/редактирования.</p>
|
||||
<p><strong>Важно:</strong> Проверка конфликтов — <strong>только на фронтенде</strong>, без вызова API. Используются загруженные данные секции (задачи, блоки, max_concurrent_tasks).</p>
|
||||
<ol>
|
||||
<li>
|
||||
<p>Вынеси логику проверки конфликтов <code>checkConflicts()</code> в отдельный метод в gantt-canvas.service.ts:</p>
|
||||
<ul>
|
||||
<li>Принимает: section (GanttSection — содержит max_concurrent_tasks и blocks), tasks (GanttTask[] — задачи секции), task_id (nullable — исключить текущую задачу), start_date, end_date</li>
|
||||
<li>Логика:
|
||||
a. Если section.max_concurrent_tasks > 0: посчитать кол-во задач (исключая task_id), пересекающихся с [start_date, end_date] на каждый день. Если в любой день count > max_concurrent_tasks — добавить конфликт concurrency
|
||||
b. Для каждого блока секции с block_concurrency = true, пересекающегося с [start_date, end_date] — добавить конфликт block</li>
|
||||
<li>Возвращает ConflictCheck</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>После <strong>каждой</strong> успешной операции с задачей (создание, обновление, drag, resize):</p>
|
||||
<ul>
|
||||
<li>Вызвать checkConflicts с параметрами задачи</li>
|
||||
<li>Если has_conflicts = true — передать конфликты в GanttConflictAlertsComponent</li>
|
||||
<li>Alert-ы показываются, действие <strong>НЕ отменяется</strong></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>Точки интеграции:</p>
|
||||
<ul>
|
||||
<li><strong>Создание задачи</strong> (форма): после успешного POST → checkConflicts → show alerts</li>
|
||||
<li><strong>Редактирование задачи</strong> (форма): после успешного PUT → checkConflicts → show alerts</li>
|
||||
<li><strong>Drag задачи</strong> (interaction-handler.ts): после успешного обновления позиции → checkConflicts → show alerts</li>
|
||||
<li><strong>Resize задачи</strong> (interaction-handler.ts): после успешного обновления размера → checkConflicts → show alerts</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>При переключении диаграммы или секции — очищать Alert-ы</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>Alert-ы автоматически удаляются, когда текущая дата прошла дату конфликта — область скрывается, когда конфликтов не осталось</p>
|
||||
</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<hr>
|
||||
<h2 id="порядок-реализации">Порядок реализации</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Этап</th>
|
||||
<th>Сторона</th>
|
||||
<th>Зависимости</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>Миграция + Домен</td>
|
||||
<td>Backend</td>
|
||||
<td>—</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>CRUD блоков</td>
|
||||
<td>Backend</td>
|
||||
<td>Этап 1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3</td>
|
||||
<td>Обновление настроек секции</td>
|
||||
<td>Backend</td>
|
||||
<td>Этап 1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td>Включение блоков в ответ секций</td>
|
||||
<td>Backend</td>
|
||||
<td>Этап 2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td>Модели + Сервис</td>
|
||||
<td>Frontend</td>
|
||||
<td>— (можно параллельно с backend)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>6</td>
|
||||
<td>Интеграция в модальные окна</td>
|
||||
<td>Frontend</td>
|
||||
<td>Этапы 5 + backend 2,3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>7</td>
|
||||
<td>Визуализация блоков на Ганте</td>
|
||||
<td>Frontend</td>
|
||||
<td>Этапы 5 + backend 4</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8</td>
|
||||
<td>Система Alert-ов</td>
|
||||
<td>Frontend</td>
|
||||
<td>Этап 5</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>9</td>
|
||||
<td>Проверка конфликтов при операциях</td>
|
||||
<td>Frontend</td>
|
||||
<td>Этапы 5,7,8</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p class="text-center mb-0">© 2026 docs.a2v.space</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/fuse.js@7.0.0/dist/fuse.min.js"></script>
|
||||
|
||||
|
||||
<script src="../../js/search.js"></script>
|
||||
<script src="../../js/creds.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large.
Load diff
+48
-74
@@ -27,33 +27,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="./diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="./yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="./yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="./yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="./yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="./yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="./yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="./yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="./kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="./semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="./semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="./semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="./plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="./leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="./guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="./creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="./personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -88,25 +102,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="./diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="./kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="./plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="./leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="./guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="./creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="./personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="./yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="./semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -124,9 +128,9 @@
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">Дневник</h3>
|
||||
<p class="card-text">Записи по датам о событиях и обсуждениях.</p>
|
||||
<a href="./diary/" class="btn btn-outline-primary">Открыть</a>
|
||||
<h3 class="card-title">Яша</h3>
|
||||
<p class="card-text">Дневник, база знаний, планы, инструкции, креды, Leadera, личный сайт.</p>
|
||||
<a href="./yasha/" class="btn btn-outline-primary">Открыть</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -134,39 +138,9 @@
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">База знаний</h3>
|
||||
<p class="card-text">Статьи и документация по различным темам.</p>
|
||||
<a href="./kb/" class="btn btn-outline-primary">Открыть</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">Планы</h3>
|
||||
<p class="card-text">Запланированные задачи и их статус.</p>
|
||||
<a href="./plans/" class="btn btn-outline-primary">Открыть</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">Инструкции</h3>
|
||||
<p class="card-text">Пошаговые инструкции для различных задач.</p>
|
||||
<a href="./guides/" class="btn btn-outline-primary">Открыть</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">Креды</h3>
|
||||
<p class="card-text">Зашифрованные учётные данные (требуется мастер-пароль).</p>
|
||||
<a href="./creds/" class="btn btn-outline-primary">Открыть</a>
|
||||
<h3 class="card-title">Семен</h3>
|
||||
<p class="card-text">История изменений, база знаний, дневник.</p>
|
||||
<a href="./semen/" class="btn btn-outline-primary">Открыть</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
[{"content":"Аудит проекта Leadera Дата: 30.03.2026\nВерсия: pre-production, миграции 1–5\n1. Обзор проекта Leadera — SaaS-система управления IT-ландшафтом с диаграммами Ганта.\nСтек Компонент Технологии Backend Go 1.21+, Gin, PostgreSQL 16, pgx/v5 + sqlx, JWT (HMAC-SHA256) Frontend Angular 21.1, Bootstrap 5, NgBootstrap, RxJS, Signals Инфраструктура Docker (Colima), pm2, nginx reverse proxy, VPS reg.ru Доменная модель User ──┬── Space (через space_members с role) │ └── GanttChart (принадлежит Space) ├── GanttSection └── GanttTask Роли: admin, editor, viewer — назначаются на уровне Space через таблицу space_members.\nЧто реализовано (endpoints) Auth (5): register, login, refresh, change-password, logout\nSpaces (9): CRUD + members CRUD\nGantt Charts (9): CRUD + archive + members (добавлен, но удалён в миграции 5)\nGantt Sections (5): CRUD + reorder\nGantt Tasks (4): CRUD\nИтого: ~32 endpoint\n2. Архитектура и качество кода 2.1. Backend — структура cmd/server/main.go — точка входа internal/ domain/ — модели (User, Space, GanttChart, etc.) dto/ — request/response DTO (auth, space, gantt) handlers/ — HTTP handlers (auth, space, gantt) middleware/ — auth, cors, space, gantt, role, logging, recovery repository/ — слой доступа к данным service/ — бизнес-логика config/ — конфигурация (.env) utils/ — jwt, password, response, validation migrations/ — SQL миграции (1-5) Оценка: ✅ Чистая layered архитектура. Разделение ответственности чёткое.\n2.2. Frontend — структура src/app/ core/auth/ — auth service, interceptor core/models/ — app.model, space.model core/services/ — space.service, space-storage.service pages/auth/ — sign-in, sign-up, reset-pw, etc. pages/gantt/ — компоненты, модалки, сервисы, модели pages/dashboards/ — spaces list, dashboard layouts/ — navbar, topbar, main-layout Оценка: ✅ Стандартная Angular-структура. Signals для state management.\n2.3. Проблемы кода # Проблема Критичность Описание C1 \\r в service-файлах Medium gantt_service.go, gantt_section_service.go содержат CRLF C2 main.go:238 — мёртвый код getUserHandler Low Неиспользуемая функция C3 Swagger не сгенерирован Medium Аннотации есть, но api/ директории нет C4 RegisterProtectedRoutes не вызывается в main.go Critical GET/PUT/DELETE /spaces/:id не зарегистрированы 3. Права доступа — главный вопрос 3.1. Текущая реализация Права назначаются исключительно через Space:\nТаблица space_members — связка user ↔ space с role (admin/editor/viewer) SpaceMiddleware.RequireSpace() — проверяет членство и записывает ContextSpaceRole в context RoleMiddleware.RequireMinRole() — проверяет иерархию ролей (admin \u0026gt; editor \u0026gt; viewer) Диаграммы Ганта НЕ имеют собственной системы ролей. Таблица gantt_chart_members была создана (миграция 4) и тут же удалена (миграция 5). Все проверки доступа к диаграммам идут через space membership.\n3.2. Как это работает Запрос → AuthMiddleware (JWT → userID) → SpaceMiddleware.RequireSpace() (проверяет space_members → ContextSpaceRole) → GanttMiddleware.RequireGanttChart() (проверяет chart.space_id == spaceID) → RoleMiddleware.RequireMinRole(\u0026#34;editor\u0026#34;) (проверяет ContextSpaceRole) Это правильно. Любой член Space с ролью editor+ может создавать/редактировать диаграммы. Viewers — только читать.\n3.3. 🔴 Критическая проблема: Frontend не соответствует Backend Frontend MembersModalComponent пытается управлять участниками диаграммы:\nganttService.addMember(chartId, dto) → /api/v1/spaces/{spaceId}/gantt-charts/{chartId}/members ganttService.updateMemberRole(chartId, userId, dto) → .../members/{userId} ganttService.removeMember(chartId, userId) → .../members/{userId} Backend GanttHandler.RegisterRoutes() НЕ регистрирует маршруты для members диаграмм. После удаления gantt_chart_members (миграциLine truncated
|
||||
[{"content":"Аудит проекта Leadera Дата: 30.03.2026\nВерсия: pre-production, миграции 1–5\n1. Обзор проекта Leadera — SaaS-система управления IT-ландшафтом с диаграммами Ганта.\nСтек Компонент Технологии Backend Go 1.21+, Gin, PostgreSQL 16, pgx/v5 + sqlx, JWT (HMAC-SHA256) Frontend Angular 21.1, Bootstrap 5, NgBootstrap, RxJS, Signals Инфраструктура Docker (Colima), pm2, nginx reverse proxy, VPS reg.ru Доменная модель User ──┬── Space (через space_members с role) │ └── GanttChart (принадлежит Space) ├── GanttSection └── GanttTask Роли: admin, editor, viewer — назначаются на уровне Space через таблицу space_members.\nЧто реализовано (endpoints) Auth (5): register, login, refresh, change-password, logout\nSpaces (9): CRUD + members CRUD\nGantt Charts (9): CRUD + archive + members (добавлен, но удалён в миграции 5)\nGantt Sections (5): CRUD + reorder\nGantt Tasks (4): CRUD\nИтого: ~32 endpoint\n2. Архитектура и качество кода 2.1. Backend — структура cmd/server/main.go — точка входа internal/ domain/ — модели (User, Space, GanttChart, etc.) dto/ — request/response DTO (auth, space, gantt) handlers/ — HTTP handlers (auth, space, gantt) middleware/ — auth, cors, space, gantt, role, logging, recovery repository/ — слой доступа к данным service/ — бизнес-логика config/ — конфигурация (.env) utils/ — jwt, password, response, validation migrations/ — SQL миграции (1-5) Оценка: ✅ Чистая layered архитектура. Разделение ответственности чёткое.\n2.2. Frontend — структура src/app/ core/auth/ — auth service, interceptor core/models/ — app.model, space.model core/services/ — space.service, space-storage.service pages/auth/ — sign-in, sign-up, reset-pw, etc. pages/gantt/ — компоненты, модалки, сервисы, модели pages/dashboards/ — spaces list, dashboard layouts/ — navbar, topbar, main-layout Оценка: ✅ Стандартная Angular-структура. Signals для state management.\n2.3. Проблемы кода # Проблема Критичность Описание C1 \\r в service-файлах Medium gantt_service.go, gantt_section_service.go содержат CRLF C2 main.go:238 — мёртвый код getUserHandler Low Неиспользуемая функция C3 Swagger не сгенерирован Medium Аннотации есть, но api/ директории нет C4 RegisterProtectedRoutes не вызывается в main.go Critical GET/PUT/DELETE /spaces/:id не зарегистрированы 3. Права доступа — главный вопрос 3.1. Текущая реализация Права назначаются исключительно через Space:\nТаблица space_members — связка user ↔ space с role (admin/editor/viewer) SpaceMiddleware.RequireSpace() — проверяет членство и записывает ContextSpaceRole в context RoleMiddleware.RequireMinRole() — проверяет иерархию ролей (admin \u0026gt; editor \u0026gt; viewer) Диаграммы Ганта НЕ имеют собственной системы ролей. Таблица gantt_chart_members была создана (миграция 4) и тут же удалена (миграция 5). Все проверки доступа к диаграммам идут через space membership.\n3.2. Как это работает Запрос → AuthMiddleware (JWT → userID) → SpaceMiddleware.RequireSpace() (проверяет space_members → ContextSpaceRole) → GanttMiddleware.RequireGanttChart() (проверяет chart.space_id == spaceID) → RoleMiddleware.RequireMinRole(\u0026#34;editor\u0026#34;) (проверяет ContextSpaceRole) Это правильно. Любой член Space с ролью editor+ может создавать/редактировать диаграммы. Viewers — только читать.\n3.3. 🔴 Критическая проблема: Frontend не соответствует Backend Frontend MembersModalComponent пытается управлять участниками диаграммы:\nganttService.addMember(chartId, dto) → /api/v1/spaces/{spaceId}/gantt-charts/{chartId}/members ganttService.updateMemberRole(chartId, userId, dto) → .../members/{userId} ganttService.removeMember(chartId, userId) → .../members/{userId} Backend GanttHandler.RegisterRoutes() НЕ регистрирует маршруты для members диаграмм. После удаления gantt_chart_members (миграциLine truncated
|
||||
+10
-5
@@ -38,11 +38,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
resultsList.innerHTML = results.map(result => {
|
||||
const item = result.item;
|
||||
const sectionLabels = {
|
||||
'diary': 'Дневник',
|
||||
'kb': 'База знаний',
|
||||
'plans': 'Планы',
|
||||
'guides': 'Инструкции',
|
||||
'creds': 'Креды'
|
||||
'yasha/diary': 'Яша / Дневник',
|
||||
'yasha/kb': 'Яша / База знаний',
|
||||
'yasha/plans': 'Яша / Планы',
|
||||
'yasha/guides': 'Яша / Инструкции',
|
||||
'yasha/creds': 'Яша / Креды',
|
||||
'yasha/leadera': 'Яша / Leadera',
|
||||
'yasha/personal-site': 'Яша / Личный сайт',
|
||||
'semen': 'Семен',
|
||||
'semen/diary': 'Семен / Дневник',
|
||||
'semen/kb': 'Семен / База знаний'
|
||||
};
|
||||
const label = sectionLabels[item.section] || item.section;
|
||||
return `<li>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
[{"content":"Аудит проекта Leadera Дата: 30.03.2026\nВерсия: pre-production, миграции 1–5\n1. Обзор проекта Leadera — SaaS-система управления IT-ландшафтом с диаграммами Ганта.\nСтек Компонент Технологии Backend Go 1.21+, Gin, PostgreSQL 16, pgx/v5 + sqlx, JWT (HMAC-SHA256) Frontend Angular 21.1, Bootstrap 5, NgBootstrap, RxJS, Signals Инфраструктура Docker (Colima), pm2, nginx reverse proxy, VPS reg.ru Доменная модель User ──┬── Space (через space_members с role) │ └── GanttChart (принадлежит Space) ├── GanttSection └── GanttTask Роли: admin, editor, viewer — назначаются на уровне Space через таблицу space_members.\nЧто реализовано (endpoints) Auth (5): register, login, refresh, change-password, logout\nSpaces (9): CRUD + members CRUD\nGantt Charts (9): CRUD + archive + members (добавлен, но удалён в миграции 5)\nGantt Sections (5): CRUD + reorder\nGantt Tasks (4): CRUD\nИтого: ~32 endpoint\n2. Архитектура и качество кода 2.1. Backend — структура cmd/server/main.go — точка входа internal/ domain/ — модели (User, Space, GanttChart, etc.) dto/ — request/response DTO (auth, space, gantt) handlers/ — HTTP handlers (auth, space, gantt) middleware/ — auth, cors, space, gantt, role, logging, recovery repository/ — слой доступа к данным service/ — бизнес-логика config/ — конфигурация (.env) utils/ — jwt, password, response, validation migrations/ — SQL миграции (1-5) Оценка: ✅ Чистая layered архитектура. Разделение ответственности чёткое.\n2.2. Frontend — структура src/app/ core/auth/ — auth service, interceptor core/models/ — app.model, space.model core/services/ — space.service, space-storage.service pages/auth/ — sign-in, sign-up, reset-pw, etc. pages/gantt/ — компоненты, модалки, сервисы, модели pages/dashboards/ — spaces list, dashboard layouts/ — navbar, topbar, main-layout Оценка: ✅ Стандартная Angular-структура. Signals для state management.\n2.3. Проблемы кода # Проблема Критичность Описание C1 \\r в service-файлах Medium gantt_service.go, gantt_section_service.go содержат CRLF C2 main.go:238 — мёртвый код getUserHandler Low Неиспользуемая функция C3 Swagger не сгенерирован Medium Аннотации есть, но api/ директории нет C4 RegisterProtectedRoutes не вызывается в main.go Critical GET/PUT/DELETE /spaces/:id не зарегистрированы 3. Права доступа — главный вопрос 3.1. Текущая реализация Права назначаются исключительно через Space:\nТаблица space_members — связка user ↔ space с role (admin/editor/viewer) SpaceMiddleware.RequireSpace() — проверяет членство и записывает ContextSpaceRole в context RoleMiddleware.RequireMinRole() — проверяет иерархию ролей (admin \u0026gt; editor \u0026gt; viewer) Диаграммы Ганта НЕ имеют собственной системы ролей. Таблица gantt_chart_members была создана (миграция 4) и тут же удалена (миграция 5). Все проверки доступа к диаграммам идут через space membership.\n3.2. Как это работает Запрос → AuthMiddleware (JWT → userID) → SpaceMiddleware.RequireSpace() (проверяет space_members → ContextSpaceRole) → GanttMiddleware.RequireGanttChart() (проверяет chart.space_id == spaceID) → RoleMiddleware.RequireMinRole(\u0026#34;editor\u0026#34;) (проверяет ContextSpaceRole) Это правильно. Любой член Space с ролью editor+ может создавать/редактировать диаграммы. Viewers — только читать.\n3.3. 🔴 Критическая проблема: Frontend не соответствует Backend Frontend MembersModalComponent пытается управлять участниками диаграммы:\nganttService.addMember(chartId, dto) → /api/v1/spaces/{spaceId}/gantt-charts/{chartId}/members ganttService.updateMemberRole(chartId, userId, dto) → .../members/{userId} ganttService.removeMember(chartId, userId) → .../members/{userId} Backend GanttHandler.RegisterRoutes() НЕ регистрирует маршруты для members диаграмм. После удаления gantt_chart_members (миграциLine truncated
|
||||
@@ -1,166 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru-ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Личный сайт (agaltsovav.ru) — docs.a2v.space</title>
|
||||
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../css/custom.css">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="../">docs.a2v.space</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMain" aria-controls="navbarMain" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../diary/">Дневник</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../kb/">База знаний</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
<form class="d-flex search-form" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Поиск..." aria-label="Search" id="search-input">
|
||||
<button class="btn btn-outline-light" type="submit">Найти</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline-light d-md-none ms-2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebar" aria-expanded="false" aria-controls="sidebar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div id="search-results" class="search-results" style="display: none;">
|
||||
<div class="search-results-content">
|
||||
<button type="button" class="btn-close float-end" id="close-search"></button>
|
||||
<h5>Результаты поиска</h5>
|
||||
<ul id="results-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
|
||||
|
||||
<main class="col-md-9 col-lg-10 main-content">
|
||||
|
||||
<div class="article-content">
|
||||
<h1>Личный сайт (agaltsovav.ru)</h1>
|
||||
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="../personal-site/netlify-node-version/">Устаревший Node.js 16 в Netlify</a></li>
|
||||
|
||||
<li><a href="../personal-site/no-node-modules/">Отсутствуют node_modules — нужна установка</a></li>
|
||||
|
||||
<li><a href="../personal-site/binary-files-in-repo/">Бинарный файл в репозитории</a></li>
|
||||
|
||||
<li><a href="../personal-site/no-analytics/">Yandex Metrika отключена</a></li>
|
||||
|
||||
<li><a href="../personal-site/twitter-handle-mismatch/">Twitter handle не соответствует имени</a></li>
|
||||
|
||||
<li><a href="../personal-site/outdated-doks-theme/">Hugo Doks 0.5 — сильно устарел</a></li>
|
||||
|
||||
<li><a href="../personal-site/digest-stale/">Digest не обновлялся с февраля 2025</a></li>
|
||||
|
||||
<li><a href="../personal-site/copyright-outdated/">Copyright без 2026 года</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p class="text-center mb-0">© 2026 docs.a2v.space</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/fuse.js@7.0.0/dist/fuse.min.js"></script>
|
||||
|
||||
|
||||
<script src="../js/search.js"></script>
|
||||
<script src="../js/creds.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
[{"content":"Проблема В params.toml:\ncopyRight = \u0026#34;Copyright (c) 2020-2025 Антон Агальцов\u0026#34; Сейчас 2026 год — нужно обновить.\nРекомендация Обновить на 2020-2026. Или сделать динамическим через Hugo template:\nCopyright (c) 2020-{{ now.Year }} Антон Агальцов ","section":"personal-site","tags":["audit","personal-site"],"title":"Copyright без 2026 года","url":"/personal-site/copyright-outdated/"},{"content":"Проблема Последний дайджест — февраль 2025. С тех пор 12+ месяцев без новых выпусков.\nЧто есть 27 файлов дайджестов (январь 2023 — февраль 2025) Автоматизация через digest.js — скрипт для генерации Но скрипт не запускается автоматически Риск Low — контент устаревает, SEO-позиции падают для страниц дайджестов.\nРекомендация Настроить cron/CI для ежемесячной генерации дайджеста Или интегрировать с OpenClaw — авто-сборка дайджеста на основе новых публикаций ","section":"personal-site","tags":["audit","personal-site"],"title":"Digest не обновлялся с февраля 2025","url":"/personal-site/digest-stale/"},{"content":"Проблема Проект использует тему Hyas/Doks v0.5.0 (package.json). Актуальная версия Doks — 1.x с полной переработкой архитектуры.\nЧто не так Doks 0.5 использует устаревший Bootstrap 5.2.0-beta1 Hugo 0.148.2 в зависимостях — новый, но интеграция с темой старая Конфигурация через theme.toml — в новых версиях перешли на другой формат exec-bin для вызова Hugo — устаревший подход, текущие версии Hugo ставятся глобально Риск Medium — работает, но миграция на Doks 1.x потребует значительного рефакторинга layout-ов и конфигурации. Чем дольше откладывать — тем сложнее.\nРекомендация Запланировать миграцию на Doks 1.x или перейти на чистый Hugo + Bootstrap без зависимости от темы-фреймворка.\n","section":"personal-site","tags":["audit","personal-site"],"title":"Hugo Doks 0.5 — сильно устарел","url":"/personal-site/outdated-doks-theme/"},{"content":"Проблема В params.toml указан Twitter: @AntonTotoshkin\nНо владелец сайта — Антон Агальцов. Ники не совпадают.\nДва варианта Это твой старый/другой аккаунт — тогда ок, но стоит указать актуальный Это чужой аккаунт — тогда нужно убрать или исправить Затронутые места twitterSite = \u0026quot;@AntonTotoshkin\u0026quot; schemaAuthorTwitter = \u0026quot;https://twitter.com/AntonTotoshkin\u0026quot; schemaTwitter = \u0026quot;https://twitter.com/AntonTotoshkin\u0026quot; Рекомендация Указать актуальный Twitter/X или убрать, если соцсеть не используется.\n","section":"personal-site","tags":["audit","personal-site"],"title":"Twitter handle не соответствует имени","url":"/personal-site/twitter-handle-mismatch/"},{"content":"Проблема В params.toml: yandexMetrika = false\nАналитика с сайта не собирается. Непонятно, есть ли вообще посетители и какие страницы популярны.\nРекомендация Включить Yandex Metrika или перейти на Plausible / Umami (privacy-friendly) Если сайт не нуждается в аналитике — оставить как есть Альтернатива: Netlify Analytics (уже встроен в хостинг) ","section":"personal-site","tags":["audit","personal-site"],"title":"Yandex Metrika отключена","url":"/personal-site/no-analytics/"},{"content":"Проблема В корне репозитория лежат два файла, которые не должны быть в git:\nШаблоны для сайта.key — Apple Keynote файл (бинарный) logo.psd — Photoshop файл (бинарный) Что не так Увеличивают размер репозитория Git неэффективно хранит бинарные файлы (каждая версия — полностьLine truncated
|
||||
@@ -0,0 +1,161 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru-ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>История изменений — docs.a2v.space</title>
|
||||
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../../css/custom.css">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="../../">docs.a2v.space</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMain" aria-controls="navbarMain" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
<form class="d-flex search-form" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Поиск..." aria-label="Search" id="search-input">
|
||||
<button class="btn btn-outline-light" type="submit">Найти</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline-light d-md-none ms-2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebar" aria-expanded="false" aria-controls="sidebar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div id="search-results" class="search-results" style="display: none;">
|
||||
<div class="search-results-content">
|
||||
<button type="button" class="btn-close float-end" id="close-search"></button>
|
||||
<h5>Результаты поиска</h5>
|
||||
<ul id="results-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item"><a class="nav-link" href="../../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
|
||||
|
||||
<main class="col-md-9 col-lg-10 main-content">
|
||||
|
||||
<div class="article-content">
|
||||
<h1>История изменений</h1>
|
||||
<div class="source-path mb-3">
|
||||
<small class="text-muted">
|
||||
📁 <code id="source-path" class="user-select-all">~/Projects/docs-a2v/content/semen/changelog.md</code>
|
||||
<button class="btn btn-sm btn-outline-secondary ms-1 py-0 px-1" onclick="navigator.clipboard.writeText('~/Projects/docs-a2v/content/semen\/changelog.md')" title="Копировать путь">
|
||||
📋
|
||||
</button>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p class="text-center mb-0">© 2026 docs.a2v.space</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/fuse.js@7.0.0/dist/fuse.min.js"></script>
|
||||
|
||||
|
||||
<script src="../../js/search.js"></script>
|
||||
<script src="../../js/creds.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,159 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru-ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Дневник — docs.a2v.space</title>
|
||||
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../../css/custom.css">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="../../">docs.a2v.space</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMain" aria-controls="navbarMain" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
<form class="d-flex search-form" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Поиск..." aria-label="Search" id="search-input">
|
||||
<button class="btn btn-outline-light" type="submit">Найти</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline-light d-md-none ms-2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebar" aria-expanded="false" aria-controls="sidebar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div id="search-results" class="search-results" style="display: none;">
|
||||
<div class="search-results-content">
|
||||
<button type="button" class="btn-close float-end" id="close-search"></button>
|
||||
<h5>Результаты поиска</h5>
|
||||
<ul id="results-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Фильтры</h6>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/diary/">Все записи</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
|
||||
|
||||
<main class="col-md-9 col-lg-10 main-content">
|
||||
|
||||
<div class="article-content">
|
||||
<h1>Дневник</h1>
|
||||
|
||||
<div class="diary-timeline">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p class="text-center mb-0">© 2026 docs.a2v.space</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/fuse.js@7.0.0/dist/fuse.min.js"></script>
|
||||
|
||||
|
||||
<script src="../../js/search.js"></script>
|
||||
<script src="../../js/creds.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
@@ -0,0 +1,184 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru-ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Семен — docs.a2v.space</title>
|
||||
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../css/custom.css">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="../">docs.a2v.space</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMain" aria-controls="navbarMain" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
<form class="d-flex search-form" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Поиск..." aria-label="Search" id="search-input">
|
||||
<button class="btn btn-outline-light" type="submit">Найти</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline-light d-md-none ms-2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebar" aria-expanded="false" aria-controls="sidebar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div id="search-results" class="search-results" style="display: none;">
|
||||
<div class="search-results-content">
|
||||
<button type="button" class="btn-close float-end" id="close-search"></button>
|
||||
<h5>Результаты поиска</h5>
|
||||
<ul id="results-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item"><a class="nav-link" href="../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
|
||||
|
||||
<main class="col-md-9 col-lg-10 main-content">
|
||||
|
||||
<div class="article-content">
|
||||
<h1>Семен</h1>
|
||||
<p class="lead">Документация Семена.</p>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">История изменений</h3>
|
||||
<p class="card-text">Лог изменений и обновлений.</p>
|
||||
<a href="../semen/changelog/" class="btn btn-outline-primary">Открыть</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">База знаний</h3>
|
||||
<p class="card-text">Статьи и документация по различным темам.</p>
|
||||
<a href="../semen/kb/" class="btn btn-outline-primary">Открыть</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h3 class="card-title">Дневник</h3>
|
||||
<p class="card-text">Записи по датам о событиях и обсуждениях.</p>
|
||||
<a href="../semen/diary/" class="btn btn-outline-primary">Открыть</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p class="text-center mb-0">© 2026 docs.a2v.space</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/fuse.js@7.0.0/dist/fuse.min.js"></script>
|
||||
|
||||
|
||||
<script src="../js/search.js"></script>
|
||||
<script src="../js/creds.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
[{"content":"","section":"semen","tags":[],"title":"История изменений","url":"/semen/changelog/"},{"content":"","section":"semen","tags":null,"title":"База знаний","url":"/semen/kb/"},{"content":"","section":"semen","tags":null,"title":"Дневник","url":"/semen/diary/"}]
|
||||
@@ -0,0 +1,159 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru-ru">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>База знаний — docs.a2v.space</title>
|
||||
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../../css/custom.css">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="../../">docs.a2v.space</a>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMain" aria-controls="navbarMain" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
<form class="d-flex search-form" role="search">
|
||||
<input class="form-control me-2" type="search" placeholder="Поиск..." aria-label="Search" id="search-input">
|
||||
<button class="btn btn-outline-light" type="submit">Найти</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline-light d-md-none ms-2" type="button" data-bs-toggle="collapse" data-bs-target="#sidebar" aria-expanded="false" aria-controls="sidebar">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
<div id="search-results" class="search-results" style="display: none;">
|
||||
<div class="search-results-content">
|
||||
<button type="button" class="btn-close float-end" id="close-search"></button>
|
||||
<h5>Результаты поиска</h5>
|
||||
<ul id="results-list"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Фильтры</h6>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/kb/">Все статьи</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</aside>
|
||||
|
||||
|
||||
<main class="col-md-9 col-lg-10 main-content">
|
||||
|
||||
<div class="article-content">
|
||||
<h1>База знаний</h1>
|
||||
|
||||
<div class="row">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p class="text-center mb-0">© 2026 docs.a2v.space</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/fuse.js@7.0.0/dist/fuse.min.js"></script>
|
||||
|
||||
|
||||
<script src="../../js/search.js"></script>
|
||||
<script src="../../js/creds.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
[]
|
||||
+64
-51
@@ -2,29 +2,35 @@
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml">
|
||||
<url>
|
||||
<loc>/leadera/</loc>
|
||||
<loc>/yasha/leadera/</loc>
|
||||
<lastmod>2026-04-09T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/personal-site/</loc>
|
||||
<loc>/yasha/personal-site/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/leadera/leadera-audit/</loc>
|
||||
<loc>/yasha/leadera/leadera-audit/</loc>
|
||||
<lastmod>2026-03-30T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/leadera/leadera-websocket-gantt/</loc>
|
||||
<loc>/yasha/leadera/leadera-websocket-gantt/</loc>
|
||||
<lastmod>2026-04-07T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/leadera/leadera-gantt-excel-export/</loc>
|
||||
<loc>/yasha/leadera/leadera-gantt-excel-export/</loc>
|
||||
<lastmod>2026-04-09T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/leadera/leadera-gantt-filters/</loc>
|
||||
<loc>/yasha/leadera/leadera-gantt-filters/</loc>
|
||||
<lastmod>2026-04-07T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/leadera/leadera-gantt-public-access/</loc>
|
||||
<loc>/yasha/leadera/leadera-gantt-public-access/</loc>
|
||||
<lastmod>2026-04-09T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/</loc>
|
||||
<lastmod>2026-04-09T00:00:00+00:00</lastmod>
|
||||
<lastmod>2026-04-26T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/semen/changelog/</loc>
|
||||
<lastmod>2026-04-26T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/semen/</loc>
|
||||
<lastmod>2026-04-26T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/excel/</loc>
|
||||
<lastmod>2026-04-09T00:00:00+00:00</lastmod>
|
||||
@@ -46,6 +52,9 @@
|
||||
</url><url>
|
||||
<loc>/tags/</loc>
|
||||
<lastmod>2026-04-09T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/yasha/</loc>
|
||||
<lastmod>2026-04-09T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/angular/</loc>
|
||||
<lastmod>2026-04-08T00:00:00+00:00</lastmod>
|
||||
@@ -56,7 +65,7 @@
|
||||
<loc>/tags/go/</loc>
|
||||
<lastmod>2026-04-08T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/leadera/leadera-milestones/</loc>
|
||||
<loc>/yasha/leadera/leadera-milestones/</loc>
|
||||
<lastmod>2026-04-08T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/ux/</loc>
|
||||
@@ -77,10 +86,10 @@
|
||||
<loc>/tags/websocket/</loc>
|
||||
<lastmod>2026-04-07T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/leadera/leadera-section-constraints/</loc>
|
||||
<loc>/yasha/leadera/leadera-section-constraints/</loc>
|
||||
<lastmod>2026-04-06T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/diary/2026-03-31/</loc>
|
||||
<loc>/yasha/diary/2026-03-31/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/audit/</loc>
|
||||
@@ -89,13 +98,10 @@
|
||||
<loc>/tags/canvas/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/personal-site/copyright-outdated/</loc>
|
||||
<loc>/yasha/personal-site/copyright-outdated/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/diary/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/personal-site/digest-stale/</loc>
|
||||
<loc>/yasha/personal-site/digest-stale/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/github/</loc>
|
||||
@@ -104,7 +110,7 @@
|
||||
<loc>/tags/hugo/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/personal-site/outdated-doks-theme/</loc>
|
||||
<loc>/yasha/personal-site/outdated-doks-theme/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/infrastructure/</loc>
|
||||
@@ -122,22 +128,25 @@
|
||||
<loc>/tags/system-utils/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/personal-site/twitter-handle-mismatch/</loc>
|
||||
<loc>/yasha/personal-site/twitter-handle-mismatch/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/personal-site/no-analytics/</loc>
|
||||
<loc>/yasha/personal-site/no-analytics/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/personal-site/binary-files-in-repo/</loc>
|
||||
<loc>/yasha/personal-site/binary-files-in-repo/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/personal-site/no-node-modules/</loc>
|
||||
<loc>/yasha/diary/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/personal-site/netlify-node-version/</loc>
|
||||
<loc>/yasha/personal-site/no-node-modules/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/diary/2026-03-30/</loc>
|
||||
<loc>/yasha/personal-site/netlify-node-version/</loc>
|
||||
<lastmod>2026-03-31T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/yasha/diary/2026-03-30/</loc>
|
||||
<lastmod>2026-03-30T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/mtproxy/</loc>
|
||||
@@ -158,7 +167,7 @@
|
||||
<loc>/tags/vikunja/</loc>
|
||||
<lastmod>2026-03-30T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/diary/2026-03-29/</loc>
|
||||
<loc>/yasha/diary/2026-03-29/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/amnezia/</loc>
|
||||
@@ -169,9 +178,6 @@
|
||||
</url><url>
|
||||
<loc>/tags/companion/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/creds/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/database/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
@@ -188,34 +194,28 @@
|
||||
<loc>/tags/docs/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/creds/docs-a2v-space-auth/</loc>
|
||||
<loc>/yasha/creds/docs-a2v-space-auth/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/eversolo/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/creds/eversolo-companion-postgres/</loc>
|
||||
<loc>/yasha/creds/eversolo-companion-postgres/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/kb/eversolo-api/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/guides/</loc>
|
||||
<loc>/yasha/kb/eversolo-api/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/identity/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/kb/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/llm/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/creds/leadera-db-docker/</loc>
|
||||
<loc>/yasha/creds/leadera-db-docker/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/creds/leadera-db-local/</loc>
|
||||
<loc>/yasha/creds/leadera-db-local/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/music/</loc>
|
||||
@@ -226,9 +226,6 @@
|
||||
</url><url>
|
||||
<loc>/tags/openclaw/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/plans/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/postgresql/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
@@ -242,7 +239,7 @@
|
||||
<loc>/tags/ssh/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/guides/ssh-tunnels-vps/</loc>
|
||||
<loc>/yasha/guides/ssh-tunnels-vps/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/streamer/</loc>
|
||||
@@ -254,38 +251,54 @@
|
||||
<loc>/tags/vpn/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/creds/vpn-vps/</loc>
|
||||
<loc>/yasha/creds/vpn-vps/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/tags/vps/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/creds/vps-reg-ru/</loc>
|
||||
<loc>/yasha/creds/vps-reg-ru/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/plans/eversolo-companion-phases-3-9/</loc>
|
||||
<loc>/yasha/kb/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/plans/ollama-openclaw/</loc>
|
||||
<loc>/yasha/plans/eversolo-companion-phases-3-9/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/guides/vps-reverse-proxy/</loc>
|
||||
<loc>/yasha/guides/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/guides/claw-a2v-space/</loc>
|
||||
<loc>/yasha/creds/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/guides/code-a2v-space/</loc>
|
||||
<loc>/yasha/plans/ollama-openclaw/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/guides/sand-a2v-space/</loc>
|
||||
<loc>/yasha/guides/vps-reverse-proxy/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/plans/eversolo-control/</loc>
|
||||
<loc>/yasha/plans/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/yasha/guides/claw-a2v-space/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/yasha/guides/code-a2v-space/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/yasha/guides/sand-a2v-space/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/yasha/plans/eversolo-control/</loc>
|
||||
<lastmod>2026-03-29T00:00:00+00:00</lastmod>
|
||||
</url><url>
|
||||
<loc>/categories/</loc>
|
||||
</url><url>
|
||||
<loc>/statuses/</loc>
|
||||
</url><url>
|
||||
<loc>/semen/kb/</loc>
|
||||
</url><url>
|
||||
<loc>/semen/diary/</loc>
|
||||
</url>
|
||||
</urlset>
|
||||
+42
-38
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -121,7 +125,7 @@
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="../../creds/vpn-vps/">VPN VPS</a></li>
|
||||
<li><a href="../../yasha/creds/vpn-vps/">VPN VPS</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
<atom:link href="/tags/amnezia/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>VPN VPS</title>
|
||||
<link>/creds/vpn-vps/</link>
|
||||
<link>/yasha/creds/vpn-vps/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/creds/vpn-vps/</guid>
|
||||
<guid>/yasha/creds/vpn-vps/</guid>
|
||||
<description>YEI16NAJ78d3lmSa/lca8Zxczt+u6GyMUYHduX47VoFQdj0Ixozd8R6yXoB7eM+AjhCtNGkOxGlOj2s73ABOBjLEIVIhD4Z+xTyzdtah4VpyLoltYOeNYsnbIIFARdF/FBYq5COXohhXs33K5ozznrtDd/awbKMjl/sbQqjXAZ+A3ZSob6/5uU+fFKetXLR6v4ZLAYQ57iiD21ImSLSggwv+rJgK5XyPDKEPD4N5nOsiDrLgoukVBMvgJ54PqfPp2Hni+2tdKOlMnOG2npTuu4SkyALcfFar</description>
|
||||
</item>
|
||||
</channel>
|
||||
|
||||
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -121,15 +125,15 @@
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="../../leadera/leadera-milestones/">Leadera: Вехи (Milestones) на диаграмме Ганта</a></li>
|
||||
<li><a href="../../yasha/leadera/leadera-milestones/">Leadera: Вехи (Milestones) на диаграмме Ганта</a></li>
|
||||
|
||||
<li><a href="../../leadera/leadera-section-constraints/">Leadera: Ограничения секций в диаграмме Ганта</a></li>
|
||||
<li><a href="../../yasha/leadera/leadera-section-constraints/">Leadera: Ограничения секций в диаграмме Ганта</a></li>
|
||||
|
||||
<li><a href="../../guides/sand-a2v-space/">Подключение sand.a2v.space (Leadera Project)</a></li>
|
||||
<li><a href="../../yasha/guides/sand-a2v-space/">Подключение sand.a2v.space (Leadera Project)</a></li>
|
||||
|
||||
<li><a href="../../plans/eversolo-companion-phases-3-9/">Завершить Eversolo Companion - этапы 3-9</a></li>
|
||||
<li><a href="../../yasha/plans/eversolo-companion-phases-3-9/">Завершить Eversolo Companion - этапы 3-9</a></li>
|
||||
|
||||
<li><a href="../../diary/2026-03-29/">29 марта 2026</a></li>
|
||||
<li><a href="../../yasha/diary/2026-03-29/">29 марта 2026</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -10,37 +10,37 @@
|
||||
<atom:link href="/tags/angular/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>Leadera: Вехи (Milestones) на диаграмме Ганта</title>
|
||||
<link>/leadera/leadera-milestones/</link>
|
||||
<link>/yasha/leadera/leadera-milestones/</link>
|
||||
<pubDate>Wed, 08 Apr 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/leadera/leadera-milestones/</guid>
|
||||
<guid>/yasha/leadera/leadera-milestones/</guid>
|
||||
<description><h2 id="бизнес-требования">Бизнес-требования</h2>
<h3 id="br-1-сущность-веха-milestone">BR-1: Сущность «Веха» (Milestone)</h3>
<p>Веха — это маркер на таймлайне диаграммы Ганта, обозначающий важную дату в проекте. В отличие от задач, веха не имеет длительности и привязана к конкретной дате.</p>
<p><strong>Поля вехи:</strong></p>
<table>
 <thead>
 <tr>
 <th>Поле</th>
 <th>Тип</th>
 <th>Описание</th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>id</td>
 <td>UUID</td>
 <td>Идентификатор</td>
 </tr>
 <tr>
 <td>gantt_chart_id</td>
 <td>UUID</td>
 <td>Принадлежность к диаграмме (не к секции!)</td>
 </tr>
 <tr>
 <td>title</td>
 <td>string</td>
 <td>Название вехи (обязательное, 1–255 символов)</td>
 </tr>
 <tr>
 <td>date</td>
 <td>date</td>
 <td>Дата вехи (без времени, обязательная)</td>
 </tr>
 <tr>
 <td>color</td>
 <td>string</td>
 <td>Цвет маркера (HEX, по умолчанию <code>#dc3545</code> — красный)</td>
 </tr>
 <tr>
 <td>created_by</td>
 <td>UUID</td>
 <td>Кто создал (не отображается в UI, хранится для аудита)</td>
 </tr>
 <tr>
 <td>updated_by</td>
 <td>UUID</td>
 <td>Кто обновил (не отображается в UI)</td>
 </tr>
 <tr>
 <td>created_at</td>
 <td>timestamp</td>
 <td>Дата создания</td>
 </tr>
 <tr>
 <td>updated_at</td>
 <td>timestamp</td>
 <td>Дата обновления</td>
 </tr>
 <tr>
 <td>deleted_at</td>
 <td>timestamp</td>
 <td>Soft delete</td>
 </tr>
 </tbody>
</table>
<p><strong>Права доступа:</strong> Создание, редактирование и удаление вех доступно участникам с правами выше «только чтение» (аналогично задачам).</p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Leadera: Ограничения секций в диаграмме Ганта</title>
|
||||
<link>/leadera/leadera-section-constraints/</link>
|
||||
<link>/yasha/leadera/leadera-section-constraints/</link>
|
||||
<pubDate>Mon, 06 Apr 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/leadera/leadera-section-constraints/</guid>
|
||||
<guid>/yasha/leadera/leadera-section-constraints/</guid>
|
||||
<description><h2 id="бизнес-требования">Бизнес-требования</h2>
<h3 id="br-1-лимит-одновременных-задач-concurrency-limit">BR-1: Лимит одновременных задач (Concurrency Limit)</h3>
<p>У каждой секции появляется параметр <strong>max_concurrent_tasks</strong> (целое число ≥ 0, по умолчанию 0).</p>
<p><strong>Поведение:</strong></p>
<ul>
<li>Если <code>max_concurrent_tasks = 3</code> — в любой данный день в этой секции не может быть больше 3 активных задач одновременно</li>
<li>Если <code>max_concurrent_tasks = 0</code> — ограничение отключено, лимита нет</li>
<li>Пересечение считается по дате: задача занимает дни с <code>start_date</code> по <code>end_date</code> включительно. Две задачи «пересекаются», если у них есть хотя бы один общий день</li>
<li>Ограничение работает <strong>только в рамках одной секции</strong>. Задачи из разных секций не влияют друг на друга</li>
</ul>
<p><strong>Срабатывание при:</strong></p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>29 марта 2026</title>
|
||||
<link>/diary/2026-03-29/</link>
|
||||
<link>/yasha/diary/2026-03-29/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/diary/2026-03-29/</guid>
|
||||
<guid>/yasha/diary/2026-03-29/</guid>
|
||||
<description><h2 id="timeline">Timeline</h2>
<ul>
<li><strong>20:03</strong> — Настроили Angular + Go проекты (leadera) через pm2, удалили proxy.conf, подняли PostgreSQL в Docker</li>
<li><strong>20:31</strong> — Изучили API Eversolo T8 — полный список REST-эндпоинтов для управления музыкой</li>
<li><strong>20:49</strong> — Обсудили важность записи контекста в файлы для сохранения памяти между сессиями</li>
<li><strong>21:13</strong> — Начали проект docs.a2v.space — персональная документация на Hugo</li>
<li><strong>21:44</strong> — Завершили настройку Identity (Братан, SOUL.md, USER.md, IDENTITY.md)</li>
<li><strong>22:15</strong> — Документировали инфраструктуру: VPS reverse proxy (claw.a2v.space, code.a2v.space, sand.a2v.space, docs.a2v.space)</li>
<li><strong>22:31</strong> — Настроили docs.a2v.space: Hugo сайт, 83 страницы, Bootstrap 5 тёмная тема, AES-256-GCM шифрование кредов (мастер-пароль: Popoloka16!)</li>
<li><strong>22:47</strong> — Добавили Basic auth: agaltsovav / Magamet787! для доступа к документации</li>
<li><strong>23:02</strong> — Создали cron задачу docs-a2v-daily-sync на ежедневное обновление в 03:00 МСК</li>
<li><strong>23:18</strong> — Полностью задокументировали Eversolo T8 API (воспроизведение, громкость, дисплей, питание, поиск и т.д.)</li>
<li><strong>23:45</strong> — Переписали TOOLS.md как полный справочник по инфраструктуре</li>
</ul>
<h2 id="eversolo-companion--статус-проекта">Eversolo Companion — статус проекта</h2>
<p><strong>Backend (Go + Gin):</strong></p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Завершить Eversolo Companion - этапы 3-9</title>
|
||||
<link>/plans/eversolo-companion-phases-3-9/</link>
|
||||
<link>/yasha/plans/eversolo-companion-phases-3-9/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/plans/eversolo-companion-phases-3-9/</guid>
|
||||
<guid>/yasha/plans/eversolo-companion-phases-3-9/</guid>
|
||||
<description><h2 id="описание">Описание</h2>
<p>Завершить разработку Eversolo Companion после решения проблемы с сетевым доступом.</p>
<h2 id="что-сделано">Что сделано</h2>
<ul>
<li><strong>Backend (Go + Gin)</strong>: собран, 17+ REST API эндпоинтов, WebSocket, CORS, state poller</li>
<li><strong>Frontend (Angular 17)</strong>: собран, тёмная тема (Spotify-like), 5 экранов, 5 сервисов</li>
<li><strong>PostgreSQL</strong>: Docker, порт 5433, миграции готовы</li>
<li><strong>Docker Compose</strong>: рабочий (postgres + api + frontend)</li>
<li><strong>Спецификация</strong>: полная в ~/Projects/eversolo-companion/SPEC.md</li>
</ul>
<h2 id="проблема">Проблема</h2>
<p>API не может достучаться до Eversolo T8 (192.168.0.156:9529) из-за ограничений OpenClaw sandbox.</p>
<h2 id="возможные-решения">Возможные решения</h2>
<ol>
<li>Настроить <code>tools.elevated.allowFrom.webchat</code> для разрешения сетевых соединений</li>
<li>Запустить API через launchd plist (как SSH туннели)</li>
<li>Использовать отдельный контейнер без ограничений</li>
</ol>
<h2 id="этапы-3-9-после-решения-сети">Этапы 3-9 (после решения сети)</h2>
<ol>
<li><strong>Библиотека синхронизации</strong> — двухсторонний sync с Eversolo</li>
<li><strong>Плейлисты</strong> — импорт/экспорт, редактирование</li>
<li><strong>Scrobbler</strong> — Last.fm / ListenBrainz</li>
<li><strong>Обложки</strong> — кэширование, отображение в UI</li>
<li><strong>Плеер</strong> — улучшенный интерфейс управления</li>
<li><strong>Система уведомлений</strong> — события от Eversolo</li>
<li><strong>Администрирование</strong> — настройка подключений</li>
</ol>
<h2 id="связанные-ресурсы">Связанные ресурсы</h2>
<ul>
<li><a href="../../docs/eversolo-companion-spec/">Спецификация проекта</a></li>
<li><a href="../../kb/eversolo-api/">API документация</a></li>
<li><a href="../../diary/2026-03-29/">Дневник разработки</a></li>
</ul></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Подключение sand.a2v.space (Leadera Project)</title>
|
||||
<link>/guides/sand-a2v-space/</link>
|
||||
<link>/yasha/guides/sand-a2v-space/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/guides/sand-a2v-space/</guid>
|
||||
<guid>/yasha/guides/sand-a2v-space/</guid>
|
||||
<description><h2 id="архитектура">Архитектура</h2>
<pre tabindex="0"><code>Mac Studio
 ├─ Frontend (Angular) :4200 (pm2: leadera-fe)
 ├─ Backend (Go) :8080 (pm2: leadera-api)
 └─ PostgreSQL 16 в Docker (Colima, container: app-leadera-db)
 → autossh SSH-туннель (-R 8082:localhost:4200 -R 8083:localhost:8080)
 → VPS 95.163.232.9
 → nginx HTTPS
 → https://sand.a2v.space
</code></pre><h2 id="параметры">Параметры</h2>
<table>
 <thead>
 <tr>
 <th>Компонент</th>
 <th>Локальный порт</th>
 <th>Туннель порт</th>
 <th>pm2 имя</th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>Frontend (Angular)</td>
 <td>4200</td>
 <td>8082</td>
 <td>leadera-fe</td>
 </tr>
 <tr>
 <td>Backend (Go)</td>
 <td>8080</td>
 <td>8083</td>
 <td>leadera-api</td>
 </tr>
 </tbody>
</table>
<p>SSL сертификат: <code>/etc/letsencrypt/live/sand.a2v.space/</code></p>
<h2 id="nginx-конфиг">nginx конфиг</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">80</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">sand.a2v.space</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">return</span> <span style="color:#ae81ff">301</span> <span style="color:#e6db74">https://</span>$server_name$request_uri;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">sand.a2v.space</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/sand.a2v.space/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/sand.a2v.space/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/api</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:8083</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span stylLine truncated
|
||||
</item>
|
||||
</channel>
|
||||
|
||||
+47
-43
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -121,15 +125,15 @@
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="../../leadera/leadera-milestones/">Leadera: Вехи (Milestones) на диаграмме Ганта</a></li>
|
||||
<li><a href="../../yasha/leadera/leadera-milestones/">Leadera: Вехи (Milestones) на диаграмме Ганта</a></li>
|
||||
|
||||
<li><a href="../../leadera/leadera-gantt-filters/">Leadera — Фильтры и реорганизация списка диаграмм Ганта</a></li>
|
||||
<li><a href="../../yasha/leadera/leadera-gantt-filters/">Leadera — Фильтры и реорганизация списка диаграмм Ганта</a></li>
|
||||
|
||||
<li><a href="../../leadera/leadera-section-constraints/">Leadera: Ограничения секций в диаграмме Ганта</a></li>
|
||||
<li><a href="../../yasha/leadera/leadera-section-constraints/">Leadera: Ограничения секций в диаграмме Ганта</a></li>
|
||||
|
||||
<li><a href="../../plans/eversolo-control/">Реализовать управление Eversolo из OpenClaw</a></li>
|
||||
<li><a href="../../yasha/plans/eversolo-control/">Реализовать управление Eversolo из OpenClaw</a></li>
|
||||
|
||||
<li><a href="../../kb/eversolo-api/">Eversolo T8 API</a></li>
|
||||
<li><a href="../../yasha/kb/eversolo-api/">Eversolo T8 API</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
+10
-10
@@ -10,37 +10,37 @@
|
||||
<atom:link href="/tags/api/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>Leadera — Фильтры и реорганизация списка диаграмм Ганта</title>
|
||||
<link>/leadera/leadera-gantt-filters/</link>
|
||||
<link>/yasha/leadera/leadera-gantt-filters/</link>
|
||||
<pubDate>Tue, 07 Apr 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/leadera/leadera-gantt-filters/</guid>
|
||||
<guid>/yasha/leadera/leadera-gantt-filters/</guid>
|
||||
<description><h1 id="фильтры-и-реорганизация-списка-диаграмм-ганта">Фильтры и реорганизация списка диаграмм Ганта</h1>
<p><strong>Дата:</strong> 07.04.2026
<strong>Статус:</strong> Требования
<strong>Проект:</strong> Leadera (dev — sand.a2v.space)</p>
<hr>
<h2 id="1-постановка-задачи">1. Постановка задачи</h2>
<p>Список диаграмм Ганта должен показывать <strong>все доступные пользователю диаграммы</strong> из всех пространств (spaces), с возможностью фильтрации и поиска. Сейчас список привязан к одному пространству — нужно убрать эту зависимость и добавить удобный toolbar.</p>
<hr>
<h2 id="2-изменения-api">2. Изменения API</h2>
<h3 id="21-новый-endpoint-списка-диаграмм">2.1. Новый endpoint списка диаграмм</h3>
<p><strong>Было:</strong></p>
<pre tabindex="0"><code>GET /api/v1/spaces/{spaceId}/gantt-charts?page=1&amp;per_page=10
</code></pre><p><strong>Стало:</strong></p>
<pre tabindex="0"><code>GET /api/v1/gantt-charts?page=1&amp;per_page=10
</code></pre><p>Пользователь видит все диаграммы из <strong>всех пространств</strong>, где он является подтверждённым участником (статус <code>accepted</code>).</p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Leadera: Вехи (Milestones) на диаграмме Ганта</title>
|
||||
<link>/leadera/leadera-milestones/</link>
|
||||
<link>/yasha/leadera/leadera-milestones/</link>
|
||||
<pubDate>Wed, 08 Apr 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/leadera/leadera-milestones/</guid>
|
||||
<guid>/yasha/leadera/leadera-milestones/</guid>
|
||||
<description><h2 id="бизнес-требования">Бизнес-требования</h2>
<h3 id="br-1-сущность-веха-milestone">BR-1: Сущность «Веха» (Milestone)</h3>
<p>Веха — это маркер на таймлайне диаграммы Ганта, обозначающий важную дату в проекте. В отличие от задач, веха не имеет длительности и привязана к конкретной дате.</p>
<p><strong>Поля вехи:</strong></p>
<table>
 <thead>
 <tr>
 <th>Поле</th>
 <th>Тип</th>
 <th>Описание</th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>id</td>
 <td>UUID</td>
 <td>Идентификатор</td>
 </tr>
 <tr>
 <td>gantt_chart_id</td>
 <td>UUID</td>
 <td>Принадлежность к диаграмме (не к секции!)</td>
 </tr>
 <tr>
 <td>title</td>
 <td>string</td>
 <td>Название вехи (обязательное, 1–255 символов)</td>
 </tr>
 <tr>
 <td>date</td>
 <td>date</td>
 <td>Дата вехи (без времени, обязательная)</td>
 </tr>
 <tr>
 <td>color</td>
 <td>string</td>
 <td>Цвет маркера (HEX, по умолчанию <code>#dc3545</code> — красный)</td>
 </tr>
 <tr>
 <td>created_by</td>
 <td>UUID</td>
 <td>Кто создал (не отображается в UI, хранится для аудита)</td>
 </tr>
 <tr>
 <td>updated_by</td>
 <td>UUID</td>
 <td>Кто обновил (не отображается в UI)</td>
 </tr>
 <tr>
 <td>created_at</td>
 <td>timestamp</td>
 <td>Дата создания</td>
 </tr>
 <tr>
 <td>updated_at</td>
 <td>timestamp</td>
 <td>Дата обновления</td>
 </tr>
 <tr>
 <td>deleted_at</td>
 <td>timestamp</td>
 <td>Soft delete</td>
 </tr>
 </tbody>
</table>
<p><strong>Права доступа:</strong> Создание, редактирование и удаление вех доступно участникам с правами выше «только чтение» (аналогично задачам).</p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Leadera: Ограничения секций в диаграмме Ганта</title>
|
||||
<link>/leadera/leadera-section-constraints/</link>
|
||||
<link>/yasha/leadera/leadera-section-constraints/</link>
|
||||
<pubDate>Mon, 06 Apr 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/leadera/leadera-section-constraints/</guid>
|
||||
<guid>/yasha/leadera/leadera-section-constraints/</guid>
|
||||
<description><h2 id="бизнес-требования">Бизнес-требования</h2>
<h3 id="br-1-лимит-одновременных-задач-concurrency-limit">BR-1: Лимит одновременных задач (Concurrency Limit)</h3>
<p>У каждой секции появляется параметр <strong>max_concurrent_tasks</strong> (целое число ≥ 0, по умолчанию 0).</p>
<p><strong>Поведение:</strong></p>
<ul>
<li>Если <code>max_concurrent_tasks = 3</code> — в любой данный день в этой секции не может быть больше 3 активных задач одновременно</li>
<li>Если <code>max_concurrent_tasks = 0</code> — ограничение отключено, лимита нет</li>
<li>Пересечение считается по дате: задача занимает дни с <code>start_date</code> по <code>end_date</code> включительно. Две задачи «пересекаются», если у них есть хотя бы один общий день</li>
<li>Ограничение работает <strong>только в рамках одной секции</strong>. Задачи из разных секций не влияют друг на друга</li>
</ul>
<p><strong>Срабатывание при:</strong></p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Eversolo T8 API</title>
|
||||
<link>/kb/eversolo-api/</link>
|
||||
<link>/yasha/kb/eversolo-api/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/kb/eversolo-api/</guid>
|
||||
<guid>/yasha/kb/eversolo-api/</guid>
|
||||
<description><p>Eversolo T8 — музыкальный стример.</p>
<p><strong>IP:</strong> 192.168.0.156
<strong>Порт:</strong> 9529
<strong>Авторизация:</strong> нет</p>
<p>Базовый URL: <code>http://192.168.0.156:9529</code></p>
<h2 id="воспроизведение">Воспроизведение</h2>
<table>
 <thead>
 <tr>
 <th>Действие</th>
 <th>URL</th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>Play/Pause</td>
 <td><code>/ZidooMusicControl/v2/playOrPause</code></td>
 </tr>
 <tr>
 <td>Следующий трек</td>
 <td><code>/ZidooMusicControl/v2/playNext</code></td>
 </tr>
 <tr>
 <td>Предыдущий трек</td>
 <td><code>/ZidooMusicControl/v2/playLast</code></td>
 </tr>
 <tr>
 <td>Перемотка (ms)</td>
 <td><code>/ZidooMusicControl/v2/seekTo?time=&lt;ms&gt;</code></td>
 </tr>
 <tr>
 <td>Воспроизвести по ID</td>
 <td><code>/ZidooMusicControl/v2/playMusic?type=4&amp;id=&lt;id&gt;</code></td>
 </tr>
 <tr>
 <td>Состояние</td>
 <td><code>/ZidooMusicControl/v2/getState</code></td>
 </tr>
 </tbody>
</table>
<h2 id="громкость">Громкость</h2>
<table>
 <thead>
 <tr>
 <th>Действие</th>
 <th>URL</th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>Громкость вверх</td>
 <td><code>/ZidooControlCenter/RemoteControl/sendkey?key=Key.VolumeUp</code></td>
 </tr>
 <tr>
 <td>Громкость вниз</td>
 <td><code>/ZidooControlCenter/RemoteControl/sendkey?key=Key.VolumeDown</code></td>
 </tr>
 <tr>
 <td>Точная громкость</td>
 <td><code>/ZidooMusicControl/v2/setDevicesVolume?volume=&lt;0-200&gt;</code></td>
 </tr>
 <tr>
 <td>Mute</td>
 <td><code>/ZidooMusicControl/v2/setMuteVolume?isMute=1</code></td>
 </tr>
 <tr>
 <td>Unmute</td>
 <td><code>/ZidooMusicControl/v2/setMuteVolume?isMute=0</code></td>
 </tr>
 </tbody>
</table>
<h2 id="входывыходы">Входы/Выходы</h2>
<table>
 <thead>
 <tr>
 <th>Действие</th>
 <th>URL</th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>Получить список</td>
 <td><code>/ZidooMusicControl/v2/getInputAndOutputList</code></td>
 </tr>
 <tr>
 <td>Установить вход</td>
 <td><code>/ZidooMusicControl/v2/setInputList?tag=&lt;tag&gt;&amp;index=&lt;n&gt;</code></td>
 </tr>
 <tr>
 <td>Установить выход</td>
 <td><code>/ZidooMusicControl/v2/setOutInputList?tag=&lt;tag&gt;&amp;index=&lt;n&gt;</code></td>
 </tr>
 </tbody>
</table>
<p>Теги выхода: RCA, XLRRCA, XLR, HDMI, SPDIF, USB</p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Реализовать управление Eversolo из OpenClaw</title>
|
||||
<link>/plans/eversolo-control/</link>
|
||||
<link>/yasha/plans/eversolo-control/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/plans/eversolo-control/</guid>
|
||||
<guid>/yasha/plans/eversolo-control/</guid>
|
||||
<description><h2 id="описание">Описание</h2>
<p>Добавить инструменты управления Eversolo T8 в OpenClaw.</p>
<h2 id="что-сделано">Что сделано</h2>
<ul>
<li>Изучен полный API Eversolo T8 (см. <a href="../../kb/eversolo-api/">Базу знаний</a>)</li>
</ul>
<h2 id="проблема">Проблема</h2>
<p>Sandbox OpenClaw не имеет доступа к локальной сети, где находится Eversolo.</p>
<h2 id="возможные-решения">Возможные решения</h2>
<ol>
<li>Настроить проброс портов</li>
<li>Запустить OpenClaw без sandbox-ограничений</li>
<li>Использовать промежуточный proxy-сервис</li>
</ol></description>
|
||||
</item>
|
||||
</channel>
|
||||
|
||||
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -121,9 +125,9 @@
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="../../leadera/leadera-websocket-gantt/">Leadera — WebSocket для диаграмм Ганта</a></li>
|
||||
<li><a href="../../yasha/leadera/leadera-websocket-gantt/">Leadera — WebSocket для диаграмм Ганта</a></li>
|
||||
|
||||
<li><a href="../../leadera/leadera-audit/">Аудит Leadera — полный отчёт</a></li>
|
||||
<li><a href="../../yasha/leadera/leadera-audit/">Аудит Leadera — полный отчёт</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -10,16 +10,16 @@
|
||||
<atom:link href="/tags/architecture/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>Аудит Leadera — полный отчёт</title>
|
||||
<link>/leadera/leadera-audit/</link>
|
||||
<link>/yasha/leadera/leadera-audit/</link>
|
||||
<pubDate>Mon, 30 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/leadera/leadera-audit/</guid>
|
||||
<guid>/yasha/leadera/leadera-audit/</guid>
|
||||
<description><h1 id="аудит-проекта-leadera">Аудит проекта Leadera</h1>
<p><strong>Дата:</strong> 30.03.2026<br>
<strong>Версия:</strong> pre-production, миграции 1–5</p>
<hr>
<h2 id="1-обзор-проекта">1. Обзор проекта</h2>
<p>Leadera — SaaS-система управления IT-ландшафтом с диаграммами Ганта.</p>
<h3 id="стек">Стек</h3>
<table>
 <thead>
 <tr>
 <th>Компонент</th>
 <th>Технологии</th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>Backend</td>
 <td>Go 1.21+, Gin, PostgreSQL 16, pgx/v5 + sqlx, JWT (HMAC-SHA256)</td>
 </tr>
 <tr>
 <td>Frontend</td>
 <td>Angular 21.1, Bootstrap 5, NgBootstrap, RxJS, Signals</td>
 </tr>
 <tr>
 <td>Инфраструктура</td>
 <td>Docker (Colima), pm2, nginx reverse proxy, VPS reg.ru</td>
 </tr>
 </tbody>
</table>
<h3 id="доменная-модель">Доменная модель</h3>
<pre tabindex="0"><code>User ──┬── Space (через space_members с role)
 │
 └── GanttChart (принадлежит Space)
 ├── GanttSection
 └── GanttTask
</code></pre><p><strong>Роли:</strong> admin, editor, viewer — назначаются <strong>на уровне Space</strong> через таблицу <code>space_members</code>.</p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Leadera — WebSocket для диаграмм Ганта</title>
|
||||
<link>/leadera/leadera-websocket-gantt/</link>
|
||||
<link>/yasha/leadera/leadera-websocket-gantt/</link>
|
||||
<pubDate>Tue, 07 Apr 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/leadera/leadera-websocket-gantt/</guid>
|
||||
<guid>/yasha/leadera/leadera-websocket-gantt/</guid>
|
||||
<description><h1 id="websocket-для-диаграмм-ганта--realtime-совместное-редактирование">WebSocket для диаграмм Ганта — realtime-совместное редактирование</h1>
<p><strong>Дата:</strong> 07.04.2026
<strong>Статус:</strong> Планирование
<strong>Проект:</strong> Leadera (dev — sand.a2v.space)</p>
<hr>
<h2 id="1-постановка-задачи">1. Постановка задачи</h2>
<p>Диаграммы Ганта должны обновляться в реальном времени при совместной работе: если два или более пользователей редактируют один Гант, изменения каждого мгновенно отображаются у остальных — без перезагрузки страницы и ручного обновления.</p>
<hr>
<h2 id="2-архитектура">2. Архитектура</h2>
<pre tabindex="0"><code>Angular frontend ←── WebSocket ──→ Go backend (Hub) ──→ PostgreSQL
 │ │
 │ HTTP (мутации) │ Broadcast
 └──→ REST API ──→ Service ──→ DB ──→ Hub ──→ все клиенты комнаты
</code></pre><p><strong>Ключевой принцип:</strong> мутации данных идут через HTTP (REST), WebSocket используется <strong>только для доставки уведомлений</strong>. Сервер — источник истины, клиент не может обойти проверки через WS.</p></description>
|
||||
</item>
|
||||
</channel>
|
||||
|
||||
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -121,25 +125,25 @@
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="../../personal-site/netlify-node-version/">Устаревший Node.js 16 в Netlify</a></li>
|
||||
<li><a href="../../yasha/personal-site/netlify-node-version/">Устаревший Node.js 16 в Netlify</a></li>
|
||||
|
||||
<li><a href="../../personal-site/no-node-modules/">Отсутствуют node_modules — нужна установка</a></li>
|
||||
<li><a href="../../yasha/personal-site/no-node-modules/">Отсутствуют node_modules — нужна установка</a></li>
|
||||
|
||||
<li><a href="../../personal-site/binary-files-in-repo/">Бинарный файл в репозитории</a></li>
|
||||
<li><a href="../../yasha/personal-site/binary-files-in-repo/">Бинарный файл в репозитории</a></li>
|
||||
|
||||
<li><a href="../../personal-site/no-analytics/">Yandex Metrika отключена</a></li>
|
||||
<li><a href="../../yasha/personal-site/no-analytics/">Yandex Metrika отключена</a></li>
|
||||
|
||||
<li><a href="../../personal-site/twitter-handle-mismatch/">Twitter handle не соответствует имени</a></li>
|
||||
<li><a href="../../yasha/personal-site/twitter-handle-mismatch/">Twitter handle не соответствует имени</a></li>
|
||||
|
||||
<li><a href="../../personal-site/outdated-doks-theme/">Hugo Doks 0.5 — сильно устарел</a></li>
|
||||
<li><a href="../../yasha/personal-site/outdated-doks-theme/">Hugo Doks 0.5 — сильно устарел</a></li>
|
||||
|
||||
<li><a href="../../personal-site/digest-stale/">Digest не обновлялся с февраля 2025</a></li>
|
||||
<li><a href="../../yasha/personal-site/digest-stale/">Digest не обновлялся с февраля 2025</a></li>
|
||||
|
||||
<li><a href="../../personal-site/copyright-outdated/">Copyright без 2026 года</a></li>
|
||||
<li><a href="../../yasha/personal-site/copyright-outdated/">Copyright без 2026 года</a></li>
|
||||
|
||||
<li><a href="../../personal-site/">Личный сайт (agaltsovav.ru)</a></li>
|
||||
<li><a href="../../yasha/personal-site/">Личный сайт (agaltsovav.ru)</a></li>
|
||||
|
||||
<li><a href="../../leadera/leadera-audit/">Аудит Leadera — полный отчёт</a></li>
|
||||
<li><a href="../../yasha/leadera/leadera-audit/">Аудит Leadera — полный отчёт</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
+20
-20
@@ -10,72 +10,72 @@
|
||||
<atom:link href="/tags/audit/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>Личный сайт (agaltsovav.ru)</title>
|
||||
<link>/personal-site/</link>
|
||||
<link>/yasha/personal-site/</link>
|
||||
<pubDate>Tue, 31 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/personal-site/</guid>
|
||||
<guid>/yasha/personal-site/</guid>
|
||||
<description></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Аудит Leadera — полный отчёт</title>
|
||||
<link>/leadera/leadera-audit/</link>
|
||||
<link>/yasha/leadera/leadera-audit/</link>
|
||||
<pubDate>Mon, 30 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/leadera/leadera-audit/</guid>
|
||||
<guid>/yasha/leadera/leadera-audit/</guid>
|
||||
<description><h1 id="аудит-проекта-leadera">Аудит проекта Leadera</h1>
<p><strong>Дата:</strong> 30.03.2026<br>
<strong>Версия:</strong> pre-production, миграции 1–5</p>
<hr>
<h2 id="1-обзор-проекта">1. Обзор проекта</h2>
<p>Leadera — SaaS-система управления IT-ландшафтом с диаграммами Ганта.</p>
<h3 id="стек">Стек</h3>
<table>
 <thead>
 <tr>
 <th>Компонент</th>
 <th>Технологии</th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>Backend</td>
 <td>Go 1.21+, Gin, PostgreSQL 16, pgx/v5 + sqlx, JWT (HMAC-SHA256)</td>
 </tr>
 <tr>
 <td>Frontend</td>
 <td>Angular 21.1, Bootstrap 5, NgBootstrap, RxJS, Signals</td>
 </tr>
 <tr>
 <td>Инфраструктура</td>
 <td>Docker (Colima), pm2, nginx reverse proxy, VPS reg.ru</td>
 </tr>
 </tbody>
</table>
<h3 id="доменная-модель">Доменная модель</h3>
<pre tabindex="0"><code>User ──┬── Space (через space_members с role)
 │
 └── GanttChart (принадлежит Space)
 ├── GanttSection
 └── GanttTask
</code></pre><p><strong>Роли:</strong> admin, editor, viewer — назначаются <strong>на уровне Space</strong> через таблицу <code>space_members</code>.</p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Copyright без 2026 года</title>
|
||||
<link>/personal-site/copyright-outdated/</link>
|
||||
<link>/yasha/personal-site/copyright-outdated/</link>
|
||||
<pubDate>Tue, 31 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/personal-site/copyright-outdated/</guid>
|
||||
<guid>/yasha/personal-site/copyright-outdated/</guid>
|
||||
<description><h2 id="проблема">Проблема</h2>
<p>В <code>params.toml</code>:</p>
<pre tabindex="0"><code>copyRight = &#34;Copyright (c) 2020-2025 Антон Агальцов&#34;
</code></pre><p>Сейчас 2026 год — нужно обновить.</p>
<h3 id="рекомендация">Рекомендация</h3>
<p>Обновить на <code>2020-2026</code>. Или сделать динамическим через Hugo template:</p>
<pre tabindex="0"><code>Copyright (c) 2020-{{ now.Year }} Антон Агальцов
</code></pre></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Digest не обновлялся с февраля 2025</title>
|
||||
<link>/personal-site/digest-stale/</link>
|
||||
<link>/yasha/personal-site/digest-stale/</link>
|
||||
<pubDate>Tue, 31 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/personal-site/digest-stale/</guid>
|
||||
<guid>/yasha/personal-site/digest-stale/</guid>
|
||||
<description><h2 id="проблема">Проблема</h2>
<p>Последний дайджест — <strong>февраль 2025</strong>. С тех пор 12+ месяцев без новых выпусков.</p>
<h3 id="что-есть">Что есть</h3>
<ul>
<li>27 файлов дайджестов (январь 2023 — февраль 2025)</li>
<li>Автоматизация через <code>digest.js</code> — скрипт для генерации</li>
<li>Но скрипт не запускается автоматически</li>
</ul>
<h3 id="риск">Риск</h3>
<p>Low — контент устаревает, SEO-позиции падают для страниц дайджестов.</p>
<h3 id="рекомендация">Рекомендация</h3>
<ol>
<li>Настроить cron/CI для ежемесячной генерации дайджеста</li>
<li>Или интегрировать с OpenClaw — авто-сборка дайджеста на основе новых публикаций</li>
</ol></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Hugo Doks 0.5 — сильно устарел</title>
|
||||
<link>/personal-site/outdated-doks-theme/</link>
|
||||
<link>/yasha/personal-site/outdated-doks-theme/</link>
|
||||
<pubDate>Tue, 31 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/personal-site/outdated-doks-theme/</guid>
|
||||
<guid>/yasha/personal-site/outdated-doks-theme/</guid>
|
||||
<description><h2 id="проблема">Проблема</h2>
<p>Проект использует тему <strong>Hyas/Doks v0.5.0</strong> (package.json). Актуальная версия Doks — <strong>1.x</strong> с полной переработкой архитектуры.</p>
<h3 id="что-не-так">Что не так</h3>
<ul>
<li>Doks 0.5 использует устаревший Bootstrap 5.2.0-beta1</li>
<li>Hugo 0.148.2 в зависимостях — новый, но интеграция с темой старая</li>
<li>Конфигурация через <code>theme.toml</code> — в новых версиях перешли на другой формат</li>
<li><code>exec-bin</code> для вызова Hugo — устаревший подход, текущие версии Hugo ставятся глобально</li>
</ul>
<h3 id="риск">Риск</h3>
<p>Medium — работает, но миграция на Doks 1.x потребует значительного рефакторинга layout-ов и конфигурации. Чем дольше откладывать — тем сложнее.</p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Twitter handle не соответствует имени</title>
|
||||
<link>/personal-site/twitter-handle-mismatch/</link>
|
||||
<link>/yasha/personal-site/twitter-handle-mismatch/</link>
|
||||
<pubDate>Tue, 31 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/personal-site/twitter-handle-mismatch/</guid>
|
||||
<guid>/yasha/personal-site/twitter-handle-mismatch/</guid>
|
||||
<description><h2 id="проблема">Проблема</h2>
<p>В <code>params.toml</code> указан Twitter: <code>@AntonTotoshkin</code></p>
<p>Но владелец сайта — <strong>Антон Агальцов</strong>. Ники не совпадают.</p>
<h3 id="два-варианта">Два варианта</h3>
<ol>
<li>Это твой старый/другой аккаунт — тогда ок, но стоит указать актуальный</li>
<li>Это чужой аккаунт — тогда нужно убрать или исправить</li>
</ol>
<h3 id="затронутые-места">Затронутые места</h3>
<ul>
<li><code>twitterSite = &quot;@AntonTotoshkin&quot;</code></li>
<li><code>schemaAuthorTwitter = &quot;https://twitter.com/AntonTotoshkin&quot;</code></li>
<li><code>schemaTwitter = &quot;https://twitter.com/AntonTotoshkin&quot;</code></li>
</ul>
<h3 id="рекомендация">Рекомендация</h3>
<p>Указать актуальный Twitter/X или убрать, если соцсеть не используется.</p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Yandex Metrika отключена</title>
|
||||
<link>/personal-site/no-analytics/</link>
|
||||
<link>/yasha/personal-site/no-analytics/</link>
|
||||
<pubDate>Tue, 31 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/personal-site/no-analytics/</guid>
|
||||
<guid>/yasha/personal-site/no-analytics/</guid>
|
||||
<description><h2 id="проблема">Проблема</h2>
<p>В <code>params.toml</code>: <code>yandexMetrika = false</code></p>
<p>Аналитика с сайта не собирается. Непонятно, есть ли вообще посетители и какие страницы популярны.</p>
<h3 id="рекомендация">Рекомендация</h3>
<ol>
<li>Включить Yandex Metrika или перейти на <strong>Plausible</strong> / <strong>Umami</strong> (privacy-friendly)</li>
<li>Если сайт не нуждается в аналитике — оставить как есть</li>
<li>Альтернатива: Netlify Analytics (уже встроен в хостинг)</li>
</ol></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Бинарный файл в репозитории</title>
|
||||
<link>/personal-site/binary-files-in-repo/</link>
|
||||
<link>/yasha/personal-site/binary-files-in-repo/</link>
|
||||
<pubDate>Tue, 31 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/personal-site/binary-files-in-repo/</guid>
|
||||
<guid>/yasha/personal-site/binary-files-in-repo/</guid>
|
||||
<description><h2 id="проблема">Проблема</h2>
<p>В корне репозитория лежат два файла, которые не должны быть в git:</p>
<ul>
<li><strong><code>Шаблоны для сайта.key</code></strong> — Apple Keynote файл (бинарный)</li>
<li><strong><code>logo.psd</code></strong> — Photoshop файл (бинарный)</li>
</ul>
<h3 id="что-не-так">Что не так</h3>
<ul>
<li>Увеличивают размер репозитория</li>
<li>Git неэффективно хранит бинарные файлы (каждая версия — полностью)</li>
<li><code>.key</code> файлы могут содержать конфиденциальные данные шаблонов</li>
</ul>
<h3 id="рекомендация">Рекомендация</h3>
<ol>
<li>Перенести в отдельное хранилище (Google Drive, облако)</li>
<li>Удалить из git-истории через <code>git filter-repo</code></li>
<li>Добавить в <code>.gitignore</code>: <code>*.key</code>, <code>*.psd</code>, <code>*.sketch</code></li>
</ol></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Отсутствуют node_modules — нужна установка</title>
|
||||
<link>/personal-site/no-node-modules/</link>
|
||||
<link>/yasha/personal-site/no-node-modules/</link>
|
||||
<pubDate>Tue, 31 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/personal-site/no-node-modules/</guid>
|
||||
<guid>/yasha/personal-site/no-node-modules/</guid>
|
||||
<description><h2 id="проблема">Проблема</h2>
<p><code>node_modules/</code> отсутствует — проект нельзя собрать без <code>npm install</code>.</p>
<h3 id="риск">Риск</h3>
<p>Low — стандартная ситуация. Но:</p>
<ul>
<li><code>package-lock.json</code> привязан к Node 16, а на Mac Studio текущий Node v24</li>
<li>Могут быть конфликты зависимостей (canvas, babel, postcss)</li>
<li>Hugo 0.148.2 ставится через <code>hugo-installer</code> в npm — может не совпасть с глобальным Hugo</li>
</ul>
<h3 id="рекомендация">Рекомендация</h3>
<ol>
<li>Убедиться что <code>npm install</code> проходит без ошибок на текущем Node</li>
<li>Если нет — обновить зависимости или использовать nvm для Node 16</li>
<li>Рассмотреть переход на глобальный Hugo вместо <code>exec-bin</code></li>
</ol></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Устаревший Node.js 16 в Netlify</title>
|
||||
<link>/personal-site/netlify-node-version/</link>
|
||||
<link>/yasha/personal-site/netlify-node-version/</link>
|
||||
<pubDate>Tue, 31 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/personal-site/netlify-node-version/</guid>
|
||||
<guid>/yasha/personal-site/netlify-node-version/</guid>
|
||||
<description><h1 id="устаревший-nodejs-16-в-netlify">Устаревший Node.js 16 в Netlify</h1>
<p><strong>Файл:</strong> <code>netlify.toml:5</code></p>
<pre tabindex="0"><code>NODE_VERSION = &#34;16.16.0&#34;
</code></pre><p>Node.js 16 вышел из поддержки (EOL) в сентябре 2023. Сборка на Netlify может начать падать или работать некорректно.</p>
<p><strong>Рекомендация:</strong> Обновить до Node.js 20 LTS:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span><span style="color:#a6e22e">NODE_VERSION</span> = <span style="color:#e6db74">&#34;20&#34;</span>
</span></span></code></pre></div></description>
|
||||
</item>
|
||||
</channel>
|
||||
|
||||
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -121,7 +125,7 @@
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="../../creds/docs-a2v-space-auth/">docs.a2v.space Basic Auth</a></li>
|
||||
<li><a href="../../yasha/creds/docs-a2v-space-auth/">docs.a2v.space Basic Auth</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
<atom:link href="/tags/authentication/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>docs.a2v.space Basic Auth</title>
|
||||
<link>/creds/docs-a2v-space-auth/</link>
|
||||
<link>/yasha/creds/docs-a2v-space-auth/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/creds/docs-a2v-space-auth/</guid>
|
||||
<guid>/yasha/creds/docs-a2v-space-auth/</guid>
|
||||
<description><h2 id="basic-authentication">Basic Authentication</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>Username: agaltsovav
</span></span><span style="display:flex;"><span>Password: Magamet787!
</span></span></code></pre></div><h2 id="конфигурация-nginx">Конфигурация Nginx</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">8091</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">docs.a2v.space</span>;
</span></span><span style="display:flex;"><span> 
</span></span><span style="display:flex;"><span> <span style="color:#f92672">auth_basic</span> <span style="color:#e6db74">&#34;Restricted</span> <span style="color:#e6db74">Access&#34;</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">auth_basic_user_file</span> <span style="color:#e6db74">/etc/nginx/.htpasswd</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="доступ">Доступ</h2>
<ul>
<li><strong>URL</strong>: <a href="https://docs.a2v.space">https://docs.a2v.space</a> (через reverse proxy)</li>
<li><strong>Local</strong>: http://localhost:8091 (для разработки)</li>
<li><strong>Auth</strong>: всегда требуется</li>
</ul>
<h2 id="мастер-пароль-для-шифрования">Мастер-пароль для шифрования</h2>
<p><strong>Важно</strong>: Пароль для расшифровки файлов в разделе <code>creds/</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>Master Password: Popoloka16!
</span></span></code></pre></div><h2 id="шифрование">Шифрование</h2>
<ul>
<li>Алгоритм: AES-256-GCM</li>
<li>Хранилище: client-side encryption</li>
<li>Файлы шифруются при сохранении</li>
<li>Расшифровываются при чтении</li>
</ul></description>
|
||||
</item>
|
||||
</channel>
|
||||
|
||||
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -121,7 +125,7 @@
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="../../diary/2026-03-31/">31 марта 2026</a></li>
|
||||
<li><a href="../../yasha/diary/2026-03-31/">31 марта 2026</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
<atom:link href="/tags/canvas/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>31 марта 2026</title>
|
||||
<link>/diary/2026-03-31/</link>
|
||||
<link>/yasha/diary/2026-03-31/</link>
|
||||
<pubDate>Tue, 31 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/diary/2026-03-31/</guid>
|
||||
<guid>/yasha/diary/2026-03-31/</guid>
|
||||
<description><h2 id="timeline">Timeline</h2>
<ul>
<li><strong>Утро</strong> — Обсуждали аналоги htop: порекомендовал btm, btop, glances. Создан конфиг <code>~/.config/bottom/bottom.toml</code></li>
<li><strong>День</strong> — Разбирались со скиллами OpenCode: как работают, автоматический подбор по description</li>
<li><strong>День</strong> — Созданы скиллы для Leadera проектов (Go: build-and-fix, restart-api; Angular: build-and-fix)</li>
<li><strong>День</strong> — Обсудили миграцию Ганта с DOM на Canvas 2D (Konva.js) — real-time коллаборация, экспорт PDF/PNG, 500+ задач</li>
<li><strong>Вечер</strong> — Установлен gh CLI, авторизован как agaltsovav</li>
<li><strong>Вечер</strong> — Созданы GitHub Projects: Leadera Roadmap (21 задача, P0–P3), agaltsovav.ru Roadmap (7 задач)</li>
</ul>
<h2 id="системные-утилиты">Системные утилиты</h2>
<ul>
<li><strong>bottom</strong> (btm) — выбранный мониторинг, конфиг в <code>~/.config/bottom/bottom.toml</code></li>
<li>Альтернативы: btop, glances</li>
</ul>
<h2 id="opencode-skills">OpenCode Skills</h2>
<p>Созданы скиллы:</p></description>
|
||||
</item>
|
||||
</channel>
|
||||
|
||||
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -121,11 +125,11 @@
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="../../plans/eversolo-companion-phases-3-9/">Завершить Eversolo Companion - этапы 3-9</a></li>
|
||||
<li><a href="../../yasha/plans/eversolo-companion-phases-3-9/">Завершить Eversolo Companion - этапы 3-9</a></li>
|
||||
|
||||
<li><a href="../../creds/eversolo-companion-postgres/">Eversolo Companion PostgreSQL</a></li>
|
||||
<li><a href="../../yasha/creds/eversolo-companion-postgres/">Eversolo Companion PostgreSQL</a></li>
|
||||
|
||||
<li><a href="../../diary/2026-03-29/">29 марта 2026</a></li>
|
||||
<li><a href="../../yasha/diary/2026-03-29/">29 марта 2026</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -10,23 +10,23 @@
|
||||
<atom:link href="/tags/companion/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>29 марта 2026</title>
|
||||
<link>/diary/2026-03-29/</link>
|
||||
<link>/yasha/diary/2026-03-29/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/diary/2026-03-29/</guid>
|
||||
<guid>/yasha/diary/2026-03-29/</guid>
|
||||
<description><h2 id="timeline">Timeline</h2>
<ul>
<li><strong>20:03</strong> — Настроили Angular + Go проекты (leadera) через pm2, удалили proxy.conf, подняли PostgreSQL в Docker</li>
<li><strong>20:31</strong> — Изучили API Eversolo T8 — полный список REST-эндпоинтов для управления музыкой</li>
<li><strong>20:49</strong> — Обсудили важность записи контекста в файлы для сохранения памяти между сессиями</li>
<li><strong>21:13</strong> — Начали проект docs.a2v.space — персональная документация на Hugo</li>
<li><strong>21:44</strong> — Завершили настройку Identity (Братан, SOUL.md, USER.md, IDENTITY.md)</li>
<li><strong>22:15</strong> — Документировали инфраструктуру: VPS reverse proxy (claw.a2v.space, code.a2v.space, sand.a2v.space, docs.a2v.space)</li>
<li><strong>22:31</strong> — Настроили docs.a2v.space: Hugo сайт, 83 страницы, Bootstrap 5 тёмная тема, AES-256-GCM шифрование кредов (мастер-пароль: Popoloka16!)</li>
<li><strong>22:47</strong> — Добавили Basic auth: agaltsovav / Magamet787! для доступа к документации</li>
<li><strong>23:02</strong> — Создали cron задачу docs-a2v-daily-sync на ежедневное обновление в 03:00 МСК</li>
<li><strong>23:18</strong> — Полностью задокументировали Eversolo T8 API (воспроизведение, громкость, дисплей, питание, поиск и т.д.)</li>
<li><strong>23:45</strong> — Переписали TOOLS.md как полный справочник по инфраструктуре</li>
</ul>
<h2 id="eversolo-companion--статус-проекта">Eversolo Companion — статус проекта</h2>
<p><strong>Backend (Go + Gin):</strong></p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Eversolo Companion PostgreSQL</title>
|
||||
<link>/creds/eversolo-companion-postgres/</link>
|
||||
<link>/yasha/creds/eversolo-companion-postgres/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/creds/eversolo-companion-postgres/</guid>
|
||||
<guid>/yasha/creds/eversolo-companion-postgres/</guid>
|
||||
<description><h2 id="docker-compose-креды-projectseversolo-companiondocker-composeyml">Docker Compose креды (~/Projects/eversolo-companion/docker-compose.yml)</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">postgres</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_USER</span>: <span style="color:#ae81ff">postgres</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_PASSWORD</span>: <span style="color:#ae81ff">password</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_DB</span>: <span style="color:#ae81ff">eversolo</span>
</span></span></code></pre></div><h2 id="локальные-креды-env-файл">Локальные креды (.env файл)</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>POSTGRES_USER<span style="color:#f92672">=</span>postgres
</span></span><span style="display:flex;"><span>POSTGRES_PASSWORD<span style="color:#f92672">=</span>password
</span></span><span style="display:flex;"><span>POSTGRES_DB<span style="color:#f92672">=</span>eversolo
</span></span></code></pre></div><h2 id="хостинг">Хостинг</h2>
<ul>
<li><strong>Host</strong>: localhost</li>
<li><strong>Port</strong>: 5433</li>
<li><strong>Database</strong>: eversolo</li>
</ul>
<h2 id="безопасность">Безопасность</h2>
<ul>
<li>Используйте разные пароли для production</li>
<li>Включите SSL для удаленного подключения</li>
<li>Регулярно делайте бэкапы</li>
</ul></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Завершить Eversolo Companion - этапы 3-9</title>
|
||||
<link>/plans/eversolo-companion-phases-3-9/</link>
|
||||
<link>/yasha/plans/eversolo-companion-phases-3-9/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/plans/eversolo-companion-phases-3-9/</guid>
|
||||
<guid>/yasha/plans/eversolo-companion-phases-3-9/</guid>
|
||||
<description><h2 id="описание">Описание</h2>
<p>Завершить разработку Eversolo Companion после решения проблемы с сетевым доступом.</p>
<h2 id="что-сделано">Что сделано</h2>
<ul>
<li><strong>Backend (Go + Gin)</strong>: собран, 17+ REST API эндпоинтов, WebSocket, CORS, state poller</li>
<li><strong>Frontend (Angular 17)</strong>: собран, тёмная тема (Spotify-like), 5 экранов, 5 сервисов</li>
<li><strong>PostgreSQL</strong>: Docker, порт 5433, миграции готовы</li>
<li><strong>Docker Compose</strong>: рабочий (postgres + api + frontend)</li>
<li><strong>Спецификация</strong>: полная в ~/Projects/eversolo-companion/SPEC.md</li>
</ul>
<h2 id="проблема">Проблема</h2>
<p>API не может достучаться до Eversolo T8 (192.168.0.156:9529) из-за ограничений OpenClaw sandbox.</p>
<h2 id="возможные-решения">Возможные решения</h2>
<ol>
<li>Настроить <code>tools.elevated.allowFrom.webchat</code> для разрешения сетевых соединений</li>
<li>Запустить API через launchd plist (как SSH туннели)</li>
<li>Использовать отдельный контейнер без ограничений</li>
</ol>
<h2 id="этапы-3-9-после-решения-сети">Этапы 3-9 (после решения сети)</h2>
<ol>
<li><strong>Библиотека синхронизации</strong> — двухсторонний sync с Eversolo</li>
<li><strong>Плейлисты</strong> — импорт/экспорт, редактирование</li>
<li><strong>Scrobbler</strong> — Last.fm / ListenBrainz</li>
<li><strong>Обложки</strong> — кэширование, отображение в UI</li>
<li><strong>Плеер</strong> — улучшенный интерфейс управления</li>
<li><strong>Система уведомлений</strong> — события от Eversolo</li>
<li><strong>Администрирование</strong> — настройка подключений</li>
</ol>
<h2 id="связанные-ресурсы">Связанные ресурсы</h2>
<ul>
<li><a href="../../docs/eversolo-companion-spec/">Спецификация проекта</a></li>
<li><a href="../../kb/eversolo-api/">API документация</a></li>
<li><a href="../../diary/2026-03-29/">Дневник разработки</a></li>
</ul></description>
|
||||
</item>
|
||||
</channel>
|
||||
|
||||
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -121,9 +125,9 @@
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="../../creds/leadera-db-local/">Mac Studio — leadera-api БД (локальная)</a></li>
|
||||
<li><a href="../../yasha/creds/leadera-db-local/">Mac Studio — leadera-api БД (локальная)</a></li>
|
||||
|
||||
<li><a href="../../creds/leadera-db-docker/">Mac Studio — leadera-api БД (docker-compose)</a></li>
|
||||
<li><a href="../../yasha/creds/leadera-db-docker/">Mac Studio — leadera-api БД (docker-compose)</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -10,16 +10,16 @@
|
||||
<atom:link href="/tags/database/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>Mac Studio — leadera-api БД (docker-compose)</title>
|
||||
<link>/creds/leadera-db-docker/</link>
|
||||
<link>/yasha/creds/leadera-db-docker/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/creds/leadera-db-docker/</guid>
|
||||
<guid>/yasha/creds/leadera-db-docker/</guid>
|
||||
<description>NYVK4PEGwmOHmAnGk8IAUc+lyFJtnEAvX6ZddBro/ILRWHOXBGYywgglFvQZ18BLDLb5DuA8GPAIdB/LwTHRXYO0KsmtlZQVWGEeeXlXAoEXpa5theoOQhebkW6LmiKyNGAVW2z9rK3nz6NnztYdn49LvGMjrHuR4IwCLbl/pYa96CPNRnxGDFlduFi8lMSspM0rBFi/bZhNGhzy36ViCl+aKr5P9cle0ZvM8E8av/6c5JjgDt88QFI3VYdTaTCE0+uHsXlnJN7Qgg/KH7kD</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Mac Studio — leadera-api БД (локальная)</title>
|
||||
<link>/creds/leadera-db-local/</link>
|
||||
<link>/yasha/creds/leadera-db-local/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/creds/leadera-db-local/</guid>
|
||||
<guid>/yasha/creds/leadera-db-local/</guid>
|
||||
<description>p7Iw7Y3zbj1kLBcbOXGoO27oDwF85AhxBW7saT538hxi0ogJpyFeM49GDuLHzxuyknvdNC/rx3tb4AFnGkCeHSrrCmj+VK8Zxt7ujeujVJSRuOeeUlsqkHdlmaiXl2KQknrIsqVCi3Tnn0V5qcFxvo+1Ktc+surF6bBpWrTHzWV5mLtWVfD6C6kvvP/UFldFBD6v+FQs79yfoOe/j/bUrtpt6v5VndbbBmhUAQcy1BV2rpXxO373dTcLQ6coRWkt/RFo</description>
|
||||
</item>
|
||||
</channel>
|
||||
|
||||
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -121,7 +125,7 @@
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="../../guides/vps-reverse-proxy/">Настройка VPS как реверс-прокси</a></li>
|
||||
<li><a href="../../yasha/guides/vps-reverse-proxy/">Настройка VPS как реверс-прокси</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
<atom:link href="/tags/devops/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>Настройка VPS как реверс-прокси</title>
|
||||
<link>/guides/vps-reverse-proxy/</link>
|
||||
<link>/yasha/guides/vps-reverse-proxy/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/guides/vps-reverse-proxy/</guid>
|
||||
<guid>/yasha/guides/vps-reverse-proxy/</guid>
|
||||
<description><h2 id="общая-схема">Общая схема</h2>
<pre tabindex="0"><code>Mac Studio (локальная сеть)
 → autossh (SSH-туннели)
 → VPS 95.163.232.9 (Debian)
 → nginx (HTTPS reverse proxy)
 → интернет
</code></pre><h2 id="что-нужно-на-vps">Что нужно на VPS</h2>
<h3 id="1-установленные-пакеты">1. Установленные пакеты</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>apt update
</span></span><span style="display:flex;"><span>apt install nginx certbot python3-certbot-nginx
</span></span></code></pre></div><h3 id="2-ssh-доступ">2. SSH-доступ</h3>
<ul>
<li>Авторизация только по ключам</li>
<li>Публичный ключ с Mac Studio добавлен в <code>/root/.ssh/authorized_keys</code></li>
</ul>
<h3 id="3-nginx">3. nginx</h3>
<p>Для каждого домена создать server block:</p>
<ul>
<li>HTTP → редирект на HTTPS</li>
<li>HTTPS с SSL-сертификатом Let&rsquo;s Encrypt</li>
<li><code>proxy_pass</code> на localhost-порт туннеля</li>
</ul>
<p>Пример структуры:</p></description>
|
||||
</item>
|
||||
</channel>
|
||||
|
||||
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -121,7 +125,7 @@
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="../../creds/leadera-db-docker/">Mac Studio — leadera-api БД (docker-compose)</a></li>
|
||||
<li><a href="../../yasha/creds/leadera-db-docker/">Mac Studio — leadera-api БД (docker-compose)</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
<atom:link href="/tags/docker-compose/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>Mac Studio — leadera-api БД (docker-compose)</title>
|
||||
<link>/creds/leadera-db-docker/</link>
|
||||
<link>/yasha/creds/leadera-db-docker/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/creds/leadera-db-docker/</guid>
|
||||
<guid>/yasha/creds/leadera-db-docker/</guid>
|
||||
<description>NYVK4PEGwmOHmAnGk8IAUc+lyFJtnEAvX6ZddBro/ILRWHOXBGYywgglFvQZ18BLDLb5DuA8GPAIdB/LwTHRXYO0KsmtlZQVWGEeeXlXAoEXpa5theoOQhebkW6LmiKyNGAVW2z9rK3nz6NnztYdn49LvGMjrHuR4IwCLbl/pYa96CPNRnxGDFlduFi8lMSspM0rBFi/bZhNGhzy36ViCl+aKr5P9cle0ZvM8E8av/6c5JjgDt88QFI3VYdTaTCE0+uHsXlnJN7Qgg/KH7kD</description>
|
||||
</item>
|
||||
</channel>
|
||||
|
||||
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -121,15 +125,15 @@
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="../../guides/sand-a2v-space/">Подключение sand.a2v.space (Leadera Project)</a></li>
|
||||
<li><a href="../../yasha/guides/sand-a2v-space/">Подключение sand.a2v.space (Leadera Project)</a></li>
|
||||
|
||||
<li><a href="../../plans/eversolo-companion-phases-3-9/">Завершить Eversolo Companion - этапы 3-9</a></li>
|
||||
<li><a href="../../yasha/plans/eversolo-companion-phases-3-9/">Завершить Eversolo Companion - этапы 3-9</a></li>
|
||||
|
||||
<li><a href="../../creds/leadera-db-local/">Mac Studio — leadera-api БД (локальная)</a></li>
|
||||
<li><a href="../../yasha/creds/leadera-db-local/">Mac Studio — leadera-api БД (локальная)</a></li>
|
||||
|
||||
<li><a href="../../creds/eversolo-companion-postgres/">Eversolo Companion PostgreSQL</a></li>
|
||||
<li><a href="../../yasha/creds/eversolo-companion-postgres/">Eversolo Companion PostgreSQL</a></li>
|
||||
|
||||
<li><a href="../../diary/2026-03-29/">29 марта 2026</a></li>
|
||||
<li><a href="../../yasha/diary/2026-03-29/">29 марта 2026</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -10,37 +10,37 @@
|
||||
<atom:link href="/tags/docker/index.xml" rel="self" type="application/rss+xml" />
|
||||
<item>
|
||||
<title>29 марта 2026</title>
|
||||
<link>/diary/2026-03-29/</link>
|
||||
<link>/yasha/diary/2026-03-29/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/diary/2026-03-29/</guid>
|
||||
<guid>/yasha/diary/2026-03-29/</guid>
|
||||
<description><h2 id="timeline">Timeline</h2>
<ul>
<li><strong>20:03</strong> — Настроили Angular + Go проекты (leadera) через pm2, удалили proxy.conf, подняли PostgreSQL в Docker</li>
<li><strong>20:31</strong> — Изучили API Eversolo T8 — полный список REST-эндпоинтов для управления музыкой</li>
<li><strong>20:49</strong> — Обсудили важность записи контекста в файлы для сохранения памяти между сессиями</li>
<li><strong>21:13</strong> — Начали проект docs.a2v.space — персональная документация на Hugo</li>
<li><strong>21:44</strong> — Завершили настройку Identity (Братан, SOUL.md, USER.md, IDENTITY.md)</li>
<li><strong>22:15</strong> — Документировали инфраструктуру: VPS reverse proxy (claw.a2v.space, code.a2v.space, sand.a2v.space, docs.a2v.space)</li>
<li><strong>22:31</strong> — Настроили docs.a2v.space: Hugo сайт, 83 страницы, Bootstrap 5 тёмная тема, AES-256-GCM шифрование кредов (мастер-пароль: Popoloka16!)</li>
<li><strong>22:47</strong> — Добавили Basic auth: agaltsovav / Magamet787! для доступа к документации</li>
<li><strong>23:02</strong> — Создали cron задачу docs-a2v-daily-sync на ежедневное обновление в 03:00 МСК</li>
<li><strong>23:18</strong> — Полностью задокументировали Eversolo T8 API (воспроизведение, громкость, дисплей, питание, поиск и т.д.)</li>
<li><strong>23:45</strong> — Переписали TOOLS.md как полный справочник по инфраструктуре</li>
</ul>
<h2 id="eversolo-companion--статус-проекта">Eversolo Companion — статус проекта</h2>
<p><strong>Backend (Go + Gin):</strong></p></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Eversolo Companion PostgreSQL</title>
|
||||
<link>/creds/eversolo-companion-postgres/</link>
|
||||
<link>/yasha/creds/eversolo-companion-postgres/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/creds/eversolo-companion-postgres/</guid>
|
||||
<guid>/yasha/creds/eversolo-companion-postgres/</guid>
|
||||
<description><h2 id="docker-compose-креды-projectseversolo-companiondocker-composeyml">Docker Compose креды (~/Projects/eversolo-companion/docker-compose.yml)</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">services</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">postgres</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">environment</span>:
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_USER</span>: <span style="color:#ae81ff">postgres</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_PASSWORD</span>: <span style="color:#ae81ff">password</span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">POSTGRES_DB</span>: <span style="color:#ae81ff">eversolo</span>
</span></span></code></pre></div><h2 id="локальные-креды-env-файл">Локальные креды (.env файл)</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>POSTGRES_USER<span style="color:#f92672">=</span>postgres
</span></span><span style="display:flex;"><span>POSTGRES_PASSWORD<span style="color:#f92672">=</span>password
</span></span><span style="display:flex;"><span>POSTGRES_DB<span style="color:#f92672">=</span>eversolo
</span></span></code></pre></div><h2 id="хостинг">Хостинг</h2>
<ul>
<li><strong>Host</strong>: localhost</li>
<li><strong>Port</strong>: 5433</li>
<li><strong>Database</strong>: eversolo</li>
</ul>
<h2 id="безопасность">Безопасность</h2>
<ul>
<li>Используйте разные пароли для production</li>
<li>Включите SSL для удаленного подключения</li>
<li>Регулярно делайте бэкапы</li>
</ul></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Mac Studio — leadera-api БД (локальная)</title>
|
||||
<link>/creds/leadera-db-local/</link>
|
||||
<link>/yasha/creds/leadera-db-local/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/creds/leadera-db-local/</guid>
|
||||
<guid>/yasha/creds/leadera-db-local/</guid>
|
||||
<description>p7Iw7Y3zbj1kLBcbOXGoO27oDwF85AhxBW7saT538hxi0ogJpyFeM49GDuLHzxuyknvdNC/rx3tb4AFnGkCeHSrrCmj+VK8Zxt7ujeujVJSRuOeeUlsqkHdlmaiXl2KQknrIsqVCi3Tnn0V5qcFxvo+1Ktc+surF6bBpWrTHzWV5mLtWVfD6C6kvvP/UFldFBD6v+FQs79yfoOe/j/bUrtpt6v5VndbbBmhUAQcy1BV2rpXxO373dTcLQ6coRWkt/RFo</description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Завершить Eversolo Companion - этапы 3-9</title>
|
||||
<link>/plans/eversolo-companion-phases-3-9/</link>
|
||||
<link>/yasha/plans/eversolo-companion-phases-3-9/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/plans/eversolo-companion-phases-3-9/</guid>
|
||||
<guid>/yasha/plans/eversolo-companion-phases-3-9/</guid>
|
||||
<description><h2 id="описание">Описание</h2>
<p>Завершить разработку Eversolo Companion после решения проблемы с сетевым доступом.</p>
<h2 id="что-сделано">Что сделано</h2>
<ul>
<li><strong>Backend (Go + Gin)</strong>: собран, 17+ REST API эндпоинтов, WebSocket, CORS, state poller</li>
<li><strong>Frontend (Angular 17)</strong>: собран, тёмная тема (Spotify-like), 5 экранов, 5 сервисов</li>
<li><strong>PostgreSQL</strong>: Docker, порт 5433, миграции готовы</li>
<li><strong>Docker Compose</strong>: рабочий (postgres + api + frontend)</li>
<li><strong>Спецификация</strong>: полная в ~/Projects/eversolo-companion/SPEC.md</li>
</ul>
<h2 id="проблема">Проблема</h2>
<p>API не может достучаться до Eversolo T8 (192.168.0.156:9529) из-за ограничений OpenClaw sandbox.</p>
<h2 id="возможные-решения">Возможные решения</h2>
<ol>
<li>Настроить <code>tools.elevated.allowFrom.webchat</code> для разрешения сетевых соединений</li>
<li>Запустить API через launchd plist (как SSH туннели)</li>
<li>Использовать отдельный контейнер без ограничений</li>
</ol>
<h2 id="этапы-3-9-после-решения-сети">Этапы 3-9 (после решения сети)</h2>
<ol>
<li><strong>Библиотека синхронизации</strong> — двухсторонний sync с Eversolo</li>
<li><strong>Плейлисты</strong> — импорт/экспорт, редактирование</li>
<li><strong>Scrobbler</strong> — Last.fm / ListenBrainz</li>
<li><strong>Обложки</strong> — кэширование, отображение в UI</li>
<li><strong>Плеер</strong> — улучшенный интерфейс управления</li>
<li><strong>Система уведомлений</strong> — события от Eversolo</li>
<li><strong>Администрирование</strong> — настройка подключений</li>
</ol>
<h2 id="связанные-ресурсы">Связанные ресурсы</h2>
<ul>
<li><a href="../../docs/eversolo-companion-spec/">Спецификация проекта</a></li>
<li><a href="../../kb/eversolo-api/">API документация</a></li>
<li><a href="../../diary/2026-03-29/">Дневник разработки</a></li>
</ul></description>
|
||||
</item>
|
||||
<item>
|
||||
<title>Подключение sand.a2v.space (Leadera Project)</title>
|
||||
<link>/guides/sand-a2v-space/</link>
|
||||
<link>/yasha/guides/sand-a2v-space/</link>
|
||||
<pubDate>Sun, 29 Mar 2026 00:00:00 +0000</pubDate>
|
||||
<guid>/guides/sand-a2v-space/</guid>
|
||||
<guid>/yasha/guides/sand-a2v-space/</guid>
|
||||
<description><h2 id="архитектура">Архитектура</h2>
<pre tabindex="0"><code>Mac Studio
 ├─ Frontend (Angular) :4200 (pm2: leadera-fe)
 ├─ Backend (Go) :8080 (pm2: leadera-api)
 └─ PostgreSQL 16 в Docker (Colima, container: app-leadera-db)
 → autossh SSH-туннель (-R 8082:localhost:4200 -R 8083:localhost:8080)
 → VPS 95.163.232.9
 → nginx HTTPS
 → https://sand.a2v.space
</code></pre><h2 id="параметры">Параметры</h2>
<table>
 <thead>
 <tr>
 <th>Компонент</th>
 <th>Локальный порт</th>
 <th>Туннель порт</th>
 <th>pm2 имя</th>
 </tr>
 </thead>
 <tbody>
 <tr>
 <td>Frontend (Angular)</td>
 <td>4200</td>
 <td>8082</td>
 <td>leadera-fe</td>
 </tr>
 <tr>
 <td>Backend (Go)</td>
 <td>8080</td>
 <td>8083</td>
 <td>leadera-api</td>
 </tr>
 </tbody>
</table>
<p>SSL сертификат: <code>/etc/letsencrypt/live/sand.a2v.space/</code></p>
<h2 id="nginx-конфиг">nginx конфиг</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">80</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">sand.a2v.space</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">return</span> <span style="color:#ae81ff">301</span> <span style="color:#e6db74">https://</span>$server_name$request_uri;
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">server</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">listen</span> <span style="color:#ae81ff">443</span> <span style="color:#e6db74">ssl</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">server_name</span> <span style="color:#e6db74">sand.a2v.space</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate</span> <span style="color:#e6db74">/etc/letsencrypt/live/sand.a2v.space/fullchain.pem</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">ssl_certificate_key</span> <span style="color:#e6db74">/etc/letsencrypt/live/sand.a2v.space/privkey.pem</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#f92672">location</span> <span style="color:#e6db74">/api</span> {
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:8083</span>;
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Host</span> $host;
</span></span><span stylLine truncated
|
||||
</item>
|
||||
</channel>
|
||||
|
||||
+44
-40
@@ -26,33 +26,47 @@
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../diary/">Дневник</a>
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Яша
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/diary/">Дневник</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/plans/">Планы</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/guides/">Инструкции</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/creds/">Креды</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/leadera/">Leadera</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../yasha/personal-site/">Личный сайт</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../kb/">База знаний</a>
|
||||
|
||||
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Семен
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/changelog/">История изменений</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/kb/">База знаний</a></li>
|
||||
|
||||
<li><a class="dropdown-item" href="../../semen/diary/">Дневник</a></li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../plans/">Планы</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../leadera/">Leadera</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../guides/">Инструкции</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../creds/">Креды</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="../../personal-site/">Личный сайт</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -87,25 +101,15 @@
|
||||
<aside class="col-md-3 col-lg-2 d-md-block sidebar collapse" id="sidebar">
|
||||
<div class="sidebar-content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h6>Разделы</h6>
|
||||
<ul class="nav flex-column">
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../diary/">Дневник</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../kb/">База знаний</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../plans/">Планы</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../leadera/">Leadera</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../guides/">Инструкции</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../creds/">Креды</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../personal-site/">Личный сайт</a></li>
|
||||
|
||||
<li class="nav-item"><a class="nav-link" href="../../yasha/">Яша</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="../../semen/">Семен</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
@@ -121,9 +125,9 @@
|
||||
|
||||
<ul>
|
||||
|
||||
<li><a href="../../creds/docs-a2v-space-auth/">docs.a2v.space Basic Auth</a></li>
|
||||
<li><a href="../../yasha/creds/docs-a2v-space-auth/">docs.a2v.space Basic Auth</a></li>
|
||||
|
||||
<li><a href="../../diary/2026-03-29/">29 марта 2026</a></li>
|
||||
<li><a href="../../yasha/diary/2026-03-29/">29 марта 2026</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
Loaded 100 of 255 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user