Files
aura-crm/core/backend/internal/staff/bootstrap.go
T

49 lines
1.4 KiB
Go

package staff
import (
"context"
"fmt"
"os"
"github.com/jackc/pgx/v5/pgxpool"
"golang.org/x/crypto/bcrypt"
)
// EnsureOwner creates the first owner account from OWNER_NAME/OWNER_EMAIL/OWNER_PASSWORD
// env vars if staff_users is empty. It is the only way to bootstrap access, since there
// is no public registration. No-op (with a log via returned error being nil) once any
// staff account exists.
func EnsureOwner(ctx context.Context, db *pgxpool.Pool) error {
var count int
if err := db.QueryRow(ctx, `SELECT count(*) FROM staff_users`).Scan(&count); err != nil {
return fmt.Errorf("count staff_users: %w", err)
}
if count > 0 {
return nil
}
name := os.Getenv("OWNER_NAME")
email := os.Getenv("OWNER_EMAIL")
password := os.Getenv("OWNER_PASSWORD")
if name == "" || email == "" || password == "" {
return fmt.Errorf("staff_users is empty and OWNER_NAME/OWNER_EMAIL/OWNER_PASSWORD are not all set — cannot bootstrap the first account")
}
if len(password) < 8 {
return fmt.Errorf("OWNER_PASSWORD must be at least 8 characters")
}
hash, err := bcrypt.GenerateFromPassword([]byte(password), 12)
if err != nil {
return fmt.Errorf("bcrypt: %w", err)
}
_, err = db.Exec(ctx,
`INSERT INTO staff_users (name, email, password_hash, role) VALUES ($1, $2, $3, 'owner')`,
name, email, string(hash),
)
if err != nil {
return fmt.Errorf("insert owner: %w", err)
}
return nil
}