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
362 lines
11 KiB
PowerShell
362 lines
11 KiB
PowerShell
<#
|
|
.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
|