From 75e62b57a113b362859d2a81689556a0cc3320f9 Mon Sep 17 00:00:00 2001 From: vrubelroman Date: Thu, 2 Jul 2026 18:32:06 +0000 Subject: [PATCH] remove admin web panel (LichessWebView) from the project Unused component that also carried several security issues (default admin credentials, hardcoded Flask secret key, debug mode, plaintext passwords). Cleaned up compose files, CI/CD pipeline, and docs accordingly; also dropped the stale IS_PROD config docs in favor of the current .env-based setup. Co-Authored-By: Claude Sonnet 5 --- .forgejo/workflows/deploy.yml | 2 - .gitignore | 3 - LichessWebView/Dockerfile | 13 - LichessWebView/app.py | 317 ------------- LichessWebView/auth_config.example.json | 14 - LichessWebView/auth_config.py | 61 --- LichessWebView/requirements.txt | 6 - LichessWebView/templates/index.html | 597 ------------------------ LichessWebView/templates/login.html | 152 ------ README.md | 66 +-- docker-compose.prod.yml | 11 - docker-compose.yml | 14 - docs/ARCHITECTURE.md | 7 +- docs/USER_GUIDE.md | 11 +- start.sh | 1 - 15 files changed, 19 insertions(+), 1256 deletions(-) delete mode 100644 LichessWebView/Dockerfile delete mode 100644 LichessWebView/app.py delete mode 100644 LichessWebView/auth_config.example.json delete mode 100644 LichessWebView/auth_config.py delete mode 100644 LichessWebView/requirements.txt delete mode 100644 LichessWebView/templates/index.html delete mode 100644 LichessWebView/templates/login.html diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index 10bc74d..ef44e77 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -33,7 +33,6 @@ jobs: docker push 192.168.8.174:3000/vrubel/lichess-api:latest docker push 192.168.8.174:3000/vrubel/lichess-bot:latest docker push 192.168.8.174:3000/vrubel/lichess-admin:latest - docker push 192.168.8.174:3000/vrubel/lichess-web:latest - name: Transfer images to prod run: | @@ -41,7 +40,6 @@ jobs: 192.168.8.174:3000/vrubel/lichess-api:latest \ 192.168.8.174:3000/vrubel/lichess-bot:latest \ 192.168.8.174:3000/vrubel/lichess-admin:latest \ - 192.168.8.174:3000/vrubel/lichess-web:latest \ | ssh -i /data/.ssh/id_ed25519 -o StrictHostKeyChecking=no root@130.17.6.76 docker load - name: Deploy on prod host diff --git a/.gitignore b/.gitignore index 6cb6333..0b371e1 100644 --- a/.gitignore +++ b/.gitignore @@ -44,9 +44,6 @@ logs/ .env.local .env.backup -# Auth config (contains passwords) -LichessWebView/auth_config.json - # Node modules (если будут использоваться) node_modules/ diff --git a/LichessWebView/Dockerfile b/LichessWebView/Dockerfile deleted file mode 100644 index 23a2103..0000000 --- a/LichessWebView/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM python:3.11-slim - -WORKDIR /app - -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt - -COPY . . - -EXPOSE 5000 - -CMD ["python", "app.py"] - diff --git a/LichessWebView/app.py b/LichessWebView/app.py deleted file mode 100644 index 94a2158..0000000 --- a/LichessWebView/app.py +++ /dev/null @@ -1,317 +0,0 @@ -from flask import Flask, jsonify, render_template, request, session, redirect, url_for -from flask_cors import CORS -import sqlite3 -from datetime import datetime, date -from functools import wraps -import os -import auth_config - -app = Flask(__name__) -# Secret key для сессий (в продакшене лучше использовать переменную окружения) -app.secret_key = os.environ.get('FLASK_SECRET_KEY', 'your-secret-key-change-in-production-please') -CORS(app) - -# Путь к базе данных бота -DB_PATH = "/app/data/lichess_bot.db" - - -def login_required(f): - """Декоратор для защиты роутов, требующих аутентификации""" - @wraps(f) - def decorated_function(*args, **kwargs): - if 'authenticated' not in session or not session['authenticated']: - return redirect(url_for('login')) - return f(*args, **kwargs) - return decorated_function - -def get_message_counters_stats(db_path): - """Get message counters statistics directly from database""" - try: - with sqlite3.connect(db_path) as conn: - cursor = conn.cursor() - - # Check if table exists - cursor.execute(''' - SELECT name FROM sqlite_master - WHERE type='table' AND name='message_counters' - ''') - if not cursor.fetchone(): - # Table doesn't exist, return empty stats - return { - 'total_all_time': 0, - 'total_today': 0, - 'by_command': {} - } - - # Reset daily counters if needed - today = date.today() - cursor.execute(''' - SELECT command, last_reset_date FROM message_counters - ''') - - for row in cursor.fetchall(): - cmd, last_reset = row[0], row[1] - if last_reset: - try: - last_reset_date = datetime.strptime(last_reset, '%Y-%m-%d').date() - if last_reset_date < today: - cursor.execute(''' - UPDATE message_counters - SET today_count = 0, last_reset_date = ? - WHERE command = ? - ''', (today.isoformat(), cmd)) - except (ValueError, TypeError): - cursor.execute(''' - UPDATE message_counters - SET today_count = 0, last_reset_date = ? - WHERE command = ? - ''', (today.isoformat(), cmd)) - else: - cursor.execute(''' - UPDATE message_counters - SET last_reset_date = ? - WHERE command = ? - ''', (today.isoformat(), cmd)) - - conn.commit() - - # Get all counters - cursor.execute(''' - SELECT command, total_count, today_count - FROM message_counters - ORDER BY command - ''') - - stats = { - 'by_command': {}, - 'total_all_time': 0, - 'total_today': 0 - } - - for row in cursor.fetchall(): - cmd, total, today = row[0], row[1], row[2] - stats['by_command'][cmd] = { - 'total': total, - 'today': today - } - stats['total_all_time'] += total - stats['total_today'] += today - - return { - 'total_all_time': stats['total_all_time'], - 'total_today': stats['total_today'], - 'by_command': stats['by_command'] - } - except Exception as e: - print(f"Error getting message counters: {e}") - import traceback - print(traceback.format_exc()) - return { - 'total_all_time': 0, - 'total_today': 0, - 'by_command': {} - } - -@app.route('/login', methods=['GET', 'POST']) -def login(): - """Страница входа""" - if request.method == 'POST': - username = request.form.get('username', '').strip() - password = request.form.get('password', '') - - if auth_config.validate_credentials(username, password): - session['authenticated'] = True - session['username'] = username - return redirect(url_for('index')) - else: - error_msg = 'Неверный логин или пароль' - return redirect(url_for('login', error=error_msg)) - - # GET запрос - показать форму логина - error = request.args.get('error', '') - # Если уже авторизован, перенаправляем на главную - if 'authenticated' in session and session.get('authenticated'): - return redirect(url_for('index')) - return render_template('login.html', error=error) - - -@app.route('/logout') -def logout(): - """Выход из системы""" - session.pop('authenticated', None) - session.pop('username', None) - return redirect(url_for('login')) - - -@app.route('/') -@login_required -def index(): - """Главная страница""" - return render_template('index.html') - -@app.route('/api/users') -@login_required -def get_users(): - """Получить всех пользователей""" - try: - with sqlite3.connect(DB_PATH) as conn: - cursor = conn.cursor() - - # Получаем всех пользователей - cursor.execute(''' - SELECT - tu.user_id, - tu.username, - tu.first_name, - tu.last_name, - tu.created_at, - tu.bot_language, - COUNT(ug.id) as gamer_count, - SUM(CASE WHEN ug.period_minutes > 0 THEN 1 ELSE 0 END) as monitored_gamers - FROM telegram_users tu - LEFT JOIN user_gamers ug ON tu.user_id = ug.user_id - GROUP BY tu.user_id, tu.username, tu.first_name, tu.last_name, tu.created_at, tu.bot_language - ORDER BY tu.created_at DESC - ''') - - rows = cursor.fetchall() - users = [] - - for row in rows: - users.append({ - 'user_id': row[0], - 'username': row[1] or '-', - 'first_name': row[2] or '-', - 'last_name': row[3], - 'created_at': row[4], - 'bot_language': row[5] or 'en', # Default to 'en' if not set - 'gamer_count': row[6], - 'monitored_gamers': row[7] - }) - - # Получаем общее количество игроков (уникальных) - cursor.execute(''' - SELECT COUNT(DISTINCT g.id) - FROM gamers g - ''') - total_gamers = cursor.fetchone()[0] - - # Count new users today - today = date.today().isoformat() - cursor.execute("SELECT COUNT(*) FROM telegram_users WHERE DATE(created_at) = ?", (today,)) - users_today = cursor.fetchone()[0] - - # Count users without gamers - cursor.execute(""" - SELECT COUNT(DISTINCT tu.user_id) - FROM telegram_users tu - LEFT JOIN user_gamers ug ON tu.user_id = ug.user_id - WHERE ug.id IS NULL - """) - users_without_gamers = cursor.fetchone()[0] - - # Count users with Russian language - cursor.execute(""" - SELECT COUNT(DISTINCT tu.user_id) - FROM telegram_users tu - WHERE tu.bot_language = 'ru' - """) - users_with_ru_language = cursor.fetchone()[0] - - # Calculate percentages - total_users = len(users) - users_without_gamers_percent = round((users_without_gamers / total_users * 100)) if total_users > 0 else 0 - users_ru_language_percent = round((users_with_ru_language / total_users * 100)) if total_users > 0 else 0 - - # Count new gamers today (from user_gamers table) - cursor.execute(""" - SELECT COUNT(DISTINCT g.id) - FROM user_gamers ug - JOIN gamers g ON ug.gamer_id = g.id - WHERE DATE(ug.created_at) = ? - """, (today,)) - gamers_today = cursor.fetchone()[0] - - # Get message counters statistics - message_stats = get_message_counters_stats(DB_PATH) - - # Filter out excluded commands (same as in admin_bot) - excluded_commands = {'lang', 'resetlang', 'start'} - if message_stats.get('by_command'): - filtered_by_command = { - cmd: data for cmd, data in message_stats['by_command'].items() - if cmd not in excluded_commands - } - message_stats['by_command'] = filtered_by_command - - return jsonify({ - 'success': True, - 'users': users, - 'total_users': total_users, - 'users_today': users_today, - 'users_without_gamers': users_without_gamers, - 'users_without_gamers_percent': users_without_gamers_percent, - 'users_with_ru_language': users_with_ru_language, - 'users_ru_language_percent': users_ru_language_percent, - 'total_gamers': total_gamers, - 'gamers_today': gamers_today, - 'message_stats': message_stats - }) - - except Exception as e: - return jsonify({ - 'success': False, - 'error': str(e) - }), 500 - -@app.route('/api/users//gamers') -@login_required -def get_user_gamers(user_id): - """Получить игроков конкретного пользователя""" - try: - with sqlite3.connect(DB_PATH) as conn: - cursor = conn.cursor() - - # Получаем игроков пользователя - cursor.execute(''' - SELECT - g.id, - g.username, - ug.token, - ug.period_minutes, - ug.created_at - FROM user_gamers ug - JOIN gamers g ON ug.gamer_id = g.id - WHERE ug.user_id = ? - ORDER BY g.username - ''', (user_id,)) - - rows = cursor.fetchall() - gamers = [] - - for row in rows: - gamers.append({ - 'id': row[0], - 'username': row[1], - 'has_token': bool(row[2]), # Token from user_gamers, not gamers - 'period_minutes': row[3], - 'created_at': row[4] - }) - - return jsonify({ - 'success': True, - 'gamers': gamers - }) - - except Exception as e: - return jsonify({ - 'success': False, - 'error': str(e) - }), 500 - -if __name__ == '__main__': - app.run(host='0.0.0.0', port=5000, debug=True) - - - - diff --git a/LichessWebView/auth_config.example.json b/LichessWebView/auth_config.example.json deleted file mode 100644 index 642bee2..0000000 --- a/LichessWebView/auth_config.example.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "users": [ - { - "username": "admin", - "password": "change_me_123" - }, - { - "username": "user2", - "password": "another_password" - } - ] -} - - diff --git a/LichessWebView/auth_config.py b/LichessWebView/auth_config.py deleted file mode 100644 index e72e086..0000000 --- a/LichessWebView/auth_config.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Модуль для загрузки конфигурации аутентификации""" -import json -import os -from typing import Dict, List, Optional - -AUTH_CONFIG_PATH = "/app/auth_config.json" -AUTH_CONFIG_EXAMPLE_PATH = "/app/auth_config.example.json" - - -def load_auth_config() -> Optional[Dict]: - """ - Загружает конфигурацию аутентификации из файла. - Возвращает None, если файл не найден. - """ - config_path = AUTH_CONFIG_PATH - - # Если файл не существует, пробуем example - if not os.path.exists(config_path): - if os.path.exists(AUTH_CONFIG_EXAMPLE_PATH): - print(f"Warning: {config_path} not found, using example config") - config_path = AUTH_CONFIG_EXAMPLE_PATH - else: - print(f"Error: Neither {AUTH_CONFIG_PATH} nor {AUTH_CONFIG_EXAMPLE_PATH} found") - return None - - try: - with open(config_path, 'r', encoding='utf-8') as f: - config = json.load(f) - return config - except json.JSONDecodeError as e: - print(f"Error parsing auth config: {e}") - return None - except Exception as e: - print(f"Error loading auth config: {e}") - return None - - -def get_users() -> List[Dict[str, str]]: - """ - Возвращает список пользователей из конфига. - """ - config = load_auth_config() - if not config: - return [] - - return config.get('users', []) - - -def validate_credentials(username: str, password: str) -> bool: - """ - Проверяет правильность логина и пароля. - """ - users = get_users() - - for user in users: - if user.get('username') == username and user.get('password') == password: - return True - - return False - - diff --git a/LichessWebView/requirements.txt b/LichessWebView/requirements.txt deleted file mode 100644 index f1fcb6f..0000000 --- a/LichessWebView/requirements.txt +++ /dev/null @@ -1,6 +0,0 @@ -Flask==3.0.0 -flask-cors==4.0.0 - - - - diff --git a/LichessWebView/templates/index.html b/LichessWebView/templates/index.html deleted file mode 100644 index 565a22f..0000000 --- a/LichessWebView/templates/index.html +++ /dev/null @@ -1,597 +0,0 @@ - - - - - - Lichess Bot Users Monitor - - - -
- -
-
-

👥 Пользователи

- Выход -
- -
- Всего пользователей: 0 (сегодня: 0)
- 👤 Пользователей без игроков: 0 (0%)
- 🇷🇺 Пользователей с русским языком: 0 (0%)
- Кол-во игроков: 0 (сегодня: 0) -
- -
-

📨 Счетчики сообщений

-
- Всего отправлено: 0 -
-
- Сегодня отправлено: 0 -
-
-
-
-
За все время:
-
-
-
-
За сегодня:
-
-
-
-
-
- - - -
-
Загрузка...
-
-
- - -
-

🎮 Отслеживаемые игроки

- - - -
-
Выберите пользователя для просмотра его игроков
-
-
-
- - - - - - - - diff --git a/LichessWebView/templates/login.html b/LichessWebView/templates/login.html deleted file mode 100644 index 4f15abc..0000000 --- a/LichessWebView/templates/login.html +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - Вход в админ-панель - - - - - - - - - diff --git a/README.md b/README.md index f14ad50..d7a6c73 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,13 @@ # Lichess Statistics Ecosystem -Полнофункциональная система для отслеживания статистики игроков Lichess.org с Telegram ботом и веб-интерфейсом. +Полнофункциональная система для отслеживания статистики игроков Lichess.org с Telegram ботом. ## 🎯 Описание проекта -Система состоит из трех взаимосвязанных компонентов: +Система состоит из двух взаимосвязанных компонентов: 1. **LichessWebServices** - REST API для получения статистики игроков Lichess 2. **LichessClientTG_bot** - Telegram бот для управления подписками и уведомлений -3. **LichessWebView** - Веб-интерфейс для просмотра пользователей и их игроков ## 🏗️ Архитектура @@ -21,15 +20,13 @@ │ │ (Управление подписками, уведомления) │ │ │ └────────┬───────────────────────────────────────────┘ │ │ │ │ -│ ├──────────────────────┐ │ -│ │ │ │ -│ ┌────────▼──────────┐ ┌──────▼──────────────┐ │ -│ │ LichessWebServices │ │ LichessWebView │ │ -│ │ │ │ │ │ -│ │ REST API для │ │ Веб-интерфейс │ │ -│ │ получения │ │ для просмотра │ │ -│ │ статистики │ │ пользователей │ │ -│ └────────┬───────────┘ └────────────────────┘ │ +│ ┌────────▼───────────┐ │ +│ │ LichessWebServices │ │ +│ │ │ │ +│ │ REST API для │ │ +│ │ получения │ │ +│ │ статистики │ │ +│ └────────┬────────────┘ │ │ │ │ └───────────┼──────────────────────────────────────────────┘ │ @@ -59,7 +56,6 @@ cd LichessStatTgWeb После запуска доступны: - **API документация**: http://localhost:8001/docs -- **Веб-интерфейс**: http://localhost:5000 - **Telegram бот**: работает в фоне ## 📦 Структура проекта @@ -79,11 +75,6 @@ LichessStatTgWeb/ │ ├── formatters.py # Форматирование ответов │ └── config.py # Конфигурация │ -├── LichessWebView/ # Веб-интерфейс -│ ├── app.py # Flask приложение -│ └── templates/ # HTML шаблоны -│ └── index.html # Главная страница -│ ├── docker-compose.yml # Общая конфигурация контейнеров ├── start.sh # Скрипт запуска всех сервисов └── README.md # Этот файл @@ -142,19 +133,6 @@ Telegram бот для управления отслеживанием игро **Подробнее о боте:** см. [about.md](about.md) -### 3. LichessWebView (Веб-интерфейс) - -Веб-интерфейс для просмотра пользователей и их игроков. - -**Возможности:** -- Просмотр всех пользователей бота -- Фильтрация пользователей по имени/никнейму -- Отображение игроков каждого пользователя -- Статистика по каждому пользователю -- Отображение активных игроков и периодов -- Отображение языка пользователя (🇷🇺 ru / 🇬🇧 en) -- Статистика пользователей с русским языком (количество и процент) - ## 🗄️ База данных Система использует SQLite базу данных с таблицами: @@ -187,20 +165,15 @@ docker-compose down ### Telegram бот -Токены ботов настраиваются в `LichessClientTG_bot/config.py`: -```python -IS_PROD = False # True для продакшн, False для тестовых токенов - -# Production токены (используются при IS_PROD = True) -TELEGRAM_BOT_TOKEN_PROD = "YOUR_PROD_TOKEN" -ADMINPANEL_TELEGRAM_BOT_TOKEN_PROD = "YOUR_ADMIN_PROD_TOKEN" - -# Test токены (используются при IS_PROD = False) -TELEGRAM_BOT_TOKEN_TEST = "YOUR_TEST_TOKEN" -ADMINPANEL_TELEGRAM_BOT_TOKEN_TEST = "YOUR_ADMIN_TEST_TOKEN" +Токены ботов задаются переменными окружения в `.env` (см. `.env.example`): +``` +TELEGRAM_BOT_TOKEN=YOUR_BOT_TOKEN +ADMINPANEL_TELEGRAM_BOT_TOKEN=YOUR_ADMIN_BOT_TOKEN ``` -Система автоматически выбирает нужные токены в зависимости от значения `IS_PROD`. +`LichessClientTG_bot/config.py` читает их через `os.getenv(...)`. Разделение на тестовый и +продакшн стенды обеспечивается не флагом в коде, а раздельными `.env` на каждом окружении: +на локальном/тестовом стенде — тестовые токены, на проде — свои, боевые. ### Lichess API @@ -231,10 +204,6 @@ docker-compose up -d # Telegram бот cd LichessClientTG_bot docker-compose up -d - -# Веб-интерфейс -cd LichessWebView -docker-compose up -d ``` ### Логи @@ -245,9 +214,6 @@ docker logs lichesswebservices_lichess-api_1 -f # Логи бота docker logs lichess-telegram-bot -f - -# Логи веб-интерфейса -docker logs lichess-web-view -f ``` ## 📝 Лицензия diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index e483a0c..3c170d9 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -50,17 +50,6 @@ services: depends_on: - lichess-api - web-view: - image: 192.168.8.174:3000/vrubel/lichess-web:latest - container_name: lichess-web-view - ports: - - "5000:5000" - volumes: - - ./data:/app/data:ro - restart: always - depends_on: - - lichess-bot - networks: default: name: lichess-network diff --git a/docker-compose.yml b/docker-compose.yml index 10c187e..0f25aac 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -62,20 +62,6 @@ services: depends_on: - lichess-api - # Web View Interface - web-view: - build: ./LichessWebView - image: 192.168.8.174:3000/vrubel/lichess-web:latest - container_name: lichess-web-view - ports: - - "5000:5000" - volumes: - - ./LichessClientTG_bot/data:/app/data:ro - - ./LichessWebView:/app - restart: always - depends_on: - - lichess-bot - networks: default: name: lichess-network diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 779d6cb..46cb0bd 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -2,11 +2,10 @@ ## Обзор проекта -Проект состоит из трех основных компонентов: +Проект состоит из двух основных компонентов: 1. **LichessWebServices** - REST API для получения статистики с Lichess 2. **LichessClientTG_bot** - Telegram бот для отслеживания игроков -3. **LichessWebView** - Веб-интерфейс для просмотра пользователей и игроков ## Структура базы данных @@ -116,17 +115,15 @@ CREATE TABLE user_gamers ( ## Docker Compose -Все три сервиса запускаются через единый `docker-compose.yml`: +Оба сервиса запускаются через единый `docker-compose.yml`: - **lichess-api**: API на порту 8001 - **lichess-telegram-bot**: Бот (host network) -- **lichess-web-view**: Веб-интерфейс на порту 5000 База данных монтируется как bind mount: `./LichessClientTG_bot/data:/app/data` ## Безопасность - Токены Lichess API хранятся в базе данных и используются только для запросов к Lichess -- Веб-интерфейс работает **без пароля** - доступен всем кто знает URL - Telegram бот токен хранится в `config.py` (можно вынести в `.env`) diff --git a/docs/USER_GUIDE.md b/docs/USER_GUIDE.md index f9717a3..f1073f7 100644 --- a/docs/USER_GUIDE.md +++ b/docs/USER_GUIDE.md @@ -41,13 +41,12 @@ chmod +x start.sh - Остановит существующие контейнеры (если есть) - Создаст бэкап базы данных - Пересоберет Docker образы -- Запустит все три сервиса +- Запустит оба сервиса ### Шаг 4: Проверка Откройте в браузере: - **API документация**: http://localhost:8001/docs -- **Веб-интерфейс**: http://localhost:5000 Проверьте логи: @@ -127,12 +126,6 @@ docker logs lichess-telegram-bot -f docker logs lichess-api -f ``` -### Только веб-интерфейс - -```bash -docker logs lichess-web-view -f -``` - ## Остановка и удаление ### Остановка без удаления данных @@ -179,7 +172,6 @@ chmod +x import_db.sh start.sh ```bash # Найдите процесс на порту sudo fuser -k 8001/tcp # Для API -sudo fuser -k 5000/tcp # Для веб-интерфейса # Перезапустите ./start.sh @@ -212,7 +204,6 @@ LichessStatTgWeb/ ├── LichessClientTG_bot/ # Telegram бот │ └── data/ │ └── lichess_bot.db # База данных (НЕ в git!) -├── LichessWebView/ # Веб-интерфейс ├── backups/ # Бэкапы БД (НЕ в git!) ├── docs/ # Документация ├── docker-compose.yml # Конфигурация Docker diff --git a/start.sh b/start.sh index daed230..c355bbd 100755 --- a/start.sh +++ b/start.sh @@ -54,7 +54,6 @@ echo "✅ Все сервисы запущены!" echo "" echo "🌐 Доступные сервисы:" echo " - API документация: http://localhost:8002/docs" -echo " - Веб-интерфейс: http://localhost:5000" echo "" echo "📋 Для просмотра логов используйте:" echo " ${COMPOSE_CMD_DISPLAY} logs -f"