+2
-1
.gitignore
+2
-1
.gitignore
+38
-7
Makefile
+38
-7
Makefile
···
1
-
all: run
1
+
.PHONY: all build install test clean fmt lint help
2
+
3
+
# Binary name
4
+
BINARY_NAME=atscand
5
+
INSTALL_PATH=$(GOPATH)/bin
6
+
7
+
# Go commands
8
+
GOCMD=go
9
+
GOBUILD=$(GOCMD) build
10
+
GOINSTALL=$(GOCMD) install
11
+
GOCLEAN=$(GOCMD) clean
12
+
GOTEST=$(GOCMD) test
13
+
GOGET=$(GOCMD) get
14
+
GOFMT=$(GOCMD) fmt
15
+
GOMOD=$(GOCMD) mod
16
+
GORUN=$(GOCMD) run
17
+
18
+
# Default target
19
+
all: build
20
+
21
+
# Build the CLI tool
22
+
build:
23
+
@echo "Building $(BINARY_NAME)..."
24
+
$(GOBUILD) -o $(BINARY_NAME) ./cmd/atscand
2
25
3
-
run:
4
-
go run cmd/atscanner.go -verbose
26
+
# Install the CLI tool globally
27
+
install:
28
+
@echo "Installing $(BINARY_NAME)..."
29
+
$(GOINSTALL) ./cmd/atscand
5
30
6
-
clean-db:
7
-
dropdb -U atscanner atscanner
8
-
createdb atscanner -O atscanner
31
+
run:
32
+
$(GORUN) cmd/atscand/main.go -verbose
9
33
10
34
update-plcbundle:
11
-
GOPROXY=direct go get -u github.com/atscan/plcbundle@latest
35
+
GOPROXY=direct go get -u github.com/atscan/plcbundle@latest
36
+
37
+
# Show help
38
+
help:
39
+
@echo "Available targets:"
40
+
@echo " make build - Build the binary"
41
+
@echo " make install - Install binary globally"
42
+
@echo " make run - Run app"
-159
cmd/atscanner.go
-159
cmd/atscanner.go
···
1
-
package main
2
-
3
-
import (
4
-
"context"
5
-
"flag"
6
-
"fmt"
7
-
"os"
8
-
"os/signal"
9
-
"syscall"
10
-
"time"
11
-
12
-
"github.com/atscan/atscanner/internal/api"
13
-
"github.com/atscan/atscanner/internal/config"
14
-
"github.com/atscan/atscanner/internal/log"
15
-
"github.com/atscan/atscanner/internal/pds"
16
-
"github.com/atscan/atscanner/internal/plc"
17
-
"github.com/atscan/atscanner/internal/storage"
18
-
"github.com/atscan/atscanner/internal/worker"
19
-
)
20
-
21
-
const VERSION = "1.0.0"
22
-
23
-
func main() {
24
-
configPath := flag.String("config", "config.yaml", "path to config file")
25
-
verbose := flag.Bool("verbose", false, "enable verbose logging")
26
-
flag.Parse()
27
-
28
-
// Load configuration
29
-
cfg, err := config.Load(*configPath)
30
-
if err != nil {
31
-
fmt.Fprintf(os.Stderr, "Failed to load config: %v\n", err)
32
-
os.Exit(1)
33
-
}
34
-
35
-
// Override verbose setting if flag is provided
36
-
if *verbose {
37
-
cfg.API.Verbose = true
38
-
}
39
-
40
-
// Initialize logger
41
-
log.Init(cfg.API.Verbose)
42
-
43
-
// Print banner
44
-
log.Banner(VERSION)
45
-
46
-
// Print configuration summary
47
-
log.PrintConfig(map[string]string{
48
-
"Database Type": cfg.Database.Type,
49
-
"Database Path": cfg.Database.Path, // Will be auto-redacted
50
-
"PLC Directory": cfg.PLC.DirectoryURL,
51
-
"PLC Scan Interval": cfg.PLC.ScanInterval.String(),
52
-
"PLC Bundle Dir": cfg.PLC.BundleDir,
53
-
"PLC Cache": fmt.Sprintf("%v", cfg.PLC.UseCache),
54
-
"PLC Index DIDs": fmt.Sprintf("%v", cfg.PLC.IndexDIDs),
55
-
"PDS Scan Interval": cfg.PDS.ScanInterval.String(),
56
-
"PDS Workers": fmt.Sprintf("%d", cfg.PDS.Workers),
57
-
"PDS Timeout": cfg.PDS.Timeout.String(),
58
-
"API Host": cfg.API.Host,
59
-
"API Port": fmt.Sprintf("%d", cfg.API.Port),
60
-
"Verbose Logging": fmt.Sprintf("%v", cfg.API.Verbose),
61
-
})
62
-
63
-
// Initialize database using factory pattern
64
-
db, err := storage.NewDatabase(cfg.Database.Type, cfg.Database.Path)
65
-
if err != nil {
66
-
log.Fatal("Failed to initialize database: %v", err)
67
-
}
68
-
defer func() {
69
-
log.Info("Closing database connection...")
70
-
db.Close()
71
-
}()
72
-
73
-
// Set scan retention from config
74
-
if cfg.PDS.ScanRetention > 0 {
75
-
db.SetScanRetention(cfg.PDS.ScanRetention)
76
-
log.Verbose("Scan retention set to %d scans per endpoint", cfg.PDS.ScanRetention)
77
-
}
78
-
79
-
// Run migrations
80
-
if err := db.Migrate(); err != nil {
81
-
log.Fatal("Failed to run migrations: %v", err)
82
-
}
83
-
84
-
ctx, cancel := context.WithCancel(context.Background())
85
-
defer cancel()
86
-
87
-
// Initialize workers
88
-
log.Info("Initializing scanners...")
89
-
90
-
bundleManager, err := plc.NewBundleManager(cfg.PLC.BundleDir, cfg.PLC.DirectoryURL, db, cfg.PLC.IndexDIDs)
91
-
if err != nil {
92
-
log.Fatal("Failed to create bundle manager: %v", err)
93
-
}
94
-
defer bundleManager.Close()
95
-
log.Verbose("✓ Bundle manager initialized (shared)")
96
-
97
-
plcScanner := plc.NewScanner(db, cfg.PLC, bundleManager)
98
-
defer plcScanner.Close()
99
-
log.Verbose("✓ PLC scanner initialized")
100
-
101
-
pdsScanner := pds.NewScanner(db, cfg.PDS)
102
-
log.Verbose("✓ PDS scanner initialized")
103
-
104
-
scheduler := worker.NewScheduler()
105
-
106
-
// Schedule PLC directory scan
107
-
scheduler.AddJob("plc_scan", cfg.PLC.ScanInterval, func() {
108
-
if err := plcScanner.Scan(ctx); err != nil {
109
-
log.Error("PLC scan error: %v", err)
110
-
}
111
-
})
112
-
log.Verbose("✓ PLC scan job scheduled (interval: %s)", cfg.PLC.ScanInterval)
113
-
114
-
// Schedule PDS availability checks
115
-
scheduler.AddJob("pds_scan", cfg.PDS.ScanInterval, func() {
116
-
if err := pdsScanner.ScanAll(ctx); err != nil {
117
-
log.Error("PDS scan error: %v", err)
118
-
}
119
-
})
120
-
log.Verbose("✓ PDS scan job scheduled (interval: %s)", cfg.PDS.ScanInterval)
121
-
122
-
// Start API server
123
-
log.Info("Starting API server on %s:%d...", cfg.API.Host, cfg.API.Port)
124
-
apiServer := api.NewServer(db, cfg.API, cfg.PLC, bundleManager)
125
-
go func() {
126
-
if err := apiServer.Start(); err != nil {
127
-
log.Fatal("API server error: %v", err)
128
-
}
129
-
}()
130
-
131
-
// Give the API server a moment to start
132
-
time.Sleep(100 * time.Millisecond)
133
-
log.Info("✓ API server started successfully")
134
-
log.Info("")
135
-
log.Info("🚀 ATScanner is running!")
136
-
log.Info(" API available at: http://%s:%d", cfg.API.Host, cfg.API.Port)
137
-
log.Info(" Press Ctrl+C to stop")
138
-
log.Info("")
139
-
140
-
// Start scheduler
141
-
scheduler.Start(ctx)
142
-
143
-
// Wait for interrupt
144
-
sigChan := make(chan os.Signal, 1)
145
-
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
146
-
<-sigChan
147
-
148
-
log.Info("")
149
-
log.Info("Shutting down gracefully...")
150
-
cancel()
151
-
152
-
log.Info("Stopping API server...")
153
-
apiServer.Shutdown(context.Background())
154
-
155
-
log.Info("Waiting for active tasks to complete...")
156
-
time.Sleep(2 * time.Second)
157
-
158
-
log.Info("✓ Shutdown complete. Goodbye!")
159
-
}
+1
-1
config.sample.yaml
+1
-1
config.sample.yaml
+1
-1
go.mod
+1
-1
go.mod
+4
-4
internal/api/handlers.go
+4
-4
internal/api/handlers.go
···
11
11
"strings"
12
12
"time"
13
13
14
-
"github.com/atscan/atscanner/internal/log"
15
-
"github.com/atscan/atscanner/internal/monitor"
16
-
"github.com/atscan/atscanner/internal/plc"
17
-
"github.com/atscan/atscanner/internal/storage"
14
+
"github.com/atscan/atscand/internal/log"
15
+
"github.com/atscan/atscand/internal/monitor"
16
+
"github.com/atscan/atscand/internal/plc"
17
+
"github.com/atscan/atscand/internal/storage"
18
18
"github.com/atscan/plcbundle"
19
19
"github.com/gorilla/mux"
20
20
)
+4
-4
internal/api/server.go
+4
-4
internal/api/server.go
···
6
6
"net/http"
7
7
"time"
8
8
9
-
"github.com/atscan/atscanner/internal/config"
10
-
"github.com/atscan/atscanner/internal/log"
11
-
"github.com/atscan/atscanner/internal/plc"
12
-
"github.com/atscan/atscanner/internal/storage"
9
+
"github.com/atscan/atscand/internal/config"
10
+
"github.com/atscan/atscand/internal/log"
11
+
"github.com/atscan/atscand/internal/plc"
12
+
"github.com/atscan/atscand/internal/storage"
13
13
"github.com/gorilla/handlers"
14
14
"github.com/gorilla/mux"
15
15
)
+5
-5
internal/pds/scanner.go
+5
-5
internal/pds/scanner.go
···
9
9
"time"
10
10
11
11
"github.com/acarl005/stripansi"
12
-
"github.com/atscan/atscanner/internal/config"
13
-
"github.com/atscan/atscanner/internal/ipinfo"
14
-
"github.com/atscan/atscanner/internal/log"
15
-
"github.com/atscan/atscanner/internal/monitor"
16
-
"github.com/atscan/atscanner/internal/storage"
12
+
"github.com/atscan/atscand/internal/config"
13
+
"github.com/atscan/atscand/internal/ipinfo"
14
+
"github.com/atscan/atscand/internal/log"
15
+
"github.com/atscan/atscand/internal/monitor"
16
+
"github.com/atscan/atscand/internal/storage"
17
17
)
18
18
19
19
type Scanner struct {
+2
-2
internal/plc/manager.go
+2
-2
internal/plc/manager.go
+3
-3
internal/plc/scanner.go
+3
-3
internal/plc/scanner.go
···
6
6
"strings"
7
7
"time"
8
8
9
-
"github.com/atscan/atscanner/internal/config"
10
-
"github.com/atscan/atscanner/internal/log"
11
-
"github.com/atscan/atscanner/internal/storage"
9
+
"github.com/atscan/atscand/internal/config"
10
+
"github.com/atscan/atscand/internal/log"
11
+
"github.com/atscan/atscand/internal/storage"
12
12
)
13
13
14
14
type Scanner struct {
+1
-1
internal/storage/postgres.go
+1
-1
internal/storage/postgres.go
+2
-2
internal/worker/scheduler.go
+2
-2
internal/worker/scheduler.go