package users import ( "context" "fmt" "strings" "github.com/jackc/pgx/v5/pgxpool" "github.com/ultisuite/ulti-backend/internal/auth" ) // ProvisionEmail returns the email stored for a newly provisioned user. func ProvisionEmail(claims *auth.Claims) string { if claims == nil { return "" } email := strings.TrimSpace(claims.Email) if email != "" { return email } return claims.Sub + "@unknown.ultimail.local" } // EnsureUser inserts or updates the Ultimail user row for OIDC claims and returns the internal UUID. func EnsureUser(ctx context.Context, db *pgxpool.Pool, claims *auth.Claims) (string, error) { if db == nil { return "", fmt.Errorf("database not configured") } if claims == nil || strings.TrimSpace(claims.Sub) == "" { return "", fmt.Errorf("missing subject claim") } email := ProvisionEmail(claims) name := strings.TrimSpace(claims.Name) var userCount int64 if err := db.QueryRow(ctx, `SELECT COUNT(*) FROM users`).Scan(&userCount); err != nil { return "", fmt.Errorf("count users: %w", err) } isFirstUser := userCount == 0 var userID string err := db.QueryRow(ctx, ` INSERT INTO users (external_id, email, name, platform_admin) VALUES ($1, $2, $3, $4) ON CONFLICT (external_id) DO UPDATE SET email = EXCLUDED.email, name = EXCLUDED.name, updated_at = NOW() RETURNING id `, claims.Sub, email, name, isFirstUser).Scan(&userID) if err != nil { return "", fmt.Errorf("provision user: %w", err) } if isFirstUser { if err := bootstrapFirstUserAdmin(ctx, db, userID); err != nil { return "", fmt.Errorf("bootstrap platform admin: %w", err) } } return userID, nil }