PrintToVeraeTimeStamp/internal/auth/setup_darwin.go

106 lines
3.2 KiB
Go

//go:build darwin
package auth
import (
"fmt"
"os"
"os/exec"
"strings"
)
// RunFirstRunSetup guides the user through customer.verae.ai signup and
// xVerae- signing-key paste. Interactive (osascript dialogs).
func RunFirstRunSetup() error {
if HasXVeraeToken() {
return nil
}
btn, err := osaButton(
"Verae Print Stamp needs a customer signing key before it can timestamp documents.\n\nCreate a free account on customer.verae.ai, open Security, and create an xVerae- token.",
"Verae customer account",
[]string{"Create account…", "I already have a key", "Cancel"},
"Create account…",
)
if err != nil {
return err
}
switch btn {
case "Cancel":
return fmt.Errorf("setup cancelled")
case "Create account…":
_ = exec.Command("open", PortalSignupURL).Start()
btn2, err2 := osaButton(
"In the browser:\n1. Create your account (or sign in)\n2. Open the Security tab\n3. Create an xVerae- token\n4. Copy it once — it is shown only once\n\nClick Continue when you have copied the key.",
"Create your signing key",
[]string{"Continue", "Cancel"},
"Continue",
)
if err2 != nil || btn2 == "Cancel" {
return fmt.Errorf("setup cancelled")
}
}
token, err := osaPrompt(
"Paste your xVerae- signing key from customer.verae.ai → Security.\n\nIt starts with xVerae- and is shown only once when created.",
"Paste signing key",
"",
)
if err != nil {
return err
}
token = strings.TrimSpace(token)
if token == "" {
return fmt.Errorf("no key pasted")
}
if err := SaveXVeraeToken(token); err != nil {
return err
}
_, _ = osaButton(
"Signing key saved.\n\nYou can Print → Destination → Verae Timestamping from any app.\nKeys are stored in Keychain (verae.xVeraeToken) and ~/.verae/xverae-token.",
"Verae Print Stamp",
[]string{"OK"},
"OK",
)
return nil
}
func osaButton(message, title string, buttons []string, defaultBtn string) (string, error) {
quoted := make([]string, len(buttons))
for i, b := range buttons {
quoted[i] = fmt.Sprintf("%q", b)
}
script := fmt.Sprintf(
`button returned of (display dialog %q with title %q buttons {%s} default button %q)`,
message, title, strings.Join(quoted, ", "), defaultBtn,
)
out, err := exec.Command("osascript", "-e", script).Output()
if err != nil {
return "", fmt.Errorf("dialog cancelled")
}
return strings.TrimSpace(string(out)), nil
}
func osaPrompt(message, title, defaultAnswer string) (string, error) {
script := fmt.Sprintf(
`text returned of (display dialog %q with title %q default answer %q with hidden answer buttons {"Cancel", "Save key"} default button "Save key")`,
message, title, defaultAnswer,
)
out, err := exec.Command("osascript", "-e", script).Output()
if err != nil {
return "", fmt.Errorf("prompt cancelled")
}
return strings.TrimSpace(string(out)), nil
}
// EnsureReady runs first-run setup when no signing key is present.
func EnsureReady() error {
if HasXVeraeToken() {
return nil
}
// Headless (no GUI session): fail with instructions
if os.Getenv("SSH_CONNECTION") != "" && os.Getenv("DISPLAY") == "" {
return fmt.Errorf("no xVerae- signing key — run: verae-print setup\nOr open %s , create an account, mint a key under Security, then:\n verae-print setup", PortalSignupURL)
}
return RunFirstRunSetup()
}