mirror of
https://github.com/DeBrosOfficial/network.git
synced 2025-12-11 09:58:49 +00:00
- Replaced all instances of DeBros with Orama throughout the codebase, including CLI commands and configuration paths. - Updated documentation to reflect the new naming convention and paths for configuration files. - Removed the outdated PRODUCTION_INSTALL.md file and added new scripts for local domain setup and testing. - Introduced a new interactive TUI installer for Orama Network, enhancing the installation experience. - Improved logging and error handling across various components to provide clearer feedback during operations.
207 lines
6.4 KiB
Go
207 lines
6.4 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,
|
|
RQLiteJoinAddress: nodeSpec.RQLiteJoinTarget,
|
|
BootstrapPeers: peerAddrs,
|
|
ClusterAPIPort: nodeSpec.ClusterAPIPort,
|
|
IPFSAPIPort: nodeSpec.IPFSAPIPort,
|
|
UnifiedGatewayPort: nodeSpec.UnifiedGatewayPort,
|
|
}
|
|
|
|
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{
|
|
BindAddr: "127.0.0.1",
|
|
HTTPPort: topology.OlricHTTPPort,
|
|
MemberlistPort: topology.OlricMemberPort,
|
|
}
|
|
|
|
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)
|
|
}
|