Merge pull request 'more optimizations' (#4) from actions into main
Build / Build (push) Successful in 30s
Build / Build (push) Successful in 30s
Reviewed-on: https://gitea.home.com:3000/TolaMironcenko/dns_service/pulls/4
This commit was merged in pull request #4.
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"dns_service/settings"
|
||||
"dns_service/utils"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Settings(w http.ResponseWriter, req *http.Request) {
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
respSettings, err := settings.GetSettings()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
utils.RespondWithJSON(w, respSettings, http.StatusOK)
|
||||
case "POST":
|
||||
var newSettings map[string]any
|
||||
if err := json.NewDecoder(req.Body).Decode(&newSettings); err != nil {
|
||||
http.Error(w, "invalid JSON format", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
respSettings, err := settings.SetSettings(newSettings)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
utils.RespondWithJSON(w, respSettings, http.StatusOK)
|
||||
default:
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"dns_service/utils"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
func SettingsFile(w http.ResponseWriter, req *http.Request) {
|
||||
configPath := os.Getenv("DNSMASQ_CONFIG")
|
||||
if configPath == "" {
|
||||
http.Error(w, "DNSMASQ_CONFIG environment variable not set", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
switch req.Method {
|
||||
case "GET":
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to read dnsmasq config file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write(data)
|
||||
case "POST":
|
||||
bodyBytes, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to read request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := os.WriteFile(configPath, bodyBytes, 0644); err != nil {
|
||||
http.Error(w, "failed to write dnsmasq config file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if err := utils.RestartDnsmasq(); err != nil {
|
||||
http.Error(w, "failed to restart dnsmasq", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write(bodyBytes)
|
||||
default:
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
)
|
||||
|
||||
//go:embed frontend/*
|
||||
var Frontend embed.FS
|
||||
|
||||
func SpaHandler(fs http.FileSystem) http.Handler {
|
||||
fileServer := http.FileServer(fs)
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("/frontend" + r.URL.Path)
|
||||
r.URL.Path = "/frontend" + path.Clean(r.URL.Path)
|
||||
if _, err := fs.Open(path.Clean(r.URL.Path)); err != nil {
|
||||
r.URL.Path = "/"
|
||||
}
|
||||
fileServer.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
@@ -1,111 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"dns_service/settings"
|
||||
"dns_service/utils"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"dns_service/api"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func Settings(w http.ResponseWriter, req *http.Request) {
|
||||
if req.Method == "GET" {
|
||||
resp_settings, err := settings.GetSettings()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
jsondata, err := json.Marshal(resp_settings)
|
||||
if err != nil {
|
||||
http.Error(w, "Error json marshal settings", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Fprintf(w, "%s", string(jsondata))
|
||||
} else if req.Method == "POST" {
|
||||
new_settings := make(map[string]any)
|
||||
decoder := json.NewDecoder(req.Body)
|
||||
decoder.Decode(&new_settings)
|
||||
old_settings, err := settings.GetSettings()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
resp_settings, err := settings.SetSettings(new_settings, old_settings)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
jsondata, err := json.Marshal(resp_settings)
|
||||
if err != nil {
|
||||
http.Error(w, "Error json marshal settings", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
fmt.Println(new_settings, old_settings, resp_settings)
|
||||
fmt.Fprintf(w, "%s", string(jsondata))
|
||||
}
|
||||
}
|
||||
|
||||
func SettingsFile(w http.ResponseWriter, req *http.Request) {
|
||||
if req.Method == "GET" {
|
||||
data, e := os.ReadFile(os.Getenv("DNSMASQ_CONFIG"))
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
fmt.Fprintf(w, "%s", string(data))
|
||||
} else if req.Method == "POST" {
|
||||
bodyBytes, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
http.Error(w, "Error reading request body", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
err = os.WriteFile(os.Getenv("DNSMASQ_CONFIG"), bodyBytes, 0644)
|
||||
if err != nil {
|
||||
http.Error(w, "Error writing dnsmasq config file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(bodyBytes))
|
||||
utils.RestartDnsmasq()
|
||||
exec.Command("rc", "restart", "dnsmasq").Run()
|
||||
w.Header().Add("Content-Type", "text/plain")
|
||||
fmt.Fprintf(w, "%s", string(bodyBytes))
|
||||
}
|
||||
}
|
||||
|
||||
//go:embed frontend/*
|
||||
var frontend embed.FS
|
||||
|
||||
func spaHandler(fs http.FileSystem) http.Handler {
|
||||
fileServer := http.FileServer(fs)
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("/frontend" + r.URL.Path)
|
||||
r.URL.Path = "/frontend" + path.Clean(r.URL.Path)
|
||||
if _, err := fs.Open(path.Clean(r.URL.Path)); err != nil {
|
||||
r.URL.Path = "/"
|
||||
}
|
||||
fileServer.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
err := godotenv.Load("/etc/dns_service/dns_service.conf")
|
||||
if err != nil {
|
||||
log.Fatal("Error loading .env file")
|
||||
}
|
||||
|
||||
http.Handle("/", spaHandler(http.FS(frontend)))
|
||||
http.HandleFunc("/api/settings", Settings)
|
||||
http.HandleFunc("/api/settings/file", SettingsFile)
|
||||
http.Handle("/", api.SpaHandler(http.FS(api.Frontend)))
|
||||
http.HandleFunc("/api/settings", api.Settings)
|
||||
http.HandleFunc("/api/settings/file", api.SettingsFile)
|
||||
|
||||
if os.Getenv("ENABLE_TLS") == "true" {
|
||||
http.ListenAndServeTLS(
|
||||
|
||||
+36
-17
@@ -2,32 +2,51 @@ package settings
|
||||
|
||||
import (
|
||||
"dns_service/utils"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func GetSettings() (map[string]any, error) {
|
||||
settings := make(map[string]any)
|
||||
data, e := os.ReadFile(os.Getenv("DNSMASQ_CONFIG"))
|
||||
if e != nil {
|
||||
return settings, e
|
||||
configPath := os.Getenv("DNSMASQ_CONFIG")
|
||||
if configPath == "" {
|
||||
return nil, fmt.Errorf("DNSMASQ_CONFIG environment variable not set")
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read config file at %s: %w", configPath, err)
|
||||
}
|
||||
|
||||
dnsmasqSettings := utils.DecodeSettings(string(data), "\n", "=")
|
||||
if dnsmasqSettings["cache-size"] != "" {
|
||||
cache_size, _ := strconv.ParseInt(dnsmasqSettings["cache-size"], 10, 64)
|
||||
settings["cache_size"] = cache_size
|
||||
settings := make(map[string]any)
|
||||
|
||||
// Parse cache size
|
||||
if cacheSizeStr, ok := dnsmasqSettings["cache-size"]; ok && cacheSizeStr != "" {
|
||||
cacheSize, err := strconv.ParseInt(cacheSizeStr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse cache-size '%s': %w", cacheSizeStr, err)
|
||||
}
|
||||
settings["cache_size"] = cacheSize
|
||||
}
|
||||
data, e = os.ReadFile(dnsmasqSettings["resolv-file"])
|
||||
if e != nil {
|
||||
return settings, e
|
||||
|
||||
// Read and parse resolv file
|
||||
if resolvFile, ok := dnsmasqSettings["resolv-file"]; ok {
|
||||
data, err := os.ReadFile(resolvFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read resolv-file at %s: %w", resolvFile, err)
|
||||
}
|
||||
settings["resolv"] = utils.DecodeResolvs(string(data))
|
||||
}
|
||||
resolvSettings := utils.DecodeResolvs(string(data))
|
||||
settings["resolv"] = resolvSettings
|
||||
data, e = os.ReadFile(dnsmasqSettings["addn-hosts"])
|
||||
if e != nil {
|
||||
return settings, e
|
||||
|
||||
// Read and parse hosts file
|
||||
if hostsFile, ok := dnsmasqSettings["addn-hosts"]; ok {
|
||||
data, err := os.ReadFile(hostsFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read addn-hosts file at %s: %w", hostsFile, err)
|
||||
}
|
||||
settings["hosts"] = utils.DecodeHosts(string(data))
|
||||
}
|
||||
hosts := utils.DecodeHosts(string(data))
|
||||
settings["hosts"] = hosts
|
||||
|
||||
return settings, nil
|
||||
}
|
||||
|
||||
+74
-39
@@ -7,51 +7,86 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func SetSettings(new_settings map[string]any, old_settings map[string]any) (map[string]any, error) {
|
||||
data, err := os.ReadFile(os.Getenv("DNSMASQ_CONFIG"))
|
||||
// Helper to write file content
|
||||
func writeFile(path, content string) error {
|
||||
return os.WriteFile(path, []byte(content), 0644)
|
||||
}
|
||||
|
||||
func SetSettings(new_settings map[string]any) (map[string]any, error) {
|
||||
// 1. Get config path and read the main dnsmasq configuration
|
||||
configPath := os.Getenv("DNSMASQ_CONFIG")
|
||||
if configPath == "" {
|
||||
return nil, fmt.Errorf("DNSMASQ_CONFIG environment variable not set")
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return old_settings, err
|
||||
return nil, fmt.Errorf("failed to read config file at %s: %w", configPath, err)
|
||||
}
|
||||
new_data := strings.Replace(
|
||||
string(data),
|
||||
"cache-size="+fmt.Sprintf("%v", old_settings["cache_size"]),
|
||||
"cache-size="+fmt.Sprintf("%v", new_settings["cache_size"]),
|
||||
1,
|
||||
)
|
||||
err = os.WriteFile(os.Getenv("DNSMASQ_CONFIG"), []byte(new_data), 0644)
|
||||
if err != nil {
|
||||
return old_settings, err
|
||||
}
|
||||
var resolvs string
|
||||
new_resolvs := new_settings["resolv"].([]any)
|
||||
for i := range new_resolvs {
|
||||
resolvs += "nameserver " + new_resolvs[i].(string) + "\n"
|
||||
}
|
||||
var hosts string
|
||||
new_hosts := new_settings["hosts"].(map[string]any)
|
||||
for key, value := range new_hosts {
|
||||
hosts += key
|
||||
switch value.(type) {
|
||||
case []any:
|
||||
arrvalue := value.([]any)
|
||||
for i := range arrvalue {
|
||||
hosts += " " + arrvalue[i].(string)
|
||||
dnsmasqSettings := utils.DecodeSettings(string(data), "\n", "=")
|
||||
|
||||
// 2. Update cache-size in the main config file
|
||||
if newCacheSize, ok := new_settings["cache_size"]; ok {
|
||||
oldCacheSize, oldOk := dnsmasqSettings["cache-size"]
|
||||
// Replace the line if it exists, otherwise dnsmasq uses a default
|
||||
if oldOk {
|
||||
newConfigContent := strings.Replace(
|
||||
string(data),
|
||||
"cache-size="+oldCacheSize,
|
||||
"cache-size="+fmt.Sprintf("%v", newCacheSize),
|
||||
1,
|
||||
)
|
||||
if err := writeFile(configPath, newConfigContent); err != nil {
|
||||
return nil, fmt.Errorf("failed to write updated config to %s: %w", configPath, err)
|
||||
}
|
||||
hosts += "\n"
|
||||
break
|
||||
case string:
|
||||
hosts += " " + value.(string) + "\n"
|
||||
}
|
||||
}
|
||||
fmt.Println(resolvs, hosts)
|
||||
err = os.WriteFile("/etc/dnsmasq.conf.d/resolv.conf", []byte(resolvs), 0644)
|
||||
if err != nil {
|
||||
return old_settings, err
|
||||
|
||||
// 3. Update the resolv file
|
||||
if newResolvs, ok := new_settings["resolv"].([]any); ok {
|
||||
resolvFile, ok := dnsmasqSettings["resolv-file"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("resolv-file path not found in dnsmasq config")
|
||||
}
|
||||
var b strings.Builder
|
||||
for _, v := range newResolvs {
|
||||
if s, ok := v.(string); ok {
|
||||
fmt.Fprintf(&b, "nameserver %s\n", s)
|
||||
}
|
||||
}
|
||||
if err := writeFile(resolvFile, b.String()); err != nil {
|
||||
return nil, fmt.Errorf("failed to write to resolv file %s: %w", resolvFile, err)
|
||||
}
|
||||
}
|
||||
err = os.WriteFile("/etc/dnsmasq.conf.d/hosts", []byte(hosts), 0644)
|
||||
if err != nil {
|
||||
return old_settings, err
|
||||
|
||||
// 4. Update the hosts file
|
||||
if newHosts, ok := new_settings["hosts"].(map[string]any); ok {
|
||||
hostsFile, ok := dnsmasqSettings["addn-hosts"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("addn-hosts path not found in dnsmasq config")
|
||||
}
|
||||
var b strings.Builder
|
||||
for key, value := range newHosts {
|
||||
b.WriteString(key)
|
||||
switch v := value.(type) {
|
||||
case []any:
|
||||
for _, ip := range v {
|
||||
fmt.Fprintf(&b, " %s", ip)
|
||||
}
|
||||
b.WriteString("\n")
|
||||
case string:
|
||||
fmt.Fprintf(&b, " %s\n", v)
|
||||
}
|
||||
}
|
||||
if err := writeFile(hostsFile, b.String()); err != nil {
|
||||
return nil, fmt.Errorf("failed to write to hosts file %s: %w", hostsFile, err)
|
||||
}
|
||||
}
|
||||
utils.RestartDnsmasq()
|
||||
|
||||
// 5. Restart dnsmasq to apply changes
|
||||
if err := utils.RestartDnsmasq(); err != nil {
|
||||
return nil, fmt.Errorf("failed to restart dnsmasq: %w", err)
|
||||
}
|
||||
|
||||
return new_settings, nil
|
||||
}
|
||||
|
||||
+12
-19
@@ -4,28 +4,21 @@ import "strings"
|
||||
|
||||
func DecodeHosts(input string) map[string]any {
|
||||
result := make(map[string]any)
|
||||
pairs := strings.SplitSeq(input, "\n")
|
||||
lines := strings.Split(input, "\n")
|
||||
|
||||
for pair := range pairs {
|
||||
if pair == "" {
|
||||
for _, line := range lines {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) == 0 {
|
||||
continue
|
||||
}
|
||||
kv := strings.Split(pair, " ")
|
||||
if len(kv) == 2 {
|
||||
key := strings.TrimSpace(kv[0])
|
||||
value := strings.TrimSpace(kv[1])
|
||||
if key != "" {
|
||||
result[key] = value
|
||||
}
|
||||
continue
|
||||
}
|
||||
if len(kv) != 0 {
|
||||
key := strings.TrimSpace(kv[0])
|
||||
var value []string
|
||||
for i := 1; i < len(kv); i++ {
|
||||
value = append(value, kv[i])
|
||||
}
|
||||
result[key] = value
|
||||
|
||||
key := fields[0]
|
||||
if len(fields) == 1 {
|
||||
result[key] = []string{}
|
||||
} else if len(fields) == 2 {
|
||||
result[key] = fields[1]
|
||||
} else {
|
||||
result[key] = fields[1:]
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
+8
-12
@@ -4,19 +4,15 @@ import "strings"
|
||||
|
||||
func DecodeResolvs(input string) []string {
|
||||
var result []string
|
||||
pairs := strings.SplitSeq(input, "\n")
|
||||
lines := strings.Split(input, "\n")
|
||||
|
||||
for pair := range pairs {
|
||||
if pair == "" {
|
||||
continue
|
||||
}
|
||||
kv := strings.SplitN(pair, " ", 2)
|
||||
if len(kv) != 2 {
|
||||
continue
|
||||
}
|
||||
value := strings.TrimSpace(kv[1])
|
||||
if value != "" {
|
||||
result = append(result, value)
|
||||
for _, line := range lines {
|
||||
trimmedLine := strings.TrimSpace(line)
|
||||
if strings.HasPrefix(trimmedLine, "nameserver ") {
|
||||
value := strings.TrimPrefix(trimmedLine, "nameserver ")
|
||||
if value != "" {
|
||||
result = append(result, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -4,20 +4,22 @@ import "strings"
|
||||
|
||||
func DecodeSettings(input string, pairDelim string, kvDelim string) map[string]string {
|
||||
result := make(map[string]string)
|
||||
pairs := strings.SplitSeq(input, pairDelim)
|
||||
pairs := strings.Split(input, pairDelim)
|
||||
|
||||
for pair := range pairs {
|
||||
if pair == "" {
|
||||
for _, pair := range pairs {
|
||||
trimmedPair := strings.TrimSpace(pair)
|
||||
if trimmedPair == "" {
|
||||
continue
|
||||
}
|
||||
kv := strings.SplitN(pair, kvDelim, 2)
|
||||
|
||||
kv := strings.SplitN(trimmedPair, kvDelim, 2)
|
||||
if len(kv) != 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(kv[0])
|
||||
value := strings.TrimSpace(kv[1])
|
||||
if key != "" {
|
||||
result[key] = value
|
||||
result[key] = strings.TrimSpace(kv[1])
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Helper to write JSON responses
|
||||
func RespondWithJSON(w http.ResponseWriter, data any, statusCode int) {
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to marshal JSON", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(statusCode)
|
||||
w.Write(jsonData)
|
||||
}
|
||||
@@ -1,17 +1,26 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func RestartDnsmasq() {
|
||||
func RestartDnsmasq() error {
|
||||
var cmd *exec.Cmd
|
||||
switch os.Getenv("SYSTEM_INITIALIZATION") {
|
||||
case "rc":
|
||||
exec.Command("rc", "restart", "dnsmasq").Run()
|
||||
cmd = exec.Command("rc", "restart", "dnsmasq")
|
||||
case "openrc":
|
||||
exec.Command("rc-service", "dnsmasq", "restart").Run()
|
||||
cmd = exec.Command("rc-service", "dnsmasq", "restart")
|
||||
case "systemd":
|
||||
exec.Command("systemctl", "restart", "dnsmasq").Run()
|
||||
cmd = exec.Command("systemctl", "restart", "dnsmasq")
|
||||
default:
|
||||
return fmt.Errorf("unsupported system initialization: %s", os.Getenv("SYSTEM_INITIALIZATION"))
|
||||
}
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to restart dnsmasq: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user