From 26777dbb89a460391b602c493e672e3b66a8c077 Mon Sep 17 00:00:00 2001 From: penguinehis Date: Fri, 29 May 2026 17:05:44 -0300 Subject: [PATCH] Fix test --- main.go | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/main.go b/main.go index d32d4b6..1718e06 100644 --- a/main.go +++ b/main.go @@ -485,6 +485,7 @@ func (a *App) handleDragonCreate(w http.ResponseWriter, r *http.Request) { Password string `json:"password"` UUID string `json:"uuid"` Days int `json:"days"` + Hours int `json:"hours"` Minutes int `json:"minutes"` MaxConnections int `json:"max_connections"` WithXray bool `json:"with_xray"` @@ -497,6 +498,9 @@ func (a *App) handleDragonCreate(w http.ResponseWriter, r *http.Request) { if p.Minutes > 0 { t := time.Now().Add(time.Duration(p.Minutes) * time.Minute) exp = &t + } else if p.Hours > 0 { + t := time.Now().Add(time.Duration(p.Hours) * time.Hour) + exp = &t } else if p.Days > 0 { t := time.Now().AddDate(0, 0, p.Days) exp = &t @@ -571,15 +575,12 @@ func (a *App) createSSH(username, password string, limit int, expiresAt *time.Ti } args := []string{"-M", "-s", "/bin/false", "-p", hash} if expiresAt != nil { - linuxExpiry := *expiresAt - if linuxExpiry.Before(time.Now().Add(24 * time.Hour)) { - linuxExpiry = time.Now().AddDate(0, 0, 2) - } + linuxExpiry := linuxExpiryForAccount(*expiresAt) args = append(args, "-e", linuxExpiry.Format("2006-01-02")) } args = append(args, username) - if out, err := exec.Command("useradd", args...).CombinedOutput(); err != nil { - return fmt.Errorf("useradd: %v: %s", err, strings.TrimSpace(string(out))) + if err := runUserAdd(args); err != nil { + return err } if err := writeCompatUserFiles(username, password, limit); err != nil { log.Printf("compat files: %v", err) @@ -595,6 +596,46 @@ func (a *App) createSSH(username, password string, limit int, expiresAt *time.Ti return a.store.saveLocked() } +func linuxExpiryForAccount(expiresAt time.Time) time.Time { + // Linux account expiry is date-only and cannot safely represent accounts that + // expire in minutes or hours. For those short tests, keep the system account + // valid for seven days and let the bridge SQLite expiry loop delete it at the + // exact minute/hour. + if expiresAt.Before(time.Now().Add(24 * time.Hour)) { + return time.Now().AddDate(0, 0, 7) + } + return expiresAt +} + +func runUserAdd(args []string) error { + out, err := exec.Command("useradd", args...).CombinedOutput() + if err == nil { + return nil + } + firstErr := fmt.Errorf("useradd: %v: %s", err, strings.TrimSpace(string(out))) + text := strings.ToLower(string(out)) + nameRejected := strings.Contains(text, "invalid user name") || + strings.Contains(text, "invalid username") || + strings.Contains(text, "bad name") || + strings.Contains(text, "does not match") + if !nameRejected { + return firstErr + } + + // DragonCore test usernames can start with numbers, for example 820etl. + // Some Linux distributions reject those by default. Debian/Ubuntu shadow-utils + // accept --badname; a few adduser wrappers use --force-badname. Try both as a + // compatibility fallback, keeping the same password, shell and expiry args. + for _, opt := range []string{"--badname", "--force-badname"} { + retryArgs := append([]string{opt}, args...) + out, err = exec.Command("useradd", retryArgs...).CombinedOutput() + if err == nil { + return nil + } + } + return firstErr +} + func (a *App) deleteSSH(username, uuid string) error { if username == "" { return fmt.Errorf("username required")