// Copyright (c) EZSCALE. // SPDX-License-Identifier: MPL-2.0 package client import ( "fmt" "net/http" "net/url" "strings" "time" ) // Client is the VirtFusion API client. type Client struct { BaseURL string Token string HTTPClient *http.Client } // New creates a new VirtFusion API client. // The endpoint can be a hostname (e.g. "cp.example.com") or a full URL // (e.g. "https://cp.example.com" or "https://cp.example.com/api/v1"). func New(endpoint, token string) (*Client, error) { if endpoint == "" { return nil, fmt.Errorf("endpoint is required") } if token == "" { return nil, fmt.Errorf("api_token is required") } baseURL := normalizeEndpoint(endpoint) return &Client{ BaseURL: baseURL, Token: token, HTTPClient: &http.Client{ Timeout: 60 * time.Second, }, }, nil } // normalizeEndpoint takes a user-provided endpoint and returns a full base URL // ending with /api/v1. Supports: // - "cp.example.com" → "https://cp.example.com/api/v1" // - "https://cp.example.com" → "https://cp.example.com/api/v1" // - "https://cp.example.com/api/v1" → "https://cp.example.com/api/v1" func normalizeEndpoint(endpoint string) string { endpoint = strings.TrimRight(endpoint, "/") // If no scheme, add https:// if !strings.Contains(endpoint, "://") { endpoint = "https://" + endpoint } // Parse to validate u, err := url.Parse(endpoint) if err != nil { // Fall back to simple construction return endpoint + "/api/v1" } // If path already ends with /api/v1, use as-is if strings.HasSuffix(u.Path, "/api/v1") { return u.String() } // Otherwise append /api/v1 u.Path = strings.TrimRight(u.Path, "/") + "/api/v1" return u.String() }