network/pkg/cli/setup.go
anonpenguin23 030ea88cf6
feat: enhance setup process with firewall configuration
- Added a new step to configure the UFW firewall during the setup process, ensuring necessary ports are opened for SSH, P2P, and Gateway API.
- Updated the setup command output to reflect the new firewall configuration step.
- Refactored the setup logic to maintain proper sequence and clarity in the setup steps.
- Modified the installation script to update the documentation link for better user guidance.
2025-10-26 12:55:32 +02:00

727 lines
21 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cli
import (
"bufio"
"fmt"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
)
// HandleSetupCommand handles the interactive 'setup' command for VPS installation
func HandleSetupCommand(args []string) {
// Parse flags
force := false
for _, arg := range args {
if arg == "--force" {
force = true
}
}
fmt.Printf("🚀 DeBros Network Setup\n\n")
// Check if running as root
if os.Geteuid() != 0 {
fmt.Fprintf(os.Stderr, "❌ This command must be run as root (use sudo)\n")
os.Exit(1)
}
// Check OS compatibility
if runtime.GOOS != "linux" {
fmt.Fprintf(os.Stderr, "❌ Setup command is only supported on Linux\n")
fmt.Fprintf(os.Stderr, " For other platforms, please install manually\n")
os.Exit(1)
}
// Detect OS
osInfo := detectLinuxDistro()
fmt.Printf("📋 Detected OS: %s\n", osInfo)
if !isSupportedOS(osInfo) {
fmt.Fprintf(os.Stderr, "⚠️ Unsupported OS: %s\n", osInfo)
fmt.Fprintf(os.Stderr, " Supported: Ubuntu 22.04/24.04/25.04, Debian 12\n")
fmt.Printf("\nContinue anyway? (yes/no): ")
if !promptYesNo() {
fmt.Println("Setup cancelled.")
os.Exit(1)
}
}
// Show setup plan
fmt.Printf("\n" + strings.Repeat("=", 70) + "\n")
fmt.Printf("Setup Plan:\n")
fmt.Printf(" 1. Create 'debros' system user (if needed)\n")
fmt.Printf(" 2. Install system dependencies (curl, git, make, build tools)\n")
fmt.Printf(" 3. Install Go 1.21+ (if needed)\n")
fmt.Printf(" 4. Install RQLite database\n")
fmt.Printf(" 5. Create directories (/home/debros/bin, /home/debros/src)\n")
fmt.Printf(" 6. Clone and build DeBros Network\n")
fmt.Printf(" 7. Generate configuration files\n")
fmt.Printf(" 8. Configure firewall (UFW) for ports 22, 4001, 6001\n")
fmt.Printf(" 9. Create systemd services (debros-node, debros-gateway)\n")
fmt.Printf(" 10. Start and enable services\n")
fmt.Printf(strings.Repeat("=", 70) + "\n\n")
fmt.Printf("Ready to begin setup? (yes/no): ")
if !promptYesNo() {
fmt.Println("Setup cancelled.")
os.Exit(1)
}
fmt.Printf("\n")
// Step 1: Setup debros user
setupDebrosUser()
// Step 2: Install dependencies
installSystemDependencies()
// Step 3: Install Go
ensureGo()
// Step 4: Install RQLite
installRQLite()
// Step 5: Setup directories
setupDirectories()
// Step 6: Clone and build
cloneAndBuild()
// Step 7: Generate configs (interactive)
generateConfigsInteractive(force)
// Step 8: Configure firewall
configureFirewall()
// Step 9: Create systemd services
createSystemdServices()
// Step 10: Start services
startServices()
// Done!
fmt.Printf("\n" + strings.Repeat("=", 70) + "\n")
fmt.Printf("✅ Setup Complete!\n")
fmt.Printf(strings.Repeat("=", 70) + "\n\n")
fmt.Printf("DeBros Network is now running!\n\n")
fmt.Printf("Service Management:\n")
fmt.Printf(" network-cli service status all\n")
fmt.Printf(" network-cli service logs node --follow\n")
fmt.Printf(" network-cli service restart gateway\n\n")
fmt.Printf("Access DeBros User:\n")
fmt.Printf(" sudo -u debros bash\n\n")
fmt.Printf("Verify Installation:\n")
fmt.Printf(" curl http://localhost:6001/health\n")
fmt.Printf(" curl http://localhost:5001/status\n\n")
}
func detectLinuxDistro() string {
if data, err := os.ReadFile("/etc/os-release"); err == nil {
lines := strings.Split(string(data), "\n")
var id, version string
for _, line := range lines {
if strings.HasPrefix(line, "ID=") {
id = strings.Trim(strings.TrimPrefix(line, "ID="), "\"")
}
if strings.HasPrefix(line, "VERSION_ID=") {
version = strings.Trim(strings.TrimPrefix(line, "VERSION_ID="), "\"")
}
}
if id != "" && version != "" {
return fmt.Sprintf("%s %s", id, version)
}
if id != "" {
return id
}
}
return "unknown"
}
func isSupportedOS(osInfo string) bool {
supported := []string{
"ubuntu 22.04",
"ubuntu 24.04",
"ubuntu 25.04",
"debian 12",
}
for _, s := range supported {
if strings.Contains(strings.ToLower(osInfo), s) {
return true
}
}
return false
}
func promptYesNo() bool {
reader := bufio.NewReader(os.Stdin)
response, _ := reader.ReadString('\n')
response = strings.ToLower(strings.TrimSpace(response))
return response == "yes" || response == "y"
}
// isValidMultiaddr validates bootstrap peer multiaddr format
func isValidMultiaddr(s string) bool {
s = strings.TrimSpace(s)
if s == "" {
return false
}
if !(strings.HasPrefix(s, "/ip4/") || strings.HasPrefix(s, "/ip6/")) {
return false
}
return strings.Contains(s, "/p2p/")
}
// isValidHostPort validates host:port format
func isValidHostPort(s string) bool {
s = strings.TrimSpace(s)
if s == "" {
return false
}
parts := strings.Split(s, ":")
if len(parts) != 2 {
return false
}
host := strings.TrimSpace(parts[0])
port := strings.TrimSpace(parts[1])
if host == "" {
return false
}
// Port must be a valid number between 1 and 65535
if portNum, err := strconv.Atoi(port); err != nil || portNum < 1 || portNum > 65535 {
return false
}
return true
}
func setupDebrosUser() {
fmt.Printf("👤 Setting up 'debros' user...\n")
// Check if user exists
userExists := false
if _, err := exec.Command("id", "debros").CombinedOutput(); err == nil {
fmt.Printf(" ✓ User 'debros' already exists\n")
userExists = true
} else {
// Create user
cmd := exec.Command("useradd", "-r", "-m", "-s", "/bin/bash", "-d", "/home/debros", "debros")
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to create user 'debros': %v\n", err)
os.Exit(1)
}
fmt.Printf(" ✓ Created user 'debros'\n")
}
// Get the user who invoked sudo (the actual user, not root)
sudoUser := os.Getenv("SUDO_USER")
if sudoUser == "" {
// If not running via sudo, skip sudoers setup
return
}
// Create sudoers rule to allow passwordless access to debros user
sudoersRule := fmt.Sprintf("%s ALL=(debros) NOPASSWD: ALL\n", sudoUser)
sudoersFile := "/etc/sudoers.d/debros-access"
// Check if sudoers rule already exists
if existing, err := os.ReadFile(sudoersFile); err == nil {
if strings.Contains(string(existing), sudoUser) {
if !userExists {
fmt.Printf(" ✓ Sudoers access configured\n")
}
return
}
}
// Write sudoers rule
if err := os.WriteFile(sudoersFile, []byte(sudoersRule), 0440); err != nil {
fmt.Fprintf(os.Stderr, "⚠️ Failed to create sudoers rule: %v\n", err)
fmt.Fprintf(os.Stderr, " You can manually switch to debros using: sudo -u debros bash\n")
return
}
// Validate the sudoers file
if err := exec.Command("visudo", "-c", "-f", sudoersFile).Run(); err != nil {
fmt.Fprintf(os.Stderr, "⚠️ Sudoers rule validation failed, removing file\n")
os.Remove(sudoersFile)
return
}
fmt.Printf(" ✓ Sudoers access configured\n")
fmt.Printf(" You can now run: sudo -u debros bash\n")
}
func installSystemDependencies() {
fmt.Printf("📦 Installing system dependencies...\n")
// Detect package manager
var installCmd *exec.Cmd
if _, err := exec.LookPath("apt"); err == nil {
installCmd = exec.Command("apt", "update")
if err := installCmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "⚠️ apt update failed: %v\n", err)
}
installCmd = exec.Command("apt", "install", "-y", "curl", "git", "make", "build-essential", "wget")
} else if _, err := exec.LookPath("yum"); err == nil {
installCmd = exec.Command("yum", "install", "-y", "curl", "git", "make", "gcc", "wget")
} else {
fmt.Fprintf(os.Stderr, "❌ No supported package manager found\n")
os.Exit(1)
}
if err := installCmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to install dependencies: %v\n", err)
os.Exit(1)
}
fmt.Printf(" ✓ Dependencies installed\n")
}
func ensureGo() {
fmt.Printf("🔧 Checking Go installation...\n")
// Check if Go is already installed
if _, err := exec.LookPath("go"); err == nil {
fmt.Printf(" ✓ Go already installed\n")
return
}
fmt.Printf(" Installing Go 1.21.6...\n")
// Download Go
arch := "amd64"
if runtime.GOARCH == "arm64" {
arch = "arm64"
}
goTarball := fmt.Sprintf("go1.21.6.linux-%s.tar.gz", arch)
goURL := fmt.Sprintf("https://go.dev/dl/%s", goTarball)
// Download
cmd := exec.Command("wget", "-q", goURL, "-O", "/tmp/"+goTarball)
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to download Go: %v\n", err)
os.Exit(1)
}
// Extract
cmd = exec.Command("tar", "-C", "/usr/local", "-xzf", "/tmp/"+goTarball)
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to extract Go: %v\n", err)
os.Exit(1)
}
// Add to PATH for current process
os.Setenv("PATH", os.Getenv("PATH")+":/usr/local/go/bin")
// Also add to debros user's .bashrc for persistent availability
debrosHome := "/home/debros"
bashrc := debrosHome + "/.bashrc"
pathLine := "\nexport PATH=$PATH:/usr/local/go/bin\n"
// Read existing bashrc
existing, _ := os.ReadFile(bashrc)
existingStr := string(existing)
// Add PATH if not already present
if !strings.Contains(existingStr, "/usr/local/go/bin") {
if err := os.WriteFile(bashrc, []byte(existingStr+pathLine), 0644); err != nil {
fmt.Fprintf(os.Stderr, "⚠️ Failed to update debros .bashrc: %v\n", err)
}
// Fix ownership
exec.Command("chown", "debros:debros", bashrc).Run()
}
fmt.Printf(" ✓ Go installed\n")
}
func installRQLite() {
fmt.Printf("🗄️ Installing RQLite...\n")
// Check if already installed
if _, err := exec.LookPath("rqlited"); err == nil {
fmt.Printf(" ✓ RQLite already installed\n")
return
}
arch := "amd64"
switch runtime.GOARCH {
case "arm64":
arch = "arm64"
case "arm":
arch = "arm"
}
version := "8.43.0"
tarball := fmt.Sprintf("rqlite-v%s-linux-%s.tar.gz", version, arch)
url := fmt.Sprintf("https://github.com/rqlite/rqlite/releases/download/v%s/%s", version, tarball)
// Download
cmd := exec.Command("wget", "-q", url, "-O", "/tmp/"+tarball)
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to download RQLite: %v\n", err)
os.Exit(1)
}
// Extract
cmd = exec.Command("tar", "-C", "/tmp", "-xzf", "/tmp/"+tarball)
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to extract RQLite: %v\n", err)
os.Exit(1)
}
// Copy binaries
dir := fmt.Sprintf("/tmp/rqlite-v%s-linux-%s", version, arch)
exec.Command("cp", dir+"/rqlited", "/usr/local/bin/").Run()
exec.Command("cp", dir+"/rqlite", "/usr/local/bin/").Run()
exec.Command("chmod", "+x", "/usr/local/bin/rqlited").Run()
exec.Command("chmod", "+x", "/usr/local/bin/rqlite").Run()
fmt.Printf(" ✓ RQLite installed\n")
}
func setupDirectories() {
fmt.Printf("📁 Creating directories...\n")
dirs := []string{
"/home/debros/bin",
"/home/debros/src",
"/home/debros/.debros",
}
for _, dir := range dirs {
if err := os.MkdirAll(dir, 0755); err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to create %s: %v\n", dir, err)
os.Exit(1)
}
// Change ownership to debros
cmd := exec.Command("chown", "-R", "debros:debros", dir)
cmd.Run()
}
fmt.Printf(" ✓ Directories created\n")
}
func cloneAndBuild() {
fmt.Printf("🔨 Cloning and building DeBros Network...\n")
// Check if already cloned
if _, err := os.Stat("/home/debros/src/.git"); err == nil {
fmt.Printf(" Updating repository...\n")
cmd := exec.Command("sudo", "-u", "debros", "git", "-C", "/home/debros/src", "pull", "origin", "nightly")
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "⚠️ Failed to update repo: %v\n", err)
}
} else {
fmt.Printf(" Cloning repository...\n")
cmd := exec.Command("sudo", "-u", "debros", "git", "clone", "--branch", "nightly", "--depth", "1", "https://github.com/DeBrosOfficial/network.git", "/home/debros/src")
if err := cmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to clone repo: %v\n", err)
os.Exit(1)
}
}
// Build
fmt.Printf(" Building binaries...\n")
// Ensure Go is in PATH for the build
os.Setenv("PATH", os.Getenv("PATH")+":/usr/local/go/bin")
// Use sudo with --preserve-env=PATH to pass Go path to debros user
cmd := exec.Command("sudo", "--preserve-env=PATH", "-u", "debros", "make", "build")
cmd.Dir = "/home/debros/src"
if output, err := cmd.CombinedOutput(); err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to build: %v\n%s\n", err, output)
os.Exit(1)
}
// Copy binaries
exec.Command("sh", "-c", "cp -r /home/debros/src/bin/* /home/debros/bin/").Run()
exec.Command("chown", "-R", "debros:debros", "/home/debros/bin").Run()
exec.Command("chmod", "-R", "755", "/home/debros/bin").Run()
fmt.Printf(" ✓ Built and installed\n")
}
func generateConfigsInteractive(force bool) {
fmt.Printf("⚙️ Generating configurations...\n")
// For single-node VPS setup, use sensible defaults
// This creates a bootstrap node that acts as the cluster leader
fmt.Printf("\n")
fmt.Printf("Setting up single-node configuration...\n")
fmt.Printf(" • Bootstrap node (cluster leader)\n")
fmt.Printf(" • No external peers required\n")
fmt.Printf(" • Gateway connected to local node\n\n")
// Generate bootstrap node config with explicit parameters
// Pass empty bootstrap-peers and no join address for bootstrap node
bootstrapArgs := []string{
"-u", "debros",
"/home/debros/bin/network-cli", "config", "init",
"--type", "bootstrap",
"--bootstrap-peers", "",
}
if force {
bootstrapArgs = append(bootstrapArgs, "--force")
}
cmd := exec.Command("sudo", bootstrapArgs...)
cmd.Stdin = nil // Explicitly close stdin to prevent interactive prompts
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Fprintf(os.Stderr, "⚠️ Failed to generate bootstrap config: %v\n", err)
if len(output) > 0 {
fmt.Fprintf(os.Stderr, " Output: %s\n", string(output))
}
} else {
fmt.Printf(" ✓ Bootstrap node config created\n")
}
// Rename bootstrap.yaml to node.yaml so the service can find it
renameCmd := exec.Command("sudo", "-u", "debros", "mv", "/home/debros/.debros/bootstrap.yaml", "/home/debros/.debros/node.yaml")
if err := renameCmd.Run(); err != nil {
fmt.Fprintf(os.Stderr, "⚠️ Failed to rename config: %v\n", err)
}
// Generate gateway config with explicit empty bootstrap peers
gatewayArgs := []string{
"-u", "debros",
"/home/debros/bin/network-cli", "config", "init",
"--type", "gateway",
"--bootstrap-peers", "",
}
if force {
gatewayArgs = append(gatewayArgs, "--force")
}
cmd = exec.Command("sudo", gatewayArgs...)
cmd.Stdin = nil // Explicitly close stdin to prevent interactive prompts
output, err = cmd.CombinedOutput()
if err != nil {
fmt.Fprintf(os.Stderr, "⚠️ Failed to generate gateway config: %v\n", err)
if len(output) > 0 {
fmt.Fprintf(os.Stderr, " Output: %s\n", string(output))
}
} else {
fmt.Printf(" ✓ Gateway config created\n")
}
fmt.Printf(" ✓ Configurations generated\n")
}
func configureFirewall() {
fmt.Printf("🔥 Configuring firewall (UFW)...\n")
// Check if UFW is installed
if _, err := exec.LookPath("ufw"); err != nil {
fmt.Printf(" UFW not installed, skipping firewall configuration\n")
fmt.Printf(" To install later: sudo apt install ufw\n")
fmt.Printf(" Required ports: 22 (SSH), 4001 (P2P), 6001 (Gateway)\n\n")
return
}
// Check if UFW is active
output, _ := exec.Command("ufw", "status").CombinedOutput()
isActive := strings.Contains(string(output), "Status: active")
if isActive {
// UFW is already active - show current status and ask before modifying
fmt.Printf("\n ⚠️ UFW firewall is already active with existing rules.\n")
fmt.Printf(" Current status:\n")
statusCmd := exec.Command("ufw", "status", "numbered")
statusOutput, _ := statusCmd.CombinedOutput()
lines := strings.Split(string(statusOutput), "\n")
for i, line := range lines {
if i < 15 && line != "" {
fmt.Printf(" %s\n", line)
}
}
fmt.Printf("\n DeBros Network requires these ports:\n")
fmt.Printf(" • 22/tcp - SSH (critical - don't block this!)\n")
fmt.Printf(" • 4001/tcp - LibP2P P2P Network\n")
fmt.Printf(" • 6001/tcp - Gateway HTTP API\n")
fmt.Printf(" • 5001, 7001 kept internal (RQLite)\n\n")
fmt.Printf(" Add DeBros ports to your existing firewall? (yes/no): ")
if !promptYesNo() {
fmt.Printf(" ⏭️ Skipping firewall configuration\n")
fmt.Printf(" You can manually add rules later:\n")
fmt.Printf(" sudo ufw allow 4001/tcp\n")
fmt.Printf(" sudo ufw allow 6001/tcp\n\n")
return
}
} else {
// UFW is not active - ask before enabling
fmt.Printf("\n UFW firewall is not active.\n")
fmt.Printf(" DeBros Network requires these ports:\n")
fmt.Printf(" • 22/tcp - SSH (will be allowed first)\n")
fmt.Printf(" • 4001/tcp - LibP2P P2P Network\n")
fmt.Printf(" • 6001/tcp - Gateway HTTP API\n")
fmt.Printf(" • 5001, 7001 kept internal (RQLite)\n\n")
fmt.Printf(" Enable UFW and configure DeBros ports? (yes/no): ")
if !promptYesNo() {
fmt.Printf(" ⏭️ Skipping firewall configuration\n")
fmt.Printf(" You can manually configure later:\n")
fmt.Printf(" sudo ufw allow 22/tcp\n")
fmt.Printf(" sudo ufw allow 4001/tcp\n")
fmt.Printf(" sudo ufw allow 6001/tcp\n")
fmt.Printf(" sudo ufw enable\n\n")
return
}
fmt.Printf("\n Enabling UFW...\n")
// CRITICAL: Allow SSH first to prevent lockout
if err := exec.Command("ufw", "allow", "22/tcp").Run(); err != nil {
fmt.Fprintf(os.Stderr, " ❌ Failed to allow SSH port: %v\n", err)
return
}
fmt.Printf(" ✓ Allowed SSH port 22/tcp (critical)\n")
// Enable UFW with --force to avoid interactive prompt
if err := exec.Command("ufw", "--force", "enable").Run(); err != nil {
fmt.Fprintf(os.Stderr, " ❌ Failed to enable UFW: %v\n", err)
return
}
fmt.Printf(" ✓ UFW enabled\n")
}
// Configure required DeBros ports
ports := map[string]string{
"4001/tcp": "LibP2P (P2P Network)",
"6001/tcp": "Gateway API",
}
for port, description := range ports {
// Check if rule already exists
checkCmd := exec.Command("ufw", "status", "numbered")
checkOutput, _ := checkCmd.CombinedOutput()
if strings.Contains(string(checkOutput), port) {
fmt.Printf(" ✓ Port %s already allowed (%s)\n", port, description)
continue
}
// Add the rule
if err := exec.Command("ufw", "allow", port).Run(); err != nil {
fmt.Fprintf(os.Stderr, " ⚠️ Failed to allow port %s: %v\n", port, err)
} else {
fmt.Printf(" ✓ Allowed port %s (%s)\n", port, description)
}
}
// Show updated firewall status
fmt.Printf("\n Updated firewall configuration:\n")
statusCmd := exec.Command("ufw", "status", "verbose")
statusOutput, _ := statusCmd.CombinedOutput()
lines := strings.Split(string(statusOutput), "\n")
for i, line := range lines {
if i < 12 && line != "" {
fmt.Printf(" %s\n", line)
}
}
fmt.Printf("\n ✅ Firewall configured successfully\n")
fmt.Printf(" Note: Ports 5001 (RQLite HTTP) and 7001 (RQLite Raft) are kept internal\n\n")
}
func createSystemdServices() {
fmt.Printf("🔧 Creating systemd services...\n")
// Node service
nodeService := `[Unit]
Description=DeBros Network Node
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=debros
Group=debros
WorkingDirectory=/home/debros/src
ExecStart=/home/debros/bin/node --config node.yaml
Environment=PATH=/usr/local/bin:/usr/bin:/bin
Environment=HOME=/home/debros
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=debros-node
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/home/debros
[Install]
WantedBy=multi-user.target
`
if err := os.WriteFile("/etc/systemd/system/debros-node.service", []byte(nodeService), 0644); err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to create node service: %v\n", err)
os.Exit(1)
}
// Gateway service
gatewayService := `[Unit]
Description=DeBros Gateway
After=debros-node.service
Wants=debros-node.service
[Service]
Type=simple
User=debros
Group=debros
WorkingDirectory=/home/debros/src
ExecStart=/home/debros/bin/gateway
Environment=PATH=/usr/local/bin:/usr/bin:/bin
Environment=HOME=/home/debros
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=debros-gateway
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/home/debros
[Install]
WantedBy=multi-user.target
`
if err := os.WriteFile("/etc/systemd/system/debros-gateway.service", []byte(gatewayService), 0644); err != nil {
fmt.Fprintf(os.Stderr, "❌ Failed to create gateway service: %v\n", err)
os.Exit(1)
}
// Reload systemd
exec.Command("systemctl", "daemon-reload").Run()
exec.Command("systemctl", "enable", "debros-node").Run()
exec.Command("systemctl", "enable", "debros-gateway").Run()
fmt.Printf(" ✓ Services created and enabled\n")
}
func startServices() {
fmt.Printf("🚀 Starting services...\n")
// Start node
if err := exec.Command("systemctl", "start", "debros-node").Run(); err != nil {
fmt.Fprintf(os.Stderr, "⚠️ Failed to start node service: %v\n", err)
} else {
fmt.Printf(" ✓ Node service started\n")
}
// Start gateway
if err := exec.Command("systemctl", "start", "debros-gateway").Run(); err != nil {
fmt.Fprintf(os.Stderr, "⚠️ Failed to start gateway service: %v\n", err)
} else {
fmt.Printf(" ✓ Gateway service started\n")
}
}