Initial commit: ROA2WEB - FastAPI + Vue.js + Telegram Bot
Modern ERP Reports Application with microservices architecture Tech Stack: - Backend: FastAPI + python-oracledb (Oracle DB integration) - Frontend: Vue.js 3 + PrimeVue + Vite - Telegram Bot: python-telegram-bot + SQLite - Infrastructure: Shared database pool, JWT authentication, SSH tunnel Features: - FastAPI backend with async Oracle connection pool - Vue.js 3 responsive frontend with PrimeVue components - Telegram bot alternative interface - Microservices architecture with shared components - Complete deployment support (Linux Docker + Windows IIS) - Comprehensive testing (Playwright E2E + pytest) Repository Structure: - reports-app/ - Main application (backend, frontend, telegram-bot) - shared/ - Shared components (database pool, auth, utils) - deployment/ - Deployment scripts (Linux & Windows) - docs/ - Project documentation - security/ - Security scanning and git hooks
This commit is contained in:
361
deployment/windows/scripts/Backup-TelegramDB.ps1
Normal file
361
deployment/windows/scripts/Backup-TelegramDB.ps1
Normal file
@@ -0,0 +1,361 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Backup ROA2WEB Telegram Bot SQLite Database
|
||||
|
||||
.DESCRIPTION
|
||||
This script creates a backup of the Telegram bot's SQLite database:
|
||||
- Copies database file with timestamp
|
||||
- Compresses backup (optional)
|
||||
- Cleans up old backups (keeps last 30 days)
|
||||
- Logs backup operations
|
||||
- Can run manually or via scheduled task
|
||||
|
||||
.PARAMETER InstallPath
|
||||
Installation path (default: C:\inetpub\wwwroot\roa2web\telegram-bot)
|
||||
|
||||
.PARAMETER RetentionDays
|
||||
Number of days to keep backups (default: 30)
|
||||
|
||||
.PARAMETER Compress
|
||||
Compress backup file (default: true)
|
||||
|
||||
.PARAMETER LogToFile
|
||||
Write backup log to file (default: true)
|
||||
|
||||
.EXAMPLE
|
||||
.\Backup-TelegramDB.ps1
|
||||
Standard backup with defaults
|
||||
|
||||
.EXAMPLE
|
||||
.\Backup-TelegramDB.ps1 -RetentionDays 60 -Compress $true
|
||||
Backup with 60-day retention and compression
|
||||
|
||||
.EXAMPLE
|
||||
.\Backup-TelegramDB.ps1 -LogToFile $false
|
||||
Backup without logging to file (console only)
|
||||
|
||||
.NOTES
|
||||
Author: ROA2WEB Team
|
||||
Requires: PowerShell 5.1+
|
||||
Can be run manually or via Task Scheduler
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$InstallPath = "C:\inetpub\wwwroot\roa2web\telegram-bot",
|
||||
[int]$RetentionDays = 30,
|
||||
[bool]$Compress = $true,
|
||||
[bool]$LogToFile = $true
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# =============================================================================
|
||||
# CONFIGURATION
|
||||
# =============================================================================
|
||||
|
||||
$script:Config = @{
|
||||
InstallPath = $InstallPath
|
||||
DataPath = Join-Path $InstallPath "data"
|
||||
BackupPath = Join-Path $InstallPath "backups"
|
||||
LogsPath = Join-Path $InstallPath "logs"
|
||||
DatabaseFile = "telegram_bot.db"
|
||||
RetentionDays = $RetentionDays
|
||||
Compress = $Compress
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# HELPER FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
function Write-Log {
|
||||
param(
|
||||
[string]$Message,
|
||||
[string]$Level = "INFO"
|
||||
)
|
||||
|
||||
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
||||
$logMessage = "[$timestamp] [$Level] $Message"
|
||||
|
||||
# Console output
|
||||
switch ($Level) {
|
||||
"ERROR" { Write-Host $logMessage -ForegroundColor Red }
|
||||
"WARN" { Write-Host $logMessage -ForegroundColor Yellow }
|
||||
"SUCCESS" { Write-Host $logMessage -ForegroundColor Green }
|
||||
default { Write-Host $logMessage -ForegroundColor Cyan }
|
||||
}
|
||||
|
||||
# File output
|
||||
if ($LogToFile) {
|
||||
$logFile = Join-Path $Config.LogsPath "backup.log"
|
||||
Add-Content -Path $logFile -Value $logMessage -Encoding UTF8
|
||||
}
|
||||
}
|
||||
|
||||
function Test-DatabaseFile {
|
||||
$dbPath = Join-Path $Config.DataPath $Config.DatabaseFile
|
||||
|
||||
if (-not (Test-Path $dbPath)) {
|
||||
Write-Log "Database file not found: $dbPath" -Level "ERROR"
|
||||
return $false
|
||||
}
|
||||
|
||||
# Check if file is accessible (not locked)
|
||||
try {
|
||||
$stream = [System.IO.File]::Open($dbPath, 'Open', 'Read', 'Read')
|
||||
$stream.Close()
|
||||
Write-Log "Database file is accessible: $dbPath" -Level "INFO"
|
||||
return $true
|
||||
} catch {
|
||||
Write-Log "Database file is locked or inaccessible: $_" -Level "ERROR"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
function Get-DatabaseSize {
|
||||
$dbPath = Join-Path $Config.DataPath $Config.DatabaseFile
|
||||
$size = (Get-Item $dbPath).Length / 1KB
|
||||
return [math]::Round($size, 2)
|
||||
}
|
||||
|
||||
function New-BackupDirectory {
|
||||
if (-not (Test-Path $Config.BackupPath)) {
|
||||
New-Item -ItemType Directory -Path $Config.BackupPath -Force | Out-Null
|
||||
Write-Log "Created backup directory: $($Config.BackupPath)" -Level "INFO"
|
||||
}
|
||||
}
|
||||
|
||||
function Backup-Database {
|
||||
Write-Log "Starting database backup..." -Level "INFO"
|
||||
|
||||
# Ensure backup directory exists
|
||||
New-BackupDirectory
|
||||
|
||||
# Generate backup filename with timestamp
|
||||
$timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
|
||||
$backupFileName = "telegram_bot_backup_$timestamp.db"
|
||||
$backupFilePath = Join-Path $Config.BackupPath $backupFileName
|
||||
|
||||
# Source database path
|
||||
$dbPath = Join-Path $Config.DataPath $Config.DatabaseFile
|
||||
|
||||
try {
|
||||
# Copy database file
|
||||
Copy-Item -Path $dbPath -Destination $backupFilePath -Force
|
||||
Write-Log "Database backed up to: $backupFileName" -Level "SUCCESS"
|
||||
|
||||
# Get backup file size
|
||||
$backupSize = (Get-Item $backupFilePath).Length / 1KB
|
||||
Write-Log "Backup size: $([math]::Round($backupSize, 2)) KB" -Level "INFO"
|
||||
|
||||
# Compress if enabled
|
||||
if ($Config.Compress) {
|
||||
$compressedPath = Compress-Backup -BackupPath $backupFilePath
|
||||
if ($compressedPath) {
|
||||
# Remove uncompressed backup
|
||||
Remove-Item -Path $backupFilePath -Force
|
||||
Write-Log "Uncompressed backup removed" -Level "INFO"
|
||||
return $compressedPath
|
||||
}
|
||||
}
|
||||
|
||||
return $backupFilePath
|
||||
} catch {
|
||||
Write-Log "Backup failed: $_" -Level "ERROR"
|
||||
return $null
|
||||
}
|
||||
}
|
||||
|
||||
function Compress-Backup {
|
||||
param([string]$BackupPath)
|
||||
|
||||
Write-Log "Compressing backup..." -Level "INFO"
|
||||
|
||||
$zipPath = "$BackupPath.zip"
|
||||
|
||||
try {
|
||||
# Create ZIP archive
|
||||
Compress-Archive -Path $BackupPath -DestinationPath $zipPath -CompressionLevel Optimal -Force
|
||||
|
||||
# Get compressed size
|
||||
$originalSize = (Get-Item $BackupPath).Length / 1KB
|
||||
$compressedSize = (Get-Item $zipPath).Length / 1KB
|
||||
$ratio = [math]::Round((1 - ($compressedSize / $originalSize)) * 100, 1)
|
||||
|
||||
Write-Log "Backup compressed: $([math]::Round($compressedSize, 2)) KB (saved $ratio%)" -Level "SUCCESS"
|
||||
|
||||
return $zipPath
|
||||
} catch {
|
||||
Write-Log "Compression failed: $_" -Level "WARN"
|
||||
return $null
|
||||
}
|
||||
}
|
||||
|
||||
function Remove-OldBackups {
|
||||
Write-Log "Cleaning up old backups (keeping last $($Config.RetentionDays) days)..." -Level "INFO"
|
||||
|
||||
$cutoffDate = (Get-Date).AddDays(-$Config.RetentionDays)
|
||||
|
||||
try {
|
||||
# Get all backup files (both .db and .zip)
|
||||
$allBackups = Get-ChildItem -Path $Config.BackupPath -File |
|
||||
Where-Object {
|
||||
$_.Name -like "telegram_bot_backup_*.db" -or
|
||||
$_.Name -like "telegram_bot_backup_*.db.zip"
|
||||
}
|
||||
|
||||
$removedCount = 0
|
||||
$freedSpace = 0
|
||||
|
||||
foreach ($backup in $allBackups) {
|
||||
if ($backup.LastWriteTime -lt $cutoffDate) {
|
||||
$size = $backup.Length / 1MB
|
||||
Remove-Item -Path $backup.FullName -Force
|
||||
$removedCount++
|
||||
$freedSpace += $size
|
||||
Write-Log "Removed old backup: $($backup.Name)" -Level "INFO"
|
||||
}
|
||||
}
|
||||
|
||||
if ($removedCount -gt 0) {
|
||||
Write-Log "Removed $removedCount old backup(s), freed $([math]::Round($freedSpace, 2)) MB" -Level "SUCCESS"
|
||||
} else {
|
||||
Write-Log "No old backups to remove" -Level "INFO"
|
||||
}
|
||||
} catch {
|
||||
Write-Log "Failed to clean up old backups: $_" -Level "WARN"
|
||||
}
|
||||
}
|
||||
|
||||
function Get-BackupStatistics {
|
||||
Write-Log "Backup Statistics:" -Level "INFO"
|
||||
|
||||
# Count backups
|
||||
$allBackups = Get-ChildItem -Path $Config.BackupPath -File |
|
||||
Where-Object {
|
||||
$_.Name -like "telegram_bot_backup_*.db" -or
|
||||
$_.Name -like "telegram_bot_backup_*.db.zip"
|
||||
}
|
||||
|
||||
$totalBackups = $allBackups.Count
|
||||
$totalSize = ($allBackups | Measure-Object -Property Length -Sum).Sum / 1MB
|
||||
|
||||
Write-Log " Total backups: $totalBackups" -Level "INFO"
|
||||
Write-Log " Total size: $([math]::Round($totalSize, 2)) MB" -Level "INFO"
|
||||
|
||||
# Latest backup
|
||||
if ($totalBackups -gt 0) {
|
||||
$latest = $allBackups | Sort-Object LastWriteTime -Descending | Select-Object -First 1
|
||||
Write-Log " Latest backup: $($latest.Name) ($([math]::Round($latest.Length / 1KB, 2)) KB)" -Level "INFO"
|
||||
Write-Log " Created: $($latest.LastWriteTime)" -Level "INFO"
|
||||
}
|
||||
|
||||
# Oldest backup
|
||||
if ($totalBackups -gt 1) {
|
||||
$oldest = $allBackups | Sort-Object LastWriteTime | Select-Object -First 1
|
||||
$age = (Get-Date) - $oldest.LastWriteTime
|
||||
Write-Log " Oldest backup: $($oldest.Name) (Age: $([math]::Round($age.TotalDays, 1)) days)" -Level "INFO"
|
||||
}
|
||||
}
|
||||
|
||||
function Test-BackupIntegrity {
|
||||
param([string]$BackupPath)
|
||||
|
||||
Write-Log "Testing backup integrity..." -Level "INFO"
|
||||
|
||||
try {
|
||||
# For ZIP files, test archive
|
||||
if ($BackupPath -like "*.zip") {
|
||||
# Try to extract to temp location
|
||||
$tempExtract = Join-Path $env:TEMP "telegram_bot_backup_test"
|
||||
if (Test-Path $tempExtract) {
|
||||
Remove-Item -Path $tempExtract -Recurse -Force
|
||||
}
|
||||
|
||||
Expand-Archive -Path $BackupPath -DestinationPath $tempExtract -Force
|
||||
|
||||
# Check if database file exists in extracted folder
|
||||
$extractedDb = Get-ChildItem -Path $tempExtract -Filter "*.db" -Recurse
|
||||
if ($extractedDb) {
|
||||
Write-Log "Backup integrity test PASSED (ZIP archive valid)" -Level "SUCCESS"
|
||||
Remove-Item -Path $tempExtract -Recurse -Force
|
||||
return $true
|
||||
} else {
|
||||
Write-Log "Backup integrity test FAILED (No database file in archive)" -Level "ERROR"
|
||||
Remove-Item -Path $tempExtract -Recurse -Force
|
||||
return $false
|
||||
}
|
||||
} else {
|
||||
# For .db files, try to open
|
||||
$stream = [System.IO.File]::Open($BackupPath, 'Open', 'Read', 'Read')
|
||||
$stream.Close()
|
||||
Write-Log "Backup integrity test PASSED (Database file readable)" -Level "SUCCESS"
|
||||
return $true
|
||||
}
|
||||
} catch {
|
||||
Write-Log "Backup integrity test FAILED: $_" -Level "ERROR"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# MAIN BACKUP FLOW
|
||||
# =============================================================================
|
||||
|
||||
function Main {
|
||||
Write-Host @"
|
||||
|
||||
====================================================================
|
||||
ROA2WEB Telegram Bot - Database Backup
|
||||
SQLite database backup and retention management
|
||||
====================================================================
|
||||
|
||||
"@ -ForegroundColor Cyan
|
||||
|
||||
Write-Log "Backup started by: $env:USERNAME" -Level "INFO"
|
||||
Write-Log "Backup script: $($MyInvocation.MyCommand.Path)" -Level "INFO"
|
||||
|
||||
# Check if database exists
|
||||
if (-not (Test-DatabaseFile)) {
|
||||
Write-Log "Backup aborted: Database file not accessible" -Level "ERROR"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Get current database size
|
||||
$dbSize = Get-DatabaseSize
|
||||
Write-Log "Current database size: $dbSize KB" -Level "INFO"
|
||||
|
||||
try {
|
||||
# Perform backup
|
||||
$backupPath = Backup-Database
|
||||
|
||||
if ($backupPath) {
|
||||
# Test backup integrity
|
||||
$integrityOk = Test-BackupIntegrity -BackupPath $backupPath
|
||||
|
||||
if ($integrityOk) {
|
||||
# Cleanup old backups
|
||||
Remove-OldBackups
|
||||
|
||||
# Show statistics
|
||||
Get-BackupStatistics
|
||||
|
||||
Write-Log "Backup completed successfully" -Level "SUCCESS"
|
||||
exit 0
|
||||
} else {
|
||||
Write-Log "Backup created but failed integrity test" -Level "ERROR"
|
||||
exit 1
|
||||
}
|
||||
} else {
|
||||
Write-Log "Backup failed" -Level "ERROR"
|
||||
exit 1
|
||||
}
|
||||
} catch {
|
||||
Write-Log "Backup process failed: $_" -Level "ERROR"
|
||||
Write-Log $_.ScriptStackTrace -Level "ERROR"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Run main backup
|
||||
Main
|
||||
Reference in New Issue
Block a user