@@ -18,6 +18,8 @@ import (
"fmt"
"io"
"net/http"
+ "runtime/debug"
+ "sync"
)
// API error types for typed error handling.
@@ -96,14 +98,48 @@ const (
DefaultBaseURL = "https://api.lunatask.app/v1"
// statusCloudflareTimeout is Cloudflare's "A Timeout Occurred" status.
statusCloudflareTimeout = 524
+ // modulePath is the import path used to find this module's version.
+ modulePath = "git.secluded.site/go-lunatask"
)
+var (
+ versionOnce sync.Once //nolint:gochecknoglobals
+ version = "unknown"
+)
+
+// getVersion returns the library version, determined from build info.
+func getVersion() string {
+ versionOnce.Do(func() {
+ buildInfo, ok := debug.ReadBuildInfo()
+ if !ok {
+ return
+ }
+
+ // When used as a dependency, find our version in the deps list.
+ for _, dep := range buildInfo.Deps {
+ if dep.Path == modulePath {
+ version = dep.Version
+
+ return
+ }
+ }
+
+ // When running as main module (e.g., tests), use main module version.
+ if buildInfo.Main.Path == modulePath && buildInfo.Main.Version != "" && buildInfo.Main.Version != "(devel)" {
+ version = buildInfo.Main.Version
+ }
+ })
+
+ return version
+}
+
// Client handles communication with the Lunatask API.
// A Client is safe for concurrent use.
type Client struct {
accessToken string
baseURL string
httpClient *http.Client
+ userAgent string
}
// Option configures a Client.
@@ -123,6 +159,14 @@ func WithBaseURL(url string) Option {
}
}
+// WithUserAgent sets a custom User-Agent prefix.
+// The library identifier (go-lunatask/version) is always appended.
+func WithUserAgent(ua string) Option {
+ return func(c *Client) {
+ c.userAgent = ua
+ }
+}
+
// NewClient creates a new Lunatask API client.
// Generate an access token in the Lunatask desktop app under Settings → Access tokens.
func NewClient(accessToken string, opts ...Option) *Client {
@@ -130,6 +174,7 @@ func NewClient(accessToken string, opts ...Option) *Client {
accessToken: accessToken,
baseURL: DefaultBaseURL,
httpClient: &http.Client{}, //nolint:exhaustruct
+ userAgent: "",
}
for _, opt := range opts {
opt(client)
@@ -156,6 +201,13 @@ func (c *Client) Ping(ctx context.Context) (*PingResponse, error) {
func (c *Client) doRequest(req *http.Request) ([]byte, int, error) {
req.Header.Set("Authorization", "bearer "+c.accessToken)
+ ua := "go-lunatask/" + getVersion()
+ if c.userAgent != "" {
+ ua = c.userAgent + " " + ua
+ }
+
+ req.Header.Set("User-Agent", ua)
+
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, 0, fmt.Errorf("failed to send HTTP request: %w", err)