client.go

  1// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
  2//
  3// SPDX-License-Identifier: AGPL-3.0-or-later
  4
  5// Package client provides a configured Lunatask API client.
  6package client
  7
  8import (
  9	"errors"
 10	"fmt"
 11	"os"
 12	"runtime/debug"
 13
 14	"git.secluded.site/go-lunatask"
 15	"github.com/zalando/go-keyring"
 16)
 17
 18// EnvAPIKey is the environment variable name for the Lunatask API key.
 19const EnvAPIKey = "LUNATASK_API_KEY" //nolint:gosec // not a credential, just env var name
 20
 21const (
 22	keyringService = "lune"
 23	keyringUser    = "api-key"
 24)
 25
 26// ErrNoAPIKey indicates the API key is not available from env or keyring.
 27var ErrNoAPIKey = errors.New("LUNATASK_API_KEY not set and no key found in system keyring")
 28
 29// New creates a Lunatask client, checking env var first, then system keyring.
 30func New() (*lunatask.Client, error) {
 31	if token := os.Getenv(EnvAPIKey); token != "" {
 32		return lunatask.NewClient(token, lunatask.UserAgent("lune/"+version())), nil
 33	}
 34
 35	token, err := keyring.Get(keyringService, keyringUser)
 36	if err != nil {
 37		if errors.Is(err, keyring.ErrNotFound) {
 38			return nil, ErrNoAPIKey
 39		}
 40
 41		return nil, fmt.Errorf("accessing system keyring: %w", err)
 42	}
 43
 44	return lunatask.NewClient(token, lunatask.UserAgent("lune/"+version())), nil
 45}
 46
 47// GetAPIKey returns the API key from keyring. Returns empty string and nil error
 48// if not found; returns error for keyring access problems.
 49func GetAPIKey() (string, error) {
 50	token, err := keyring.Get(keyringService, keyringUser)
 51	if err != nil {
 52		if errors.Is(err, keyring.ErrNotFound) {
 53			return "", nil
 54		}
 55
 56		return "", fmt.Errorf("accessing system keyring: %w", err)
 57	}
 58
 59	return token, nil
 60}
 61
 62// SetAPIKey stores the API key in the system keyring.
 63func SetAPIKey(token string) error {
 64	if err := keyring.Set(keyringService, keyringUser, token); err != nil {
 65		return fmt.Errorf("keyring set: %w", err)
 66	}
 67
 68	return nil
 69}
 70
 71// DeleteAPIKey removes the API key from the system keyring.
 72func DeleteAPIKey() error {
 73	if err := keyring.Delete(keyringService, keyringUser); err != nil {
 74		return fmt.Errorf("keyring delete: %w", err)
 75	}
 76
 77	return nil
 78}
 79
 80// HasKeyringKey checks if an API key is stored in the keyring.
 81// Returns (true, nil) if found, (false, nil) if not found,
 82// or (false, error) if there was a keyring access problem.
 83func HasKeyringKey() (bool, error) {
 84	_, err := keyring.Get(keyringService, keyringUser)
 85	if err != nil {
 86		if errors.Is(err, keyring.ErrNotFound) {
 87			return false, nil
 88		}
 89
 90		return false, fmt.Errorf("accessing system keyring: %w", err)
 91	}
 92
 93	return true, nil
 94}
 95
 96// version returns the module version from build info, or "dev" if unavailable.
 97func version() string {
 98	info, ok := debug.ReadBuildInfo()
 99	if !ok || info.Main.Version == "" || info.Main.Version == "(devel)" {
100		return "dev"
101	}
102
103	return info.Main.Version
104}