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