Fix go version
This commit is contained in:
@@ -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
113
main.go
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user