1package handlers
2
3import (
4 "context"
5 "strings"
6
7 "github.com/bnema/archup/internal/application/commands"
8 "github.com/bnema/archup/internal/application/dto"
9 "github.com/bnema/archup/internal/domain/ports"
10 "github.com/bnema/archup/internal/domain/system"
11)
12
13// PreflightHandler handles preflight checks
14type PreflightHandler struct {
15 fs ports.FileSystem
16 cmdExec ports.CommandExecutor
17 logger ports.Logger
18 rules *system.SystemValidationRules
19}
20
21// NewPreflightHandler creates a new preflight handler
22func NewPreflightHandler(fs ports.FileSystem, cmdExec ports.CommandExecutor, logger ports.Logger) *PreflightHandler {
23 return &PreflightHandler{
24 fs: fs,
25 cmdExec: cmdExec,
26 logger: logger,
27 rules: system.NewSystemValidationRules(),
28 }
29}
30
31// Handle executes preflight checks
32func (h *PreflightHandler) Handle(ctx context.Context, cmd commands.PreflightCommand) (*dto.PreflightResult, error) {
33 h.logger.Info("Starting preflight checks")
34
35 result := &dto.PreflightResult{
36 SystemInfo: &dto.SystemInfo{},
37 CPUInfo: &dto.CPUInfo{},
38 ChecksPassed: true,
39 Warnings: []string{},
40 CriticalErrors: []string{},
41 }
42
43 // Check if running as root
44 if output, err := h.cmdExec.Execute(ctx, "id", "-u"); err == nil {
45 if strings.TrimSpace(string(output)) != "0" {
46 result.CriticalErrors = append(result.CriticalErrors, "Must run as root user")
47 result.ChecksPassed = false
48 h.logger.Warn("Not running as root")
49 }
50 }
51
52 // Check for Arch Linux
53 if err := h.rules.ValidateArchLinux(ctx, h.fs); err != nil {
54 result.CriticalErrors = append(result.CriticalErrors, err.Error())
55 result.ChecksPassed = false
56 h.logger.Error("Not running on Arch Linux", "error", err)
57 }
58
59 // Detect distribution type
60 if distro, err := system.DetectDistribution(ctx, h.fs); err == nil {
61 result.SystemInfo.Distribution = distro.String()
62 h.logger.Info("Detected distribution", "distro", distro.String())
63
64 // Check for derivatives (must be vanilla Arch)
65 if err := h.rules.ValidateNotDerivative(ctx, h.fs); err != nil {
66 result.CriticalErrors = append(result.CriticalErrors, err.Error())
67 result.ChecksPassed = false
68 h.logger.Error("Derivative distribution detected", "error", err)
69 }
70 }
71
72 // Check architecture
73 if output, err := h.cmdExec.Execute(ctx, "uname", "-m"); err == nil && len(output) > 0 {
74 arch := strings.TrimSpace(string(output))
75 result.SystemInfo.Architecture = arch
76 h.logger.Info("Detected architecture", "arch", arch)
77
78 // Validate x86_64
79 if err := h.rules.ValidateArchitecture(arch); err != nil {
80 result.CriticalErrors = append(result.CriticalErrors, err.Error())
81 result.ChecksPassed = false
82 h.logger.Error("Invalid architecture", "error", err)
83 }
84 }
85
86 // Check UEFI boot
87 if err := h.rules.ValidateUEFIBoot(ctx, h.fs); err != nil {
88 result.CriticalErrors = append(result.CriticalErrors, err.Error())
89 result.ChecksPassed = false
90 result.SystemInfo.IsUEFI = false
91 h.logger.Error("Not booted in UEFI mode", "error", err)
92 } else {
93 result.SystemInfo.IsUEFI = true
94 h.logger.Info("UEFI boot mode confirmed")
95 }
96
97 // Check Secure Boot status
98 if enabled, err := h.rules.DetectSecureBoot(ctx, h.cmdExec); err == nil {
99 result.SystemInfo.SecureBootEnabled = enabled
100 if enabled {
101 h.logger.Warn("Secure Boot is enabled")
102 } else {
103 h.logger.Info("Secure Boot is disabled")
104 }
105
106 // Validate that Secure Boot is disabled
107 if err := h.rules.ValidateSecureBootDisabled(ctx, h.cmdExec); err != nil {
108 result.CriticalErrors = append(result.CriticalErrors, err.Error())
109 result.ChecksPassed = false
110 h.logger.Error("Secure Boot must be disabled", "error", err)
111 }
112 } else {
113 h.logger.Warn("Could not detect Secure Boot status", "error", err)
114 }
115
116 // Check internet connectivity (try DNS)
117 if _, err := h.cmdExec.Execute(ctx, "ping", "-c", "1", "archlinux.org"); err != nil {
118 result.Warnings = append(result.Warnings, "Could not verify internet connectivity")
119 h.logger.Warn("Internet connectivity check failed")
120 }
121
122 // Get CPU info
123 if output, err := h.cmdExec.Execute(ctx, "grep", "model name", "/proc/cpuinfo"); err == nil && len(output) > 0 {
124 model := strings.TrimSpace(string(output))
125 result.CPUInfo.Model = model
126 h.logger.Info("Detected CPU", "model", model)
127 }
128
129 if result.ChecksPassed {
130 h.logger.Info("Preflight checks passed")
131 } else {
132 h.logger.Error("Preflight checks failed", "errors", result.CriticalErrors)
133 }
134
135 return result, nil
136}