Skip to content

Commit 419efc8

Browse files
committed
feat: DNS Server
1 parent c49ea89 commit 419efc8

File tree

15 files changed

+612
-101
lines changed

15 files changed

+612
-101
lines changed

cmd/goup/main.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,14 @@
11
package main
22

33
import (
4-
"fmt"
5-
"os"
6-
74
"github.com/mirkobrombin/goup/internal/cli"
8-
"github.com/mirkobrombin/goup/internal/config"
95
"github.com/mirkobrombin/goup/internal/plugin"
106
"github.com/mirkobrombin/goup/plugins"
117
)
128

139
func main() {
1410
// Load global configuration
15-
if err := config.LoadGlobalConfig(); err != nil {
16-
fmt.Printf("Error loading global config: %v\n", err)
17-
os.Exit(1)
18-
}
11+
// Global config is now loaded via CLI PersistentPreRun to support flags
1912

2013
pluginManager := plugin.NewPluginManager()
2114
plugin.SetDefaultPluginManager(pluginManager)

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ require (
3131
github.com/mattn/go-colorable v0.1.13 // indirect
3232
github.com/mattn/go-isatty v0.0.19 // indirect
3333
github.com/mattn/go-runewidth v0.0.15 // indirect
34+
github.com/miekg/dns v1.1.72 // indirect
3435
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
3536
github.com/quic-go/qpack v0.5.1 // indirect
3637
github.com/rivo/uniseg v0.4.7 // indirect

go.sum

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP
4949
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
5050
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
5151
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
52+
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
53+
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
5254
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
5355
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
5456
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
@@ -137,6 +139,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
137139
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
138140
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
139141
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
142+
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
140143
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
141144
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
142145
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

internal/cli/cli.go

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,19 @@ import (
1717
var tuiMode bool
1818
var benchMode bool
1919
var configPath string
20+
var globalConfigPath string
2021

2122
// rootCmd represents the base command when called without any subcommands.
2223
var rootCmd = &cobra.Command{
2324
Use: "goup",
2425
Short: "GoUP is a minimal configurable web server",
2526
Long: `GoUP is a minimal configurable web server written in Go.`,
27+
PersistentPreRun: func(cmd *cobra.Command, args []string) {
28+
if err := config.LoadGlobalConfig(globalConfigPath); err != nil {
29+
fmt.Printf("Error loading global config: %v\n", err)
30+
os.Exit(1)
31+
}
32+
},
2633
}
2734

2835
// Execute adds all child commands to the root command and sets flags appropriately.
@@ -37,6 +44,8 @@ func init() {
3744
rootCmd.AddCommand(generateCmd)
3845
rootCmd.AddCommand(genPassCmd)
3946
rootCmd.AddCommand(startCmd)
47+
rootCmd.AddCommand(startWebCmd)
48+
rootCmd.AddCommand(startDNSCmd)
4049
rootCmd.AddCommand(validateCmd)
4150
rootCmd.AddCommand(listCmd)
4251
rootCmd.AddCommand(pluginsCmd)
@@ -45,7 +54,8 @@ func init() {
4554
startCmd.Flags().BoolVarP(&benchMode, "bench", "b", false, "Enable benchmark mode")
4655

4756
// Global flags
48-
rootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "", "Path to specific configuration file")
57+
rootCmd.PersistentFlags().StringVarP(&configPath, "config", "c", "", "Path to specific site configuration file")
58+
rootCmd.PersistentFlags().StringVar(&globalConfigPath, "global-config", "", "Path to specific global configuration file")
4959
}
5060

5161
var generateCmd = &cobra.Command{
@@ -155,21 +165,10 @@ var startCmd = &cobra.Command{
155165
}
156166

157167
func start(cmd *cobra.Command, args []string) {
158-
var configs []config.SiteConfig
159-
var err error
160-
161-
if configPath != "" {
162-
configs, err = config.LoadConfigsFromFile(configPath)
163-
if err != nil {
164-
fmt.Printf("Error loading config from %s: %v\n", configPath, err)
165-
os.Exit(1)
166-
}
167-
} else {
168-
configs, err = config.LoadAllConfigs()
169-
if err != nil {
170-
fmt.Printf("Error loading configurations: %v\n", err)
171-
os.Exit(1)
172-
}
168+
configs, err := loadConfigs()
169+
if err != nil {
170+
fmt.Printf("Error loading configurations: %v\n", err)
171+
os.Exit(1)
173172
}
174173

175174
if len(configs) == 0 {
@@ -181,8 +180,8 @@ func start(cmd *cobra.Command, args []string) {
181180
os.Exit(1)
182181
}
183182

184-
fmt.Println("Starting servers...")
185-
server.StartServers(configs, tuiMode, benchMode)
183+
fmt.Println("Starting full GoUp server (Web + DNS)...")
184+
server.StartServers(configs, tuiMode, benchMode, server.ModeAll)
186185

187186
// Wait indefinitely if not in TUI mode, the servers will keep running
188187
// and loggers will keep writing to both the stdout and the log files.
@@ -191,6 +190,53 @@ func start(cmd *cobra.Command, args []string) {
191190
}
192191
}
193192

193+
var startWebCmd = &cobra.Command{
194+
Use: "start-web",
195+
Short: "Start only the web server",
196+
Run: startWeb,
197+
}
198+
199+
func startWeb(cmd *cobra.Command, args []string) {
200+
configs, err := loadConfigs()
201+
if err != nil {
202+
fmt.Printf("Error loading configurations: %v\n", err)
203+
os.Exit(1)
204+
}
205+
206+
fmt.Println("Starting GoUp Web Server...")
207+
server.StartServers(configs, tuiMode, benchMode, server.ModeWeb)
208+
209+
if !tuiMode {
210+
select {}
211+
}
212+
}
213+
214+
var startDNSCmd = &cobra.Command{
215+
Use: "start-dns",
216+
Short: "Start only the DNS server",
217+
Run: startDNS,
218+
}
219+
220+
func startDNS(cmd *cobra.Command, args []string) {
221+
// We might not strictly need site configs for DNS only, but StartServers expects them
222+
// effectively ignoring them if ModeWeb is not set, except for context setup.
223+
configs, _ := loadConfigs()
224+
225+
fmt.Println("Starting GoUp DNS Server...")
226+
server.StartServers(configs, tuiMode, benchMode, server.ModeDNS)
227+
228+
if !tuiMode {
229+
select {}
230+
}
231+
}
232+
233+
func loadConfigs() ([]config.SiteConfig, error) {
234+
if configPath != "" {
235+
return config.LoadConfigsFromFile(configPath)
236+
}
237+
return config.LoadAllConfigs()
238+
}
239+
194240
var validateCmd = &cobra.Command{
195241
Use: "validate",
196242
Short: "Validate the configuration files",

internal/config/dns_config.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package config
2+
3+
// DNSRecord represents a single DNS record entry.
4+
type DNSRecord struct {
5+
Type string `json:"type"` // A, AAAA, CNAME, TXT, MX, NS
6+
Name string `json:"name"` // @ for apex, or subdomain
7+
Value string `json:"value"` // IP, target, or text
8+
TTL uint32 `json:"ttl"` // Time-to-live in seconds
9+
Prio uint16 `json:"prio"` // Priority (for MX records)
10+
}
11+
12+
// DNSConfig defines configuration for the integrated DNS server.
13+
type DNSConfig struct {
14+
Enable bool `json:"enable"`
15+
Port int `json:"port"` // Default: 53
16+
UpstreamResolvers []string `json:"upstream_resolvers"` // Optional forwarding
17+
Zones map[string][]DNSRecord `json:"zones"` // zone -> records
18+
}
19+
20+
// DefaultDNSConfig returns the default DNS configuration.
21+
func DefaultDNSConfig() *DNSConfig {
22+
return &DNSConfig{
23+
Enable: false,
24+
Port: 53,
25+
UpstreamResolvers: []string{},
26+
Zones: make(map[string][]DNSRecord),
27+
}
28+
}

internal/config/global_config.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,30 @@ type GlobalConfig struct {
2828
DashboardPort int `json:"dashboard_port"`
2929
EnabledPlugins []string `json:"enabled_plugins"` // empty means all enabled
3030
SafeGuard SafeGuardConfig `json:"safeguard"`
31+
DNS *DNSConfig `json:"dns"`
3132
}
3233

3334
// GlobalConf is the global configuration in memory.
3435
var GlobalConf *GlobalConfig
3536
var globalConfName = "conf.global.json"
3637

3738
// LoadGlobalConfig loads the global configuration file.
38-
func LoadGlobalConfig() error {
39-
configDir := GetConfigDir()
40-
configFile := filepath.Join(configDir, globalConfName)
39+
func LoadGlobalConfig(customPath string) error {
40+
var configFile string
41+
if customPath != "" {
42+
configFile = customPath
43+
} else {
44+
configDir := GetConfigDir()
45+
configFile = filepath.Join(configDir, globalConfName)
46+
}
47+
4148
if _, err := os.Stat(configFile); os.IsNotExist(err) {
4249
GlobalConf = &GlobalConfig{
4350
EnableAPI: false,
4451
APIPort: 6007,
4552
DashboardPort: 0, // Disabled by default
4653
EnabledPlugins: []string{},
54+
DNS: DefaultDNSConfig(),
4755
}
4856
return nil
4957
}

0 commit comments

Comments
 (0)