Обновлено форматирование даты в логах на 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:
@echo "$(YELLOW)→ Установка зависимостей Python...$(NC)"
@if ! command -v pip3 >/dev/null 2>&1; then \
echo "$(RED)✗ pip3 не найден. Установите python3-pip$(NC)"; \
@if command -v pip3 >/dev/null 2>&1; then \
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; \
fi
@pip3 install -q requests cryptography 2>/dev/null || pip3 install requests cryptography
@echo "$(GREEN)✓ Зависимости установлены$(NC)"
# Копирование скрипта
@@ -193,17 +215,20 @@ uninstall: check-root
@echo "$(RED)║ Удаление Let's Encrypt SSL Manager ║$(NC)"
@echo "$(RED)╚════════════════════════════════════════════════════════════════╝$(NC)"
@echo ""
@read -p "Вы уверены? Это удалит все файлы и настройки [y/N]: " -n 1 -r; \
echo ""; \
if [[ $$REPLY =~ ^[Yy]$$ ]]; then \
$(MAKE) remove-service; \
$(MAKE) remove-cron; \
$(MAKE) remove-files; \
echo ""; \
echo "$(GREEN)✓ Удаление завершено$(NC)"; \
else \
echo "$(YELLOW)Удаление отменено$(NC)"; \
fi
@printf "Вы уверены? Это удалит все файлы и настройки [y/N]: "; \
read REPLY; \
case "$$REPLY" in \
[Yy]* ) \
$(MAKE) remove-service; \
$(MAKE) remove-cron; \
$(MAKE) remove-files; \
echo ""; \
echo "$(GREEN)✓ Удаление завершено$(NC)"; \
;; \
* ) \
echo "$(YELLOW)Удаление отменено$(NC)"; \
;; \
esac
# Удаление systemd service
remove-service:
@@ -229,23 +254,29 @@ remove-files:
@rm -rf $(INSTALL_DIR)
@echo "$(GREEN)✓ Директория $(INSTALL_DIR) удалена$(NC)"
@echo ""
@read -p "Удалить конфигурацию $(CONFIG_FILE)? [y/N]: " -n 1 -r; \
echo ""; \
if [[ $$REPLY =~ ^[Yy]$$ ]]; then \
rm -f $(CONFIG_FILE); \
echo "$(GREEN)✓ Конфигурация удалена$(NC)"; \
else \
echo "$(YELLOW)Конфигурация сохранена$(NC)"; \
fi
@printf "Удалить конфигурацию $(CONFIG_FILE)? [y/N]: "; \
read REPLY; \
case "$$REPLY" in \
[Yy]* ) \
rm -f $(CONFIG_FILE); \
echo "$(GREEN)✓ Конфигурация удалена$(NC)"; \
;; \
* ) \
echo "$(YELLOW)Конфигурация сохранена$(NC)"; \
;; \
esac
@echo ""
@read -p "Удалить логи? [y/N]: " -n 1 -r; \
echo ""; \
if [[ $$REPLY =~ ^[Yy]$$ ]]; then \
rm -f $(LOG_FILE) $(CRON_LOG); \
echo "$(GREEN)✓ Логи удалены$(NC)"; \
else \
echo "$(YELLOW)Логи сохранены$(NC)"; \
fi
@printf "Удалить логи? [y/N]: "; \
read REPLY; \
case "$$REPLY" in \
[Yy]* ) \
rm -f $(LOG_FILE) $(CRON_LOG); \
echo "$(GREEN)✓ Логи удалены$(NC)"; \
;; \
* ) \
echo "$(YELLOW)Логи сохранены$(NC)"; \
;; \
esac
# ==============================================================================
# Утилиты
@@ -301,12 +332,24 @@ check-config:
@echo ""
@if [ ! -f "$(CONFIG_FILE)" ]; then \
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; \
fi
@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 ""
@$(PYTHON) -c "import json; print(json.dumps(json.load(open('$(CONFIG_FILE)')), indent=2, ensure_ascii=False))" 2>/dev/null || \
(echo "$(RED)✗ Ошибка: Неверный формат JSON$(NC)"; exit 1)
@$(PYTHON) -c "import json; print(json.dumps(json.load(open('$(CONFIG_FILE)')), indent=2, ensure_ascii=False))" 2>&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 "$(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)"

View File

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