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
 18const (
 19	keyringService = "lune"
 20	keyringUser    = "api-key"
 21)
 22
 23// ErrNoToken indicates no access token is available.
 24var ErrNoToken = errors.New("no access token found; set LUNE_ACCESS_TOKEN or run 'lune init' to configure")
 25
 26// New creates a Lunatask client using the access token from LUNE_ACCESS_TOKEN
 27// environment variable or system keyring. Environment variable takes precedence.
 28func New() (*lunatask.Client, error) {
 29	token, err := GetToken()
 30	if err != nil {
 31		return nil, err
 32	}
 33
 34	if token == "" {
 35		return nil, ErrNoToken
 36	}
 37
 38	return lunatask.NewClient(token, lunatask.UserAgent("lune/"+version())), nil
 39}
 40
 41// GetToken returns the access token from LUNE_ACCESS_TOKEN environment variable
 42// or keyring. Returns empty string and nil error if not found in either location;
 43// returns error for keyring access problems. Environment variable takes precedence.
 44func GetToken() (string, error) {
 45	// Env var takes precedence for explicit override
 46	if token := os.Getenv("LUNE_ACCESS_TOKEN"); token != "" {
 47		return token, nil
 48	}
 49
 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// SetToken stores the access token in the system keyring.
 63func SetToken(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// DeleteToken removes the access token from the system keyring.
 72func DeleteToken() 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// HasKeyringToken checks if an access token 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 HasKeyringToken() (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}