mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-12-15 22:58:49 +00:00
- Renamed the `make down` command to `make stop` in the README for clarity. - Enhanced the node configuration to include additional parameters for RQLite, specifically `RQLiteRaftInternalPort`, `HTTPAdvAddress`, and `RaftAdvAddress`. - Updated the HTTPS gateway to use Let's Encrypt in production mode by default, removing references to the staging environment and improving logging for certificate management.
212 lines
6.7 KiB
Go
212 lines
6.7 KiB
Go
package development
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/DeBrosOfficial/network/pkg/encryption"
|
|
"github.com/DeBrosOfficial/network/pkg/environments/templates"
|
|
)
|
|
|
|
// ConfigEnsurer handles all config file creation and validation
|
|
type ConfigEnsurer struct {
|
|
oramaDir string
|
|
}
|
|
|
|
// NewConfigEnsurer creates a new config ensurer
|
|
func NewConfigEnsurer(oramaDir string) *ConfigEnsurer {
|
|
return &ConfigEnsurer{
|
|
oramaDir: oramaDir,
|
|
}
|
|
}
|
|
|
|
// EnsureAll ensures all necessary config files and secrets exist
|
|
func (ce *ConfigEnsurer) EnsureAll() error {
|
|
// Create directories
|
|
if err := os.MkdirAll(ce.oramaDir, 0755); err != nil {
|
|
return fmt.Errorf("failed to create .orama directory: %w", err)
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Join(ce.oramaDir, "logs"), 0755); err != nil {
|
|
return fmt.Errorf("failed to create logs directory: %w", err)
|
|
}
|
|
|
|
// Ensure shared secrets
|
|
if err := ce.ensureSharedSecrets(); err != nil {
|
|
return fmt.Errorf("failed to ensure shared secrets: %w", err)
|
|
}
|
|
|
|
// Load topology
|
|
topology := DefaultTopology()
|
|
|
|
// Generate identities for first two nodes and collect their multiaddrs as peer addresses
|
|
// All nodes use these addresses for initial peer discovery
|
|
peerAddrs := []string{}
|
|
for i := 0; i < 2 && i < len(topology.Nodes); i++ {
|
|
nodeSpec := topology.Nodes[i]
|
|
addr, err := ce.ensureNodeIdentity(nodeSpec)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to ensure identity for %s: %w", nodeSpec.Name, err)
|
|
}
|
|
peerAddrs = append(peerAddrs, addr)
|
|
}
|
|
|
|
// Ensure configs for all nodes
|
|
for _, nodeSpec := range topology.Nodes {
|
|
if err := ce.ensureNodeConfig(nodeSpec, peerAddrs); err != nil {
|
|
return fmt.Errorf("failed to ensure config for %s: %w", nodeSpec.Name, err)
|
|
}
|
|
}
|
|
|
|
// Gateway configuration is now embedded in each node's config
|
|
// No separate gateway.yaml needed anymore
|
|
|
|
// Ensure Olric config
|
|
if err := ce.ensureOlric(); err != nil {
|
|
return fmt.Errorf("failed to ensure olric: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ensureSharedSecrets creates cluster secret and swarm key if they don't exist
|
|
func (ce *ConfigEnsurer) ensureSharedSecrets() error {
|
|
secretPath := filepath.Join(ce.oramaDir, "cluster-secret")
|
|
if _, err := os.Stat(secretPath); os.IsNotExist(err) {
|
|
secret := generateRandomHex(64) // 64 hex chars = 32 bytes
|
|
if err := os.WriteFile(secretPath, []byte(secret), 0600); err != nil {
|
|
return fmt.Errorf("failed to write cluster secret: %w", err)
|
|
}
|
|
fmt.Printf("✓ Generated cluster secret\n")
|
|
}
|
|
|
|
swarmKeyPath := filepath.Join(ce.oramaDir, "swarm.key")
|
|
if _, err := os.Stat(swarmKeyPath); os.IsNotExist(err) {
|
|
keyHex := strings.ToUpper(generateRandomHex(64))
|
|
content := fmt.Sprintf("/key/swarm/psk/1.0.0/\n/base16/\n%s\n", keyHex)
|
|
if err := os.WriteFile(swarmKeyPath, []byte(content), 0600); err != nil {
|
|
return fmt.Errorf("failed to write swarm key: %w", err)
|
|
}
|
|
fmt.Printf("✓ Generated IPFS swarm key\n")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ensureNodeIdentity creates or loads a node identity and returns its multiaddr
|
|
func (ce *ConfigEnsurer) ensureNodeIdentity(nodeSpec NodeSpec) (string, error) {
|
|
nodeDir := filepath.Join(ce.oramaDir, nodeSpec.DataDir)
|
|
identityPath := filepath.Join(nodeDir, "identity.key")
|
|
|
|
// Create identity if missing
|
|
var peerID string
|
|
if _, err := os.Stat(identityPath); os.IsNotExist(err) {
|
|
if err := os.MkdirAll(nodeDir, 0755); err != nil {
|
|
return "", fmt.Errorf("failed to create node directory: %w", err)
|
|
}
|
|
|
|
info, err := encryption.GenerateIdentity()
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to generate identity: %w", err)
|
|
}
|
|
|
|
if err := encryption.SaveIdentity(info, identityPath); err != nil {
|
|
return "", fmt.Errorf("failed to save identity: %w", err)
|
|
}
|
|
|
|
peerID = info.PeerID.String()
|
|
fmt.Printf("✓ Generated %s identity (Peer ID: %s)\n", nodeSpec.Name, peerID)
|
|
} else {
|
|
info, err := encryption.LoadIdentity(identityPath)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to load identity: %w", err)
|
|
}
|
|
peerID = info.PeerID.String()
|
|
}
|
|
|
|
// Return multiaddr
|
|
return fmt.Sprintf("/ip4/127.0.0.1/tcp/%d/p2p/%s", nodeSpec.P2PPort, peerID), nil
|
|
}
|
|
|
|
// ensureNodeConfig creates or updates a node configuration
|
|
func (ce *ConfigEnsurer) ensureNodeConfig(nodeSpec NodeSpec, peerAddrs []string) error {
|
|
nodeDir := filepath.Join(ce.oramaDir, nodeSpec.DataDir)
|
|
configPath := filepath.Join(ce.oramaDir, nodeSpec.ConfigFilename)
|
|
|
|
if err := os.MkdirAll(nodeDir, 0755); err != nil {
|
|
return fmt.Errorf("failed to create node directory: %w", err)
|
|
}
|
|
|
|
// Generate node config (all nodes are unified)
|
|
data := templates.NodeConfigData{
|
|
NodeID: nodeSpec.Name,
|
|
P2PPort: nodeSpec.P2PPort,
|
|
DataDir: nodeDir,
|
|
RQLiteHTTPPort: nodeSpec.RQLiteHTTPPort,
|
|
RQLiteRaftPort: nodeSpec.RQLiteRaftPort,
|
|
RQLiteRaftInternalPort: nodeSpec.RQLiteRaftPort,
|
|
RQLiteJoinAddress: nodeSpec.RQLiteJoinTarget,
|
|
BootstrapPeers: peerAddrs,
|
|
ClusterAPIPort: nodeSpec.ClusterAPIPort,
|
|
IPFSAPIPort: nodeSpec.IPFSAPIPort,
|
|
UnifiedGatewayPort: nodeSpec.UnifiedGatewayPort,
|
|
HTTPAdvAddress: fmt.Sprintf("localhost:%d", nodeSpec.RQLiteHTTPPort),
|
|
RaftAdvAddress: fmt.Sprintf("localhost:%d", nodeSpec.RQLiteRaftPort),
|
|
}
|
|
|
|
config, err := templates.RenderNodeConfig(data)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to render node config: %w", err)
|
|
}
|
|
|
|
if err := os.WriteFile(configPath, []byte(config), 0644); err != nil {
|
|
return fmt.Errorf("failed to write node config: %w", err)
|
|
}
|
|
|
|
fmt.Printf("✓ Generated %s.yaml\n", nodeSpec.Name)
|
|
|
|
return nil
|
|
}
|
|
|
|
// Gateway configuration is now embedded in each node's config
|
|
// ensureGateway is no longer needed - each node runs its own embedded gateway
|
|
|
|
// ensureOlric creates Olric config
|
|
func (ce *ConfigEnsurer) ensureOlric() error {
|
|
configPath := filepath.Join(ce.oramaDir, "olric-config.yaml")
|
|
|
|
topology := DefaultTopology()
|
|
data := templates.OlricConfigData{
|
|
ServerBindAddr: "127.0.0.1",
|
|
HTTPPort: topology.OlricHTTPPort,
|
|
MemberlistBindAddr: "127.0.0.1", // localhost for development
|
|
MemberlistPort: topology.OlricMemberPort,
|
|
MemberlistEnvironment: "local", // development environment
|
|
}
|
|
|
|
config, err := templates.RenderOlricConfig(data)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to render olric config: %w", err)
|
|
}
|
|
|
|
if err := os.WriteFile(configPath, []byte(config), 0644); err != nil {
|
|
return fmt.Errorf("failed to write olric config: %w", err)
|
|
}
|
|
|
|
fmt.Printf("✓ Generated olric-config.yaml\n")
|
|
return nil
|
|
}
|
|
|
|
// generateRandomHex generates a random hex string of specified length
|
|
func generateRandomHex(length int) string {
|
|
bytes := make([]byte, length/2)
|
|
if _, err := rand.Read(bytes); err != nil {
|
|
panic(fmt.Sprintf("failed to generate random bytes: %v", err))
|
|
}
|
|
return hex.EncodeToString(bytes)
|
|
}
|