Files
DevOpsLab/docs/WEB_INTERFACE_QUICKSTART.md
Сергей Антропов 1fbf9185a2 feat: добавлена пометка типа операции (Build/Push) в истории сборок Dockerfile
- Добавлена колонка 'Тип' во все таблицы истории сборок
- Для push операций отображается registry вместо платформ
- Сохранение пользователя при создании push лога
- Исправлена ошибка с logger в push_docker_image endpoint
- Улучшено отображение истории сборок с визуальными индикаторами
2026-02-15 22:59:02 +03:00

307 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Быстрый старт веб-интерфейса 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)