Обновлено форматирование даты в логах на DD.MM.YYYY для скриптов и хуков

This commit is contained in:
Dmitriy Fofanov
2025-10-28 00:19:51 +03:00
parent c855db843e
commit 8612e71996
3 changed files with 123 additions and 60 deletions

107
Makefile
View File

@@ -103,11 +103,33 @@ setup-dirs:
# Установка зависимостей # Установка зависимостей
install-dependencies: install-dependencies:
@echo "$(YELLOW)→ Установка зависимостей Python...$(NC)" @echo "$(YELLOW)→ Установка зависимостей Python...$(NC)"
@if ! command -v pip3 >/dev/null 2>&1; then \ @if command -v pip3 >/dev/null 2>&1; then \
echo "$(RED)✗ pip3 не найден. Установите python3-pip$(NC)"; \ pip3 install -q requests cryptography 2>/dev/null || pip3 install requests cryptography; \
elif command -v pip >/dev/null 2>&1; then \
pip install -q requests cryptography 2>/dev/null || pip install requests cryptography; \
elif command -v python3 >/dev/null 2>&1; then \
if python3 -m pip --version >/dev/null 2>&1; then \
python3 -m pip install -q requests cryptography 2>/dev/null || python3 -m pip install requests cryptography; \
else \
echo "$(RED)✗ pip не установлен. Выполните:$(NC)"; \
echo " $(CYAN)sudo apt-get update && sudo apt-get install -y python3-pip$(NC)"; \
echo " или"; \
echo " $(CYAN)curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && python3 get-pip.py$(NC)"; \
exit 1; \
fi; \
elif command -v python >/dev/null 2>&1; then \
if python -m pip --version >/dev/null 2>&1; then \
python -m pip install -q requests cryptography 2>/dev/null || python -m pip install requests cryptography; \
else \
echo "$(RED)✗ pip не установлен. Выполните:$(NC)"; \
echo " $(CYAN)sudo apt-get update && sudo apt-get install -y python3-pip$(NC)"; \
exit 1; \
fi; \
else \
echo "$(RED)✗ Python не найден. Установите Python 3:$(NC)"; \
echo " $(CYAN)sudo apt-get update && sudo apt-get install -y python3 python3-pip$(NC)"; \
exit 1; \ exit 1; \
fi fi
@pip3 install -q requests cryptography 2>/dev/null || pip3 install requests cryptography
@echo "$(GREEN)✓ Зависимости установлены$(NC)" @echo "$(GREEN)✓ Зависимости установлены$(NC)"
# Копирование скрипта # Копирование скрипта
@@ -193,17 +215,20 @@ uninstall: check-root
@echo "$(RED)║ Удаление Let's Encrypt SSL Manager ║$(NC)" @echo "$(RED)║ Удаление Let's Encrypt SSL Manager ║$(NC)"
@echo "$(RED)╚════════════════════════════════════════════════════════════════╝$(NC)" @echo "$(RED)╚════════════════════════════════════════════════════════════════╝$(NC)"
@echo "" @echo ""
@read -p "Вы уверены? Это удалит все файлы и настройки [y/N]: " -n 1 -r; \ @printf "Вы уверены? Это удалит все файлы и настройки [y/N]: "; \
echo ""; \ read REPLY; \
if [[ $$REPLY =~ ^[Yy]$$ ]]; then \ case "$$REPLY" in \
$(MAKE) remove-service; \ [Yy]* ) \
$(MAKE) remove-cron; \ $(MAKE) remove-service; \
$(MAKE) remove-files; \ $(MAKE) remove-cron; \
echo ""; \ $(MAKE) remove-files; \
echo "$(GREEN)✓ Удаление завершено$(NC)"; \ echo ""; \
else \ echo "$(GREEN)✓ Удаление завершено$(NC)"; \
echo "$(YELLOW)Удаление отменено$(NC)"; \ ;; \
fi * ) \
echo "$(YELLOW)Удаление отменено$(NC)"; \
;; \
esac
# Удаление systemd service # Удаление systemd service
remove-service: remove-service:
@@ -229,23 +254,29 @@ remove-files:
@rm -rf $(INSTALL_DIR) @rm -rf $(INSTALL_DIR)
@echo "$(GREEN)✓ Директория $(INSTALL_DIR) удалена$(NC)" @echo "$(GREEN)✓ Директория $(INSTALL_DIR) удалена$(NC)"
@echo "" @echo ""
@read -p "Удалить конфигурацию $(CONFIG_FILE)? [y/N]: " -n 1 -r; \ @printf "Удалить конфигурацию $(CONFIG_FILE)? [y/N]: "; \
echo ""; \ read REPLY; \
if [[ $$REPLY =~ ^[Yy]$$ ]]; then \ case "$$REPLY" in \
rm -f $(CONFIG_FILE); \ [Yy]* ) \
echo "$(GREEN)✓ Конфигурация удалена$(NC)"; \ rm -f $(CONFIG_FILE); \
else \ echo "$(GREEN)✓ Конфигурация удалена$(NC)"; \
echo "$(YELLOW)Конфигурация сохранена$(NC)"; \ ;; \
fi * ) \
echo "$(YELLOW)Конфигурация сохранена$(NC)"; \
;; \
esac
@echo "" @echo ""
@read -p "Удалить логи? [y/N]: " -n 1 -r; \ @printf "Удалить логи? [y/N]: "; \
echo ""; \ read REPLY; \
if [[ $$REPLY =~ ^[Yy]$$ ]]; then \ case "$$REPLY" in \
rm -f $(LOG_FILE) $(CRON_LOG); \ [Yy]* ) \
echo "$(GREEN)✓ Логи удалены$(NC)"; \ rm -f $(LOG_FILE) $(CRON_LOG); \
else \ echo "$(GREEN)✓ Логи удалены$(NC)"; \
echo "$(YELLOW)Логи сохранены$(NC)"; \ ;; \
fi * ) \
echo "$(YELLOW)Логи сохранены$(NC)"; \
;; \
esac
# ============================================================================== # ==============================================================================
# Утилиты # Утилиты
@@ -301,12 +332,24 @@ check-config:
@echo "" @echo ""
@if [ ! -f "$(CONFIG_FILE)" ]; then \ @if [ ! -f "$(CONFIG_FILE)" ]; then \
echo "$(RED)✗ Конфигурация не найдена: $(CONFIG_FILE)$(NC)"; \ echo "$(RED)✗ Конфигурация не найдена: $(CONFIG_FILE)$(NC)"; \
echo "$(YELLOW)Совет: Скопируйте config.json.example в $(CONFIG_FILE)$(NC)"; \
echo " $(CYAN)sudo cp config.json.example $(CONFIG_FILE)$(NC)"; \
echo " $(CYAN)sudo chmod 644 $(CONFIG_FILE)$(NC)"; \
exit 1; \ exit 1; \
fi fi
@echo "$(GREEN)✓ Конфигурация найдена$(NC)" @echo "$(GREEN)✓ Конфигурация найдена$(NC)"
@if [ ! -r "$(CONFIG_FILE)" ]; then \
echo "$(RED)✗ Нет прав для чтения: $(CONFIG_FILE)$(NC)"; \
echo "$(YELLOW)Решение: Запустите команду с sudo:$(NC)"; \
echo " $(CYAN)sudo make check-config$(NC)"; \
exit 1; \
fi
@echo "" @echo ""
@$(PYTHON) -c "import json; print(json.dumps(json.load(open('$(CONFIG_FILE)')), indent=2, ensure_ascii=False))" 2>/dev/null || \ @$(PYTHON) -c "import json; print(json.dumps(json.load(open('$(CONFIG_FILE)')), indent=2, ensure_ascii=False))" 2>&1 || \
(echo "$(RED)✗ Ошибка: Неверный формат JSON$(NC)"; exit 1) (echo "$(RED)✗ Ошибка: Неверный формат JSON$(NC)"; \
echo "$(YELLOW)Подробности:$(NC)"; \
$(PYTHON) -c "import json; json.load(open('$(CONFIG_FILE)'))" 2>&1 | head -5; \
exit 1)
@echo "" @echo ""
@echo "$(YELLOW)→ Проверка обязательных параметров:$(NC)" @echo "$(YELLOW)→ Проверка обязательных параметров:$(NC)"
@$(PYTHON) -c "import json; c=json.load(open('$(CONFIG_FILE)')); assert c.get('regru_username'), 'regru_username не задан'" && echo " $(GREEN)✓ regru_username$(NC)" || echo " $(RED)✗ regru_username$(NC)" @$(PYTHON) -c "import json; c=json.load(open('$(CONFIG_FILE)')); assert c.get('regru_username'), 'regru_username не задан'" && echo " $(GREEN)✓ regru_username$(NC)" || echo " $(RED)✗ regru_username$(NC)"

View File

@@ -28,7 +28,7 @@ NC='\033[0m' # No Color
# Функция логирования # Функция логирования
# ============================================================================== # ==============================================================================
log() { log() {
echo -e "${2:-$NC}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}" | tee -a "$LOG_FILE" echo -e "${2:-$NC}[$(date +'%d.%m.%Y %H:%M:%S')] $1${NC}" | tee -a "$LOG_FILE"
} }
# ============================================================================== # ==============================================================================

View File

@@ -115,7 +115,7 @@ def setup_logging(log_file: str, verbose: bool = False) -> logging.Logger:
# Настройка форматирования # Настройка форматирования
formatter = logging.Formatter( formatter = logging.Formatter(
'%(asctime)s - %(levelname)s - %(message)s', '%(asctime)s - %(levelname)s - %(message)s',
datefmt='%Y-%m-%d %H:%M:%S' datefmt='%d.%m.%Y %H:%M:%S'
) )
# Создаем logger # Создаем logger
@@ -406,6 +406,10 @@ class NginxProxyManagerAPI:
""" """
Загрузка нового сертификата в NPM Загрузка нового сертификата в NPM
ВАЖНО: NPM автоматически извлекает информацию из сертификата.
Мы загружаем сертификат через веб-интерфейс формы (multipart/form-data),
а не через JSON API, так как JSON endpoint имеет строгую валидацию схемы.
Args: Args:
domain: Основной домен domain: Основной домен
cert_path: Путь к файлу сертификата cert_path: Путь к файлу сертификата
@@ -425,29 +429,36 @@ class NginxProxyManagerAPI:
with open(key_path, 'r') as f: with open(key_path, 'r') as f:
certificate_key = f.read() certificate_key = f.read()
# Если есть цепочка, объединяем с сертификатом # Используем промежуточный сертификат если доступен
intermediate_certificate = ""
if chain_path and os.path.exists(chain_path): if chain_path and os.path.exists(chain_path):
with open(chain_path, 'r') as f: with open(chain_path, 'r') as f:
chain = f.read() intermediate_certificate = f.read()
# NPM ожидает fullchain (cert + chain)
certificate = certificate + "\n" + chain
# Формируем payload для API # NPM Web UI использует multipart/form-data для загрузки custom сертификатов
payload = { # Эмулируем загрузку через веб-форму
"provider": "other", files = {
"nice_name": f"{domain} - Let's Encrypt", 'certificate': ('cert.pem', certificate, 'application/x-pem-file'),
"domain_names": [domain], 'certificate_key': ('privkey.pem', certificate_key, 'application/x-pem-file'),
"certificate": certificate,
"certificate_key": certificate_key,
"meta": {
"letsencrypt_agree": True,
"dns_challenge": True,
"dns_provider": "reg_ru"
}
} }
# Добавляем промежуточный сертификат если есть
if intermediate_certificate:
files['intermediate_certificate'] = ('chain.pem', intermediate_certificate, 'application/x-pem-file')
# Дополнительные поля формы
data = {
'nice_name': domain,
'provider': 'other', # Обязательное поле: 'letsencrypt' или 'other'
}
self.logger.debug(f"Uploading certificate as multipart/form-data")
self.logger.debug(f"Files: {list(files.keys())}")
self.logger.debug(f"Data: {data}")
self.logger.info(f"Загрузка сертификата для {domain} в NPM...") self.logger.info(f"Загрузка сертификата для {domain} в NPM...")
response = self.session.post(url, json=payload, timeout=30)
# Отправляем как multipart/form-data
response = self.session.post(url, files=files, data=data, timeout=30)
response.raise_for_status() response.raise_for_status()
result = response.json() result = response.json()
@@ -493,20 +504,29 @@ class NginxProxyManagerAPI:
with open(key_path, 'r') as f: with open(key_path, 'r') as f:
certificate_key = f.read() certificate_key = f.read()
# Если есть цепочка, объединяем с сертификатом # Используем промежуточный сертификат если доступен
intermediate_certificate = ""
if chain_path and os.path.exists(chain_path): if chain_path and os.path.exists(chain_path):
with open(chain_path, 'r') as f: with open(chain_path, 'r') as f:
chain = f.read() intermediate_certificate = f.read()
certificate = certificate + "\n" + chain
# Формируем payload для обновления # NPM Web UI использует multipart/form-data для обновления
payload = { files = {
"certificate": certificate, 'certificate': ('cert.pem', certificate, 'application/x-pem-file'),
"certificate_key": certificate_key 'certificate_key': ('privkey.pem', certificate_key, 'application/x-pem-file'),
}
# Добавляем промежуточный сертификат если есть
if intermediate_certificate:
files['intermediate_certificate'] = ('chain.pem', intermediate_certificate, 'application/x-pem-file')
# Дополнительные поля формы
data = {
'provider': 'other', # Обязательное поле
} }
self.logger.info(f"Обновление сертификата ID {cert_id} в NPM...") self.logger.info(f"Обновление сертификата ID {cert_id} в NPM...")
response = self.session.put(url, json=payload, timeout=30) response = self.session.put(url, files=files, data=data, timeout=30)
response.raise_for_status() response.raise_for_status()
self.logger.info("Сертификат успешно обновлен в NPM") self.logger.info("Сертификат успешно обновлен в NPM")
@@ -813,7 +833,7 @@ class LetsEncryptManager:
expiry_date = cert.not_valid_after expiry_date = cert.not_valid_after
days_left = (expiry_date - datetime.now()).days days_left = (expiry_date - datetime.now()).days
self.logger.info(f"Сертификат истекает: {expiry_date.strftime('%Y-%m-%d')}") self.logger.info(f"Сертификат истекает: {expiry_date.strftime('%d.%m.%Y %H:%M:%S')}")
self.logger.info(f"Осталось дней: {days_left}") self.logger.info(f"Осталось дней: {days_left}")
return days_left return days_left