From 0ebcabf7dfefd9b768137341029b7e1a3ee3ee01 Mon Sep 17 00:00:00 2001 From: weichen Date: Tue, 22 Sep 2026 18:18:33 +0800 Subject: [PATCH] fix(auth): tolerate concurrent demo-user seed unique conflicts Parallel NewServer bootstrap raced with goose fixed-id user rows and sequence drift, panicking the server package on wangzong insert. Treat unique violations as success when the demo username exists (or retry once). Co-authored-by: Cursor --- internal/auth/bootstrap.go | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/internal/auth/bootstrap.go b/internal/auth/bootstrap.go index 5b538fa6..c1c92870 100644 --- a/internal/auth/bootstrap.go +++ b/internal/auth/bootstrap.go @@ -2,13 +2,25 @@ package auth import ( "context" + "errors" "fmt" "os" + "strings" "chat-agent-system/internal/config" + "github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgxpool" ) +func isUniqueViolation(err error) bool { + var pgErr *pgconn.PgError + if errors.As(err, &pgErr) && pgErr.Code == "23505" { + return true + } + msg := err.Error() + return strings.Contains(msg, "23505") || strings.Contains(msg, "duplicate key") +} + // BootstrapAdminFromConfig 确保配置的管理员账户在 PG 中存在且具备 role-admin。 func BootstrapAdminFromConfig(ctx context.Context, store AuthStore, cfg *config.Config) error { if store == nil || cfg == nil { @@ -102,6 +114,28 @@ func BootstrapDevSeedUsers(ctx context.Context, store AuthStore, cfg *config.Con EmployeeID: su.empID, }) if err != nil { + // Concurrent NewServer bootstrap and sequence drift after fixed-id + // goose seeds can race on users_pkey/username. If the demo user + // exists afterwards, treat the seed as successful. + if _, getErr := store.GetUserByUsername(ctx, su.username); getErr == nil { + continue + } + if isUniqueViolation(err) { + if _, retryErr := store.CreateUser(ctx, &UserAccount{ + Username: su.username, + Name: su.name, + PasswordHash: passHash, + Status: StatusActive, + RoleID: su.roleID, + Position: su.position, + Department: su.dept, + EmployeeID: su.empID, + }); retryErr == nil { + continue + } else if _, getErr := store.GetUserByUsername(ctx, su.username); getErr == nil { + continue + } + } return fmt.Errorf("seed user %s: %w", su.username, err) } }