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 <cursoragent@cursor.com>
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user