From b8a6d7f0bcd5c469b8a47e898a92874926b7ae90 Mon Sep 17 00:00:00 2001 From: Kavalar Date: Fri, 20 Mar 2026 11:30:30 +0300 Subject: [PATCH] v1.3.2 --- .gitignore | 29 +++++- server.sh | 247 ++++++++++++++++++++++++++++++++++++++++++++++ server_manager.py | 230 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 505 insertions(+), 1 deletion(-) create mode 100755 server.sh create mode 100644 server_manager.py diff --git a/.gitignore b/.gitignore index 5d9c3b8..2a24176 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,31 @@ bin lib -rabota_today.db \ No newline at end of file +rabota_today.db + +# Логи сервера +server.log +server_error.log +server.pid + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +venv/ +.venv/ +env/ +.env + +# База данных +*.db +*.db-journal + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ \ No newline at end of file diff --git a/server.sh b/server.sh new file mode 100755 index 0000000..4d730f0 --- /dev/null +++ b/server.sh @@ -0,0 +1,247 @@ +#!/bin/bash +# server.sh - Bash скрипт для управления сервером + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PID_FILE="$SCRIPT_DIR/server.pid" +LOG_FILE="$SCRIPT_DIR/server.log" +ERROR_LOG="$SCRIPT_DIR/server_error.log" +PYTHON_CMD="python3" +SERVER_SCRIPT="main.py" + +# Цвета для вывода +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Функция для вывода сообщений +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Проверка, запущен ли сервер +is_running() { + if [ -f "$PID_FILE" ]; then + PID=$(cat "$PID_FILE") + if ps -p "$PID" > /dev/null 2>&1; then + return 0 + else + rm -f "$PID_FILE" + return 1 + fi + fi + return 1 +} + +# Получить PID +get_pid() { + if [ -f "$PID_FILE" ]; then + cat "$PID_FILE" + else + echo "" + fi +} + +# Запуск сервера +start() { + if is_running; then + log_error "Сервер уже запущен (PID: $(get_pid))" + return 1 + fi + + log_info "Запуск сервера в фоновом режиме..." + + cd "$SCRIPT_DIR" || exit 1 + + # Запускаем в фоне + nohup $PYTHON_CMD $SERVER_SCRIPT >> "$LOG_FILE" 2>> "$ERROR_LOG" & + PID=$! + + echo $PID > "$PID_FILE" + + # Ждем немного + sleep 2 + + if is_running; then + log_success "Сервер запущен (PID: $PID)" + log_info "Логи: $LOG_FILE" + log_info "Логи ошибок: $ERROR_LOG" + return 0 + else + log_error "Сервер не запустился" + return 1 + fi +} + +# Остановка сервера +stop() { + if ! is_running; then + log_error "Сервер не запущен" + return 1 + fi + + PID=$(get_pid) + log_info "Остановка сервера (PID: $PID)..." + + # Пытаемся остановить gracefully + kill -TERM "$PID" 2>/dev/null + + # Ждем завершения + for i in {1..10}; do + if ! is_running; then + log_success "Сервер остановлен" + rm -f "$PID_FILE" + return 0 + fi + sleep 1 + done + + # Если не остановился, принудительно завершаем + log_warning "Сервер не отвечает, принудительное завершение..." + kill -KILL "$PID" 2>/dev/null + sleep 1 + + if ! is_running; then + log_success "Сервер принудительно остановлен" + rm -f "$PID_FILE" + return 0 + else + log_error "Не удалось остановить сервер" + return 1 + fi +} + +# Перезапуск +restart() { + log_info "Перезапуск сервера..." + stop + sleep 2 + start +} + +# Статус +status() { + if is_running; then + PID=$(get_pid) + log_success "Сервер запущен (PID: $PID)" + + # Показываем использование ресурсов + if command -v ps &> /dev/null; then + ps -p "$PID" -o pid,ppid,cmd,%mem,%cpu,etime + fi + + # Показываем последние строки лога + if [ -f "$LOG_FILE" ]; then + echo "" + log_info "Последние строки лога:" + tail -5 "$LOG_FILE" | while read line; do + echo " $line" + done + fi + else + log_error "Сервер не запущен" + fi +} + +# Показать логи +logs() { + lines=${1:-20} + if [ -f "$LOG_FILE" ]; then + echo "📋 Последние $lines строк лога:" + echo "----------------------------------------" + tail -n "$lines" "$LOG_FILE" + else + log_error "Лог файл не найден" + fi +} + +# Показать ошибки +errors() { + lines=${1:-20} + if [ -f "$ERROR_LOG" ]; then + echo "⚠️ Последние $lines строк лога ошибок:" + echo "----------------------------------------" + tail -n "$lines" "$ERROR_LOG" + else + log_info "Лог ошибок не найден" + fi +} + +# Очистить логи +clear_logs() { + if [ -f "$LOG_FILE" ]; then + > "$LOG_FILE" + log_success "Лог очищен" + fi + + if [ -f "$ERROR_LOG" ]; then + > "$ERROR_LOG" + log_success "Лог ошибок очищен" + fi +} + +# Мониторинг в реальном времени +monitor() { + if ! is_running; then + log_error "Сервер не запущен" + return 1 + fi + + log_info "Мониторинг сервера (Ctrl+C для выхода)..." + tail -f "$LOG_FILE" +} + +# Основная логика +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart) + restart + ;; + status) + status + ;; + logs) + logs "$2" + ;; + errors) + errors "$2" + ;; + clear-logs) + clear_logs + ;; + monitor) + monitor + ;; + *) + echo "Использование: $0 {start|stop|restart|status|logs|errors|clear-logs|monitor}" + echo "" + echo "Команды:" + echo " start - запустить сервер в фоне" + echo " stop - остановить сервер" + echo " restart - перезапустить сервер" + echo " status - показать статус сервера" + echo " logs [N] - показать последние N строк лога (по умолчанию 20)" + echo " errors [N] - показать последние N строк лога ошибок" + echo " clear-logs - очистить логи" + echo " monitor - мониторинг логов в реальном времени" + exit 1 + ;; +esac \ No newline at end of file diff --git a/server_manager.py b/server_manager.py new file mode 100644 index 0000000..a17c588 --- /dev/null +++ b/server_manager.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 +# server_manager.py - Скрипт для управления сервером в фоновом режиме + +import os +import sys +import signal +import subprocess +import time +import atexit +from pathlib import Path + +# Конфигурация +PID_FILE = "server.pid" +LOG_FILE = "server.log" +ERROR_LOG = "server_error.log" +PYTHON_CMD = sys.executable +SERVER_SCRIPT = "server.py" + + +class ServerManager: + def __init__(self): + self.pid_file = Path(PID_FILE) + self.log_file = Path(LOG_FILE) + self.error_log = Path(ERROR_LOG) + + def is_running(self): + """Проверка, запущен ли сервер""" + if not self.pid_file.exists(): + return False + + try: + with open(self.pid_file, 'r') as f: + pid = int(f.read().strip()) + + # Проверяем, существует ли процесс + os.kill(pid, 0) + return True + except (OSError, ValueError, ProcessLookupError): + # PID файл существует, но процесс не найден + self.pid_file.unlink(missing_ok=True) + return False + + def get_pid(self): + """Получить PID сервера""" + if not self.pid_file.exists(): + return None + + try: + with open(self.pid_file, 'r') as f: + return int(f.read().strip()) + except: + return None + + def start(self): + """Запуск сервера в фоновом режиме""" + if self.is_running(): + print(f"❌ Сервер уже запущен (PID: {self.get_pid()})") + return False + + print("🚀 Запуск сервера в фоновом режиме...") + + try: + # Открываем файлы для логов + log_fd = open(self.log_file, 'a') + err_fd = open(self.error_log, 'a') + + # Запускаем процесс + process = subprocess.Popen( + [PYTHON_CMD, SERVER_SCRIPT], + stdout=log_fd, + stderr=err_fd, + stdin=subprocess.DEVNULL, + start_new_session=True, # Отдельная сессия для фонового процесса + cwd=os.path.dirname(os.path.abspath(__file__)) + ) + + # Сохраняем PID + with open(self.pid_file, 'w') as f: + f.write(str(process.pid)) + + # Небольшая задержка для проверки запуска + time.sleep(2) + + if self.is_running(): + print(f"✅ Сервер успешно запущен (PID: {process.pid})") + print(f"📝 Логи: {self.log_file}") + print(f"❌ Логи ошибок: {self.error_log}") + return True + else: + print("❌ Сервер не запустился") + return False + + except Exception as e: + print(f"❌ Ошибка при запуске: {e}") + return False + + def stop(self): + """Остановка сервера""" + if not self.is_running(): + print("❌ Сервер не запущен") + return False + + pid = self.get_pid() + print(f"🛑 Остановка сервера (PID: {pid})...") + + try: + # Отправляем SIGTERM + os.kill(pid, signal.SIGTERM) + + # Ждем завершения + timeout = 10 + for _ in range(timeout): + time.sleep(1) + if not self.is_running(): + print("✅ Сервер остановлен") + self.pid_file.unlink(missing_ok=True) + return True + + # Если не остановился, принудительно завершаем + print("⚠️ Сервер не отвечает, принудительное завершение...") + os.kill(pid, signal.SIGKILL) + time.sleep(1) + + if not self.is_running(): + print("✅ Сервер принудительно остановлен") + self.pid_file.unlink(missing_ok=True) + return True + else: + print("❌ Не удалось остановить сервер") + return False + + except ProcessLookupError: + print("⚠️ Процесс не найден, удаляем PID файл") + self.pid_file.unlink(missing_ok=True) + return True + except Exception as e: + print(f"❌ Ошибка при остановке: {e}") + return False + + def restart(self): + """Перезапуск сервера""" + print("🔄 Перезапуск сервера...") + if self.is_running(): + self.stop() + time.sleep(2) + return self.start() + + def status(self): + """Статус сервера""" + if self.is_running(): + pid = self.get_pid() + print(f"✅ Сервер запущен (PID: {pid})") + print(f"📝 Логи: {self.log_file} ({self.log_file.stat().st_size} bytes)") + print( + f"❌ Логи ошибок: {self.error_log} ({self.error_log.stat().st_size if self.error_log.exists() else 0} bytes)") + + # Показываем последние строки лога + if self.log_file.exists(): + print("\n📋 Последние строки лога:") + with open(self.log_file, 'r') as f: + lines = f.readlines() + for line in lines[-5:]: + print(f" {line.strip()}") + return True + else: + print("❌ Сервер не запущен") + return False + + def logs(self, lines=20): + """Показать последние строки лога""" + if not self.log_file.exists(): + print("📝 Лог файл не найден") + return + + print(f"📋 Последние {lines} строк лога:") + print("-" * 50) + with open(self.log_file, 'r') as f: + all_lines = f.readlines() + for line in all_lines[-lines:]: + print(line.strip()) + + def errors(self, lines=20): + """Показать последние строки лога ошибок""" + if not self.error_log.exists(): + print("❌ Лог ошибок не найден") + return + + print(f"⚠️ Последние {lines} строк лога ошибок:") + print("-" * 50) + with open(self.error_log, 'r') as f: + all_lines = f.readlines() + for line in all_lines[-lines:]: + print(line.strip()) + + +def main(): + manager = ServerManager() + + if len(sys.argv) < 2: + print("Использование:") + print(" python server_manager.py start - запустить сервер") + print(" python server_manager.py stop - остановить сервер") + print(" python server_manager.py restart - перезапустить сервер") + print(" python server_manager.py status - статус сервера") + print(" python server_manager.py logs - показать логи") + print(" python server_manager.py errors - показать ошибки") + return + + command = sys.argv[1].lower() + + if command == 'start': + manager.start() + elif command == 'stop': + manager.stop() + elif command == 'restart': + manager.restart() + elif command == 'status': + manager.status() + elif command == 'logs': + lines = int(sys.argv[2]) if len(sys.argv) > 2 else 20 + manager.logs(lines) + elif command == 'errors': + lines = int(sys.argv[2]) if len(sys.argv) > 2 else 20 + manager.errors(lines) + else: + print(f"❌ Неизвестная команда: {command}") + + +if __name__ == "__main__": + main() \ No newline at end of file