910 lines
53 KiB
HTML
910 lines
53 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru-ru">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>Guides — 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>
|
||
|
||
</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/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/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>
|
||
|
||
</ul>
|
||
|
||
|
||
|
||
</div>
|
||
|
||
</aside>
|
||
|
||
|
||
<main class="col-md-9 col-lg-10 main-content">
|
||
|
||
<div class="article-content">
|
||
<h1>Инструкции</h1>
|
||
|
||
<div class="row">
|
||
|
||
<div class="col-md-6 mb-4">
|
||
<div class="card h-100">
|
||
<div class="card-body">
|
||
<h3 class="card-title"><a href="../guides/leadera-audit/">Аудит Leadera</a></h3>
|
||
<p class="card-text"><h2 id="обзор-проекта">Обзор проекта</h2>
|
||
<p>Leadera — SaaS-система управления IT-ландшафтом с поддержкой диаграмм Ганта. Проект разделен на два компонента:</p>
|
||
<h3 id="бекенд">Бекенд</h3>
|
||
<ul>
|
||
<li><strong>Стек:</strong> Go 1.21+, GIN, PostgreSQL 16+, pgx v5 + sqlx, JWT</li>
|
||
<li><strong>Архитектура:</strong> Clean Architecture с разделением на слои</li>
|
||
<li><strong>Особенности:</strong> RESTful API, автоматические миграции, система ролей через spaces</li>
|
||
</ul>
|
||
<h3 id="фронтенд">Фронтенд</h3>
|
||
<ul>
|
||
<li><strong>Стек:</strong> Angular 21.1.3, Bootstrap 5, RxJS</li>
|
||
<li><strong>Особенности:</strong> JWT-аутентификация, перехватчики HTTP, lazy loading</li>
|
||
</ul>
|
||
<h2 id="бекенд--найденные-проблемы">Бекенд — найденные проблемы</h2>
|
||
<h3 id="критические-проблемы">Критические проблемы</h3>
|
||
<h4 id="1-уязвимость-xss-в-сериализации-dom">1. Уязвимость XSS в сериализации DOM</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>internal/handlers/space_handler.go:47-63</code></li>
|
||
<li><strong>Критичность:</strong> <strong>critical</strong></li>
|
||
<li><strong>Описание:</strong> В методе <code>GetSpaceMembers</code> используется <code>c.BindJSON(&req)</code> без валидации, что может привести к XSS при сериализации данных в JSON</li>
|
||
<li><strong>Рекомендация:</strong> Добавить валидацию DTO и использование HTML sanitizer</li>
|
||
</ul>
|
||
<h4 id="2-небезопасное-хранение-токенов">2. Небезопасное хранение токенов</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>internal/utils/password.go:19-21</code></li>
|
||
<li><strong>Критичность:</strong> <strong>critical</strong></li>
|
||
<li><strong>Описание:</strong> Refresh токены хэшируются с помощью SHA256 вместо bcrypt, что делает их уязвимыми для перебора</li>
|
||
<li><strong>Рекомендация:</strong> Перейти на bcrypt для хэширования refresh токенов</li>
|
||
</ul>
|
||
<h4 id="3-отсутствие-rate-limiting">3. Отсутствие Rate Limiting</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> Нет реализации</li>
|
||
<li><strong>Критичность:</strong> <strong>high</strong></li>
|
||
<li><strong>Описание:</strong> Нет ограничений на количество запросов к API, что делает систему уязвимой для brute-force атак</li>
|
||
<li><strong>Рекомендация:</strong> Реализовать middleware для rate limiting</li>
|
||
</ul>
|
||
<h3 id="высокие-проблемы">Высокие проблемы</h3>
|
||
<h4 id="4-отсутствие-валидации-входных-данных">4. Отсутствие валидации входных данных</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>internal/handlers/auth_handler.go:26-32, 52-58</code></li>
|
||
<li><strong>Критичность:</strong> <strong>high</strong></li>
|
||
<li><strong>Описание:</strong> Нет строгой валидации email, длинны строк и других параметров запросов</li>
|
||
<li><strong>Рекомендация:</strong> Внедрить библиотеку для валидации (go-playground/validator)</li>
|
||
</ul>
|
||
<h4 id="5-отсутствие-защиты-от-csrf">5. Отсутствие защиты от CSRF</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> Нет реализации</li>
|
||
<li><strong>Критичность:</strong> <strong>high</strong></li>
|
||
<li><strong>Описание:</strong> Нет CSRF-токенов для POST/PUT/DELETE запросов</li>
|
||
<li><strong>Рекомендация:</strong> Реализовать CSRF middleware</li>
|
||
</ul>
|
||
<h4 id="6-потенциальные-n1-запросы">6. Потенциальные N+1 запросы</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>internal/repository/space_repository.go</code></li>
|
||
<li><strong>Критичность:</strong> <strong>high</strong></li>
|
||
<li><strong>Описание:</strong> Нет индексов и оптимизации для запросов с JOIN при получении членов spaces</li>
|
||
<li><strong>Рекомендация:</strong> Добавить правильные индексы и использовать eager loading</li>
|
||
</ul>
|
||
<h3 id="средние-проблемы">Средние проблемы</h3>
|
||
<h4 id="7-отсутствие-логирования-ошибок">7. Отсутствие логирования ошибок</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>internal/handlers/gantt_handler.go:78-95</code></li>
|
||
<li><strong>Критичность:</strong> <strong>medium</strong></li>
|
||
<li><strong>Описание:</strong> Ошибки валидации не логируются, что затрудняет отладку</li>
|
||
<li><strong>Рекомендация:</strong> Структурированное логирование всех ошибок</li>
|
||
</ul>
|
||
<h4 id="8-жестко-закодированные-настройки">8. Жестко закодированные настройки</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>internal/middleware/space_middleware.go:121-128</code></li>
|
||
<li><strong>Критичность:</strong> <strong>medium</strong></li>
|
||
<li><strong>Описание:</strong> Статическая проверка subscription status без гибкой конфигурации</li>
|
||
<li><strong>Рекомендация:</strong> Вынести в конфигурацию возможные статусы подписки</li>
|
||
</ul>
|
||
<h4 id="9-отсутствие-ограничений-на-количество-записей">9. Отсутствие ограничений на количество записей</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>internal/handlers/gantt_handler.go</code></li>
|
||
<li><strong>Критичность:</strong> <strong>medium</strong></li>
|
||
<li><strong>Описание:</strong> Нет ограничений на количество задач, разделов или диаграмм на пользователя</li>
|
||
<li><strong>Рекомендация:</strong> Добавить лимиты через middleware</li>
|
||
</ul>
|
||
<h3 id="низкие-проблемы">Низкие проблемы</h3>
|
||
<h4 id="10-отсутствие-валидации-для-special-символов">10. Отсутствие валидации для special символов</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>internal/utils/validation.go:21-24</code></li>
|
||
<li><strong>Критичность:</strong> <strong>low</strong></li>
|
||
<li><strong>Описание:</strong> Валидация паролей не включает некоторые специальные символы</li>
|
||
<li><strong>Рекомендация:</strong> Расширить список разрешенных символов</li>
|
||
</ul>
|
||
<h4 id="11-отсутствие-health-check-для-базы-данных">11. Отсутствие Health Check для базы данных</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>internal/handlers/handler.go</code></li>
|
||
<li><strong>Критичность:</strong> <strong>low</strong></li>
|
||
<li><strong>Описание:</strong> Health endpoint не проверяет доступность базы данных</li>
|
||
<li><strong>Рекомендация:</strong> Добавить ping к базе данных в health check</li>
|
||
</ul>
|
||
<h2 id="фронтенд--найденные-проблемы">Фронтенд — найденные проблемы</h2>
|
||
<h3 id="критические-проблемы-1">Критические проблемы</h3>
|
||
<h4 id="1-хранение-jwt-токенов-в-localstorage">1. Хранение JWT токенов в localStorage</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>src/app/core/auth/auth.service.ts:25-40</code></li>
|
||
<li><strong>Критичность:</strong> <strong>critical</strong></li>
|
||
<li><strong>Описание:</strong> Токены хранятся в localStorage, уязвимом для XSS атак</li>
|
||
<li><strong>Рекомендация:</strong> Использовать HTTP-only cookies или secure storage</li>
|
||
</ul>
|
||
<h4 id="2-отсутствие-content-security-policy-csp">2. Отсутствие Content Security Policy (CSP)</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>src/index.html</code></li>
|
||
<li><strong>Критичность:</strong> <strong>critical</strong></li>
|
||
<li><strong>Описание:</strong> Нет заголовков CSP для защиты от XSS</li>
|
||
<li><strong>Рекомендация:</strong> Добавить заголовки CSP в HTTP ответы</li>
|
||
</ul>
|
||
<h3 id="высокие-проблемы-1">Высокие проблемы</h3>
|
||
<h4 id="3-нет-валидации-форм-на-стороне-клиента">3. Нет валидации форм на стороне клиента</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>src/app/pages/auth/components/sign-up/sign-up.component.ts</code></li>
|
||
<li><strong>Критичность:</strong> <strong>high</strong></li>
|
||
<li><strong>Описание:</strong> Формы не проходят полную валидацию перед отправкой</li>
|
||
<li><strong>Рекомендация:</strong> Реализовать полноценную клиентскую валидацию</li>
|
||
</ul>
|
||
<h4 id="4-отсутствие-обработки-race-conditions">4. Отсутствие обработки race conditions</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>src/app/core/auth/auth.interceptor.ts:15-45</code></li>
|
||
<li><strong>Критичность:</strong> <strong>high</strong></li>
|
||
<li><strong>Описание:</strong> Потенциальные race conditions при обновлении токенов</li>
|
||
<li><strong>Рекомендация:</strong> Реализовать очередь запросов при обновлении токенов</li>
|
||
</ul>
|
||
<h4 id="5-нет-ограничений-на-длину-вводимых-данных">5. Нет ограничений на длину вводимых данных</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>src/app/components/forms/gantt-form.component.ts</code></li>
|
||
<li><strong>Критичность:</strong> <strong>high</strong></li>
|
||
<li><strong>Описание:</strong> Пользователь может вводить очень длинные тексты без ограничений</li>
|
||
<li><strong>Рекомендация:</strong> Добавить максимальную длину для полей ввода</li>
|
||
</ul>
|
||
<h3 id="средние-проблемы-1">Средние проблемы</h3>
|
||
<h4 id="6-нет-proper-error-handling-states">6. Нет proper error handling states</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>src/app/layouts/components/navbar/navbar.component.ts</code></li>
|
||
<li><strong>Критичность:</strong> <strong>medium</strong></li>
|
||
<li><strong>Описание:</strong> Нет graceful handling ошибок при загрузке данных</li>
|
||
<li><strong>Рекомендация:</strong> Реализовать skeleton loaders и error boundaries</li>
|
||
</ul>
|
||
<h4 id="7-плохая-производительность-при-большом-количестве-данных">7. Плохая производительность при большом количестве данных</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>src/app/pages/dashboard/gantt/gantt-page.component.ts</code></li>
|
||
<li><strong>Критичность:</strong> <strong>medium</strong></li>
|
||
<li><strong>Описание:</strong> Нет virtual scrolling для больших списков задач</li>
|
||
<li><strong>Рекомендация:</strong> Реализовать virtual scrolling и lazy loading</li>
|
||
</ul>
|
||
<h4 id="8-отсутствие-a11y-для-некоторых-компонентов">8. Отсутствие a11y для некоторых компонентов</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>src/app/components/charts/gantt-chart/gantt-chart.component.ts</code></li>
|
||
<li><strong>Критичность:</strong> <strong>medium</strong></li>
|
||
<li><strong>Описание:</strong> Гант-диаграмма не имеет proper ARIA labels</li>
|
||
<li><strong>Рекомендация:</strong> Добавить ARIA атрибуты и keyboard navigation</li>
|
||
</ul>
|
||
<h3 id="низкие-проблемы-1">Низкие проблемы</h3>
|
||
<h4 id="9-нет-csrf-protection">9. Нет CSRF protection</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>src/app/core/auth/auth.interceptor.ts</code></li>
|
||
<li><strong>Критичность:</strong> <strong>low</strong></li>
|
||
<li><strong>Описание:</strong> Отсутствие CSRF токенов для чувствительных операций</li>
|
||
<li><strong>Рекомендация:</strong> Добавить CSRF токены в заголовки</li>
|
||
</ul>
|
||
<h4 id="10-плохая-мобильная-адаптация">10. Плохая мобильная адаптация</h4>
|
||
<ul>
|
||
<li><strong>Файл:</strong> <code>src/app/styles/gantt.scss</code></li>
|
||
<li><strong>Критичность:</strong> <strong>low</strong></li>
|
||
<li><strong>Описание:</strong> Нет proper responsive design для мобильных устройств</li>
|
||
<li><strong>Рекомендация:</strong> Добавить медиа-запросы и touch-friendly интерфейс</li>
|
||
</ul>
|
||
<h2 id="безопасность">Безопасность</h2>
|
||
<h3 id="обнаруженные-уязвимости">Обнаруженные уязвимости:</h3>
|
||
<ol>
|
||
<li><strong>XSS уязвимости</strong> - Отсутствие валидации входных данных на бэкенде</li>
|
||
<li><strong>Небезопасное хранение токенов</strong> - Refresh токены используют SHA256 вместо bcrypt</li>
|
||
<li><strong>Отсутствие rate limiting</strong> - Уязвимость для brute-force атак</li>
|
||
<li><strong>CSRF уязвимости</strong> - Отсутствие CSRF-токенов</li>
|
||
<li><strong>Storage vulnerabilities</strong> - localStorage используется для хранения JWT</li>
|
||
</ol>
|
||
<h3 id="рекомендации-по-безопасности">Рекомендации по безопасности:</h3>
|
||
<ol>
|
||
<li>Реализовать Input Validation на всех уровнях</li>
|
||
<li>Перейти на HTTP-only cookies для токенов</li>
|
||
<li>Реализовать rate limiting middleware</li>
|
||
<li>Добавить CSRF protection</li>
|
||
<li>Внедрить заголовки безопасности (CSP, HSTS, X-Frame-Options)</li>
|
||
<li>Реализовать proper logging и мониторинг</li>
|
||
<li>Добавить автоматические тесты безопасности</li>
|
||
</ol>
|
||
<h2 id="ux-улучшения">UX улучшения</h2>
|
||
<h3 id="критические-улучшения">Критические улучшения:</h3>
|
||
<ol>
|
||
<li><strong>Надежность аутентификации</strong> - Реализовать MFA</li>
|
||
<li><strong>Уведомления об ошибках</strong> - Лучшее error messaging</li>
|
||
<li><strong>Прогресс-бары</strong> - Для долгих операций</li>
|
||
</ol>
|
||
<h3 id="высокие-улучшения">Высокие улучшения:</h3>
|
||
<ol>
|
||
<li><strong>Поиск по проекту</strong> - Быстрый поиск across spaces</li>
|
||
<li><strong>Drag & Drop</strong> - Для задач и диаграмм</li>
|
||
<li><strong>Keyboard shortcuts</strong> - Повышение продуктивности</li>
|
||
<li><strong>Темная тема</strong> - Улучшение UX при длительной работе</li>
|
||
</ol>
|
||
<h3 id="средние-улучшения">Средние улучшения:</h3>
|
||
<ol>
|
||
<li><strong>Экспорт данных</strong> -多种 форматы</li>
|
||
<li><strong>Коллаборация в реальном времени</strong> - WebSocket для live updates</li>
|
||
<li><strong>Шаблоны диаграмм</strong> - Pre-built templates</li>
|
||
<li><strong>Аналитика и отчеты</strong> - Insights по проектам</li>
|
||
</ol>
|
||
<h3 id="низкие-улучшения">Низкие улучшения:</h3>
|
||
<ol>
|
||
<li><strong>Mobile app</strong> - iOS/Android приложения</li>
|
||
<li><strong>Интеграции</strong> - Slack, Teams, Jira и др.</li>
|
||
<li><strong>API для плагинов</strong> - Расширяемость системы</li>
|
||
</ol>
|
||
<h2 id="приоритизированный-план-исправления">Приоритизированный план исправления</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Приоритет</th>
|
||
<th>Проблема</th>
|
||
<th>Файлы</th>
|
||
<th>Трудоёмкость</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong>Critical</strong></td>
|
||
<td>XSS уязвимости</td>
|
||
<td><code>internal/handlers/*.go</code></td>
|
||
<td>3 дня</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Critical</strong></td>
|
||
<td>Хранение JWT в localStorage</td>
|
||
<td><code>src/app/core/auth/*.ts</code></td>
|
||
<td>1 день</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Critical</strong></td>
|
||
<td>Небезопасное хранение refresh токенов</td>
|
||
<td><code>internal/utils/password.go</code></td>
|
||
<td>1 день</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>High</strong></td>
|
||
<td>Rate limiting middleware</td>
|
||
<td><code>internal/middleware/</code></td>
|
||
<td>2 дня</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>High</strong></td>
|
||
<td>CSRF protection</td>
|
||
<td><code>internal/middleware/</code>, <code>src/app/core/auth/</code></td>
|
||
<td>2 дня</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>High</strong></td>
|
||
<td>Валидация входных данных</td>
|
||
<td><code>internal/handlers/</code>, <code>internal/utils/</code></td>
|
||
<td>3 дня</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Medium</strong></td>
|
||
<td>Оптимизация запросов БД</td>
|
||
<td><code>internal/repository/</code></td>
|
||
<td>2 дня</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Medium</strong></td>
|
||
<td>Структурированное логирование</td>
|
||
<td><code>internal/handlers/</code></td>
|
||
<td>1 день</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Low</strong></td>
|
||
<td>CSP заголовки</td>
|
||
<td><code>src/index.html</code>, конфигурация сервера</td>
|
||
<td>1 день</td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Low</strong></td>
|
||
<td>A11y улучшения</td>
|
||
<td><code>src/app/components/</code></td>
|
||
<td>3 дня</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="общая-оценка">Общая оценка:</h3>
|
||
<ul>
|
||
<li><strong>Critical issues:</strong> 6 задач, 8 дней</li>
|
||
<li><strong>High issues:</strong> 5 задач, 7 дней</li>
|
||
<li><strong>Medium issues:</strong> 3 задачи, 6 дней</li>
|
||
<li><strong>Low issues:</strong> 4 задачи, 5 дней</li>
|
||
</ul>
|
||
<p><strong>Общая трудоёмкость:</strong> 26 дней разработки + 3 дня на тестирование = 29 дней</p></p>
|
||
|
||
<div class="tags">
|
||
|
||
<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/ux/" class="tag">ux</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-6 mb-4">
|
||
<div class="card h-100">
|
||
<div class="card-body">
|
||
<h3 class="card-title"><a href="../guides/ssh-tunnels-vps/">SSH туннели для VPS reverse proxy</a></h3>
|
||
<p class="card-text"><h2 id="настройка-ssh-туннелей">Настройка SSH туннелей</h2>
|
||
<h3 id="vps-regru-951632329">VPS reg.ru (95.163.232.9)</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><span style="color:#75715e"># На Mac Studio: создать туннель для каждого сервиса</span>
|
||
</span></span><span style="display:flex;"><span>ssh -N -R 8080:localhost:8080 root@95.163.232.9 <span style="color:#75715e"># leadera-api</span>
|
||
</span></span><span style="display:flex;"><span>ssh -N -R 8081:localhost:4096 root@95.163.232.9 <span style="color:#75715e"># opencode</span>
|
||
</span></span><span style="display:flex;"><span>ssh -N -R 8082:localhost:4200 root@95.163.232.9 <span style="color:#75715e"># leadera-fe</span>
|
||
</span></span><span style="display:flex;"><span>ssh -N -R 8083:localhost:8080 root@95.163.232.9 <span style="color:#75715e"># docs-a2v</span>
|
||
</span></span></code></pre></div><h3 id="настройка-launchd-plist">Настройка launchd plist</h3>
|
||
<p><strong>Файл</strong>: <code>~/Library/LaunchAgents/com.openclaw.sshtunnel.plist</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-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#75715e"><?xml version="1.0" encoding="UTF-8"?></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672"><plist</span> <span style="color:#a6e22e">version=</span><span style="color:#e6db74">"1.0"</span><span style="color:#f92672">></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672"><dict></span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672"><key></span>Label<span style="color:#f92672"></key></span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672"><string></span>com.openclaw.sshtunnel<span style="color:#f92672"></string></span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672"><key></span>ProgramArguments<span style="color:#f92672"></key></span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672"><array></span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672"><string></span>ssh<span style="color:#f92672"></string></span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672"><string></span>-N<span style="color:#f92672"></string></span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672"><string></span>-R<span style="color:#f92672"></string></span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672"><string></span>8080:localhost:8080<span style="color:#f92672"></string></span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672"><string></span>root@95.163.232.9<span style="color:#f92672"></string></span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672"></array></span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672"><key></span>RunAtLoad<span style="color:#f92672"></key></span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672"><true/></span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672"><key></span>KeepAlive<span style="color:#f92672"></key></span>
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672"><true/></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672"></dict></span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#f92672"></plist></span>
|
||
</span></span></code></pre></div><h3 id="автозапуск">Автозапуск</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><span style="color:#75715e"># Загрузить plist</span>
|
||
</span></span><span style="display:flex;"><span>launchctl load ~/Library/LaunchAgents/com.openclaw.sshtunnel.plist
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Проверить статус</span>
|
||
</span></span><span style="display:flex;"><span>launchctl list | grep sshtunnel
|
||
</span></span><span style="display:flex;"><span>
|
||
</span></span><span style="display:flex;"><span><span style="color:#75715e"># Удалить (при необходимости)</span>
|
||
</span></span><span style="display:flex;"><span>launchctl unload ~/Library/LaunchAgents/com.openclaw.sshtunnel.plist
|
||
</span></span></code></pre></div><h2 id="доступ-к-сервисам">Доступ к сервисам</h2>
|
||
<p>Через reverse proxy на VPS:</p></p>
|
||
|
||
<div class="tags">
|
||
|
||
<a href="../tags/ssh/" class="tag">ssh</a>
|
||
|
||
<a href="../tags/tunnel/" class="tag">tunnel</a>
|
||
|
||
<a href="../tags/vps/" class="tag">vps</a>
|
||
|
||
<a href="../tags/reverse-proxy/" class="tag">reverse-proxy</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-6 mb-4">
|
||
<div class="card h-100">
|
||
<div class="card-body">
|
||
<h3 class="card-title"><a href="../guides/vps-reverse-proxy/">Настройка VPS как реверс-прокси</a></h3>
|
||
<p class="card-text"><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’s Encrypt</li>
|
||
<li><code>proxy_pass</code> на localhost-порт туннеля</li>
|
||
</ul>
|
||
<p>Пример структуры:</p></p>
|
||
|
||
<div class="tags">
|
||
|
||
<a href="../tags/vps/" class="tag">vps</a>
|
||
|
||
<a href="../tags/nginx/" class="tag">nginx</a>
|
||
|
||
<a href="../tags/ssh/" class="tag">ssh</a>
|
||
|
||
<a href="../tags/reverse-proxy/" class="tag">reverse-proxy</a>
|
||
|
||
<a href="../tags/devops/" class="tag">devops</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-6 mb-4">
|
||
<div class="card h-100">
|
||
<div class="card-body">
|
||
<h3 class="card-title"><a href="../guides/claw-a2v-space/">Подключение claw.a2v.space (OpenClaw)</a></h3>
|
||
<p class="card-text"><h2 id="архитектура">Архитектура</h2>
|
||
<pre tabindex="0"><code>Mac Studio (192.168.0.37:18789)
|
||
→ autossh SSH-туннель
|
||
→ VPS 95.163.232.9:8080
|
||
→ nginx HTTPS
|
||
→ https://claw.a2v.space
|
||
</code></pre><h2 id="шаги-настройки">Шаги настройки</h2>
|
||
<h3 id="1-dns">1. DNS</h3>
|
||
<p>A-запись <code>claw.a2v.space</code> → <code>95.163.232.9</code></p>
|
||
<p>NS-серверы: <code>ns1.hosting.reg.ru</code>, <code>ns2.hosting.reg.ru</code></p>
|
||
<h3 id="2-mac-studio--openclaw">2. Mac Studio — OpenClaw</h3>
|
||
<ul>
|
||
<li>Порт: 18789</li>
|
||
<li>Конфиг: <code>~/.openclaw/openclaw.json</code></li>
|
||
</ul>
|
||
<h3 id="3-ssh-туннель-launchd">3. SSH-туннель (launchd)</h3>
|
||
<p>Plist: <code>~/Library/LaunchAgents/com.openclaw.sshtunnel.plist</code></p>
|
||
<p>Ключевой флаг: <code>-R 8080:localhost:18789</code></p>
|
||
<h3 id="4-vps--nginx-конфиг">4. VPS — nginx конфиг</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-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">claw.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">claw.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/claw.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/claw.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">/</span> {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:8080</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 style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Real-IP</span> $remote_addr;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $proxy_add_x_forwarded_for;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
|
||
</span></span><span style="display:flex;"><span> }
|
||
</span></span><span style="display:flex;"><span>}
|
||
</span></span></code></pre></div><h3 id="5-ssl-сертификат">5. SSL-сертификат</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>certbot certonly --nginx -d claw.a2v.space
|
||
</span></span></code></pre></div><h3 id="6-openclaw-config">6. OpenClaw config</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-json" data-lang="json"><span style="display:flex;"><span>{
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"allowedOrigins"</span>: [<span style="color:#e6db74">"https://claw.a2v.space"</span>],
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">"trustedProxies"</span>: [<span style="color:#e6db74">"127.0.0.1"</span>]
|
||
</span></span><span style="display:flex;"><span>}
|
||
</span></span></code></pre></div></p>
|
||
|
||
<div class="tags">
|
||
|
||
<a href="../tags/openclaw/" class="tag">openclaw</a>
|
||
|
||
<a href="../tags/ssh/" class="tag">ssh</a>
|
||
|
||
<a href="../tags/nginx/" class="tag">nginx</a>
|
||
|
||
<a href="../tags/vps/" class="tag">vps</a>
|
||
|
||
<a href="../tags/reverse-proxy/" class="tag">reverse-proxy</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-6 mb-4">
|
||
<div class="card h-100">
|
||
<div class="card-body">
|
||
<h3 class="card-title"><a href="../guides/code-a2v-space/">Подключение code.a2v.space (OpenCode)</a></h3>
|
||
<p class="card-text"><h2 id="архитектура">Архитектура</h2>
|
||
<pre tabindex="0"><code>Mac Studio (localhost:4096)
|
||
→ autossh SSH-туннель (-R 8081:localhost:4096)
|
||
→ VPS 95.163.232.9:8081
|
||
→ nginx HTTPS
|
||
→ https://code.a2v.space
|
||
</code></pre><h2 id="параметры">Параметры</h2>
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Параметр</th>
|
||
<th>Значение</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>Локальный порт</td>
|
||
<td>4096</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Туннель порт</td>
|
||
<td>8081</td>
|
||
</tr>
|
||
<tr>
|
||
<td>pm2 процесс</td>
|
||
<td><code>opencode</code></td>
|
||
</tr>
|
||
<tr>
|
||
<td>SSL сертификат</td>
|
||
<td><code>/etc/letsencrypt/live/code.a2v.space/</code></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<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">code.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">code.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/code.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/code.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">/</span> {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:8081</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 style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Real-IP</span> $remote_addr;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $proxy_add_x_forwarded_for;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_http_version</span> <span style="color:#ae81ff">1</span><span style="color:#e6db74">.1</span>;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Upgrade</span> $http_upgrade;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">Connection</span> <span style="color:#e6db74">"upgrade"</span>;
|
||
</span></span><span style="display:flex;"><span> }
|
||
</span></span><span style="display:flex;"><span>}
|
||
</span></span></code></pre></div><h2 id="ssl-сертификат">SSL-сертификат</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>certbot certonly --nginx -d code.a2v.space
|
||
</span></span></code></pre></div></p>
|
||
|
||
<div class="tags">
|
||
|
||
<a href="../tags/opencode/" class="tag">opencode</a>
|
||
|
||
<a href="../tags/ssh/" class="tag">ssh</a>
|
||
|
||
<a href="../tags/nginx/" class="tag">nginx</a>
|
||
|
||
<a href="../tags/vps/" class="tag">vps</a>
|
||
|
||
<a href="../tags/reverse-proxy/" class="tag">reverse-proxy</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-md-6 mb-4">
|
||
<div class="card h-100">
|
||
<div class="card-body">
|
||
<h3 class="card-title"><a href="../guides/sand-a2v-space/">Подключение sand.a2v.space (Leadera Project)</a></h3>
|
||
<p class="card-text"><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 style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Real-IP</span> $remote_addr;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $proxy_add_x_forwarded_for;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
|
||
</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">location</span> <span style="color:#e6db74">/</span> {
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_pass</span> <span style="color:#e6db74">http://127.0.0.1:8082</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 style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Real-IP</span> $remote_addr;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-For</span> $proxy_add_x_forwarded_for;
|
||
</span></span><span style="display:flex;"><span> <span style="color:#f92672">proxy_set_header</span> <span style="color:#e6db74">X-Forwarded-Proto</span> $scheme;
|
||
</span></span><span style="display:flex;"><span> }
|
||
</span></span><span style="display:flex;"><span>}
|
||
</span></span></code></pre></div><h2 id="база-данных">База данных</h2>
|
||
<p>PostgreSQL 16 в Docker через Colima:</p></p>
|
||
|
||
<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/nginx/" class="tag">nginx</a>
|
||
|
||
<a href="../tags/vps/" class="tag">vps</a>
|
||
|
||
<a href="../tags/reverse-proxy/" class="tag">reverse-proxy</a>
|
||
|
||
</div>
|
||
|
||
</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>
|