Implements complete offline-first architecture with SQLite caching and MariaDB synchronization. Key features: - Local SQLite database for offline operation (data/quoteforge.db) - Connection settings with encrypted credentials - Component and pricelist caching with auto-sync - Sync API endpoints (/api/sync/status, /components, /pricelists, /all) - Real-time sync status indicator in UI with auto-refresh - Offline mode detection middleware - Migration tool for database initialization - Setup wizard for initial configuration New components: - internal/localdb: SQLite repository layer (components, pricelists, sync) - internal/services/sync: Synchronization service - internal/handlers/sync: Sync API handlers - internal/handlers/setup: Setup wizard handlers - internal/middleware/offline: Offline detection - cmd/migrate: Database migration tool UI improvements: - Setup page for database configuration - Sync status indicator with online/offline detection - Warning icons for pending synchronization - Auto-refresh every 30 seconds Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
84 lines
2.8 KiB
Go
84 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"log"
|
|
|
|
"git.mchus.pro/mchus/quoteforge/internal/config"
|
|
"git.mchus.pro/mchus/quoteforge/internal/models"
|
|
"git.mchus.pro/mchus/quoteforge/internal/repository"
|
|
"git.mchus.pro/mchus/quoteforge/internal/services/alerts"
|
|
"git.mchus.pro/mchus/quoteforge/internal/services/pricing"
|
|
"gorm.io/driver/mysql"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
func main() {
|
|
configPath := flag.String("config", "config.yaml", "path to config file")
|
|
cronJob := flag.String("job", "", "type of cron job to run (alerts, update-prices)")
|
|
flag.Parse()
|
|
|
|
cfg, err := config.Load(*configPath)
|
|
if err != nil {
|
|
log.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
|
|
db, err := gorm.Open(mysql.Open(cfg.Database.DSN()), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Silent),
|
|
})
|
|
if err != nil {
|
|
log.Fatalf("Failed to connect to database: %v", err)
|
|
}
|
|
|
|
// Ensure tables exist
|
|
if err := models.Migrate(db); err != nil {
|
|
log.Fatalf("Migration failed: %v", err)
|
|
}
|
|
|
|
// Initialize repositories
|
|
statsRepo := repository.NewStatsRepository(db)
|
|
alertRepo := repository.NewAlertRepository(db)
|
|
componentRepo := repository.NewComponentRepository(db)
|
|
priceRepo := repository.NewPriceRepository(db)
|
|
|
|
// Initialize services
|
|
alertService := alerts.NewService(alertRepo, componentRepo, priceRepo, statsRepo, cfg.Alerts, cfg.Pricing)
|
|
pricingService := pricing.NewService(componentRepo, priceRepo, cfg.Pricing)
|
|
|
|
switch *cronJob {
|
|
case "alerts":
|
|
log.Println("Running alerts check...")
|
|
if err := alertService.CheckAndGenerateAlerts(); err != nil {
|
|
log.Printf("Error running alerts check: %v", err)
|
|
} else {
|
|
log.Println("Alerts check completed successfully")
|
|
}
|
|
case "update-prices":
|
|
log.Println("Recalculating all prices...")
|
|
updated, errors := pricingService.RecalculateAllPrices()
|
|
log.Printf("Prices recalculated: %d updated, %d errors", updated, errors)
|
|
case "reset-counters":
|
|
log.Println("Resetting usage counters...")
|
|
if err := statsRepo.ResetWeeklyCounters(); err != nil {
|
|
log.Printf("Error resetting weekly counters: %v", err)
|
|
}
|
|
if err := statsRepo.ResetMonthlyCounters(); err != nil {
|
|
log.Printf("Error resetting monthly counters: %v", err)
|
|
}
|
|
log.Println("Usage counters reset completed")
|
|
case "update-popularity":
|
|
log.Println("Updating popularity scores...")
|
|
if err := statsRepo.UpdatePopularityScores(); err != nil {
|
|
log.Printf("Error updating popularity scores: %v", err)
|
|
} else {
|
|
log.Println("Popularity scores updated successfully")
|
|
}
|
|
default:
|
|
log.Println("No valid cron job specified. Available jobs:")
|
|
log.Println(" - alerts: Check and generate alerts")
|
|
log.Println(" - update-prices: Recalculate all prices")
|
|
log.Println(" - reset-counters: Reset usage counters")
|
|
log.Println(" - update-popularity: Update popularity scores")
|
|
}
|
|
} |