mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-12-11 10:18:50 +00:00
- Consolidated development commands into a new `dev` command group for better organization. - Introduced a `prod` command group to manage production environment operations. - Updated Makefile to simplify the development environment setup and improve logging. - Enhanced README to clarify the development process and health check requirements. - Removed deprecated configuration and service management commands to streamline the CLI interface.
239 lines
6.7 KiB
Go
239 lines
6.7 KiB
Go
package production
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/DeBrosOfficial/network/pkg/environments/templates"
|
|
"github.com/libp2p/go-libp2p/core/crypto"
|
|
"github.com/libp2p/go-libp2p/core/peer"
|
|
)
|
|
|
|
// ConfigGenerator manages generation of node, gateway, and service configs
|
|
type ConfigGenerator struct {
|
|
debrosDir string
|
|
}
|
|
|
|
// NewConfigGenerator creates a new config generator
|
|
func NewConfigGenerator(debrosDir string) *ConfigGenerator {
|
|
return &ConfigGenerator{
|
|
debrosDir: debrosDir,
|
|
}
|
|
}
|
|
|
|
// GenerateNodeConfig generates node.yaml configuration
|
|
func (cg *ConfigGenerator) GenerateNodeConfig(isBootstrap bool, bootstrapPeers []string, vpsIP string) (string, error) {
|
|
var nodeID string
|
|
if isBootstrap {
|
|
nodeID = "bootstrap"
|
|
} else {
|
|
nodeID = "node"
|
|
}
|
|
|
|
if isBootstrap {
|
|
data := templates.BootstrapConfigData{
|
|
NodeID: nodeID,
|
|
P2PPort: 4001,
|
|
DataDir: filepath.Join(cg.debrosDir, "data", "bootstrap"),
|
|
RQLiteHTTPPort: 5001,
|
|
RQLiteRaftPort: 7001,
|
|
ClusterAPIPort: 9094,
|
|
IPFSAPIPort: 4501,
|
|
}
|
|
return templates.RenderBootstrapConfig(data)
|
|
}
|
|
|
|
// Regular node
|
|
rqliteJoinAddr := "localhost:7001"
|
|
if vpsIP != "" {
|
|
rqliteJoinAddr = vpsIP + ":7001"
|
|
}
|
|
|
|
data := templates.NodeConfigData{
|
|
NodeID: nodeID,
|
|
P2PPort: 4001,
|
|
DataDir: filepath.Join(cg.debrosDir, "data", "node"),
|
|
RQLiteHTTPPort: 5001,
|
|
RQLiteRaftPort: 7001,
|
|
RQLiteJoinAddress: rqliteJoinAddr,
|
|
BootstrapPeers: bootstrapPeers,
|
|
ClusterAPIPort: 9094,
|
|
IPFSAPIPort: 4501,
|
|
}
|
|
return templates.RenderNodeConfig(data)
|
|
}
|
|
|
|
// GenerateGatewayConfig generates gateway.yaml configuration
|
|
func (cg *ConfigGenerator) GenerateGatewayConfig(bootstrapPeers []string, enableHTTPS bool, domain string, olricServers []string) (string, error) {
|
|
tlsCacheDir := ""
|
|
if enableHTTPS {
|
|
tlsCacheDir = filepath.Join(cg.debrosDir, "tls-cache")
|
|
}
|
|
|
|
data := templates.GatewayConfigData{
|
|
ListenPort: 6001,
|
|
BootstrapPeers: bootstrapPeers,
|
|
OlricServers: olricServers,
|
|
ClusterAPIPort: 9094,
|
|
IPFSAPIPort: 4501,
|
|
EnableHTTPS: enableHTTPS,
|
|
DomainName: domain,
|
|
TLSCacheDir: tlsCacheDir,
|
|
RQLiteDSN: "", // Empty for now, can be configured later
|
|
}
|
|
return templates.RenderGatewayConfig(data)
|
|
}
|
|
|
|
// GenerateOlricConfig generates Olric configuration
|
|
func (cg *ConfigGenerator) GenerateOlricConfig(bindAddr string, httpPort, memberlistPort int) (string, error) {
|
|
data := templates.OlricConfigData{
|
|
BindAddr: bindAddr,
|
|
HTTPPort: httpPort,
|
|
MemberlistPort: memberlistPort,
|
|
}
|
|
return templates.RenderOlricConfig(data)
|
|
}
|
|
|
|
// SecretGenerator manages generation of shared secrets and keys
|
|
type SecretGenerator struct {
|
|
debrosDir string
|
|
}
|
|
|
|
// NewSecretGenerator creates a new secret generator
|
|
func NewSecretGenerator(debrosDir string) *SecretGenerator {
|
|
return &SecretGenerator{
|
|
debrosDir: debrosDir,
|
|
}
|
|
}
|
|
|
|
// EnsureClusterSecret gets or generates the IPFS Cluster secret
|
|
func (sg *SecretGenerator) EnsureClusterSecret() (string, error) {
|
|
secretPath := filepath.Join(sg.debrosDir, "secrets", "cluster-secret")
|
|
secretDir := filepath.Dir(secretPath)
|
|
|
|
// Ensure secrets directory exists
|
|
if err := os.MkdirAll(secretDir, 0755); err != nil {
|
|
return "", fmt.Errorf("failed to create secrets directory: %w", err)
|
|
}
|
|
|
|
// Try to read existing secret
|
|
if data, err := os.ReadFile(secretPath); err == nil {
|
|
secret := strings.TrimSpace(string(data))
|
|
if len(secret) == 64 {
|
|
return secret, nil
|
|
}
|
|
}
|
|
|
|
// Generate new secret (32 bytes = 64 hex chars)
|
|
bytes := make([]byte, 32)
|
|
if _, err := rand.Read(bytes); err != nil {
|
|
return "", fmt.Errorf("failed to generate cluster secret: %w", err)
|
|
}
|
|
secret := hex.EncodeToString(bytes)
|
|
|
|
// Write and protect
|
|
if err := os.WriteFile(secretPath, []byte(secret), 0600); err != nil {
|
|
return "", fmt.Errorf("failed to save cluster secret: %w", err)
|
|
}
|
|
|
|
return secret, nil
|
|
}
|
|
|
|
// EnsureSwarmKey gets or generates the IPFS private swarm key
|
|
func (sg *SecretGenerator) EnsureSwarmKey() ([]byte, error) {
|
|
swarmKeyPath := filepath.Join(sg.debrosDir, "secrets", "swarm.key")
|
|
secretDir := filepath.Dir(swarmKeyPath)
|
|
|
|
// Ensure secrets directory exists
|
|
if err := os.MkdirAll(secretDir, 0755); err != nil {
|
|
return nil, fmt.Errorf("failed to create secrets directory: %w", err)
|
|
}
|
|
|
|
// Try to read existing key
|
|
if data, err := os.ReadFile(swarmKeyPath); err == nil {
|
|
if strings.Contains(string(data), "/key/swarm/psk/1.0.0/") {
|
|
return data, nil
|
|
}
|
|
}
|
|
|
|
// Generate new key (32 bytes)
|
|
keyBytes := make([]byte, 32)
|
|
if _, err := rand.Read(keyBytes); err != nil {
|
|
return nil, fmt.Errorf("failed to generate swarm key: %w", err)
|
|
}
|
|
|
|
keyHex := strings.ToUpper(hex.EncodeToString(keyBytes))
|
|
content := fmt.Sprintf("/key/swarm/psk/1.0.0/\n/base16/\n%s\n", keyHex)
|
|
|
|
// Write and protect
|
|
if err := os.WriteFile(swarmKeyPath, []byte(content), 0600); err != nil {
|
|
return nil, fmt.Errorf("failed to save swarm key: %w", err)
|
|
}
|
|
|
|
return []byte(content), nil
|
|
}
|
|
|
|
// EnsureNodeIdentity gets or generates the node's LibP2P identity
|
|
func (sg *SecretGenerator) EnsureNodeIdentity(nodeType string) (peer.ID, error) {
|
|
keyDir := filepath.Join(sg.debrosDir, "data", nodeType)
|
|
keyPath := filepath.Join(keyDir, "identity.key")
|
|
|
|
// Ensure data directory exists
|
|
if err := os.MkdirAll(keyDir, 0755); err != nil {
|
|
return "", fmt.Errorf("failed to create data directory: %w", err)
|
|
}
|
|
|
|
// Try to read existing key
|
|
if data, err := os.ReadFile(keyPath); err == nil {
|
|
priv, err := crypto.UnmarshalPrivateKey(data)
|
|
if err == nil {
|
|
pub := priv.GetPublic()
|
|
peerID, _ := peer.IDFromPublicKey(pub)
|
|
return peerID, nil
|
|
}
|
|
}
|
|
|
|
// Generate new identity
|
|
priv, pub, err := crypto.GenerateKeyPair(crypto.Ed25519, 2048)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to generate identity: %w", err)
|
|
}
|
|
|
|
peerID, _ := peer.IDFromPublicKey(pub)
|
|
|
|
// Marshal and save private key
|
|
keyData, err := crypto.MarshalPrivateKey(priv)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to marshal private key: %w", err)
|
|
}
|
|
|
|
if err := os.WriteFile(keyPath, keyData, 0600); err != nil {
|
|
return "", fmt.Errorf("failed to save identity key: %w", err)
|
|
}
|
|
|
|
return peerID, nil
|
|
}
|
|
|
|
// SaveConfig writes a configuration file to disk
|
|
func (sg *SecretGenerator) SaveConfig(filename string, content string) error {
|
|
configDir := filepath.Join(sg.debrosDir, "configs")
|
|
if err := os.MkdirAll(configDir, 0755); err != nil {
|
|
return fmt.Errorf("failed to create configs directory: %w", err)
|
|
}
|
|
|
|
configPath := filepath.Join(configDir, filename)
|
|
if err := os.WriteFile(configPath, []byte(content), 0644); err != nil {
|
|
return fmt.Errorf("failed to write config %s: %w", filename, err)
|
|
}
|
|
|
|
// Fix ownership
|
|
exec.Command("chown", "debros:debros", configPath).Run()
|
|
|
|
return nil
|
|
}
|