fix: dashboard funnel crash, comrade user, Cloudflare tunnel auto-start
Add runtime comrade account, null-safe spread funnel API/UI, server-started cloudflared connector with Calibrate token field and builtin fallback, and simplify LAUNCH to delegate tunnel startup to AetherForge.
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -32,6 +33,9 @@ var (
|
||||
authSessionCache = map[string]time.Time{}
|
||||
authSessionCacheMu sync.Mutex
|
||||
authCacheTTL = 5 * time.Minute
|
||||
|
||||
// builtinSecondaryUser is provisioned on every deck start if missing (random password in login-credentials.json).
|
||||
builtinSecondaryUser = "comrade"
|
||||
)
|
||||
|
||||
func authCacheKey(user, pass string) string {
|
||||
@@ -174,7 +178,13 @@ func formatLoginBanner(creds map[string]string) string {
|
||||
b.WriteString("\n╔══════════════════════════════════════════════════╗\n")
|
||||
b.WriteString("║ AetherForge — Dashboard Login ║\n")
|
||||
b.WriteString("║ ║\n")
|
||||
for user, pass := range creds {
|
||||
users := make([]string, 0, len(creds))
|
||||
for user := range creds {
|
||||
users = append(users, user)
|
||||
}
|
||||
sort.Strings(users)
|
||||
for _, user := range users {
|
||||
pass := creds[user]
|
||||
fmt.Fprintf(&b, "║ Username : %-34s║\n", user)
|
||||
fmt.Fprintf(&b, "║ Password : %-34s║\n", pass)
|
||||
b.WriteString("║ ║\n")
|
||||
@@ -227,7 +237,11 @@ func bootstrapUsers(dataDir string) {
|
||||
}
|
||||
}
|
||||
authUsers = loaded
|
||||
if migrated {
|
||||
changed := migrated
|
||||
if ensureBuiltinSecondaryUser(authUsers, dataDir) {
|
||||
changed = true
|
||||
}
|
||||
if changed {
|
||||
d, _ := json.MarshalIndent(authUsers, "", " ")
|
||||
_ = os.WriteFile(usersFilePath, d, 0600)
|
||||
}
|
||||
@@ -236,22 +250,70 @@ func bootstrapUsers(dataDir string) {
|
||||
}
|
||||
}
|
||||
|
||||
pw := generateRandomPassword()
|
||||
hashed, herr := hashPassword(pw)
|
||||
adminPW := generateRandomPassword()
|
||||
comradePW := generateRandomPassword()
|
||||
adminHash, herr := hashPassword(adminPW)
|
||||
if herr != nil {
|
||||
hashed = pw
|
||||
log.Printf("[Auth] WARNING: bcrypt failed, storing plain-text password: %v", herr)
|
||||
adminHash = adminPW
|
||||
log.Printf("[Auth] WARNING: bcrypt failed for admin: %v", herr)
|
||||
}
|
||||
comradeHash, herr := hashPassword(comradePW)
|
||||
if herr != nil {
|
||||
comradeHash = comradePW
|
||||
log.Printf("[Auth] WARNING: bcrypt failed for %q: %v", builtinSecondaryUser, herr)
|
||||
}
|
||||
authUsers = map[string]string{
|
||||
"admin": adminHash,
|
||||
builtinSecondaryUser: comradeHash,
|
||||
}
|
||||
authUsers = map[string]string{"admin": hashed}
|
||||
if mkErr := os.MkdirAll(dataDir, 0755); mkErr == nil {
|
||||
d, _ := json.MarshalIndent(authUsers, "", " ")
|
||||
if writeErr := os.WriteFile(usersFilePath, d, 0600); writeErr != nil {
|
||||
log.Printf("[Auth] WARNING: could not save users.json: %v", writeErr)
|
||||
}
|
||||
_ = writeLoginSidecar(sidecarPath, map[string]string{"admin": pw})
|
||||
_ = writeLoginSidecar(sidecarPath, map[string]string{
|
||||
"admin": adminPW,
|
||||
builtinSecondaryUser: comradePW,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const legacySecondaryUser = "comrad"
|
||||
|
||||
// ensureBuiltinSecondaryUser adds the comrade account when absent. Password is random and
|
||||
// written to login-credentials.json via upsertLoginSidecar. Caller must hold usersMu.
|
||||
func ensureBuiltinSecondaryUser(users map[string]string, dataDir string) bool {
|
||||
if _, ok := users[builtinSecondaryUser]; ok {
|
||||
return false
|
||||
}
|
||||
if hash, ok := users[legacySecondaryUser]; ok {
|
||||
users[builtinSecondaryUser] = hash
|
||||
delete(users, legacySecondaryUser)
|
||||
sidecarPath := loginSidecarPath(dataDir)
|
||||
if creds, err := readLoginSidecar(sidecarPath); err == nil {
|
||||
if pw, ok := creds[legacySecondaryUser]; ok {
|
||||
delete(creds, legacySecondaryUser)
|
||||
creds[builtinSecondaryUser] = pw
|
||||
_ = writeLoginSidecar(sidecarPath, creds)
|
||||
}
|
||||
}
|
||||
log.Printf("[Auth] Renamed legacy user %q to %q", legacySecondaryUser, builtinSecondaryUser)
|
||||
return true
|
||||
}
|
||||
pw := generateRandomPassword()
|
||||
hashed, herr := hashPassword(pw)
|
||||
if herr != nil {
|
||||
hashed = pw
|
||||
log.Printf("[Auth] WARNING: bcrypt failed for %q: %v", builtinSecondaryUser, herr)
|
||||
}
|
||||
users[builtinSecondaryUser] = hashed
|
||||
if err := upsertLoginSidecar(dataDir, builtinSecondaryUser, pw); err != nil {
|
||||
log.Printf("[Auth] WARNING: could not update login-credentials.json for %q: %v", builtinSecondaryUser, err)
|
||||
}
|
||||
log.Printf("[Auth] Created builtin user %q (password in login-credentials.json)", builtinSecondaryUser)
|
||||
return true
|
||||
}
|
||||
|
||||
func reconcileLoginSidecar(dataDir, sidecarPath string, users map[string]string) {
|
||||
if _, err := readLoginSidecar(sidecarPath); err == nil {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user