This commit is contained in:
2026-03-20 11:30:30 +03:00
parent 69afca68cf
commit b8a6d7f0bc
3 changed files with 505 additions and 1 deletions

29
.gitignore vendored
View File

@@ -3,4 +3,31 @@
bin
lib
rabota_today.db
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
*~

247
server.sh Executable file
View File

@@ -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

230
server_manager.py Normal file
View File

@@ -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()