feat: добавить поддержку множественных проектов Docker Compose
- Добавлен API эндпоинт /api/projects для получения списка проектов - Обновлен API /api/services для поддержки фильтрации по множественным проектам - Добавлен селектор проектов в веб-интерфейс - Добавлена переменная окружения LOGBOARD_PROJECTS - Обновлен HTML шаблон с JavaScript функциональностью - Добавлена функция fetchProjects() для загрузки списка проектов - Обновлена функция fetchServices() для работы с выбранными проектами Автор: Сергей Антропов Сайт: https://devops.org.ru
This commit is contained in:
75
app.py
75
app.py
@@ -17,6 +17,7 @@ DEFAULT_TAIL = int(os.getenv("LOGBOARD_TAIL", "500"))
|
||||
BASIC_USER = os.getenv("LOGBOARD_USER", "admin")
|
||||
BASIC_PASS = os.getenv("LOGBOARD_PASS", "admin")
|
||||
DEFAULT_PROJECT = os.getenv("COMPOSE_PROJECT_NAME") # filter by compose project
|
||||
DEFAULT_PROJECTS = os.getenv("LOGBOARD_PROJECTS") # multiple projects filter
|
||||
SKIP_UNHEALTHY = os.getenv("LOGBOARD_SKIP_UNHEALTHY", "true").lower() == "true"
|
||||
CONTAINER_LIST_TIMEOUT = int(os.getenv("LOGBOARD_CONTAINER_LIST_TIMEOUT", "10"))
|
||||
CONTAINER_INFO_TIMEOUT = int(os.getenv("LOGBOARD_CONTAINER_INFO_TIMEOUT", "3"))
|
||||
@@ -51,9 +52,48 @@ def verify_ws_token(token: str) -> bool:
|
||||
return raw == f"{BASIC_USER}:{BASIC_PASS}"
|
||||
|
||||
# ---------- DOCKER HELPERS ----------
|
||||
def list_containers(project: Optional[str] = None, include_stopped: bool = False) -> List[Dict]:
|
||||
def get_all_projects() -> List[str]:
|
||||
"""
|
||||
Получает список контейнеров с упрощенной логикой для предотвращения зависания
|
||||
Получает список всех проектов Docker Compose
|
||||
Автор: Сергей Антропов
|
||||
Сайт: https://devops.org.ru
|
||||
"""
|
||||
projects = set()
|
||||
|
||||
try:
|
||||
containers = docker_client.containers.list(all=True)
|
||||
|
||||
for c in containers:
|
||||
try:
|
||||
labels = c.labels or {}
|
||||
project = labels.get("com.docker.compose.project")
|
||||
if project:
|
||||
projects.add(project)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
# Добавляем контейнеры без проекта как "standalone"
|
||||
standalone_count = 0
|
||||
for c in containers:
|
||||
try:
|
||||
labels = c.labels or {}
|
||||
if not labels.get("com.docker.compose.project"):
|
||||
standalone_count += 1
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
if standalone_count > 0:
|
||||
projects.add("standalone")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка получения списка проектов: {e}")
|
||||
return []
|
||||
|
||||
return sorted(list(projects))
|
||||
|
||||
def list_containers(projects: Optional[List[str]] = None, include_stopped: bool = False) -> List[Dict]:
|
||||
"""
|
||||
Получает список контейнеров с поддержкой множественных проектов
|
||||
Автор: Сергей Антропов
|
||||
Сайт: https://devops.org.ru
|
||||
"""
|
||||
@@ -93,9 +133,12 @@ def list_containers(project: Optional[str] = None, include_stopped: bool = False
|
||||
except Exception:
|
||||
pass # Оставляем "unknown"
|
||||
|
||||
# Фильтрация по проекту
|
||||
if project and basic_info["project"] != project:
|
||||
continue
|
||||
# Фильтрация по проектам
|
||||
if projects:
|
||||
# Если проект не указан, считаем его standalone
|
||||
container_project = basic_info["project"] or "standalone"
|
||||
if container_project not in projects:
|
||||
continue
|
||||
|
||||
# Добавляем контейнер в список
|
||||
items.append(basic_info)
|
||||
@@ -130,11 +173,27 @@ def index(creds: HTTPBasicCredentials = Depends(check_basic)):
|
||||
def healthz():
|
||||
return "ok"
|
||||
|
||||
@app.get("/api/projects")
|
||||
def api_projects(_: HTTPBasicCredentials = Depends(check_basic)):
|
||||
"""Получить список всех проектов Docker Compose"""
|
||||
return JSONResponse(get_all_projects())
|
||||
|
||||
@app.get("/api/services")
|
||||
def api_services(project: Optional[str] = Query(None), include_stopped: bool = Query(False),
|
||||
def api_services(projects: Optional[str] = Query(None), include_stopped: bool = Query(False),
|
||||
_: HTTPBasicCredentials = Depends(check_basic)):
|
||||
proj = project or DEFAULT_PROJECT
|
||||
return JSONResponse(list_containers(project=proj, include_stopped=include_stopped))
|
||||
"""
|
||||
Получить список контейнеров с поддержкой множественных проектов
|
||||
projects: список проектов через запятую (например: "project1,project2")
|
||||
"""
|
||||
project_list = None
|
||||
if projects:
|
||||
project_list = [p.strip() for p in projects.split(",") if p.strip()]
|
||||
elif DEFAULT_PROJECTS:
|
||||
project_list = [p.strip() for p in DEFAULT_PROJECTS.split(",") if p.strip()]
|
||||
elif DEFAULT_PROJECT:
|
||||
project_list = [DEFAULT_PROJECT]
|
||||
|
||||
return JSONResponse(list_containers(projects=project_list, include_stopped=include_stopped))
|
||||
|
||||
@app.post("/api/snapshot")
|
||||
def api_snapshot(
|
||||
|
||||
Reference in New Issue
Block a user