Fix go version

This commit is contained in:
2026-05-27 14:37:13 -03:00
parent 6f7fdef746
commit 096a8275be
3 changed files with 120 additions and 61 deletions

2
go.mod
View File

@@ -1,3 +1,3 @@
module dragoncore-bridge
go 1.21
go 1.13

View File

@@ -28,17 +28,75 @@ fi
need_cmd() { command -v "$1" >/dev/null 2>&1; }
rand_hex() { openssl rand -hex "$1"; }
# The bridge source is intentionally Go 1.13-compatible so old VPS images
# such as Ubuntu 20.04 can compile it with their distro package.
# If the distro Go is older than 1.13, install a known-good official Go.
GO_MIN_MAJOR=1
GO_MIN_MINOR=13
GO_VERSION="${GO_VERSION:-1.21.13}"
GO_BIN=""
go_version_ok() {
if ! need_cmd go; then
return 1
fi
ver="$(go version 2>/dev/null | awk '{print $3}' | sed 's/^go//; s/[^0-9.].*$//')"
major="$(printf '%s' "$ver" | cut -d. -f1)"
minor="$(printf '%s' "$ver" | cut -d. -f2)"
case "$major:$minor" in
*[!0-9:]*|:*) return 1 ;;
esac
if [ "$major" -gt "$GO_MIN_MAJOR" ]; then
return 0
fi
if [ "$major" -eq "$GO_MIN_MAJOR" ] && [ "$minor" -ge "$GO_MIN_MINOR" ]; then
return 0
fi
return 1
}
install_official_go() {
arch="$(uname -m)"
case "$arch" in
x86_64|amd64) goarch="amd64" ;;
aarch64|arm64) goarch="arm64" ;;
armv6l|armv7l) goarch="armv6l" ;;
i386|i686) goarch="386" ;;
*) echo "Unsupported CPU architecture for automatic Go install: $arch"; exit 1 ;;
esac
url="https://go.dev/dl/go${GO_VERSION}.linux-${goarch}.tar.gz"
tmp="/tmp/go${GO_VERSION}.linux-${goarch}.tar.gz"
echo "Installing Go ${GO_VERSION} from ${url} ..."
curl -fsSL "$url" -o "$tmp"
rm -rf /usr/local/go
tar -C /usr/local -xzf "$tmp"
ln -sf /usr/local/go/bin/go /usr/local/bin/go
ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt
GO_BIN="/usr/local/go/bin/go"
}
export DEBIAN_FRONTEND=noninteractive
if need_cmd apt-get; then
apt-get update -y
apt-get install -y ca-certificates curl wget git openssl build-essential
if ! need_cmd go; then
apt-get install -y golang-go
apt-get install -y golang-go || true
fi
elif need_cmd yum; then
yum install -y ca-certificates curl wget git openssl gcc make golang
yum install -y ca-certificates curl wget git openssl gcc make golang || yum install -y ca-certificates curl wget git openssl gcc make
elif need_cmd dnf; then
dnf install -y ca-certificates curl wget git openssl gcc make golang
dnf install -y ca-certificates curl wget git openssl gcc make golang || dnf install -y ca-certificates curl wget git openssl gcc make
fi
if go_version_ok; then
GO_BIN="$(command -v go)"
else
install_official_go
fi
if ! "$GO_BIN" version >/dev/null 2>&1; then
echo "Go installation failed. Cannot build dragoncore-bridge."
exit 1
fi
mkdir -p "$INSTALL_DIR" "$CONFIG_DIR"
@@ -56,7 +114,7 @@ else
fi
cd "$INSTALL_DIR/src"
go build -trimpath -ldflags "-s -w" -o "$BIN" .
"$GO_BIN" build -trimpath -ldflags "-s -w" -o "$BIN" .
chmod 755 "$BIN"
USER_NAME="admin"

113
main.go
View File

@@ -8,6 +8,7 @@ import (
"errors"
"flag"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
@@ -121,7 +122,7 @@ func getenv(k, def string) string {
}
func loadConfig(path string) (Config, error) {
b, err := os.ReadFile(path)
b, err := ioutil.ReadFile(path)
if err != nil {
return Config{}, err
}
@@ -134,7 +135,7 @@ func loadConfig(path string) (Config, error) {
func loadStore(path string) (*Store, error) {
st := &Store{path: path, Accounts: map[string]Account{}}
b, err := os.ReadFile(path)
b, err := ioutil.ReadFile(path)
if errors.Is(err, os.ErrNotExist) {
return st, nil
}
@@ -157,7 +158,7 @@ func loadStore(path string) (*Store, error) {
func (s *Store) saveLocked() error {
tmp := s.path + ".tmp"
b, _ := json.MarshalIndent(s, "", " ")
if err := os.WriteFile(tmp, b, 0600); err != nil {
if err := ioutil.WriteFile(tmp, b, 0600); err != nil {
return err
}
return os.Rename(tmp, s.path)
@@ -165,7 +166,7 @@ func (s *Store) saveLocked() error {
func randToken(n int) string { b := make([]byte, n); _, _ = rand.Read(b); return hex.EncodeToString(b) }
func writeJSON(w http.ResponseWriter, v any) {
func writeJSON(w http.ResponseWriter, v interface{}) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(v)
}
@@ -189,7 +190,7 @@ func (a *App) handleLogin(w http.ResponseWriter, r *http.Request) {
a.sessMu.Lock()
a.sessions[tok] = time.Now().Add(12 * time.Hour)
a.sessMu.Unlock()
writeJSON(w, map[string]any{"token": tok, "username": a.cfg.Username, "role": "admin"})
writeJSON(w, map[string]interface{}{"token": tok, "username": a.cfg.Username, "role": "admin"})
}
func (a *App) auth(next http.Handler) http.Handler {
@@ -215,7 +216,7 @@ func (a *App) auth(next http.Handler) http.Handler {
}
func (a *App) handleMe(w http.ResponseWriter, r *http.Request) {
writeJSON(w, map[string]any{"username": a.cfg.Username, "role": "admin"})
writeJSON(w, map[string]interface{}{"username": a.cfg.Username, "role": "admin"})
}
func (a *App) handleUsers(w http.ResponseWriter, r *http.Request) {
@@ -303,7 +304,7 @@ func (a *App) handleDragonCreate(w http.ResponseWriter, r *http.Request) {
errText(w, 400, err.Error())
return
}
writeJSON(w, map[string]any{"ok": true})
writeJSON(w, map[string]interface{}{"ok": true})
}
func (a *App) handleDragonDelete(w http.ResponseWriter, r *http.Request) {
@@ -322,7 +323,7 @@ func (a *App) handleDragonDelete(w http.ResponseWriter, r *http.Request) {
errText(w, 400, err.Error())
return
}
writeJSON(w, map[string]any{"ok": true})
writeJSON(w, map[string]interface{}{"ok": true})
}
func (a *App) handleDragonSync(w http.ResponseWriter, r *http.Request) {
@@ -352,7 +353,7 @@ func (a *App) handleDragonSync(w http.ResponseWriter, r *http.Request) {
ok++
}
}
writeJSON(w, map[string]any{"ok": ok, "fail": fail})
writeJSON(w, map[string]interface{}{"ok": ok, "fail": fail})
}
func (a *App) createSSH(username, password string, limit int, expiresAt *time.Time, uuid string, withXray bool) error {
@@ -437,7 +438,7 @@ func passwordHash(password string) (string, error) {
func writeCompatUserFiles(username, password string, limit int) error {
_ = os.MkdirAll("/etc/SSHPlus/senha", 0755)
_ = os.WriteFile("/etc/SSHPlus/senha/"+username, []byte(password+"\n"), 0600)
_ = ioutil.WriteFile("/etc/SSHPlus/senha/"+username, []byte(password+"\n"), 0600)
upsertLine("/root/usuarios.db", username, fmt.Sprintf("%s %d", username, limit))
if _, err := os.Stat("/opt/DragonCore/menu.php"); err == nil {
_ = exec.Command("php", "/opt/DragonCore/menu.php", "deleteData", username).Run()
@@ -464,7 +465,7 @@ func upsertLine(path, prefix, line string) {
}
}
func removeLinePrefix(path, prefix string) {
b, err := os.ReadFile(path)
b, err := ioutil.ReadFile(path)
if err != nil {
return
}
@@ -478,7 +479,7 @@ func removeLinePrefix(path, prefix string) {
}
out = append(out, l)
}
_ = os.WriteFile(path, []byte(strings.Join(out, "\n")+"\n"), 0644)
_ = ioutil.WriteFile(path, []byte(strings.Join(out, "\n")+"\n"), 0644)
}
func (a *App) expiryLoop() {
@@ -500,12 +501,12 @@ func (a *App) expiryLoop() {
}
}
func listSystemUsers(st *Store) []map[string]any {
func listSystemUsers(st *Store) []map[string]interface{} {
st.mu.Lock()
defer st.mu.Unlock()
out := make([]map[string]any, 0, len(st.Accounts))
out := make([]map[string]interface{}, 0, len(st.Accounts))
for _, ac := range st.Accounts {
out = append(out, map[string]any{"username": ac.Username, "active_conns": activeProcCount(ac.Username), "max_connections": ac.MaxConnections, "expires_at": ac.ExpiresAt, "uuid": ac.UUID})
out = append(out, map[string]interface{}{"username": ac.Username, "active_conns": activeProcCount(ac.Username), "max_connections": ac.MaxConnections, "expires_at": ac.ExpiresAt, "uuid": ac.UUID})
}
sort.Slice(out, func(i, j int) bool { return fmt.Sprint(out[i]["username"]) < fmt.Sprint(out[j]["username"]) })
return out
@@ -541,22 +542,22 @@ func parseTimeMaybe(s string) (*time.Time, error) {
func xrayConfigPaths() []string {
return []string{"/usr/local/etc/xray/config.json", "/etc/xray/config.json", "/etc/v2ray/config.json"}
}
func readJSONFile(path string) (map[string]any, error) {
b, err := os.ReadFile(path)
func readJSONFile(path string) (map[string]interface{}, error) {
b, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
var m map[string]any
var m map[string]interface{}
err = json.Unmarshal(b, &m)
return m, err
}
func writeJSONFile(path string, m map[string]any) error {
func writeJSONFile(path string, m map[string]interface{}) error {
b, err := json.MarshalIndent(m, "", " ")
if err != nil {
return err
}
tmp := path + ".tmp"
if err := os.WriteFile(tmp, b, 0644); err != nil {
if err := ioutil.WriteFile(tmp, b, 0644); err != nil {
return err
}
return os.Rename(tmp, path)
@@ -572,28 +573,28 @@ func addXrayClientAll(uuid, email string) error {
if err != nil {
return err
}
inb, _ := cfg["inbounds"].([]any)
inb, _ := cfg["inbounds"].([]interface{})
fileChanged := false
for _, it := range inb {
im, ok := it.(map[string]any)
im, ok := it.(map[string]interface{})
if !ok || fmt.Sprint(im["protocol"]) != "vless" {
continue
}
settings, _ := im["settings"].(map[string]any)
settings, _ := im["settings"].(map[string]interface{})
if settings == nil {
settings = map[string]any{}
settings = map[string]interface{}{}
im["settings"] = settings
}
clients, _ := settings["clients"].([]any)
clients, _ := settings["clients"].([]interface{})
exists := false
for _, c := range clients {
cm, _ := c.(map[string]any)
cm, _ := c.(map[string]interface{})
if fmt.Sprint(cm["id"]) == uuid || fmt.Sprint(cm["email"]) == email {
exists = true
}
}
if !exists {
settings["clients"] = append(clients, map[string]any{"id": uuid, "alterId": 0, "email": email})
settings["clients"] = append(clients, map[string]interface{}{"id": uuid, "alterId": 0, "email": email})
fileChanged = true
}
}
@@ -620,21 +621,21 @@ func removeXrayClientAll(uuid string) error {
if err != nil {
return err
}
inb, _ := cfg["inbounds"].([]any)
inb, _ := cfg["inbounds"].([]interface{})
fileChanged := false
for _, it := range inb {
im, ok := it.(map[string]any)
im, ok := it.(map[string]interface{})
if !ok || fmt.Sprint(im["protocol"]) != "vless" {
continue
}
settings, _ := im["settings"].(map[string]any)
settings, _ := im["settings"].(map[string]interface{})
if settings == nil {
continue
}
clients, _ := settings["clients"].([]any)
out := make([]any, 0, len(clients))
clients, _ := settings["clients"].([]interface{})
out := make([]interface{}, 0, len(clients))
for _, c := range clients {
cm, _ := c.(map[string]any)
cm, _ := c.(map[string]interface{})
if fmt.Sprint(cm["id"]) == uuid {
fileChanged = true
continue
@@ -663,9 +664,9 @@ func restartXray() {
func (a *App) handleXrayInbounds(w http.ResponseWriter, r *http.Request) {
type Inb struct {
Tag string `json:"tag"`
Protocol string `json:"protocol"`
Clients []map[string]any `json:"clients"`
Tag string `json:"tag"`
Protocol string `json:"protocol"`
Clients []map[string]interface{} `json:"clients"`
}
var out []Inb
for _, path := range xrayConfigPaths() {
@@ -673,19 +674,19 @@ func (a *App) handleXrayInbounds(w http.ResponseWriter, r *http.Request) {
if err != nil {
continue
}
inb, _ := cfg["inbounds"].([]any)
inb, _ := cfg["inbounds"].([]interface{})
for i, it := range inb {
im, _ := it.(map[string]any)
im, _ := it.(map[string]interface{})
proto := fmt.Sprint(im["protocol"])
tag := fmt.Sprint(im["tag"])
if tag == "<nil>" || tag == "" {
tag = fmt.Sprintf("%s-%d", proto, i)
}
settings, _ := im["settings"].(map[string]any)
clientsAny, _ := settings["clients"].([]any)
clients := []map[string]any{}
settings, _ := im["settings"].(map[string]interface{})
clientsAny, _ := settings["clients"].([]interface{})
clients := []map[string]interface{}{}
for _, c := range clientsAny {
if cm, ok := c.(map[string]any); ok {
if cm, ok := c.(map[string]interface{}); ok {
clients = append(clients, cm)
}
}
@@ -736,7 +737,7 @@ func (a *App) handleStats(w http.ResponseWriter, r *http.Request) { writeJSON(w,
type cpuSample struct{ idle, total uint64 }
func readCPU() cpuSample {
b, _ := os.ReadFile("/proc/stat")
b, _ := ioutil.ReadFile("/proc/stat")
fields := strings.Fields(strings.SplitN(string(b), "\n", 2)[0])
var vals []uint64
for _, f := range fields[1:] {
@@ -753,7 +754,7 @@ func readCPU() cpuSample {
}
return cpuSample{idle, total}
}
func collectStats() map[string]any {
func collectStats() map[string]interface{} {
c1 := readCPU()
time.Sleep(150 * time.Millisecond)
c2 := readCPU()
@@ -762,10 +763,10 @@ func collectStats() map[string]any {
cpu = 100 * (1 - float64(c2.idle-c1.idle)/float64(c2.total-c1.total))
}
mem := readMem()
return map[string]any{"cpu_percent": cpu, "mem_total_bytes": mem["total"], "mem_used_bytes": mem["used"], "mem_avail_bytes": mem["avail"], "mem_percent": mem["percent"], "interfaces": readNet()}
return map[string]interface{}{"cpu_percent": cpu, "mem_total_bytes": mem["total"], "mem_used_bytes": mem["used"], "mem_avail_bytes": mem["avail"], "mem_percent": mem["percent"], "interfaces": readNet()}
}
func readMem() map[string]any {
b, _ := os.ReadFile("/proc/meminfo")
func readMem() map[string]interface{} {
b, _ := ioutil.ReadFile("/proc/meminfo")
vals := map[string]uint64{}
for _, l := range strings.Split(string(b), "\n") {
f := strings.Fields(l)
@@ -782,11 +783,11 @@ func readMem() map[string]any {
used = total - avail
pct = 100 * float64(used) / float64(total)
}
return map[string]any{"total": total, "used": used, "avail": avail, "percent": pct}
return map[string]interface{}{"total": total, "used": used, "avail": avail, "percent": pct}
}
func readNet() []map[string]any {
b, _ := os.ReadFile("/proc/net/dev")
var out []map[string]any
func readNet() []map[string]interface{} {
b, _ := ioutil.ReadFile("/proc/net/dev")
var out []map[string]interface{}
for _, l := range strings.Split(string(b), "\n") {
if !strings.Contains(l, ":") {
continue
@@ -800,7 +801,7 @@ func readNet() []map[string]any {
if len(f) >= 16 {
rx, _ := strconv.ParseUint(f[0], 10, 64)
tx, _ := strconv.ParseUint(f[8], 10, 64)
out = append(out, map[string]any{"name": name, "rx_bytes": rx, "tx_bytes": tx})
out = append(out, map[string]interface{}{"name": name, "rx_bytes": rx, "tx_bytes": tx})
}
}
return out
@@ -808,20 +809,20 @@ func readNet() []map[string]any {
func (a *App) handleReboot(w http.ResponseWriter, r *http.Request) {
go exec.Command("shutdown", "-r", "+1", "DragonCore bridge reboot requested").Run()
writeJSON(w, map[string]any{"ok": true})
writeJSON(w, map[string]interface{}{"ok": true})
}
func (a *App) handleRestartSSH(w http.ResponseWriter, r *http.Request) {
_ = exec.Command("systemctl", "restart", "ssh").Run()
_ = exec.Command("systemctl", "restart", "sshd").Run()
writeJSON(w, map[string]any{"ok": true})
writeJSON(w, map[string]interface{}{"ok": true})
}
func (a *App) handleCleanup(w http.ResponseWriter, r *http.Request) {
_ = exec.Command("sh", "-c", "apt-get clean 2>/dev/null; journalctl --vacuum-time=3d 2>/dev/null; rm -rf /tmp/* 2>/dev/null").Run()
writeJSON(w, map[string]any{"ok": true})
writeJSON(w, map[string]interface{}{"ok": true})
}
func (a *App) handleXrayFix(w http.ResponseWriter, r *http.Request) {
restartXray()
writeJSON(w, map[string]any{"ok": true})
writeJSON(w, map[string]interface{}{"ok": true})
}
func (a *App) handleLegacyCommand(w http.ResponseWriter, r *http.Request) {