Skip to content

Commit 8b93719

Browse files
authored
feat: add workflow automation system with category actions, orphan scanner, and hardlink detection (#818)
1 parent e10fba8 commit 8b93719

87 files changed

Lines changed: 17660 additions & 2281 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Makefile

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ INTERNAL_WEB_DIR = internal/web
1818
# Go build flags with Polar credentials
1919
LDFLAGS = -ldflags "-X github.com/autobrr/qui/internal/buildinfo.Version=$(VERSION) -X main.PolarOrgID=$(POLAR_ORG_ID)"
2020

21-
.PHONY: all build frontend backend dev dev-backend dev-frontend dev-expose clean test help themes-fetch themes-clean
21+
.PHONY: all build frontend backend dev dev-backend dev-frontend dev-expose clean test help themes-fetch themes-clean lint lint-full lint-json lint-fix fmt modern deps
2222

2323
# Default target
2424
all: build
@@ -100,7 +100,7 @@ clean: themes-clean
100100
# Run tests
101101
test:
102102
@echo "Running tests..."
103-
go test -race -v ./...
103+
go test -race -count=3 -v ./...
104104

105105
# Validate OpenAPI specification
106106
test-openapi:
@@ -113,12 +113,32 @@ fmt:
113113
go fmt ./...
114114
cd $(WEB_DIR) && pnpm format
115115

116-
# Lint code
116+
# Lint code (changed files only - fast feedback for AI iteration)
117117
lint:
118-
@echo "Linting code..."
119-
golangci-lint run
118+
@echo "Linting changed Go code..."
119+
golangci-lint run --new-from-merge-base=main --timeout=5m
120+
@echo "Linting frontend..."
120121
cd $(WEB_DIR) && pnpm lint
121122

123+
# Full lint (entire codebase - use before commits/PRs)
124+
lint-full:
125+
@echo "Linting entire Go codebase..."
126+
golangci-lint run --timeout=10m
127+
@echo "Linting frontend..."
128+
cd $(WEB_DIR) && pnpm lint
129+
130+
# Lint with JSON output (for AI agent consumption)
131+
lint-json:
132+
@echo "Generating lint report..."
133+
golangci-lint run --new-from-merge-base=main --output.json.path=./lint-report.json --timeout=5m || true
134+
@echo "Lint report saved to lint-report.json"
135+
136+
# Lint with auto-fix where possible
137+
lint-fix:
138+
@echo "Running linters with auto-fix..."
139+
golangci-lint run --fix --timeout=10m
140+
cd $(WEB_DIR) && pnpm lint --fix
141+
122142
# Modernize Go code (interface{} -> any, etc)
123143
modern:
124144
@echo "Modernizing Go code..."
@@ -133,20 +153,36 @@ deps:
133153
# Help
134154
help:
135155
@echo "Available targets:"
136-
@echo " make build - Build both frontend and backend"
137-
@echo " make frontend - Build frontend only"
138-
@echo " make backend - Build backend only"
139-
@echo " make themes-fetch - Fetch premium themes from private repository"
140-
@echo " make themes-clean - Clean premium themes"
141-
@echo " make dev - Run development servers"
142-
@echo " make dev-backend - Run backend with hot reload"
143-
@echo " make dev-frontend - Run frontend development server"
144-
@echo " make dev-expose - Run frontend dev server exposed on 0.0.0.0"
145-
@echo " make clean - Clean build artifacts"
146-
@echo " make test - Run all tests"
147-
@echo " make test-openapi - Validate OpenAPI specification"
148-
@echo " make fmt - Format code"
149-
@echo " make lint - Lint code"
150-
@echo " make modern - Modernize Go code (interface{} -> any, etc)"
151-
@echo " make deps - Install dependencies"
152-
@echo " make help - Show this help message"
156+
@echo ""
157+
@echo "Build:"
158+
@echo " make build - Build both frontend and backend"
159+
@echo " make frontend - Build frontend only"
160+
@echo " make backend - Build backend only"
161+
@echo " make build/docker - Build Docker image"
162+
@echo ""
163+
@echo "Development:"
164+
@echo " make dev - Run development servers (air + pnpm dev)"
165+
@echo " make dev-backend - Run backend with hot reload"
166+
@echo " make dev-frontend - Run frontend development server"
167+
@echo " make dev-expose - Run frontend dev server exposed on 0.0.0.0"
168+
@echo ""
169+
@echo "Testing:"
170+
@echo " make test - Run all tests with race detection"
171+
@echo " make test-openapi - Validate OpenAPI specification"
172+
@echo ""
173+
@echo "Linting:"
174+
@echo " make lint - Lint changed files only (fast, for iteration)"
175+
@echo " make lint-full - Lint entire codebase"
176+
@echo " make lint-json - Generate JSON lint report for AI agents"
177+
@echo " make lint-fix - Auto-fix linting issues where possible"
178+
@echo ""
179+
@echo "Formatting:"
180+
@echo " make fmt - Format Go and frontend code"
181+
@echo " make modern - Modernize Go code (interface{} -> any)"
182+
@echo ""
183+
@echo "Other:"
184+
@echo " make themes-fetch - Fetch premium themes from private repository"
185+
@echo " make themes-clean - Clean premium themes"
186+
@echo " make clean - Clean build artifacts"
187+
@echo " make deps - Install dependencies"
188+
@echo " make help - Show this help message"

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ A fast, modern web interface for qBittorrent. Supports managing multiple qBittor
4040
- **OIDC Single Sign-On**: Authenticate through your OpenID Connect provider
4141
- **External Programs**: Launch custom scripts from the torrent context menu ([guide](internal/api/handlers/EXTERNAL_PROGRAMS.md))
4242
- **Tracker Reannounce**: Automatically fix stalled torrents when qBittorrent doesn't retry fast enough ([info](internal/services/reannounce/REANNOUNCE.md))
43-
- **Tracker Rules**: Apply per-tracker speed limits, ratio caps, and seeding time limits automatically ([info](internal/services/trackerrules/TRACKER_RULES.md))
43+
- **Automations**: Rule-based torrent management with conditions, actions (delete, pause, tag, limit speeds), and cross-seed awareness ([info](internal/services/automations/AUTOMATIONS.md))
44+
- **Orphan Scan**: Find and remove files not associated with any torrent ([info](internal/services/orphanscan/ORPHANSCAN.md))
4445
- **Backups & Restore**: Scheduled snapshots with incremental, overwrite, and complete restore modes ([info](#backups--restore-modes))
4546
- **Cross-Seed**: Automatically find and add matching torrents across trackers with autobrr webhook integration ([info](#cross-seed))
4647
- **Reverse Proxy**: Transparent qBittorrent proxy for external apps like autobrr, Sonarr, and Radarr—no credential sharing needed ([info](#reverse-proxy-for-external-applications))

cmd/qui/main.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,14 @@ import (
3232
"github.com/autobrr/qui/internal/models"
3333
"github.com/autobrr/qui/internal/polar"
3434
"github.com/autobrr/qui/internal/qbittorrent"
35+
"github.com/autobrr/qui/internal/services/automations"
3536
"github.com/autobrr/qui/internal/services/crossseed"
3637
"github.com/autobrr/qui/internal/services/filesmanager"
3738
"github.com/autobrr/qui/internal/services/jackett"
3839
"github.com/autobrr/qui/internal/services/license"
40+
"github.com/autobrr/qui/internal/services/orphanscan"
3941
"github.com/autobrr/qui/internal/services/reannounce"
4042
"github.com/autobrr/qui/internal/services/trackericons"
41-
"github.com/autobrr/qui/internal/services/trackerrules"
4243
"github.com/autobrr/qui/internal/update"
4344
"github.com/autobrr/qui/pkg/sqlite3store"
4445
)
@@ -482,7 +483,7 @@ func (app *Application) runServer() {
482483
log.Warn().Err(err).Msg("Failed to preload reannounce settings cache")
483484
}
484485

485-
trackerRuleStore := models.NewTrackerRuleStore(db)
486+
automationStore := models.NewAutomationStore(db)
486487
trackerCustomizationStore := models.NewTrackerCustomizationStore(db)
487488
dashboardSettingsStore := models.NewDashboardSettingsStore(db)
488489

@@ -556,7 +557,11 @@ func (app *Application) runServer() {
556557
instanceCrossSeedCompletionStore := models.NewInstanceCrossSeedCompletionStore(db)
557558
crossSeedService := crossseed.NewService(instanceStore, syncManager, filesManagerService, crossSeedStore, jackettService, externalProgramStore, instanceCrossSeedCompletionStore)
558559
reannounceService := reannounce.NewService(reannounce.DefaultConfig(), instanceStore, instanceReannounceStore, reannounceSettingsCache, clientPool, syncManager)
559-
trackerRuleService := trackerrules.NewService(trackerrules.DefaultConfig(), instanceStore, trackerRuleStore, syncManager)
560+
automationActivityStore := models.NewAutomationActivityStore(db)
561+
automationService := automations.NewService(automations.DefaultConfig(), instanceStore, automationStore, automationActivityStore, trackerCustomizationStore, syncManager)
562+
563+
orphanScanStore := models.NewOrphanScanStore(db)
564+
orphanScanService := orphanscan.NewService(orphanscan.DefaultConfig(), instanceStore, orphanScanStore, syncManager)
560565

561566
syncManager.SetTorrentCompletionHandler(crossSeedService.HandleTorrentCompletion)
562567

@@ -570,9 +575,13 @@ func (app *Application) runServer() {
570575
defer reannounceCancel()
571576
reannounceService.Start(reannounceCtx)
572577

573-
trackerRulesCtx, trackerRulesCancel := context.WithCancel(context.Background())
574-
defer trackerRulesCancel()
575-
trackerRuleService.Start(trackerRulesCtx)
578+
automationsCtx, automationsCancel := context.WithCancel(context.Background())
579+
defer automationsCancel()
580+
automationService.Start(automationsCtx)
581+
582+
orphanScanCtx, orphanScanCancel := context.WithCancel(context.Background())
583+
defer orphanScanCancel()
584+
orphanScanService.Start(orphanScanCtx)
576585

577586
backupStore := models.NewBackupStore(db)
578587
backupService := backups.NewService(backupStore, syncManager, jackettService, backups.Config{DataDir: cfg.GetDataDir()})
@@ -657,11 +666,14 @@ func (app *Application) runServer() {
657666
CrossSeedService: crossSeedService,
658667
JackettService: jackettService,
659668
TorznabIndexerStore: torznabIndexerStore,
660-
TrackerRuleStore: trackerRuleStore,
661-
TrackerRuleService: trackerRuleService,
669+
AutomationStore: automationStore,
670+
AutomationActivityStore: automationActivityStore,
671+
AutomationService: automationService,
662672
TrackerCustomizationStore: trackerCustomizationStore,
663673
DashboardSettingsStore: dashboardSettingsStore,
664674
InstanceCrossSeedCompletionStore: instanceCrossSeedCompletionStore,
675+
OrphanScanStore: orphanScanStore,
676+
OrphanScanService: orphanScanService,
665677
})
666678

667679
errorChannel := make(chan error)

0 commit comments

Comments
 (0)