fix(cli): stop -getApiToken accumulating admin tokens (#6175)

* fix(cli): stop -getApiToken accumulating admin tokens

`x-ui setting -getApiToken` reads like a getter, but when tokens already exist
it minted a brand-new one named `cli-fallback-<unix>` on every invocation. The
plaintext is printed once and the row stays enabled forever, so an operator who
runs the command a few times while debugging silently leaves several
admin-equivalent credentials behind that nobody can tell apart or revoke
knowingly.

Keep the convenience the fallback was added for, but rotate a single
`cli-fallback` token instead: RecreateByName drops any existing row with that
name before issuing a new one, so at most one CLI-issued token exists at a time
and the previous plaintext stops working.

* fix(api-token): preserve token on failed replacement

---------

Co-authored-by: n0ctal <293235942+n0ctal@users.noreply.github.com>
This commit is contained in:
n0ctal
2026-08-14 23:12:43 +05:00
committed by GitHub
parent 17fea2f656
commit 34c248bb79
3 changed files with 108 additions and 6 deletions
+8 -5
View File
@@ -12,7 +12,6 @@ import (
"os"
"os/signal"
"syscall"
"time"
_ "unsafe"
"github.com/mhsanaei/3x-ui/v3/internal/config"
@@ -32,6 +31,10 @@ import (
"github.com/op/go-logging"
)
// cliFallbackTokenName is the single token the CLI regenerates, so `-getApiToken`
// cannot accumulate admin-equivalent credentials that are never revoked.
const cliFallbackTokenName = "cli-fallback"
// runWebServer initializes and starts the web server for the 3x-ui panel.
func runWebServer() {
log.Printf("Starting %v %v", config.GetName(), config.GetPanelVersion())
@@ -455,14 +458,14 @@ func GetApiToken(getApiToken bool) {
fmt.Printf("There are %d API token(s) configured. Existing tokens cannot be retrieved in plaintext because only hashes are stored.\n", len(tokens))
fmt.Println("If you have lost your token, you can manage and generate new tokens through the Panel UI (Settings -> API Tokens).")
// Create a new fallback token so the CLI is still useful without the UI
fallbackName := fmt.Sprintf("cli-fallback-%d", time.Now().Unix())
created, err := apiTokenService.Create(fallbackName)
// Rotate one reusable fallback so repeated calls cannot pile up
// indefinitely many admin-equivalent tokens that never expire.
created, err := apiTokenService.RecreateByName(cliFallbackTokenName)
if err != nil {
fmt.Println("Failed to create a fallback API token:", err)
return
}
fmt.Println("\nA new fallback token has been generated for your convenience:")
fmt.Println("\nThe CLI fallback token has been regenerated (any previous one is now invalid):")
fmt.Println("apiToken:", created.Token)
return
}