feat: добавлена пометка типа операции (Build/Push) в истории сборок Dockerfile
- Добавлена колонка 'Тип' во все таблицы истории сборок - Для push операций отображается registry вместо платформ - Сохранение пользователя при создании push лога - Исправлена ошибка с logger в push_docker_image endpoint - Улучшено отображение истории сборок с визуальными индикаторами
This commit is contained in:
306
docs/WEB_INTERFACE_QUICKSTART.md
Normal file
306
docs/WEB_INTERFACE_QUICKSTART.md
Normal file
@@ -0,0 +1,306 @@
|
||||
# Быстрый старт веб-интерфейса DevOpsLab
|
||||
|
||||
**Автор:** Сергей Антропов
|
||||
**Сайт:** https://devops.org.ru
|
||||
|
||||
## 🚀 Быстрый старт
|
||||
|
||||
### 1. Создание git ветки
|
||||
|
||||
```bash
|
||||
git checkout -b feature/web-interface
|
||||
git push -u origin feature/web-interface
|
||||
```
|
||||
|
||||
### 2. Структура проекта
|
||||
|
||||
```
|
||||
app/
|
||||
├── main.py # FastAPI приложение
|
||||
├── api/v1/endpoints/ # API endpoints
|
||||
├── templates/ # HTMX шаблоны
|
||||
├── static/ # CSS, JS
|
||||
└── core/ # Ядро (MakeExecutor, DockerClient)
|
||||
```
|
||||
|
||||
### 3. Основные компоненты
|
||||
|
||||
#### MakeExecutor - выполнение Makefile команд
|
||||
|
||||
```python
|
||||
# app/core/make_executor.py
|
||||
import subprocess
|
||||
from typing import Dict, List
|
||||
|
||||
class MakeExecutor:
|
||||
def execute(self, command: str, args: List[str] = None) -> Dict:
|
||||
cmd = ["make"] + command.split() + (args or [])
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
return {
|
||||
"success": result.returncode == 0,
|
||||
"stdout": result.stdout,
|
||||
"stderr": result.stderr
|
||||
}
|
||||
```
|
||||
|
||||
#### FastAPI роуты
|
||||
|
||||
```python
|
||||
# app/api/v1/endpoints/roles.py
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from app.core.make_executor import MakeExecutor
|
||||
|
||||
router = APIRouter()
|
||||
templates = Jinja2Templates(directory="app/templates")
|
||||
executor = MakeExecutor()
|
||||
|
||||
@router.get("/roles", response_class=HTMLResponse)
|
||||
async def list_roles(request: Request):
|
||||
# Получаем список ролей через make
|
||||
result = executor.execute("role list")
|
||||
roles = parse_roles(result["stdout"])
|
||||
return templates.TemplateResponse(
|
||||
"pages/roles/list.html",
|
||||
{"request": request, "roles": roles}
|
||||
)
|
||||
|
||||
@router.post("/roles/{role_name}/test")
|
||||
async def test_role(role_name: str, preset: str = "default"):
|
||||
# Запуск теста
|
||||
result = executor.execute(f"role test {preset}")
|
||||
return {"status": "success" if result["success"] else "failed"}
|
||||
```
|
||||
|
||||
#### HTMX шаблон
|
||||
|
||||
```html
|
||||
<!-- app/templates/pages/roles/list.html -->
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{% for role in roles %}
|
||||
<div class="card">
|
||||
<h3>{{ role.name }}</h3>
|
||||
<p>{{ role.description }}</p>
|
||||
<button
|
||||
hx-post="/api/v1/roles/{{ role.name }}/test"
|
||||
hx-target="#test-results"
|
||||
class="btn-primary">
|
||||
Запустить тест
|
||||
</button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
```
|
||||
|
||||
### 4. Примеры страниц
|
||||
|
||||
#### Dashboard
|
||||
|
||||
```html
|
||||
<!-- app/templates/pages/dashboard.html -->
|
||||
<div class="dashboard">
|
||||
<div class="stats">
|
||||
<div class="stat-card">
|
||||
<h3>Всего ролей</h3>
|
||||
<p hx-get="/api/v1/stats/roles" hx-trigger="every 5s">
|
||||
{{ total_roles }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="recent-tests">
|
||||
<h2>Последние тесты</h2>
|
||||
<div id="test-list"
|
||||
hx-get="/api/v1/tests/recent"
|
||||
hx-trigger="every 10s">
|
||||
<!-- Загружается через HTMX -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### Создание роли
|
||||
|
||||
```html
|
||||
<!-- app/templates/pages/roles/create.html -->
|
||||
<form hx-post="/api/v1/roles/create" hx-target="#result">
|
||||
<div class="step" id="step-1">
|
||||
<h3>Шаг 1: Базовая информация</h3>
|
||||
<input name="name" placeholder="Имя роли" required>
|
||||
<textarea name="description" placeholder="Описание"></textarea>
|
||||
<select name="template">
|
||||
<option value="service">Service</option>
|
||||
<option value="package">Package</option>
|
||||
<option value="config">Config</option>
|
||||
</select>
|
||||
<button type="button" hx-get="/api/v1/roles/create/step/2">
|
||||
Далее
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="step" id="step-2" style="display:none;">
|
||||
<h3>Шаг 2: Переменные</h3>
|
||||
<div id="variables">
|
||||
<div class="variable-row">
|
||||
<input name="var_name" placeholder="Имя переменной">
|
||||
<input name="var_value" placeholder="Значение">
|
||||
<select name="var_type">
|
||||
<option value="string">String</option>
|
||||
<option value="int">Integer</option>
|
||||
<option value="bool">Boolean</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" hx-post="/api/v1/roles/create/add-variable">
|
||||
Добавить переменную
|
||||
</button>
|
||||
<button type="submit">Создать роль</button>
|
||||
</div>
|
||||
</form>
|
||||
```
|
||||
|
||||
### 5. WebSocket для live логов
|
||||
|
||||
```python
|
||||
# app/api/v1/endpoints/websocket.py
|
||||
from fastapi import WebSocket
|
||||
from app.core.make_executor import MakeExecutor
|
||||
|
||||
@router.websocket("/ws/test/{test_id}")
|
||||
async def test_websocket(websocket: WebSocket, test_id: str):
|
||||
await websocket.accept()
|
||||
executor = MakeExecutor()
|
||||
|
||||
# Запуск теста с потоковым выводом
|
||||
process = await executor.execute_stream(f"role test {test_id}")
|
||||
|
||||
async for line in process.stdout:
|
||||
await websocket.send_json({
|
||||
"type": "log",
|
||||
"data": line.decode()
|
||||
})
|
||||
|
||||
await websocket.send_json({"type": "complete"})
|
||||
```
|
||||
|
||||
```javascript
|
||||
// app/static/js/test-live.js
|
||||
const ws = new WebSocket('ws://localhost:8000/ws/test/nginx');
|
||||
const logContainer = document.getElementById('test-logs');
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.type === 'log') {
|
||||
logContainer.innerHTML += `<div>${data.data}</div>`;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 6. Запуск приложения
|
||||
|
||||
```python
|
||||
# app/main.py
|
||||
from fastapi import FastAPI
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from app.api.v1.endpoints import roles, presets, tests
|
||||
|
||||
app = FastAPI(title="DevOpsLab Web Interface")
|
||||
|
||||
# Статика
|
||||
app.mount("/static", StaticFiles(directory="app/static"), name="static")
|
||||
|
||||
# Шаблоны
|
||||
templates = Jinja2Templates(directory="app/templates")
|
||||
|
||||
# Роуты
|
||||
app.include_router(roles.router, prefix="/api/v1")
|
||||
app.include_router(presets.router, prefix="/api/v1")
|
||||
app.include_router(tests.router, prefix="/api/v1")
|
||||
|
||||
@app.get("/")
|
||||
async def dashboard(request: Request):
|
||||
return templates.TemplateResponse("pages/dashboard.html", {"request": request})
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
### 7. Docker Compose для разработки
|
||||
|
||||
```yaml
|
||||
# docker-compose.dev.yml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
web:
|
||||
build: ./app
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- ./app:/app
|
||||
- .:/workspace
|
||||
environment:
|
||||
- PROJECT_ROOT=/workspace
|
||||
command: uvicorn main:app --reload --host 0.0.0.0
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
ports:
|
||||
- "6379:6379"
|
||||
|
||||
celery:
|
||||
build: ./app
|
||||
command: celery -A tasks.celery_tasks worker --loglevel=info
|
||||
volumes:
|
||||
- ./app:/app
|
||||
- .:/workspace
|
||||
depends_on:
|
||||
- redis
|
||||
```
|
||||
|
||||
### 8. Команды для разработки
|
||||
|
||||
```bash
|
||||
# Установка зависимостей
|
||||
cd app
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Запуск в режиме разработки
|
||||
uvicorn main:app --reload
|
||||
|
||||
# Запуск через Docker
|
||||
docker-compose -f docker-compose.dev.yml up
|
||||
|
||||
# Запуск Celery worker
|
||||
celery -A tasks.celery_tasks worker --loglevel=info
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Чек-лист реализации
|
||||
|
||||
- [ ] Создать git ветку
|
||||
- [ ] Настроить структуру проекта
|
||||
- [ ] Реализовать MakeExecutor
|
||||
- [ ] Создать базовые шаблоны
|
||||
- [ ] Реализовать Dashboard
|
||||
- [ ] Реализовать список ролей
|
||||
- [ ] Реализовать создание роли
|
||||
- [ ] Реализовать тестирование роли
|
||||
- [ ] Добавить WebSocket для live логов
|
||||
- [ ] Настроить Celery для фоновых задач
|
||||
- [ ] Добавить базу данных для истории
|
||||
- [ ] Улучшить UI/UX
|
||||
- [ ] Добавить аутентификацию
|
||||
- [ ] Написать документацию
|
||||
|
||||
---
|
||||
|
||||
**Полная версия:** [WEB_INTERFACE_PROPOSAL.md](WEB_INTERFACE_PROPOSAL.md)
|
||||
Reference in New Issue
Block a user