# Manage Backups V2.0 by Teccnia # https://teccnia.com.br #!/usr/bin/env python3 import os import shutil import time import logging from datetime import datetime, timedelta from pathlib import Path # ========================================== # CONFIGURAÇÃO # ========================================== # Diretório base onde estão as pastas dos usuários FTP DIRETORIO_BASE = "/home/ftpuser" # Nome das pastas para ignorar na varredura de usuários DIRETORIOS_IGNORAR = ['antigos', 'logs'] # Extensões para ignorar (não serão movidas nem deletadas) # Exemplo: ['.exe', '.iso'] EXTENSOES_IGNORAR = ['.exe', '.iso'] # Configurações de Retenção DIAS_MOVER = 15 # Arquivos com mais de X dias são movidos para 'antigos' DIAS_EXCLUIR_ANTIGOS = 60 # Pastas em 'antigos' com mais de Y dias são excluídas DIAS_RETENCAO_LOGS = 30 # Logs com mais de Z dias são excluídos # ======= NÃO ALTERAR A PARTIR DESTE PONTO!!! ======= # Nomes de diretórios de sistema NOME_DIR_ANTIGOS = "antigos" NOME_DIR_LOGS = "logs" # Caminhos completos (serão definidos dinamicamente se baseados no DIRETORIO_BASE) DIR_LOGS = os.path.join(DIRETORIO_BASE, NOME_DIR_LOGS) DIR_ANTIGOS = os.path.join(DIRETORIO_BASE, NOME_DIR_ANTIGOS) # Data atual para organização HOJE = datetime.now() DATA_HOJE_STR = HOJE.strftime('%Y-%m-%d') DATA_LOG_STR = HOJE.strftime('%d%m%Y') # ========================================== # CONFIGURAÇÃO DE LOGS # ========================================== def configurar_logger(): if not os.path.exists(DIR_LOGS): try: os.makedirs(DIR_LOGS) except Exception as e: print(f"ERRO CRÍTICO: Não foi possível criar diretório de logs {DIR_LOGS}: {e}") exit(1) arquivo_log = os.path.join(DIR_LOGS, f"{DATA_LOG_STR}.log") logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler(arquivo_log), logging.StreamHandler() ] ) return logging.getLogger() # ========================================== # FUNÇÕES # ========================================== def idade_arquivo_dias(caminho_arquivo): """Retorna a idade do arquivo em dias.""" timestamp_arquivo = os.path.getmtime(caminho_arquivo) data_arquivo = datetime.fromtimestamp(timestamp_arquivo) diferenca = HOJE - data_arquivo return diferenca.days def listar_subdiretorios_usuarios(logger): """Retorna lista de subdiretórios de usuários, ignorando pastas de sistema.""" subdiretorios = [] if not os.path.exists(DIRETORIO_BASE): logger.error(f"Diretório base não existe: {DIRETORIO_BASE}") return [] try: itens = os.listdir(DIRETORIO_BASE) for item in itens: caminho_completo = os.path.join(DIRETORIO_BASE, item) if os.path.isdir(caminho_completo): if item not in DIRETORIOS_IGNORAR: subdiretorios.append(item) except Exception as e: logger.error(f"Erro ao listar diretório base: {e}") return subdiretorios def processar_arquivos_antigos(usuario, logger): """Move arquivos antigos do diretório do usuário para a pasta de arquivamento.""" origem = os.path.join(DIRETORIO_BASE, usuario) destino_base = os.path.join(DIR_ANTIGOS, usuario) if not os.path.exists(origem): logger.warning(f"Diretório de origem não encontrado: {origem}") return arquivos_movidos = 0 try: destino_criado = False for arquivo in os.listdir(origem): caminho_arquivo = os.path.join(origem, arquivo) if not os.path.isfile(caminho_arquivo): continue _, ext = os.path.splitext(arquivo) if ext.lower() in [e.lower() for e in EXTENSOES_IGNORAR]: continue idade = idade_arquivo_dias(caminho_arquivo) if idade > DIAS_MOVER: if not destino_criado: os.makedirs(destino_base, exist_ok=True) destino_criado = True shutil.move(caminho_arquivo, os.path.join(destino_base, arquivo)) logger.info(f"Movido: {usuario}/{arquivo} ({idade} dias) -> {destino_base}") arquivos_movidos += 1 except Exception as e: logger.error(f"Erro ao processar usuário {usuario}: {e}") if arquivos_movidos > 0: logger.info(f"Usuário {usuario}: {arquivos_movidos} arquivos movidos.") def limpar_arquivos_antigos(logger): """Remove ARQUIVOS antigos em 'antigos' recursivamente.""" if not os.path.exists(DIR_ANTIGOS): return logger.info("Verificando limpeza de backups antigos (estrutura plana)...") arquivos_removidos = 0 try: for root, dirs, files in os.walk(DIR_ANTIGOS): for file in files: caminho_arquivo = os.path.join(root, file) # Verificar extensão ignorada _, ext = os.path.splitext(file) if ext.lower() in [e.lower() for e in EXTENSOES_IGNORAR]: continue try: idade = idade_arquivo_dias(caminho_arquivo) if idade > DIAS_EXCLUIR_ANTIGOS: os.remove(caminho_arquivo) logger.info(f"Removido backup antigo: {caminho_arquivo} ({idade} dias)") arquivos_removidos += 1 except Exception as e: logger.error(f"Erro ao verificar arquivo {caminho_arquivo}: {e}") for root, dirs, files in os.walk(DIR_ANTIGOS, topdown=False): for name in dirs: caminho_dir = os.path.join(root, name) if not os.listdir(caminho_dir): # Se estiver vazio os.rmdir(caminho_dir) logger.debug(f"Removido diretório vazio: {caminho_dir}") except Exception as e: logger.error(f"Erro ao limpar backups antigos: {e}") if arquivos_removidos > 0: logger.info(f"Total de backups antigos removidos: {arquivos_removidos}") def rotacionar_logs(logger): """Remove logs antigos.""" if not os.path.exists(DIR_LOGS): return logger.info("Verificando rotação de logs...") try: for arquivo in os.listdir(DIR_LOGS): caminho_arquivo = os.path.join(DIR_LOGS, arquivo) if os.path.isfile(caminho_arquivo): idade = idade_arquivo_dias(caminho_arquivo) if idade > DIAS_RETENCAO_LOGS: os.remove(caminho_arquivo) logger.info(f"Log removido: {arquivo} ({idade} dias)") except Exception as e: logger.error(f"Erro ao rotacionar logs: {e}") # ========================================== # MAIN # ========================================== def main(): # Inicializa Logger logger = configurar_logger() logger.info("=== Iniciando execução do script de backup ===") logger.info(f"Diretório Base: {DIRETORIO_BASE}") # 1. Identificar usuários (subdiretórios) usuarios = listar_subdiretorios_usuarios(logger) logger.info(f"Usuários encontrados: {usuarios}") # 2. Processar cada usuário for usuario in usuarios: processar_arquivos_antigos(usuario, logger) # 3. Limpar pastas antigas de retenção limpar_arquivos_antigos(logger) # 4. Rotacionar logs rotacionar_logs(logger) logger.info("=== Execução finalizada com sucesso ===") if __name__ == "__main__": main()