49 lines
1.4 KiB
Go
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
|
|
}
|