Add cron job functionality and Docker integration
This commit is contained in:
69
CLAUDE.md
69
CLAUDE.md
@@ -327,40 +327,57 @@ go run ./cmd/server
|
|||||||
# Run importer (one-time setup)
|
# Run importer (one-time setup)
|
||||||
go run ./cmd/importer
|
go run ./cmd/importer
|
||||||
|
|
||||||
|
# Run cron jobs manually
|
||||||
|
go run ./cmd/cron -job=alerts # Check and generate alerts
|
||||||
|
go run ./cmd/cron -job=update-prices # Recalculate all prices
|
||||||
|
go run ./cmd/cron -job=reset-counters # Reset usage counters
|
||||||
|
go run ./cmd/cron -job=update-popularity # Update popularity scores
|
||||||
|
|
||||||
# Build for production
|
# Build for production
|
||||||
CGO_ENABLED=0 go build -ldflags="-s -w" -o bin/quoteforge ./cmd/server
|
CGO_ENABLED=0 go build -ldflags="-s -w" -o bin/quoteforge ./cmd/server
|
||||||
|
CGO_ENABLED=0 go build -ldflags="-s -w" -o bin/quoteforge-cron ./cmd/cron
|
||||||
|
|
||||||
# Run tests
|
# Run tests
|
||||||
go test ./...
|
go test ./...
|
||||||
|
````
|
||||||
|
|
||||||
|
## Cron Jobs
|
||||||
|
|
||||||
|
QuoteForge now includes automated cron jobs for maintenance tasks. These can be run using the built-in cron functionality in the Docker container.
|
||||||
|
|
||||||
|
### Docker Compose Setup
|
||||||
|
|
||||||
|
The Docker setup includes a dedicated cron service that runs the following jobs:
|
||||||
|
|
||||||
|
- **Alerts check**: Every hour (0 * * * *)
|
||||||
|
- **Price updates**: Daily at 2 AM (0 2 * * *)
|
||||||
|
- **Usage counter reset**: Weekly on Sunday at 1 AM (0 1 * * 0)
|
||||||
|
- **Popularity score updates**: Daily at 3 AM (0 3 * * *)
|
||||||
|
|
||||||
|
### Manual Cron Job Execution
|
||||||
|
|
||||||
|
You can also run cron jobs manually using the quoteforge-cron binary:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check and generate alerts
|
||||||
|
go run ./cmd/cron -job=alerts
|
||||||
|
|
||||||
|
# Recalculate all prices
|
||||||
|
go run ./cmd/cron -job=update-prices
|
||||||
|
|
||||||
|
# Reset usage counters
|
||||||
|
go run ./cmd/cron -job=reset-counters
|
||||||
|
|
||||||
|
# Update popularity scores
|
||||||
|
go run ./cmd/cron -job=update-popularity
|
||||||
```
|
```
|
||||||
|
|
||||||
## Dependencies (go.mod)
|
### Cron Job Details
|
||||||
|
|
||||||
```go
|
- **Alerts check**: Generates alerts for components with high demand and stale prices, trending components without prices, and components with no recent quotes
|
||||||
module git.mchus.pro/mchus/quoteforge
|
- **Price updates**: Recalculates prices for all components using configured methods (median, weighted median, average)
|
||||||
|
- **Usage counter reset**: Resets weekly and monthly usage counters for components
|
||||||
go 1.22
|
- **Popularity score updates**: Recalculates popularity scores based on supplier quote activity
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/gin-gonic/gin v1.9.1
|
|
||||||
github.com/go-sql-driver/mysql v1.7.1
|
|
||||||
gorm.io/gorm v1.25.5
|
|
||||||
gorm.io/driver/mysql v1.5.2
|
|
||||||
github.com/xuri/excelize/v2 v2.8.0
|
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.0
|
|
||||||
github.com/google/uuid v1.5.0
|
|
||||||
golang.org/x/crypto v0.17.0
|
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development Priorities
|
|
||||||
|
|
||||||
1. **Phase 1 (MVP):** Project setup, models, component API, basic UI, CSV export
|
|
||||||
2. **Phase 2:** JWT auth with roles, pricing admin UI, all price methods
|
|
||||||
3. **Phase 3:** Save/load configs, JSON import/export, XLSX export, cron jobs
|
|
||||||
4. **Phase 4:** Usage stats, alerts system, dashboard
|
|
||||||
5. **Phase 5:** Polish, tests, Docker, documentation
|
|
||||||
|
|
||||||
## Code Style
|
## Code Style
|
||||||
|
|
||||||
|
|||||||
18
Dockerfile
18
Dockerfile
@@ -12,16 +12,22 @@ RUN go mod download
|
|||||||
# Copy source code
|
# Copy source code
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
# Build the binary
|
# Build the main binary
|
||||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
|
||||||
-ldflags="-s -w" \
|
-ldflags="-s -w" \
|
||||||
-o /app/quoteforge \
|
-o /app/quoteforge \
|
||||||
./cmd/server
|
./cmd/server
|
||||||
|
|
||||||
|
# Build the cron binary
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
|
||||||
|
-ldflags="-s -w" \
|
||||||
|
-o /app/quoteforge-cron \
|
||||||
|
./cmd/cron
|
||||||
|
|
||||||
# Final stage
|
# Final stage
|
||||||
FROM alpine:3.19
|
FROM alpine:3.19
|
||||||
|
|
||||||
RUN apk add --no-cache ca-certificates tzdata
|
RUN apk add --no-cache ca-certificates tzdata cron
|
||||||
|
|
||||||
# Create non-root user
|
# Create non-root user
|
||||||
RUN adduser -D -g '' appuser
|
RUN adduser -D -g '' appuser
|
||||||
@@ -30,6 +36,14 @@ WORKDIR /app
|
|||||||
|
|
||||||
# Copy binary from builder
|
# Copy binary from builder
|
||||||
COPY --from=builder /app/quoteforge .
|
COPY --from=builder /app/quoteforge .
|
||||||
|
COPY --from=builder /app/quoteforge-cron .
|
||||||
|
|
||||||
|
# Copy cron job configuration
|
||||||
|
COPY crontab /etc/crontabs/appuser
|
||||||
|
RUN chmod 0600 /etc/crontabs/appuser
|
||||||
|
|
||||||
|
# Create log directory
|
||||||
|
RUN mkdir -p /var/log/cron
|
||||||
|
|
||||||
# Copy web templates and static files
|
# Copy web templates and static files
|
||||||
COPY --from=builder /app/web ./web
|
COPY --from=builder /app/web ./web
|
||||||
|
|||||||
40
README.md
40
README.md
@@ -163,12 +163,48 @@ GET /api/configs # Сохранённые конфигурации
|
|||||||
|
|
||||||
## Cron Jobs
|
## Cron Jobs
|
||||||
|
|
||||||
Система автоматического обновления цен не реализована в текущей версии. Для обновления цен можно использовать команду:
|
QuoteForge now includes automated cron jobs for maintenance tasks. These can be run using the built-in cron functionality in the Docker container.
|
||||||
|
|
||||||
|
### Docker Compose Setup
|
||||||
|
|
||||||
|
The Docker setup includes a dedicated cron service that runs the following jobs:
|
||||||
|
|
||||||
|
- **Alerts check**: Every hour (0 * * * *)
|
||||||
|
- **Price updates**: Daily at 2 AM (0 2 * * *)
|
||||||
|
- **Usage counter reset**: Weekly on Sunday at 1 AM (0 1 * * 0)
|
||||||
|
- **Popularity score updates**: Daily at 3 AM (0 3 * * *)
|
||||||
|
|
||||||
|
To enable cron jobs in Docker, run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go run ./cmd/server -migrate
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Manual Cron Job Execution
|
||||||
|
|
||||||
|
You can also run cron jobs manually using the quoteforge-cron binary:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check and generate alerts
|
||||||
|
go run ./cmd/cron -job=alerts
|
||||||
|
|
||||||
|
# Recalculate all prices
|
||||||
|
go run ./cmd/cron -job=update-prices
|
||||||
|
|
||||||
|
# Reset usage counters
|
||||||
|
go run ./cmd/cron -job=reset-counters
|
||||||
|
|
||||||
|
# Update popularity scores
|
||||||
|
go run ./cmd/cron -job=update-popularity
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cron Job Details
|
||||||
|
|
||||||
|
- **Alerts check**: Generates alerts for components with high demand and stale prices, trending components without prices, and components with no recent quotes
|
||||||
|
- **Price updates**: Recalculates prices for all components using configured methods (median, weighted median, average)
|
||||||
|
- **Usage counter reset**: Resets weekly and monthly usage counters for components
|
||||||
|
- **Popularity score updates**: Recalculates popularity scores based on supplier quote activity
|
||||||
|
|
||||||
## Разработка
|
## Разработка
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
85
cmd/cron/main.go
Normal file
85
cmd/cron/main.go
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"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")
|
||||||
|
}
|
||||||
|
}
|
||||||
15
crontab
Normal file
15
crontab
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Cron jobs for QuoteForge
|
||||||
|
# Run alerts check every hour
|
||||||
|
0 * * * * /app/quoteforge-cron -job=alerts
|
||||||
|
|
||||||
|
# Run price updates daily at 2 AM
|
||||||
|
0 2 * * * /app/quoteforge-cron -job=update-prices
|
||||||
|
|
||||||
|
# Reset weekly counters every Sunday at 1 AM
|
||||||
|
0 1 * * 0 /app/quoteforge-cron -job=reset-counters
|
||||||
|
|
||||||
|
# Update popularity scores daily at 3 AM
|
||||||
|
0 3 * * * /app/quoteforge-cron -job=update-popularity
|
||||||
|
|
||||||
|
# Log rotation (optional)
|
||||||
|
# 0 0 * * * /usr/bin/logrotate /etc/logrotate.conf
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
quoteforge:
|
quoteforge:
|
||||||
build: .
|
build: .
|
||||||
@@ -15,3 +17,14 @@ services:
|
|||||||
timeout: 3s
|
timeout: 3s
|
||||||
retries: 3
|
retries: 3
|
||||||
start_period: 5s
|
start_period: 5s
|
||||||
|
|
||||||
|
quoteforge-cron:
|
||||||
|
build: .
|
||||||
|
container_name: quoteforge-cron
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- ./config.yaml:/app/config.yaml:ro
|
||||||
|
- ./logs:/app/logs
|
||||||
|
command: /usr/sbin/crond -f -l 8
|
||||||
|
environment:
|
||||||
|
- TZ=Europe/Moscow
|
||||||
|
|||||||
Reference in New Issue
Block a user